diff --git a/.ci/clang-format.sh b/.ci/clang-format.sh index 0ccd4062d..c0d8c2c2d 100755 --- a/.ci/clang-format.sh +++ b/.ci/clang-format.sh @@ -10,7 +10,7 @@ if grep -nrI '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .ci* dis fi # Default clang-format points to default 3.5 version one -CLANG_FORMAT=clang-format-17 +CLANG_FORMAT=clang-format-18 $CLANG_FORMAT --version if [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a3b3ee8ec..878c10868 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ jobs: continue-on-error: true steps: - uses: actions/checkout@v4 - - uses: fsfe/reuse-action@v4 + - uses: fsfe/reuse-action@v5 clang-format: runs-on: ubuntu-latest @@ -30,9 +30,9 @@ 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-17 main' + sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main' sudo apt update - sudo apt install clang-format-17 + sudo apt install clang-format-18 - name: Build env: COMMIT_RANGE: ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} @@ -92,7 +92,7 @@ jobs: run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - name: Build - run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $env:NUMBER_OF_PROCESSORS - name: Upload Windows SDL artifact uses: actions/upload-artifact@v4 @@ -146,7 +146,7 @@ jobs: run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - name: Build - run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $env:NUMBER_OF_PROCESSORS - name: Deploy and Package run: | @@ -315,7 +315,7 @@ jobs: run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - name: Build - run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc) - name: Package and Upload Linux(ubuntu64) SDL artifact run: | @@ -371,7 +371,7 @@ jobs: run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -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 - name: Build - run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel3 + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc) - name: Run AppImage packaging script run: ./.github/linux-appimage-qt.sh diff --git a/.gitignore b/.gitignore index 61d9e32e1..683f6f0a6 100644 --- a/.gitignore +++ b/.gitignore @@ -414,3 +414,7 @@ FodyWeavers.xsd # for macOS **/.DS_Store + +# JetBrains +.idea +cmake-build-* diff --git a/.gitmodules b/.gitmodules index 07d1d4ef7..8010250a9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -102,3 +102,8 @@ [submodule "externals/LibAtrac9"] path = externals/LibAtrac9 url = https://github.com/shadps4-emu/ext-LibAtrac9.git + shallow = true +[submodule "externals/libpng"] + path = externals/libpng + url = https://github.com/pnggroup/libpng + shallow = true \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c69eef27..b1b49946d 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,8 @@ # SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project # SPDX-License-Identifier: GPL-2.0-or-later -cmake_minimum_required(VERSION 3.16.3) +# Version 3.24 needed for FetchContent OVERRIDE_FIND_PACKAGE +cmake_minimum_required(VERSION 3.24) set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED True) @@ -110,18 +111,20 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/common/scm_rev.cpp.in" "${CMAKE_ find_package(Boost 1.84.0 CONFIG) find_package(FFmpeg 5.1.2 MODULE) find_package(fmt 10.2.0 CONFIG) -find_package(glslang 14.2.0 CONFIG) +find_package(glslang 15 CONFIG) find_package(half 1.12.0 MODULE) -find_package(magic_enum 0.9.6 CONFIG) +find_package(magic_enum 0.9.7 CONFIG) +find_package(PNG 1.6 MODULE) find_package(RenderDoc 1.6.0 MODULE) find_package(SDL3 3.1.2 CONFIG) +find_package(stb MODULE) find_package(toml11 4.2.0 CONFIG) find_package(tsl-robin-map 1.3.0 CONFIG) -find_package(VulkanHeaders 1.3.289 CONFIG) +find_package(VulkanHeaders 1.4.303 CONFIG) find_package(VulkanMemoryAllocator 3.1.0 CONFIG) find_package(xbyak 7.07 CONFIG) find_package(xxHash 0.8.2 MODULE) -find_package(zlib-ng 2.1.7 MODULE) +find_package(ZLIB 1.3 MODULE) find_package(Zydis 5.0.0 CONFIG) find_package(pugixml 1.14 CONFIG) @@ -176,10 +179,6 @@ if(ENABLE_QT_GUI) qt_add_resources(TRANSLATIONS ${TRANSLATIONS_QRC}) endif() -set(AUDIO_CORE src/audio_core/sdl_audio.cpp - src/audio_core/sdl_audio.h -) - set(AJM_LIB src/core/libraries/ajm/ajm.cpp src/core/libraries/ajm/ajm.h src/core/libraries/ajm/ajm_at9.cpp @@ -199,6 +198,9 @@ set(AUDIO_LIB src/core/libraries/audio/audioin.cpp src/core/libraries/audio/audioin.h src/core/libraries/audio/audioout.cpp src/core/libraries/audio/audioout.h + src/core/libraries/audio/sdl_audio.cpp + src/core/libraries/audio/sdl_audio.h + src/core/libraries/audio/audioout_error.h src/core/libraries/ngs2/ngs2.cpp src/core/libraries/ngs2/ngs2.h ) @@ -208,30 +210,43 @@ set(GNM_LIB src/core/libraries/gnmdriver/gnmdriver.cpp src/core/libraries/gnmdriver/gnm_error.h ) -set(KERNEL_LIB src/core/libraries/kernel/event_flag/event_flag.cpp - src/core/libraries/kernel/event_flag/event_flag.h - src/core/libraries/kernel/event_flag/event_flag_obj.cpp - src/core/libraries/kernel/event_flag/event_flag_obj.h +set(KERNEL_LIB src/core/libraries/kernel/sync/mutex.cpp + src/core/libraries/kernel/sync/mutex.h + src/core/libraries/kernel/sync/semaphore.h + src/core/libraries/kernel/threads/condvar.cpp + src/core/libraries/kernel/threads/event_flag.cpp + src/core/libraries/kernel/threads/exception.cpp + src/core/libraries/kernel/threads/exception.h + src/core/libraries/kernel/threads/mutex.cpp + src/core/libraries/kernel/threads/pthread_attr.cpp + src/core/libraries/kernel/threads/pthread_clean.cpp + src/core/libraries/kernel/threads/pthread.cpp + src/core/libraries/kernel/threads/pthread_spec.cpp src/core/libraries/kernel/threads/rwlock.cpp src/core/libraries/kernel/threads/semaphore.cpp - src/core/libraries/kernel/threads/keys.cpp - src/core/libraries/kernel/threads/threads.h - src/core/libraries/kernel/cpu_management.cpp - src/core/libraries/kernel/cpu_management.h - src/core/libraries/kernel/event_queue.cpp - src/core/libraries/kernel/event_queue.h - src/core/libraries/kernel/event_queues.cpp - src/core/libraries/kernel/event_queues.h + src/core/libraries/kernel/threads/sleepq.cpp + src/core/libraries/kernel/threads/sleepq.h + src/core/libraries/kernel/threads/stack.cpp + src/core/libraries/kernel/threads/tcb.cpp + src/core/libraries/kernel/threads/pthread.h + src/core/libraries/kernel/threads/thread_state.cpp + src/core/libraries/kernel/threads/thread_state.h + src/core/libraries/kernel/process.cpp + src/core/libraries/kernel/process.h + src/core/libraries/kernel/equeue.cpp + src/core/libraries/kernel/equeue.h src/core/libraries/kernel/file_system.cpp src/core/libraries/kernel/file_system.h - src/core/libraries/kernel/libkernel.cpp - src/core/libraries/kernel/libkernel.h - src/core/libraries/kernel/memory_management.cpp - src/core/libraries/kernel/memory_management.h - src/core/libraries/kernel/thread_management.cpp - src/core/libraries/kernel/thread_management.h - src/core/libraries/kernel/time_management.cpp - src/core/libraries/kernel/time_management.h + src/core/libraries/kernel/kernel.cpp + src/core/libraries/kernel/kernel.h + src/core/libraries/kernel/memory.cpp + src/core/libraries/kernel/memory.h + src/core/libraries/kernel/threads.cpp + src/core/libraries/kernel/threads.h + src/core/libraries/kernel/time.cpp + src/core/libraries/kernel/time.h + src/core/libraries/kernel/orbis_error.h + src/core/libraries/kernel/posix_error.h ) set(NETWORK_LIBS src/core/libraries/network/http.cpp @@ -247,6 +262,21 @@ set(NETWORK_LIBS src/core/libraries/network/http.cpp src/core/libraries/network/ssl.h ) +set(AVPLAYER_LIB src/core/libraries/avplayer/avplayer_common.cpp + src/core/libraries/avplayer/avplayer_common.h + src/core/libraries/avplayer/avplayer_file_streamer.cpp + src/core/libraries/avplayer/avplayer_file_streamer.h + src/core/libraries/avplayer/avplayer_impl.cpp + src/core/libraries/avplayer/avplayer_impl.h + src/core/libraries/avplayer/avplayer_source.cpp + src/core/libraries/avplayer/avplayer_source.h + src/core/libraries/avplayer/avplayer_state.cpp + src/core/libraries/avplayer/avplayer_state.h + src/core/libraries/avplayer/avplayer.cpp + src/core/libraries/avplayer/avplayer.h + src/core/libraries/avplayer/avplayer_error.h +) + set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp src/core/libraries/system/commondialog.h src/core/libraries/system/msgdialog.cpp @@ -268,30 +298,22 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp src/core/libraries/save_data/dialog/savedatadialog_ui.h src/core/libraries/system/sysmodule.cpp src/core/libraries/system/sysmodule.h + src/core/libraries/system/system_error.h src/core/libraries/system/systemservice.cpp src/core/libraries/system/systemservice.h + src/core/libraries/system/systemservice_error.h src/core/libraries/system/userservice.cpp src/core/libraries/system/userservice.h + src/core/libraries/system/userservice_error.h src/core/libraries/app_content/app_content.cpp src/core/libraries/app_content/app_content.h + src/core/libraries/app_content/app_content_error.h src/core/libraries/rtc/rtc.cpp src/core/libraries/rtc/rtc.h src/core/libraries/rtc/rtc_error.h src/core/libraries/disc_map/disc_map.cpp src/core/libraries/disc_map/disc_map.h src/core/libraries/disc_map/disc_map_codes.h - src/core/libraries/avplayer/avplayer_common.cpp - src/core/libraries/avplayer/avplayer_common.h - src/core/libraries/avplayer/avplayer_file_streamer.cpp - src/core/libraries/avplayer/avplayer_file_streamer.h - src/core/libraries/avplayer/avplayer_impl.cpp - src/core/libraries/avplayer/avplayer_impl.h - src/core/libraries/avplayer/avplayer_source.cpp - src/core/libraries/avplayer/avplayer_source.h - src/core/libraries/avplayer/avplayer_state.cpp - src/core/libraries/avplayer/avplayer_state.h - src/core/libraries/avplayer/avplayer.cpp - src/core/libraries/avplayer/avplayer.h src/core/libraries/ngs2/ngs2.cpp src/core/libraries/ngs2/ngs2.h src/core/libraries/ngs2/ngs2_error.h @@ -309,6 +331,8 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp src/core/libraries/remote_play/remoteplay.h src/core/libraries/share_play/shareplay.cpp src/core/libraries/share_play/shareplay.h + src/core/libraries/razor_cpu/razor_cpu.cpp + src/core/libraries/razor_cpu/razor_cpu.h ) set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h @@ -316,6 +340,7 @@ set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h src/core/libraries/videoout/driver.h src/core/libraries/videoout/video_out.cpp src/core/libraries/videoout/video_out.h + src/core/libraries/videoout/videoout_error.h ) set(LIBC_SOURCES src/core/libraries/libc_internal/libc_internal.cpp @@ -333,18 +358,28 @@ set(IME_LIB src/core/libraries/ime/error_dialog.cpp src/core/libraries/ime/ime_ui.h src/core/libraries/ime/ime.cpp src/core/libraries/ime/ime.h + src/core/libraries/ime/ime_error.h ) set(PAD_LIB src/core/libraries/pad/pad.cpp src/core/libraries/pad/pad.h + src/core/libraries/pad/pad_errors.h ) set(PNG_LIB src/core/libraries/libpng/pngdec.cpp src/core/libraries/libpng/pngdec.h + src/core/libraries/libpng/pngdec_error.h +) + +set(JPEG_LIB src/core/libraries/jpeg/jpeg_error.h + src/core/libraries/jpeg/jpegenc.cpp + src/core/libraries/jpeg/jpegenc.h ) set(PLAYGO_LIB src/core/libraries/playgo/playgo.cpp src/core/libraries/playgo/playgo.h + src/core/libraries/playgo/playgo_dialog.cpp + src/core/libraries/playgo/playgo_dialog.h src/core/libraries/playgo/playgo_types.h ) @@ -359,6 +394,7 @@ set(USBD_LIB src/core/libraries/usbd/usbd.cpp set(FIBER_LIB src/core/libraries/fiber/fiber.cpp src/core/libraries/fiber/fiber.h + src/core/libraries/fiber/fiber_error.h ) set(VDEC_LIB src/core/libraries/videodec/videodec2_impl.cpp @@ -368,6 +404,7 @@ set(VDEC_LIB src/core/libraries/videodec/videodec2_impl.cpp src/core/libraries/videodec/videodec2_avc.h src/core/libraries/videodec/videodec.cpp src/core/libraries/videodec/videodec.h + src/core/libraries/videodec/videodec_error.h src/core/libraries/videodec/videodec_impl.cpp src/core/libraries/videodec/videodec_impl.h ) @@ -380,6 +417,7 @@ set(NP_LIBS src/core/libraries/np_manager/np_manager.cpp src/core/libraries/np_trophy/np_trophy.h src/core/libraries/np_trophy/trophy_ui.cpp src/core/libraries/np_trophy/trophy_ui.h + src/core/libraries/np_trophy/np_trophy_error.h ) set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp @@ -401,10 +439,14 @@ set(DEV_TOOLS src/core/devtools/layer.cpp src/core/devtools/widget/frame_graph.cpp src/core/devtools/widget/frame_graph.h src/core/devtools/widget/imgui_memory_editor.h + src/core/devtools/widget/memory_map.cpp + src/core/devtools/widget/memory_map.h src/core/devtools/widget/reg_popup.cpp src/core/devtools/widget/reg_popup.h src/core/devtools/widget/reg_view.cpp src/core/devtools/widget/reg_view.h + src/core/devtools/widget/shader_list.cpp + src/core/devtools/widget/shader_list.h src/core/devtools/widget/text_editor.cpp src/core/devtools/widget/text_editor.h ) @@ -453,7 +495,12 @@ set(COMMON src/common/logging/backend.cpp src/common/signal_context.h src/common/signal_context.cpp src/common/singleton.h + src/common/slab_heap.h src/common/slot_vector.h + src/common/spin_lock.cpp + src/common/spin_lock.h + src/common/stb.cpp + src/common/stb.h src/common/string_util.cpp src/common/string_util.h src/common/thread.cpp @@ -461,6 +508,7 @@ set(COMMON src/common/logging/backend.cpp src/common/types.h src/common/uint128.h src/common/unique_function.h + src/common/va_ctx.h src/common/version.h src/common/ntapi.h src/common/ntapi.cpp @@ -485,6 +533,12 @@ set(CORE src/core/aerolib/stubs.cpp src/core/crypto/crypto.cpp src/core/crypto/crypto.h src/core/crypto/keys.h + src/core/devices/base_device.cpp + src/core/devices/base_device.h + src/core/devices/ioccom.h + src/core/devices/logger.cpp + src/core/devices/logger.h + src/core/devices/nop_device.h src/core/file_format/pfs.h src/core/file_format/pkg.cpp src/core/file_format/pkg.h @@ -508,10 +562,10 @@ set(CORE src/core/aerolib/stubs.cpp src/core/loader/elf.h src/core/loader/symbols_resolver.h src/core/loader/symbols_resolver.cpp - src/core/libraries/error_codes.h src/core/libraries/libs.h src/core/libraries/libs.cpp ${AJM_LIB} + ${AVPLAYER_LIB} ${AUDIO_LIB} ${GNM_LIB} ${KERNEL_LIB} @@ -522,6 +576,7 @@ set(CORE src/core/aerolib/stubs.cpp ${VIDEOOUT_LIB} ${NP_LIBS} ${PNG_LIB} + ${JPEG_LIB} ${PLAYGO_LIB} ${RANDOM_LIB} ${USBD_LIB} @@ -541,10 +596,10 @@ set(CORE src/core/aerolib/stubs.cpp src/core/platform.h src/core/signals.cpp src/core/signals.h + src/core/thread.cpp + src/core/thread.h src/core/tls.cpp src/core/tls.h - src/core/virtual_memory.cpp - src/core/virtual_memory.h ) if (ARCHITECTURE STREQUAL "x86_64") @@ -657,8 +712,6 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp src/video_core/buffer_cache/word_manager.h src/video_core/renderer_vulkan/liverpool_to_vk.cpp src/video_core/renderer_vulkan/liverpool_to_vk.h - src/video_core/renderer_vulkan/renderer_vulkan.cpp - src/video_core/renderer_vulkan/renderer_vulkan.h src/video_core/renderer_vulkan/vk_common.cpp src/video_core/renderer_vulkan/vk_common.h src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -677,6 +730,8 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp src/video_core/renderer_vulkan/vk_pipeline_common.h src/video_core/renderer_vulkan/vk_platform.cpp src/video_core/renderer_vulkan/vk_platform.h + src/video_core/renderer_vulkan/vk_presenter.cpp + src/video_core/renderer_vulkan/vk_presenter.h src/video_core/renderer_vulkan/vk_rasterizer.cpp src/video_core/renderer_vulkan/vk_rasterizer.h src/video_core/renderer_vulkan/vk_resource_pool.cpp @@ -820,12 +875,20 @@ endif() create_target_directory_groups(shadps4) -target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half) -target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3 pugixml::pugixml) +target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG) +target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers) target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h") target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h") +if (ENABLE_DISCORD_RPC) + target_compile_definitions(shadps4 PRIVATE ENABLE_DISCORD_RPC) +endif() + +if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + target_compile_definitions(shadps4 PRIVATE ENABLE_USERFAULTFD) +endif() + if (APPLE) option(USE_SYSTEM_VULKAN_LOADER "Enables using the system Vulkan loader instead of directly linking with MoltenVK. Useful for loading validation layers." OFF) if (USE_SYSTEM_VULKAN_LOADER) @@ -850,9 +913,9 @@ if (NOT ENABLE_QT_GUI) endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND MSVC) - target_link_libraries(shadps4 PRIVATE cryptoppwin zlib-ng::zlib) + target_link_libraries(shadps4 PRIVATE cryptoppwin) else() - target_link_libraries(shadps4 PRIVATE cryptopp::cryptopp zlib-ng::zlib) + target_link_libraries(shadps4 PRIVATE cryptopp::cryptopp) endif() if (ENABLE_QT_GUI) @@ -924,7 +987,10 @@ if (ENABLE_QT_GUI) set_target_properties(shadps4 PROPERTIES # WIN32_EXECUTABLE ON MACOSX_BUNDLE ON - MACOSX_BUNDLE_ICON_FILE shadPS4.icns) + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/dist/MacOSBundleInfo.plist.in" + MACOSX_BUNDLE_ICON_FILE "shadPS4.icns" + MACOSX_BUNDLE_SHORT_VERSION_STRING "0.4.1" + ) set_source_files_properties(src/images/shadPS4.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources) diff --git a/REUSE.toml b/REUSE.toml index b0e482ef3..47e8ee4d7 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -7,6 +7,7 @@ path = [ ".github/FUNDING.yml", ".github/shadps4.png", ".gitmodules", + "dist/MacOSBundleInfo.plist.in", "dist/net.shadps4.shadPS4.desktop", "dist/net.shadps4.shadPS4_metadata.pot", "dist/net.shadps4.shadPS4.metainfo.xml", @@ -41,6 +42,7 @@ path = [ "src/images/refresh_icon.png", "src/images/settings_icon.png", "src/images/stop_icon.png", + "src/images/utils_icon.png", "src/images/shadPS4.icns", "src/images/shadps4.ico", "src/images/net.shadps4.shadPS4.svg", @@ -68,7 +70,7 @@ SPDX-FileCopyrightText = "2019-2024 Baldur Karlsson" SPDX-License-Identifier = "MIT" [[annotations]] -path = "externals/stb_image.h" +path = "externals/stb/**" precedence = "aggregate" SPDX-FileCopyrightText = "2017 Sean Barrett" SPDX-License-Identifier = "MIT" diff --git a/cmake/Findstb.cmake b/cmake/Findstb.cmake new file mode 100644 index 000000000..667911e1d --- /dev/null +++ b/cmake/Findstb.cmake @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +find_path(stb_image_INCLUDE_DIR stb_image.h PATH_SUFFIXES stb) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(stb + REQUIRED_VARS stb_image_INCLUDE_DIR +) + +if (stb_FOUND AND NOT TARGET stb::headers) + add_library(stb::headers INTERFACE IMPORTED) + set_property(TARGET stb::headers PROPERTY + INTERFACE_INCLUDE_DIRECTORIES + "${stb_image_INCLUDE_DIR}" + ) +endif() + +mark_as_advanced(stb_image_INCLUDE_DIR) diff --git a/cmake/Findzlib-ng.cmake b/cmake/Findzlib-ng.cmake deleted file mode 100644 index ec6f14b4a..000000000 --- a/cmake/Findzlib-ng.cmake +++ /dev/null @@ -1,15 +0,0 @@ -# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -# SPDX-License-Identifier: GPL-2.0-or-later - -find_package(PkgConfig QUIET) -pkg_search_module(ZLIB_NG QUIET IMPORTED_TARGET zlib-ng) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(zlib-ng - REQUIRED_VARS ZLIB_NG_LINK_LIBRARIES - VERSION_VAR ZLIB_NG_VERSION -) - -if (zlib-ng_FOUND AND NOT TARGET zlib-ng::zlib) - add_library(zlib-ng::zlib ALIAS PkgConfig::ZLIB_NG) -endif() diff --git a/dist/MacOSBundleInfo.plist.in b/dist/MacOSBundleInfo.plist.in new file mode 100644 index 000000000..70cbfb4ab --- /dev/null +++ b/dist/MacOSBundleInfo.plist.in @@ -0,0 +1,46 @@ + + + + + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + + CFBundleName + shadps4 + CFBundleIdentifier + com.shadps4-emu.shadps4 + CFBundleExecutable + shadps4 + + CFBundleVersion + 1.0.0 + CFBundleShortVersionString + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} + + LSMinimumSystemVersion + ${CMAKE_OSX_DEPLOYMENT_TARGET} + LSApplicationCategoryType + public.app-category.games + GCSupportsGameMode + + + NSHumanReadableCopyright + + + CFBundleIconFile + ${MACOSX_BUNDLE_ICON_FILE} + + CFBundleDevelopmentRegion + en + CFBundleAllowMixedLocalizations + + + NSPrincipalClass + NSApplication + + NSSupportsAutomaticGraphicsSwitching + + + diff --git a/documents/building-windows.md b/documents/building-windows.md index 48fd09c41..d01e7b81e 100644 --- a/documents/building-windows.md +++ b/documents/building-windows.md @@ -25,8 +25,8 @@ Once you are within the installer: Beware, this requires you to create a Qt account. If you do not want to do this, please follow the MSYS2/MinGW compilation method instead. -1. Under the current, non beta version of Qt (at the time of writing 6.7.2), select the option `MSVC 2019 64-bit` or similar. - If you are on Windows on ARM / Qualcomm Snapdragon Elite X, select `MSVC 2019 ARM64` instead. +1. Under the current, non beta version of Qt (at the time of writing 6.7.3), select the option `MSVC 2022 64-bit` or similar. + If you are on Windows on ARM / Qualcomm Snapdragon Elite X, select `MSVC 2022 ARM64` instead. Go through the installation normally. If you know what you are doing, you may unselect individual components that eat up too much disk space. @@ -35,7 +35,7 @@ Beware, this requires you to create a Qt account. If you do not want to do this, Once you are finished, you will have to configure Qt within Visual Studio: 1. Tools -> Options -> Qt -> Versions -2. Add a new Qt version and navigate it to the correct folder. Should look like so: `C:\Qt\6.7.2\msvc2019_64` +2. Add a new Qt version and navigate it to the correct folder. Should look like so: `C:\Qt\6.7.3\msvc2022_64` 3. Enable the default checkmark on the new version you just created. ### (Prerequisite) Download [**Git for Windows**](https://git-scm.com/download/win) @@ -55,16 +55,16 @@ Go through the Git for Windows installation as normal 3. If you want to build shadPS4 with the Qt Gui: 1. Click x64-Clang-Release and select "Manage Configurations" 2. Look for "CMake command arguments" and add to the text field - `-DENABLE_QT_GUI=ON -DCMAKE_PREFIX_PATH=C:\Qt\6.7.2\msvc2019_64` + `-DENABLE_QT_GUI=ON -DCMAKE_PREFIX_PATH=C:\Qt\6.7.3\msvc2022_64` (Change Qt path if you've installed it to non-default path) 3. Press CTRL+S to save and wait a moment for CMake generation 4. Change the project to build to shadps4.exe 5. Build -> Build All -Your shadps4.exe will be in `c:\path\to\source\Build\x64-Clang-Release\` +Your shadps4.exe will be in `C:\path\to\source\Build\x64-Clang-Release\` To automatically populate the necessary files to run shadPS4.exe, run in a command prompt or terminal: -`C:\Qt\6.7.2\msvc2019_64\bin\windeployqt.exe "c:\path\to\shadps4.exe"` +`C:\Qt\6.7.3\msvc2022_64\bin\windeployqt.exe "C:\path\to\shadps4.exe"` (Change Qt path if you've installed it to non-default path) ## Option 2: MSYS2/MinGW @@ -79,7 +79,7 @@ Normal x86-based computers, follow: 1. Open "MSYS2 MINGW64" from your new applications 2. Run `pacman -Syu`, let it complete; -3. Run `pacman -S --needed git mingw-w64-x86_64-binutils mingw-w64-x86_64-clang mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-ffmpeg` +3. Run `pacman -S --needed git mingw-w64-x86_64-binutils mingw-w64-x86_64-clang mingw-w64-x86_64-cmake mingw-w64-x86_64-rapidjson mingw-w64-x86_64-ninja mingw-w64-x86_64-ffmpeg` 1. Optional (Qt only): run `pacman -S --needed mingw-w64-x86_64-qt6-base mingw-w64-x86_64-qt6-tools mingw-w64-x86_64-qt6-multimedia` 4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4` 5. Run `cd shadPS4` @@ -93,7 +93,7 @@ ARM64-based computers, follow: 1. Open "MSYS2 CLANGARM64" from your new applications 2. Run `pacman -Syu`, let it complete; -3. Run `pacman -S --needed git mingw-w64-clang-aarch64-binutils mingw-w64-clang-aarch64-clang mingw-w64-clang-aarch64-cmake mingw-w64-clang-aarch64-ninja mingw-w64-clang-aarch64-ffmpeg` +3. Run `pacman -S --needed git mingw-w64-clang-aarch64-binutils mingw-w64-clang-aarch64-clang mingw-w64-clang-aarch64-rapidjson mingw-w64-clang-aarch64-cmake mingw-w64-clang-aarch64-ninja mingw-w64-clang-aarch64-ffmpeg` 1. Optional (Qt only): run `pacman -S --needed mingw-w64-clang-aarch64-qt6-base mingw-w64-clang-aarch64-qt6-tools mingw-w64-clang-aarch64-qt6-multimedia` 4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4` 5. Run `cd shadPS4` diff --git a/documents/patching-shader.md b/documents/patching-shader.md new file mode 100644 index 000000000..613e89bf9 --- /dev/null +++ b/documents/patching-shader.md @@ -0,0 +1,19 @@ + + +### Install Vulkan SDK and \*ensure `spirv-cross` and `glslc` are in PATH\*. + +1. Enable `dumpShaders` in config.toml + +2. Run `spirv-cross -V fs_0x000000.spv --output fs_0x000000.glsl` to decompile the SPIR-V IR to GLSL. + +3. Edit the GLSL file as you wish + +4. To compile back to SPIR-V, run (change the _**-fshader-stage**_ to correct stage): + `glslc --target-env=vulkan1.3 --target-spv=spv1.6 -fshader-stage=frag fs_0x000000.glsl -o fs_0x000000.spv` + +5. Put the updated .spv file to `shader/patch` folder with the same name as the original shader + +6. Enable `patchShaders` in config.toml \ No newline at end of file diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 17d710878..082be211a 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -35,10 +35,11 @@ else() if (NOT TARGET cryptopp::cryptopp) set(CRYPTOPP_INSTALL OFF) set(CRYPTOPP_BUILD_TESTING OFF) - set(CRYPTOPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cryptopp/) + set(CRYPTOPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cryptopp) add_subdirectory(cryptopp-cmake) file(COPY cryptopp DESTINATION cryptopp FILES_MATCHING PATTERN "*.h") - target_include_directories(cryptopp INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/cryptopp") + # remove externals/cryptopp from include directories because it contains a conflicting zlib.h file + set_target_properties(cryptopp PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/cryptopp") endif() endif() @@ -52,14 +53,23 @@ file(GLOB LIBATRAC9_SOURCES LibAtrac9/C/src/*.c) add_library(LibAtrac9 STATIC ${LIBATRAC9_SOURCES}) target_include_directories(LibAtrac9 INTERFACE LibAtrac9/C/src) -# Zlib-Ng -if (NOT TARGET zlib-ng::zlib) +# zlib +if (NOT TARGET ZLIB::ZLIB) set(ZLIB_ENABLE_TESTS OFF) set(WITH_GTEST OFF) set(WITH_NEW_STRATEGIES ON) set(WITH_NATIVE_INSTRUCTIONS ON) - add_subdirectory(zlib-ng) - add_library(zlib-ng::zlib ALIAS zlib) + set(ZLIB_COMPAT ON CACHE BOOL "" FORCE) + include(FetchContent) + FetchContent_Declare( + ZLIB + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/zlib-ng" + OVERRIDE_FIND_PACKAGE + ) + FetchContent_MakeAvailable(ZLIB) + add_library(ZLIB::ZLIB ALIAS zlib) + # libpng expects this variable to exist after its find_package(ZLIB) + set(ZLIB_INCLUDE_DIRS "${FETCHCONTENT_BASE_DIR}/zlib-build") endif() # SDL3 @@ -153,6 +163,17 @@ if (NOT TARGET half::half) add_library(half::half ALIAS half) endif() +# libpng +if (NOT TARGET PNG::PNG) + set(PNG_SHARED OFF CACHE BOOL "" FORCE) + set(PNG_STATIC ON CACHE BOOL "" FORCE) + set(PNG_TESTS OFF CACHE BOOL "" FORCE) + set(PNG_TOOLS OFF CACHE BOOL "" FORCE) + set(SKIP_INSTALL_ALL OFF CACHE BOOL "" FORCE) + add_subdirectory(libpng) + add_library(PNG::PNG ALIAS png_static) +endif() + if (APPLE) # date if (NOT TARGET date::date-tz) @@ -195,9 +216,16 @@ endif() # Discord RPC if (ENABLE_DISCORD_RPC) set(BUILD_EXAMPLES OFF) - add_subdirectory(discord-rpc/) + add_subdirectory(discord-rpc) target_include_directories(discord-rpc INTERFACE discord-rpc/include) endif() # GCN Headers add_subdirectory(gcn) + +# stb +if (NOT TARGET stb::headers) + add_library(stb INTERFACE) + target_include_directories(stb INTERFACE stb) + add_library(stb::headers ALIAS stb) +endif() diff --git a/externals/LibAtrac9 b/externals/LibAtrac9 index 3acdcdc78..9640129dc 160000 --- a/externals/LibAtrac9 +++ b/externals/LibAtrac9 @@ -1 +1 @@ -Subproject commit 3acdcdc78f129c2e6145331ff650fa76dd88d62c +Subproject commit 9640129dc6f2afbca6ceeca3019856e8653a5fb2 diff --git a/externals/date b/externals/date index dd8affc6d..28b7b2325 160000 --- a/externals/date +++ b/externals/date @@ -1 +1 @@ -Subproject commit dd8affc6de5755e07638bf0a14382d29549d6ee9 +Subproject commit 28b7b232521ace2c8ef3f2ad4126daec3569c14f diff --git a/externals/ext-boost b/externals/ext-boost index f2474e1b5..ca6f230e6 160000 --- a/externals/ext-boost +++ b/externals/ext-boost @@ -1 +1 @@ -Subproject commit f2474e1b584fb7a3ed6f85ba875e6eacd742ec8a +Subproject commit ca6f230e67be7cc45fc919057f07b2aee64dadc1 diff --git a/externals/ffmpeg-core b/externals/ffmpeg-core index e30b7d7fe..27de97c82 160000 --- a/externals/ffmpeg-core +++ b/externals/ffmpeg-core @@ -1 +1 @@ -Subproject commit e30b7d7fe228bfb3f6e41ce1040b44a15eb7d5e0 +Subproject commit 27de97c826b6b40c255891c37ac046a25836a575 diff --git a/externals/glslang b/externals/glslang index e61d7bb30..a0995c49e 160000 --- a/externals/glslang +++ b/externals/glslang @@ -1 +1 @@ -Subproject commit e61d7bb3006f451968714e2f653412081871e1ee +Subproject commit a0995c49ebcaca2c6d3b03efbabf74f3843decdb diff --git a/externals/libpng b/externals/libpng new file mode 160000 index 000000000..c1cc0f3f4 --- /dev/null +++ b/externals/libpng @@ -0,0 +1 @@ +Subproject commit c1cc0f3f4c3d4abd11ca68c59446a29ff6f95003 diff --git a/externals/magic_enum b/externals/magic_enum index 126539e13..1a1824df7 160000 --- a/externals/magic_enum +++ b/externals/magic_enum @@ -1 +1 @@ -Subproject commit 126539e13cccdc2e75ce770e94f3c26403099fa5 +Subproject commit 1a1824df7ac798177a521eed952720681b0bf482 diff --git a/externals/pugixml b/externals/pugixml index 3b1718437..4bc14418d 160000 --- a/externals/pugixml +++ b/externals/pugixml @@ -1 +1 @@ -Subproject commit 3b17184379fcaaeb7f1fbe08018b7fedf2640b3b +Subproject commit 4bc14418d12d289dd9978fdce9490a45deeb653e diff --git a/externals/sdl3 b/externals/sdl3 index 54e622c2e..3a1d76d29 160000 --- a/externals/sdl3 +++ b/externals/sdl3 @@ -1 +1 @@ -Subproject commit 54e622c2e6af456bfef382fae44c17682d5ac88a +Subproject commit 3a1d76d298db023f6cf37fb08ee766f20a4e12ab diff --git a/externals/stb_image.h b/externals/stb/stb_image.h similarity index 100% rename from externals/stb_image.h rename to externals/stb/stb_image.h diff --git a/externals/toml11 b/externals/toml11 index f925e7f28..7f6c574ff 160000 --- a/externals/toml11 +++ b/externals/toml11 @@ -1 +1 @@ -Subproject commit f925e7f287c0008813c2294798cf9ca167fd9ffd +Subproject commit 7f6c574ff5aa1053534e7e19c0a4f22bf4c6aaca diff --git a/externals/tracy b/externals/tracy index b8061982c..143a53d19 160000 --- a/externals/tracy +++ b/externals/tracy @@ -1 +1 @@ -Subproject commit b8061982cad0210b649541016c88ff5faa90733c +Subproject commit 143a53d1985b8e52a7590a0daca30a0a7c653b42 diff --git a/externals/vma b/externals/vma index 1c35ba99c..5a53a1989 160000 --- a/externals/vma +++ b/externals/vma @@ -1 +1 @@ -Subproject commit 1c35ba99ce775f8342d87a83a3f0f696f99c2a39 +Subproject commit 5a53a198945ba8260fbc58fadb788745ce6aa263 diff --git a/externals/vulkan-headers b/externals/vulkan-headers index d91597a82..6a74a7d65 160000 --- a/externals/vulkan-headers +++ b/externals/vulkan-headers @@ -1 +1 @@ -Subproject commit d91597a82f881d473887b560a03a7edf2720b72c +Subproject commit 6a74a7d65cafa19e38ec116651436cce6efd5b2e diff --git a/externals/xbyak b/externals/xbyak index d067f0d3f..4e44f4614 160000 --- a/externals/xbyak +++ b/externals/xbyak @@ -1 +1 @@ -Subproject commit d067f0d3f55696ae8bc9a25ad7012ee80f221d54 +Subproject commit 4e44f4614ddbf038f2a6296f5b906d5c72691e0f diff --git a/externals/xxhash b/externals/xxhash index d4ad85e4a..2bf8313b9 160000 --- a/externals/xxhash +++ b/externals/xxhash @@ -1 +1 @@ -Subproject commit d4ad85e4afaad5c780f54db1dc967fff5a869ffd +Subproject commit 2bf8313b934633b2a5b7e8fd239645b85e10c852 diff --git a/externals/zydis b/externals/zydis index 9d298eb80..bffbb610c 160000 --- a/externals/zydis +++ b/externals/zydis @@ -1 +1 @@ -Subproject commit 9d298eb8067ff62a237203d1e1470785033e185c +Subproject commit bffbb610cfea643b98e87658b9058382f7522807 diff --git a/src/audio_core/sdl_audio.cpp b/src/audio_core/sdl_audio.cpp deleted file mode 100644 index 7fed42a44..000000000 --- a/src/audio_core/sdl_audio.cpp +++ /dev/null @@ -1,166 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "sdl_audio.h" - -#include "common/assert.h" -#include "core/libraries/error_codes.h" - -#include -#include -#include - -#include // std::unique_lock - -namespace Audio { - -constexpr int AUDIO_STREAM_BUFFER_THRESHOLD = 65536; // Define constant for buffer threshold - -s32 SDLAudio::AudioOutOpen(int type, u32 samples_num, u32 freq, - Libraries::AudioOut::OrbisAudioOutParamFormat format) { - using Libraries::AudioOut::OrbisAudioOutParamFormat; - std::unique_lock lock{m_mutex}; - for (int id = 0; id < portsOut.size(); id++) { - auto& port = portsOut[id]; - if (!port.isOpen) { - port.isOpen = true; - port.type = type; - port.samples_num = samples_num; - port.freq = freq; - port.format = format; - SDL_AudioFormat sampleFormat; - switch (format) { - case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_MONO: - sampleFormat = SDL_AUDIO_S16; - port.channels_num = 1; - port.sample_size = 2; - break; - case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_MONO: - sampleFormat = SDL_AUDIO_F32; - port.channels_num = 1; - port.sample_size = 4; - break; - case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_STEREO: - sampleFormat = SDL_AUDIO_S16; - port.channels_num = 2; - port.sample_size = 2; - break; - case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_STEREO: - sampleFormat = SDL_AUDIO_F32; - port.channels_num = 2; - port.sample_size = 4; - break; - case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_8CH: - sampleFormat = SDL_AUDIO_S16; - port.channels_num = 8; - port.sample_size = 2; - break; - case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_8CH: - sampleFormat = SDL_AUDIO_F32; - port.channels_num = 8; - port.sample_size = 4; - break; - case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_8CH_STD: - sampleFormat = SDL_AUDIO_S16; - port.channels_num = 8; - port.sample_size = 2; - break; - case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_8CH_STD: - sampleFormat = SDL_AUDIO_F32; - port.channels_num = 8; - port.sample_size = 4; - break; - default: - UNREACHABLE_MSG("Unknown format"); - } - - for (int i = 0; i < port.channels_num; i++) { - port.volume[i] = Libraries::AudioOut::SCE_AUDIO_OUT_VOLUME_0DB; - } - - SDL_AudioSpec fmt; - SDL_zero(fmt); - fmt.format = sampleFormat; - fmt.channels = port.channels_num; - fmt.freq = freq; // Set frequency from the argument - port.stream = - SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &fmt, NULL, NULL); - SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(port.stream)); - return id + 1; - } - } - - LOG_ERROR(Lib_AudioOut, "Audio ports are full"); - return ORBIS_AUDIO_OUT_ERROR_PORT_FULL; // all ports are used -} - -s32 SDLAudio::AudioOutOutput(s32 handle, const void* ptr) { - std::shared_lock lock{m_mutex}; - auto& port = portsOut[handle - 1]; - if (!port.isOpen) { - return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; - } - - const size_t data_size = port.samples_num * port.sample_size * port.channels_num; - - bool result = SDL_PutAudioStreamData(port.stream, ptr, data_size); - - lock.unlock(); // Unlock only after necessary operations - - while (SDL_GetAudioStreamAvailable(port.stream) > AUDIO_STREAM_BUFFER_THRESHOLD) { - SDL_Delay(0); - } - - return result ? ORBIS_OK : -1; -} - -s32 SDLAudio::AudioOutSetVolume(s32 handle, s32 bitflag, s32* volume) { - using Libraries::AudioOut::OrbisAudioOutParamFormat; - std::shared_lock lock{m_mutex}; - auto& port = portsOut[handle - 1]; - if (!port.isOpen) { - return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; - } - - for (int i = 0; i < port.channels_num; i++, bitflag >>= 1u) { - auto bit = bitflag & 0x1u; - - if (bit == 1) { - int src_index = i; - if (port.format == - OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_8CH_STD || - port.format == OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_8CH_STD) { - switch (i) { - case 4: - src_index = 6; - break; - case 5: - src_index = 7; - break; - case 6: - src_index = 4; - break; - case 7: - src_index = 5; - break; - default: - break; - } - } - port.volume[i] = volume[src_index]; - } - } - - return ORBIS_OK; -} - -s32 SDLAudio::AudioOutGetStatus(s32 handle, int* type, int* channels_num) { - std::shared_lock lock{m_mutex}; - auto& port = portsOut[handle - 1]; - *type = port.type; - *channels_num = port.channels_num; - - return ORBIS_OK; -} - -} // namespace Audio diff --git a/src/audio_core/sdl_audio.h b/src/audio_core/sdl_audio.h deleted file mode 100644 index 0d4783f19..000000000 --- a/src/audio_core/sdl_audio.h +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include "core/libraries/audio/audioout.h" - -namespace Audio { - -class SDLAudio { -public: - SDLAudio() = default; - virtual ~SDLAudio() = default; - - s32 AudioOutOpen(int type, u32 samples_num, u32 freq, - Libraries::AudioOut::OrbisAudioOutParamFormat format); - s32 AudioOutOutput(s32 handle, const void* ptr); - s32 AudioOutSetVolume(s32 handle, s32 bitflag, s32* volume); - s32 AudioOutGetStatus(s32 handle, int* type, int* channels_num); - -private: - struct PortOut { - SDL_AudioStream* stream = nullptr; - u32 samples_num = 0; - u32 freq = 0; - u32 format = -1; - int type = 0; - int channels_num = 0; - int volume[8] = {}; - u8 sample_size = 0; - bool isOpen = false; - }; - std::shared_mutex m_mutex; - std::array portsOut; -}; - -} // namespace Audio diff --git a/src/common/alignment.h b/src/common/alignment.h index 8480fae26..3fb961c63 100644 --- a/src/common/alignment.h +++ b/src/common/alignment.h @@ -22,6 +22,12 @@ template return static_cast(value - value % size); } +template + requires std::is_integral_v +[[nodiscard]] constexpr bool IsAligned(T value, std::size_t alignment) { + return (value & (alignment - 1)) == 0; +} + template requires std::is_integral_v [[nodiscard]] constexpr bool Is16KBAligned(T value) { diff --git a/src/common/config.cpp b/src/common/config.cpp index e97a46005..eae8897c8 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -3,13 +3,14 @@ #include #include -#include #include #include // for wstring support #include -#include "common/logging/formatter.h" + #include "common/path_util.h" #include "config.h" +#include "logging/formatter.h" +#include "version.h" namespace toml { template @@ -46,11 +47,13 @@ static std::string backButtonBehavior = "left"; static bool useSpecialPad = false; static int specialPadClass = 1; static bool isDebugDump = false; +static bool isShaderDebug = false; static bool isShowSplash = false; static bool isAutoUpdate = false; static bool isNullGpu = false; static bool shouldCopyGPUBuffers = false; static bool shouldDumpShaders = false; +static bool shouldPatchShaders = true; static u32 vblankDivider = 1; static bool vkValidation = false; static bool vkValidationSync = false; @@ -81,7 +84,8 @@ std::vector m_pkg_viewer; std::vector m_elf_viewer; std::vector m_recent_files; std::string emulator_language = "en"; -// Settings + +// Language u32 m_language = 1; // english bool isNeoMode() { @@ -156,6 +160,10 @@ bool debugDump() { return isDebugDump; } +bool collectShadersForDebug() { + return isShaderDebug; +} + bool showSplash() { return isShowSplash; } @@ -176,6 +184,10 @@ bool dumpShaders() { return shouldDumpShaders; } +bool patchShaders() { + return shouldPatchShaders; +} + bool isRdocEnabled() { return rdocEnable; } @@ -228,6 +240,10 @@ void setDebugDump(bool enable) { isDebugDump = enable; } +void setCollectShaderForDebug(bool enable) { + isShaderDebug = enable; +} + void setShowSplash(bool enable) { isShowSplash = enable; } @@ -334,6 +350,7 @@ void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) { main_window_geometry_w = w; main_window_geometry_h = h; } + bool addGameInstallDir(const std::filesystem::path& dir) { if (std::find(settings_install_dirs.begin(), settings_install_dirs.end(), dir) == settings_install_dirs.end()) { @@ -342,47 +359,60 @@ bool addGameInstallDir(const std::filesystem::path& dir) { } return false; } + void removeGameInstallDir(const std::filesystem::path& dir) { auto iterator = std::find(settings_install_dirs.begin(), settings_install_dirs.end(), dir); if (iterator != settings_install_dirs.end()) { settings_install_dirs.erase(iterator); } } + void setAddonInstallDir(const std::filesystem::path& dir) { settings_addon_install_dir = dir; } + void setMainWindowTheme(u32 theme) { mw_themes = theme; } + void setIconSize(u32 size) { m_icon_size = size; } + void setIconSizeGrid(u32 size) { m_icon_size_grid = size; } + void setSliderPosition(u32 pos) { m_slider_pos = pos; } + void setSliderPositionGrid(u32 pos) { m_slider_pos_grid = pos; } + void setTableMode(u32 mode) { m_table_mode = mode; } + void setMainWindowWidth(u32 width) { m_window_size_W = width; } + void setMainWindowHeight(u32 height) { m_window_size_H = height; } + void setPkgViewer(const std::vector& pkgList) { m_pkg_viewer.resize(pkgList.size()); m_pkg_viewer = pkgList; } + void setElfViewer(const std::vector& elfList) { m_elf_viewer.resize(elfList.size()); m_elf_viewer = elfList; } + void setRecentFiles(const std::vector& recentFiles) { m_recent_files.resize(recentFiles.size()); m_recent_files = recentFiles; @@ -395,18 +425,23 @@ void setEmulatorLanguage(std::string language) { u32 getMainWindowGeometryX() { return main_window_geometry_x; } + u32 getMainWindowGeometryY() { return main_window_geometry_y; } + u32 getMainWindowGeometryW() { return main_window_geometry_w; } + u32 getMainWindowGeometryH() { return main_window_geometry_h; } + const std::vector& getGameInstallDirs() { return settings_install_dirs; } + std::filesystem::path getAddonInstallDir() { if (settings_addon_install_dir.empty()) { // Default for users without a config file or a config file from before this option existed @@ -414,36 +449,47 @@ std::filesystem::path getAddonInstallDir() { } return settings_addon_install_dir; } + u32 getMainWindowTheme() { return mw_themes; } + u32 getIconSize() { return m_icon_size; } + u32 getIconSizeGrid() { return m_icon_size_grid; } + u32 getSliderPosition() { return m_slider_pos; } + u32 getSliderPositionGrid() { return m_slider_pos_grid; } + u32 getTableMode() { return m_table_mode; } + u32 getMainWindowWidth() { return m_window_size_W; } + u32 getMainWindowHeight() { return m_window_size_H; } + std::vector getPkgViewer() { return m_pkg_viewer; } + std::vector getElfViewer() { return m_elf_viewer; } + std::vector getRecentFiles() { return m_recent_files; } @@ -455,6 +501,7 @@ std::string getEmulatorLanguage() { u32 GetLanguage() { return m_language; } + void load(const std::filesystem::path& path) { // If the configuration file does not exist, create it and return std::error_code error; @@ -513,6 +560,7 @@ void load(const std::filesystem::path& path) { isNullGpu = toml::find_or(gpu, "nullGpu", false); shouldCopyGPUBuffers = toml::find_or(gpu, "copyGPUBuffers", false); shouldDumpShaders = toml::find_or(gpu, "dumpShaders", false); + shouldPatchShaders = toml::find_or(gpu, "patchShaders", true); vblankDivider = toml::find_or(gpu, "vblankDivider", 1); } @@ -532,6 +580,7 @@ void load(const std::filesystem::path& path) { const toml::value& debug = data.at("Debug"); isDebugDump = toml::find_or(debug, "DebugDump", false); + isShaderDebug = toml::find_or(debug, "CollectShader", false); } if (data.contains("GUI")) { @@ -545,16 +594,10 @@ void load(const std::filesystem::path& path) { m_window_size_W = toml::find_or(gui, "mw_width", 0); m_window_size_H = toml::find_or(gui, "mw_height", 0); - // TODO Migration code, after a major release this should be removed. - auto old_game_install_dir = toml::find_fs_path_or(gui, "installDir", {}); - if (!old_game_install_dir.empty()) { - addGameInstallDir(std::filesystem::path{old_game_install_dir}); - } else { - const auto install_dir_array = - toml::find_or>(gui, "installDirs", {}); - for (const auto& dir : install_dir_array) { - addGameInstallDir(std::filesystem::path{dir}); - } + const auto install_dir_array = + toml::find_or>(gui, "installDirs", {}); + for (const auto& dir : install_dir_array) { + addGameInstallDir(std::filesystem::path{dir}); } settings_addon_install_dir = toml::find_fs_path_or(gui, "addonInstallDir", {}); @@ -575,6 +618,7 @@ void load(const std::filesystem::path& path) { m_language = toml::find_or(settings, "consoleLanguage", 1); } } + void save(const std::filesystem::path& path) { toml::value data; @@ -618,6 +662,7 @@ void save(const std::filesystem::path& path) { data["GPU"]["nullGpu"] = isNullGpu; data["GPU"]["copyGPUBuffers"] = shouldCopyGPUBuffers; data["GPU"]["dumpShaders"] = shouldDumpShaders; + data["GPU"]["patchShaders"] = shouldPatchShaders; data["GPU"]["vblankDivider"] = vblankDivider; data["Vulkan"]["gpuId"] = gpuId; data["Vulkan"]["validation"] = vkValidation; @@ -627,6 +672,7 @@ void save(const std::filesystem::path& path) { data["Vulkan"]["rdocMarkersEnable"] = vkMarkers; data["Vulkan"]["crashDiagnostic"] = vkCrashDiagnostic; data["Debug"]["DebugDump"] = isDebugDump; + data["Debug"]["CollectShader"] = isShaderDebug; data["GUI"]["theme"] = mw_themes; data["GUI"]["iconSize"] = m_icon_size; data["GUI"]["sliderPos"] = m_slider_pos; @@ -655,9 +701,6 @@ void save(const std::filesystem::path& path) { data["Settings"]["consoleLanguage"] = m_language; - // TODO Migration code, after a major release this should be removed. - data.at("GUI").as_table().erase("installDir"); - std::ofstream file(path, std::ios::binary); file << data; file.close(); @@ -685,6 +728,7 @@ void setDefaultValues() { useSpecialPad = false; specialPadClass = 1; isDebugDump = false; + isShaderDebug = false; isShowSplash = false; isAutoUpdate = false; isNullGpu = false; diff --git a/src/common/config.h b/src/common/config.h index 9c71c96a8..d98c94480 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -37,15 +37,18 @@ u32 getScreenHeight(); s32 getGpuId(); bool debugDump(); +bool collectShadersForDebug(); bool showSplash(); bool autoUpdate(); bool nullGpu(); bool copyGPUCmdBuffers(); bool dumpShaders(); +bool patchShaders(); bool isRdocEnabled(); u32 vblankDiv(); void setDebugDump(bool enable); +void setCollectShaderForDebug(bool enable); void setShowSplash(bool enable); void setAutoUpdate(bool enable); void setNullGpu(bool enable); diff --git a/src/common/debug.h b/src/common/debug.h index 596ad7b84..091c6191d 100644 --- a/src/common/debug.h +++ b/src/common/debug.h @@ -41,7 +41,7 @@ enum MarkersPalette : int { #define RENDERER_TRACE ZoneScopedC(RendererMarkerColor) #define HLE_TRACE ZoneScopedC(HleMarkerColor) -#define TRACE_HINT(str) ZoneText(str.c_str(), str.size()) +#define TRACE_HINT(str) ZoneText(str.data(), str.size()) #define TRACE_WARN(msg) \ [](const auto& msg) { TracyMessageC(msg.c_str(), msg.size(), tracy::Color::DarkOrange); }(msg); diff --git a/src/common/discord_rpc_handler.cpp b/src/common/discord_rpc_handler.cpp index 91b278a15..448cb4a7f 100644 --- a/src/common/discord_rpc_handler.cpp +++ b/src/common/discord_rpc_handler.cpp @@ -3,7 +3,7 @@ #include #include -#include "src/common/discord_rpc_handler.h" +#include "discord_rpc_handler.h" namespace DiscordRPCHandler { @@ -17,7 +17,7 @@ void RPC::init() { void RPC::setStatusIdling() { DiscordRichPresence rpc{}; - rpc.largeImageKey = "https://github.com/shadps4-emu/shadPS4/raw/main/.github/shadps4.png"; + rpc.largeImageKey = "https://cdn.jsdelivr.net/gh/shadps4-emu/shadPS4@main/.github/shadps4.png"; rpc.largeImageText = "shadPS4 is a PS4 emulator"; rpc.startTimestamp = startTimestamp; rpc.details = "Idle"; diff --git a/src/common/io_file.cpp b/src/common/io_file.cpp index dd3a40cae..067010a26 100644 --- a/src/common/io_file.cpp +++ b/src/common/io_file.cpp @@ -377,16 +377,18 @@ bool IOFile::Seek(s64 offset, SeekOrigin origin) const { return false; } - u64 size = GetSize(); - if (origin == SeekOrigin::CurrentPosition && Tell() + offset > size) { - LOG_ERROR(Common_Filesystem, "Seeking past the end of the file"); - return false; - } else if (origin == SeekOrigin::SetOrigin && (u64)offset > size) { - LOG_ERROR(Common_Filesystem, "Seeking past the end of the file"); - return false; - } else if (origin == SeekOrigin::End && offset > 0) { - LOG_ERROR(Common_Filesystem, "Seeking past the end of the file"); - return false; + if (False(file_access_mode & (FileAccessMode::Write | FileAccessMode::Append))) { + u64 size = GetSize(); + if (origin == SeekOrigin::CurrentPosition && Tell() + offset > size) { + LOG_ERROR(Common_Filesystem, "Seeking past the end of the file"); + return false; + } else if (origin == SeekOrigin::SetOrigin && (u64)offset > size) { + LOG_ERROR(Common_Filesystem, "Seeking past the end of the file"); + return false; + } else if (origin == SeekOrigin::End && offset > 0) { + LOG_ERROR(Common_Filesystem, "Seeking past the end of the file"); + return false; + } } errno = 0; diff --git a/src/common/io_file.h b/src/common/io_file.h index 8fed4981f..feb2110ac 100644 --- a/src/common/io_file.h +++ b/src/common/io_file.h @@ -10,6 +10,7 @@ #include "common/concepts.h" #include "common/types.h" +#include "enum.h" namespace Common::FS { @@ -42,6 +43,7 @@ enum class FileAccessMode { */ ReadAppend = Read | Append, }; +DECLARE_ENUM_FLAG_OPERATORS(FileAccessMode); enum class FileType { BinaryFile, diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 5ca594bf7..75c61a188 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -69,6 +69,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Common, Memory) \ CLS(Core) \ SUB(Core, Linker) \ + SUB(Core, Devices) \ CLS(Config) \ CLS(Debug) \ CLS(Kernel) \ @@ -105,7 +106,9 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, Rtc) \ SUB(Lib, DiscMap) \ SUB(Lib, Png) \ + SUB(Lib, Jpeg) \ SUB(Lib, PlayGo) \ + SUB(Lib, PlayGoDialog) \ SUB(Lib, Random) \ SUB(Lib, Usbd) \ SUB(Lib, Ajm) \ @@ -121,6 +124,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, Fiber) \ SUB(Lib, Vdec2) \ SUB(Lib, Videodec) \ + SUB(Lib, RazorCpu) \ CLS(Frontend) \ CLS(Render) \ SUB(Render, Vulkan) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index 2821729d4..a0e7d021f 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -35,6 +35,7 @@ enum class Class : u8 { Common_Memory, ///< Memory mapping and management functions Core, ///< LLE emulation core Core_Linker, ///< The module linker + Core_Devices, ///< Devices emulation Config, ///< Emulator configuration (including commandline) Debug, ///< Debugging tools Kernel, ///< The HLE implementation of the PS4 kernel. @@ -72,7 +73,9 @@ enum class Class : u8 { Lib_Rtc, ///< The LibSceRtc implementation. Lib_DiscMap, ///< The LibSceDiscMap implementation. Lib_Png, ///< The LibScePng implementation. + Lib_Jpeg, ///< The LibSceJpeg implementation. Lib_PlayGo, ///< The LibScePlayGo implementation. + Lib_PlayGoDialog, ///< The LibScePlayGoDialog implementation. Lib_Random, ///< The libSceRandom implementation. Lib_Usbd, ///< The LibSceUsbd implementation. Lib_Ajm, ///< The LibSceAjm implementation. @@ -88,6 +91,7 @@ enum class Class : u8 { Lib_Fiber, ///< The LibSceFiber implementation. Lib_Vdec2, ///< The LibSceVideodec2 implementation. Lib_Videodec, ///< The LibSceVideodec implementation. + Lib_RazorCpu, ///< The LibRazorCpu 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 d2930cf5e..6b79edb9f 100644 --- a/src/common/memory_patcher.cpp +++ b/src/common/memory_patcher.cpp @@ -28,7 +28,7 @@ std::string g_game_serial; std::string patchFile; std::vector pending_patches; -std::string toHex(unsigned long long value, size_t byteSize) { +std::string toHex(u64 value, size_t byteSize) { std::stringstream ss; ss << std::hex << std::setfill('0') << std::setw(byteSize * 2) << value; return ss.str(); @@ -38,16 +38,16 @@ std::string convertValueToHex(const std::string type, const std::string valueStr std::string result; if (type == "byte") { - unsigned int value = std::stoul(valueStr, nullptr, 16); + const u32 value = std::stoul(valueStr, nullptr, 16); result = toHex(value, 1); } else if (type == "bytes16") { - unsigned int value = std::stoul(valueStr, nullptr, 16); + const u32 value = std::stoul(valueStr, nullptr, 16); result = toHex(value, 2); } else if (type == "bytes32") { - unsigned long value = std::stoul(valueStr, nullptr, 16); + const u32 value = std::stoul(valueStr, nullptr, 16); result = toHex(value, 4); } else if (type == "bytes64") { - unsigned long long value = std::stoull(valueStr, nullptr, 16); + const u64 value = std::stoull(valueStr, nullptr, 16); result = toHex(value, 8); } else if (type == "float32") { union { diff --git a/src/common/ntapi.cpp b/src/common/ntapi.cpp index 0fe797e09..c76c4657e 100644 --- a/src/common/ntapi.cpp +++ b/src/common/ntapi.cpp @@ -5,8 +5,11 @@ #include "ntapi.h" -NtDelayExecution_t NtDelayExecution = nullptr; +NtClose_t NtClose = nullptr; NtSetInformationFile_t NtSetInformationFile = nullptr; +NtCreateThread_t NtCreateThread = nullptr; +NtTerminateThread_t NtTerminateThread = nullptr; +NtQueueApcThreadEx_t NtQueueApcThreadEx = nullptr; namespace Common::NtApi { @@ -14,9 +17,12 @@ void Initialize() { HMODULE nt_handle = GetModuleHandleA("ntdll.dll"); // http://stackoverflow.com/a/31411628/4725495 - NtDelayExecution = (NtDelayExecution_t)GetProcAddress(nt_handle, "NtDelayExecution"); + NtClose = (NtClose_t)GetProcAddress(nt_handle, "NtClose"); NtSetInformationFile = (NtSetInformationFile_t)GetProcAddress(nt_handle, "NtSetInformationFile"); + NtCreateThread = (NtCreateThread_t)GetProcAddress(nt_handle, "NtCreateThread"); + NtTerminateThread = (NtTerminateThread_t)GetProcAddress(nt_handle, "NtTerminateThread"); + NtQueueApcThreadEx = (NtQueueApcThreadEx_t)GetProcAddress(nt_handle, "NtQueueApcThreadEx"); } } // namespace Common::NtApi diff --git a/src/common/ntapi.h b/src/common/ntapi.h index 17d353403..daab8440d 100644 --- a/src/common/ntapi.h +++ b/src/common/ntapi.h @@ -108,14 +108,444 @@ typedef struct _FILE_DISPOSITION_INFORMATION { BOOLEAN DeleteFile; } FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION; -typedef u32(__stdcall* NtDelayExecution_t)(BOOL Alertable, PLARGE_INTEGER DelayInterval); +typedef struct _UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; + PWCH Buffer; +} UNICODE_STRING, *PUNICODE_STRING; -typedef u32(__stdcall* NtSetInformationFile_t)(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, +typedef const UNICODE_STRING* PCUNICODE_STRING; + +typedef struct _OBJECT_ATTRIBUTES { + ULONG Length; + HANDLE RootDirectory; + PCUNICODE_STRING ObjectName; + ULONG Attributes; + PVOID SecurityDescriptor; // PSECURITY_DESCRIPTOR; + PVOID SecurityQualityOfService; // PSECURITY_QUALITY_OF_SERVICE +} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES; + +typedef const OBJECT_ATTRIBUTES* PCOBJECT_ATTRIBUTES; + +typedef struct _CLIENT_ID { + HANDLE UniqueProcess; + HANDLE UniqueThread; +} CLIENT_ID, *PCLIENT_ID; + +typedef struct _INITIAL_TEB { + struct { + PVOID OldStackBase; + PVOID OldStackLimit; + } OldInitialTeb; + PVOID StackBase; + PVOID StackLimit; + PVOID StackAllocationBase; +} INITIAL_TEB, *PINITIAL_TEB; + +typedef struct _PEB_LDR_DATA { + ULONG Length; + BOOLEAN Initialized; + PVOID SsHandle; + LIST_ENTRY InLoadOrderModuleList; + LIST_ENTRY InMemoryOrderModuleList; + LIST_ENTRY InInitializationOrderModuleList; + PVOID EntryInProgress; + BOOLEAN ShutdownInProgress; + HANDLE ShutdownThreadId; +} PEB_LDR_DATA, *PPEB_LDR_DATA; + +typedef struct _CURDIR { + UNICODE_STRING DosPath; + PVOID Handle; +} CURDIR, *PCURDIR; + +typedef struct RTL_DRIVE_LETTER_CURDIR { + USHORT Flags; + USHORT Length; + ULONG TimeStamp; + UNICODE_STRING DosPath; +} RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR; + +typedef struct _RTL_USER_PROCESS_PARAMETERS { + ULONG AllocationSize; + ULONG Size; + ULONG Flags; + ULONG DebugFlags; + HANDLE ConsoleHandle; + ULONG ConsoleFlags; + HANDLE hStdInput; + HANDLE hStdOutput; + HANDLE hStdError; + CURDIR CurrentDirectory; + UNICODE_STRING DllPath; + UNICODE_STRING ImagePathName; + UNICODE_STRING CommandLine; + PWSTR Environment; + ULONG dwX; + ULONG dwY; + ULONG dwXSize; + ULONG dwYSize; + ULONG dwXCountChars; + ULONG dwYCountChars; + ULONG dwFillAttribute; + ULONG dwFlags; + ULONG wShowWindow; + UNICODE_STRING WindowTitle; + UNICODE_STRING Desktop; + UNICODE_STRING ShellInfo; + UNICODE_STRING RuntimeInfo; + RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[0x20]; + ULONG_PTR EnvironmentSize; + ULONG_PTR EnvironmentVersion; + PVOID PackageDependencyData; + ULONG ProcessGroupId; + ULONG LoaderThreads; +} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS; + +typedef struct tagRTL_BITMAP { + ULONG SizeOfBitMap; + PULONG Buffer; +} RTL_BITMAP, *PRTL_BITMAP; + +typedef struct { + UINT next; + UINT id; + ULONGLONG addr; + ULONGLONG size; + UINT args[4]; +} CROSS_PROCESS_WORK_ENTRY; + +typedef union { + struct { + UINT first; + UINT counter; + }; + volatile LONGLONG hdr; +} CROSS_PROCESS_WORK_HDR; + +typedef struct { + CROSS_PROCESS_WORK_HDR free_list; + CROSS_PROCESS_WORK_HDR work_list; + ULONGLONG unknown[4]; + CROSS_PROCESS_WORK_ENTRY entries[1]; +} CROSS_PROCESS_WORK_LIST; + +typedef struct _CHPEV2_PROCESS_INFO { + ULONG Wow64ExecuteFlags; /* 000 */ + USHORT NativeMachineType; /* 004 */ + USHORT EmulatedMachineType; /* 006 */ + HANDLE SectionHandle; /* 008 */ + CROSS_PROCESS_WORK_LIST* CrossProcessWorkList; /* 010 */ + void* unknown; /* 018 */ +} CHPEV2_PROCESS_INFO, *PCHPEV2_PROCESS_INFO; + +typedef u64(__stdcall* KERNEL_CALLBACK_PROC)(void*, ULONG); + +typedef struct _PEB { /* win32/win64 */ + BOOLEAN InheritedAddressSpace; /* 000/000 */ + BOOLEAN ReadImageFileExecOptions; /* 001/001 */ + BOOLEAN BeingDebugged; /* 002/002 */ + UCHAR ImageUsedLargePages : 1; /* 003/003 */ + UCHAR IsProtectedProcess : 1; + UCHAR IsImageDynamicallyRelocated : 1; + UCHAR SkipPatchingUser32Forwarders : 1; + UCHAR IsPackagedProcess : 1; + UCHAR IsAppContainer : 1; + UCHAR IsProtectedProcessLight : 1; + UCHAR IsLongPathAwareProcess : 1; + HANDLE Mutant; /* 004/008 */ + HMODULE ImageBaseAddress; /* 008/010 */ + PPEB_LDR_DATA LdrData; /* 00c/018 */ + RTL_USER_PROCESS_PARAMETERS* ProcessParameters; /* 010/020 */ + PVOID SubSystemData; /* 014/028 */ + HANDLE ProcessHeap; /* 018/030 */ + PRTL_CRITICAL_SECTION FastPebLock; /* 01c/038 */ + PVOID AtlThunkSListPtr; /* 020/040 */ + PVOID IFEOKey; /* 024/048 */ + ULONG ProcessInJob : 1; /* 028/050 */ + ULONG ProcessInitializing : 1; + ULONG ProcessUsingVEH : 1; + ULONG ProcessUsingVCH : 1; + ULONG ProcessUsingFTH : 1; + ULONG ProcessPreviouslyThrottled : 1; + ULONG ProcessCurrentlyThrottled : 1; + ULONG ProcessImagesHotPatched : 1; + ULONG ReservedBits0 : 24; + KERNEL_CALLBACK_PROC* KernelCallbackTable; /* 02c/058 */ + ULONG Reserved; /* 030/060 */ + ULONG AtlThunkSListPtr32; /* 034/064 */ + PVOID ApiSetMap; /* 038/068 */ + ULONG TlsExpansionCounter; /* 03c/070 */ + PRTL_BITMAP TlsBitmap; /* 040/078 */ + ULONG TlsBitmapBits[2]; /* 044/080 */ + PVOID ReadOnlySharedMemoryBase; /* 04c/088 */ + PVOID SharedData; /* 050/090 */ + PVOID* ReadOnlyStaticServerData; /* 054/098 */ + PVOID AnsiCodePageData; /* 058/0a0 */ + PVOID OemCodePageData; /* 05c/0a8 */ + PVOID UnicodeCaseTableData; /* 060/0b0 */ + ULONG NumberOfProcessors; /* 064/0b8 */ + ULONG NtGlobalFlag; /* 068/0bc */ + LARGE_INTEGER CriticalSectionTimeout; /* 070/0c0 */ + SIZE_T HeapSegmentReserve; /* 078/0c8 */ + SIZE_T HeapSegmentCommit; /* 07c/0d0 */ + SIZE_T HeapDeCommitTotalFreeThreshold; /* 080/0d8 */ + SIZE_T HeapDeCommitFreeBlockThreshold; /* 084/0e0 */ + ULONG NumberOfHeaps; /* 088/0e8 */ + ULONG MaximumNumberOfHeaps; /* 08c/0ec */ + PVOID* ProcessHeaps; /* 090/0f0 */ + PVOID GdiSharedHandleTable; /* 094/0f8 */ + PVOID ProcessStarterHelper; /* 098/100 */ + PVOID GdiDCAttributeList; /* 09c/108 */ + PVOID LoaderLock; /* 0a0/110 */ + ULONG OSMajorVersion; /* 0a4/118 */ + ULONG OSMinorVersion; /* 0a8/11c */ + ULONG OSBuildNumber; /* 0ac/120 */ + ULONG OSPlatformId; /* 0b0/124 */ + ULONG ImageSubSystem; /* 0b4/128 */ + ULONG ImageSubSystemMajorVersion; /* 0b8/12c */ + ULONG ImageSubSystemMinorVersion; /* 0bc/130 */ + KAFFINITY ActiveProcessAffinityMask; /* 0c0/138 */ +#ifdef _WIN64 + ULONG GdiHandleBuffer[60]; /* /140 */ +#else + ULONG GdiHandleBuffer[34]; /* 0c4/ */ +#endif + PVOID PostProcessInitRoutine; /* 14c/230 */ + PRTL_BITMAP TlsExpansionBitmap; /* 150/238 */ + ULONG TlsExpansionBitmapBits[32]; /* 154/240 */ + ULONG SessionId; /* 1d4/2c0 */ + ULARGE_INTEGER AppCompatFlags; /* 1d8/2c8 */ + ULARGE_INTEGER AppCompatFlagsUser; /* 1e0/2d0 */ + PVOID ShimData; /* 1e8/2d8 */ + PVOID AppCompatInfo; /* 1ec/2e0 */ + UNICODE_STRING CSDVersion; /* 1f0/2e8 */ + PVOID ActivationContextData; /* 1f8/2f8 */ + PVOID ProcessAssemblyStorageMap; /* 1fc/300 */ + PVOID SystemDefaultActivationData; /* 200/308 */ + PVOID SystemAssemblyStorageMap; /* 204/310 */ + SIZE_T MinimumStackCommit; /* 208/318 */ + PVOID* FlsCallback; /* 20c/320 */ + LIST_ENTRY FlsListHead; /* 210/328 */ + union { + PRTL_BITMAP FlsBitmap; /* 218/338 */ +#ifdef _WIN64 + CHPEV2_PROCESS_INFO* ChpeV2ProcessInfo; /* /338 */ +#endif + }; + ULONG FlsBitmapBits[4]; /* 21c/340 */ + ULONG FlsHighIndex; /* 22c/350 */ + PVOID WerRegistrationData; /* 230/358 */ + PVOID WerShipAssertPtr; /* 234/360 */ + PVOID EcCodeBitMap; /* 238/368 */ + PVOID pImageHeaderHash; /* 23c/370 */ + ULONG HeapTracingEnabled : 1; /* 240/378 */ + ULONG CritSecTracingEnabled : 1; + ULONG LibLoaderTracingEnabled : 1; + ULONG SpareTracingBits : 29; + ULONGLONG CsrServerReadOnlySharedMemoryBase; /* 248/380 */ + ULONG TppWorkerpListLock; /* 250/388 */ + LIST_ENTRY TppWorkerpList; /* 254/390 */ + PVOID WaitOnAddressHashTable[0x80]; /* 25c/3a0 */ + PVOID TelemetryCoverageHeader; /* 45c/7a0 */ + ULONG CloudFileFlags; /* 460/7a8 */ + ULONG CloudFileDiagFlags; /* 464/7ac */ + CHAR PlaceholderCompatibilityMode; /* 468/7b0 */ + CHAR PlaceholderCompatibilityModeReserved[7]; /* 469/7b1 */ + PVOID LeapSecondData; /* 470/7b8 */ + ULONG LeapSecondFlags; /* 474/7c0 */ + ULONG NtGlobalFlag2; /* 478/7c4 */ +} PEB, *PPEB; + +typedef struct _RTL_ACTIVATION_CONTEXT_STACK_FRAME { + struct _RTL_ACTIVATION_CONTEXT_STACK_FRAME* Previous; + struct _ACTIVATION_CONTEXT* ActivationContext; + ULONG Flags; +} RTL_ACTIVATION_CONTEXT_STACK_FRAME, *PRTL_ACTIVATION_CONTEXT_STACK_FRAME; + +typedef struct _ACTIVATION_CONTEXT_STACK { + RTL_ACTIVATION_CONTEXT_STACK_FRAME* ActiveFrame; + LIST_ENTRY FrameListCache; + ULONG Flags; + ULONG NextCookieSequenceNumber; + ULONG_PTR StackId; +} ACTIVATION_CONTEXT_STACK, *PACTIVATION_CONTEXT_STACK; + +typedef struct _GDI_TEB_BATCH { + ULONG Offset; + HANDLE HDC; + ULONG Buffer[0x136]; +} GDI_TEB_BATCH; + +typedef struct _TEB_ACTIVE_FRAME_CONTEXT { + ULONG Flags; + const char* FrameName; +} TEB_ACTIVE_FRAME_CONTEXT, *PTEB_ACTIVE_FRAME_CONTEXT; + +typedef struct _TEB_ACTIVE_FRAME { + ULONG Flags; + struct _TEB_ACTIVE_FRAME* Previous; + TEB_ACTIVE_FRAME_CONTEXT* Context; +} TEB_ACTIVE_FRAME, *PTEB_ACTIVE_FRAME; + +typedef struct _TEB { /* win32/win64 */ + NT_TIB Tib; /* 000/0000 */ + PVOID EnvironmentPointer; /* 01c/0038 */ + CLIENT_ID ClientId; /* 020/0040 */ + PVOID ActiveRpcHandle; /* 028/0050 */ + PVOID ThreadLocalStoragePointer; /* 02c/0058 */ + PPEB Peb; /* 030/0060 */ + ULONG LastErrorValue; /* 034/0068 */ + ULONG CountOfOwnedCriticalSections; /* 038/006c */ + PVOID CsrClientThread; /* 03c/0070 */ + PVOID Win32ThreadInfo; /* 040/0078 */ + ULONG User32Reserved[26]; /* 044/0080 */ + ULONG UserReserved[5]; /* 0ac/00e8 */ + PVOID WOW32Reserved; /* 0c0/0100 */ + ULONG CurrentLocale; /* 0c4/0108 */ + ULONG FpSoftwareStatusRegister; /* 0c8/010c */ + PVOID ReservedForDebuggerInstrumentation[16]; /* 0cc/0110 */ +#ifdef _WIN64 + PVOID SystemReserved1[30]; /* /0190 */ +#else + PVOID SystemReserved1[26]; /* 10c/ */ +#endif + char PlaceholderCompatibilityMode; /* 174/0280 */ + BOOLEAN PlaceholderHydrationAlwaysExplicit; /* 175/0281 */ + char PlaceholderReserved[10]; /* 176/0282 */ + DWORD ProxiedProcessId; /* 180/028c */ + ACTIVATION_CONTEXT_STACK ActivationContextStack; /* 184/0290 */ + UCHAR WorkingOnBehalfOfTicket[8]; /* 19c/02b8 */ + LONG ExceptionCode; /* 1a4/02c0 */ + ACTIVATION_CONTEXT_STACK* ActivationContextStackPointer; /* 1a8/02c8 */ + ULONG_PTR InstrumentationCallbackSp; /* 1ac/02d0 */ + ULONG_PTR InstrumentationCallbackPreviousPc; /* 1b0/02d8 */ + ULONG_PTR InstrumentationCallbackPreviousSp; /* 1b4/02e0 */ +#ifdef _WIN64 + ULONG TxFsContext; /* /02e8 */ + BOOLEAN InstrumentationCallbackDisabled; /* /02ec */ + BOOLEAN UnalignedLoadStoreExceptions; /* /02ed */ +#else + BOOLEAN InstrumentationCallbackDisabled; /* 1b8/ */ + BYTE SpareBytes1[23]; /* 1b9/ */ + ULONG TxFsContext; /* 1d0/ */ +#endif + GDI_TEB_BATCH GdiTebBatch; /* 1d4/02f0 */ + CLIENT_ID RealClientId; /* 6b4/07d8 */ + HANDLE GdiCachedProcessHandle; /* 6bc/07e8 */ + ULONG GdiClientPID; /* 6c0/07f0 */ + ULONG GdiClientTID; /* 6c4/07f4 */ + PVOID GdiThreadLocaleInfo; /* 6c8/07f8 */ + ULONG_PTR Win32ClientInfo[62]; /* 6cc/0800 */ + PVOID glDispatchTable[233]; /* 7c4/09f0 */ + PVOID glReserved1[29]; /* b68/1138 */ + PVOID glReserved2; /* bdc/1220 */ + PVOID glSectionInfo; /* be0/1228 */ + PVOID glSection; /* be4/1230 */ + PVOID glTable; /* be8/1238 */ + PVOID glCurrentRC; /* bec/1240 */ + PVOID glContext; /* bf0/1248 */ + ULONG LastStatusValue; /* bf4/1250 */ + UNICODE_STRING StaticUnicodeString; /* bf8/1258 */ + WCHAR StaticUnicodeBuffer[261]; /* c00/1268 */ + PVOID DeallocationStack; /* e0c/1478 */ + PVOID TlsSlots[64]; /* e10/1480 */ + LIST_ENTRY TlsLinks; /* f10/1680 */ + PVOID Vdm; /* f18/1690 */ + PVOID ReservedForNtRpc; /* f1c/1698 */ + PVOID DbgSsReserved[2]; /* f20/16a0 */ + ULONG HardErrorMode; /* f28/16b0 */ +#ifdef _WIN64 + PVOID Instrumentation[11]; /* /16b8 */ +#else + PVOID Instrumentation[9]; /* f2c/ */ +#endif + GUID ActivityId; /* f50/1710 */ + PVOID SubProcessTag; /* f60/1720 */ + PVOID PerflibData; /* f64/1728 */ + PVOID EtwTraceData; /* f68/1730 */ + PVOID WinSockData; /* f6c/1738 */ + ULONG GdiBatchCount; /* f70/1740 */ + ULONG IdealProcessorValue; /* f74/1744 */ + ULONG GuaranteedStackBytes; /* f78/1748 */ + PVOID ReservedForPerf; /* f7c/1750 */ + PVOID ReservedForOle; /* f80/1758 */ + ULONG WaitingOnLoaderLock; /* f84/1760 */ + PVOID SavedPriorityState; /* f88/1768 */ + ULONG_PTR ReservedForCodeCoverage; /* f8c/1770 */ + PVOID ThreadPoolData; /* f90/1778 */ + PVOID* TlsExpansionSlots; /* f94/1780 */ +#ifdef _WIN64 + union { + PVOID DeallocationBStore; /* /1788 */ + PVOID* ChpeV2CpuAreaInfo; /* /1788 */ + } DUMMYUNIONNAME; + PVOID BStoreLimit; /* /1790 */ +#endif + ULONG MuiGeneration; /* f98/1798 */ + ULONG IsImpersonating; /* f9c/179c */ + PVOID NlsCache; /* fa0/17a0 */ + PVOID ShimData; /* fa4/17a8 */ + ULONG HeapVirtualAffinity; /* fa8/17b0 */ + PVOID CurrentTransactionHandle; /* fac/17b8 */ + TEB_ACTIVE_FRAME* ActiveFrame; /* fb0/17c0 */ + PVOID* FlsSlots; /* fb4/17c8 */ + PVOID PreferredLanguages; /* fb8/17d0 */ + PVOID UserPrefLanguages; /* fbc/17d8 */ + PVOID MergedPrefLanguages; /* fc0/17e0 */ + ULONG MuiImpersonation; /* fc4/17e8 */ + USHORT CrossTebFlags; /* fc8/17ec */ + USHORT SameTebFlags; /* fca/17ee */ + PVOID TxnScopeEnterCallback; /* fcc/17f0 */ + PVOID TxnScopeExitCallback; /* fd0/17f8 */ + PVOID TxnScopeContext; /* fd4/1800 */ + ULONG LockCount; /* fd8/1808 */ + LONG WowTebOffset; /* fdc/180c */ + PVOID ResourceRetValue; /* fe0/1810 */ + PVOID ReservedForWdf; /* fe4/1818 */ + ULONGLONG ReservedForCrt; /* fe8/1820 */ + GUID EffectiveContainerId; /* ff0/1828 */ +} TEB, *PTEB; +static_assert(offsetof(TEB, DeallocationStack) == + 0x1478); /* The only member we care about at the moment */ + +typedef enum _QUEUE_USER_APC_FLAGS { + QueueUserApcFlagsNone, + QueueUserApcFlagsSpecialUserApc, + QueueUserApcFlagsMaxValue +} QUEUE_USER_APC_FLAGS; + +typedef union _USER_APC_OPTION { + ULONG_PTR UserApcFlags; + HANDLE MemoryReserveHandle; +} USER_APC_OPTION, *PUSER_APC_OPTION; + +using PPS_APC_ROUTINE = void (*)(PVOID ApcArgument1, PVOID ApcArgument2, PVOID ApcArgument3, + PCONTEXT Context); + +typedef u64(__stdcall* NtClose_t)(HANDLE Handle); + +typedef u64(__stdcall* NtSetInformationFile_t)(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, FILE_INFORMATION_CLASS FileInformationClass); -extern NtDelayExecution_t NtDelayExecution; +typedef u64(__stdcall* NtCreateThread_t)(PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, + PCOBJECT_ATTRIBUTES ObjectAttributes, HANDLE ProcessHandle, + PCLIENT_ID ClientId, PCONTEXT ThreadContext, + PINITIAL_TEB InitialTeb, BOOLEAN CreateSuspended); + +typedef u64(__stdcall* NtTerminateThread_t)(HANDLE ThreadHandle, u64 ExitStatus); + +typedef u64(__stdcall* NtQueueApcThreadEx_t)(HANDLE ThreadHandle, + USER_APC_OPTION UserApcReserveHandle, + PPS_APC_ROUTINE ApcRoutine, PVOID ApcArgument1, + PVOID ApcArgument2, PVOID ApcArgument3); + +extern NtClose_t NtClose; extern NtSetInformationFile_t NtSetInformationFile; +extern NtCreateThread_t NtCreateThread; +extern NtTerminateThread_t NtTerminateThread; +extern NtQueueApcThreadEx_t NtQueueApcThreadEx; namespace Common::NtApi { void Initialize(); diff --git a/src/common/slab_heap.h b/src/common/slab_heap.h new file mode 100644 index 000000000..7648ebea3 --- /dev/null +++ b/src/common/slab_heap.h @@ -0,0 +1,163 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "common/assert.h" +#include "common/spin_lock.h" + +namespace Common { + +class SlabHeapImpl { +public: + struct Node { + Node* next{}; + }; + +public: + constexpr SlabHeapImpl() = default; + + void Initialize() { + ASSERT(m_head == nullptr); + } + + Node* GetHead() const { + return m_head; + } + + void* Allocate() { + m_lock.lock(); + + Node* ret = m_head; + if (ret != nullptr) { + m_head = ret->next; + } + + m_lock.unlock(); + return ret; + } + + void Free(void* obj) { + m_lock.lock(); + + Node* node = static_cast(obj); + node->next = m_head; + m_head = node; + + m_lock.unlock(); + } + +private: + std::atomic m_head{}; + Common::SpinLock m_lock; +}; + +class SlabHeapBase : protected SlabHeapImpl { +private: + size_t m_obj_size{}; + uintptr_t m_peak{}; + uintptr_t m_start{}; + uintptr_t m_end{}; + +public: + constexpr SlabHeapBase() = default; + + bool Contains(uintptr_t address) const { + return m_start <= address && address < m_end; + } + + void Initialize(size_t obj_size, void* memory, size_t memory_size) { + // Ensure we don't initialize a slab using null memory. + ASSERT(memory != nullptr); + + // Set our object size. + m_obj_size = obj_size; + + // Initialize the base allocator. + SlabHeapImpl::Initialize(); + + // Set our tracking variables. + const size_t num_obj = (memory_size / obj_size); + m_start = reinterpret_cast(memory); + m_end = m_start + num_obj * obj_size; + m_peak = m_start; + + // Free the objects. + u8* cur = reinterpret_cast(m_end); + + for (size_t i = 0; i < num_obj; i++) { + cur -= obj_size; + SlabHeapImpl::Free(cur); + } + } + + size_t GetSlabHeapSize() const { + return (m_end - m_start) / this->GetObjectSize(); + } + + size_t GetObjectSize() const { + return m_obj_size; + } + + void* Allocate() { + void* obj = SlabHeapImpl::Allocate(); + return obj; + } + + void Free(void* obj) { + // Don't allow freeing an object that wasn't allocated from this heap. + const bool contained = this->Contains(reinterpret_cast(obj)); + ASSERT(contained); + SlabHeapImpl::Free(obj); + } + + size_t GetObjectIndex(const void* obj) const { + return (reinterpret_cast(obj) - m_start) / this->GetObjectSize(); + } + + size_t GetPeakIndex() const { + return this->GetObjectIndex(reinterpret_cast(m_peak)); + } + + uintptr_t GetSlabHeapAddress() const { + return m_start; + } + + size_t GetNumRemaining() const { + // Only calculate the number of remaining objects under debug configuration. + return 0; + } +}; + +template +class SlabHeap final : public SlabHeapBase { +private: + using BaseHeap = SlabHeapBase; + +public: + constexpr SlabHeap() = default; + + void Initialize(void* memory, size_t memory_size) { + BaseHeap::Initialize(sizeof(T), memory, memory_size); + } + + T* Allocate() { + T* obj = static_cast(BaseHeap::Allocate()); + + if (obj != nullptr) [[likely]] { + std::construct_at(obj); + } + return obj; + } + + void Free(T* obj) { + BaseHeap::Free(obj); + } + + size_t GetObjectIndex(const T* obj) const { + return BaseHeap::GetObjectIndex(obj); + } +}; + +} // namespace Common diff --git a/src/common/slot_vector.h b/src/common/slot_vector.h index 36e647971..d4ac51361 100644 --- a/src/common/slot_vector.h +++ b/src/common/slot_vector.h @@ -3,10 +3,7 @@ #pragma once -#include -#include #include -#include #include #include #include "common/assert.h" diff --git a/src/common/spin_lock.cpp b/src/common/spin_lock.cpp new file mode 100755 index 000000000..9d4cfe36b --- /dev/null +++ b/src/common/spin_lock.cpp @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/spin_lock.h" + +#if _MSC_VER +#include +#if _M_AMD64 +#define __x86_64__ 1 +#endif +#if _M_ARM64 +#define __aarch64__ 1 +#endif +#else +#if __x86_64__ +#include +#endif +#endif + +namespace { + +void ThreadPause() { +#if __x86_64__ + _mm_pause(); +#elif __aarch64__ && _MSC_VER + __yield(); +#elif __aarch64__ + asm("yield"); +#endif +} + +} // Anonymous namespace + +namespace Common { + +void SpinLock::lock() { + while (lck.test_and_set(std::memory_order_acquire)) { + ThreadPause(); + } +} + +void SpinLock::unlock() { + lck.clear(std::memory_order_release); +} + +bool SpinLock::try_lock() { + if (lck.test_and_set(std::memory_order_acquire)) { + return false; + } + return true; +} + +} // namespace Common diff --git a/src/common/spin_lock.h b/src/common/spin_lock.h new file mode 100755 index 000000000..3229a8c6a --- /dev/null +++ b/src/common/spin_lock.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +namespace Common { + +/** + * SpinLock class + * a lock similar to mutex that forces a thread to spin wait instead calling the + * supervisor. Should be used on short sequences of code. + */ +class SpinLock { +public: + SpinLock() = default; + + SpinLock(const SpinLock&) = delete; + SpinLock& operator=(const SpinLock&) = delete; + + SpinLock(SpinLock&&) = delete; + SpinLock& operator=(SpinLock&&) = delete; + + void lock(); + void unlock(); + [[nodiscard]] bool try_lock(); + +private: + std::atomic_flag lck = ATOMIC_FLAG_INIT; +}; + +} // namespace Common diff --git a/src/common/stb.cpp b/src/common/stb.cpp new file mode 100644 index 000000000..0cd916185 --- /dev/null +++ b/src/common/stb.cpp @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#define STB_IMAGE_IMPLEMENTATION +#define STBI_ONLY_PNG +#define STBI_NO_STDIO +#include "common/stb.h" diff --git a/src/common/stb.h b/src/common/stb.h new file mode 100644 index 000000000..6f4d34483 --- /dev/null +++ b/src/common/stb.h @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include diff --git a/src/common/support/avdec.h b/src/common/support/avdec.h new file mode 100644 index 000000000..fa3483dc4 --- /dev/null +++ b/src/common/support/avdec.h @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +// support header file for libav + +// The av_err2str macro in libavutil/error.h does not play nice with C++ +#ifdef av_err2str +#undef av_err2str +#include +av_always_inline std::string av_err2string(int errnum) { + char errbuf[AV_ERROR_MAX_STRING_SIZE]; + return av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, errnum); +} +#define av_err2str(err) av_err2string(err).c_str() +#endif // av_err2str diff --git a/src/common/thread.cpp b/src/common/thread.cpp index 46df68c38..c87aea6ef 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp @@ -147,6 +147,10 @@ void SetCurrentThreadName(const char* name) { SetThreadDescription(GetCurrentThread(), UTF8ToUTF16W(name).data()); } +void SetThreadName(void* thread, const char* name) { + SetThreadDescription(thread, UTF8ToUTF16W(name).data()); +} + #else // !MSVC_VER, so must be POSIX threads // MinGW with the POSIX threading model does not support pthread_setname_np @@ -170,11 +174,19 @@ void SetCurrentThreadName(const char* name) { pthread_setname_np(pthread_self(), name); #endif } + +void SetThreadName(void* thread, const char* name) { + // TODO +} #endif #if defined(_WIN32) void SetCurrentThreadName(const char*) { - // Do Nothing on MingW + // Do Nothing on MinGW +} + +void SetThreadName(void* thread, const char* name) { + // Do Nothing on MinGW } #endif diff --git a/src/common/thread.h b/src/common/thread.h index fd962f8e5..175ba9445 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -23,6 +23,8 @@ void SetCurrentThreadPriority(ThreadPriority new_priority); void SetCurrentThreadName(const char* name); +void SetThreadName(void* thread, const char* name); + class AccurateTimer { std::chrono::nanoseconds target_interval{}; std::chrono::nanoseconds total_wait{}; diff --git a/src/common/va_ctx.h b/src/common/va_ctx.h new file mode 100644 index 000000000..e0b8c0bab --- /dev/null +++ b/src/common/va_ctx.h @@ -0,0 +1,111 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + +#include +#include "common/types.h" + +#define VA_ARGS \ + uint64_t rdi, uint64_t rsi, uint64_t rdx, uint64_t rcx, uint64_t r8, uint64_t r9, \ + uint64_t overflow_arg_area, __m128 xmm0, __m128 xmm1, __m128 xmm2, __m128 xmm3, \ + __m128 xmm4, __m128 xmm5, __m128 xmm6, __m128 xmm7, ... + +#define VA_CTX(ctx) \ + alignas(16)::Common::VaCtx ctx{}; \ + (ctx).reg_save_area.gp[0] = rdi; \ + (ctx).reg_save_area.gp[1] = rsi; \ + (ctx).reg_save_area.gp[2] = rdx; \ + (ctx).reg_save_area.gp[3] = rcx; \ + (ctx).reg_save_area.gp[4] = r8; \ + (ctx).reg_save_area.gp[5] = r9; \ + (ctx).reg_save_area.fp[0] = xmm0; \ + (ctx).reg_save_area.fp[1] = xmm1; \ + (ctx).reg_save_area.fp[2] = xmm2; \ + (ctx).reg_save_area.fp[3] = xmm3; \ + (ctx).reg_save_area.fp[4] = xmm4; \ + (ctx).reg_save_area.fp[5] = xmm5; \ + (ctx).reg_save_area.fp[6] = xmm6; \ + (ctx).reg_save_area.fp[7] = xmm7; \ + (ctx).va_list.reg_save_area = &(ctx).reg_save_area; \ + (ctx).va_list.gp_offset = offsetof(::Common::VaRegSave, gp); \ + (ctx).va_list.fp_offset = offsetof(::Common::VaRegSave, fp); \ + (ctx).va_list.overflow_arg_area = &overflow_arg_area; + +namespace Common { + +// https://stackoverflow.com/questions/4958384/what-is-the-format-of-the-x86-64-va-list-structure + +struct VaList { + u32 gp_offset; + u32 fp_offset; + void* overflow_arg_area; + void* reg_save_area; +}; + +struct VaRegSave { + u64 gp[6]; + __m128 fp[8]; +}; + +struct VaCtx { + VaRegSave reg_save_area; + VaList va_list; +}; + +template +T vaArgRegSaveAreaGp(VaList* l) { + auto* addr = reinterpret_cast(static_cast(l->reg_save_area) + l->gp_offset); + l->gp_offset += Size; + return *addr; +} +template +T vaArgOverflowArgArea(VaList* l) { + auto ptr = ((reinterpret_cast(l->overflow_arg_area) + (Align - 1)) & ~(Align - 1)); + auto* addr = reinterpret_cast(ptr); + l->overflow_arg_area = reinterpret_cast(ptr + Size); + return *addr; +} + +template +T vaArgRegSaveAreaFp(VaList* l) { + auto* addr = reinterpret_cast(static_cast(l->reg_save_area) + l->fp_offset); + l->fp_offset += Size; + return *addr; +} + +inline int vaArgInteger(VaList* l) { + if (l->gp_offset <= 40) { + return vaArgRegSaveAreaGp(l); + } + return vaArgOverflowArgArea(l); +} + +inline long long vaArgLongLong(VaList* l) { + if (l->gp_offset <= 40) { + return vaArgRegSaveAreaGp(l); + } + return vaArgOverflowArgArea(l); +} +inline long vaArgLong(VaList* l) { + if (l->gp_offset <= 40) { + return vaArgRegSaveAreaGp(l); + } + return vaArgOverflowArgArea(l); +} + +inline double vaArgDouble(VaList* l) { + if (l->fp_offset <= 160) { + return vaArgRegSaveAreaFp(l); + } + return vaArgOverflowArgArea(l); +} + +template +T* vaArgPtr(VaList* l) { + if (l->gp_offset <= 40) { + return vaArgRegSaveAreaGp(l); + } + return vaArgOverflowArgArea(l); +} + +} // namespace Common diff --git a/src/core/address_space.cpp b/src/core/address_space.cpp index 8ba99e32d..24f5e9f87 100644 --- a/src/core/address_space.cpp +++ b/src/core/address_space.cpp @@ -1,13 +1,14 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include "common/alignment.h" #include "common/arch.h" #include "common/assert.h" #include "common/error.h" #include "core/address_space.h" -#include "core/libraries/kernel/memory_management.h" +#include "core/libraries/kernel/memory.h" #include "core/memory.h" #include "libraries/error_codes.h" @@ -40,6 +41,12 @@ static constexpr size_t BackingSize = SCE_KERNEL_MAIN_DMEM_SIZE_PRO; } } +struct MemoryRegion { + VAddr base; + size_t size; + bool is_mapped; +}; + struct AddressSpace::Impl { Impl() : process{GetCurrentProcess()} { // Allocate virtual address placeholder for our address space. @@ -75,6 +82,7 @@ struct AddressSpace::Impl { Common::GetLastErrorMsg()); // Take the reduction off of the system managed area, and leave the others unchanged. + reduction = size_t(virtual_base - SYSTEM_MANAGED_MIN); system_managed_base = virtual_base; system_managed_size = SystemManagedSize - reduction; system_reserved_base = reinterpret_cast(SYSTEM_RESERVED_MIN); @@ -95,7 +103,8 @@ struct AddressSpace::Impl { const uintptr_t system_managed_addr = reinterpret_cast(system_managed_base); const uintptr_t system_reserved_addr = reinterpret_cast(system_reserved_base); const uintptr_t user_addr = reinterpret_cast(user_base); - placeholders.insert({system_managed_addr, virtual_size - reduction}); + regions.emplace(system_managed_addr, + MemoryRegion{system_managed_addr, virtual_size - reduction, false}); // Allocate backing file that represents the total physical memory. backing_handle = @@ -132,42 +141,15 @@ struct AddressSpace::Impl { } void* Map(VAddr virtual_addr, PAddr phys_addr, size_t size, ULONG prot, uintptr_t fd = 0) { - const size_t aligned_size = Common::AlignUp(size, 16_KB); - const auto it = placeholders.find(virtual_addr); - ASSERT_MSG(it != placeholders.end(), "Cannot map already mapped region"); - ASSERT_MSG(virtual_addr >= it->lower() && virtual_addr + aligned_size <= it->upper(), - "Map range must be fully contained in a placeholder"); - - // Windows only allows splitting a placeholder into two. - // This means that if the map range is fully - // contained the the placeholder we need to perform two split operations, - // one at the start and at the end. - const VAddr placeholder_start = it->lower(); - const VAddr placeholder_end = it->upper(); - const VAddr virtual_end = virtual_addr + aligned_size; - - // If the placeholder doesn't exactly start at virtual_addr, split it at the start. - if (placeholder_start != virtual_addr) { - VirtualFreeEx(process, reinterpret_cast(placeholder_start), - virtual_addr - placeholder_start, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); - } - - // If the placeholder doesn't exactly end at virtual_end, split it at the end. - if (placeholder_end != virtual_end) { - VirtualFreeEx(process, reinterpret_cast(virtual_end), - placeholder_end - virtual_end, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); - } - - // Remove the placeholder. - placeholders.erase({virtual_addr, virtual_end}); - - // Perform the map. + // Before mapping we must carve a placeholder with the exact properties of our mapping. + auto* region = EnsureSplitRegionForMapping(virtual_addr, size); + region->is_mapped = true; void* ptr = nullptr; if (phys_addr != -1) { HANDLE backing = fd ? reinterpret_cast(fd) : backing_handle; if (fd && prot == PAGE_READONLY) { DWORD resultvar; - ptr = VirtualAlloc2(process, reinterpret_cast(virtual_addr), aligned_size, + ptr = VirtualAlloc2(process, reinterpret_cast(virtual_addr), size, MEM_RESERVE | MEM_COMMIT | MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0); bool ret = ReadFile(backing, ptr, size, &resultvar, NULL); @@ -176,12 +158,11 @@ struct AddressSpace::Impl { ASSERT_MSG(ret, "VirtualProtect failed. {}", Common::GetLastErrorMsg()); } else { ptr = MapViewOfFile3(backing, process, reinterpret_cast(virtual_addr), - phys_addr, aligned_size, MEM_REPLACE_PLACEHOLDER, prot, - nullptr, 0); + phys_addr, size, MEM_REPLACE_PLACEHOLDER, prot, nullptr, 0); } } else { ptr = - VirtualAlloc2(process, reinterpret_cast(virtual_addr), aligned_size, + VirtualAlloc2(process, reinterpret_cast(virtual_addr), size, MEM_RESERVE | MEM_COMMIT | MEM_REPLACE_PLACEHOLDER, prot, nullptr, 0); } ASSERT_MSG(ptr, "{}", Common::GetLastErrorMsg()); @@ -202,33 +183,118 @@ struct AddressSpace::Impl { // The unmap call will create a new placeholder region. We need to see if we can coalesce it // with neighbors. - VAddr placeholder_start = virtual_addr; - VAddr placeholder_end = virtual_addr + size; + JoinRegionsAfterUnmap(virtual_addr, size); + } + + // The following code is inspired from Dolphin's MemArena + // https://github.com/dolphin-emu/dolphin/blob/deee3ee4/Source/Core/Common/MemArenaWin.cpp#L212 + MemoryRegion* EnsureSplitRegionForMapping(VAddr address, size_t size) { + // Find closest region that is <= the given address by using upper bound and decrementing + auto it = regions.upper_bound(address); + ASSERT_MSG(it != regions.begin(), "Invalid address {:#x}", address); + --it; + ASSERT_MSG(!it->second.is_mapped, + "Attempt to map {:#x} with size {:#x} which overlaps with {:#x} mapping", + address, size, it->second.base); + auto& [base, region] = *it; + + const VAddr mapping_address = region.base; + const size_t region_size = region.size; + if (mapping_address == address) { + // If this region is already split up correctly we don't have to do anything + if (region_size == size) { + return ®ion; + } + + ASSERT_MSG(region_size >= size, + "Region with address {:#x} and size {:#x} can't fit {:#x}", mapping_address, + region_size, size); + + // Split the placeholder. + if (!VirtualFreeEx(process, LPVOID(address), size, + MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER)) { + UNREACHABLE_MSG("Region splitting failed: {}", Common::GetLastErrorMsg()); + return nullptr; + } + + // Update tracked mappings and return the first of the two + region.size = size; + const VAddr new_mapping_start = address + size; + regions.emplace_hint(std::next(it), new_mapping_start, + MemoryRegion(new_mapping_start, region_size - size, false)); + return ®ion; + } + + ASSERT(mapping_address < address); + + // Is there enough space to map this? + const size_t offset_in_region = address - mapping_address; + const size_t minimum_size = size + offset_in_region; + ASSERT(region_size >= minimum_size); + + // Split the placeholder. + if (!VirtualFreeEx(process, LPVOID(address), size, + MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER)) { + UNREACHABLE_MSG("Region splitting failed: {}", Common::GetLastErrorMsg()); + return nullptr; + } + + // Do we now have two regions or three regions? + if (region_size == minimum_size) { + // Split into two; update tracked mappings and return the second one + region.size = offset_in_region; + it = regions.emplace_hint(std::next(it), address, MemoryRegion(address, size, false)); + return &it->second; + } else { + // Split into three; update tracked mappings and return the middle one + region.size = offset_in_region; + const VAddr middle_mapping_start = address; + const size_t middle_mapping_size = size; + const VAddr after_mapping_start = address + size; + const size_t after_mapping_size = region_size - minimum_size; + it = regions.emplace_hint(std::next(it), after_mapping_start, + MemoryRegion(after_mapping_start, after_mapping_size, false)); + it = regions.emplace_hint( + it, middle_mapping_start, + MemoryRegion(middle_mapping_start, middle_mapping_size, false)); + return &it->second; + } + } + + void JoinRegionsAfterUnmap(VAddr address, size_t size) { + // There should be a mapping that matches the request exactly, find it + auto it = regions.find(address); + ASSERT_MSG(it != regions.end() && it->second.size == size, + "Invalid address/size given to unmap."); + auto& [base, region] = *it; + region.is_mapped = false; // Check if a placeholder exists right before us. - const auto left_it = placeholders.find(virtual_addr - 1); - if (left_it != placeholders.end()) { - ASSERT_MSG(left_it->upper() == virtual_addr, - "Left placeholder does not end at virtual_addr!"); - placeholder_start = left_it->lower(); - VirtualFreeEx(process, reinterpret_cast(placeholder_start), - placeholder_end - placeholder_start, - MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS); + auto it_prev = it != regions.begin() ? std::prev(it) : regions.end(); + if (it_prev != regions.end() && !it_prev->second.is_mapped) { + const size_t total_size = it_prev->second.size + size; + if (!VirtualFreeEx(process, LPVOID(it_prev->first), total_size, + MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS)) { + UNREACHABLE_MSG("Region coalescing failed: {}", Common::GetLastErrorMsg()); + } + + it_prev->second.size = total_size; + regions.erase(it); + it = it_prev; } // Check if a placeholder exists right after us. - const auto right_it = placeholders.find(placeholder_end + 1); - if (right_it != placeholders.end()) { - ASSERT_MSG(right_it->lower() == placeholder_end, - "Right placeholder does not start at virtual_end!"); - placeholder_end = right_it->upper(); - VirtualFreeEx(process, reinterpret_cast(placeholder_start), - placeholder_end - placeholder_start, - MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS); - } + auto it_next = std::next(it); + if (it_next != regions.end() && !it_next->second.is_mapped) { + const size_t total_size = it->second.size + it_next->second.size; + if (!VirtualFreeEx(process, LPVOID(it->first), total_size, + MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS)) { + UNREACHABLE_MSG("Region coalescing failed: {}", Common::GetLastErrorMsg()); + } - // Insert the new placeholder. - placeholders.insert({placeholder_start, placeholder_end}); + it->second.size = total_size; + regions.erase(it_next); + } } void Protect(VAddr virtual_addr, size_t size, bool read, bool write, bool execute) { @@ -251,18 +317,22 @@ struct AddressSpace::Impl { return; } - DWORD old_flags{}; - bool success = - VirtualProtect(reinterpret_cast(virtual_addr), size, new_flags, &old_flags); - - if (!success) { - LOG_ERROR(Common_Memory, - "Failed to change virtual memory protection for address {:#x}, size {}", - virtual_addr, size); + const VAddr virtual_end = virtual_addr + size; + auto it = --regions.upper_bound(virtual_addr); + for (; it->first < virtual_end; it++) { + if (!it->second.is_mapped) { + continue; + } + const auto& region = it->second; + const size_t range_addr = std::max(region.base, virtual_addr); + const size_t range_size = std::min(region.base + region.size, virtual_end) - range_addr; + DWORD old_flags{}; + if (!VirtualProtectEx(process, LPVOID(range_addr), range_size, new_flags, &old_flags)) { + UNREACHABLE_MSG( + "Failed to change virtual memory protection for address {:#x}, size {}", + range_addr, range_size); + } } - - // Use assert to ensure success in debug builds - DEBUG_ASSERT(success && "Failed to change virtual memory protection"); } HANDLE process{}; @@ -275,7 +345,7 @@ struct AddressSpace::Impl { size_t system_reserved_size{}; u8* user_base{}; size_t user_size{}; - boost::icl::separate_interval_set placeholders; + std::map regions; }; #else diff --git a/src/core/cpu_patches.h b/src/core/cpu_patches.h index f9f7fe646..1ccac073a 100644 --- a/src/core/cpu_patches.h +++ b/src/core/cpu_patches.h @@ -3,6 +3,8 @@ #pragma once +#include "common/types.h" + namespace Core { /// Initializes a stack for the current thread for use by patch implementations. diff --git a/src/core/debug_state.cpp b/src/core/debug_state.cpp index adcb0cadb..562cb62e8 100644 --- a/src/core/debug_state.cpp +++ b/src/core/debug_state.cpp @@ -8,7 +8,7 @@ #include "common/singleton.h" #include "debug_state.h" #include "devtools/widget/common.h" -#include "libraries/kernel/time_management.h" +#include "libraries/kernel/time.h" #include "libraries/system/msgdialog.h" #include "video_core/amdgpu/pm4_cmds.h" @@ -157,7 +157,7 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, if (is_compute) { dump.is_compute = true; const auto& cs = dump.regs.cs_program; - dump.cs_data = ComputerShaderDump{ + dump.cs_data = PipelineComputerProgramDump{ .cs_program = cs, .code = std::vector{cs.Code().begin(), cs.Code().end()}, }; @@ -167,7 +167,7 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, auto stage = regs.ProgramForStage(i); if (stage->address_lo != 0) { auto code = stage->Code(); - dump.stages[i] = ShaderDump{ + dump.stages[i] = PipelineShaderProgramDump{ .user_data = *stage, .code = std::vector{code.begin(), code.end()}, }; @@ -176,3 +176,10 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, } } } + +void DebugStateImpl::CollectShader(const std::string& name, std::span spv, + std::span raw_code) { + shader_dump_list.emplace_back(name, std::vector{spv.begin(), spv.end()}, + std::vector{raw_code.begin(), raw_code.end()}); + std::ranges::sort(shader_dump_list, {}, &ShaderDump::name); +} diff --git a/src/core/debug_state.h b/src/core/debug_state.h index cd1c6aa93..759755b52 100644 --- a/src/core/debug_state.h +++ b/src/core/debug_state.h @@ -30,7 +30,8 @@ namespace Core::Devtools { class Layer; namespace Widget { class FrameGraph; -} +class ShaderList; +} // namespace Widget } // namespace Core::Devtools namespace DebugStateType { @@ -49,12 +50,12 @@ struct QueueDump { uintptr_t base_addr; }; -struct ShaderDump { +struct PipelineShaderProgramDump { Vulkan::Liverpool::ShaderProgram user_data{}; std::vector code{}; }; -struct ComputerShaderDump { +struct PipelineComputerProgramDump { Vulkan::Liverpool::ComputeProgram cs_program{}; std::vector code{}; }; @@ -63,8 +64,8 @@ struct RegDump { bool is_compute{false}; static constexpr size_t MaxShaderStages = 5; Vulkan::Liverpool::Regs regs{}; - std::array stages{}; - ComputerShaderDump cs_data{}; + std::array stages{}; + PipelineComputerProgramDump cs_data{}; }; struct FrameDump { @@ -73,9 +74,41 @@ struct FrameDump { std::unordered_map regs; // address -> reg dump }; +struct ShaderDump { + std::string name; + std::vector spv; + std::vector raw_code; + + std::string cache_spv_disasm{}; + std::string cache_raw_disasm{}; + + ShaderDump(std::string name, std::vector spv, std::vector raw_code) + : name(std::move(name)), spv(std::move(spv)), raw_code(std::move(raw_code)) {} + + ShaderDump(const ShaderDump& other) = delete; + ShaderDump(ShaderDump&& other) noexcept + : name{std::move(other.name)}, spv{std::move(other.spv)}, + raw_code{std::move(other.raw_code)}, cache_spv_disasm{std::move(other.cache_spv_disasm)}, + cache_raw_disasm{std::move(other.cache_raw_disasm)} {} + ShaderDump& operator=(const ShaderDump& other) = delete; + ShaderDump& operator=(ShaderDump&& other) noexcept { + if (this == &other) + return *this; + name = std::move(other.name); + spv = std::move(other.spv); + raw_code = std::move(other.raw_code); + cache_spv_disasm = std::move(other.cache_spv_disasm); + cache_raw_disasm = std::move(other.cache_raw_disasm); + return *this; + } +}; + class DebugStateImpl { friend class Core::Devtools::Layer; friend class Core::Devtools::Widget::FrameGraph; + friend class Core::Devtools::Widget::ShaderList; + + std::queue debug_message_popup; std::mutex guest_threads_mutex{}; std::vector guest_threads{}; @@ -94,7 +127,7 @@ class DebugStateImpl { std::shared_mutex frame_dump_list_mutex; std::vector frame_dump_list{}; - std::queue debug_message_popup; + std::vector shader_dump_list{}; public: void ShowDebugMessage(std::string message) { @@ -152,6 +185,9 @@ public: void PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, const AmdGpu::Liverpool::Regs& regs, bool is_compute = false); + + void CollectShader(const std::string& name, std::span spv, + std::span raw_code); }; } // namespace DebugStateType diff --git a/src/core/devices/base_device.cpp b/src/core/devices/base_device.cpp new file mode 100644 index 000000000..4f91c81c7 --- /dev/null +++ b/src/core/devices/base_device.cpp @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "base_device.h" + +namespace Core::Devices { + +BaseDevice::BaseDevice() = default; + +BaseDevice::~BaseDevice() = default; + +} // namespace Core::Devices \ No newline at end of file diff --git a/src/core/devices/base_device.h b/src/core/devices/base_device.h new file mode 100644 index 000000000..351af82b4 --- /dev/null +++ b/src/core/devices/base_device.h @@ -0,0 +1,72 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "common/types.h" +#include "common/va_ctx.h" + +namespace Libraries::Kernel { +struct OrbisKernelStat; +struct SceKernelIovec; +} // namespace Libraries::Kernel + +namespace Core::Devices { + +class BaseDevice { +public: + explicit BaseDevice(); + + virtual ~BaseDevice() = 0; + + virtual int ioctl(u64 cmd, Common::VaCtx* args) { + return ORBIS_KERNEL_ERROR_ENOTTY; + } + + virtual s64 write(const void* buf, size_t nbytes) { + return ORBIS_KERNEL_ERROR_EBADF; + } + + virtual size_t readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { + return ORBIS_KERNEL_ERROR_EBADF; + } + + virtual size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { + return ORBIS_KERNEL_ERROR_EBADF; + } + + virtual s64 preadv(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; + } + + virtual s64 read(void* buf, size_t nbytes) { + return ORBIS_KERNEL_ERROR_EBADF; + } + + virtual int fstat(Libraries::Kernel::OrbisKernelStat* sb) { + return ORBIS_KERNEL_ERROR_EBADF; + } + + virtual s32 fsync() { + return ORBIS_KERNEL_ERROR_EBADF; + } + + virtual int ftruncate(s64 length) { + return ORBIS_KERNEL_ERROR_EBADF; + } + + virtual int getdents(void* buf, u32 nbytes, s64* basep) { + return ORBIS_KERNEL_ERROR_EBADF; + } + + virtual s64 pwrite(const void* buf, size_t nbytes, u64 offset) { + return ORBIS_KERNEL_ERROR_EBADF; + } +}; + +} // namespace Core::Devices diff --git a/src/core/devices/ioccom.h b/src/core/devices/ioccom.h new file mode 100644 index 000000000..671ee33d4 --- /dev/null +++ b/src/core/devices/ioccom.h @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +/*- + * Copyright (c) 1982, 1986, 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ioccom.h 8.2 (Berkeley) 3/28/94 + * $FreeBSD$ + */ + +#define IOCPARM_SHIFT 13 /* number of bits for ioctl size */ +#define IOCPARM_MASK ((1 << IOCPARM_SHIFT) - 1) /* parameter length mask */ +#define IOCPARM_LEN(x) (((x) >> 16) & IOCPARM_MASK) +#define IOCBASECMD(x) ((x) & ~(IOCPARM_MASK << 16)) +#define IOCGROUP(x) (((x) >> 8) & 0xff) + +#define IOCPARM_MAX (1 << IOCPARM_SHIFT) /* max size of ioctl */ +#define IOC_VOID 0x20000000 /* no parameters */ +#define IOC_OUT 0x40000000 /* copy out parameters */ +#define IOC_IN 0x80000000 /* copy in parameters */ +#define IOC_INOUT (IOC_IN | IOC_OUT) +#define IOC_DIRMASK (IOC_VOID | IOC_OUT | IOC_IN) + +#define _IOC(inout, group, num, len) \ + ((unsigned long)((inout) | (((len) & IOCPARM_MASK) << 16) | ((group) << 8) | (num))) +#define _IO(g, n) _IOC(IOC_VOID, (g), (n), 0) +#define _IOWINT(g, n) _IOC(IOC_VOID, (g), (n), sizeof(int)) +#define _IOR(g, n, t) _IOC(IOC_OUT, (g), (n), sizeof(t)) +#define _IOW(g, n, t) _IOC(IOC_IN, (g), (n), sizeof(t)) +/* this should be _IORW, but stdio got there first */ +#define _IOWR(g, n, t) _IOC(IOC_INOUT, (g), (n), sizeof(t)) + +/* +# Simple parse of ioctl cmd +def parse(v): + print('inout', (v >> 24 & 0xFF)) + print('len', hex(v >> 16 & 0xFF)) + print('group', chr(v >> 8 & 0xFF)) + print('num', hex(v & 0xFF)) +*/ diff --git a/src/core/devices/logger.cpp b/src/core/devices/logger.cpp new file mode 100644 index 000000000..6f104509c --- /dev/null +++ b/src/core/devices/logger.cpp @@ -0,0 +1,65 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/libraries/kernel/file_system.h" +#include "logger.h" + +namespace Core::Devices { + +Logger::Logger(std::string prefix, bool is_err) : prefix(std::move(prefix)), is_err(is_err) {} + +Logger::~Logger() = default; + +s64 Logger::write(const void* buf, size_t nbytes) { + log(static_cast(buf), nbytes); + return nbytes; +} + +size_t Logger::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) { + for (int i = 0; i < iovcnt; i++) { + log(static_cast(iov[i].iov_base), iov[i].iov_len); + } + return iovcnt; +} + +s64 Logger::pwrite(const void* buf, size_t nbytes, u64 offset) { + log(static_cast(buf), nbytes); + return nbytes; +} + +s32 Logger::fsync() { + log_flush(); + return 0; +} + +void Logger::log(const char* buf, size_t nbytes) { + std::scoped_lock lock{mtx}; + const char* end = buf + nbytes; + for (const char* it = buf; it < end; ++it) { + char c = *it; + if (c == '\r') { + continue; + } + if (c == '\n') { + log_flush(); + continue; + } + buffer.push_back(c); + } +} + +void Logger::log_flush() { + std::scoped_lock lock{mtx}; + if (buffer.empty()) { + return; + } + if (is_err) { + LOG_ERROR(Tty, "[{}] {}", prefix, std::string_view{buffer}); + } else { + LOG_INFO(Tty, "[{}] {}", prefix, std::string_view{buffer}); + } + buffer.clear(); +} + +} // namespace Core::Devices \ No newline at end of file diff --git a/src/core/devices/logger.h b/src/core/devices/logger.h new file mode 100644 index 000000000..bfb07f337 --- /dev/null +++ b/src/core/devices/logger.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "base_device.h" + +#include +#include +#include + +namespace Core::Devices { + +class Logger final : BaseDevice { + std::string prefix; + bool is_err; + + std::recursive_mutex mtx; + std::vector buffer; + +public: + explicit Logger(std::string prefix, bool is_err); + + ~Logger() override; + + s64 write(const void* buf, size_t nbytes) override; + size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override; + s64 pwrite(const void* buf, size_t nbytes, u64 offset) override; + + s32 fsync() override; + +private: + void log(const char* buf, size_t nbytes); + void log_flush(); +}; + +} // namespace Core::Devices diff --git a/src/core/devices/nop_device.h b/src/core/devices/nop_device.h new file mode 100644 index 000000000..a75b92f1b --- /dev/null +++ b/src/core/devices/nop_device.h @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once +#include "base_device.h" + +namespace Core::Devices { + +class NopDevice final : BaseDevice { + u32 handle; + +public: + explicit NopDevice(u32 handle) : handle(handle) {} + + ~NopDevice() override = default; + + int ioctl(u64 cmd, Common::VaCtx* args) override { + return 0; + } + s64 write(const void* buf, size_t nbytes) override { + return 0; + } + size_t readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override { + return 0; + } + size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override { + return 0; + } + s64 preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) override { + return 0; + } + s64 lseek(s64 offset, int whence) override { + return 0; + } + s64 read(void* buf, size_t nbytes) override { + return 0; + } + int fstat(Libraries::Kernel::OrbisKernelStat* sb) override { + return 0; + } + s32 fsync() override { + return 0; + } + int ftruncate(s64 length) override { + return 0; + } + int getdents(void* buf, u32 nbytes, s64* basep) override { + return 0; + } + s64 pwrite(const void* buf, size_t nbytes, u64 offset) override { + return 0; + } +}; + +} // namespace Core::Devices diff --git a/src/core/devtools/gcn/gcn_context_regs.cpp b/src/core/devtools/gcn/gcn_context_regs.cpp index 843ba9e65..5a591111e 100644 --- a/src/core/devtools/gcn/gcn_context_regs.cpp +++ b/src/core/devtools/gcn/gcn_context_regs.cpp @@ -289,6 +289,16 @@ const char* GetContextRegName(u32 reg_offset) { return "mmSPI_PS_INPUT_CNTL_2"; case mmSPI_PS_INPUT_CNTL_3: return "mmSPI_PS_INPUT_CNTL_3"; + case mmPA_SU_POLY_OFFSET_FRONT_SCALE: + return "mmPA_SU_POLY_OFFSET_FRONT_SCALE"; + case mmPA_SU_POLY_OFFSET_FRONT_OFFSET: + return "mmPA_SU_POLY_OFFSET_FRONT_OFFSET"; + case mmPA_SU_POLY_OFFSET_BACK_SCALE: + return "mmPA_SU_POLY_OFFSET_BACK_SCALE"; + case mmPA_SU_POLY_OFFSET_BACK_OFFSET: + return "mmPA_SU_POLY_OFFSET_BACK_OFFSET"; + case mmPA_SU_POLY_OFFSET_CLAMP: + return "mmPA_SU_POLY_OFFSET_CLAMP"; default: break; } diff --git a/src/core/devtools/layer.cpp b/src/core/devtools/layer.cpp index 264b3be0d..2c2099f4d 100644 --- a/src/core/devtools/layer.cpp +++ b/src/core/devtools/layer.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "layer.h" + #include #include "common/config.h" @@ -9,10 +11,14 @@ #include "core/debug_state.h" #include "imgui/imgui_std.h" #include "imgui_internal.h" -#include "layer.h" #include "options.h" +#include "video_core/renderer_vulkan/vk_presenter.h" #include "widget/frame_dump.h" #include "widget/frame_graph.h" +#include "widget/memory_map.h" +#include "widget/shader_list.h" + +extern std::unique_ptr presenter; using namespace ImGui; using namespace Core::Devtools; @@ -32,6 +38,9 @@ static float debug_popup_timing = 3.0f; static bool just_opened_options = false; +static Widget::MemoryMapViewer memory_map; +static Widget::ShaderList shader_list; + // clang-format off static std::string help_text = #include "help.txt" @@ -60,6 +69,7 @@ void L::DrawMenuBar() { } if (BeginMenu("GPU Tools")) { MenuItem("Show frame info", nullptr, &frame_graph.is_open); + MenuItem("Show loaded shaders", nullptr, &shader_list.open); if (BeginMenu("Dump frames")) { SliderInt("Count", &dump_frame_count, 1, 5); if (MenuItem("Dump", "Ctrl+Alt+F9", nullptr, !DebugState.DumpingCurrentFrame())) { @@ -71,6 +81,19 @@ void L::DrawMenuBar() { open_popup_help = MenuItem("Help & Tips"); ImGui::EndMenu(); } + if (BeginMenu("Display")) { + if (BeginMenu("Brightness")) { + SliderFloat("Gamma", &presenter->GetGammaRef(), 0.1f, 2.0f); + ImGui::EndMenu(); + } + ImGui::EndMenu(); + } + if (BeginMenu("Debug")) { + if (MenuItem("Memory map")) { + memory_map.open = true; + } + ImGui::EndMenu(); + } EndMainMenuBar(); } @@ -165,19 +188,29 @@ void L::DrawAdvanced() { bool close_popup_options = true; if (BeginPopupModal("GPU Tools Options", &close_popup_options, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings)) { - static char disassembly_cli[512]; + static char disassembler_cli_isa[512]; + static char disassembler_cli_spv[512]; static bool frame_dump_render_on_collapse; if (just_opened_options) { just_opened_options = false; - auto s = Options.disassembly_cli.copy(disassembly_cli, sizeof(disassembly_cli) - 1); - disassembly_cli[s] = '\0'; + auto s = Options.disassembler_cli_isa.copy(disassembler_cli_isa, + sizeof(disassembler_cli_isa) - 1); + disassembler_cli_isa[s] = '\0'; + s = Options.disassembler_cli_spv.copy(disassembler_cli_spv, + sizeof(disassembler_cli_spv) - 1); + disassembler_cli_spv[s] = '\0'; frame_dump_render_on_collapse = Options.frame_dump_render_on_collapse; } - InputText("Shader disassembler: ", disassembly_cli, sizeof(disassembly_cli)); + InputText("Shader isa disassembler: ", disassembler_cli_isa, sizeof(disassembler_cli_isa)); if (IsItemHovered()) { - SetTooltip(R"(Command to disassemble shaders. Example "dis.exe" --raw "{src}")"); + SetTooltip(R"(Command to disassemble shaders. Example: dis.exe --raw "{src}")"); + } + InputText("Shader SPIRV disassembler: ", disassembler_cli_spv, + sizeof(disassembler_cli_spv)); + if (IsItemHovered()) { + SetTooltip(R"(Command to disassemble shaders. Example: spirv-cross -V "{src}")"); } Checkbox("Show frame dump popups even when collapsed", &frame_dump_render_on_collapse); if (IsItemHovered()) { @@ -186,7 +219,8 @@ void L::DrawAdvanced() { } if (Button("Save")) { - Options.disassembly_cli = disassembly_cli; + Options.disassembler_cli_isa = disassembler_cli_isa; + Options.disassembler_cli_spv = disassembler_cli_spv; Options.frame_dump_render_on_collapse = frame_dump_render_on_collapse; SaveIniSettingsToDisk(io.IniFilename); CloseCurrentPopup(); @@ -209,6 +243,13 @@ void L::DrawAdvanced() { EndPopup(); } + + if (memory_map.open) { + memory_map.Draw(); + } + if (shader_list.open) { + shader_list.Draw(); + } } void L::DrawSimple() { diff --git a/src/core/devtools/options.cpp b/src/core/devtools/options.cpp index 1b49da76b..2def42071 100644 --- a/src/core/devtools/options.cpp +++ b/src/core/devtools/options.cpp @@ -12,8 +12,12 @@ TOptions Options; void LoadOptionsConfig(const char* line) { char str[512]; int i; - if (sscanf(line, "disassembly_cli=%511[^\n]", str) == 1) { - Options.disassembly_cli = str; + if (sscanf(line, "disassembler_cli_isa=%511[^\n]", str) == 1) { + Options.disassembler_cli_isa = str; + return; + } + if (sscanf(line, "disassembler_cli_spv=%511[^\n]", str) == 1) { + Options.disassembler_cli_spv = str; return; } if (sscanf(line, "frame_dump_render_on_collapse=%d", &i) == 1) { @@ -23,7 +27,8 @@ void LoadOptionsConfig(const char* line) { } void SerializeOptionsConfig(ImGuiTextBuffer* buf) { - buf->appendf("disassembly_cli=%s\n", Options.disassembly_cli.c_str()); + 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); } diff --git a/src/core/devtools/options.h b/src/core/devtools/options.h index c3a8aaf31..70e1d137b 100644 --- a/src/core/devtools/options.h +++ b/src/core/devtools/options.h @@ -10,7 +10,8 @@ struct ImGuiTextBuffer; namespace Core::Devtools { struct TOptions { - std::string disassembly_cli{}; + std::string disassembler_cli_isa{"clrxdisasm --raw \"{src}\""}; + std::string disassembler_cli_spv{"spirv-cross -V \"{src}\""}; bool frame_dump_render_on_collapse{false}; }; diff --git a/src/core/devtools/widget/cmd_list.cpp b/src/core/devtools/widget/cmd_list.cpp index 9a42f8238..7c550cf2e 100644 --- a/src/core/devtools/widget/cmd_list.cpp +++ b/src/core/devtools/widget/cmd_list.cpp @@ -3,6 +3,7 @@ // Credits to https://github.com/psucien/tlg-emu-tools/ +#include #include #include #include @@ -1224,12 +1225,12 @@ void CmdListViewer::Draw(bool only_batches_view) { } Text("queue : %s", queue_name); - Text("base addr: %08llX", cmdb_addr); + Text("base addr: %08" PRIXPTR, cmdb_addr); SameLine(); if (SmallButton("Memory >")) { cmdb_view.Open ^= true; } - Text("size : %04llX", cmdb_size); + Text("size : %04zX", cmdb_size); Separator(); { @@ -1292,12 +1293,12 @@ void CmdListViewer::Draw(bool only_batches_view) { if (batch.type == static_cast(0xFF)) { ignore_header = true; } else if (!batch.marker.empty()) { - snprintf(batch_hdr, sizeof(batch_hdr), "%08llX: batch-%03d %s | %s", + snprintf(batch_hdr, sizeof(batch_hdr), "%08" PRIXPTR ": batch-%03d %s | %s", cmdb_addr + batch.start_addr, batch.id, Gcn::GetOpCodeName(static_cast(batch.type)), batch.marker.c_str()); } else { - snprintf(batch_hdr, sizeof(batch_hdr), "%08llX: batch-%03d %s", + snprintf(batch_hdr, sizeof(batch_hdr), "%08" PRIXPTR ": batch-%03d %s", cmdb_addr + batch.start_addr, batch.id, Gcn::GetOpCodeName(static_cast(batch.type))); } @@ -1305,7 +1306,7 @@ void CmdListViewer::Draw(bool only_batches_view) { if (batch.id == batch_bp) { // highlight batch at breakpoint PushStyleColor(ImGuiCol_Header, ImVec4{1.0f, 0.5f, 0.5f, 0.5f}); } - if (batch.id == highlight_batch) { + if (batch.id == highlight_batch && !group_batches) { PushStyleColor(ImGuiCol_Text, ImVec4{1.0f, 0.7f, 0.7f, 1.0f}); } @@ -1348,7 +1349,7 @@ void CmdListViewer::Draw(bool only_batches_view) { } if (show_batch_content) { - auto processed_size = 0ull; + size_t processed_size = 0; auto bb = ctx.LastItemData.Rect; if (group_batches && !ignore_header) { Indent(); @@ -1364,9 +1365,9 @@ void CmdListViewer::Draw(bool only_batches_view) { op = pm4_t3->opcode; char header_name[128]; - sprintf(header_name, "%08llX: %s", - cmdb_addr + batch.start_addr + processed_size, - Gcn::GetOpCodeName((u32)op)); + snprintf(header_name, sizeof(header_name), "%08" PRIXPTR ": %s", + cmdb_addr + batch.start_addr + processed_size, + Gcn::GetOpCodeName(static_cast(op))); bool open_pm4 = TreeNode(header_name); if (!group_batches) { @@ -1458,7 +1459,7 @@ void CmdListViewer::Draw(bool only_batches_view) { } } - if (batch.id == highlight_batch) { + if (batch.id == highlight_batch && !group_batches) { PopStyleColor(); } diff --git a/src/core/devtools/widget/common.h b/src/core/devtools/widget/common.h index e650f5fc7..5f669eb65 100644 --- a/src/core/devtools/widget/common.h +++ b/src/core/devtools/widget/common.h @@ -3,16 +3,24 @@ #pragma once +#include #include #include #include -#include +#include #include "common/bit_field.h" +#include "common/io_file.h" #include "common/types.h" +#include "core/debug_state.h" #include "video_core/amdgpu/pm4_opcodes.h" +#if defined(_WIN32) +#define popen _popen +#define pclose _pclose +#endif + namespace Core::Devtools::Widget { /* * Generic PM4 header @@ -106,4 +114,53 @@ static bool IsDrawCall(AmdGpu::PM4ItOpcode opcode) { } } +inline std::optional exec_cli(const char* cli) { + std::array buffer{}; + std::string output; + const auto f = popen(cli, "r"); + if (!f) { + pclose(f); + return {}; + } + while (fgets(buffer.data(), buffer.size(), f)) { + output += buffer.data(); + } + pclose(f); + return output; +} + +inline std::string RunDisassembler(const std::string& disassembler_cli, + const std::vector& shader_code) { + std::string shader_dis; + + if (disassembler_cli.empty()) { + shader_dis = "No disassembler set"; + } else { + auto bin_path = std::filesystem::temp_directory_path() / "shadps4_tmp_shader.bin"; + + constexpr std::string_view src_arg = "{src}"; + std::string cli = disassembler_cli; + const auto pos = cli.find(src_arg); + if (pos == std::string::npos) { + DebugState.ShowDebugMessage("Disassembler CLI does not contain {src} argument\n" + + disassembler_cli); + } else { + cli.replace(pos, src_arg.size(), "\"" + bin_path.string() + "\""); + Common::FS::IOFile file(bin_path, Common::FS::FileAccessMode::Write); + file.Write(shader_code); + file.Close(); + + auto result = exec_cli(cli.c_str()); + shader_dis = result.value_or("Could not disassemble shader"); + if (shader_dis.empty()) { + shader_dis = "Disassembly empty or failed"; + } + + std::filesystem::remove(bin_path); + } + } + + return shader_dis; +} + } // namespace Core::Devtools::Widget \ No newline at end of file diff --git a/src/core/devtools/widget/frame_dump.cpp b/src/core/devtools/widget/frame_dump.cpp index 86ba7b86e..055ce1333 100644 --- a/src/core/devtools/widget/frame_dump.cpp +++ b/src/core/devtools/widget/frame_dump.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include "common/io_file.h" #include "core/devtools/options.h" diff --git a/src/core/devtools/widget/imgui_memory_editor.h b/src/core/devtools/widget/imgui_memory_editor.h index fb1f46767..086cfb1d2 100644 --- a/src/core/devtools/widget/imgui_memory_editor.h +++ b/src/core/devtools/widget/imgui_memory_editor.h @@ -458,7 +458,7 @@ struct MemoryEditor { data_write = data_next = true; if (data_editing_addr_next != (size_t)-1) data_write = data_next = false; - unsigned int data_input_value = 0; + u32 data_input_value = 0; if (!ReadOnly && data_write && sscanf(DataInputBuf, "%X", &data_input_value) == 1) { if (WriteFn) @@ -929,7 +929,7 @@ struct MemoryEditor { default: case ImGuiDataType_COUNT: break; - } // Switch + } // Switch IM_ASSERT(0); // Shouldn't reach } }; diff --git a/src/core/devtools/widget/memory_map.cpp b/src/core/devtools/widget/memory_map.cpp new file mode 100644 index 000000000..7edd676e9 --- /dev/null +++ b/src/core/devtools/widget/memory_map.cpp @@ -0,0 +1,135 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include + +#include "core/debug_state.h" +#include "core/memory.h" +#include "memory_map.h" + +using namespace ImGui; + +namespace Core::Devtools::Widget { + +bool MemoryMapViewer::Iterator::DrawLine() { + if (is_vma) { + if (vma.it == vma.end) { + return false; + } + auto m = vma.it->second; + if (m.type == VMAType::Free) { + ++vma.it; + return DrawLine(); + } + TableNextColumn(); + Text("%" PRIXPTR, m.base); + TableNextColumn(); + Text("%zX", m.size); + TableNextColumn(); + Text("%s", magic_enum::enum_name(m.type).data()); + TableNextColumn(); + Text("%s", magic_enum::enum_name(m.prot).data()); + TableNextColumn(); + if (m.is_exec) { + Text("X"); + } + TableNextColumn(); + Text("%s", m.name.c_str()); + ++vma.it; + return true; + } + if (dmem.it == dmem.end) { + return false; + } + auto m = dmem.it->second; + if (m.is_free) { + ++dmem.it; + return DrawLine(); + } + TableNextColumn(); + Text("%" PRIXPTR, m.base); + TableNextColumn(); + Text("%zX", m.size); + TableNextColumn(); + auto type = static_cast<::Libraries::Kernel::MemoryTypes>(m.memory_type); + Text("%s", magic_enum::enum_name(type).data()); + TableNextColumn(); + Text("%d", m.is_pooled); + ++dmem.it; + return true; +} + +void MemoryMapViewer::Draw() { + SetNextWindowSize({600.0f, 500.0f}, ImGuiCond_FirstUseEver); + if (!Begin("Memory map", &open)) { + End(); + return; + } + + auto mem = Memory::Instance(); + std::scoped_lock lck{mem->mutex}; + + { + bool next_showing_vma = showing_vma; + if (showing_vma) { + PushStyleColor(ImGuiCol_Button, ImVec4{1.0f, 0.7f, 0.7f, 1.0f}); + } + if (Button("VMem")) { + next_showing_vma = true; + } + if (showing_vma) { + PopStyleColor(); + } + SameLine(); + if (!showing_vma) { + PushStyleColor(ImGuiCol_Button, ImVec4{1.0f, 0.7f, 0.7f, 1.0f}); + } + if (Button("DMem")) { + next_showing_vma = false; + } + if (!showing_vma) { + PopStyleColor(); + } + showing_vma = next_showing_vma; + } + + Iterator it{}; + if (showing_vma) { + it.is_vma = true; + it.vma.it = mem->vma_map.begin(); + it.vma.end = mem->vma_map.end(); + } else { + it.is_vma = false; + it.dmem.it = mem->dmem_map.begin(); + it.dmem.end = mem->dmem_map.end(); + } + + if (BeginTable("memory_view_table", showing_vma ? 6 : 4, + ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | + ImGuiTableFlags_SizingFixedFit)) { + if (showing_vma) { + TableSetupColumn("Address"); + TableSetupColumn("Size"); + TableSetupColumn("Type"); + TableSetupColumn("Prot"); + TableSetupColumn("Is Exec"); + TableSetupColumn("Name"); + } else { + TableSetupColumn("Address"); + TableSetupColumn("Size"); + TableSetupColumn("Type"); + TableSetupColumn("Pooled"); + } + TableHeadersRow(); + + while (it.DrawLine()) + ; + EndTable(); + } + + End(); +} + +} // namespace Core::Devtools::Widget \ No newline at end of file diff --git a/src/core/devtools/widget/memory_map.h b/src/core/devtools/widget/memory_map.h new file mode 100644 index 000000000..cc7697c8c --- /dev/null +++ b/src/core/devtools/widget/memory_map.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/memory.h" + +namespace Core::Devtools::Widget { + +class MemoryMapViewer { + struct Iterator { + bool is_vma; + struct { + MemoryManager::DMemMap::iterator it; + MemoryManager::DMemMap::iterator end; + } dmem; + struct { + MemoryManager::VMAMap::iterator it; + MemoryManager::VMAMap::iterator end; + } vma; + + bool DrawLine(); + }; + + bool showing_vma = true; + +public: + bool open = false; + + void Draw(); +}; + +} // namespace Core::Devtools::Widget diff --git a/src/core/devtools/widget/reg_popup.cpp b/src/core/devtools/widget/reg_popup.cpp index 0633e76e6..2727e1745 100644 --- a/src/core/devtools/widget/reg_popup.cpp +++ b/src/core/devtools/widget/reg_popup.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include "cmd_list.h" #include "common.h" diff --git a/src/core/devtools/widget/reg_view.cpp b/src/core/devtools/widget/reg_view.cpp index 10cc88085..79b02a849 100644 --- a/src/core/devtools/widget/reg_view.cpp +++ b/src/core/devtools/widget/reg_view.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include "common.h" @@ -25,21 +25,6 @@ using magic_enum::enum_name; constexpr auto depth_id = 0xF3; -static std::optional exec_cli(const char* cli) { - std::array buffer{}; - std::string output; - const auto f = popen(cli, "r"); - if (!f) { - pclose(f); - return {}; - } - while (fgets(buffer.data(), buffer.size(), f)) { - output += buffer.data(); - } - pclose(f); - return output; -} - namespace Core::Devtools::Widget { void RegView::ProcessShader(int shader_id) { @@ -54,38 +39,12 @@ void RegView::ProcessShader(int shader_id) { user_data = s.user_data.user_data; } - std::string shader_dis; - - if (Options.disassembly_cli.empty()) { - shader_dis = "No disassembler set"; - } else { - auto bin_path = std::filesystem::temp_directory_path() / "shadps4_tmp_shader.bin"; - - constexpr std::string_view src_arg = "{src}"; - std::string cli = Options.disassembly_cli; - const auto pos = cli.find(src_arg); - if (pos == std::string::npos) { - DebugState.ShowDebugMessage("Disassembler CLI does not contain {src} argument"); - } else { - cli.replace(pos, src_arg.size(), "\"" + bin_path.string() + "\""); - Common::FS::IOFile file(bin_path, Common::FS::FileAccessMode::Write); - file.Write(shader_code); - file.Close(); - - auto result = exec_cli(cli.c_str()); - shader_dis = result.value_or("Could not disassemble shader"); - if (shader_dis.empty()) { - shader_dis = "Disassembly empty or failed"; - } - - std::filesystem::remove(bin_path); - } - } + std::string shader_dis = RunDisassembler(Options.disassembler_cli_isa, shader_code); MemoryEditor hex_view; hex_view.Open = true; hex_view.ReadOnly = true; - hex_view.Cols = 8; + hex_view.Cols = 16; hex_view.OptShowAscii = false; hex_view.OptShowOptions = false; @@ -376,7 +335,9 @@ void RegView::Draw() { if (!shader) { Text("Stage not selected"); } else { - shader->hex_view.DrawContents(shader->user_data.data(), shader->user_data.size()); + shader->hex_view.DrawContents(shader->user_data.data(), + shader->user_data.size() * + sizeof(Vulkan::Liverpool::UserData::value_type)); } } End(); diff --git a/src/core/devtools/widget/shader_list.cpp b/src/core/devtools/widget/shader_list.cpp new file mode 100644 index 000000000..b056880dd --- /dev/null +++ b/src/core/devtools/widget/shader_list.cpp @@ -0,0 +1,95 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "shader_list.h" + +#include + +#include "common.h" +#include "common/config.h" +#include "core/debug_state.h" +#include "core/devtools/options.h" +#include "imgui/imgui_std.h" + +using namespace ImGui; + +namespace Core::Devtools::Widget { + +void ShaderList::DrawShader(DebugStateType::ShaderDump& value) { + if (!loaded_data) { + loaded_data = true; + if (value.cache_raw_disasm.empty()) { + value.cache_raw_disasm = RunDisassembler(Options.disassembler_cli_isa, value.raw_code); + } + isa_editor.SetText(value.cache_raw_disasm); + + if (value.cache_spv_disasm.empty()) { + value.cache_spv_disasm = RunDisassembler(Options.disassembler_cli_spv, value.spv); + } + spv_editor.SetText(value.cache_spv_disasm); + } + + if (SmallButton("<-")) { + selected_shader = -1; + } + SameLine(); + Text("%s", value.name.c_str()); + SameLine(0.0f, 7.0f); + if (BeginCombo("Shader type", showing_isa ? "ISA" : "SPIRV", ImGuiComboFlags_WidthFitPreview)) { + if (Selectable("SPIRV")) { + showing_isa = false; + } + if (Selectable("ISA")) { + showing_isa = true; + } + EndCombo(); + } + + if (showing_isa) { + isa_editor.Render("ISA", GetContentRegionAvail()); + } else { + spv_editor.Render("SPIRV", GetContentRegionAvail()); + } +} + +ShaderList::ShaderList() { + isa_editor.SetPalette(TextEditor::GetDarkPalette()); + isa_editor.SetReadOnly(true); + spv_editor.SetPalette(TextEditor::GetDarkPalette()); + spv_editor.SetReadOnly(true); + spv_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::GLSL()); +} + +void ShaderList::Draw() { + SetNextWindowSize({500.0f, 600.0f}, ImGuiCond_FirstUseEver); + if (!Begin("Shader list", &open)) { + End(); + return; + } + + if (!Config::collectShadersForDebug()) { + DrawCenteredText("Enable 'CollectShader' in config to see shaders"); + End(); + return; + } + + if (selected_shader >= 0) { + DrawShader(DebugState.shader_dump_list[selected_shader]); + End(); + return; + } + + auto width = GetContentRegionAvail().x; + int i = 0; + for (const auto& shader : DebugState.shader_dump_list) { + if (ButtonEx(shader.name.c_str(), {width, 20.0f}, ImGuiButtonFlags_NoHoveredOnFocus)) { + selected_shader = i; + loaded_data = false; + } + i++; + } + + End(); +} + +} // namespace Core::Devtools::Widget \ No newline at end of file diff --git a/src/core/devtools/widget/shader_list.h b/src/core/devtools/widget/shader_list.h new file mode 100644 index 000000000..5a47f656d --- /dev/null +++ b/src/core/devtools/widget/shader_list.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/debug_state.h" +#include "text_editor.h" + +namespace Core::Devtools::Widget { + +class ShaderList { + int selected_shader = -1; + TextEditor isa_editor{}; + TextEditor spv_editor{}; + bool loaded_data = false; + bool showing_isa = false; + + void DrawShader(DebugStateType::ShaderDump& value); + +public: + ShaderList(); + + bool open = false; + + void Draw(); +}; + +} // namespace Core::Devtools::Widget \ No newline at end of file diff --git a/src/core/devtools/widget/text_editor.cpp b/src/core/devtools/widget/text_editor.cpp index f447e45a2..07f2f658d 100644 --- a/src/core/devtools/widget/text_editor.cpp +++ b/src/core/devtools/widget/text_editor.cpp @@ -131,7 +131,7 @@ static int UTF8CharLength(TextEditor::Char c) { } // "Borrowed" from ImGui source -static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c) { +static inline int ImTextCharToUtf8(char* buf, int buf_size, u32 c) { if (c < 0x80) { buf[0] = (char)c; return 1; diff --git a/src/core/file_format/pkg.cpp b/src/core/file_format/pkg.cpp index 0ae9f57eb..a6b5eb9a8 100644 --- a/src/core/file_format/pkg.cpp +++ b/src/core/file_format/pkg.cpp @@ -1,31 +1,30 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include +#include #include "common/io_file.h" #include "common/logging/formatter.h" #include "core/file_format/pkg.h" #include "core/file_format/pkg_type.h" -static void DecompressPFSC(std::span compressed_data, - std::span decompressed_data) { - zng_stream decompressStream; +static void DecompressPFSC(std::span compressed_data, std::span decompressed_data) { + z_stream decompressStream; decompressStream.zalloc = Z_NULL; decompressStream.zfree = Z_NULL; decompressStream.opaque = Z_NULL; - if (zng_inflateInit(&decompressStream) != Z_OK) { + if (inflateInit(&decompressStream) != Z_OK) { // std::cerr << "Error initializing zlib for deflation." << std::endl; } decompressStream.avail_in = compressed_data.size(); - decompressStream.next_in = reinterpret_cast(compressed_data.data()); + decompressStream.next_in = reinterpret_cast(compressed_data.data()); decompressStream.avail_out = decompressed_data.size(); - decompressStream.next_out = reinterpret_cast(decompressed_data.data()); + decompressStream.next_out = reinterpret_cast(decompressed_data.data()); - if (zng_inflate(&decompressStream, Z_FINISH)) { + if (inflate(&decompressStream, Z_FINISH)) { } - if (zng_inflateEnd(&decompressStream) != Z_OK) { + if (inflateEnd(&decompressStream) != Z_OK) { // std::cerr << "Error ending zlib inflate" << std::endl; } } diff --git a/src/core/file_format/playgo_chunk.h b/src/core/file_format/playgo_chunk.h index b6d38e0e1..12d8f022e 100644 --- a/src/core/file_format/playgo_chunk.h +++ b/src/core/file_format/playgo_chunk.h @@ -97,24 +97,22 @@ struct PlaygoChunk { class PlaygoFile { public: - bool initialized; - OrbisPlayGoHandle handle; - OrbisPlayGoChunkId id; - OrbisPlayGoLocus locus; - OrbisPlayGoInstallSpeed speed; - s64 speed_tick; - OrbisPlayGoEta eta; - OrbisPlayGoLanguageMask langMask; + OrbisPlayGoHandle handle = 0; + OrbisPlayGoChunkId id = 0; + OrbisPlayGoLocus locus = OrbisPlayGoLocus::NotDownloaded; + OrbisPlayGoInstallSpeed speed = OrbisPlayGoInstallSpeed::Trickle; + s64 speed_tick = 0; + OrbisPlayGoEta eta = 0; + OrbisPlayGoLanguageMask langMask = 0; std::vector chunks; public: - PlaygoFile() - : initialized(false), handle(0), id(0), locus(0), speed(ORBIS_PLAYGO_INSTALL_SPEED_TRICKLE), - speed_tick(0), eta(0), langMask(0), playgoHeader{0} {} + explicit PlaygoFile() = default; ~PlaygoFile() = default; bool Open(const std::filesystem::path& filepath); bool LoadChunks(const Common::FS::IOFile& file); + PlaygoHeader& GetPlaygoHeader() { return playgoHeader; } diff --git a/src/core/file_format/psf.cpp b/src/core/file_format/psf.cpp index 0502f29d2..7e0ffc9a3 100644 --- a/src/core/file_format/psf.cpp +++ b/src/core/file_format/psf.cpp @@ -21,8 +21,13 @@ static inline u32 get_max_size(std::string_view key, u32 default_value) { } bool PSF::Open(const std::filesystem::path& filepath) { + using namespace std::chrono; if (std::filesystem::exists(filepath)) { - last_write = std::filesystem::last_write_time(filepath); + const auto t = std::filesystem::last_write_time(filepath); + const auto rel = + duration_cast(t - std::filesystem::file_time_type::clock::now()).count(); + const auto tp = system_clock::to_time_t(system_clock::now() + seconds{rel}); + last_write = system_clock::from_time_t(tp); } Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Read); @@ -99,7 +104,7 @@ bool PSF::Encode(const std::filesystem::path& filepath) const { return false; } - last_write = std::filesystem::file_time_type::clock::now(); + last_write = std::chrono::system_clock::now(); const auto psf_buffer = Encode(); const size_t written = file.Write(psf_buffer); diff --git a/src/core/file_format/psf.h b/src/core/file_format/psf.h index 6f35fa69a..0f6621315 100644 --- a/src/core/file_format/psf.h +++ b/src/core/file_format/psf.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include #include @@ -71,7 +72,7 @@ public: void AddString(std::string key, std::string value, bool update = false); void AddInteger(std::string key, s32 value, bool update = false); - [[nodiscard]] std::filesystem::file_time_type GetLastWrite() const { + [[nodiscard]] std::chrono::system_clock::time_point GetLastWrite() const { return last_write; } @@ -80,7 +81,7 @@ public: } private: - mutable std::filesystem::file_time_type last_write; + mutable std::chrono::system_clock::time_point last_write; std::vector entry_list; diff --git a/src/core/file_format/splash.cpp b/src/core/file_format/splash.cpp index 5e06c912d..b68702157 100644 --- a/src/core/file_format/splash.cpp +++ b/src/core/file_format/splash.cpp @@ -5,13 +5,9 @@ #include "common/assert.h" #include "common/io_file.h" +#include "common/stb.h" #include "splash.h" -#define STB_IMAGE_IMPLEMENTATION -#define STBI_ONLY_PNG -#define STBI_NO_STDIO -#include "externals/stb_image.h" - bool Splash::Open(const std::filesystem::path& filepath) { ASSERT_MSG(filepath.stem().string() != "png", "Unexpected file format passed"); diff --git a/src/core/file_sys/file.cpp b/src/core/file_sys/file.cpp new file mode 100644 index 000000000..be6bc76bb --- /dev/null +++ b/src/core/file_sys/file.cpp @@ -0,0 +1,114 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "common/error.h" +#include "core/file_sys/file.h" + +#ifdef _WIN64 +#include +#include +#include +#include "common/ntapi.h" +#endif + +namespace Core::FileSys { + +#ifdef _WIN64 + +int File::Open(const std::filesystem::path& path, Common::FS::FileAccessMode f_access) { + DWORD access{}; + if (f_access == Common::FS::FileAccessMode::Read) { + access = GENERIC_READ; + } else if (f_access == Common::FS::FileAccessMode::Write) { + access = GENERIC_WRITE; + } else if (f_access == Common::FS::FileAccessMode::ReadWrite) { + access = GENERIC_READ | GENERIC_WRITE; + } else { + UNREACHABLE(); + } + handle = CreateFileW(path.native().c_str(), access, FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (handle == INVALID_HANDLE_VALUE) { + return ENOENT; + } +} + +s64 File::Read(void* buf, size_t nbytes) { + DWORD bytes_read; + if (!ReadFile(handle, buf, nbytes, &bytes_read, nullptr)) { + UNREACHABLE_MSG("ReadFile failed: {}", Common::GetLastErrorMsg()); + } + return bytes_read; +} + +s64 File::Pread(void* buf, size_t nbytes, s64 offset) { + OVERLAPPED ol{}; + ol.Offset = offset; + ol.OffsetHigh = offset >> 32; + DWORD bytes_read; + if (!ReadFile(handle, buf, nbytes, &bytes_read, &ol)) { + UNREACHABLE_MSG("ReadFile failed: {}", Common::GetLastErrorMsg()); + } + return bytes_read; +} + +s64 File::Write(const void* buf, size_t nbytes) { + DWORD bytes_written; + if (!WriteFile(handle, buf, nbytes, &bytes_written, nullptr)) { + UNREACHABLE_MSG("WriteFile failed: {}", Common::GetLastErrorMsg()); + } + return bytes_written; +} + +s64 File::Pwrite(const void* buf, size_t nbytes, s64 offset) { + OVERLAPPED ol{}; + ol.Offset = offset; + ol.OffsetHigh = offset >> 32; + DWORD bytes_written; + if (!WriteFile(handle, buf, nbytes, &bytes_written, &ol)) { + UNREACHABLE_MSG("WriteFile failed: {}", Common::GetLastErrorMsg()); + } + return bytes_written; +} + +void File::SetSize(s64 size) { + Lseek(size, 0); + if (!SetEndOfFile(handle)) { + UNREACHABLE_MSG("SetEndOfFile failed: {}", Common::GetLastErrorMsg()); + } +} + +void File::Flush() { + FlushFileBuffers(handle); +} + +s64 File::Lseek(s64 offset, int whence) { + LARGE_INTEGER new_file_pointer; + DWORD origin{}; + if (whence == 0) { + origin = FILE_BEGIN; + } else if (whence == 1) { + origin = FILE_CURRENT; + } else if (whence == 2) { + origin = FILE_END; + } + if (!SetFilePointerEx(handle, LARGE_INTEGER{.QuadPart = offset}, &new_file_pointer, origin)) { + UNREACHABLE_MSG("SetFilePointerEx failed: {}", Common::GetLastErrorMsg()); + } + return new_file_pointer.QuadPart; +} + +void File::Unlink() { + FILE_DISPOSITION_INFORMATION disposition; + IO_STATUS_BLOCK iosb; + disposition.DeleteFile = TRUE; + NtSetInformationFile(handle, &iosb, &disposition, sizeof(disposition), + FileDispositionInformation); +} + +#else + +#endif + +} // namespace Core::FileSys \ No newline at end of file diff --git a/src/core/file_sys/fs.cpp b/src/core/file_sys/fs.cpp index 769940cf0..0fdbb2783 100644 --- a/src/core/file_sys/fs.cpp +++ b/src/core/file_sys/fs.cpp @@ -4,12 +4,12 @@ #include #include "common/config.h" #include "common/string_util.h" +#include "core/devices/logger.h" +#include "core/devices/nop_device.h" #include "core/file_sys/fs.h" namespace Core::FileSys { -constexpr int RESERVED_HANDLES = 3; // First 3 handles are stdin,stdout,stderr - void MntPoints::Mount(const std::filesystem::path& host_folder, const std::string& guest_folder, bool read_only) { std::scoped_lock lock{m_mutex}; @@ -135,7 +135,6 @@ int HandleTable::CreateHandle() { std::scoped_lock lock{m_mutex}; auto* file = new File{}; - file->is_directory = false; file->is_opened = false; int existingFilesNum = m_files.size(); @@ -143,23 +142,23 @@ int HandleTable::CreateHandle() { for (int index = 0; index < existingFilesNum; index++) { if (m_files.at(index) == nullptr) { m_files[index] = file; - return index + RESERVED_HANDLES; + return index; } } m_files.push_back(file); - return m_files.size() + RESERVED_HANDLES - 1; + return m_files.size() - 1; } void HandleTable::DeleteHandle(int d) { std::scoped_lock lock{m_mutex}; - delete m_files.at(d - RESERVED_HANDLES); - m_files[d - RESERVED_HANDLES] = nullptr; + delete m_files.at(d); + m_files[d] = nullptr; } File* HandleTable::GetFile(int d) { std::scoped_lock lock{m_mutex}; - return m_files.at(d - RESERVED_HANDLES); + return m_files.at(d); } File* HandleTable::GetFile(const std::filesystem::path& host_name) { @@ -171,4 +170,20 @@ File* HandleTable::GetFile(const std::filesystem::path& host_name) { return nullptr; } +void HandleTable::CreateStdHandles() { + auto setup = [this](const char* path, auto* device) { + int fd = CreateHandle(); + auto* file = GetFile(fd); + file->is_opened = true; + file->type = FileType::Device; + file->m_guest_name = path; + file->device = + std::shared_ptr{reinterpret_cast(device)}; + }; + // order matters + setup("/dev/stdin", new Devices::NopDevice(0)); // stdin + setup("/dev/stdout", new Devices::Logger("stdout", false)); // stdout + setup("/dev/stderr", new Devices::Logger("stderr", true)); // stderr +} + } // namespace Core::FileSys diff --git a/src/core/file_sys/fs.h b/src/core/file_sys/fs.h index eeaeaf781..b0153c162 100644 --- a/src/core/file_sys/fs.h +++ b/src/core/file_sys/fs.h @@ -9,6 +9,7 @@ #include #include #include "common/io_file.h" +#include "core/devices/base_device.h" namespace Core::FileSys { @@ -55,15 +56,22 @@ struct DirEntry { bool isFile; }; +enum class FileType { + Regular, // standard file + Directory, + Device, +}; + struct File { std::atomic_bool is_opened{}; - std::atomic_bool is_directory{}; + std::atomic type{FileType::Regular}; std::filesystem::path m_host_name; std::string m_guest_name; Common::FS::IOFile f; std::vector dirents; u32 dirents_index; std::mutex m_mutex; + std::shared_ptr device; // only valid for type == Device }; class HandleTable { @@ -76,6 +84,8 @@ public: File* GetFile(int d); File* GetFile(const std::filesystem::path& host_name); + void CreateStdHandles(); + private: std::vector m_files; std::mutex m_mutex; diff --git a/src/core/libraries/ajm/ajm.cpp b/src/core/libraries/ajm/ajm.cpp index 2396669b6..3184fa64f 100644 --- a/src/core/libraries/ajm/ajm.cpp +++ b/src/core/libraries/ajm/ajm.cpp @@ -9,7 +9,7 @@ #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" -#include +#include namespace Libraries::Ajm { @@ -19,7 +19,7 @@ constexpr int ORBIS_AJM_CHANNELMASK_QUAD = 0x0033; constexpr int ORBIS_AJM_CHANNELMASK_5POINT1 = 0x060F; constexpr int ORBIS_AJM_CHANNELMASK_7POINT1 = 0x063F; -static std::unique_ptr context{}; +static std::unordered_map> contexts{}; u32 GetChannelMask(u32 num_channels) { switch (num_channels) { @@ -40,7 +40,13 @@ u32 GetChannelMask(u32 num_channels) { int PS4_SYSV_ABI sceAjmBatchCancel(const u32 context_id, const u32 batch_id) { LOG_INFO(Lib_Ajm, "called context_id = {} batch_id = {}", context_id, batch_id); - return context->BatchCancel(batch_id); + + auto it = contexts.find(context_id); + if (it == contexts.end()) { + return ORBIS_AJM_ERROR_INVALID_CONTEXT; + } + + return it->second->BatchCancel(batch_id); } int PS4_SYSV_ABI sceAjmBatchErrorDump() { @@ -90,14 +96,26 @@ int PS4_SYSV_ABI sceAjmBatchStartBuffer(u32 context_id, u8* p_batch, u32 batch_s u32* out_batch_id) { LOG_TRACE(Lib_Ajm, "called context = {}, batch_size = {:#x}, priority = {}", context_id, batch_size, priority); - return context->BatchStartBuffer(p_batch, batch_size, priority, batch_error, out_batch_id); + + auto it = contexts.find(context_id); + if (it == contexts.end()) { + return ORBIS_AJM_ERROR_INVALID_CONTEXT; + } + + return it->second->BatchStartBuffer(p_batch, batch_size, priority, batch_error, out_batch_id); } int PS4_SYSV_ABI sceAjmBatchWait(const u32 context_id, const u32 batch_id, const u32 timeout, AjmBatchError* const batch_error) { LOG_TRACE(Lib_Ajm, "called context = {}, batch_id = {}, timeout = {}", context_id, batch_id, timeout); - return context->BatchWait(batch_id, timeout, batch_error); + + auto it = contexts.find(context_id); + if (it == contexts.end()) { + return ORBIS_AJM_ERROR_INVALID_CONTEXT; + } + + return it->second->BatchWait(batch_id, timeout, batch_error); } int PS4_SYSV_ABI sceAjmDecAt9ParseConfigData() { @@ -117,12 +135,12 @@ int PS4_SYSV_ABI sceAjmFinalize() { int PS4_SYSV_ABI sceAjmInitialize(s64 reserved, u32* p_context_id) { LOG_INFO(Lib_Ajm, "called reserved = {}", reserved); - ASSERT_MSG(context == nullptr, "Multiple contexts are currently unsupported."); if (p_context_id == nullptr || reserved != 0) { return ORBIS_AJM_ERROR_INVALID_PARAMETER; } - *p_context_id = 1; - context = std::make_unique(); + u32 id = contexts.size() + 1; + *p_context_id = id; + contexts.emplace(id, std::make_unique()); return ORBIS_OK; } @@ -135,12 +153,24 @@ int PS4_SYSV_ABI sceAjmInstanceCreate(u32 context_id, AjmCodecType codec_type, AjmInstanceFlags flags, u32* out_instance) { LOG_INFO(Lib_Ajm, "called context = {}, codec_type = {}, flags = {:#x}", context_id, magic_enum::enum_name(codec_type), flags.raw); - return context->InstanceCreate(codec_type, flags, out_instance); + + auto it = contexts.find(context_id); + if (it == contexts.end()) { + return ORBIS_AJM_ERROR_INVALID_CONTEXT; + } + + return it->second->InstanceCreate(codec_type, flags, out_instance); } int PS4_SYSV_ABI sceAjmInstanceDestroy(u32 context_id, u32 instance_id) { LOG_INFO(Lib_Ajm, "called context = {}, instance = {}", context_id, instance_id); - return context->InstanceDestroy(instance_id); + + auto it = contexts.find(context_id); + if (it == contexts.end()) { + return ORBIS_AJM_ERROR_INVALID_CONTEXT; + } + + return it->second->InstanceDestroy(instance_id); } int PS4_SYSV_ABI sceAjmInstanceExtend() { @@ -168,7 +198,13 @@ int PS4_SYSV_ABI sceAjmModuleRegister(u32 context_id, AjmCodecType codec_type, s if (reserved != 0) { return ORBIS_AJM_ERROR_INVALID_PARAMETER; } - return context->ModuleRegister(codec_type); + + auto it = contexts.find(context_id); + if (it == contexts.end()) { + return ORBIS_AJM_ERROR_INVALID_CONTEXT; + } + + return it->second->ModuleRegister(codec_type); } int PS4_SYSV_ABI sceAjmModuleUnregister() { diff --git a/src/core/libraries/ajm/ajm_context.cpp b/src/core/libraries/ajm/ajm_context.cpp index e30e1c478..09255110c 100644 --- a/src/core/libraries/ajm/ajm_context.cpp +++ b/src/core/libraries/ajm/ajm_context.cpp @@ -3,6 +3,7 @@ #include "common/assert.h" #include "common/logging/log.h" +#include "common/thread.h" #include "core/libraries/ajm/ajm.h" #include "core/libraries/ajm/ajm_at9.h" #include "core/libraries/ajm/ajm_context.h" @@ -53,6 +54,7 @@ s32 AjmContext::ModuleRegister(AjmCodecType type) { } void AjmContext::WorkerThread(std::stop_token stop) { + Common::SetCurrentThreadName("shadPS4:AjmWorker"); while (!stop.stop_requested()) { auto batch = batch_queue.PopWait(stop); if (batch != nullptr) { diff --git a/src/core/libraries/ajm/ajm_instance.cpp b/src/core/libraries/ajm/ajm_instance.cpp index 4e04eea74..ea7fd5617 100644 --- a/src/core/libraries/ajm/ajm_instance.cpp +++ b/src/core/libraries/ajm/ajm_instance.cpp @@ -5,7 +5,7 @@ #include "core/libraries/ajm/ajm_instance.h" #include "core/libraries/ajm/ajm_mp3.h" -#include +#include namespace Libraries::Ajm { diff --git a/src/core/libraries/ajm/ajm_mp3.cpp b/src/core/libraries/ajm/ajm_mp3.cpp index 3b464238d..2c572a01b 100644 --- a/src/core/libraries/ajm/ajm_mp3.cpp +++ b/src/core/libraries/ajm/ajm_mp3.cpp @@ -12,6 +12,8 @@ extern "C" { #include } +#include "common/support/avdec.h" + namespace Libraries::Ajm { // Following tables have been reversed from AJM library diff --git a/src/core/libraries/app_content/app_content.cpp b/src/core/libraries/app_content/app_content.cpp index f912639eb..ca3cdad39 100644 --- a/src/core/libraries/app_content/app_content.cpp +++ b/src/core/libraries/app_content/app_content.cpp @@ -6,34 +6,30 @@ #include "app_content.h" #include "common/assert.h" #include "common/config.h" -#include "common/io_file.h" #include "common/logging/log.h" -#include "common/path_util.h" #include "common/singleton.h" -#include "common/string_util.h" #include "core/file_format/psf.h" #include "core/file_sys/fs.h" -#include "core/libraries/error_codes.h" +#include "core/libraries/app_content/app_content_error.h" #include "core/libraries/libs.h" namespace Libraries::AppContent { -int32_t addcont_count = 0; - struct AddContInfo { - char entitlementLabel[ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE]; + char entitlement_label[ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE]; OrbisAppContentAddcontDownloadStatus status; OrbisAppContentGetEntitlementKey key; }; -std::array addcont_info = {{ +static std::array addcont_info = {{ {"0000000000000000", - ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_INSTALLED, + OrbisAppContentAddcontDownloadStatus::Installed, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, }}; -std::string title_id; +static s32 addcont_count = 0; +static std::string title_id; int PS4_SYSV_ABI _Z5dummyv() { LOG_ERROR(Lib_AppContent, "(STUBBED) called"); @@ -64,12 +60,11 @@ int PS4_SYSV_ABI sceAppContentAddcontMount(u32 service_label, auto* mnt = Common::Singleton::Instance(); for (int i = 0; i < addcont_count; i++) { - if (strncmp(entitlement_label->data, addcont_info[i].entitlementLabel, + if (strncmp(entitlement_label->data, addcont_info[i].entitlement_label, ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE - 1) != 0) { continue; } - - if (addcont_info[i].status != ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_INSTALLED) { + if (addcont_info[i].status != OrbisAppContentAddcontDownloadStatus::Installed) { return ORBIS_APP_CONTENT_ERROR_NOT_FOUND; } @@ -170,14 +165,14 @@ int PS4_SYSV_ABI sceAppContentGetAddcontInfo(u32 service_label, } for (auto i = 0; i < addcont_count; i++) { - if (strncmp(entitlementLabel->data, addcont_info[i].entitlementLabel, + if (strncmp(entitlementLabel->data, addcont_info[i].entitlement_label, ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE - 1) != 0) { continue; } LOG_INFO(Lib_AppContent, "found DLC {}", entitlementLabel->data); - strncpy(info->entitlement_label.data, addcont_info[i].entitlementLabel, + strncpy(info->entitlement_label.data, addcont_info[i].entitlement_label, ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE); info->status = addcont_info[i].status; return ORBIS_OK; @@ -202,7 +197,7 @@ int PS4_SYSV_ABI sceAppContentGetAddcontInfoList(u32 service_label, int dlcs_to_list = addcont_count < list_num ? addcont_count : list_num; for (int i = 0; i < dlcs_to_list; i++) { - strncpy(list[i].entitlement_label.data, addcont_info[i].entitlementLabel, + strncpy(list[i].entitlement_label.data, addcont_info[i].entitlement_label, ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE); list[i].status = addcont_info[i].status; } @@ -224,7 +219,7 @@ int PS4_SYSV_ABI sceAppContentGetEntitlementKey( } for (int i = 0; i < addcont_count; i++) { - if (strncmp(entitlement_label->data, addcont_info[i].entitlementLabel, + if (strncmp(entitlement_label->data, addcont_info[i].entitlement_label, ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE - 1) != 0) { continue; } @@ -252,21 +247,19 @@ int PS4_SYSV_ABI sceAppContentInitialize(const OrbisAppContentInitParam* initPar } else { UNREACHABLE_MSG("Failed to get TITLE_ID"); } - auto addon_path = addons_dir / title_id; - if (std::filesystem::exists(addon_path)) { - for (const auto& entry : std::filesystem::directory_iterator(addon_path)) { - if (entry.is_directory()) { - auto entitlement_label = entry.path().filename().string(); - - AddContInfo info{}; - info.status = ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_INSTALLED; - strcpy(info.entitlementLabel, entitlement_label.c_str()); - - addcont_info[addcont_count++] = info; - } - } + const auto addon_path = addons_dir / title_id; + if (!std::filesystem::exists(addon_path)) { + return ORBIS_OK; } + for (const auto& entry : std::filesystem::directory_iterator(addon_path)) { + if (entry.is_directory()) { + auto entitlement_label = entry.path().filename().string(); + auto& info = addcont_info[addcont_count++]; + info.status = OrbisAppContentAddcontDownloadStatus::Installed; + entitlement_label.copy(info.entitlement_label, sizeof(info.entitlement_label)); + } + } return ORBIS_OK; } @@ -314,9 +307,11 @@ int PS4_SYSV_ABI sceAppContentTemporaryDataMount() { int PS4_SYSV_ABI sceAppContentTemporaryDataMount2(OrbisAppContentTemporaryDataOption option, OrbisAppContentMountPoint* mountPoint) { - if (mountPoint == nullptr) + if (mountPoint == nullptr) { return ORBIS_APP_CONTENT_ERROR_PARAMETER; - strncpy(mountPoint->data, "/temp0", 16); + } + static constexpr std::string_view TmpMount = "/temp0"; + TmpMount.copy(mountPoint->data, sizeof(mountPoint->data)); LOG_INFO(Lib_AppContent, "sceAppContentTemporaryDataMount2: option = {}, mountPoint = {}", option, mountPoint->data); return ORBIS_OK; diff --git a/src/core/libraries/app_content/app_content.h b/src/core/libraries/app_content/app_content.h index a16da5b40..f41f7dccf 100644 --- a/src/core/libraries/app_content/app_content.h +++ b/src/core/libraries/app_content/app_content.h @@ -30,7 +30,7 @@ struct OrbisAppContentBootParam { char reserved2[32]; }; -typedef u32 OrbisAppContentTemporaryDataOption; +using OrbisAppContentTemporaryDataOption = u32; constexpr int ORBIS_APP_CONTENT_MOUNTPOINT_DATA_MAXSIZE = 16; @@ -44,12 +44,12 @@ constexpr int ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE = 17; constexpr int ORBIS_APP_CONTENT_ENTITLEMENT_KEY_SIZE = 16; constexpr int ORBIS_APP_CONTENT_INFO_LIST_MAX_SIZE = 2500; -enum OrbisAppContentAddcontDownloadStatus : u32 { - ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_NO_EXTRA_DATA = 0, - ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_NO_IN_QUEUE = 1, - ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_DOWNLOADING = 2, - ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_DOWNLOAD_SUSPENDED = 3, - ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_INSTALLED = 4 +enum class OrbisAppContentAddcontDownloadStatus : u32 { + NoExtraData = 0, + NoInQueue = 1, + Downloading = 2, + DownloadSuspended = 3, + Installed = 4 }; struct OrbisNpUnifiedEntitlementLabel { @@ -57,11 +57,11 @@ struct OrbisNpUnifiedEntitlementLabel { char padding[3]; }; -typedef u32 OrbisAppContentAppParamId; +using OrbisAppContentAppParamId = u32; struct OrbisAppContentAddcontInfo { OrbisNpUnifiedEntitlementLabel entitlement_label; - u32 status; + OrbisAppContentAddcontDownloadStatus status; }; struct OrbisAppContentGetEntitlementKey { @@ -119,4 +119,4 @@ int PS4_SYSV_ABI sceAppContentGetAddcontInfoListByIroTag(); int PS4_SYSV_ABI sceAppContentGetDownloadedStoreCountry(); void RegisterlibSceAppContent(Core::Loader::SymbolsResolver* sym); -} // namespace Libraries::AppContent \ No newline at end of file +} // namespace Libraries::AppContent diff --git a/src/core/libraries/app_content/app_content_error.h b/src/core/libraries/app_content/app_content_error.h new file mode 100644 index 000000000..3a582b998 --- /dev/null +++ b/src/core/libraries/app_content/app_content_error.h @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// AppContent library +constexpr int ORBIS_APP_CONTENT_ERROR_PARAMETER = 0x80D90002; +constexpr int ORBIS_APP_CONTENT_ERROR_DRM_NO_ENTITLEMENT = 0x80D90007; +constexpr int ORBIS_APP_CONTENT_ERROR_NOT_FOUND = 0x80D90005; diff --git a/src/core/libraries/audio/audioout.cpp b/src/core/libraries/audio/audioout.cpp index 778d777c2..78b04cc90 100644 --- a/src/core/libraries/audio/audioout.cpp +++ b/src/core/libraries/audio/audioout.cpp @@ -2,32 +2,32 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include -#include +#include -#include "audio_core/sdl_audio.h" #include "common/assert.h" #include "common/logging/log.h" #include "core/libraries/audio/audioout.h" -#include "core/libraries/error_codes.h" +#include "core/libraries/audio/audioout_error.h" +#include "core/libraries/audio/sdl_audio.h" #include "core/libraries/libs.h" namespace Libraries::AudioOut { -static std::unique_ptr audio; +static std::unique_ptr audio; -static std::string_view GetAudioOutPort(u32 port) { +static std::string_view GetAudioOutPort(OrbisAudioOutPort port) { switch (port) { - case ORBIS_AUDIO_OUT_PORT_TYPE_MAIN: + case OrbisAudioOutPort::Main: return "MAIN"; - case ORBIS_AUDIO_OUT_PORT_TYPE_BGM: + case OrbisAudioOutPort::Bgm: return "BGM"; - case ORBIS_AUDIO_OUT_PORT_TYPE_VOICE: + case OrbisAudioOutPort::Voice: return "VOICE"; - case ORBIS_AUDIO_OUT_PORT_TYPE_PERSONAL: + case OrbisAudioOutPort::Personal: return "PERSONAL"; - case ORBIS_AUDIO_OUT_PORT_TYPE_PADSPK: + case OrbisAudioOutPort::Padspk: return "PADSPK"; - case ORBIS_AUDIO_OUT_PORT_TYPE_AUX: + case OrbisAudioOutPort::Aux: return "AUX"; default: return "INVALID"; @@ -36,21 +36,21 @@ static std::string_view GetAudioOutPort(u32 port) { static std::string_view GetAudioOutParamFormat(OrbisAudioOutParamFormat param) { switch (param) { - case ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_MONO: + case OrbisAudioOutParamFormat::S16Mono: return "S16_MONO"; - case ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_STEREO: + case OrbisAudioOutParamFormat::S16Stereo: return "S16_STEREO"; - case ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_8CH: + case OrbisAudioOutParamFormat::S16_8CH: return "S16_8CH"; - case ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_MONO: + case OrbisAudioOutParamFormat::FloatMono: return "FLOAT_MONO"; - case ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_STEREO: + case OrbisAudioOutParamFormat::FloatStereo: return "FLOAT_STEREO"; - case ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_8CH: + case OrbisAudioOutParamFormat::Float_8CH: return "FLOAT_8CH"; - case ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_8CH_STD: + case OrbisAudioOutParamFormat::S16_8CH_Std: return "S16_8CH_STD"; - case ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_8CH_STD: + case OrbisAudioOutParamFormat::Float_8CH_Std: return "FLOAT_8CH_STD"; default: return "INVALID"; @@ -59,11 +59,11 @@ static std::string_view GetAudioOutParamFormat(OrbisAudioOutParamFormat param) { static std::string_view GetAudioOutParamAttr(OrbisAudioOutParamAttr attr) { switch (attr) { - case ORBIS_AUDIO_OUT_PARAM_ATTR_NONE: + case OrbisAudioOutParamAttr::None: return "NONE"; - case ORBIS_AUDIO_OUT_PARAM_ATTR_RESTRICTED: + case OrbisAudioOutParamAttr::Restricted: return "RESTRICTED"; - case ORBIS_AUDIO_OUT_PARAM_ATTR_MIX_TO_MAIN: + case OrbisAudioOutParamAttr::MixToMain: return "MIX_TO_MAIN"; default: return "INVALID"; @@ -180,29 +180,23 @@ int PS4_SYSV_ABI sceAudioOutGetPortState(s32 handle, OrbisAudioOutPortState* sta return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; } - int type = 0; - int channels_num = 0; - - if (const auto err = audio->AudioOutGetStatus(handle, &type, &channels_num); err != ORBIS_OK) { - return err; - } - + const auto [type, channels_num] = audio->GetStatus(handle); state->rerouteCounter = 0; - state->volume = 127; // max volume + state->volume = 127; switch (type) { - case ORBIS_AUDIO_OUT_PORT_TYPE_MAIN: - case ORBIS_AUDIO_OUT_PORT_TYPE_BGM: - case ORBIS_AUDIO_OUT_PORT_TYPE_VOICE: + case OrbisAudioOutPort::Main: + case OrbisAudioOutPort::Bgm: + case OrbisAudioOutPort::Voice: state->output = 1; state->channel = (channels_num > 2 ? 2 : channels_num); break; - case ORBIS_AUDIO_OUT_PORT_TYPE_PERSONAL: - case ORBIS_AUDIO_OUT_PORT_TYPE_PADSPK: + case OrbisAudioOutPort::Personal: + case OrbisAudioOutPort::Padspk: state->output = 4; state->channel = 1; break; - case ORBIS_AUDIO_OUT_PORT_TYPE_AUX: + case OrbisAudioOutPort::Aux: state->output = 0; state->channel = 0; break; @@ -243,7 +237,7 @@ int PS4_SYSV_ABI sceAudioOutInit() { if (audio != nullptr) { return ORBIS_AUDIO_OUT_ERROR_ALREADY_INIT; } - audio = std::make_unique(); + audio = std::make_unique(); return ORBIS_OK; } @@ -287,7 +281,8 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id, user_id, GetAudioOutPort(port_type), index, length, sample_rate, GetAudioOutParamFormat(param_type.data_format), GetAudioOutParamAttr(param_type.attributes)); - if ((port_type < 0 || port_type > 4) && (port_type != 127)) { + if ((port_type < OrbisAudioOutPort::Main || port_type > OrbisAudioOutPort::Padspk) && + (port_type != OrbisAudioOutPort::Aux)) { LOG_ERROR(Lib_AudioOut, "Invalid port type"); return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT_TYPE; } @@ -303,18 +298,19 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id, if (index != 0) { LOG_ERROR(Lib_AudioOut, "index is not valid !=0 {}", index); } - OrbisAudioOutParamFormat format = param_type.data_format; - if (format < 0 || format > 7) { + const auto format = param_type.data_format.Value(); + if (format < OrbisAudioOutParamFormat::S16Mono || + format > OrbisAudioOutParamFormat::Float_8CH_Std) { LOG_ERROR(Lib_AudioOut, "Invalid format"); return ORBIS_AUDIO_OUT_ERROR_INVALID_FORMAT; } - OrbisAudioOutParamAttr attr = param_type.attributes; - if (attr < 0 || attr > 2) { + const auto attr = param_type.attributes; + if (attr < OrbisAudioOutParamAttr::None || attr > OrbisAudioOutParamAttr::MixToMain) { // TODO Handle attributes in output audio device LOG_ERROR(Lib_AudioOut, "Invalid format attribute"); return ORBIS_AUDIO_OUT_ERROR_INVALID_FORMAT; } - return audio->AudioOutOpen(port_type, length, sample_rate, format); + return audio->Open(port_type, length, sample_rate, format); } int PS4_SYSV_ABI sceAudioOutOpenEx() { @@ -330,7 +326,7 @@ s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, const void* ptr) { // Nothing to output return ORBIS_OK; } - return audio->AudioOutOutput(handle, ptr); + return audio->Output(handle, ptr); } int PS4_SYSV_ABI sceAudioOutOutputs(OrbisAudioOutOutputParam* param, u32 num) { @@ -435,7 +431,7 @@ s32 PS4_SYSV_ABI sceAudioOutSetVolume(s32 handle, s32 flag, s32* vol) { if (handle < 1 || handle > SCE_AUDIO_OUT_NUM_PORTS) { return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; } - return audio->AudioOutSetVolume(handle, flag, vol); + return audio->SetVolume(handle, flag, vol); } int PS4_SYSV_ABI sceAudioOutSetVolumeDown() { diff --git a/src/core/libraries/audio/audioout.h b/src/core/libraries/audio/audioout.h index 95cfc1707..e8e718b87 100644 --- a/src/core/libraries/audio/audioout.h +++ b/src/core/libraries/audio/audioout.h @@ -9,46 +9,36 @@ namespace Libraries::AudioOut { -constexpr int SCE_AUDIO_OUT_VOLUME_0DB = 32768; // max volume value - -// main up to 8 ports, BGM 1 port, voice up to 4 ports, +// Main up to 8 ports, BGM 1 port, voice up to 4 ports, // personal up to 4 ports, padspk up to 5 ports, aux 1 port constexpr int SCE_AUDIO_OUT_NUM_PORTS = 22; +constexpr int SCE_AUDIO_OUT_VOLUME_0DB = 32768; // max volume value -enum OrbisAudioOutPort { - ORBIS_AUDIO_OUT_PORT_TYPE_MAIN = 0, - ORBIS_AUDIO_OUT_PORT_TYPE_BGM = 1, - ORBIS_AUDIO_OUT_PORT_TYPE_VOICE = 2, - ORBIS_AUDIO_OUT_PORT_TYPE_PERSONAL = 3, - ORBIS_AUDIO_OUT_PORT_TYPE_PADSPK = 4, - ORBIS_AUDIO_OUT_PORT_TYPE_AUX = 127 +enum class OrbisAudioOutPort { Main = 0, Bgm = 1, Voice = 2, Personal = 3, Padspk = 4, Aux = 127 }; + +enum class OrbisAudioOutParamFormat { + S16Mono = 0, + S16Stereo = 1, + S16_8CH = 2, + FloatMono = 3, + FloatStereo = 4, + Float_8CH = 5, + S16_8CH_Std = 6, + Float_8CH_Std = 7 }; -enum OrbisAudioOutParamFormat { - ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_MONO = 0, - ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_STEREO = 1, - ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_8CH = 2, - ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_MONO = 3, - ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_STEREO = 4, - ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_8CH = 5, - ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_8CH_STD = 6, - ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_8CH_STD = 7 +enum class OrbisAudioOutParamAttr { + None = 0, + Restricted = 1, + MixToMain = 2, }; -enum OrbisAudioOutParamAttr { - ORBIS_AUDIO_OUT_PARAM_ATTR_NONE = 0, - ORBIS_AUDIO_OUT_PARAM_ATTR_RESTRICTED = 1, - ORBIS_AUDIO_OUT_PARAM_ATTR_MIX_TO_MAIN = 2, -}; - -struct OrbisAudioOutParamExtendedInformation { - union { - BitField<0, 8, OrbisAudioOutParamFormat> data_format; - BitField<8, 8, u32> reserve0; - BitField<16, 4, OrbisAudioOutParamAttr> attributes; - BitField<20, 10, u32> reserve1; - BitField<31, 1, u32> unused; - }; +union OrbisAudioOutParamExtendedInformation { + BitField<0, 8, OrbisAudioOutParamFormat> data_format; + BitField<8, 8, u32> reserve0; + BitField<16, 4, OrbisAudioOutParamAttr> attributes; + BitField<20, 10, u32> reserve1; + BitField<31, 1, u32> unused; }; struct OrbisAudioOutOutputParam { diff --git a/src/core/libraries/audio/audioout_error.h b/src/core/libraries/audio/audioout_error.h new file mode 100644 index 000000000..7642e87e7 --- /dev/null +++ b/src/core/libraries/audio/audioout_error.h @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// AudioOut library +constexpr int ORBIS_AUDIO_OUT_ERROR_NOT_OPENED = 0x80260001; +constexpr int ORBIS_AUDIO_OUT_ERROR_BUSY = 0x80260002; +constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_PORT = 0x80260003; +constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_POINTER = 0x80260004; +constexpr int ORBIS_AUDIO_OUT_ERROR_PORT_FULL = 0x80260005; +constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_SIZE = 0x80260006; +constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_FORMAT = 0x80260007; +constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_SAMPLE_FREQ = 0x80260008; +constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_VOLUME = 0x80260009; +constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_PORT_TYPE = 0x8026000A; +constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_CONF_TYPE = 0x8026000C; +constexpr int ORBIS_AUDIO_OUT_ERROR_OUT_OF_MEMORY = 0x8026000D; +constexpr int ORBIS_AUDIO_OUT_ERROR_ALREADY_INIT = 0x8026000E; +constexpr int ORBIS_AUDIO_OUT_ERROR_NOT_INIT = 0x8026000F; +constexpr int ORBIS_AUDIO_OUT_ERROR_MEMORY = 0x80260010; +constexpr int ORBIS_AUDIO_OUT_ERROR_SYSTEM_RESOURCE = 0x80260011; +constexpr int ORBIS_AUDIO_OUT_ERROR_TRANS_EVENT = 0x80260012; +constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_FLAG = 0x80260013; +constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_MIXLEVEL = 0x80260014; +constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_ARG = 0x80260015; +constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_PARAM = 0x80260016; +constexpr int ORBIS_AUDIO_OUT_ERROR_MASTERING_FATAL = 0x80260200; +constexpr int ORBIS_AUDIO_OUT_ERROR_MASTERING_INVALID_API_PARAM = 0x80260201; +constexpr int ORBIS_AUDIO_OUT_ERROR_MASTERING_INVALID_CONFIG = 0x80260202; +constexpr int ORBIS_AUDIO_OUT_ERROR_MASTERING_NOT_INITIALIZED = 0x80260203; +constexpr int ORBIS_AUDIO_OUT_ERROR_MASTERING_INVALID_STATES_ID = 0x80260204; diff --git a/src/core/libraries/audio/sdl_audio.cpp b/src/core/libraries/audio/sdl_audio.cpp new file mode 100644 index 000000000..8cc823abe --- /dev/null +++ b/src/core/libraries/audio/sdl_audio.cpp @@ -0,0 +1,141 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include + +#include "common/assert.h" +#include "core/libraries/audio/audioout_error.h" +#include "core/libraries/audio/sdl_audio.h" + +namespace Libraries::AudioOut { + +constexpr int AUDIO_STREAM_BUFFER_THRESHOLD = 65536; // Define constant for buffer threshold + +s32 SDLAudioOut::Open(OrbisAudioOutPort type, u32 samples_num, u32 freq, + OrbisAudioOutParamFormat format) { + std::scoped_lock lock{m_mutex}; + const auto port = std::ranges::find(ports_out, false, &PortOut::is_open); + if (port == ports_out.end()) { + LOG_ERROR(Lib_AudioOut, "Audio ports are full"); + return ORBIS_AUDIO_OUT_ERROR_PORT_FULL; + } + + port->is_open = true; + port->type = type; + port->samples_num = samples_num; + port->freq = freq; + port->format = format; + SDL_AudioFormat sampleFormat; + switch (format) { + case OrbisAudioOutParamFormat::S16Mono: + sampleFormat = SDL_AUDIO_S16; + port->channels_num = 1; + port->sample_size = 2; + break; + case OrbisAudioOutParamFormat::FloatMono: + sampleFormat = SDL_AUDIO_F32; + port->channels_num = 1; + port->sample_size = 4; + break; + case OrbisAudioOutParamFormat::S16Stereo: + sampleFormat = SDL_AUDIO_S16; + port->channels_num = 2; + port->sample_size = 2; + break; + case OrbisAudioOutParamFormat::FloatStereo: + sampleFormat = SDL_AUDIO_F32; + port->channels_num = 2; + port->sample_size = 4; + break; + case OrbisAudioOutParamFormat::S16_8CH: + sampleFormat = SDL_AUDIO_S16; + port->channels_num = 8; + port->sample_size = 2; + break; + case OrbisAudioOutParamFormat::Float_8CH: + sampleFormat = SDL_AUDIO_F32; + port->channels_num = 8; + port->sample_size = 4; + break; + case OrbisAudioOutParamFormat::S16_8CH_Std: + sampleFormat = SDL_AUDIO_S16; + port->channels_num = 8; + port->sample_size = 2; + break; + case OrbisAudioOutParamFormat::Float_8CH_Std: + sampleFormat = SDL_AUDIO_F32; + port->channels_num = 8; + port->sample_size = 4; + break; + default: + UNREACHABLE_MSG("Unknown format"); + } + + port->volume.fill(Libraries::AudioOut::SCE_AUDIO_OUT_VOLUME_0DB); + + SDL_AudioSpec fmt; + SDL_zero(fmt); + fmt.format = sampleFormat; + fmt.channels = port->channels_num; + fmt.freq = freq; + port->stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &fmt, NULL, NULL); + SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(port->stream)); + return std::distance(ports_out.begin(), port) + 1; +} + +s32 SDLAudioOut::Output(s32 handle, const void* ptr) { + auto& port = ports_out.at(handle - 1); + if (!port.is_open) { + return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; + } + + const size_t data_size = port.samples_num * port.sample_size * port.channels_num; + bool result = SDL_PutAudioStreamData(port.stream, ptr, data_size); + while (SDL_GetAudioStreamAvailable(port.stream) > AUDIO_STREAM_BUFFER_THRESHOLD) { + SDL_Delay(0); + } + return result ? ORBIS_OK : -1; +} + +s32 SDLAudioOut::SetVolume(s32 handle, s32 bitflag, s32* volume) { + using Libraries::AudioOut::OrbisAudioOutParamFormat; + auto& port = ports_out.at(handle - 1); + if (!port.is_open) { + return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; + } + + for (int i = 0; i < port.channels_num; i++, bitflag >>= 1u) { + auto bit = bitflag & 0x1u; + + if (bit == 1) { + int src_index = i; + if (port.format == OrbisAudioOutParamFormat::Float_8CH_Std || + port.format == OrbisAudioOutParamFormat::S16_8CH_Std) { + switch (i) { + case 4: + src_index = 6; + break; + case 5: + src_index = 7; + break; + case 6: + src_index = 4; + break; + case 7: + src_index = 5; + break; + default: + break; + } + } + port.volume[i] = volume[src_index]; + } + } + + return ORBIS_OK; +} + +} // namespace Libraries::AudioOut diff --git a/src/core/libraries/audio/sdl_audio.h b/src/core/libraries/audio/sdl_audio.h new file mode 100644 index 000000000..2c34f8e29 --- /dev/null +++ b/src/core/libraries/audio/sdl_audio.h @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include "core/libraries/audio/audioout.h" + +namespace Libraries::AudioOut { + +class SDLAudioOut { +public: + explicit SDLAudioOut() = default; + ~SDLAudioOut() = default; + + s32 Open(OrbisAudioOutPort type, u32 samples_num, u32 freq, OrbisAudioOutParamFormat format); + s32 Output(s32 handle, const void* ptr); + s32 SetVolume(s32 handle, s32 bitflag, s32* volume); + + constexpr std::pair GetStatus(s32 handle) const { + const auto& port = ports_out.at(handle - 1); + return std::make_pair(port.type, port.channels_num); + } + +private: + struct PortOut { + SDL_AudioStream* stream; + u32 samples_num; + u32 freq; + OrbisAudioOutParamFormat format; + OrbisAudioOutPort type; + int channels_num; + std::array volume; + u8 sample_size; + bool is_open; + }; + std::shared_mutex m_mutex; + std::array ports_out{}; +}; + +} // namespace Libraries::AudioOut diff --git a/src/core/libraries/audio3d/audio3d.cpp b/src/core/libraries/audio3d/audio3d.cpp index 63815a068..44670d87b 100644 --- a/src/core/libraries/audio3d/audio3d.cpp +++ b/src/core/libraries/audio3d/audio3d.cpp @@ -1,19 +1,15 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "audio3d.h" -#include "audio3d_error.h" -#include "audio3d_impl.h" - #include "common/logging/log.h" #include "core/libraries/audio/audioout.h" +#include "core/libraries/audio3d/audio3d.h" +#include "core/libraries/audio3d/audio3d_error.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" namespace Libraries::Audio3d { -// Audio3d - int PS4_SYSV_ABI sceAudio3dInitialize(s64 iReserved) { LOG_INFO(Lib_Audio3d, "iReserved = {}", iReserved); return ORBIS_OK; @@ -25,18 +21,19 @@ int PS4_SYSV_ABI sceAudio3dTerminate() { return ORBIS_OK; } -void PS4_SYSV_ABI sceAudio3dGetDefaultOpenParameters(OrbisAudio3dOpenParameters* sParameters) { - if (sParameters != NULL) { - sParameters->szSizeThis = sizeof(OrbisAudio3dOpenParameters); - sParameters->uiGranularity = 256; - sParameters->eRate = ORBIS_AUDIO3D_RATE_48000; - sParameters->uiMaxObjects = 512; - sParameters->uiQueueDepth = 2; - sParameters->eBufferMode = ORBIS_AUDIO3D_BUFFER_ADVANCE_AND_PUSH; - sParameters->uiNumBeds = 2; - } else { +void PS4_SYSV_ABI sceAudio3dGetDefaultOpenParameters(OrbisAudio3dOpenParameters* parameters) { + if (parameters == nullptr) { LOG_ERROR(Lib_Audio3d, "Invalid OpenParameters ptr"); + return; } + + parameters->size_this = sizeof(OrbisAudio3dOpenParameters); + parameters->granularity = 256; + parameters->rate = OrbisAudio3dRate::Rate48000; + parameters->max_objects = 512; + parameters->queue_depth = 2; + parameters->buffer_mode = OrbisAudio3dBufferMode::AdvanceAndPush; + parameters->num_beds = 2; } int PS4_SYSV_ABI sceAudio3dPortOpen(OrbisUserServiceUserId iUserId, @@ -65,24 +62,24 @@ int PS4_SYSV_ABI sceAudio3dPortFlush(OrbisAudio3dPortId uiPortId) { } int PS4_SYSV_ABI sceAudio3dPortAdvance(OrbisAudio3dPortId uiPortId) { - LOG_INFO(Lib_Audio3d, "uiPortId = {}", uiPortId); + LOG_TRACE(Lib_Audio3d, "uiPortId = {}", uiPortId); return ORBIS_OK; } int PS4_SYSV_ABI sceAudio3dPortPush(OrbisAudio3dPortId uiPortId, OrbisAudio3dBlocking eBlocking) { - LOG_INFO(Lib_Audio3d, "uiPortId = {}", uiPortId); + LOG_TRACE(Lib_Audio3d, "uiPortId = {}", uiPortId); return ORBIS_OK; } int PS4_SYSV_ABI sceAudio3dPortGetAttributesSupported(OrbisAudio3dPortId uiPortId, OrbisAudio3dAttributeId* pCapabilities, - unsigned int* pNumCapabilities) { + u32* pNumCapabilities) { LOG_INFO(Lib_Audio3d, "uiPortId = {}", uiPortId); return ORBIS_OK; } -int PS4_SYSV_ABI sceAudio3dPortGetQueueLevel(OrbisAudio3dPortId uiPortId, unsigned int* pQueueLevel, - unsigned int* pQueueAvailable) { +int PS4_SYSV_ABI sceAudio3dPortGetQueueLevel(OrbisAudio3dPortId uiPortId, u32* pQueueLevel, + u32* pQueueAvailable) { LOG_INFO(Lib_Audio3d, "uiPortId = {}", uiPortId); return ORBIS_OK; } @@ -107,24 +104,24 @@ int PS4_SYSV_ABI sceAudio3dObjectSetAttributes(OrbisAudio3dPortId uiPortId, return ORBIS_OK; } -int PS4_SYSV_ABI sceAudio3dBedWrite(OrbisAudio3dPortId uiPortId, unsigned int uiNumChannels, +int PS4_SYSV_ABI sceAudio3dBedWrite(OrbisAudio3dPortId uiPortId, u32 uiNumChannels, OrbisAudio3dFormat eFormat, const void* pBuffer, - unsigned int uiNumSamples) { - LOG_INFO(Lib_Audio3d, "uiPortId = {}, uiNumChannels = {}, uiNumSamples = {}", uiPortId, - uiNumChannels, uiNumSamples); + u32 uiNumSamples) { + LOG_TRACE(Lib_Audio3d, "uiPortId = {}, uiNumChannels = {}, uiNumSamples = {}", uiPortId, + uiNumChannels, uiNumSamples); return ORBIS_OK; } -int PS4_SYSV_ABI sceAudio3dBedWrite2(OrbisAudio3dPortId uiPortId, unsigned int uiNumChannels, +int PS4_SYSV_ABI sceAudio3dBedWrite2(OrbisAudio3dPortId uiPortId, u32 uiNumChannels, OrbisAudio3dFormat eFormat, const void* pBuffer, - unsigned int uiNumSamples, - OrbisAudio3dOutputRoute eOutputRoute, bool bRestricted) { + u32 uiNumSamples, OrbisAudio3dOutputRoute eOutputRoute, + bool bRestricted) { LOG_INFO(Lib_Audio3d, "uiPortId = {}, uiNumChannels = {}, uiNumSamples = {}, bRestricted = {}", uiPortId, uiNumChannels, uiNumSamples, bRestricted); return ORBIS_OK; } -size_t PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMemorySize(unsigned int uiNumSpeakers, bool bIs3d) { +size_t PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMemorySize(u32 uiNumSpeakers, bool bIs3d) { LOG_INFO(Lib_Audio3d, "uiNumSpeakers = {}, bIs3d = {}", uiNumSpeakers, bIs3d); return ORBIS_OK; } @@ -152,7 +149,7 @@ int PS4_SYSV_ABI sceAudio3dDeleteSpeakerArray(OrbisAudio3dSpeakerArrayHandle han int PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMixCoefficients(OrbisAudio3dSpeakerArrayHandle handle, OrbisAudio3dPosition pos, float fSpread, float* pCoefficients, - unsigned int uiNumCoefficients) { + u32 uiNumCoefficients) { LOG_INFO(Lib_Audio3d, "fSpread = {}, uiNumCoefficients = {}", fSpread, uiNumCoefficients); if (handle == nullptr) { LOG_ERROR(Lib_Audio3d, "invalid SpeakerArrayHandle"); @@ -164,8 +161,7 @@ int PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMixCoefficients(OrbisAudio3dSpeakerArr int PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMixCoefficients2(OrbisAudio3dSpeakerArrayHandle handle, OrbisAudio3dPosition pos, float fSpread, float* pCoefficients, - unsigned int uiNumCoefficients, - bool bHeightAware, + u32 uiNumCoefficients, bool bHeightAware, float fDownmixSpreadRadius) { LOG_INFO(Lib_Audio3d, "fSpread = {}, uiNumCoefficients = {}, bHeightAware = {}, fDownmixSpreadRadius = {}", @@ -191,7 +187,7 @@ s32 PS4_SYSV_ABI sceAudio3dAudioOutClose(s32 handle) { } s32 PS4_SYSV_ABI sceAudio3dAudioOutOutput(s32 handle, const void* ptr) { - LOG_INFO(Lib_Audio3d, "handle = {}", handle); + LOG_TRACE(Lib_Audio3d, "handle = {}", handle); if (ptr == nullptr) { LOG_ERROR(Lib_Audio3d, "invalid Output ptr"); return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; @@ -209,8 +205,8 @@ s32 PS4_SYSV_ABI sceAudio3dAudioOutOutputs(::Libraries::AudioOut::OrbisAudioOutO return ORBIS_OK; } -int PS4_SYSV_ABI sceAudio3dPortCreate(unsigned int uiGranularity, OrbisAudio3dRate eRate, - s64 iReserved, OrbisAudio3dPortId* pId) { +int PS4_SYSV_ABI sceAudio3dPortCreate(u32 uiGranularity, OrbisAudio3dRate eRate, s64 iReserved, + OrbisAudio3dPortId* pId) { LOG_INFO(Lib_Audio3d, "uiGranularity = {}, iReserved = {}", uiGranularity, iReserved); return ORBIS_OK; } @@ -341,4 +337,4 @@ void RegisterlibSceAudio3d(Core::Loader::SymbolsResolver* sym) { sceAudio3dSetGpuRenderer); }; -} // namespace Libraries::Audio3d \ No newline at end of file +} // namespace Libraries::Audio3d diff --git a/src/core/libraries/audio3d/audio3d.h b/src/core/libraries/audio3d/audio3d.h index 6cbe2d02f..6f344226f 100644 --- a/src/core/libraries/audio3d/audio3d.h +++ b/src/core/libraries/audio3d/audio3d.h @@ -15,56 +15,57 @@ namespace Libraries::Audio3d { class Audio3d; -typedef int OrbisUserServiceUserId; -typedef unsigned int OrbisAudio3dPortId; -typedef unsigned int OrbisAudio3dObjectId; -typedef unsigned int OrbisAudio3dAttributeId; +using OrbisUserServiceUserId = s32; +using OrbisAudio3dPortId = u32; +using OrbisAudio3dObjectId = u32; +using OrbisAudio3dAttributeId = u32; -enum OrbisAudio3dFormat { - ORBIS_AUDIO3D_FORMAT_S16 = 0, // s16 - ORBIS_AUDIO3D_FORMAT_FLOAT = 1 // f32 +enum class OrbisAudio3dFormat { + S16 = 0, + Float = 1, }; -enum OrbisAudio3dRate { ORBIS_AUDIO3D_RATE_48000 = 0 }; - -enum OrbisAudio3dBufferMode { - ORBIS_AUDIO3D_BUFFER_NO_ADVANCE = 0, - ORBIS_AUDIO3D_BUFFER_ADVANCE_NO_PUSH = 1, - ORBIS_AUDIO3D_BUFFER_ADVANCE_AND_PUSH = 2 +enum class OrbisAudio3dRate { + Rate48000 = 0, }; -enum OrbisAudio3dBlocking { ORBIS_AUDIO3D_BLOCKING_ASYNC = 0, ORBIS_AUDIO3D_BLOCKING_SYNC = 1 }; +enum class OrbisAudio3dBufferMode { NoAdvance = 0, AdvanceNoPush = 1, AdvanceAndPush = 2 }; -enum OrbisAudio3dPassthrough { - ORBIS_AUDIO3D_PASSTHROUGH_NONE = 0, - ORBIS_AUDIO3D_PASSTHROUGH_LEFT = 1, - ORBIS_AUDIO3D_PASSTHROUGH_RIGHT = 2 +enum class OrbisAudio3dBlocking { + Async = 0, + Sync = 1, }; -enum OrbisAudio3dOutputRoute { - ORBIS_AUDIO3D_OUTPUT_BOTH = 0, - ORBIS_AUDIO3D_OUTPUT_HMU_ONLY = 1, - ORBIS_AUDIO3D_OUTPUT_TV_ONLY = 2 +enum class OrbisAudio3dPassthrough { + None = 0, + Left = 1, + Right = 2, }; -enum OrbisAudio3dAmbisonics { - ORBIS_AUDIO3D_AMBISONICS_NONE = ~0, - ORBIS_AUDIO3D_AMBISONICS_W = 0, - ORBIS_AUDIO3D_AMBISONICS_X = 1, - ORBIS_AUDIO3D_AMBISONICS_Y = 2, - ORBIS_AUDIO3D_AMBISONICS_Z = 3, - ORBIS_AUDIO3D_AMBISONICS_R = 4, - ORBIS_AUDIO3D_AMBISONICS_S = 5, - ORBIS_AUDIO3D_AMBISONICS_T = 6, - ORBIS_AUDIO3D_AMBISONICS_U = 7, - ORBIS_AUDIO3D_AMBISONICS_V = 8, - ORBIS_AUDIO3D_AMBISONICS_K = 9, - ORBIS_AUDIO3D_AMBISONICS_L = 10, - ORBIS_AUDIO3D_AMBISONICS_M = 11, - ORBIS_AUDIO3D_AMBISONICS_N = 12, - ORBIS_AUDIO3D_AMBISONICS_O = 13, - ORBIS_AUDIO3D_AMBISONICS_P = 14, - ORBIS_AUDIO3D_AMBISONICS_Q = 15 +enum class OrbisAudio3dOutputRoute { + Both = 0, + HmuOnly = 1, + TvOnly = 2, +}; + +enum class OrbisAudio3dAmbisonics : u32 { + None = ~0U, + W = 0, + X = 1, + Y = 2, + Z = 3, + R = 4, + S = 5, + T = 6, + U = 7, + V = 8, + K = 9, + L = 10, + M = 11, + N = 12, + O = 13, + P = 14, + Q = 15 }; static const OrbisAudio3dAttributeId s_sceAudio3dAttributePcm = 0x00000001; @@ -86,21 +87,21 @@ struct OrbisAudio3dSpeakerArray; using OrbisAudio3dSpeakerArrayHandle = OrbisAudio3dSpeakerArray*; // head struct OrbisAudio3dOpenParameters { - size_t szSizeThis; - unsigned int uiGranularity; - OrbisAudio3dRate eRate; - unsigned int uiMaxObjects; - unsigned int uiQueueDepth; - OrbisAudio3dBufferMode eBufferMode; + size_t size_this; + u32 granularity; + OrbisAudio3dRate rate; + u32 max_objects; + u32 queue_depth; + OrbisAudio3dBufferMode buffer_mode; char padding[32]; - unsigned int uiNumBeds; + u32 num_beds; }; struct OrbisAudio3dAttribute { - OrbisAudio3dAttributeId uiAttributeId; + OrbisAudio3dAttributeId attribute_id; char padding[32]; - const void* pValue; - size_t szValue; + const void* p_value; + size_t value; }; struct OrbisAudio3dPosition { @@ -110,25 +111,25 @@ struct OrbisAudio3dPosition { }; struct OrbisAudio3dPcm { - OrbisAudio3dFormat eFormat; - const void* pSampleBuffer; - unsigned int uiNumSamples; + OrbisAudio3dFormat format; + const void* sample_buffer; + u32 num_samples; }; struct OrbisAudio3dSpeakerArrayParameters { - OrbisAudio3dPosition* pSpeakerPosition; - unsigned int uiNumSpeakers; - bool bIs3d; - void* pBuffer; - size_t szSize; + OrbisAudio3dPosition* speaker_position; + u32 num_speakers; + bool is_3d; + void* buffer; + size_t size; }; struct OrbisAudio3dApplicationSpecific { - size_t szSizeThis; - u8 cApplicationSpecific[32]; + size_t size_this; + u8 application_specific[32]; }; void PS4_SYSV_ABI sceAudio3dGetDefaultOpenParameters(OrbisAudio3dOpenParameters* sParameters); void RegisterlibSceAudio3d(Core::Loader::SymbolsResolver* sym); -} // namespace Libraries::Audio3d \ No newline at end of file +} // namespace Libraries::Audio3d diff --git a/src/core/libraries/audio3d/audio3d_error.h b/src/core/libraries/audio3d/audio3d_error.h index ff9d9749c..626ac8699 100644 --- a/src/core/libraries/audio3d/audio3d_error.h +++ b/src/core/libraries/audio3d/audio3d_error.h @@ -3,6 +3,8 @@ #pragma once +#include "core/libraries/error_codes.h" + constexpr int ORBIS_AUDIO3D_ERROR_UNKNOWN = 0x80EA0001; constexpr int ORBIS_AUDIO3D_ERROR_INVALID_PORT = 0x80EA0002; constexpr int ORBIS_AUDIO3D_ERROR_INVALID_OBJECT = 0x80EA0003; diff --git a/src/core/libraries/audio3d/audio3d_impl.cpp b/src/core/libraries/audio3d/audio3d_impl.cpp index c267c096f..3069e8800 100644 --- a/src/core/libraries/audio3d/audio3d_impl.cpp +++ b/src/core/libraries/audio3d/audio3d_impl.cpp @@ -6,7 +6,7 @@ #include "common/logging/log.h" #include "core/libraries/error_codes.h" -#include "core/libraries/kernel/libkernel.h" +#include "core/libraries/kernel/kernel.h" using namespace Libraries::Kernel; diff --git a/src/core/libraries/audio3d/audio3d_impl.h b/src/core/libraries/audio3d/audio3d_impl.h index 4e6342b1b..1213a030e 100644 --- a/src/core/libraries/audio3d/audio3d_impl.h +++ b/src/core/libraries/audio3d/audio3d_impl.h @@ -10,7 +10,7 @@ namespace Libraries::Audio3d { class Audio3d { public: private: - typedef unsigned int OrbisAudio3dPluginId; + using OrbisAudio3dPluginId = u32; }; } // namespace Libraries::Audio3d diff --git a/src/core/libraries/avplayer/avplayer.cpp b/src/core/libraries/avplayer/avplayer.cpp index 60d68c4f7..176fda137 100644 --- a/src/core/libraries/avplayer/avplayer.cpp +++ b/src/core/libraries/avplayer/avplayer.cpp @@ -1,18 +1,14 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "avplayer.h" - -#include "avplayer_impl.h" #include "common/logging/log.h" -#include "core/libraries/error_codes.h" -#include "core/libraries/kernel/thread_management.h" +#include "core/libraries/avplayer/avplayer.h" +#include "core/libraries/avplayer/avplayer_error.h" +#include "core/libraries/avplayer/avplayer_impl.h" #include "core/libraries/libs.h" namespace Libraries::AvPlayer { -using namespace Kernel; - s32 PS4_SYSV_ABI sceAvPlayerAddSource(SceAvPlayerHandle handle, const char* filename) { LOG_TRACE(Lib_AvPlayer, "filename = {}", filename); if (handle == nullptr) { @@ -309,7 +305,7 @@ void RegisterlibSceAvPlayer(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("XC9wM+xULz8", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerJumpToTime); LIB_FUNCTION("9y5v+fGN4Wk", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerPause); LIB_FUNCTION("HD1YKVU26-M", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerPostInit); - LIB_FUNCTION("agig-iDRrTE", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerPrintf); + // LIB_FUNCTION("agig-iDRrTE", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerPrintf); LIB_FUNCTION("w5moABNwnRY", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerResume); LIB_FUNCTION("k-q+xOxdc3E", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerSetAvSyncMode); diff --git a/src/core/libraries/avplayer/avplayer.h b/src/core/libraries/avplayer/avplayer.h index 98e932070..2d472f801 100644 --- a/src/core/libraries/avplayer/avplayer.h +++ b/src/core/libraries/avplayer/avplayer.h @@ -5,8 +5,8 @@ #include "common/types.h" -#include // va_list -#include // size_t +#include // va_list +#include // size_t namespace Core::Loader { class SymbolsResolver; @@ -18,17 +18,26 @@ class AvPlayer; using SceAvPlayerHandle = AvPlayer*; -enum SceAvPlayerUriType { SCE_AVPLAYER_URI_TYPE_SOURCE = 0 }; +enum class SceAvPlayerUriType : u32 { + Source = 0, +}; struct SceAvPlayerUri { const char* name; u32 length; }; -enum SceAvPlayerSourceType { - SCE_AVPLAYER_SOURCE_TYPE_UNKNOWN = 0, - SCE_AVPLAYER_SOURCE_TYPE_FILE_MP4 = 1, - SCE_AVPLAYER_SOURCE_TYPE_HLS = 8 +enum class SceAvPlayerSourceType { + Unknown = 0, + FileMp4 = 1, + Hls = 8, +}; + +enum class SceAvPlayerStreamType : u32 { + Video, + Audio, + TimedText, + Unknown, }; struct SceAvPlayerSourceDetails { @@ -50,7 +59,7 @@ struct SceAvPlayerVideo { u32 width; u32 height; f32 aspect_ratio; - u8 language_code[4]; + char language_code[4]; }; struct SceAvPlayerTextPosition { @@ -82,7 +91,7 @@ struct SceAvPlayerFrameInfo { }; struct SceAvPlayerStreamInfo { - u32 type; + SceAvPlayerStreamType type; u8 reserved[4]; SceAvPlayerStreamDetails details; u64 duration; @@ -135,10 +144,10 @@ struct SceAvPlayerFrameInfoEx { SceAvPlayerStreamDetailsEx details; }; -typedef void* PS4_SYSV_ABI (*SceAvPlayerAllocate)(void* p, u32 align, u32 size); -typedef void PS4_SYSV_ABI (*SceAvPlayerDeallocate)(void* p, void* mem); -typedef void* PS4_SYSV_ABI (*SceAvPlayerAllocateTexture)(void* p, u32 align, u32 size); -typedef void PS4_SYSV_ABI (*SceAvPlayerDeallocateTexture)(void* p, void* mem); +using SceAvPlayerAllocate = void* PS4_SYSV_ABI (*)(void* p, u32 align, u32 size); +using SceAvPlayerDeallocate = void PS4_SYSV_ABI (*)(void* p, void* mem); +using SceAvPlayerAllocateTexture = void* PS4_SYSV_ABI (*)(void* p, u32 align, u32 size); +using SceAvPlayerDeallocateTexture = void PS4_SYSV_ABI (*)(void* p, void* mem); struct SceAvPlayerMemAllocator { void* object_ptr; @@ -148,10 +157,10 @@ struct SceAvPlayerMemAllocator { SceAvPlayerDeallocateTexture deallocate_texture; }; -typedef s32 PS4_SYSV_ABI (*SceAvPlayerOpenFile)(void* p, const char* name); -typedef s32 PS4_SYSV_ABI (*SceAvPlayerCloseFile)(void* p); -typedef s32 PS4_SYSV_ABI (*SceAvPlayerReadOffsetFile)(void* p, u8* buf, u64 pos, u32 len); -typedef u64 PS4_SYSV_ABI (*SceAvPlayerSizeFile)(void* p); +using SceAvPlayerOpenFile = s32 PS4_SYSV_ABI (*)(void* p, const char* name); +using SceAvPlayerCloseFile = s32 PS4_SYSV_ABI (*)(void* p); +using SceAvPlayerReadOffsetFile = s32 PS4_SYSV_ABI (*)(void* p, u8* buf, u64 pos, u32 len); +using SceAvPlayerSizeFile = u64 PS4_SYSV_ABI (*)(void* p); struct SceAvPlayerFileReplacement { void* object_ptr; @@ -161,31 +170,31 @@ struct SceAvPlayerFileReplacement { SceAvPlayerSizeFile size; }; -enum SceAvPlayerEvents { - SCE_AVPLAYER_STATE_STOP = 0x01, - SCE_AVPLAYER_STATE_READY = 0x02, - SCE_AVPLAYER_STATE_PLAY = 0x03, - SCE_AVPLAYER_STATE_PAUSE = 0x04, - SCE_AVPLAYER_STATE_BUFFERING = 0x05, - SCE_AVPLAYER_TIMED_TEXT_DELIVERY = 0x10, - SCE_AVPLAYER_WARNING_ID = 0x20, - SCE_AVPLAYER_ENCRYPTION = 0x30, - SCE_AVPLAYER_DRM_ERROR = 0x40 +enum class SceAvPlayerEvents { + StateStop = 0x01, + StateReady = 0x02, + StatePlay = 0x03, + StatePause = 0x04, + StateBuffering = 0x05, + TimedTextDelivery = 0x10, + WarningId = 0x20, + Encryption = 0x30, + DrmError = 0x40, }; -typedef void PS4_SYSV_ABI (*SceAvPlayerEventCallback)(void* p, SceAvPlayerEvents event, s32 src_id, - void* data); +using SceAvPlayerEventCallback = void PS4_SYSV_ABI (*)(void* p, SceAvPlayerEvents event, s32 src_id, + void* data); struct SceAvPlayerEventReplacement { void* object_ptr; SceAvPlayerEventCallback event_callback; }; -enum SceAvPlayerDebuglevels { - SCE_AVPLAYER_DBG_NONE, - SCE_AVPLAYER_DBG_INFO, - SCE_AVPLAYER_DBG_WARNINGS, - SCE_AVPLAYER_DBG_ALL +enum class SceAvPlayerDebuglevels { + None, + Info, + Warnings, + All, }; struct SceAvPlayerInitData { @@ -224,24 +233,17 @@ struct SceAvPlayerInitDataEx { u8 reserved[3]; }; -enum SceAvPlayerStreamType { - SCE_AVPLAYER_VIDEO, - SCE_AVPLAYER_AUDIO, - SCE_AVPLAYER_TIMEDTEXT, - SCE_AVPLAYER_UNKNOWN +enum class SceAvPlayerVideoDecoderType { + Default = 0, + Reserved1, + Software, + Software2, }; -enum SceAvPlayerVideoDecoderType { - SCE_AVPLAYER_VIDEO_DECODER_TYPE_DEFAULT = 0, - SCE_AVPLAYER_VIDEO_DECODER_TYPE_RESERVED1, - SCE_AVPLAYER_VIDEO_DECODER_TYPE_SOFTWARE, - SCE_AVPLAYER_VIDEO_DECODER_TYPE_SOFTWARE2 -}; - -enum SceAvPlayerAudioDecoderType { - SCE_AVPLAYER_AUDIO_DECODER_TYPE_DEFAULT = 0, - SCE_AVPLAYER_AUDIO_DECODER_TYPE_RESERVED1, - SCE_AVPLAYER_AUDIO_DECODER_TYPE_RESERVED2 +enum class SceAvPlayerAudioDecoderType { + Default = 0, + Reserved1, + Reserved2, }; struct SceAvPlayerDecoderInit { @@ -281,12 +283,12 @@ struct SceAvPlayerPostInitData { u8 reserved[56]; }; -enum SceAvPlayerAvSyncMode { - SCE_AVPLAYER_AV_SYNC_MODE_DEFAULT = 0, - SCE_AVPLAYER_AV_SYNC_MODE_NONE +enum class SceAvPlayerAvSyncMode { + Default = 0, + None, }; -typedef int PS4_SYSV_ABI (*SceAvPlayerLogCallback)(void* p, const char* format, va_list args); +using SceAvPlayerLogCallback = int PS4_SYSV_ABI (*)(void* p, const char* format, va_list args); void RegisterlibSceAvPlayer(Core::Loader::SymbolsResolver* sym); diff --git a/src/core/libraries/avplayer/avplayer_common.cpp b/src/core/libraries/avplayer/avplayer_common.cpp index 306603e29..28d7803a1 100644 --- a/src/core/libraries/avplayer/avplayer_common.cpp +++ b/src/core/libraries/avplayer/avplayer_common.cpp @@ -1,29 +1,21 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "avplayer.h" -#include "avplayer_common.h" +#include // std::equal +#include // std::tolower -#include // std::equal -#include // std::tolower -#include // std::string_view +#include "core/libraries/avplayer/avplayer.h" +#include "core/libraries/avplayer/avplayer_common.h" namespace Libraries::AvPlayer { -using namespace Kernel; - -static bool ichar_equals(char a, char b) { - return std::tolower(static_cast(a)) == - std::tolower(static_cast(b)); -} - static bool iequals(std::string_view l, std::string_view r) { - return std::ranges::equal(l, r, ichar_equals); + return std::ranges::equal(l, r, [](u8 a, u8 b) { return std::tolower(a) == std::tolower(b); }); } SceAvPlayerSourceType GetSourceType(std::string_view path) { if (path.empty()) { - return SCE_AVPLAYER_SOURCE_TYPE_UNKNOWN; + return SceAvPlayerSourceType::Unknown; } std::string_view name = path; @@ -33,14 +25,14 @@ SceAvPlayerSourceType GetSourceType(std::string_view path) { // -> schema://server.domain/path/to/file.ext/and/beyond name = path.substr(0, path.find_first_of("?#")); if (name.empty()) { - return SCE_AVPLAYER_SOURCE_TYPE_UNKNOWN; + return SceAvPlayerSourceType::Unknown; } } // schema://server.domain/path/to/file.ext/and/beyond -> .ext/and/beyond auto ext = name.substr(name.rfind('.')); if (ext.empty()) { - return SCE_AVPLAYER_SOURCE_TYPE_UNKNOWN; + return SceAvPlayerSourceType::Unknown; } // .ext/and/beyond -> .ext @@ -48,14 +40,14 @@ SceAvPlayerSourceType GetSourceType(std::string_view path) { if (iequals(ext, ".mp4") || iequals(ext, ".m4v") || iequals(ext, ".m3d") || iequals(ext, ".m4a") || iequals(ext, ".mov")) { - return SCE_AVPLAYER_SOURCE_TYPE_FILE_MP4; + return SceAvPlayerSourceType::FileMp4; } if (iequals(ext, ".m3u8")) { - return SCE_AVPLAYER_SOURCE_TYPE_HLS; + return SceAvPlayerSourceType::Hls; } - return SCE_AVPLAYER_SOURCE_TYPE_UNKNOWN; + return SceAvPlayerSourceType::Unknown; } } // namespace Libraries::AvPlayer diff --git a/src/core/libraries/avplayer/avplayer_common.h b/src/core/libraries/avplayer/avplayer_common.h index a53696ecf..dc3cd787f 100644 --- a/src/core/libraries/avplayer/avplayer_common.h +++ b/src/core/libraries/avplayer/avplayer_common.h @@ -3,16 +3,14 @@ #pragma once -#include "avplayer.h" - -#include "common/assert.h" -#include "common/logging/log.h" -#include "core/libraries/kernel/thread_management.h" - +#include #include +#include #include #include +#include "core/libraries/avplayer/avplayer.h" + #define AVPLAYER_IS_ERROR(x) ((x) < 0) namespace Libraries::AvPlayer { diff --git a/src/core/libraries/avplayer/avplayer_error.h b/src/core/libraries/avplayer/avplayer_error.h new file mode 100644 index 000000000..ebe4d0dd3 --- /dev/null +++ b/src/core/libraries/avplayer/avplayer_error.h @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// AvPlayer library +constexpr int ORBIS_AVPLAYER_ERROR_INVALID_PARAMS = 0x806A0001; +constexpr int ORBIS_AVPLAYER_ERROR_OPERATION_FAILED = 0x806A0002; +constexpr int ORBIS_AVPLAYER_ERROR_NO_MEMORY = 0x806A0003; +constexpr int ORBIS_AVPLAYER_ERROR_NOT_SUPPORTED = 0x806A0004; +constexpr int ORBIS_AVPLAYER_ERROR_WAR_FILE_NONINTERLEAVED = 0x806A00A0; +constexpr int ORBIS_AVPLAYER_ERROR_WAR_LOOPING_BACK = 0x806A00A1; +constexpr int ORBIS_AVPLAYER_ERROR_WAR_JUMP_COMPLETE = 0x806A00A3; +constexpr int ORBIS_AVPLAYER_ERROR_INFO_MARLIN_ENCRY = 0x806A00B0; +constexpr int ORBIS_AVPLAYER_ERROR_INFO_PLAYREADY_ENCRY = 0x806A00B4; +constexpr int ORBIS_AVPLAYER_ERROR_INFO_AES_ENCRY = 0x806A00B5; +constexpr int ORBIS_AVPLAYER_ERROR_INFO_OTHER_ENCRY = 0x806A00BF; diff --git a/src/core/libraries/avplayer/avplayer_file_streamer.cpp b/src/core/libraries/avplayer/avplayer_file_streamer.cpp index c7bd5b5de..19faeb273 100644 --- a/src/core/libraries/avplayer/avplayer_file_streamer.cpp +++ b/src/core/libraries/avplayer/avplayer_file_streamer.cpp @@ -1,20 +1,16 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "avplayer_file_streamer.h" - -#include "avplayer_common.h" - -#include +#include // std::max, std::min +#include +#include "core/libraries/avplayer/avplayer_file_streamer.h" extern "C" { #include #include } -#include // std::max, std::min - -#define AVPLAYER_AVIO_BUFFER_SIZE 4096 +constexpr u32 AVPLAYER_AVIO_BUFFER_SIZE = 4096; namespace Libraries::AvPlayer { diff --git a/src/core/libraries/avplayer/avplayer_file_streamer.h b/src/core/libraries/avplayer/avplayer_file_streamer.h index 034e40dd4..bc096bccc 100644 --- a/src/core/libraries/avplayer/avplayer_file_streamer.h +++ b/src/core/libraries/avplayer/avplayer_file_streamer.h @@ -3,11 +3,9 @@ #pragma once -#include "avplayer.h" -#include "avplayer_data_streamer.h" - #include -#include +#include "core/libraries/avplayer/avplayer.h" +#include "core/libraries/avplayer/avplayer_data_streamer.h" struct AVIOContext; diff --git a/src/core/libraries/avplayer/avplayer_impl.cpp b/src/core/libraries/avplayer/avplayer_impl.cpp index 1c414c961..d9a67134c 100644 --- a/src/core/libraries/avplayer/avplayer_impl.cpp +++ b/src/core/libraries/avplayer/avplayer_impl.cpp @@ -1,17 +1,10 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "avplayer_common.h" -#include "avplayer_file_streamer.h" -#include "avplayer_impl.h" - -#include "common/logging/log.h" -#include "common/singleton.h" -#include "core/libraries/error_codes.h" -#include "core/libraries/kernel/libkernel.h" -#include "core/linker.h" - -using namespace Libraries::Kernel; +#include "core/libraries/avplayer/avplayer_common.h" +#include "core/libraries/avplayer/avplayer_error.h" +#include "core/libraries/avplayer/avplayer_impl.h" +#include "core/tls.h" namespace Libraries::AvPlayer { @@ -19,32 +12,28 @@ void* PS4_SYSV_ABI AvPlayer::Allocate(void* handle, u32 alignment, u32 size) { const auto* const self = reinterpret_cast(handle); const auto allocate = self->m_init_data_original.memory_replacement.allocate; const auto ptr = self->m_init_data_original.memory_replacement.object_ptr; - const auto* linker = Common::Singleton::Instance(); - return linker->ExecuteGuest(allocate, ptr, alignment, size); + return Core::ExecuteGuest(allocate, ptr, alignment, size); } void PS4_SYSV_ABI AvPlayer::Deallocate(void* handle, void* memory) { const auto* const self = reinterpret_cast(handle); const auto deallocate = self->m_init_data_original.memory_replacement.deallocate; const auto ptr = self->m_init_data_original.memory_replacement.object_ptr; - const auto* linker = Common::Singleton::Instance(); - return linker->ExecuteGuest(deallocate, ptr, memory); + return Core::ExecuteGuest(deallocate, ptr, memory); } void* PS4_SYSV_ABI AvPlayer::AllocateTexture(void* handle, u32 alignment, u32 size) { const auto* const self = reinterpret_cast(handle); const auto allocate = self->m_init_data_original.memory_replacement.allocate_texture; const auto ptr = self->m_init_data_original.memory_replacement.object_ptr; - const auto* linker = Common::Singleton::Instance(); - return linker->ExecuteGuest(allocate, ptr, alignment, size); + return Core::ExecuteGuest(allocate, ptr, alignment, size); } void PS4_SYSV_ABI AvPlayer::DeallocateTexture(void* handle, void* memory) { const auto* const self = reinterpret_cast(handle); const auto deallocate = self->m_init_data_original.memory_replacement.deallocate_texture; const auto ptr = self->m_init_data_original.memory_replacement.object_ptr; - const auto* linker = Common::Singleton::Instance(); - return linker->ExecuteGuest(deallocate, ptr, memory); + return Core::ExecuteGuest(deallocate, ptr, memory); } int PS4_SYSV_ABI AvPlayer::OpenFile(void* handle, const char* filename) { @@ -53,8 +42,7 @@ int PS4_SYSV_ABI AvPlayer::OpenFile(void* handle, const char* filename) { const auto open = self->m_init_data_original.file_replacement.open; const auto ptr = self->m_init_data_original.file_replacement.object_ptr; - const auto* linker = Common::Singleton::Instance(); - return linker->ExecuteGuest(open, ptr, filename); + return Core::ExecuteGuest(open, ptr, filename); } int PS4_SYSV_ABI AvPlayer::CloseFile(void* handle) { @@ -63,8 +51,7 @@ int PS4_SYSV_ABI AvPlayer::CloseFile(void* handle) { const auto close = self->m_init_data_original.file_replacement.close; const auto ptr = self->m_init_data_original.file_replacement.object_ptr; - const auto* linker = Common::Singleton::Instance(); - return linker->ExecuteGuest(close, ptr); + return Core::ExecuteGuest(close, ptr); } int PS4_SYSV_ABI AvPlayer::ReadOffsetFile(void* handle, u8* buffer, u64 position, u32 length) { @@ -73,8 +60,7 @@ int PS4_SYSV_ABI AvPlayer::ReadOffsetFile(void* handle, u8* buffer, u64 position const auto read_offset = self->m_init_data_original.file_replacement.readOffset; const auto ptr = self->m_init_data_original.file_replacement.object_ptr; - const auto* linker = Common::Singleton::Instance(); - return linker->ExecuteGuest(read_offset, ptr, buffer, position, length); + return Core::ExecuteGuest(read_offset, ptr, buffer, position, length); } u64 PS4_SYSV_ABI AvPlayer::SizeFile(void* handle) { @@ -83,8 +69,7 @@ u64 PS4_SYSV_ABI AvPlayer::SizeFile(void* handle) { const auto size = self->m_init_data_original.file_replacement.size; const auto ptr = self->m_init_data_original.file_replacement.object_ptr; - const auto* linker = Common::Singleton::Instance(); - return linker->ExecuteGuest(size, ptr); + return Core::ExecuteGuest(size, ptr); } SceAvPlayerInitData AvPlayer::StubInitData(const SceAvPlayerInitData& data) { diff --git a/src/core/libraries/avplayer/avplayer_impl.h b/src/core/libraries/avplayer/avplayer_impl.h index d7f28094e..984d81499 100644 --- a/src/core/libraries/avplayer/avplayer_impl.h +++ b/src/core/libraries/avplayer/avplayer_impl.h @@ -3,11 +3,8 @@ #pragma once -#include "avplayer.h" -#include "avplayer_data_streamer.h" -#include "avplayer_state.h" - -#include "core/libraries/kernel/thread_management.h" +#include "core/libraries/avplayer/avplayer.h" +#include "core/libraries/avplayer/avplayer_state.h" #include @@ -17,7 +14,6 @@ extern "C" { } #include -#include namespace Libraries::AvPlayer { diff --git a/src/core/libraries/avplayer/avplayer_source.cpp b/src/core/libraries/avplayer/avplayer_source.cpp index 19925ba0c..cf783403c 100644 --- a/src/core/libraries/avplayer/avplayer_source.cpp +++ b/src/core/libraries/avplayer/avplayer_source.cpp @@ -1,18 +1,14 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "avplayer_source.h" - -#include "avplayer_file_streamer.h" - #include "common/alignment.h" #include "common/singleton.h" #include "common/thread.h" - #include "core/file_sys/fs.h" -#include "core/libraries/kernel/time_management.h" +#include "core/libraries/avplayer/avplayer_file_streamer.h" +#include "core/libraries/avplayer/avplayer_source.h" -#include +#include extern "C" { #include @@ -22,21 +18,10 @@ extern "C" { #include } -// The av_err2str macro in libavutil/error.h does not play nice with C++ -#ifdef av_err2str -#undef av_err2str -#include -av_always_inline std::string av_err2string(int errnum) { - char errbuf[AV_ERROR_MAX_STRING_SIZE]; - return av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, errnum); -} -#define av_err2str(err) av_err2string(err).c_str() -#endif // av_err2str +#include "common/support/avdec.h" namespace Libraries::AvPlayer { -using namespace Kernel; - AvPlayerSource::AvPlayerSource(AvPlayerStateCallback& state, bool use_vdec2) : m_state(state), m_use_vdec2(use_vdec2) {} @@ -91,17 +76,17 @@ s32 AvPlayerSource::GetStreamCount() { return m_avformat_context->nb_streams; } -static s32 CodecTypeToStreamType(AVMediaType codec_type) { +static SceAvPlayerStreamType CodecTypeToStreamType(AVMediaType codec_type) { switch (codec_type) { case AVMediaType::AVMEDIA_TYPE_VIDEO: - return SCE_AVPLAYER_VIDEO; + return SceAvPlayerStreamType::Video; case AVMediaType::AVMEDIA_TYPE_AUDIO: - return SCE_AVPLAYER_AUDIO; + return SceAvPlayerStreamType::Audio; case AVMediaType::AVMEDIA_TYPE_SUBTITLE: - return SCE_AVPLAYER_TIMEDTEXT; + return SceAvPlayerStreamType::TimedText; default: LOG_ERROR(Lib_AvPlayer, "Unexpected AVMediaType {}", magic_enum::enum_name(codec_type)); - return -1; + return SceAvPlayerStreamType::Unknown; } } @@ -130,7 +115,7 @@ bool AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info LOG_WARNING(Lib_AvPlayer, "Stream {} language is unknown", stream_index); } switch (info.type) { - case SCE_AVPLAYER_VIDEO: { + case SceAvPlayerStreamType::Video: { LOG_INFO(Lib_AvPlayer, "Stream {} is a video stream.", stream_index); info.details.video.aspect_ratio = f32(p_stream->codecpar->width) / p_stream->codecpar->height; @@ -148,7 +133,7 @@ bool AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info } break; } - case SCE_AVPLAYER_AUDIO: { + case SceAvPlayerStreamType::Audio: { LOG_INFO(Lib_AvPlayer, "Stream {} is an audio stream.", stream_index); info.details.audio.channel_count = p_stream->codecpar->ch_layout.nb_channels; info.details.audio.sample_rate = p_stream->codecpar->sample_rate; @@ -159,7 +144,7 @@ bool AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info } break; } - case SCE_AVPLAYER_TIMEDTEXT: { + case SceAvPlayerStreamType::TimedText: { LOG_WARNING(Lib_AvPlayer, "Stream {} is a timedtext stream.", stream_index); info.details.subs.font_size = 12; info.details.subs.text_size = 12; @@ -170,7 +155,8 @@ bool AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info break; } default: { - LOG_ERROR(Lib_AvPlayer, "Stream {} type is unknown: {}.", stream_index, info.type); + LOG_ERROR(Lib_AvPlayer, "Stream {} type is unknown: {}.", stream_index, + magic_enum::enum_name(info.type)); return false; } } @@ -258,11 +244,9 @@ bool AvPlayerSource::Start() { LOG_ERROR(Lib_AvPlayer, "Could not start playback. NULL context."); return false; } - m_demuxer_thread = std::jthread([this](std::stop_token stop) { this->DemuxerThread(stop); }); - m_video_decoder_thread = - std::jthread([this](std::stop_token stop) { this->VideoDecoderThread(stop); }); - m_audio_decoder_thread = - std::jthread([this](std::stop_token stop) { this->AudioDecoderThread(stop); }); + m_demuxer_thread.Run([this](std::stop_token stop) { this->DemuxerThread(stop); }); + m_video_decoder_thread.Run([this](std::stop_token stop) { this->VideoDecoderThread(stop); }); + m_audio_decoder_thread.Run([this](std::stop_token stop) { this->AudioDecoderThread(stop); }); m_start_time = std::chrono::high_resolution_clock::now(); return true; } @@ -275,18 +259,10 @@ bool AvPlayerSource::Stop() { return false; } - m_video_decoder_thread.request_stop(); - m_audio_decoder_thread.request_stop(); - m_demuxer_thread.request_stop(); - if (m_demuxer_thread.joinable()) { - m_demuxer_thread.join(); - } - if (m_video_decoder_thread.joinable()) { - m_video_decoder_thread.join(); - } - if (m_audio_decoder_thread.joinable()) { - m_audio_decoder_thread.join(); - } + m_video_decoder_thread.Stop(); + m_audio_decoder_thread.Stop(); + m_demuxer_thread.Stop(); + if (m_current_audio_frame.has_value()) { m_audio_buffers.Push(std::move(m_current_audio_frame.value())); m_current_audio_frame.reset(); @@ -510,12 +486,8 @@ void AvPlayerSource::DemuxerThread(std::stop_token stop) { m_video_frames_cv.Notify(); m_audio_frames_cv.Notify(); - if (m_video_decoder_thread.joinable()) { - m_video_decoder_thread.join(); - } - if (m_audio_decoder_thread.joinable()) { - m_audio_decoder_thread.join(); - } + m_video_decoder_thread.Join(); + m_audio_decoder_thread.Join(); m_state.OnEOF(); LOG_INFO(Lib_AvPlayer, "Demuxer Thread exited normally"); @@ -808,8 +780,8 @@ void AvPlayerSource::AudioDecoderThread(std::stop_token stop) { } bool AvPlayerSource::HasRunningThreads() const { - return m_demuxer_thread.joinable() || m_video_decoder_thread.joinable() || - m_audio_decoder_thread.joinable(); + return m_demuxer_thread.Joinable() || m_video_decoder_thread.Joinable() || + m_audio_decoder_thread.Joinable(); } } // namespace Libraries::AvPlayer diff --git a/src/core/libraries/avplayer/avplayer_source.h b/src/core/libraries/avplayer/avplayer_source.h index 505d74465..7e199c457 100644 --- a/src/core/libraries/avplayer/avplayer_source.h +++ b/src/core/libraries/avplayer/avplayer_source.h @@ -3,20 +3,18 @@ #pragma once -#include "avplayer.h" -#include "avplayer_common.h" -#include "avplayer_data_streamer.h" - -#include "common/polyfill_thread.h" -#include "common/types.h" -#include "core/libraries/kernel/thread_management.h" - #include #include #include #include #include -#include +#include + +#include "common/assert.h" +#include "core/libraries/avplayer/avplayer.h" +#include "core/libraries/avplayer/avplayer_common.h" +#include "core/libraries/avplayer/avplayer_data_streamer.h" +#include "core/libraries/kernel/threads.h" struct AVCodecContext; struct AVFormatContext; @@ -139,8 +137,6 @@ public: bool IsActive(); private: - using ScePthread = Kernel::ScePthread; - static void ReleaseAVPacket(AVPacket* packet); static void ReleaseAVFrame(AVFrame* frame); static void ReleaseAVCodecContext(AVCodecContext* context); @@ -204,9 +200,9 @@ private: EventCV m_stop_cv{}; std::mutex m_state_mutex{}; - std::jthread m_demuxer_thread{}; - std::jthread m_video_decoder_thread{}; - std::jthread m_audio_decoder_thread{}; + Kernel::Thread m_demuxer_thread{}; + Kernel::Thread m_video_decoder_thread{}; + Kernel::Thread m_audio_decoder_thread{}; AVFormatContextPtr m_avformat_context{nullptr, &ReleaseAVFormatContext}; AVCodecContextPtr m_video_codec_context{nullptr, &ReleaseAVCodecContext}; diff --git a/src/core/libraries/avplayer/avplayer_state.cpp b/src/core/libraries/avplayer/avplayer_state.cpp index e66100679..143df749c 100644 --- a/src/core/libraries/avplayer/avplayer_state.cpp +++ b/src/core/libraries/avplayer/avplayer_state.cpp @@ -1,27 +1,22 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "avplayer_file_streamer.h" -#include "avplayer_source.h" -#include "avplayer_state.h" - -#include "common/singleton.h" +#include "common/logging/log.h" #include "common/thread.h" -#include "core/libraries/error_codes.h" -#include "core/libraries/kernel/time_management.h" -#include "core/linker.h" +#include "core/libraries/avplayer/avplayer_error.h" +#include "core/libraries/avplayer/avplayer_source.h" +#include "core/libraries/avplayer/avplayer_state.h" +#include "core/tls.h" -#include +#include namespace Libraries::AvPlayer { -using namespace Kernel; - void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, SceAvPlayerEvents event_id, s32 source_id, void* event_data) { auto const self = reinterpret_cast(opaque); - if (event_id == SCE_AVPLAYER_STATE_READY) { + if (event_id == SceAvPlayerEvents::StateReady) { s32 video_stream_index = -1; s32 audio_stream_index = -1; s32 timedtext_stream_index = -1; @@ -41,36 +36,37 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, SceAvPlayer return; } - const std::string_view default_language( - reinterpret_cast(self->m_default_language)); + const std::string_view default_language{self->m_default_language}; switch (info.type) { - case SCE_AVPLAYER_VIDEO: + case SceAvPlayerStreamType::Video: if (video_stream_index == -1) { video_stream_index = stream_index; } if (!default_language.empty() && - default_language == reinterpret_cast(info.details.video.language_code)) { + default_language == info.details.video.language_code) { video_stream_index = stream_index; } break; - case SCE_AVPLAYER_AUDIO: + case SceAvPlayerStreamType::Audio: if (audio_stream_index == -1) { audio_stream_index = stream_index; } if (!default_language.empty() && - default_language == reinterpret_cast(info.details.video.language_code)) { + default_language == info.details.video.language_code) { audio_stream_index = stream_index; } break; - case SCE_AVPLAYER_TIMEDTEXT: + case SceAvPlayerStreamType::TimedText: if (default_language.empty()) { timedtext_stream_index = stream_index; break; } - if (default_language == reinterpret_cast(info.details.video.language_code)) { + if (default_language == info.details.video.language_code) { timedtext_stream_index = stream_index; } break; + default: + break; } } @@ -96,8 +92,7 @@ void AvPlayerState::DefaultEventCallback(void* opaque, SceAvPlayerEvents event_i const auto callback = self->m_event_replacement.event_callback; const auto ptr = self->m_event_replacement.object_ptr; if (callback != nullptr) { - const auto* linker = Common::Singleton::Instance(); - linker->ExecuteGuest(callback, ptr, event_id, 0, event_data); + Core::ExecuteGuest(callback, ptr, event_id, 0, event_data); } } @@ -123,10 +118,7 @@ AvPlayerState::~AvPlayerState() { std::unique_lock lock(m_source_mutex); m_up_source.reset(); } - if (m_controller_thread.joinable()) { - m_controller_thread.request_stop(); - m_controller_thread.join(); - } + m_controller_thread.Stop(); m_event_queue.Clear(); } @@ -150,7 +142,7 @@ bool AvPlayerState::AddSource(std::string_view path, SceAvPlayerSourceType sourc m_up_source = std::make_unique( *this, m_post_init_data.video_decoder_init.decoderType.video_type == - SCE_AVPLAYER_VIDEO_DECODER_TYPE_SOFTWARE2); + SceAvPlayerVideoDecoderType::Software2); if (!m_up_source->Init(m_init_data, path)) { SetState(AvState::Error); m_up_source.reset(); @@ -227,8 +219,7 @@ void AvPlayerState::WarningEvent(s32 id) { // Called inside GAME thread void AvPlayerState::StartControllerThread() { - m_controller_thread = - std::jthread([this](std::stop_token stop) { this->AvControllerThread(stop); }); + m_controller_thread.Run([this](std::stop_token stop) { this->AvControllerThread(stop); }); } // Called inside GAME thread @@ -327,23 +318,23 @@ void AvPlayerState::OnEOF() { void AvPlayerState::OnPlaybackStateChanged(AvState state) { switch (state) { case AvState::Ready: { - EmitEvent(SCE_AVPLAYER_STATE_READY); + EmitEvent(SceAvPlayerEvents::StateReady); break; } case AvState::Play: { - EmitEvent(SCE_AVPLAYER_STATE_PLAY); + EmitEvent(SceAvPlayerEvents::StatePlay); break; } case AvState::Stop: { - EmitEvent(SCE_AVPLAYER_STATE_STOP); + EmitEvent(SceAvPlayerEvents::StateStop); break; } case AvState::Pause: { - EmitEvent(SCE_AVPLAYER_STATE_PAUSE); + EmitEvent(SceAvPlayerEvents::StatePause); break; } case AvState::Buffering: { - EmitEvent(SCE_AVPLAYER_STATE_BUFFERING); + EmitEvent(SceAvPlayerEvents::StateBuffering); break; } default: diff --git a/src/core/libraries/avplayer/avplayer_state.h b/src/core/libraries/avplayer/avplayer_state.h index d106127e4..48cd17bf2 100644 --- a/src/core/libraries/avplayer/avplayer_state.h +++ b/src/core/libraries/avplayer/avplayer_state.h @@ -3,17 +3,14 @@ #pragma once -#include "avplayer.h" -#include "avplayer_data_streamer.h" -#include "avplayer_source.h" - -#include "common/polyfill_thread.h" -#include "core/libraries/kernel/thread_management.h" - #include #include #include +#include "core/libraries/avplayer/avplayer.h" +#include "core/libraries/avplayer/avplayer_source.h" +#include "core/libraries/kernel/threads.h" + namespace Libraries::AvPlayer { class Stream; @@ -72,7 +69,7 @@ private: SceAvPlayerPostInitData m_post_init_data{}; SceAvPlayerEventReplacement m_event_replacement{}; bool m_auto_start{}; - u8 m_default_language[4]{}; + char m_default_language[4]{}; std::atomic m_current_state; std::atomic m_previous_state; @@ -83,7 +80,7 @@ private: std::shared_mutex m_source_mutex{}; std::mutex m_state_machine_mutex{}; std::mutex m_event_handler_mutex{}; - std::jthread m_controller_thread{}; + Kernel::Thread m_controller_thread{}; AvPlayerQueue m_event_queue{}; }; diff --git a/src/core/libraries/error_codes.h b/src/core/libraries/error_codes.h index 6bbdc3080..e1902b61c 100644 --- a/src/core/libraries/error_codes.h +++ b/src/core/libraries/error_codes.h @@ -3,616 +3,6 @@ #pragma once -// posix error codes -constexpr int POSIX_EPERM = 1; -constexpr int POSIX_ENOENT = 2; -constexpr int POSIX_ESRCH = 3; -constexpr int POSIX_EINTR = 4; -constexpr int POSIX_EIO = 5; -constexpr int POSIX_ENXIO = 6; -constexpr int POSIX_E2BIG = 7; -constexpr int POSIX_ENOEXEC = 8; -constexpr int POSIX_EBADF = 9; -constexpr int POSIX_ECHILD = 10; -constexpr int POSIX_EDEADLK = 11; -constexpr int POSIX_ENOMEM = 12; -constexpr int POSIX_EACCES = 13; -constexpr int POSIX_EFAULT = 14; -constexpr int POSIX_ENOTBLK = 15; -constexpr int POSIX_EBUSY = 16; -constexpr int POSIX_EEXIST = 17; -constexpr int POSIX_EXDEV = 18; -constexpr int POSIX_ENODEV = 19; -constexpr int POSIX_ENOTDIR = 20; -constexpr int POSIX_EISDIR = 21; -constexpr int POSIX_EINVAL = 22; -constexpr int POSIX_ENFILE = 23; -constexpr int POSIX_EMFILE = 24; -constexpr int POSIX_ENOTTY = 25; -constexpr int POSIX_ETXTBSY = 26; -constexpr int POSIX_EFBIG = 27; -constexpr int POSIX_ENOSPC = 28; -constexpr int POSIX_ESPIPE = 29; -constexpr int POSIX_EROFS = 30; -constexpr int POSIX_EMLINK = 31; -constexpr int POSIX_EPIPE = 32; -constexpr int POSIX_EDOM = 33; -constexpr int POSIX_ERANGE = 34; -constexpr int POSIX_EAGAIN = 35; -constexpr int POSIX_EWOULDBLOCK = 35; -constexpr int POSIX_EINPROGRESS = 36; -constexpr int POSIX_EALREADY = 37; -constexpr int POSIX_ENOTSOCK = 38; -constexpr int POSIX_EDESTADDRREQ = 39; -constexpr int POSIX_EMSGSIZE = 40; -constexpr int POSIX_EPROTOTYPE = 41; -constexpr int POSIX_ENOPROTOOPT = 42; -constexpr int POSIX_EPROTONOSUPPORT = 43; -constexpr int POSIX_ESOCKTNOSUPPORT = 44; -constexpr int POSIX_EOPNOTSUPP = 45; -constexpr int POSIX_ENOTSUP = 45; -constexpr int POSIX_EPFNOSUPPORT = 46; -constexpr int POSIX_EAFNOSUPPORT = 47; -constexpr int POSIX_EADDRINUSE = 48; -constexpr int POSIX_EADDRNOTAVAIL = 49; -constexpr int POSIX_ENETDOWN = 50; -constexpr int POSIX_ENETUNREACH = 51; -constexpr int POSIX_ENETRESET = 52; -constexpr int POSIX_ECONNABORTED = 53; -constexpr int POSIX_ECONNRESET = 54; -constexpr int POSIX_ENOBUFS = 55; -constexpr int POSIX_EISCONN = 56; -constexpr int POSIX_ENOTCONN = 57; -constexpr int POSIX_ESHUTDOWN = 58; -constexpr int POSIX_ETOOMANYREFS = 59; -constexpr int POSIX_ETIMEDOUT = 60; -constexpr int POSIX_ECONNREFUSED = 61; -constexpr int POSIX_ELOOP = 62; -constexpr int POSIX_ENAMETOOLONG = 63; -constexpr int POSIX_EHOSTDOWN = 64; -constexpr int POSIX_EHOSTUNREACH = 65; -constexpr int POSIX_ENOTEMPTY = 66; -constexpr int POSIX_EPROCLIM = 67; -constexpr int POSIX_EUSERS = 68; -constexpr int POSIX_EDQUOT = 69; -constexpr int POSIX_ESTALE = 70; -constexpr int POSIX_EREMOTE = 71; -constexpr int POSIX_EBADRPC = 72; -constexpr int POSIX_ERPCMISMATCH = 73; -constexpr int POSIX_EPROGUNAVAIL = 74; -constexpr int POSIX_EPROGMISMATCH = 75; -constexpr int POSIX_EPROCUNAVAIL = 76; -constexpr int POSIX_ENOLCK = 77; -constexpr int POSIX_ENOSYS = 78; -constexpr int POSIX_EFTYPE = 79; -constexpr int POSIX_EAUTH = 80; -constexpr int POSIX_ENEEDAUTH = 81; -constexpr int POSIX_EIDRM = 82; -constexpr int POSIX_ENOMSG = 83; -constexpr int POSIX_EOVERFLOW = 84; -constexpr int POSIX_ECANCELED = 85; -constexpr int POSIX_EILSEQ = 86; -constexpr int POSIX_ENOATTR = 87; -constexpr int POSIX_EDOOFUS = 88; -constexpr int POSIX_EBADMSG = 89; -constexpr int POSIX_EMULTIHOP = 90; -constexpr int POSIX_ENOLINK = 91; -constexpr int POSIX_EPROTO = 92; -constexpr int POSIX_ENOTCAPABLE = 93; -constexpr int POSIX_ECAPMODE = 94; -constexpr int POSIX_ENOBLK = 95; -constexpr int POSIX_EICV = 96; -constexpr int POSIX_ENOPLAYGOENT = 97; -constexpr int POSIX_EREVOKE = 98; -constexpr int POSIX_ESDKVERSION = 99; -constexpr int POSIX_ESTART = 100; -constexpr int POSIX_ESTOP = 101; -constexpr int POSIX_EINVALID2MB = 102; -constexpr int POSIX_ELAST = 102; -constexpr int POSIX_EADHOC = 160; -constexpr int POSIX_EINACTIVEDISABLED = 163; -constexpr int POSIX_ENETNODATA = 164; -constexpr int POSIX_ENETDESC = 165; -constexpr int POSIX_ENETDESCTIMEDOUT = 166; -constexpr int POSIX_ENETINTR = 167; -constexpr int POSIX_ERETURN = 205; -constexpr int POSIX_EFPOS = 152; -constexpr int POSIX_ENODATA = 1040; -constexpr int POSIX_ENOSR = 1050; -constexpr int POSIX_ENOSTR = 1051; -constexpr int POSIX_ENOTRECOVERABLE = 1056; -constexpr int POSIX_EOTHER = 1062; -constexpr int POSIX_EOWNERDEAD = 1064; -constexpr int POSIX_ETIME = 1074; - -constexpr int SCE_OK = 0; - -// kernel error codes -constexpr int SCE_KERNEL_ERROR_UNKNOWN = 0x80020000; -constexpr int SCE_KERNEL_ERROR_EPERM = 0x80020001; -constexpr int SCE_KERNEL_ERROR_ENOENT = 0x80020002; -constexpr int SCE_KERNEL_ERROR_ESRCH = 0x80020003; -constexpr int SCE_KERNEL_ERROR_EINTR = 0x80020004; -constexpr int SCE_KERNEL_ERROR_EIO = 0x80020005; -constexpr int SCE_KERNEL_ERROR_ENXIO = 0x80020006; -constexpr int SCE_KERNEL_ERROR_E2BIG = 0x80020007; -constexpr int SCE_KERNEL_ERROR_ENOEXEC = 0x80020008; -constexpr int SCE_KERNEL_ERROR_EBADF = 0x80020009; -constexpr int SCE_KERNEL_ERROR_ECHILD = 0x8002000A; -constexpr int SCE_KERNEL_ERROR_EDEADLK = 0x8002000B; -constexpr int SCE_KERNEL_ERROR_ENOMEM = 0x8002000C; -constexpr int SCE_KERNEL_ERROR_EACCES = 0x8002000D; -constexpr int SCE_KERNEL_ERROR_EFAULT = 0x8002000E; -constexpr int SCE_KERNEL_ERROR_ENOTBLK = 0x8002000F; -constexpr int SCE_KERNEL_ERROR_EBUSY = 0x80020010; -constexpr int SCE_KERNEL_ERROR_EEXIST = 0x80020011; -constexpr int SCE_KERNEL_ERROR_EXDEV = 0x80020012; -constexpr int SCE_KERNEL_ERROR_ENODEV = 0x80020013; -constexpr int SCE_KERNEL_ERROR_ENOTDIR = 0x80020014; -constexpr int SCE_KERNEL_ERROR_EISDIR = 0x80020015; -constexpr int SCE_KERNEL_ERROR_EINVAL = 0x80020016; -constexpr int SCE_KERNEL_ERROR_ENFILE = 0x80020017; -constexpr int SCE_KERNEL_ERROR_EMFILE = 0x80020018; -constexpr int SCE_KERNEL_ERROR_ENOTTY = 0x80020019; -constexpr int SCE_KERNEL_ERROR_ETXTBSY = 0x8002001A; -constexpr int SCE_KERNEL_ERROR_EFBIG = 0x8002001B; -constexpr int SCE_KERNEL_ERROR_ENOSPC = 0x8002001C; -constexpr int SCE_KERNEL_ERROR_ESPIPE = 0x8002001D; -constexpr int SCE_KERNEL_ERROR_EROFS = 0x8002001E; -constexpr int SCE_KERNEL_ERROR_EMLINK = 0x8002001F; -constexpr int SCE_KERNEL_ERROR_EPIPE = 0x80020020; -constexpr int SCE_KERNEL_ERROR_EDOM = 0x80020021; -constexpr int SCE_KERNEL_ERROR_ERANGE = 0x80020022; -constexpr int SCE_KERNEL_ERROR_EAGAIN = 0x80020023; -constexpr int SCE_KERNEL_ERROR_EWOULDBLOCK = 0x80020023; -constexpr int SCE_KERNEL_ERROR_EINPROGRESS = 0x80020024; -constexpr int SCE_KERNEL_ERROR_EALREADY = 0x80020025; -constexpr int SCE_KERNEL_ERROR_ENOTSOCK = 0x80020026; -constexpr int SCE_KERNEL_ERROR_EDESTADDRREQ = 0x80020027; -constexpr int SCE_KERNEL_ERROR_EMSGSIZE = 0x80020028; -constexpr int SCE_KERNEL_ERROR_EPROTOTYPE = 0x80020029; -constexpr int SCE_KERNEL_ERROR_ENOPROTOOPT = 0x8002002A; -constexpr int SCE_KERNEL_ERROR_EPROTONOSUPPORT = 0x8002002B; -constexpr int SCE_KERNEL_ERROR_ESOCKTNOSUPPORT = 0x8002002C; -constexpr int SCE_KERNEL_ERROR_EOPNOTSUPP = 0x8002002D; -constexpr int SCE_KERNEL_ERROR_ENOTSUP = 0x8002002D; -constexpr int SCE_KERNEL_ERROR_EPFNOSUPPORT = 0x8002002E; -constexpr int SCE_KERNEL_ERROR_EAFNOSUPPORT = 0x8002002F; -constexpr int SCE_KERNEL_ERROR_EADDRINUSE = 0x80020030; -constexpr int SCE_KERNEL_ERROR_EADDRNOTAVAIL = 0x80020031; -constexpr int SCE_KERNEL_ERROR_ENETDOWN = 0x80020032; -constexpr int SCE_KERNEL_ERROR_ENETUNREACH = 0x80020033; -constexpr int SCE_KERNEL_ERROR_ENETRESET = 0x80020034; -constexpr int SCE_KERNEL_ERROR_ECONNABORTED = 0x80020035; -constexpr int SCE_KERNEL_ERROR_ECONNRESET = 0x80020036; -constexpr int SCE_KERNEL_ERROR_ENOBUFS = 0x80020037; -constexpr int SCE_KERNEL_ERROR_EISCONN = 0x80020038; -constexpr int SCE_KERNEL_ERROR_ENOTCONN = 0x80020039; -constexpr int SCE_KERNEL_ERROR_ESHUTDOWN = 0x8002003A; -constexpr int SCE_KERNEL_ERROR_ETOOMANYREFS = 0x8002003B; -constexpr int SCE_KERNEL_ERROR_ETIMEDOUT = 0x8002003C; -constexpr int SCE_KERNEL_ERROR_ECONNREFUSED = 0x8002003D; -constexpr int SCE_KERNEL_ERROR_ELOOP = 0x8002003E; -constexpr int SCE_KERNEL_ERROR_ENAMETOOLONG = 0x8002003F; -constexpr int SCE_KERNEL_ERROR_EHOSTDOWN = 0x80020040; -constexpr int SCE_KERNEL_ERROR_EHOSTUNREACH = 0x80020041; -constexpr int SCE_KERNEL_ERROR_ENOTEMPTY = 0x80020042; -constexpr int SCE_KERNEL_ERROR_EPROCLIM = 0x80020043; -constexpr int SCE_KERNEL_ERROR_EUSERS = 0x80020044; -constexpr int SCE_KERNEL_ERROR_EDQUOT = 0x80020045; -constexpr int SCE_KERNEL_ERROR_ESTALE = 0x80020046; -constexpr int SCE_KERNEL_ERROR_EREMOTE = 0x80020047; -constexpr int SCE_KERNEL_ERROR_EBADRPC = 0x80020048; -constexpr int SCE_KERNEL_ERROR_ERPCMISMATCH = 0x80020049; -constexpr int SCE_KERNEL_ERROR_EPROGUNAVAIL = 0x8002004A; -constexpr int SCE_KERNEL_ERROR_EPROGMISMATCH = 0x8002004B; -constexpr int SCE_KERNEL_ERROR_EPROCUNAVAIL = 0x8002004C; -constexpr int SCE_KERNEL_ERROR_ENOLCK = 0x8002004D; -constexpr int SCE_KERNEL_ERROR_ENOSYS = 0x8002004E; -constexpr int SCE_KERNEL_ERROR_EFTYPE = 0x8002004F; -constexpr int SCE_KERNEL_ERROR_EAUTH = 0x80020050; -constexpr int SCE_KERNEL_ERROR_ENEEDAUTH = 0x80020051; -constexpr int SCE_KERNEL_ERROR_EIDRM = 0x80020052; -constexpr int SCE_KERNEL_ERROR_ENOMSG = 0x80020053; -constexpr int SCE_KERNEL_ERROR_EOVERFLOW = 0x80020054; -constexpr int SCE_KERNEL_ERROR_ECANCELED = 0x80020055; -constexpr int SCE_KERNEL_ERROR_EILSEQ = 0x80020056; -constexpr int SCE_KERNEL_ERROR_ENOATTR = 0x80020057; -constexpr int SCE_KERNEL_ERROR_EDOOFUS = 0x80020058; -constexpr int SCE_KERNEL_ERROR_EBADMSG = 0x80020059; -constexpr int SCE_KERNEL_ERROR_EMULTIHOP = 0x8002005A; -constexpr int SCE_KERNEL_ERROR_ENOLINK = 0x8002005B; -constexpr int SCE_KERNEL_ERROR_EPROTO = 0x8002005C; -constexpr int SCE_KERNEL_ERROR_ENOTCAPABLE = 0x8002005D; -constexpr int SCE_KERNEL_ERROR_ECAPMODE = 0x8002005E; -constexpr int SCE_KERNEL_ERROR_ENOBLK = 0x8002005F; -constexpr int SCE_KERNEL_ERROR_EICV = 0x80020060; -constexpr int SCE_KERNEL_ERROR_ENOPLAYGOENT = 0x80020061; -constexpr int SCE_KERNEL_ERROR_EREVOKE = 0x80020062; -constexpr int SCE_KERNEL_ERROR_ESDKVERSION = 0x80020063; -constexpr int SCE_KERNEL_ERROR_ESTART = 0x80020064; -constexpr int SCE_KERNEL_ERROR_ESTOP = 0x80020065; - -// videoOut -constexpr int SCE_VIDEO_OUT_ERROR_INVALID_VALUE = 0x80290001; // invalid argument -constexpr int SCE_VIDEO_OUT_ERROR_INVALID_ADDRESS = 0x80290002; // invalid addresses -constexpr int SCE_VIDEO_OUT_ERROR_INVALID_TILING_MODE = 0x80290007; // invalid tiling mode -constexpr int SCE_VIDEO_OUT_ERROR_INVALID_ASPECT_RATIO = 0x80290008; // invalid aspect ration -constexpr int SCE_VIDEO_OUT_ERROR_RESOURCE_BUSY = 0x80290009; // already opened -constexpr int SCE_VIDEO_OUT_ERROR_INVALID_INDEX = 0x8029000A; // invalid buffer index -constexpr int SCE_VIDEO_OUT_ERROR_INVALID_HANDLE = 0x8029000B; // invalid handle -constexpr int SCE_VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE = 0x8029000C; // Invalid event queue -constexpr int SCE_VIDEO_OUT_ERROR_SLOT_OCCUPIED = 0x80290010; // slot already used -constexpr int SCE_VIDEO_OUT_ERROR_FLIP_QUEUE_FULL = 0x80290012; // flip queue is full -constexpr int SCE_VIDEO_OUT_ERROR_INVALID_OPTION = 0x8029001A; // Invalid buffer attribute option - // Generic constexpr int ORBIS_OK = 0x00000000; constexpr int ORBIS_FAIL = 0xFFFFFFFF; - -// Libkernel library -constexpr int ORBIS_KERNEL_ERROR_UNKNOWN = 0x80020000; -constexpr int ORBIS_KERNEL_ERROR_EPERM = 0x80020001; -constexpr int ORBIS_KERNEL_ERROR_ENOENT = 0x80020002; -constexpr int ORBIS_KERNEL_ERROR_ESRCH = 0x80020003; -constexpr int ORBIS_KERNEL_ERROR_EINTR = 0x80020004; -constexpr int ORBIS_KERNEL_ERROR_EIO = 0x80020005; -constexpr int ORBIS_KERNEL_ERROR_ENXIO = 0x80020006; -constexpr int ORBIS_KERNEL_ERROR_E2BIG = 0x80020007; -constexpr int ORBIS_KERNEL_ERROR_ENOEXEC = 0x80020008; -constexpr int ORBIS_KERNEL_ERROR_EBADF = 0x80020009; -constexpr int ORBIS_KERNEL_ERROR_ECHILD = 0x8002000A; -constexpr int ORBIS_KERNEL_ERROR_EDEADLK = 0x8002000B; -constexpr int ORBIS_KERNEL_ERROR_ENOMEM = 0x8002000C; -constexpr int ORBIS_KERNEL_ERROR_EACCES = 0x8002000D; -constexpr int ORBIS_KERNEL_ERROR_EFAULT = 0x8002000E; -constexpr int ORBIS_KERNEL_ERROR_ENOTBLK = 0x8002000F; -constexpr int ORBIS_KERNEL_ERROR_EBUSY = 0x80020010; -constexpr int ORBIS_KERNEL_ERROR_EEXIST = 0x80020011; -constexpr int ORBIS_KERNEL_ERROR_EXDEV = 0x80020012; -constexpr int ORBIS_KERNEL_ERROR_ENODEV = 0x80020013; -constexpr int ORBIS_KERNEL_ERROR_ENOTDIR = 0x80020014; -constexpr int ORBIS_KERNEL_ERROR_EISDIR = 0x80020015; -constexpr int ORBIS_KERNEL_ERROR_EINVAL = 0x80020016; -constexpr int ORBIS_KERNEL_ERROR_ENFILE = 0x80020017; -constexpr int ORBIS_KERNEL_ERROR_EMFILE = 0x80020018; -constexpr int ORBIS_KERNEL_ERROR_ENOTTY = 0x80020019; -constexpr int ORBIS_KERNEL_ERROR_ETXTBSY = 0x8002001A; -constexpr int ORBIS_KERNEL_ERROR_EFBIG = 0x8002001B; -constexpr int ORBIS_KERNEL_ERROR_ENOSPC = 0x8002001C; -constexpr int ORBIS_KERNEL_ERROR_ESPIPE = 0x8002001D; -constexpr int ORBIS_KERNEL_ERROR_EROFS = 0x8002001E; -constexpr int ORBIS_KERNEL_ERROR_EMLINK = 0x8002001F; -constexpr int ORBIS_KERNEL_ERROR_EPIPE = 0x80020020; -constexpr int ORBIS_KERNEL_ERROR_EDOM = 0x80020021; -constexpr int ORBIS_KERNEL_ERROR_ERANGE = 0x80020022; -constexpr int ORBIS_KERNEL_ERROR_EAGAIN = 0x80020023; -constexpr int ORBIS_KERNEL_ERROR_EWOULDBLOCK = 0x80020023; -constexpr int ORBIS_KERNEL_ERROR_EINPROGRESS = 0x80020024; -constexpr int ORBIS_KERNEL_ERROR_EALREADY = 0x80020025; -constexpr int ORBIS_KERNEL_ERROR_ENOTSOCK = 0x80020026; -constexpr int ORBIS_KERNEL_ERROR_EDESTADDRREQ = 0x80020027; -constexpr int ORBIS_KERNEL_ERROR_EMSGSIZE = 0x80020028; -constexpr int ORBIS_KERNEL_ERROR_EPROTOTYPE = 0x80020029; -constexpr int ORBIS_KERNEL_ERROR_ENOPROTOOPT = 0x8002002A; -constexpr int ORBIS_KERNEL_ERROR_EPROTONOSUPPORT = 0x8002002B; -constexpr int ORBIS_KERNEL_ERROR_ESOCKTNOSUPPORT = 0x8002002C; -constexpr int ORBIS_KERNEL_ERROR_ENOTSUP = 0x8002002D; -constexpr int ORBIS_KERNEL_ERROR_EOPNOTSUPP = 0x8002002D; -constexpr int ORBIS_KERNEL_ERROR_EPFNOSUPPORT = 0x8002002E; -constexpr int ORBIS_KERNEL_ERROR_EAFNOSUPPORT = 0x8002002F; -constexpr int ORBIS_KERNEL_ERROR_EADDRINUSE = 0x80020030; -constexpr int ORBIS_KERNEL_ERROR_EADDRNOTAVAIL = 0x80020031; -constexpr int ORBIS_KERNEL_ERROR_ENETDOWN = 0x80020032; -constexpr int ORBIS_KERNEL_ERROR_ENETUNREACH = 0x80020033; -constexpr int ORBIS_KERNEL_ERROR_ENETRESET = 0x80020034; -constexpr int ORBIS_KERNEL_ERROR_ECONNABORTED = 0x80020035; -constexpr int ORBIS_KERNEL_ERROR_ECONNRESET = 0x80020036; -constexpr int ORBIS_KERNEL_ERROR_ENOBUFS = 0x80020037; -constexpr int ORBIS_KERNEL_ERROR_EISCONN = 0x80020038; -constexpr int ORBIS_KERNEL_ERROR_ENOTCONN = 0x80020039; -constexpr int ORBIS_KERNEL_ERROR_ESHUTDOWN = 0x8002003A; -constexpr int ORBIS_KERNEL_ERROR_ETOOMANYREFS = 0x8002003B; -constexpr int ORBIS_KERNEL_ERROR_ETIMEDOUT = 0x8002003C; -constexpr int ORBIS_KERNEL_ERROR_ECONNREFUSED = 0x8002003D; -constexpr int ORBIS_KERNEL_ERROR_ELOOP = 0x8002003E; -constexpr int ORBIS_KERNEL_ERROR_ENAMETOOLONG = 0x8002003F; -constexpr int ORBIS_KERNEL_ERROR_EHOSTDOWN = 0x80020040; -constexpr int ORBIS_KERNEL_ERROR_EHOSTUNREACH = 0x80020041; -constexpr int ORBIS_KERNEL_ERROR_ENOTEMPTY = 0x80020042; -constexpr int ORBIS_KERNEL_ERROR_EPROCLIM = 0x80020043; -constexpr int ORBIS_KERNEL_ERROR_EUSERS = 0x80020044; -constexpr int ORBIS_KERNEL_ERROR_EDQUOT = 0x80020045; -constexpr int ORBIS_KERNEL_ERROR_ESTALE = 0x80020046; -constexpr int ORBIS_KERNEL_ERROR_EREMOTE = 0x80020047; -constexpr int ORBIS_KERNEL_ERROR_EBADRPC = 0x80020048; -constexpr int ORBIS_KERNEL_ERROR_ERPCMISMATCH = 0x80020049; -constexpr int ORBIS_KERNEL_ERROR_EPROGUNAVAIL = 0x8002004A; -constexpr int ORBIS_KERNEL_ERROR_EPROGMISMATCH = 0x8002004B; -constexpr int ORBIS_KERNEL_ERROR_EPROCUNAVAIL = 0x8002004C; -constexpr int ORBIS_KERNEL_ERROR_ENOLCK = 0x8002004D; -constexpr int ORBIS_KERNEL_ERROR_ENOSYS = 0x8002004E; -constexpr int ORBIS_KERNEL_ERROR_EFTYPE = 0x8002004F; -constexpr int ORBIS_KERNEL_ERROR_EAUTH = 0x80020050; -constexpr int ORBIS_KERNEL_ERROR_ENEEDAUTH = 0x80020051; -constexpr int ORBIS_KERNEL_ERROR_EIDRM = 0x80020052; -constexpr int ORBIS_KERNEL_ERROR_ENOMSG = 0x80020053; -constexpr int ORBIS_KERNEL_ERROR_EOVERFLOW = 0x80020054; -constexpr int ORBIS_KERNEL_ERROR_ECANCELED = 0x80020055; -constexpr int ORBIS_KERNEL_ERROR_EILSEQ = 0x80020056; -constexpr int ORBIS_KERNEL_ERROR_ENOATTR = 0x80020057; -constexpr int ORBIS_KERNEL_ERROR_EDOOFUS = 0x80020058; -constexpr int ORBIS_KERNEL_ERROR_EBADMSG = 0x80020059; -constexpr int ORBIS_KERNEL_ERROR_EMULTIHOP = 0x8002005A; -constexpr int ORBIS_KERNEL_ERROR_ENOLINK = 0x8002005B; -constexpr int ORBIS_KERNEL_ERROR_EPROTO = 0x8002005C; -constexpr int ORBIS_KERNEL_ERROR_ENOTCAPABLE = 0x8002005D; -constexpr int ORBIS_KERNEL_ERROR_ECAPMODE = 0x8002005E; -constexpr int ORBIS_KERNEL_ERROR_ENOBLK = 0x8002005F; -constexpr int ORBIS_KERNEL_ERROR_EICV = 0x80020060; -constexpr int ORBIS_KERNEL_ERROR_ENOPLAYGOENT = 0x80020061; - -// AudioOut library -constexpr int ORBIS_AUDIO_OUT_ERROR_NOT_OPENED = 0x80260001; -constexpr int ORBIS_AUDIO_OUT_ERROR_BUSY = 0x80260002; -constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_PORT = 0x80260003; -constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_POINTER = 0x80260004; -constexpr int ORBIS_AUDIO_OUT_ERROR_PORT_FULL = 0x80260005; -constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_SIZE = 0x80260006; -constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_FORMAT = 0x80260007; -constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_SAMPLE_FREQ = 0x80260008; -constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_VOLUME = 0x80260009; -constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_PORT_TYPE = 0x8026000A; -constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_CONF_TYPE = 0x8026000C; -constexpr int ORBIS_AUDIO_OUT_ERROR_OUT_OF_MEMORY = 0x8026000D; -constexpr int ORBIS_AUDIO_OUT_ERROR_ALREADY_INIT = 0x8026000E; -constexpr int ORBIS_AUDIO_OUT_ERROR_NOT_INIT = 0x8026000F; -constexpr int ORBIS_AUDIO_OUT_ERROR_MEMORY = 0x80260010; -constexpr int ORBIS_AUDIO_OUT_ERROR_SYSTEM_RESOURCE = 0x80260011; -constexpr int ORBIS_AUDIO_OUT_ERROR_TRANS_EVENT = 0x80260012; -constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_FLAG = 0x80260013; -constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_MIXLEVEL = 0x80260014; -constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_ARG = 0x80260015; -constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_PARAM = 0x80260016; -constexpr int ORBIS_AUDIO_OUT_ERROR_MASTERING_FATAL = 0x80260200; -constexpr int ORBIS_AUDIO_OUT_ERROR_MASTERING_INVALID_API_PARAM = 0x80260201; -constexpr int ORBIS_AUDIO_OUT_ERROR_MASTERING_INVALID_CONFIG = 0x80260202; -constexpr int ORBIS_AUDIO_OUT_ERROR_MASTERING_NOT_INITIALIZED = 0x80260203; -constexpr int ORBIS_AUDIO_OUT_ERROR_MASTERING_INVALID_STATES_ID = 0x80260204; - -// VideoOut library -constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_VALUE = 0x80290001; -constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS = 0x80290002; -constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_PIXEL_FORMAT = 0x80290003; -constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_PITCH = 0x80290004; -constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_RESOLUTION = 0x80290005; -constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_FLIP_MODE = 0x80290006; -constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_TILING_MODE = 0x80290007; -constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_ASPECT_RATIO = 0x80290008; -constexpr int ORBIS_VIDEO_OUT_ERROR_RESOURCE_BUSY = 0x80290009; -constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_INDEX = 0x8029000A; -constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE = 0x8029000B; -constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE = 0x8029000C; -constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT = 0x8029000D; -constexpr int ORBIS_VIDEO_OUT_ERROR_NO_EMPTY_SLOT = 0x8029000F; -constexpr int ORBIS_VIDEO_OUT_ERROR_SLOT_OCCUPIED = 0x80290010; -constexpr int ORBIS_VIDEO_OUT_ERROR_FLIP_QUEUE_FULL = 0x80290012; -constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_MEMORY = 0x80290013; -constexpr int ORBIS_VIDEO_OUT_ERROR_MEMORY_NOT_PHYSICALLY_CONTIGUOUS = 0x80290014; -constexpr int ORBIS_VIDEO_OUT_ERROR_MEMORY_INVALID_ALIGNMENT = 0x80290015; -constexpr int ORBIS_VIDEO_OUT_ERROR_UNSUPPORTED_OUTPUT_MODE = 0x80290016; -constexpr int ORBIS_VIDEO_OUT_ERROR_OVERFLOW = 0x80290017; -constexpr int ORBIS_VIDEO_OUT_ERROR_NO_DEVICE = 0x80290018; -constexpr int ORBIS_VIDEO_OUT_ERROR_UNAVAILABLE_OUTPUT_MODE = 0x80290019; -constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_OPTION = 0x8029001A; -constexpr int ORBIS_VIDEO_OUT_ERROR_UNKNOWN = 0x802900FE; -constexpr int ORBIS_VIDEO_OUT_ERROR_FATAL = 0x802900FF; -constexpr int ORBIS_VIDEO_OUT_ERROR_ENOMEM = 0x8029100C; - -// Pad library -constexpr int ORBIS_PAD_ERROR_INVALID_ARG = 0x80920001; -constexpr int ORBIS_PAD_ERROR_INVALID_PORT = 0x80920002; -constexpr int ORBIS_PAD_ERROR_INVALID_HANDLE = 0x80920003; -constexpr int ORBIS_PAD_ERROR_ALREADY_OPENED = 0x80920004; -constexpr int ORBIS_PAD_ERROR_NOT_INITIALIZED = 0x80920005; -constexpr int ORBIS_PAD_ERROR_INVALID_LIGHTBAR_SETTING = 0x80920006; -constexpr int ORBIS_PAD_ERROR_DEVICE_NOT_CONNECTED = 0x80920007; -constexpr int ORBIS_PAD_ERROR_DEVICE_NO_HANDLE = 0x80920008; -constexpr int ORBIS_PAD_ERROR_FATAL = 0x809200FF; -constexpr int ORBIS_PAD_ERROR_NOT_PERMITTED = 0x80920101; -constexpr int ORBIS_PAD_ERROR_INVALID_BUFFER_LENGTH = 0x80920102; -constexpr int ORBIS_PAD_ERROR_INVALID_REPORT_LENGTH = 0x80920103; -constexpr int ORBIS_PAD_ERROR_INVALID_REPORT_ID = 0x80920104; -constexpr int ORBIS_PAD_ERROR_SEND_AGAIN = 0x80920105; - -// UserService library -constexpr int ORBIS_USER_SERVICE_ERROR_INTERNAL = 0x80960001; -constexpr int ORBIS_USER_SERVICE_ERROR_NOT_INITIALIZED = 0x80960002; -constexpr int ORBIS_USER_SERVICE_ERROR_ALREADY_INITIALIZED = 0x80960003; -constexpr int ORBIS_USER_SERVICE_ERROR_NO_MEMORY = 0x80960004; -constexpr int ORBIS_USER_SERVICE_ERROR_INVALID_ARGUMENT = 0x80960005; -constexpr int ORBIS_USER_SERVICE_ERROR_OPERATION_NOT_SUPPORTED = 0x80960006; -constexpr int ORBIS_USER_SERVICE_ERROR_NO_EVENT = 0x80960007; -constexpr int ORBIS_USER_SERVICE_ERROR_NOT_LOGGED_IN = 0x80960009; -constexpr int ORBIS_USER_SERVICE_ERROR_BUFFER_TOO_SHORT = 0x8096000A; - -// SystemService library -constexpr int ORBIS_SYSTEM_SERVICE_ERROR_PARAMETER = 0x80A10003; -constexpr int ORBIS_SYSTEM_SERVICE_ERROR_NO_EVENT = 0x80A10004; - -// NpTrophy library -constexpr int ORBIS_NP_TROPHY_ERROR_UNKNOWN = 0x80551600; -constexpr int ORBIS_NP_TROPHY_ERROR_NOT_INITIALIZED = 0x80551601; -constexpr int ORBIS_NP_TROPHY_ERROR_ALREADY_INITIALIZED = 0x80551602; -constexpr int ORBIS_NP_TROPHY_ERROR_OUT_OF_MEMORY = 0x80551603; -constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT = 0x80551604; -constexpr int ORBIS_NP_TROPHY_ERROR_INSUFFICIENT_BUFFER = 0x80551605; -constexpr int ORBIS_NP_TROPHY_ERROR_EXCEEDS_MAX = 0x80551606; -constexpr int ORBIS_NP_TROPHY_ERROR_ABORT = 0x80551607; -constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE = 0x80551608; -constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT = 0x80551609; -constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_TROPHY_ID = 0x8055160A; -constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_GROUP_ID = 0x8055160B; -constexpr int ORBIS_NP_TROPHY_ERROR_TROPHY_ALREADY_UNLOCKED = 0x8055160C; -constexpr int ORBIS_NP_TROPHY_ERROR_PLATINUM_CANNOT_UNLOCK = 0x8055160D; -constexpr int ORBIS_NP_TROPHY_ERROR_ACCOUNTID_NOT_MATCH = 0x8055160E; -constexpr int ORBIS_NP_TROPHY_ERROR_NOT_REGISTERED = 0x8055160F; -constexpr int ORBIS_NP_TROPHY_ERROR_ALREADY_REGISTERED = 0x80551610; -constexpr int ORBIS_NP_TROPHY_ERROR_BROKEN_DATA = 0x80551611; -constexpr int ORBIS_NP_TROPHY_ERROR_INSUFFICIENT_SPACE = 0x80551612; -constexpr int ORBIS_NP_TROPHY_ERROR_CONTEXT_ALREADY_EXISTS = 0x80551613; -constexpr int ORBIS_NP_TROPHY_ERROR_ICON_FILE_NOT_FOUND = 0x80551614; -constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_TRP_FILE_FORMAT = 0x80551616; -constexpr int ORBIS_NP_TROPHY_ERROR_UNSUPPORTED_TRP_FILE = 0x80551617; -constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_TROPHY_CONF_FORMAT = 0x80551618; -constexpr int ORBIS_NP_TROPHY_ERROR_UNSUPPORTED_TROPHY_CONF = 0x80551619; -constexpr int ORBIS_NP_TROPHY_ERROR_TROPHY_NOT_UNLOCKED = 0x8055161A; -constexpr int ORBIS_NP_TROPHY_ERROR_USER_NOT_FOUND = 0x8055161C; -constexpr int ORBIS_NP_TROPHY_ERROR_USER_NOT_LOGGED_IN = 0x8055161D; -constexpr int ORBIS_NP_TROPHY_ERROR_CONTEXT_USER_LOGOUT = 0x8055161E; -constexpr int ORBIS_NP_TROPHY_ERROR_USE_TRP_FOR_DEVELOPMENT = 0x8055161F; -constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_NP_SERVICE_LABEL = 0x80551621; -constexpr int ORBIS_NP_TROPHY_ERROR_NOT_SUPPORTED = 0x80551622; -constexpr int ORBIS_NP_TROPHY_ERROR_CONTEXT_EXCEEDS_MAX = 0x80551623; -constexpr int ORBIS_NP_TROPHY_ERROR_HANDLE_EXCEEDS_MAX = 0x80551624; -constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_USER_ID = 0x80551625; -constexpr int ORBIS_NP_TROPHY_ERROR_TITLE_CONF_NOT_INSTALLED = 0x80551626; -constexpr int ORBIS_NP_TROPHY_ERROR_BROKEN_TITLE_CONF = 0x80551627; -constexpr int ORBIS_NP_TROPHY_ERROR_INCONSISTENT_TITLE_CONF = 0x80551628; -constexpr int ORBIS_NP_TROPHY_ERROR_TITLE_BACKGROUND = 0x80551629; -constexpr int ORBIS_NP_TROPHY_ERROR_SCREENSHOT_DISABLED = 0x8055162B; -constexpr int ORBIS_NP_TROPHY_ERROR_SCREENSHOT_DISPLAY_BUFFER_NOT_IN_USE = 0x8055162D; - -// AvPlayer library -constexpr int ORBIS_AVPLAYER_ERROR_INVALID_PARAMS = 0x806A0001; -constexpr int ORBIS_AVPLAYER_ERROR_OPERATION_FAILED = 0x806A0002; -constexpr int ORBIS_AVPLAYER_ERROR_NO_MEMORY = 0x806A0003; -constexpr int ORBIS_AVPLAYER_ERROR_NOT_SUPPORTED = 0x806A0004; -constexpr int ORBIS_AVPLAYER_ERROR_WAR_FILE_NONINTERLEAVED = 0x806A00A0; -constexpr int ORBIS_AVPLAYER_ERROR_WAR_LOOPING_BACK = 0x806A00A1; -constexpr int ORBIS_AVPLAYER_ERROR_WAR_JUMP_COMPLETE = 0x806A00A3; -constexpr int ORBIS_AVPLAYER_ERROR_INFO_MARLIN_ENCRY = 0x806A00B0; -constexpr int ORBIS_AVPLAYER_ERROR_INFO_PLAYREADY_ENCRY = 0x806A00B4; -constexpr int ORBIS_AVPLAYER_ERROR_INFO_AES_ENCRY = 0x806A00B5; -constexpr int ORBIS_AVPLAYER_ERROR_INFO_OTHER_ENCRY = 0x806A00BF; - -// AppContent library -constexpr int ORBIS_APP_CONTENT_ERROR_PARAMETER = 0x80D90002; -constexpr int ORBIS_APP_CONTENT_ERROR_DRM_NO_ENTITLEMENT = 0x80D90007; -constexpr int ORBIS_APP_CONTENT_ERROR_NOT_FOUND = 0x80D90005; - -// Fiber library -constexpr int ORBIS_FIBER_ERROR_NULL = 0x80590001; -constexpr int ORBIS_FIBER_ERROR_ALIGNMENT = 0x80590002; -constexpr int ORBIS_FIBER_ERROR_RANGE = 0x80590003; -constexpr int ORBIS_FIBER_ERROR_INVALID = 0x80590004; -constexpr int ORBIS_FIBER_ERROR_PERMISSION = 0x80590005; -constexpr int ORBIS_FIBER_ERROR_STATE = 0x80590006; - -// ImeDialog library -constexpr int ORBIS_ERROR_DIALOG_ERROR_NOT_INITIALIZED = 0x80ED0001; -constexpr int ORBIS_ERROR_DIALOG_ERROR_ALREADY_INITIALIZED = 0x80ED0002; -constexpr int ORBIS_ERROR_DIALOG_ERROR_PARAM_INVALID = 0x80ED0003; -constexpr int ORBIS_ERROR_DIALOG_ERROR_UNEXPECTED_FATAL = 0x80ED0004; -constexpr int ORBIS_ERROR_DIALOG_ERROR_INVALID_STATE = 0x80ED0005; -constexpr int ORBIS_ERROR_DIALOG_ERROR_SERVICE_BUSY = 0x80ED0006; -constexpr int ORBIS_ERROR_DIALOG_ERROR_INVALID_USER_ID = 0x80ED0007; - -// Ime library -constexpr int ORBIS_IME_ERROR_BUSY = 0x80BC0001; -constexpr int ORBIS_IME_ERROR_NOT_OPENED = 0x80BC0002; -constexpr int ORBIS_IME_ERROR_NO_MEMORY = 0x80BC0003; -constexpr int ORBIS_IME_ERROR_CONNECTION_FAILED = 0x80BC0004; -constexpr int ORBIS_IME_ERROR_TOO_MANY_REQUESTS = 0x80BC0005; -constexpr int ORBIS_IME_ERROR_INVALID_TEXT = 0x80BC0006; -constexpr int ORBIS_IME_ERROR_EVENT_OVERFLOW = 0x80BC0007; -constexpr int ORBIS_IME_ERROR_NOT_ACTIVE = 0x80BC0008; -constexpr int ORBIS_IME_ERROR_IME_SUSPENDING = 0x80BC0009; -constexpr int ORBIS_IME_ERROR_DEVICE_IN_USE = 0x80BC000A; -constexpr int ORBIS_IME_ERROR_INVALID_USER_ID = 0x80BC0010; -constexpr int ORBIS_IME_ERROR_INVALID_TYPE = 0x80BC0011; -constexpr int ORBIS_IME_ERROR_INVALID_SUPPORTED_LANGUAGES = 0x80BC0012; -constexpr int ORBIS_IME_ERROR_INVALID_ENTER_LABEL = 0x80BC0013; -constexpr int ORBIS_IME_ERROR_INVALID_INPUT_METHOD = 0x80BC0014; -constexpr int ORBIS_IME_ERROR_INVALID_OPTION = 0x80BC0015; -constexpr int ORBIS_IME_ERROR_INVALID_MAX_TEXT_LENGTH = 0x80BC0016; -constexpr int ORBIS_IME_ERROR_INVALID_INPUT_TEXT_BUFFER = 0x80BC0017; -constexpr int ORBIS_IME_ERROR_INVALID_POSX = 0x80BC0018; -constexpr int ORBIS_IME_ERROR_INVALID_POSY = 0x80BC0019; -constexpr int ORBIS_IME_ERROR_INVALID_HORIZONTAL_ALIGNMENT = 0x80BC001A; -constexpr int ORBIS_IME_ERROR_INVALID_VERTICAL_ALIGNMENT = 0x80BC001B; -constexpr int ORBIS_IME_ERROR_INVALID_EXTENDED = 0x80BC001C; -constexpr int ORBIS_IME_ERROR_INVALID_KEYBOARD_TYPE = 0x80BC001D; -constexpr int ORBIS_IME_ERROR_INVALID_WORK = 0x80BC0020; -constexpr int ORBIS_IME_ERROR_INVALID_ARG = 0x80BC0021; -constexpr int ORBIS_IME_ERROR_INVALID_HANDLER = 0x80BC0022; -constexpr int ORBIS_IME_ERROR_NO_RESOURCE_ID = 0x80BC0023; -constexpr int ORBIS_IME_ERROR_INVALID_MODE = 0x80BC0024; -constexpr int ORBIS_IME_ERROR_INVALID_PARAM = 0x80BC0030; -constexpr int ORBIS_IME_ERROR_INVALID_ADDRESS = 0x80BC0031; -constexpr int ORBIS_IME_ERROR_INVALID_RESERVED = 0x80BC0032; -constexpr int ORBIS_IME_ERROR_INVALID_TIMING = 0x80BC0033; -constexpr int ORBIS_IME_ERROR_INTERNAL = 0x80BC00FF; - -// Videodec2 library -constexpr int ORBIS_VIDEODEC2_ERROR_API_FAIL = 0x811D0100; -constexpr int ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE = 0x811D0101; -constexpr int ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER = 0x811D0102; -constexpr int ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE = 0x811D0103; -constexpr int ORBIS_VIDEODEC2_ERROR_MEMORY_SIZE = 0x811D0104; -constexpr int ORBIS_VIDEODEC2_ERROR_MEMORY_POINTER = 0x811D0105; -constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_BUFFER_SIZE = 0x811D0106; -constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_BUFFER_POINTER = 0x811D0107; -constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_BUFFER_ALIGNMENT = 0x811D0108; -constexpr int ORBIS_VIDEODEC2_ERROR_NOT_ONION_MEMORY = 0x811D0109; -constexpr int ORBIS_VIDEODEC2_ERROR_NOT_GARLIC_MEMORY = 0x811D010A; -constexpr int ORBIS_VIDEODEC2_ERROR_NOT_DIRECT_MEMORY = 0x811D010B; -constexpr int ORBIS_VIDEODEC2_ERROR_MEMORY_INFO = 0x811D010C; -constexpr int ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_SIZE = 0x811D010D; -constexpr int ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_POINTER = 0x811D010E; -constexpr int ORBIS_VIDEODEC2_ERROR_OUTPUT_INFO = 0x811D010F; -constexpr int ORBIS_VIDEODEC2_ERROR_COMPUTE_QUEUE = 0x811D0110; -constexpr int ORBIS_VIDEODEC2_ERROR_FATAL_STATE = 0x811D0111; -constexpr int ORBIS_VIDEODEC2_ERROR_PRESET_VALUE = 0x811D0112; -constexpr int ORBIS_VIDEODEC2_ERROR_CONFIG_INFO = 0x811D0200; -constexpr int ORBIS_VIDEODEC2_ERROR_COMPUTE_PIPE_ID = 0x811D0201; -constexpr int ORBIS_VIDEODEC2_ERROR_COMPUTE_QUEUE_ID = 0x811D0202; -constexpr int ORBIS_VIDEODEC2_ERROR_RESOURCE_TYPE = 0x811D0203; -constexpr int ORBIS_VIDEODEC2_ERROR_CODEC_TYPE = 0x811D0204; -constexpr int ORBIS_VIDEODEC2_ERROR_PROFILE_LEVEL = 0x811D0205; -constexpr int ORBIS_VIDEODEC2_ERROR_PIPELINE_DEPTH = 0x811D0206; -constexpr int ORBIS_VIDEODEC2_ERROR_AFFINITY_MASK = 0x811D0207; -constexpr int ORBIS_VIDEODEC2_ERROR_THREAD_PRIORITY = 0x811D0208; -constexpr int ORBIS_VIDEODEC2_ERROR_DPB_FRAME_COUNT = 0x811D0209; -constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_WIDTH_HEIGHT = 0x811D020A; -constexpr int ORBIS_VIDEODEC2_ERROR_EXTRA_CONFIG_INFO = 0x811D020B; -constexpr int ORBIS_VIDEODEC2_ERROR_NEW_SEQUENCE = 0x811D0300; -constexpr int ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT = 0x811D0301; -constexpr int ORBIS_VIDEODEC2_ERROR_OVERSIZE_DECODE = 0x811D0302; -constexpr int ORBIS_VIDEODEC2_ERROR_INVALID_SEQUENCE = 0x811D0303; -constexpr int ORBIS_VIDEODEC2_ERROR_FATAL_STREAM = 0x811D0304; - -// Videodec library - -constexpr int ORBIS_VIDEODEC_ERROR_API_FAIL = 0x80C10000; -constexpr int ORBIS_VIDEODEC_ERROR_CODEC_TYPE = 0x80C10001; -constexpr int ORBIS_VIDEODEC_ERROR_STRUCT_SIZE = 0x80C10002; -constexpr int ORBIS_VIDEODEC_ERROR_HANDLE = 0x80C10003; -constexpr int ORBIS_VIDEODEC_ERROR_CPU_MEMORY_SIZE = 0x80C10004; -constexpr int ORBIS_VIDEODEC_ERROR_CPU_MEMORY_POINTER = 0x80C10005; -constexpr int ORBIS_VIDEODEC_ERROR_CPU_GPU_MEMORY_SIZE = 0x80C10006; -constexpr int ORBIS_VIDEODEC_ERROR_CPU_GPU_MEMORY_POINTER = 0x80C10007; -constexpr int ORBIS_VIDEODEC_ERROR_SHADER_CONTEXT_POINTER = 0x80C10008; -constexpr int ORBIS_VIDEODEC_ERROR_AU_SIZE = 0x80C10009; -constexpr int ORBIS_VIDEODEC_ERROR_AU_POINTER = 0x80C1000A; -constexpr int ORBIS_VIDEODEC_ERROR_FRAME_BUFFER_SIZE = 0x80C1000B; -constexpr int ORBIS_VIDEODEC_ERROR_FRAME_BUFFER_POINTER = 0x80C1000C; -constexpr int ORBIS_VIDEODEC_ERROR_FRAME_BUFFER_ALIGNMENT = 0x80C1000D; -constexpr int ORBIS_VIDEODEC_ERROR_CONFIG_INFO = 0x80C1000E; -constexpr int ORBIS_VIDEODEC_ERROR_ARGUMENT_POINTER = 0x80C1000F; -constexpr int ORBIS_VIDEODEC_ERROR_NEW_SEQUENCE = 0x80C10010; -constexpr int ORBIS_VIDEODEC_ERROR_DECODE_AU = 0x80C10011; -constexpr int ORBIS_VIDEODEC_ERROR_MISMATCH_SPEC = 0x80C10012; -constexpr int ORBIS_VIDEODEC_ERROR_INVALID_SEQUENCE = 0x80C10013; -constexpr int ORBIS_VIDEODEC_ERROR_FATAL_STREAM = 0x80C10014; -constexpr int ORBIS_VIDEODEC_ERROR_FATAL_STATE = 0x80C10015; diff --git a/src/core/libraries/fiber/fiber.cpp b/src/core/libraries/fiber/fiber.cpp index a0bfd6850..c28d8d734 100644 --- a/src/core/libraries/fiber/fiber.cpp +++ b/src/core/libraries/fiber/fiber.cpp @@ -4,10 +4,9 @@ #include "fiber.h" #include "common/logging/log.h" -#include "common/singleton.h" -#include "core/libraries/error_codes.h" +#include "core/libraries/fiber/fiber_error.h" #include "core/libraries/libs.h" -#include "core/linker.h" +#include "core/tls.h" #ifdef _WIN64 #include @@ -31,9 +30,7 @@ void FiberEntry(void* param) { argRun = *fiber->pArgRun; } - const auto* linker = Common::Singleton::Instance(); - linker->ExecuteGuest(fiber->entry, fiber->argOnInitialize, argRun); - + Core::ExecuteGuest(fiber->entry, fiber->argOnInitialize, argRun); UNREACHABLE(); } @@ -281,4 +278,4 @@ void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("JzyT91ucGDc", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberRename); } -} // namespace Libraries::Fiber \ No newline at end of file +} // namespace Libraries::Fiber diff --git a/src/core/libraries/fiber/fiber.h b/src/core/libraries/fiber/fiber.h index 930409caa..00099f93b 100644 --- a/src/core/libraries/fiber/fiber.h +++ b/src/core/libraries/fiber/fiber.h @@ -26,17 +26,12 @@ struct SceFiber { u64 signature; FiberState state; SceFiberEntry entry; - u64 argOnInitialize; - u64 argRun; u64* pArgRun; - u64 argReturn; u64* pArgReturn; - u64 sizeContext; - char name[ORBIS_FIBER_MAX_NAME_LENGTH]; void* handle; }; @@ -53,7 +48,7 @@ struct SceFiberInfo { }; static_assert(sizeof(SceFiberInfo) <= 128); -typedef void* SceFiberOptParam; +using SceFiberOptParam = void*; s32 PS4_SYSV_ABI sceFiberInitialize(SceFiber* fiber, const char* name, SceFiberEntry entry, u64 argOnInitialize, void* addrContext, u64 sizeContext, diff --git a/src/core/libraries/fiber/fiber_error.h b/src/core/libraries/fiber/fiber_error.h new file mode 100644 index 000000000..c89fe30b1 --- /dev/null +++ b/src/core/libraries/fiber/fiber_error.h @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// Fiber library +constexpr int ORBIS_FIBER_ERROR_NULL = 0x80590001; +constexpr int ORBIS_FIBER_ERROR_ALIGNMENT = 0x80590002; +constexpr int ORBIS_FIBER_ERROR_RANGE = 0x80590003; +constexpr int ORBIS_FIBER_ERROR_INVALID = 0x80590004; +constexpr int ORBIS_FIBER_ERROR_PERMISSION = 0x80590005; +constexpr int ORBIS_FIBER_ERROR_STATE = 0x80590006; diff --git a/src/core/libraries/gnmdriver/gnm_error.h b/src/core/libraries/gnmdriver/gnm_error.h index eab684a24..b8e57f6f5 100644 --- a/src/core/libraries/gnmdriver/gnm_error.h +++ b/src/core/libraries/gnmdriver/gnm_error.h @@ -3,6 +3,8 @@ #pragma once +#include "core/libraries/error_codes.h" + constexpr int ORBIS_GNM_ERROR_SUBMISSION_FAILED_INVALID_ARGUMENT = 0x80D11000; constexpr int ORBIS_GNM_ERROR_SUBMISSION_NOT_ENOUGH_RESOURCES = 0x80D11001; constexpr int ORBIS_GNM_ERROR_SUBMISSION_AND_FLIP_FAILED_INVALID_COMMAND_BUFFER = 0x80D11080; diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index 08f534c72..18035e6ce 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -8,21 +8,21 @@ #include "common/config.h" #include "common/debug.h" #include "common/logging/log.h" -#include "common/path_util.h" #include "common/slot_vector.h" #include "core/address_space.h" #include "core/debug_state.h" -#include "core/libraries/error_codes.h" -#include "core/libraries/kernel/libkernel.h" +#include "core/libraries/gnmdriver/gnm_error.h" +#include "core/libraries/kernel/orbis_error.h" +#include "core/libraries/kernel/process.h" #include "core/libraries/libs.h" #include "core/libraries/videoout/video_out.h" #include "core/platform.h" #include "video_core/amdgpu/liverpool.h" #include "video_core/amdgpu/pm4_cmds.h" -#include "video_core/renderer_vulkan/renderer_vulkan.h" +#include "video_core/renderer_vulkan/vk_presenter.h" extern Frontend::WindowSDL* g_window; -std::unique_ptr renderer; +std::unique_ptr presenter; std::unique_ptr liverpool; namespace Libraries::GnmDriver { @@ -308,6 +308,7 @@ struct AscQueueInfo { }; static Common::SlotVector asc_queues{}; static constexpr VAddr tessellation_factors_ring_addr = Core::SYSTEM_RESERVED_MAX - 0xFFFFFFF; +static constexpr u32 tessellation_offchip_buffer_size = 0x800000u; static void ResetSubmissionLock(Platform::InterruptId irq) { std::unique_lock lock{m_submission}; @@ -376,9 +377,12 @@ int PS4_SYSV_ABI sceGnmAreSubmitsAllowed() { return submission_lock == 0; } -int PS4_SYSV_ABI sceGnmBeginWorkload() { - LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceGnmBeginWorkload(u32 workload_stream, u64* workload) { + if (workload) { + *workload = (-(u32)(workload_stream < 0x10) & 1); + return 0xf < workload_stream; + } + return 3; } s32 PS4_SYSV_ABI sceGnmComputeWaitOnAddress(u32* cmdbuf, u32 size, uintptr_t addr, u32 mask, @@ -412,9 +416,12 @@ int PS4_SYSV_ABI sceGnmComputeWaitSemaphore() { return ORBIS_OK; } -int PS4_SYSV_ABI sceGnmCreateWorkloadStream() { - LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceGnmCreateWorkloadStream(u64 param1, u32* workload_stream) { + if (param1 != 0 && workload_stream) { + *workload_stream = 1; + return 0; + } + return 3; } int PS4_SYSV_ABI sceGnmDebuggerGetAddressWatch() { @@ -537,15 +544,15 @@ void PS4_SYSV_ABI sceGnmDingDong(u32 gnm_vqid, u32 next_offs_dw) { .base_addr = base_addr, }); } - liverpool->SubmitAsc(vqid, acb_span); + liverpool->SubmitAsc(gnm_vqid, acb_span); *asc_queue.read_addr += acb_size; *asc_queue.read_addr %= asc_queue.ring_size_dw * 4; } -int PS4_SYSV_ABI sceGnmDingDongForWorkload() { - LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); - return ORBIS_OK; +void PS4_SYSV_ABI sceGnmDingDongForWorkload(u32 gnm_vqid, u32 next_offs_dw, u64 workload_id) { + LOG_DEBUG(Lib_GnmDriver, "called, redirecting to sceGnmDingDong"); + sceGnmDingDong(gnm_vqid, next_offs_dw); } int PS4_SYSV_ABI sceGnmDisableMipStatsReport() { @@ -672,18 +679,50 @@ s32 PS4_SYSV_ABI sceGnmDrawIndexIndirect(u32* cmdbuf, u32 size, u32 data_offset, return -1; } -int PS4_SYSV_ABI sceGnmDrawIndexIndirectCountMulti() { - LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceGnmDrawIndexIndirectCountMulti(u32* cmdbuf, u32 size, u32 data_offset, + u32 max_count, u64 count_addr, u32 shader_stage, + u32 vertex_sgpr_offset, u32 instance_sgpr_offset, + u32 flags) { + LOG_TRACE(Lib_GnmDriver, "called"); + + if (cmdbuf && (size == 16) && (shader_stage < ShaderStages::Max) && + (vertex_sgpr_offset < 0x10u) && (instance_sgpr_offset < 0x10u)) { + + cmdbuf = WriteHeader(cmdbuf, 2); + cmdbuf = WriteBody(cmdbuf, 0u); + cmdbuf += 1; + + const auto predicate = flags & 1 ? PM4Predicate::PredEnable : PM4Predicate::PredDisable; + cmdbuf = WriteHeader( + cmdbuf, 9, 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] = (count_addr != 0 ? 1u : 0u) << 0x1e; + cmdbuf[4] = max_count; + *(u64*)(&cmdbuf[5]) = count_addr; + cmdbuf[7] = sizeof(DrawIndexedIndirectArgs); + cmdbuf[8] = 0; + + cmdbuf += 9; + WriteTrailingNop<2>(cmdbuf); + return ORBIS_OK; + } + return -1; } int PS4_SYSV_ABI sceGnmDrawIndexIndirectMulti() { LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); + UNREACHABLE(); return ORBIS_OK; } int PS4_SYSV_ABI sceGnmDrawIndexMultiInstanced() { LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); + UNREACHABLE(); return ORBIS_OK; } @@ -730,11 +769,13 @@ s32 PS4_SYSV_ABI sceGnmDrawIndirect(u32* cmdbuf, u32 size, u32 data_offset, u32 int PS4_SYSV_ABI sceGnmDrawIndirectCountMulti() { LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); + UNREACHABLE(); return ORBIS_OK; } int PS4_SYSV_ABI sceGnmDrawIndirectMulti() { LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); + UNREACHABLE(); return ORBIS_OK; } @@ -917,9 +958,11 @@ int PS4_SYSV_ABI sceGnmDriverTriggerCapture() { return ORBIS_OK; } -int PS4_SYSV_ABI sceGnmEndWorkload() { - LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceGnmEndWorkload(u64 workload) { + if (workload != 0) { + return (0xf < ((workload >> 0x38) & 0xff)) * 2; + } + return 2; } s32 PS4_SYSV_ABI sceGnmFindResourcesPublic() { @@ -992,8 +1035,8 @@ int PS4_SYSV_ABI sceGnmGetNumTcaUnits() { } int PS4_SYSV_ABI sceGnmGetOffChipTessellationBufferSize() { - LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); - return ORBIS_OK; + LOG_TRACE(Lib_GnmDriver, "called"); + return tessellation_offchip_buffer_size; } int PS4_SYSV_ABI sceGnmGetOwnerName() { @@ -2089,6 +2132,14 @@ s32 PS4_SYSV_ABI sceGnmSubmitAndFlipCommandBuffers(u32 count, u32* dcb_gpu_addrs u32* dcb_sizes_in_bytes, u32* ccb_gpu_addrs[], u32* ccb_sizes_in_bytes, u32 vo_handle, u32 buf_idx, u32 flip_mode, u32 flip_arg) { + return sceGnmSubmitAndFlipCommandBuffersForWorkload( + count, count, dcb_gpu_addrs, dcb_sizes_in_bytes, ccb_gpu_addrs, ccb_sizes_in_bytes, + vo_handle, buf_idx, flip_mode, flip_arg); +} + +s32 PS4_SYSV_ABI sceGnmSubmitAndFlipCommandBuffersForWorkload( + u32 workload, u32 count, u32* dcb_gpu_addrs[], u32* dcb_sizes_in_bytes, u32* ccb_gpu_addrs[], + u32* ccb_sizes_in_bytes, u32 vo_handle, u32 buf_idx, u32 flip_mode, u32 flip_arg) { LOG_DEBUG(Lib_GnmDriver, "called [buf = {}]", buf_idx); auto* cmdbuf = dcb_gpu_addrs[count - 1]; @@ -2105,14 +2156,11 @@ s32 PS4_SYSV_ABI sceGnmSubmitAndFlipCommandBuffers(u32 count, u32* dcb_gpu_addrs ccb_sizes_in_bytes); } -int PS4_SYSV_ABI sceGnmSubmitAndFlipCommandBuffersForWorkload() { - LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); - return ORBIS_OK; -} - -s32 PS4_SYSV_ABI sceGnmSubmitCommandBuffers(u32 count, const u32* dcb_gpu_addrs[], - u32* dcb_sizes_in_bytes, const u32* ccb_gpu_addrs[], - u32* ccb_sizes_in_bytes) { +int PS4_SYSV_ABI sceGnmSubmitCommandBuffersForWorkload(u32 workload, u32 count, + const u32* dcb_gpu_addrs[], + u32* dcb_sizes_in_bytes, + const u32* ccb_gpu_addrs[], + u32* ccb_sizes_in_bytes) { LOG_DEBUG(Lib_GnmDriver, "called"); if (!dcb_gpu_addrs || !dcb_sizes_in_bytes) { @@ -2197,9 +2245,11 @@ s32 PS4_SYSV_ABI sceGnmSubmitCommandBuffers(u32 count, const u32* dcb_gpu_addrs[ return ORBIS_OK; } -int PS4_SYSV_ABI sceGnmSubmitCommandBuffersForWorkload() { - LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceGnmSubmitCommandBuffers(u32 count, const u32* dcb_gpu_addrs[], + u32* dcb_sizes_in_bytes, const u32* ccb_gpu_addrs[], + u32* ccb_sizes_in_bytes) { + return sceGnmSubmitCommandBuffersForWorkload(count, count, dcb_gpu_addrs, dcb_sizes_in_bytes, + ccb_gpu_addrs, ccb_sizes_in_bytes); } int PS4_SYSV_ABI sceGnmSubmitDone() { @@ -2438,8 +2488,8 @@ int PS4_SYSV_ABI sceGnmValidateGetVersion() { } int PS4_SYSV_ABI sceGnmValidateOnSubmitEnabled() { - LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); - return ORBIS_OK; + LOG_TRACE(Lib_GnmDriver, "called"); + return 0; } int PS4_SYSV_ABI sceGnmValidateResetState() { @@ -2708,9 +2758,9 @@ int PS4_SYSV_ABI Func_F916890425496553() { } void RegisterlibSceGnmDriver(Core::Loader::SymbolsResolver* sym) { - LOG_INFO(Lib_GnmDriver, "Initializing renderer"); + LOG_INFO(Lib_GnmDriver, "Initializing presenter"); liverpool = std::make_unique(); - renderer = std::make_unique(*g_window, liverpool.get()); + presenter = std::make_unique(*g_window, liverpool.get()); const int result = sceKernelGetCompiledSdkVersion(&sdk_version); if (result != ORBIS_OK) { diff --git a/src/core/libraries/gnmdriver/gnmdriver.h b/src/core/libraries/gnmdriver/gnmdriver.h index a95daa90d..017dbe3ad 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.h +++ b/src/core/libraries/gnmdriver/gnmdriver.h @@ -4,7 +4,7 @@ #pragma once #include "common/types.h" -#include "core/libraries/kernel/event_queues.h" +#include "core/libraries/kernel/equeue.h" namespace Core::Loader { class SymbolsResolver; @@ -16,11 +16,11 @@ using namespace Kernel; s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata); int PS4_SYSV_ABI sceGnmAreSubmitsAllowed(); -int PS4_SYSV_ABI sceGnmBeginWorkload(); +int PS4_SYSV_ABI sceGnmBeginWorkload(u32 workload_stream, u64* workload); s32 PS4_SYSV_ABI sceGnmComputeWaitOnAddress(u32* cmdbuf, u32 size, uintptr_t addr, u32 mask, u32 cmp_func, u32 ref); int PS4_SYSV_ABI sceGnmComputeWaitSemaphore(); -int PS4_SYSV_ABI sceGnmCreateWorkloadStream(); +int PS4_SYSV_ABI sceGnmCreateWorkloadStream(u64 param1, u32* workload_stream); int PS4_SYSV_ABI sceGnmDebuggerGetAddressWatch(); int PS4_SYSV_ABI sceGnmDebuggerHaltWavefront(); int PS4_SYSV_ABI sceGnmDebuggerReadGds(); @@ -34,7 +34,7 @@ int PS4_SYSV_ABI sceGnmDebugHardwareStatus(); s32 PS4_SYSV_ABI sceGnmDeleteEqEvent(SceKernelEqueue eq, u64 id); int PS4_SYSV_ABI sceGnmDestroyWorkloadStream(); void PS4_SYSV_ABI sceGnmDingDong(u32 gnm_vqid, u32 next_offs_dw); -int PS4_SYSV_ABI sceGnmDingDongForWorkload(); +void PS4_SYSV_ABI sceGnmDingDongForWorkload(u32 gnm_vqid, u32 next_offs_dw, u64 workload_id); int PS4_SYSV_ABI sceGnmDisableMipStatsReport(); s32 PS4_SYSV_ABI sceGnmDispatchDirect(u32* cmdbuf, u32 size, u32 threads_x, u32 threads_y, u32 threads_z, u32 flags); @@ -47,7 +47,10 @@ s32 PS4_SYSV_ABI sceGnmDrawIndexAuto(u32* cmdbuf, u32 size, u32 index_count, u32 s32 PS4_SYSV_ABI sceGnmDrawIndexIndirect(u32* cmdbuf, u32 size, u32 data_offset, u32 shader_stage, u32 vertex_sgpr_offset, u32 instance_sgpr_offset, u32 flags); -int PS4_SYSV_ABI sceGnmDrawIndexIndirectCountMulti(); +s32 PS4_SYSV_ABI sceGnmDrawIndexIndirectCountMulti(u32* cmdbuf, u32 size, u32 data_offset, + 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 sceGnmDrawIndexMultiInstanced(); s32 PS4_SYSV_ABI sceGnmDrawIndexOffset(u32* cmdbuf, u32 size, u32 index_offset, u32 index_count, @@ -74,7 +77,7 @@ int PS4_SYSV_ABI sceGnmDriverInternalRetrieveGnmInterfaceForValidation(); int PS4_SYSV_ABI sceGnmDriverInternalVirtualQuery(); int PS4_SYSV_ABI sceGnmDriverTraceInProgress(); int PS4_SYSV_ABI sceGnmDriverTriggerCapture(); -int PS4_SYSV_ABI sceGnmEndWorkload(); +int PS4_SYSV_ABI sceGnmEndWorkload(u64 workload); s32 PS4_SYSV_ABI sceGnmFindResourcesPublic(); void PS4_SYSV_ABI sceGnmFlushGarlic(); int PS4_SYSV_ABI sceGnmGetCoredumpAddress(); @@ -207,11 +210,17 @@ s32 PS4_SYSV_ABI sceGnmSubmitAndFlipCommandBuffers(u32 count, u32* dcb_gpu_addrs u32* dcb_sizes_in_bytes, u32* ccb_gpu_addrs[], u32* ccb_sizes_in_bytes, u32 vo_handle, u32 buf_idx, u32 flip_mode, u32 flip_arg); -int PS4_SYSV_ABI sceGnmSubmitAndFlipCommandBuffersForWorkload(); +int PS4_SYSV_ABI sceGnmSubmitAndFlipCommandBuffersForWorkload( + u32 workload, u32 count, u32* dcb_gpu_addrs[], u32* dcb_sizes_in_bytes, u32* ccb_gpu_addrs[], + u32* ccb_sizes_in_bytes, u32 vo_handle, u32 buf_idx, u32 flip_mode, u32 flip_arg); s32 PS4_SYSV_ABI sceGnmSubmitCommandBuffers(u32 count, const u32* dcb_gpu_addrs[], u32* dcb_sizes_in_bytes, const u32* ccb_gpu_addrs[], u32* ccb_sizes_in_bytes); -int PS4_SYSV_ABI sceGnmSubmitCommandBuffersForWorkload(); +int PS4_SYSV_ABI sceGnmSubmitCommandBuffersForWorkload(u32 workload, u32 count, + const u32* dcb_gpu_addrs[], + u32* dcb_sizes_in_bytes, + const u32* ccb_gpu_addrs[], + u32* ccb_sizes_in_bytes); int PS4_SYSV_ABI sceGnmSubmitDone(); int PS4_SYSV_ABI sceGnmUnmapComputeQueue(); int PS4_SYSV_ABI sceGnmUnregisterAllResourcesForOwner(); diff --git a/src/core/libraries/ime/error_dialog.cpp b/src/core/libraries/ime/error_dialog.cpp index 811f2cb99..07580fe1d 100644 --- a/src/core/libraries/ime/error_dialog.cpp +++ b/src/core/libraries/ime/error_dialog.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include "common/assert.h" #include "common/logging/log.h" diff --git a/src/core/libraries/ime/ime.cpp b/src/core/libraries/ime/ime.cpp index 0310c5153..dfd659db8 100644 --- a/src/core/libraries/ime/ime.cpp +++ b/src/core/libraries/ime/ime.cpp @@ -2,14 +2,12 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include -#include "ime.h" -#include "ime_ui.h" - #include "common/logging/log.h" -#include "common/singleton.h" -#include "core/libraries/error_codes.h" +#include "core/libraries/ime/ime.h" +#include "core/libraries/ime/ime_error.h" +#include "core/libraries/ime/ime_ui.h" #include "core/libraries/libs.h" -#include "core/linker.h" +#include "core/tls.h" namespace Libraries::Ime { @@ -37,7 +35,7 @@ public: // Open an event to let the game know the IME has started OrbisImeEvent openEvent{}; - openEvent.id = (ime_mode ? OrbisImeEventId::OPEN : OrbisImeEventId::KEYBOARD_OPEN); + openEvent.id = (ime_mode ? OrbisImeEventId::Open : OrbisImeEventId::KeyboardOpen); if (ime_mode) { sceImeGetPanelSize(&m_param.ime, &openEvent.param.rect.width, @@ -45,11 +43,15 @@ public: openEvent.param.rect.x = m_param.ime.posx; openEvent.param.rect.y = m_param.ime.posy; } else { - openEvent.param.resourceIdArray.userId = 1; - openEvent.param.resourceIdArray.resourceId[0] = 1; + openEvent.param.resource_id_array.userId = 1; + openEvent.param.resource_id_array.resourceId[0] = 1; } - Execute(nullptr, &openEvent, true); + // Are we supposed to call the event handler on init with + // ADD_OSK? + if (!ime_mode && False(m_param.key.option & OrbisImeKeyboardOption::AddOsk)) { + Execute(nullptr, &openEvent, true); + } if (ime_mode) { g_ime_state = ImeState(&m_param.ime); @@ -58,6 +60,11 @@ public: } s32 Update(OrbisImeEventHandler handler) { + if (!m_ime_mode) { + /* We don't handle any events for ImeKeyboard */ + return ORBIS_OK; + } + std::unique_lock lock{g_ime_state.queue_mutex}; while (!g_ime_state.event_queue.empty()) { @@ -70,25 +77,33 @@ public: } void Execute(OrbisImeEventHandler handler, OrbisImeEvent* event, bool use_param_handler) { - const auto* linker = Common::Singleton::Instance(); - if (m_ime_mode) { OrbisImeParam param = m_param.ime; if (use_param_handler) { - linker->ExecuteGuest(param.handler, param.arg, event); + Core::ExecuteGuest(param.handler, param.arg, event); } else { - linker->ExecuteGuest(handler, param.arg, event); + Core::ExecuteGuest(handler, param.arg, event); } } else { OrbisImeKeyboardParam param = m_param.key; if (use_param_handler) { - linker->ExecuteGuest(param.handler, param.arg, event); + Core::ExecuteGuest(param.handler, param.arg, event); } else { - linker->ExecuteGuest(handler, param.arg, event); + Core::ExecuteGuest(handler, param.arg, event); } } } + s32 SetText(const char16_t* text, u32 length) { + g_ime_state.SetText(text, length); + return ORBIS_OK; + } + + s32 SetCaret(const OrbisImeCaret* caret) { + g_ime_state.SetCaret(caret->index); + return ORBIS_OK; + } + bool IsIme() { return m_ime_mode; } @@ -102,6 +117,7 @@ private: }; static std::unique_ptr g_ime_handler; +static std::unique_ptr g_keyboard_handler; int PS4_SYSV_ABI FinalizeImeModule() { LOG_ERROR(Lib_Ime, "(STUBBED) called"); @@ -134,9 +150,6 @@ s32 PS4_SYSV_ABI sceImeClose() { if (!g_ime_handler) { return ORBIS_IME_ERROR_NOT_OPENED; } - if (!g_ime_handler->IsIme()) { - return ORBIS_IME_ERROR_NOT_OPENED; - } g_ime_handler.release(); g_ime_ui = ImeUi(); @@ -217,15 +230,15 @@ s32 PS4_SYSV_ABI sceImeGetPanelSize(const OrbisImeParam* param, u32* width, u32* } switch (param->type) { - case OrbisImeType::DEFAULT: - case OrbisImeType::BASIC_LATIN: - case OrbisImeType::URL: - case OrbisImeType::MAIL: + case OrbisImeType::Default: + case OrbisImeType::BasicLatin: + case OrbisImeType::Url: + case OrbisImeType::Mail: // We set our custom sizes, commented sizes are the original ones *width = 500; // 793 *height = 100; // 408 break; - case OrbisImeType::NUMBER: + case OrbisImeType::Number: *width = 370; *height = 402; break; @@ -237,14 +250,11 @@ s32 PS4_SYSV_ABI sceImeGetPanelSize(const OrbisImeParam* param, u32* width, u32* s32 PS4_SYSV_ABI sceImeKeyboardClose(s32 userId) { LOG_INFO(Lib_Ime, "(STUBBED) called"); - if (!g_ime_handler) { - return ORBIS_IME_ERROR_NOT_OPENED; - } - if (g_ime_handler->IsIme()) { + if (!g_keyboard_handler) { return ORBIS_IME_ERROR_NOT_OPENED; } - g_ime_handler.release(); + g_keyboard_handler.release(); return ORBIS_OK; } @@ -259,18 +269,17 @@ int PS4_SYSV_ABI sceImeKeyboardGetResourceId() { } s32 PS4_SYSV_ABI sceImeKeyboardOpen(s32 userId, const OrbisImeKeyboardParam* param) { - LOG_ERROR(Lib_Ime, "(STUBBED) called"); + LOG_INFO(Lib_Ime, "called"); if (!param) { return ORBIS_IME_ERROR_INVALID_ADDRESS; } - if (g_ime_handler) { + if (g_keyboard_handler) { return ORBIS_IME_ERROR_BUSY; } - // g_ime_handler = std::make_unique(param); - // return ORBIS_OK; - return ORBIS_IME_ERROR_CONNECTION_FAILED; // Fixup + g_keyboard_handler = std::make_unique(param); + return ORBIS_OK; } int PS4_SYSV_ABI sceImeKeyboardOpenInternal() { @@ -291,16 +300,14 @@ int PS4_SYSV_ABI sceImeKeyboardUpdate() { s32 PS4_SYSV_ABI sceImeOpen(const OrbisImeParam* param, const void* extended) { LOG_INFO(Lib_Ime, "called"); - if (!g_ime_handler) { - g_ime_handler = std::make_unique(param); - } else { - if (g_ime_handler->IsIme()) { - return ORBIS_IME_ERROR_BUSY; - } - - g_ime_handler->Init((void*)param, true); + if (!param) { + return ORBIS_IME_ERROR_INVALID_ADDRESS; + } + if (g_ime_handler) { + return ORBIS_IME_ERROR_BUSY; } + g_ime_handler = std::make_unique(param); return ORBIS_OK; } @@ -317,7 +324,7 @@ void PS4_SYSV_ABI sceImeParamInit(OrbisImeParam* param) { } memset(param, 0, sizeof(OrbisImeParam)); - param->userId = -1; + param->user_id = -1; } int PS4_SYSV_ABI sceImeSetCandidateIndex() { @@ -326,13 +333,29 @@ int PS4_SYSV_ABI sceImeSetCandidateIndex() { } int PS4_SYSV_ABI sceImeSetCaret(const OrbisImeCaret* caret) { - LOG_ERROR(Lib_Ime, "(STUBBED) called"); - return ORBIS_OK; + LOG_TRACE(Lib_Ime, "called"); + + if (!g_ime_handler) { + return ORBIS_IME_ERROR_NOT_OPENED; + } + if (!caret) { + return ORBIS_IME_ERROR_INVALID_ADDRESS; + } + + return g_ime_handler->SetCaret(caret); } -int PS4_SYSV_ABI sceImeSetText() { - LOG_ERROR(Lib_Ime, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceImeSetText(const char16_t* text, u32 length) { + LOG_TRACE(Lib_Ime, "called"); + + if (!g_ime_handler) { + return ORBIS_IME_ERROR_NOT_OPENED; + } + if (!text) { + return ORBIS_IME_ERROR_INVALID_ADDRESS; + } + + return g_ime_handler->SetText(text, length); } int PS4_SYSV_ABI sceImeSetTextGeometry() { @@ -341,13 +364,19 @@ int PS4_SYSV_ABI sceImeSetTextGeometry() { } s32 PS4_SYSV_ABI sceImeUpdate(OrbisImeEventHandler handler) { - LOG_TRACE(Lib_Ime, "called"); + if (g_ime_handler) { + g_ime_handler->Update(handler); + } - if (!g_ime_handler) { + if (g_keyboard_handler) { + g_keyboard_handler->Update(handler); + } + + if (!g_ime_handler || !g_keyboard_handler) { return ORBIS_IME_ERROR_NOT_OPENED; } - return g_ime_handler->Update(handler); + return ORBIS_OK; } int PS4_SYSV_ABI sceImeVshClearPreedit() { @@ -503,4 +532,4 @@ void RegisterlibSceIme(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("fwcPR7+7Rks", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshUpdateContext2); }; -} // namespace Libraries::Ime \ No newline at end of file +} // namespace Libraries::Ime diff --git a/src/core/libraries/ime/ime.h b/src/core/libraries/ime/ime.h index fc98d426a..448ee6896 100644 --- a/src/core/libraries/ime/ime.h +++ b/src/core/libraries/ime/ime.h @@ -3,9 +3,9 @@ #pragma once +#include "common/enum.h" #include "common/types.h" - -#include "ime_common.h" +#include "core/libraries/ime/ime_common.h" namespace Core::Loader { class SymbolsResolver; @@ -16,16 +16,34 @@ namespace Libraries::Ime { constexpr u32 ORBIS_IME_MAX_TEXT_LENGTH = 2048; enum class OrbisImeKeyboardOption : u32 { - DEFAULT = 0, - REPEAT = 1, - REPEAT_EACH_KEY = 2, - ADD_OSK = 4, - EFFECTIVE_WITH_TIME = 8, - DISABLE_RESUME = 16, - DISABLE_CAPSLOCK_WITHOUT_SHIFT = 32, + Default = 0, + Repeat = 1, + RepeatEachKey = 2, + AddOsk = 4, + EffectiveWithTime = 8, + DisableResume = 16, + DisableCapslockWithoutShift = 32, }; DECLARE_ENUM_FLAG_OPERATORS(OrbisImeKeyboardOption) +enum class OrbisImeOption : u32 { + DEFAULT = 0, + MULTILINE = 1, + NO_AUTO_CAPITALIZATION = 2, + PASSWORD = 4, + LANGUAGES_FORCED = 8, + EXT_KEYBOARD = 16, + NO_LEARNING = 32, + FIXED_POSITION = 64, + DISABLE_RESUME = 256, + DISABLE_AUTO_SPACE = 512, + DISABLE_POSITION_ADJUSTMENT = 2048, + EXPANDED_PREEDIT_BUFFER = 4096, + USE_JAPANESE_EISUU_KEY_AS_CAPSLOCK = 8192, + USE_2K_COORDINATES = 16384, +}; +DECLARE_ENUM_FLAG_OPERATORS(OrbisImeOption) + struct OrbisImeKeyboardParam { OrbisImeKeyboardOption option; s8 reserved1[4]; @@ -35,19 +53,19 @@ struct OrbisImeKeyboardParam { }; struct OrbisImeParam { - s32 userId; + s32 user_id; OrbisImeType type; - u64 supportedLanguages; - OrbisImeEnterLabel enterLabel; - OrbisImeInputMethod inputMethod; + u64 supported_languages; + OrbisImeEnterLabel enter_label; + OrbisImeInputMethod input_method; OrbisImeTextFilter filter; - u32 option; + OrbisImeOption option; u32 maxTextLength; char16_t* inputTextBuffer; float posx; float posy; - OrbisImeHorizontalAlignment horizontalAlignment; - OrbisImeVerticalAlignment verticalAlignment; + OrbisImeHorizontalAlignment horizontal_alignment; + OrbisImeVerticalAlignment vertical_alignment; void* work; void* arg; OrbisImeEventHandler handler; @@ -93,7 +111,7 @@ int PS4_SYSV_ABI sceImeOpenInternal(); void PS4_SYSV_ABI sceImeParamInit(OrbisImeParam* param); int PS4_SYSV_ABI sceImeSetCandidateIndex(); s32 PS4_SYSV_ABI sceImeSetCaret(const OrbisImeCaret* caret); -int PS4_SYSV_ABI sceImeSetText(); +s32 PS4_SYSV_ABI sceImeSetText(const char16_t* text, u32 length); int PS4_SYSV_ABI sceImeSetTextGeometry(); s32 PS4_SYSV_ABI sceImeUpdate(OrbisImeEventHandler handler); int PS4_SYSV_ABI sceImeVshClearPreedit(); @@ -117,4 +135,5 @@ int PS4_SYSV_ABI sceImeVshUpdateContext(); int PS4_SYSV_ABI sceImeVshUpdateContext2(); void RegisterlibSceIme(Core::Loader::SymbolsResolver* sym); -} // namespace Libraries::Ime \ No newline at end of file + +} // namespace Libraries::Ime diff --git a/src/core/libraries/ime/ime_common.h b/src/core/libraries/ime/ime_common.h index 078a6469e..6d4afd81d 100644 --- a/src/core/libraries/ime/ime_common.h +++ b/src/core/libraries/ime/ime_common.h @@ -2,65 +2,64 @@ // SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include -#include "common/enum.h" + #include "common/types.h" #include "core/libraries/rtc/rtc.h" enum class OrbisImeType : u32 { - DEFAULT = 0, - BASIC_LATIN = 1, - URL = 2, - MAIL = 3, - NUMBER = 4, + Default = 0, + BasicLatin = 1, + Url = 2, + Mail = 3, + Number = 4, }; enum class OrbisImeHorizontalAlignment : u32 { - LEFT = 0, - CENTER = 1, - RIGHT = 2, + Left = 0, + Center = 1, + Right = 2, }; enum class OrbisImeVerticalAlignment : u32 { - TOP = 0, - CENTER = 1, - BOTTOM = 2, + Top = 0, + Center = 1, + Bottom = 2, }; enum class OrbisImeEnterLabel : u32 { - DEFAULT = 0, - SEND = 1, - SEARCH = 2, - GO = 3, + Default = 0, + Send = 1, + Search = 2, + Go = 3, }; enum class OrbisImeInputMethod : u32 { - DEFAULT = 0, + Default = 0, }; enum class OrbisImeEventId : u32 { - OPEN = 0, - UPDATE_TEXT = 1, - UPDATE_CARET = 2, - PRESS_CLOSE = 4, - PRESS_ENTER = 5, - ABORT = 6, - CANDIDATE_LIST_START = 7, - CANDIDATE_LIST_END = 8, - CANDIDATE_WORD = 9, - CANDIDATE_INDEX = 10, - CANDIDATE_DONE = 11, - CANDIDATE_CANCEL = 12, - CHANGE_DEVICE = 14, - CHANGE_INPUT_METHOD_STATE = 18, + Open = 0, + UpdateText = 1, + UpdateCaret = 2, + PressClose = 4, + PressEnter = 5, + Abort = 6, + CandidateListStart = 7, + CandidateListEnd = 8, + CandidateWord = 9, + CandidateIndex = 10, + CandidateDone = 11, + CandidateCancel = 12, + ChangeDevice = 14, + ChangeInputMethodState = 18, - KEYBOARD_OPEN = 256, - KEYBOARD_KEYCODE_DOWN = 257, - KEYBOARD_KEYCODE_UP = 258, - KEYBOARD_KEYCODE_REPEAT = 259, - KEYBOARD_CONNECTION = 260, - KEYBOARD_DISCONNECTION = 261, - KEYBOARD_ABORT = 262, + KeyboardOpen = 256, + KeyboardKeycodeDoen = 257, + KeyboardKeycodeUp = 258, + KeyboardKeycodeRepeat = 259, + KeyboardConnection = 260, + KeyboardDisconnection = 261, + KeyboardAbort = 262, }; enum class OrbisImeKeyboardType : u32 { @@ -105,10 +104,10 @@ enum class OrbisImeKeyboardType : u32 { }; enum class OrbisImeDeviceType : u32 { - NONE = 0, - CONTROLLER = 1, - EXT_KEYBOARD = 2, - REMOTE_OSK = 3, + None = 0, + Controller = 1, + ExtKeyboard = 2, + RemoteOsk = 3, }; struct OrbisImeRect { @@ -126,9 +125,9 @@ struct OrbisImeTextAreaProperty { struct OrbisImeEditText { char16_t* str; - u32 caretIndex; - u32 areaNum; - OrbisImeTextAreaProperty textArea[4]; + u32 caret_index; + u32 area_num; + OrbisImeTextAreaProperty text_area[4]; }; struct OrbisImeKeycode { @@ -136,40 +135,40 @@ struct OrbisImeKeycode { char16_t character; u32 status; OrbisImeKeyboardType type; - s32 userId; - u32 resourceId; + s32 user_id; + u32 resource_id; Libraries::Rtc::OrbisRtcTick timestamp; }; struct OrbisImeKeyboardResourceIdArray { s32 userId; - u32 resourceId[6]; + u32 resourceId[5]; }; enum class OrbisImeCaretMovementDirection : u32 { - STILL = 0, - LEFT = 1, - RIGHT = 2, - UP = 3, - DOWN = 4, - HOME = 5, - END = 6, - PAGE_UP = 7, - PAGE_DOWN = 8, - TOP = 9, - BOTTOM = 10, + Still = 0, + Left = 1, + Right = 2, + Up = 3, + Down = 4, + Home = 5, + End = 6, + PageUp = 7, + PageDown = 8, + Top = 9, + Bottom = 10, }; union OrbisImeEventParam { OrbisImeRect rect; OrbisImeEditText text; - OrbisImeCaretMovementDirection caretMove; + OrbisImeCaretMovementDirection caret_move; OrbisImeKeycode keycode; - OrbisImeKeyboardResourceIdArray resourceIdArray; - char16_t* candidateWord; - s32 candidateIndex; - OrbisImeDeviceType deviceType; - u32 inputMethodState; + OrbisImeKeyboardResourceIdArray resource_id_array; + char16_t* candidate_word; + s32 candidate_index; + OrbisImeDeviceType device_type; + u32 input_method_state; s8 reserved[64]; }; @@ -178,7 +177,7 @@ struct OrbisImeEvent { OrbisImeEventParam param; }; -typedef PS4_SYSV_ABI int (*OrbisImeTextFilter)(char16_t* outText, u32* outTextLength, - const char16_t* srcText, u32 srcTextLength); +using OrbisImeTextFilter = PS4_SYSV_ABI int (*)(char16_t* outText, u32* outTextLength, + const char16_t* srcText, u32 srcTextLength); -typedef PS4_SYSV_ABI void (*OrbisImeEventHandler)(void* arg, const OrbisImeEvent* e); +using OrbisImeEventHandler = PS4_SYSV_ABI void (*)(void* arg, const OrbisImeEvent* e); diff --git a/src/core/libraries/ime/ime_dialog.cpp b/src/core/libraries/ime/ime_dialog.cpp index 63b52706a..9151aa64e 100644 --- a/src/core/libraries/ime/ime_dialog.cpp +++ b/src/core/libraries/ime/ime_dialog.cpp @@ -2,7 +2,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include -#include +#include + #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" @@ -14,24 +15,24 @@ static constexpr std::array MAX_Y_POSITIONS = {2160.0f, 1080.0f}; namespace Libraries::ImeDialog { -static OrbisImeDialogStatus g_ime_dlg_status = OrbisImeDialogStatus::NONE; +static OrbisImeDialogStatus g_ime_dlg_status = OrbisImeDialogStatus::None; static OrbisImeDialogResult g_ime_dlg_result{}; static ImeDialogState g_ime_dlg_state{}; static ImeDialogUi g_ime_dlg_ui; static bool IsValidOption(OrbisImeDialogOption option, OrbisImeType type) { if (False(~option & - (OrbisImeDialogOption::MULTILINE | OrbisImeDialogOption::NO_AUTO_COMPLETION))) { + (OrbisImeDialogOption::Multiline | OrbisImeDialogOption::NoAutoCompletion))) { return false; } - if (True(option & OrbisImeDialogOption::MULTILINE) && type != OrbisImeType::DEFAULT && - type != OrbisImeType::BASIC_LATIN) { + if (True(option & OrbisImeDialogOption::Multiline) && type != OrbisImeType::Default && + type != OrbisImeType::BasicLatin) { return false; } - if (True(option & OrbisImeDialogOption::NO_AUTO_COMPLETION) && type != OrbisImeType::NUMBER && - type != OrbisImeType::BASIC_LATIN) { + if (True(option & OrbisImeDialogOption::NoAutoCompletion) && type != OrbisImeType::Number && + type != OrbisImeType::BasicLatin) { return false; } @@ -39,29 +40,29 @@ static bool IsValidOption(OrbisImeDialogOption option, OrbisImeType type) { } Error PS4_SYSV_ABI sceImeDialogAbort() { - if (g_ime_dlg_status == OrbisImeDialogStatus::NONE) { + if (g_ime_dlg_status == OrbisImeDialogStatus::None) { LOG_INFO(Lib_ImeDialog, "IME dialog not in use"); return Error::DIALOG_NOT_IN_USE; } - if (g_ime_dlg_status != OrbisImeDialogStatus::RUNNING) { + if (g_ime_dlg_status != OrbisImeDialogStatus::Running) { LOG_INFO(Lib_ImeDialog, "IME dialog not running"); return Error::DIALOG_NOT_RUNNING; } - g_ime_dlg_status = OrbisImeDialogStatus::FINISHED; - g_ime_dlg_result.endstatus = OrbisImeDialogEndStatus::ABORTED; + g_ime_dlg_status = OrbisImeDialogStatus::Finished; + g_ime_dlg_result.endstatus = OrbisImeDialogEndStatus::Aborted; return Error::OK; } Error PS4_SYSV_ABI sceImeDialogForceClose() { - if (g_ime_dlg_status == OrbisImeDialogStatus::NONE) { + if (g_ime_dlg_status == OrbisImeDialogStatus::None) { LOG_INFO(Lib_ImeDialog, "IME dialog not in use"); return Error::DIALOG_NOT_IN_USE; } - g_ime_dlg_status = OrbisImeDialogStatus::NONE; + g_ime_dlg_status = OrbisImeDialogStatus::None; g_ime_dlg_ui = ImeDialogUi(); g_ime_dlg_state = ImeDialogState(); @@ -93,7 +94,7 @@ int PS4_SYSV_ABI sceImeDialogGetPanelSizeExtended() { } Error PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result) { - if (g_ime_dlg_status == OrbisImeDialogStatus::NONE) { + if (g_ime_dlg_status == OrbisImeDialogStatus::None) { LOG_INFO(Lib_ImeDialog, "IME dialog is not running"); return Error::DIALOG_NOT_IN_USE; } @@ -105,7 +106,7 @@ Error PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result) { result->endstatus = g_ime_dlg_result.endstatus; - if (g_ime_dlg_status == OrbisImeDialogStatus::RUNNING) { + if (g_ime_dlg_status == OrbisImeDialogStatus::Running) { return Error::DIALOG_NOT_FINISHED; } @@ -114,7 +115,7 @@ Error PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result) { } OrbisImeDialogStatus PS4_SYSV_ABI sceImeDialogGetStatus() { - if (g_ime_dlg_status == OrbisImeDialogStatus::RUNNING) { + if (g_ime_dlg_status == OrbisImeDialogStatus::Running) { g_ime_dlg_state.CallTextFilter(); } @@ -122,7 +123,7 @@ OrbisImeDialogStatus PS4_SYSV_ABI sceImeDialogGetStatus() { } Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExtended* extended) { - if (g_ime_dlg_status != OrbisImeDialogStatus::NONE) { + if (g_ime_dlg_status != OrbisImeDialogStatus::None) { LOG_INFO(Lib_ImeDialog, "IME dialog is already running"); return Error::BUSY; } @@ -142,24 +143,24 @@ Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExt if (param->posx < 0.0f || param->posx >= - MAX_X_POSITIONS[False(param->option & OrbisImeDialogOption::LARGE_RESOLUTION)]) { + MAX_X_POSITIONS[False(param->option & OrbisImeDialogOption::LargeResolution)]) { LOG_INFO(Lib_ImeDialog, "Invalid param->posx"); return Error::INVALID_POSX; } if (param->posy < 0.0f || param->posy >= - MAX_Y_POSITIONS[False(param->option & OrbisImeDialogOption::LARGE_RESOLUTION)]) { + MAX_Y_POSITIONS[False(param->option & OrbisImeDialogOption::LargeResolution)]) { LOG_INFO(Lib_ImeDialog, "Invalid param->posy"); return Error::INVALID_POSY; } - if (!magic_enum::enum_contains(param->horizontalAlignment)) { + if (!magic_enum::enum_contains(param->horizontal_alignment)) { LOG_INFO(Lib_ImeDialog, "Invalid param->horizontalAlignment"); return Error::INVALID_HORIZONTALIGNMENT; } - if (!magic_enum::enum_contains(param->verticalAlignment)) { + if (!magic_enum::enum_contains(param->vertical_alignment)) { LOG_INFO(Lib_ImeDialog, "Invalid param->verticalAlignment"); return Error::INVALID_VERTICALALIGNMENT; } @@ -169,7 +170,7 @@ Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExt return Error::INVALID_PARAM; } - if (param->inputTextBuffer == nullptr) { + if (param->input_text_buffer == nullptr) { LOG_INFO(Lib_ImeDialog, "Invalid param->inputTextBuffer"); return Error::INVALID_INPUT_TEXT_BUFFER; } @@ -182,25 +183,25 @@ Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExt // TODO: do correct extended->option validation - if ((extended->extKeyboardMode & 0xe3fffffc) != 0) { + if ((extended->ext_keyboard_mode & 0xe3fffffc) != 0) { LOG_INFO(Lib_ImeDialog, "Invalid extended->extKeyboardMode"); return Error::INVALID_EXTENDED; } - if (extended->disableDevice > 7) { + if (extended->disable_device > 7) { LOG_INFO(Lib_ImeDialog, "Invalid extended->disableDevice"); return Error::INVALID_EXTENDED; } } - if (param->maxTextLength > ORBIS_IME_DIALOG_MAX_TEXT_LENGTH) { + if (param->max_text_length > ORBIS_IME_DIALOG_MAX_TEXT_LENGTH) { LOG_INFO(Lib_ImeDialog, "Invalid param->maxTextLength"); return Error::INVALID_MAX_TEXT_LENGTH; } g_ime_dlg_result = {}; g_ime_dlg_state = ImeDialogState(param, extended); - g_ime_dlg_status = OrbisImeDialogStatus::RUNNING; + g_ime_dlg_status = OrbisImeDialogStatus::Running; g_ime_dlg_ui = ImeDialogUi(&g_ime_dlg_state, &g_ime_dlg_status, &g_ime_dlg_result); return Error::OK; @@ -227,17 +228,17 @@ int PS4_SYSV_ABI sceImeDialogSetPanelPosition() { } Error PS4_SYSV_ABI sceImeDialogTerm() { - if (g_ime_dlg_status == OrbisImeDialogStatus::NONE) { + if (g_ime_dlg_status == OrbisImeDialogStatus::None) { LOG_INFO(Lib_ImeDialog, "IME dialog not in use"); return Error::DIALOG_NOT_IN_USE; } - if (g_ime_dlg_status == OrbisImeDialogStatus::RUNNING) { + if (g_ime_dlg_status == OrbisImeDialogStatus::Running) { LOG_INFO(Lib_ImeDialog, "IME dialog is still running"); return Error::DIALOG_NOT_FINISHED; } - g_ime_dlg_status = OrbisImeDialogStatus::NONE; + g_ime_dlg_status = OrbisImeDialogStatus::None; g_ime_dlg_ui = ImeDialogUi(); g_ime_dlg_state = ImeDialogState(); @@ -274,4 +275,4 @@ void RegisterlibSceImeDialog(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("gyTyVn+bXMw", "libSceImeDialog", 1, "libSceImeDialog", 1, 1, sceImeDialogTerm); }; -} // namespace Libraries::ImeDialog \ No newline at end of file +} // namespace Libraries::ImeDialog diff --git a/src/core/libraries/ime/ime_dialog.h b/src/core/libraries/ime/ime_dialog.h index e99d29613..c8b228498 100644 --- a/src/core/libraries/ime/ime_dialog.h +++ b/src/core/libraries/ime/ime_dialog.h @@ -58,32 +58,32 @@ enum class Error : u32 { }; enum class OrbisImeDialogStatus : u32 { - NONE = 0, - RUNNING = 1, - FINISHED = 2, + None = 0, + Running = 1, + Finished = 2, }; enum class OrbisImeDialogEndStatus : u32 { - OK = 0, - USER_CANCELED = 1, - ABORTED = 2, + Ok = 0, + UserCanceled = 1, + Aborted = 2, }; enum class OrbisImeDialogOption : u32 { - DEFAULT = 0, - MULTILINE = 1, - NO_AUTO_CORRECTION = 2, - NO_AUTO_COMPLETION = 4, + Default = 0, + Multiline = 1, + NoAutoCorrection = 2, + NoAutoCompletion = 4, // TODO: Document missing options - LARGE_RESOLUTION = 1024, + LargeResolution = 1024, }; DECLARE_ENUM_FLAG_OPERATORS(OrbisImeDialogOption) enum class OrbisImePanelPriority : u32 { - DEFAULT = 0, - ALPHABET = 1, - SYMBOL = 2, - ACCENT = 3, + Default = 0, + Alphabet = 1, + Symbol = 2, + Accent = 3, }; struct OrbisImeColor { @@ -103,29 +103,29 @@ struct OrbisImeKeycode { char16_t character; u32 status; OrbisImeKeyboardType type; - s32 userId; - u32 resourceId; + s32 user_id; + u32 resource_id; u64 timestamp; }; -typedef PS4_SYSV_ABI int (*OrbisImeExtKeyboardFilter)(const OrbisImeKeycode* srcKeycode, - u16* outKeycode, u32* outStatus, - void* reserved); +using OrbisImeExtKeyboardFilter = PS4_SYSV_ABI int (*)(const OrbisImeKeycode* srcKeycode, + u16* outKeycode, u32* outStatus, + void* reserved); struct OrbisImeDialogParam { - s32 userId; + s32 user_id; OrbisImeType type; - u64 supportedLanguages; - OrbisImeEnterLabel enterLabel; - OrbisImeInputMethod inputMethod; + u64 supported_languages; + OrbisImeEnterLabel enter_label; + OrbisImeInputMethod input_method; OrbisImeTextFilter filter; OrbisImeDialogOption option; - u32 maxTextLength; - char16_t* inputTextBuffer; + u32 max_text_length; + char16_t* input_text_buffer; float posx; float posy; - OrbisImeHorizontalAlignment horizontalAlignment; - OrbisImeVerticalAlignment verticalAlignment; + OrbisImeHorizontalAlignment horizontal_alignment; + OrbisImeVerticalAlignment vertical_alignment; const char16_t* placeholder; const char16_t* title; s8 reserved[16]; @@ -133,20 +133,20 @@ struct OrbisImeDialogParam { struct OrbisImeParamExtended { u32 option; // OrbisImeDialogOptionExtended - OrbisImeColor colorBase; - OrbisImeColor colorLine; - OrbisImeColor colorTextField; - OrbisImeColor colorPreedit; - OrbisImeColor colorButtonDefault; - OrbisImeColor colorButtonFunction; - OrbisImeColor colorButtonSymbol; - OrbisImeColor colorText; - OrbisImeColor colorSpecial; + OrbisImeColor color_base; + OrbisImeColor color_line; + OrbisImeColor color_text_field; + OrbisImeColor color_preedit; + OrbisImeColor color_button_default; + OrbisImeColor color_button_function; + OrbisImeColor color_button_symbol; + OrbisImeColor color_text; + OrbisImeColor color_special; OrbisImePanelPriority priority; - char* additionalDictionaryPath; - OrbisImeExtKeyboardFilter extKeyboardFilter; - uint32_t disableDevice; - uint32_t extKeyboardMode; + char* additional_dictionary_path; + OrbisImeExtKeyboardFilter ext_keyboard_filter; + uint32_t disable_device; + uint32_t ext_keyboard_mode; int8_t reserved[60]; }; @@ -167,4 +167,4 @@ int PS4_SYSV_ABI sceImeDialogSetPanelPosition(); Error PS4_SYSV_ABI sceImeDialogTerm(); void RegisterlibSceImeDialog(Core::Loader::SymbolsResolver* sym); -} // namespace Libraries::ImeDialog \ No newline at end of file +} // namespace Libraries::ImeDialog diff --git a/src/core/libraries/ime/ime_dialog_ui.cpp b/src/core/libraries/ime/ime_dialog_ui.cpp index bba45d0fe..51183c79b 100644 --- a/src/core/libraries/ime/ime_dialog_ui.cpp +++ b/src/core/libraries/ime/ime_dialog_ui.cpp @@ -4,14 +4,13 @@ #include #include #include -#include +#include #include "common/assert.h" #include "common/logging/log.h" -#include "common/singleton.h" #include "core/libraries/ime/ime_dialog.h" #include "core/libraries/ime/ime_dialog_ui.h" -#include "core/linker.h" +#include "core/tls.h" #include "imgui/imgui_std.h" using namespace ImGui; @@ -26,15 +25,15 @@ ImeDialogState::ImeDialogState(const OrbisImeDialogParam* param, return; } - userId = param->userId; - is_multiLine = True(param->option & OrbisImeDialogOption::MULTILINE); - is_numeric = param->type == OrbisImeType::NUMBER; + user_id = param->user_id; + is_multi_line = True(param->option & OrbisImeDialogOption::Multiline); + is_numeric = param->type == OrbisImeType::Number; type = param->type; - enter_label = param->enterLabel; + enter_label = param->enter_label; text_filter = param->filter; - keyboard_filter = extended ? extended->extKeyboardFilter : nullptr; - max_text_length = param->maxTextLength; - text_buffer = param->inputTextBuffer; + keyboard_filter = extended ? extended->ext_keyboard_filter : nullptr; + max_text_length = param->max_text_length; + text_buffer = param->input_text_buffer; if (param->title) { std::size_t title_len = std::char_traits::length(param->title); @@ -65,12 +64,12 @@ ImeDialogState::ImeDialogState(const OrbisImeDialogParam* param, } ImeDialogState::ImeDialogState(ImeDialogState&& other) noexcept - : input_changed(other.input_changed), userId(other.userId), is_multiLine(other.is_multiLine), - is_numeric(other.is_numeric), type(other.type), enter_label(other.enter_label), - text_filter(other.text_filter), keyboard_filter(other.keyboard_filter), - max_text_length(other.max_text_length), text_buffer(other.text_buffer), - title(std::move(other.title)), placeholder(std::move(other.placeholder)), - current_text(other.current_text) { + : input_changed(other.input_changed), user_id(other.user_id), + is_multi_line(other.is_multi_line), is_numeric(other.is_numeric), type(other.type), + enter_label(other.enter_label), text_filter(other.text_filter), + keyboard_filter(other.keyboard_filter), max_text_length(other.max_text_length), + text_buffer(other.text_buffer), title(std::move(other.title)), + placeholder(std::move(other.placeholder)), current_text(other.current_text) { other.text_buffer = nullptr; } @@ -78,8 +77,8 @@ ImeDialogState::ImeDialogState(ImeDialogState&& other) noexcept ImeDialogState& ImeDialogState::operator=(ImeDialogState&& other) { if (this != &other) { input_changed = other.input_changed; - userId = other.userId; - is_multiLine = other.is_multiLine; + user_id = other.user_id; + is_multi_line = other.is_multi_line; is_numeric = other.is_numeric; type = other.type; enter_label = other.enter_label; @@ -124,9 +123,8 @@ bool ImeDialogState::CallTextFilter() { return false; } - auto* linker = Common::Singleton::Instance(); int ret = - linker->ExecuteGuest(text_filter, out_text, &out_text_length, src_text, src_text_length); + Core::ExecuteGuest(text_filter, out_text, &out_text_length, src_text, src_text_length); if (ret != 0) { return false; @@ -147,15 +145,12 @@ bool ImeDialogState::CallKeyboardFilter(const OrbisImeKeycode* src_keycode, u16* return true; } - auto* linker = Common::Singleton::Instance(); - int ret = linker->ExecuteGuest(keyboard_filter, src_keycode, out_keycode, out_status, nullptr); - + int ret = Core::ExecuteGuest(keyboard_filter, src_keycode, out_keycode, out_status, nullptr); return ret == 0; } bool ImeDialogState::ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len, char* utf8_text, std::size_t utf8_text_len) { - std::fill(utf8_text, utf8_text + utf8_text_len, '\0'); const ImWchar* orbis_text_ptr = reinterpret_cast(orbis_text); ImTextStrToUtf8(utf8_text, utf8_text_len, orbis_text_ptr, orbis_text_ptr + orbis_text_len); @@ -165,7 +160,6 @@ bool ImeDialogState::ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t bool ImeDialogState::ConvertUTF8ToOrbis(const char* utf8_text, std::size_t utf8_text_len, char16_t* orbis_text, std::size_t orbis_text_len) { - std::fill(orbis_text, orbis_text + orbis_text_len, u'\0'); ImTextStrFromUtf8(reinterpret_cast(orbis_text), orbis_text_len, utf8_text, nullptr); @@ -176,7 +170,7 @@ ImeDialogUi::ImeDialogUi(ImeDialogState* state, OrbisImeDialogStatus* status, OrbisImeDialogResult* result) : state(state), status(status), result(result) { - if (state && *status == OrbisImeDialogStatus::RUNNING) { + if (state && *status == OrbisImeDialogStatus::Running) { AddLayer(this); } } @@ -196,7 +190,7 @@ ImeDialogUi::ImeDialogUi(ImeDialogUi&& other) noexcept other.status = nullptr; other.result = nullptr; - if (state && *status == OrbisImeDialogStatus::RUNNING) { + if (state && *status == OrbisImeDialogStatus::Running) { AddLayer(this); } } @@ -213,7 +207,7 @@ ImeDialogUi& ImeDialogUi::operator=(ImeDialogUi&& other) { other.status = nullptr; other.result = nullptr; - if (state && *status == OrbisImeDialogStatus::RUNNING) { + if (state && *status == OrbisImeDialogStatus::Running) { AddLayer(this); } @@ -231,7 +225,7 @@ void ImeDialogUi::Draw() { return; } - if (!status || *status != OrbisImeDialogStatus::RUNNING) { + if (!status || *status != OrbisImeDialogStatus::Running) { return; } @@ -240,7 +234,7 @@ void ImeDialogUi::Draw() { ImVec2 window_size; - if (state->is_multiLine) { + if (state->is_multi_line) { window_size = {500.0f, 300.0f}; } else { window_size = {500.0f, 150.0f}; @@ -264,7 +258,7 @@ void ImeDialogUi::Draw() { SetWindowFontScale(1.0f); } - if (state->is_multiLine) { + if (state->is_multi_line) { DrawMultiLineInputText(); } else { DrawInputText(); @@ -275,16 +269,16 @@ void ImeDialogUi::Draw() { const char* button_text; switch (state->enter_label) { - case OrbisImeEnterLabel::GO: + case OrbisImeEnterLabel::Go: button_text = "Go##ImeDialogOK"; break; - case OrbisImeEnterLabel::SEARCH: + case OrbisImeEnterLabel::Search: button_text = "Search##ImeDialogOK"; break; - case OrbisImeEnterLabel::SEND: + case OrbisImeEnterLabel::Send: button_text = "Send##ImeDialogOK"; break; - case OrbisImeEnterLabel::DEFAULT: + case OrbisImeEnterLabel::Default: default: button_text = "OK##ImeDialogOK"; break; @@ -297,16 +291,16 @@ void ImeDialogUi::Draw() { SetCursorPosX(button_start_pos); if (Button(button_text, BUTTON_SIZE) || - (!state->is_multiLine && IsKeyPressed(ImGuiKey_Enter))) { - *status = OrbisImeDialogStatus::FINISHED; - result->endstatus = OrbisImeDialogEndStatus::OK; + (!state->is_multi_line && IsKeyPressed(ImGuiKey_Enter))) { + *status = OrbisImeDialogStatus::Finished; + result->endstatus = OrbisImeDialogEndStatus::Ok; } SameLine(0.0f, button_spacing); if (Button("Cancel##ImeDialogCancel", BUTTON_SIZE)) { - *status = OrbisImeDialogStatus::FINISHED; - result->endstatus = OrbisImeDialogEndStatus::USER_CANCELED; + *status = OrbisImeDialogStatus::Finished; + result->endstatus = OrbisImeDialogEndStatus::UserCanceled; } } End(); @@ -367,9 +361,10 @@ int ImeDialogUi::InputTextCallback(ImGuiInputTextCallbackData* data) { .status = 1, // ??? 1 = key pressed, 0 = key released .type = OrbisImeKeyboardType::ENGLISH_US, // TODO set this to the correct value (maybe use // the current language?) - .userId = ui->state->userId, - .resourceId = 0, - .timestamp = 0}; + .user_id = ui->state->user_id, + .resource_id = 0, + .timestamp = 0, + }; if (!ui->state->ConvertUTF8ToOrbis(event_char, 4, &src_keycode.character, 1)) { LOG_ERROR(Lib_ImeDialog, "Failed to convert orbis char to utf8"); @@ -387,4 +382,4 @@ int ImeDialogUi::InputTextCallback(ImGuiInputTextCallbackData* data) { return 0; } -} // namespace Libraries::ImeDialog \ No newline at end of file +} // namespace Libraries::ImeDialog diff --git a/src/core/libraries/ime/ime_dialog_ui.h b/src/core/libraries/ime/ime_dialog_ui.h index a4cf6e9f7..10dff5eeb 100644 --- a/src/core/libraries/ime/ime_dialog_ui.h +++ b/src/core/libraries/ime/ime_dialog_ui.h @@ -20,8 +20,8 @@ class ImeDialogState final { bool input_changed = false; - s32 userId{}; - bool is_multiLine{}; + s32 user_id{}; + bool is_multi_line{}; bool is_numeric{}; OrbisImeType type{}; OrbisImeEnterLabel enter_label{}; diff --git a/src/core/libraries/ime/ime_error.h b/src/core/libraries/ime/ime_error.h new file mode 100644 index 000000000..77eeada39 --- /dev/null +++ b/src/core/libraries/ime/ime_error.h @@ -0,0 +1,51 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// ImeDialog library +constexpr int ORBIS_ERROR_DIALOG_ERROR_NOT_INITIALIZED = 0x80ED0001; +constexpr int ORBIS_ERROR_DIALOG_ERROR_ALREADY_INITIALIZED = 0x80ED0002; +constexpr int ORBIS_ERROR_DIALOG_ERROR_PARAM_INVALID = 0x80ED0003; +constexpr int ORBIS_ERROR_DIALOG_ERROR_UNEXPECTED_FATAL = 0x80ED0004; +constexpr int ORBIS_ERROR_DIALOG_ERROR_INVALID_STATE = 0x80ED0005; +constexpr int ORBIS_ERROR_DIALOG_ERROR_SERVICE_BUSY = 0x80ED0006; +constexpr int ORBIS_ERROR_DIALOG_ERROR_INVALID_USER_ID = 0x80ED0007; + +// Ime library +constexpr int ORBIS_IME_ERROR_BUSY = 0x80BC0001; +constexpr int ORBIS_IME_ERROR_NOT_OPENED = 0x80BC0002; +constexpr int ORBIS_IME_ERROR_NO_MEMORY = 0x80BC0003; +constexpr int ORBIS_IME_ERROR_CONNECTION_FAILED = 0x80BC0004; +constexpr int ORBIS_IME_ERROR_TOO_MANY_REQUESTS = 0x80BC0005; +constexpr int ORBIS_IME_ERROR_INVALID_TEXT = 0x80BC0006; +constexpr int ORBIS_IME_ERROR_EVENT_OVERFLOW = 0x80BC0007; +constexpr int ORBIS_IME_ERROR_NOT_ACTIVE = 0x80BC0008; +constexpr int ORBIS_IME_ERROR_IME_SUSPENDING = 0x80BC0009; +constexpr int ORBIS_IME_ERROR_DEVICE_IN_USE = 0x80BC000A; +constexpr int ORBIS_IME_ERROR_INVALID_USER_ID = 0x80BC0010; +constexpr int ORBIS_IME_ERROR_INVALID_TYPE = 0x80BC0011; +constexpr int ORBIS_IME_ERROR_INVALID_SUPPORTED_LANGUAGES = 0x80BC0012; +constexpr int ORBIS_IME_ERROR_INVALID_ENTER_LABEL = 0x80BC0013; +constexpr int ORBIS_IME_ERROR_INVALID_INPUT_METHOD = 0x80BC0014; +constexpr int ORBIS_IME_ERROR_INVALID_OPTION = 0x80BC0015; +constexpr int ORBIS_IME_ERROR_INVALID_MAX_TEXT_LENGTH = 0x80BC0016; +constexpr int ORBIS_IME_ERROR_INVALID_INPUT_TEXT_BUFFER = 0x80BC0017; +constexpr int ORBIS_IME_ERROR_INVALID_POSX = 0x80BC0018; +constexpr int ORBIS_IME_ERROR_INVALID_POSY = 0x80BC0019; +constexpr int ORBIS_IME_ERROR_INVALID_HORIZONTAL_ALIGNMENT = 0x80BC001A; +constexpr int ORBIS_IME_ERROR_INVALID_VERTICAL_ALIGNMENT = 0x80BC001B; +constexpr int ORBIS_IME_ERROR_INVALID_EXTENDED = 0x80BC001C; +constexpr int ORBIS_IME_ERROR_INVALID_KEYBOARD_TYPE = 0x80BC001D; +constexpr int ORBIS_IME_ERROR_INVALID_WORK = 0x80BC0020; +constexpr int ORBIS_IME_ERROR_INVALID_ARG = 0x80BC0021; +constexpr int ORBIS_IME_ERROR_INVALID_HANDLER = 0x80BC0022; +constexpr int ORBIS_IME_ERROR_NO_RESOURCE_ID = 0x80BC0023; +constexpr int ORBIS_IME_ERROR_INVALID_MODE = 0x80BC0024; +constexpr int ORBIS_IME_ERROR_INVALID_PARAM = 0x80BC0030; +constexpr int ORBIS_IME_ERROR_INVALID_ADDRESS = 0x80BC0031; +constexpr int ORBIS_IME_ERROR_INVALID_RESERVED = 0x80BC0032; +constexpr int ORBIS_IME_ERROR_INVALID_TIMING = 0x80BC0033; +constexpr int ORBIS_IME_ERROR_INTERNAL = 0x80BC00FF; diff --git a/src/core/libraries/ime/ime_ui.cpp b/src/core/libraries/ime/ime_ui.cpp index 09d361263..8eaa48178 100644 --- a/src/core/libraries/ime/ime_ui.cpp +++ b/src/core/libraries/ime/ime_ui.cpp @@ -26,15 +26,13 @@ ImeState::ImeState(const OrbisImeParam* param) { } ImeState::ImeState(ImeState&& other) noexcept - : input_changed(other.input_changed), work_buffer(other.work_buffer), - text_buffer(other.text_buffer), current_text(std::move(other.current_text)), - event_queue(std::move(other.event_queue)) { + : work_buffer(other.work_buffer), text_buffer(other.text_buffer), + current_text(std::move(other.current_text)), event_queue(std::move(other.event_queue)) { other.text_buffer = nullptr; } ImeState& ImeState::operator=(ImeState&& other) noexcept { if (this != &other) { - input_changed = other.input_changed; work_buffer = other.work_buffer; text_buffer = other.text_buffer; current_text = std::move(other.current_text); @@ -52,17 +50,21 @@ void ImeState::SendEvent(OrbisImeEvent* event) { void ImeState::SendEnterEvent() { OrbisImeEvent enterEvent{}; - enterEvent.id = OrbisImeEventId::PRESS_ENTER; + enterEvent.id = OrbisImeEventId::PressEnter; SendEvent(&enterEvent); } void ImeState::SendCloseEvent() { OrbisImeEvent closeEvent{}; - closeEvent.id = OrbisImeEventId::PRESS_CLOSE; + closeEvent.id = OrbisImeEventId::PressClose; closeEvent.param.text.str = reinterpret_cast(work_buffer); SendEvent(&closeEvent); } +void ImeState::SetText(const char16_t* text, u32 length) {} + +void ImeState::SetCaret(u32 position) {} + bool ImeState::ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len, char* utf8_text, std::size_t utf8_text_len) { std::fill(utf8_text, utf8_text + utf8_text_len, '\0'); @@ -182,7 +184,6 @@ void ImeUi::DrawInputText() { } if (InputTextEx("##ImeInput", nullptr, state->current_text.begin(), ime_param->maxTextLength, input_size, ImGuiInputTextFlags_CallbackAlways, InputTextCallback, this)) { - state->input_changed = true; } } @@ -190,36 +191,17 @@ int ImeUi::InputTextCallback(ImGuiInputTextCallbackData* data) { ImeUi* ui = static_cast(data->UserData); ASSERT(ui); - static int lastCaretPos = -1; - if (lastCaretPos == -1) { - lastCaretPos = data->CursorPos; - } else if (data->CursorPos != lastCaretPos) { - OrbisImeCaretMovementDirection caretDirection = OrbisImeCaretMovementDirection::STILL; - if (data->CursorPos < lastCaretPos) { - caretDirection = OrbisImeCaretMovementDirection::LEFT; - } else if (data->CursorPos > lastCaretPos) { - caretDirection = OrbisImeCaretMovementDirection::RIGHT; - } - - OrbisImeEvent event{}; - event.id = OrbisImeEventId::UPDATE_CARET; - event.param.caretMove = caretDirection; - - lastCaretPos = data->CursorPos; - ui->state->SendEvent(&event); - } - static std::string lastText; std::string currentText(data->Buf, data->BufTextLen); if (currentText != lastText) { OrbisImeEditText eventParam{}; eventParam.str = reinterpret_cast(ui->ime_param->work); - eventParam.caretIndex = data->CursorPos; - eventParam.areaNum = 1; + eventParam.caret_index = data->CursorPos; + eventParam.area_num = 1; - eventParam.textArea[0].mode = 1; // Edit mode - eventParam.textArea[0].index = data->CursorPos; - eventParam.textArea[0].length = data->BufTextLen; + eventParam.text_area[0].mode = 1; // Edit mode + eventParam.text_area[0].index = data->CursorPos; + eventParam.text_area[0].length = data->BufTextLen; if (!ui->state->ConvertUTF8ToOrbis(data->Buf, data->BufTextLen, eventParam.str, ui->ime_param->maxTextLength)) { @@ -235,13 +217,32 @@ int ImeUi::InputTextCallback(ImGuiInputTextCallbackData* data) { } OrbisImeEvent event{}; - event.id = OrbisImeEventId::UPDATE_TEXT; + event.id = OrbisImeEventId::UpdateText; event.param.text = eventParam; lastText = currentText; ui->state->SendEvent(&event); } + static int lastCaretPos = -1; + if (lastCaretPos == -1) { + lastCaretPos = data->CursorPos; + } else if (data->CursorPos != lastCaretPos) { + OrbisImeCaretMovementDirection caretDirection = OrbisImeCaretMovementDirection::Still; + if (data->CursorPos < lastCaretPos) { + caretDirection = OrbisImeCaretMovementDirection::Left; + } else if (data->CursorPos > lastCaretPos) { + caretDirection = OrbisImeCaretMovementDirection::Right; + } + + OrbisImeEvent event{}; + event.id = OrbisImeEventId::UpdateCaret; + event.param.caret_move = caretDirection; + + lastCaretPos = data->CursorPos; + ui->state->SendEvent(&event); + } + return 0; } @@ -249,4 +250,4 @@ void ImeUi::Free() { RemoveLayer(this); } -}; // namespace Libraries::Ime \ No newline at end of file +}; // namespace Libraries::Ime diff --git a/src/core/libraries/ime/ime_ui.h b/src/core/libraries/ime/ime_ui.h index ebd70a7c8..a2a806bb9 100644 --- a/src/core/libraries/ime/ime_ui.h +++ b/src/core/libraries/ime/ime_ui.h @@ -22,10 +22,7 @@ class ImeState { friend class ImeHandler; friend class ImeUi; - bool input_changed = false; - void* work_buffer{}; - char16_t* text_buffer{}; // A character can hold up to 4 bytes in UTF-8 @@ -43,6 +40,9 @@ public: void SendEnterEvent(); void SendCloseEvent(); + void SetText(const char16_t* text, u32 length); + void SetCaret(u32 position); + private: bool ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len, char* utf8_text, std::size_t native_text_len); diff --git a/src/core/libraries/jpeg/jpeg_error.h b/src/core/libraries/jpeg/jpeg_error.h new file mode 100644 index 000000000..b9aa7483c --- /dev/null +++ b/src/core/libraries/jpeg/jpeg_error.h @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +constexpr int ORBIS_JPEG_ENC_ERROR_INVALID_ADDR = 0x80650101; +constexpr int ORBIS_JPEG_ENC_ERROR_INVALID_SIZE = 0x80650102; +constexpr int ORBIS_JPEG_ENC_ERROR_INVALID_PARAM = 0x80650103; +constexpr int ORBIS_JPEG_ENC_ERROR_INVALID_HANDLE = 0x80650104; diff --git a/src/core/libraries/jpeg/jpegenc.cpp b/src/core/libraries/jpeg/jpegenc.cpp new file mode 100644 index 000000000..b9c88d094 --- /dev/null +++ b/src/core/libraries/jpeg/jpegenc.cpp @@ -0,0 +1,208 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#include "common/alignment.h" +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" +#include "jpeg_error.h" +#include "jpegenc.h" + +namespace Libraries::JpegEnc { + +constexpr s32 ORBIS_JPEG_ENC_MINIMUM_MEMORY_SIZE = 0x800; +constexpr u32 ORBIS_JPEG_ENC_MAX_IMAGE_DIMENSION = 0xFFFF; +constexpr u32 ORBIS_JPEG_ENC_MAX_IMAGE_PITCH = 0xFFFFFFF; +constexpr u32 ORBIS_JPEG_ENC_MAX_IMAGE_SIZE = 0x7FFFFFFF; + +static s32 ValidateJpegEncCreateParam(const OrbisJpegEncCreateParam* param) { + if (!param) { + return ORBIS_JPEG_ENC_ERROR_INVALID_ADDR; + } + if (param->size != sizeof(OrbisJpegEncCreateParam)) { + return ORBIS_JPEG_ENC_ERROR_INVALID_SIZE; + } + if (param->attr != ORBIS_JPEG_ENC_ATTRIBUTE_NONE) { + return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM; + } + return ORBIS_OK; +} + +static s32 ValidateJpegEncMemory(const void* memory, const u32 memory_size) { + if (!memory) { + return ORBIS_JPEG_ENC_ERROR_INVALID_ADDR; + } + if (memory_size < ORBIS_JPEG_ENC_MINIMUM_MEMORY_SIZE) { + return ORBIS_JPEG_ENC_ERROR_INVALID_SIZE; + } + return ORBIS_OK; +} + +static s32 ValidateJpegEncEncodeParam(const OrbisJpegEncEncodeParam* param) { + + // Validate addresses + if (!param) { + return ORBIS_JPEG_ENC_ERROR_INVALID_ADDR; + } + if (!param->image || (param->pixel_format != ORBIS_JPEG_ENC_PIXEL_FORMAT_Y8 && + !Common::IsAligned(reinterpret_cast(param->image), 4))) { + return ORBIS_JPEG_ENC_ERROR_INVALID_ADDR; + } + if (!param->jpeg) { + return ORBIS_JPEG_ENC_ERROR_INVALID_ADDR; + } + + // Validate sizes + if (param->image_size == 0 || param->jpeg_size == 0) { + return ORBIS_JPEG_ENC_ERROR_INVALID_SIZE; + } + + // Validate parameters + if (param->image_width > ORBIS_JPEG_ENC_MAX_IMAGE_DIMENSION || + param->image_height > ORBIS_JPEG_ENC_MAX_IMAGE_DIMENSION) { + return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM; + } + if (param->image_pitch == 0 || param->image_pitch > ORBIS_JPEG_ENC_MAX_IMAGE_PITCH || + (param->pixel_format != ORBIS_JPEG_ENC_PIXEL_FORMAT_Y8 && + !Common::IsAligned(param->image_pitch, 4))) { + return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM; + } + const auto calculated_size = param->image_height * param->image_pitch; + if (calculated_size > ORBIS_JPEG_ENC_MAX_IMAGE_SIZE || calculated_size > param->image_size) { + return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM; + } + if (param->encode_mode != ORBIS_JPEG_ENC_ENCODE_MODE_NORMAL && + param->encode_mode != ORBIS_JPEG_ENC_ENCODE_MODE_MJPEG) { + return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM; + } + if (param->color_space != ORBIS_JPEG_ENC_COLOR_SPACE_YCC && + param->color_space != ORBIS_JPEG_ENC_COLOR_SPACE_GRAYSCALE) { + return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM; + } + if (param->sampling_type != ORBIS_JPEG_ENC_SAMPLING_TYPE_FULL && + param->sampling_type != ORBIS_JPEG_ENC_SAMPLING_TYPE_422 && + param->sampling_type != ORBIS_JPEG_ENC_SAMPLING_TYPE_420) { + return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM; + } + if (param->restart_interval > ORBIS_JPEG_ENC_MAX_IMAGE_DIMENSION) { + return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM; + } + switch (param->pixel_format) { + case ORBIS_JPEG_ENC_PIXEL_FORMAT_R8G8B8A8: + case ORBIS_JPEG_ENC_PIXEL_FORMAT_B8G8R8A8: + if (param->image_pitch >> 2 < param->image_width || + param->color_space != ORBIS_JPEG_ENC_COLOR_SPACE_YCC || + param->sampling_type == ORBIS_JPEG_ENC_SAMPLING_TYPE_FULL) { + return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM; + } + break; + case ORBIS_JPEG_ENC_PIXEL_FORMAT_Y8U8Y8V8: + if (param->image_pitch >> 1 < Common::AlignUp(param->image_width, 2) || + param->color_space != ORBIS_JPEG_ENC_COLOR_SPACE_YCC || + param->sampling_type == ORBIS_JPEG_ENC_SAMPLING_TYPE_FULL) { + return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM; + } + break; + case ORBIS_JPEG_ENC_PIXEL_FORMAT_Y8: + if (param->image_pitch < param->image_width || + param->color_space != ORBIS_JPEG_ENC_COLOR_SPACE_GRAYSCALE || + param->sampling_type != ORBIS_JPEG_ENC_SAMPLING_TYPE_FULL) { + return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM; + } + break; + default: + return ORBIS_JPEG_ENC_ERROR_INVALID_PARAM; + } + + return ORBIS_OK; +} + +static s32 ValidateJpecEngHandle(OrbisJpegEncHandle handle) { + if (!handle || !Common::IsAligned(reinterpret_cast(handle), 0x20) || + handle->handle != handle) { + return ORBIS_JPEG_ENC_ERROR_INVALID_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceJpegEncCreate(const OrbisJpegEncCreateParam* param, void* memory, + const u32 memory_size, OrbisJpegEncHandle* handle) { + if (auto param_ret = ValidateJpegEncCreateParam(param); param_ret != ORBIS_OK) { + LOG_ERROR(Lib_Jpeg, "Invalid create param"); + return param_ret; + } + if (auto memory_ret = ValidateJpegEncMemory(memory, memory_size); memory_ret != ORBIS_OK) { + LOG_ERROR(Lib_Jpeg, "Invalid memory"); + return memory_ret; + } + if (!handle) { + LOG_ERROR(Lib_Jpeg, "Invalid handle output"); + return ORBIS_JPEG_ENC_ERROR_INVALID_ADDR; + } + + auto* handle_internal = reinterpret_cast( + Common::AlignUp(reinterpret_cast(memory), 0x20)); + handle_internal->handle = handle_internal; + handle_internal->handle_size = sizeof(OrbisJpegEncHandleInternal*); + *handle = handle_internal; + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceJpegEncDelete(OrbisJpegEncHandle handle) { + if (auto handle_ret = ValidateJpecEngHandle(handle); handle_ret != ORBIS_OK) { + LOG_ERROR(Lib_Jpeg, "Invalid handle"); + return handle_ret; + } + handle->handle = nullptr; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceJpegEncEncode(OrbisJpegEncHandle handle, const OrbisJpegEncEncodeParam* param, + OrbisJpegEncOutputInfo* output_info) { + if (auto handle_ret = ValidateJpecEngHandle(handle); handle_ret != ORBIS_OK) { + LOG_ERROR(Lib_Jpeg, "Invalid handle"); + return handle_ret; + } + if (auto param_ret = ValidateJpegEncEncodeParam(param); param_ret != ORBIS_OK) { + LOG_ERROR(Lib_Jpeg, "Invalid encode param"); + return param_ret; + } + + LOG_ERROR(Lib_Jpeg, + "(STUBBED) image_size = {} , jpeg_size = {} , image_width = {} , image_height = {} , " + "image_pitch = {} , pixel_format = {} , encode_mode = {} , color_space = {} , " + "sampling_type = {} , compression_ratio = {} , restart_interval = {}", + param->image_size, param->jpeg_size, param->image_width, param->image_height, + param->image_pitch, magic_enum::enum_name(param->pixel_format), + magic_enum::enum_name(param->encode_mode), magic_enum::enum_name(param->color_space), + magic_enum::enum_name(param->sampling_type), param->compression_ratio, + param->restart_interval); + + if (output_info) { + output_info->size = param->jpeg_size; + output_info->height = param->image_height; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceJpegEncQueryMemorySize(const OrbisJpegEncCreateParam* param) { + if (auto param_ret = ValidateJpegEncCreateParam(param); param_ret != ORBIS_OK) { + LOG_ERROR(Lib_Jpeg, "Invalid create param"); + return param_ret; + } + return ORBIS_JPEG_ENC_MINIMUM_MEMORY_SIZE; +} + +void RegisterlibSceJpegEnc(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("K+rocojkr-I", "libSceJpegEnc", 1, "libSceJpegEnc", 1, 1, sceJpegEncCreate); + LIB_FUNCTION("j1LyMdaM+C0", "libSceJpegEnc", 1, "libSceJpegEnc", 1, 1, sceJpegEncDelete); + LIB_FUNCTION("QbrU0cUghEM", "libSceJpegEnc", 1, "libSceJpegEnc", 1, 1, sceJpegEncEncode); + LIB_FUNCTION("o6ZgXfFdWXQ", "libSceJpegEnc", 1, "libSceJpegEnc", 1, 1, + sceJpegEncQueryMemorySize); +}; + +} // namespace Libraries::JpegEnc diff --git a/src/core/libraries/jpeg/jpegenc.h b/src/core/libraries/jpeg/jpegenc.h new file mode 100644 index 000000000..a6b4d311a --- /dev/null +++ b/src/core/libraries/jpeg/jpegenc.h @@ -0,0 +1,84 @@ +// 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; +} + +namespace Libraries::JpegEnc { + +enum OrbisJpegEncCreateParamAttributes : u32 { ORBIS_JPEG_ENC_ATTRIBUTE_NONE = 0 }; + +enum OrbisJpegEncEncodeParamPixelFormat : u16 { + ORBIS_JPEG_ENC_PIXEL_FORMAT_R8G8B8A8 = 0, + ORBIS_JPEG_ENC_PIXEL_FORMAT_B8G8R8A8 = 1, + ORBIS_JPEG_ENC_PIXEL_FORMAT_Y8U8Y8V8 = 10, + ORBIS_JPEG_ENC_PIXEL_FORMAT_Y8 = 11 +}; + +enum OrbisJpengEncEncodeParamEncodeMode : u16 { + ORBIS_JPEG_ENC_ENCODE_MODE_NORMAL = 0, + ORBIS_JPEG_ENC_ENCODE_MODE_MJPEG = 1 +}; + +enum OrbisJpengEncEncodeParamColorSpace : u16 { + ORBIS_JPEG_ENC_COLOR_SPACE_YCC = 1, + ORBIS_JPEG_ENC_COLOR_SPACE_GRAYSCALE = 2 +}; + +enum OrbisJpengEncEncodeParamSamplingType : u8 { + ORBIS_JPEG_ENC_SAMPLING_TYPE_FULL = 0, + ORBIS_JPEG_ENC_SAMPLING_TYPE_422 = 1, + ORBIS_JPEG_ENC_SAMPLING_TYPE_420 = 2 +}; + +struct OrbisJpegEncHandleInternal { + OrbisJpegEncHandleInternal* handle; + u32 handle_size; +}; +static_assert(sizeof(OrbisJpegEncHandleInternal) == 0x10); + +typedef OrbisJpegEncHandleInternal* OrbisJpegEncHandle; + +struct OrbisJpegEncCreateParam { + u32 size; + OrbisJpegEncCreateParamAttributes attr; +}; +static_assert(sizeof(OrbisJpegEncCreateParam) == 0x8); + +struct OrbisJpegEncEncodeParam { + void* image; + void* jpeg; + u32 image_size; + u32 jpeg_size; + u32 image_width; + u32 image_height; + u32 image_pitch; + OrbisJpegEncEncodeParamPixelFormat pixel_format; + OrbisJpengEncEncodeParamEncodeMode encode_mode; + OrbisJpengEncEncodeParamColorSpace color_space; + OrbisJpengEncEncodeParamSamplingType sampling_type; + u8 compression_ratio; + s32 restart_interval; +}; +static_assert(sizeof(OrbisJpegEncEncodeParam) == 0x30); + +struct OrbisJpegEncOutputInfo { + u32 size; + u32 height; +}; +static_assert(sizeof(OrbisJpegEncOutputInfo) == 0x8); + +s32 PS4_SYSV_ABI sceJpegEncCreate(const OrbisJpegEncCreateParam* param, void* memory, + u32 memory_size, OrbisJpegEncHandle* handle); +s32 PS4_SYSV_ABI sceJpegEncDelete(OrbisJpegEncHandle handle); +s32 PS4_SYSV_ABI sceJpegEncEncode(OrbisJpegEncHandle handle, const OrbisJpegEncEncodeParam* param, + OrbisJpegEncOutputInfo* output_info); +s32 PS4_SYSV_ABI sceJpegEncQueryMemorySize(const OrbisJpegEncCreateParam* param); + +void RegisterlibSceJpegEnc(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::JpegEnc diff --git a/src/core/libraries/kernel/cpu_management.cpp b/src/core/libraries/kernel/cpu_management.cpp deleted file mode 100644 index 3bf609dfe..000000000 --- a/src/core/libraries/kernel/cpu_management.cpp +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/config.h" -#include "common/logging/log.h" -#include "core/libraries/kernel/cpu_management.h" - -namespace Libraries::Kernel { - -int PS4_SYSV_ABI sceKernelIsNeoMode() { - LOG_DEBUG(Kernel_Sce, "called"); - return Config::isNeoMode(); -} - -} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/event_queues.cpp b/src/core/libraries/kernel/equeue.cpp similarity index 56% rename from src/core/libraries/kernel/event_queues.cpp rename to src/core/libraries/kernel/equeue.cpp index 540c20c43..6543cc319 100644 --- a/src/core/libraries/kernel/event_queues.cpp +++ b/src/core/libraries/kernel/equeue.cpp @@ -4,11 +4,142 @@ #include "common/assert.h" #include "common/debug.h" #include "common/logging/log.h" -#include "core/libraries/error_codes.h" -#include "core/libraries/kernel/event_queues.h" +#include "core/libraries/kernel/equeue.h" +#include "core/libraries/kernel/orbis_error.h" +#include "core/libraries/libs.h" namespace Libraries::Kernel { +bool EqueueInternal::AddEvent(EqueueEvent& event) { + std::scoped_lock lock{m_mutex}; + + event.time_added = std::chrono::steady_clock::now(); + + const auto& it = std::ranges::find(m_events, event); + if (it != m_events.cend()) { + *it = std::move(event); + } else { + m_events.emplace_back(std::move(event)); + } + + return true; +} + +bool EqueueInternal::RemoveEvent(u64 id) { + bool has_found = false; + std::scoped_lock lock{m_mutex}; + + const auto& it = + std::ranges::find_if(m_events, [id](auto& ev) { return ev.event.ident == id; }); + if (it != m_events.cend()) { + m_events.erase(it); + has_found = true; + } + return has_found; +} + +int EqueueInternal::WaitForEvents(SceKernelEvent* ev, int num, u32 micros) { + int count = 0; + + const auto predicate = [&] { + count = GetTriggeredEvents(ev, num); + return count > 0; + }; + + if (micros == 0) { + std::unique_lock lock{m_mutex}; + m_cond.wait(lock, predicate); + } else { + std::unique_lock lock{m_mutex}; + m_cond.wait_for(lock, std::chrono::microseconds(micros), predicate); + } + + if (HasSmallTimer()) { + if (count > 0) { + const auto time_waited = std::chrono::duration_cast( + std::chrono::steady_clock::now() - m_events[0].time_added) + .count(); + count = WaitForSmallTimer(ev, num, std::max(0l, long(micros - time_waited))); + } + small_timer_event.event.data = 0; + } + + if (ev->flags & SceKernelEvent::Flags::OneShot) { + for (auto ev_id = 0u; ev_id < count; ++ev_id) { + RemoveEvent(ev->ident); + } + } + + return count; +} + +bool EqueueInternal::TriggerEvent(u64 ident, s16 filter, void* trigger_data) { + bool has_found = false; + { + std::scoped_lock lock{m_mutex}; + for (auto& event : m_events) { + if (event.event.ident == ident && event.event.filter == filter) { + event.Trigger(trigger_data); + has_found = true; + } + } + } + m_cond.notify_one(); + return has_found; +} + +int EqueueInternal::GetTriggeredEvents(SceKernelEvent* ev, int num) { + int count = 0; + for (auto& event : m_events) { + if (event.IsTriggered()) { + if (event.event.flags & SceKernelEvent::Flags::Clear) { + event.Reset(); + } + ev[count++] = event.event; + if (count == num) { + break; + } + } + } + + return count; +} + +bool EqueueInternal::AddSmallTimer(EqueueEvent& ev) { + // We assume that only one timer event (with the same ident across calls) + // can be posted to the queue, based on observations so far. In the opposite case, + // the small timer storage and wait logic should be reworked. + ASSERT(!HasSmallTimer() || small_timer_event.event.ident == ev.event.ident); + ev.time_added = std::chrono::steady_clock::now(); + small_timer_event = std::move(ev); + return true; +} + +int EqueueInternal::WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros) { + int count{}; + + ASSERT(num == 1); + + auto curr_clock = std::chrono::steady_clock::now(); + const auto wait_end_us = curr_clock + std::chrono::microseconds{micros}; + + do { + curr_clock = std::chrono::steady_clock::now(); + { + std::scoped_lock lock{m_mutex}; + if ((curr_clock - small_timer_event.time_added) > + std::chrono::microseconds{small_timer_event.event.data}) { + ev[count++] = small_timer_event.event; + small_timer_event.event.data = 0; + break; + } + } + std::this_thread::yield(); + } while (curr_clock < wait_end_us); + + return count; +} + extern boost::asio::io_context io_context; extern void KernelSignalRequest(); @@ -42,8 +173,7 @@ int PS4_SYSV_ABI sceKernelCreateEqueue(SceKernelEqueue* eq, const char* name) { LOG_INFO(Kernel_Event, "name = {}", name); - *eq = new EqueueInternal; - (*eq)->setName(std::string(name)); + *eq = new EqueueInternal(name); return ORBIS_OK; } @@ -211,4 +341,19 @@ int PS4_SYSV_ABI sceKernelDeleteUserEvent(SceKernelEqueue eq, int id) { s16 PS4_SYSV_ABI sceKernelGetEventFilter(const SceKernelEvent* ev) { return ev->filter; } + +void RegisterEventQueue(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("D0OdFMjp46I", "libkernel", 1, "libkernel", 1, 1, sceKernelCreateEqueue); + LIB_FUNCTION("jpFjmgAC5AE", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteEqueue); + LIB_FUNCTION("fzyMKs9kim0", "libkernel", 1, "libkernel", 1, 1, sceKernelWaitEqueue); + LIB_FUNCTION("vz+pg2zdopI", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventUserData); + LIB_FUNCTION("4R6-OvI2cEA", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEvent); + LIB_FUNCTION("WDszmSbWuDk", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEventEdge); + LIB_FUNCTION("R74tt43xP6k", "libkernel", 1, "libkernel", 1, 1, sceKernelAddHRTimerEvent); + LIB_FUNCTION("F6e0kwo4cnk", "libkernel", 1, "libkernel", 1, 1, sceKernelTriggerUserEvent); + LIB_FUNCTION("LJDwdSNTnDg", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteUserEvent); + LIB_FUNCTION("mJ7aghmgvfc", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventId); + LIB_FUNCTION("23CPPI1tyBY", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventFilter); +} + } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/event_queue.h b/src/core/libraries/kernel/equeue.h similarity index 89% rename from src/core/libraries/kernel/event_queue.h rename to src/core/libraries/kernel/equeue.h index 30fdb41e3..5a13bdecd 100644 --- a/src/core/libraries/kernel/event_queue.h +++ b/src/core/libraries/kernel/equeue.h @@ -7,11 +7,14 @@ #include #include #include - #include #include "common/types.h" +namespace Core::Loader { +class SymbolsResolver; +} + namespace Libraries::Kernel { class EqueueInternal; @@ -89,14 +92,12 @@ private: class EqueueInternal { public: - EqueueInternal() = default; - virtual ~EqueueInternal(); - void setName(const std::string& m_name) { - this->m_name = m_name; - } - const auto& GetName() const { + explicit EqueueInternal(std::string_view name) : m_name(name) {} + + std::string_view GetName() const { return m_name; } + bool AddEvent(EqueueEvent& event); bool RemoveEvent(u64 id); int WaitForEvents(SceKernelEvent* ev, int num, u32 micros); @@ -107,6 +108,7 @@ public: bool HasSmallTimer() const { return small_timer_event.event.data != 0; } + int WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros); private: @@ -117,4 +119,9 @@ private: std::condition_variable m_cond; }; +using SceKernelUseconds = u32; +using SceKernelEqueue = EqueueInternal*; + +void RegisterEventQueue(Core::Loader::SymbolsResolver* sym); + } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/event_flag/event_flag.h b/src/core/libraries/kernel/event_flag/event_flag.h deleted file mode 100644 index 2147e3f15..000000000 --- a/src/core/libraries/kernel/event_flag/event_flag.h +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/types.h" -#include "event_flag_codes.h" -#include "event_flag_obj.h" - -namespace Core::Loader { -class SymbolsResolver; -} - -namespace Libraries::Kernel { - -using OrbisKernelUseconds = u32; -using OrbisKernelEventFlag = EventFlagInternal*; - -struct OrbisKernelEventFlagOptParam { - size_t size; -}; - -int PS4_SYSV_ABI sceKernelCreateEventFlag(OrbisKernelEventFlag* ef, const char* pName, u32 attr, - u64 initPattern, - const OrbisKernelEventFlagOptParam* pOptParam); -int PS4_SYSV_ABI sceKernelDeleteEventFlag(OrbisKernelEventFlag ef); -int PS4_SYSV_ABI sceKernelOpenEventFlag(); -int PS4_SYSV_ABI sceKernelCloseEventFlag(); -int PS4_SYSV_ABI sceKernelClearEventFlag(OrbisKernelEventFlag ef, u64 bitPattern); -int PS4_SYSV_ABI sceKernelCancelEventFlag(OrbisKernelEventFlag ef, u64 setPattern, - int* pNumWaitThreads); -int PS4_SYSV_ABI sceKernelSetEventFlag(OrbisKernelEventFlag ef, u64 bitPattern); -int PS4_SYSV_ABI sceKernelPollEventFlag(OrbisKernelEventFlag ef, u64 bitPattern, u32 waitMode, - u64* pResultPat); -int PS4_SYSV_ABI sceKernelWaitEventFlag(OrbisKernelEventFlag ef, u64 bitPattern, u32 waitMode, - u64* pResultPat, OrbisKernelUseconds* pTimeout); - -void RegisterKernelEventFlag(Core::Loader::SymbolsResolver* sym); - -} // namespace Libraries::Kernel \ No newline at end of file diff --git a/src/core/libraries/kernel/event_flag/event_flag_codes.h b/src/core/libraries/kernel/event_flag/event_flag_codes.h deleted file mode 100644 index 92b265c8d..000000000 --- a/src/core/libraries/kernel/event_flag/event_flag_codes.h +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -constexpr int ORBIS_KERNEL_EVF_ATTR_TH_FIFO = 0x01; -constexpr int ORBIS_KERNEL_EVF_ATTR_TH_PRIO = 0x02; -constexpr int ORBIS_KERNEL_EVF_ATTR_SINGLE = 0x10; -constexpr int ORBIS_KERNEL_EVF_ATTR_MULTI = 0x20; - -constexpr int ORBIS_KERNEL_EVF_WAITMODE_AND = 0x01; -constexpr int ORBIS_KERNEL_EVF_WAITMODE_OR = 0x02; -constexpr int ORBIS_KERNEL_EVF_WAITMODE_CLEAR_ALL = 0x10; -constexpr int ORBIS_KERNEL_EVF_WAITMODE_CLEAR_PAT = 0x20; \ No newline at end of file diff --git a/src/core/libraries/kernel/event_flag/event_flag_obj.cpp b/src/core/libraries/kernel/event_flag/event_flag_obj.cpp deleted file mode 100644 index 6d6dcf7a9..000000000 --- a/src/core/libraries/kernel/event_flag/event_flag_obj.cpp +++ /dev/null @@ -1,111 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include - -#include "core/libraries/error_codes.h" -#include "event_flag_obj.h" - -namespace Libraries::Kernel { -int EventFlagInternal::Wait(u64 bits, WaitMode wait_mode, ClearMode clear_mode, u64* result, - u32* ptr_micros) { - std::unique_lock lock{m_mutex}; - - uint32_t micros = 0; - bool infinitely = true; - if (ptr_micros != nullptr) { - micros = *ptr_micros; - infinitely = false; - } - - if (m_thread_mode == ThreadMode::Single && m_waiting_threads > 0) { - return ORBIS_KERNEL_ERROR_EPERM; - } - - auto const start = std::chrono::system_clock::now(); - m_waiting_threads++; - auto waitFunc = [this, wait_mode, bits] { - return (m_status == Status::Canceled || m_status == Status::Deleted || - (wait_mode == WaitMode::And && (m_bits & bits) == bits) || - (wait_mode == WaitMode::Or && (m_bits & bits) != 0)); - }; - - if (infinitely) { - m_cond_var.wait(lock, waitFunc); - } else { - if (!m_cond_var.wait_for(lock, std::chrono::microseconds(micros), waitFunc)) { - if (result != nullptr) { - *result = m_bits; - } - *ptr_micros = 0; - --m_waiting_threads; - return ORBIS_KERNEL_ERROR_ETIMEDOUT; - } - } - --m_waiting_threads; - if (result != nullptr) { - *result = m_bits; - } - - auto elapsed = std::chrono::duration_cast( - std::chrono::system_clock::now() - start) - .count(); - if (result != nullptr) { - *result = m_bits; - } - - if (ptr_micros != nullptr) { - *ptr_micros = (elapsed >= micros ? 0 : micros - elapsed); - } - - if (m_status == Status::Canceled) { - return ORBIS_KERNEL_ERROR_ECANCELED; - } else if (m_status == Status::Deleted) { - return ORBIS_KERNEL_ERROR_EACCES; - } - - if (clear_mode == ClearMode::All) { - m_bits = 0; - } else if (clear_mode == ClearMode::Bits) { - m_bits &= ~bits; - } - - return ORBIS_OK; -} - -int EventFlagInternal::Poll(u64 bits, WaitMode wait_mode, ClearMode clear_mode, u64* result) { - u32 micros = 0; - auto ret = Wait(bits, wait_mode, clear_mode, result, µs); - if (ret == ORBIS_KERNEL_ERROR_ETIMEDOUT) { - // Poll returns EBUSY instead. - ret = ORBIS_KERNEL_ERROR_EBUSY; - } - return ret; -} - -void EventFlagInternal::Set(u64 bits) { - std::unique_lock lock{m_mutex}; - - while (m_status != Status::Set) { - m_mutex.unlock(); - std::this_thread::sleep_for(std::chrono::microseconds(10)); - m_mutex.lock(); - } - - m_bits |= bits; - - m_cond_var.notify_all(); -} - -void EventFlagInternal::Clear(u64 bits) { - std::unique_lock lock{m_mutex}; - while (m_status != Status::Set) { - m_mutex.unlock(); - std::this_thread::sleep_for(std::chrono::microseconds(10)); - m_mutex.lock(); - } - - m_bits &= bits; -} - -} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/event_flag/event_flag_obj.h b/src/core/libraries/kernel/event_flag/event_flag_obj.h deleted file mode 100644 index 8d1624e2b..000000000 --- a/src/core/libraries/kernel/event_flag/event_flag_obj.h +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include - -#include "common/types.h" - -namespace Libraries::Kernel { - -class EventFlagInternal { -public: - enum class ClearMode { None, All, Bits }; - - enum class WaitMode { And, Or }; - - enum class ThreadMode { Single, Multi }; - - enum class QueueMode { Fifo, ThreadPrio }; - - EventFlagInternal(const std::string& name, ThreadMode thread_mode, QueueMode queue_mode, - uint64_t bits) - : m_name(name), m_thread_mode(thread_mode), m_queue_mode(queue_mode), m_bits(bits){}; - - int Wait(u64 bits, WaitMode wait_mode, ClearMode clear_mode, u64* result, u32* ptr_micros); - int Poll(u64 bits, WaitMode wait_mode, ClearMode clear_mode, u64* result); - void Set(u64 bits); - void Clear(u64 bits); - -private: - enum class Status { Set, Canceled, Deleted }; - - std::mutex m_mutex; - std::condition_variable m_cond_var; - Status m_status = Status::Set; - int m_waiting_threads = 0; - std::string m_name; - ThreadMode m_thread_mode = ThreadMode::Single; - QueueMode m_queue_mode = QueueMode::Fifo; - u64 m_bits = 0; -}; -} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/event_queue.cpp b/src/core/libraries/kernel/event_queue.cpp deleted file mode 100644 index 88918bf54..000000000 --- a/src/core/libraries/kernel/event_queue.cpp +++ /dev/null @@ -1,150 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include - -#include "common/assert.h" -#include "core/libraries/kernel/event_queue.h" - -namespace Libraries::Kernel { - -EqueueInternal::~EqueueInternal() = default; - -bool EqueueInternal::AddEvent(EqueueEvent& event) { - std::scoped_lock lock{m_mutex}; - - event.time_added = std::chrono::steady_clock::now(); - - const auto& it = std::ranges::find(m_events, event); - if (it != m_events.cend()) { - *it = std::move(event); - } else { - m_events.emplace_back(std::move(event)); - } - - return true; -} - -bool EqueueInternal::RemoveEvent(u64 id) { - bool has_found = false; - std::scoped_lock lock{m_mutex}; - - const auto& it = - std::ranges::find_if(m_events, [id](auto& ev) { return ev.event.ident == id; }); - if (it != m_events.cend()) { - m_events.erase(it); - has_found = true; - } - return has_found; -} - -int EqueueInternal::WaitForEvents(SceKernelEvent* ev, int num, u32 micros) { - int count = 0; - - const auto predicate = [&] { - count = GetTriggeredEvents(ev, num); - return count > 0; - }; - - if (micros == 0) { - std::unique_lock lock{m_mutex}; - m_cond.wait(lock, predicate); - } else { - std::unique_lock lock{m_mutex}; - m_cond.wait_for(lock, std::chrono::microseconds(micros), predicate); - } - - if (HasSmallTimer()) { - if (count > 0) { - const auto time_waited = std::chrono::duration_cast( - std::chrono::steady_clock::now() - m_events[0].time_added) - .count(); - count = WaitForSmallTimer(ev, num, std::max(0l, long(micros - time_waited))); - } - small_timer_event.event.data = 0; - } - - if (ev->flags & SceKernelEvent::Flags::OneShot) { - for (auto ev_id = 0u; ev_id < count; ++ev_id) { - RemoveEvent(ev->ident); - } - } - - return count; -} - -bool EqueueInternal::TriggerEvent(u64 ident, s16 filter, void* trigger_data) { - bool has_found = false; - { - std::scoped_lock lock{m_mutex}; - - for (auto& event : m_events) { - if ((event.event.ident == ident) && (event.event.filter == filter)) { - event.Trigger(trigger_data); - has_found = true; - } - } - } - m_cond.notify_one(); - return has_found; -} - -int EqueueInternal::GetTriggeredEvents(SceKernelEvent* ev, int num) { - int count = 0; - - for (auto& event : m_events) { - if (event.IsTriggered()) { - if (event.event.flags & SceKernelEvent::Flags::Clear) { - event.Reset(); - } - - ev[count++] = event.event; - - if (count == num) { - break; - } - } - } - - return count; -} - -bool EqueueInternal::AddSmallTimer(EqueueEvent& ev) { - // We assume that only one timer event (with the same ident across calls) - // can be posted to the queue, based on observations so far. In the opposite case, - // the small timer storage and wait logic should be reworked. - ASSERT(!HasSmallTimer() || small_timer_event.event.ident == ev.event.ident); - ev.time_added = std::chrono::steady_clock::now(); - small_timer_event = std::move(ev); - return true; -} - -int EqueueInternal::WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros) { - int count{}; - - ASSERT(num == 1); - - auto curr_clock = std::chrono::steady_clock::now(); - const auto wait_end_us = curr_clock + std::chrono::microseconds{micros}; - - do { - curr_clock = std::chrono::steady_clock::now(); - - { - std::unique_lock lock{m_mutex}; - if ((curr_clock - small_timer_event.time_added) > - std::chrono::microseconds{small_timer_event.event.data}) { - ev[count++] = small_timer_event.event; - small_timer_event.event.data = 0; - break; - } - } - - std::this_thread::yield(); - - } while (curr_clock < wait_end_us); - - return count; -} - -} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/event_queues.h b/src/core/libraries/kernel/event_queues.h deleted file mode 100644 index d400ff187..000000000 --- a/src/core/libraries/kernel/event_queues.h +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/libraries/kernel/event_queue.h" - -namespace Libraries::Kernel { - -using SceKernelUseconds = u32; -using SceKernelEqueue = EqueueInternal*; - -int PS4_SYSV_ABI sceKernelCreateEqueue(SceKernelEqueue* eq, const char* name); -int PS4_SYSV_ABI sceKernelDeleteEqueue(SceKernelEqueue eq); -int PS4_SYSV_ABI sceKernelWaitEqueue(SceKernelEqueue eq, SceKernelEvent* ev, int num, int* out, - SceKernelUseconds* timo); -void* PS4_SYSV_ABI sceKernelGetEventUserData(const SceKernelEvent* ev); -u64 PS4_SYSV_ABI sceKernelGetEventId(const SceKernelEvent* ev); -int PS4_SYSV_ABI sceKernelTriggerUserEvent(SceKernelEqueue eq, int id, void* udata); -int PS4_SYSV_ABI sceKernelDeleteUserEvent(SceKernelEqueue eq, int id); -int PS4_SYSV_ABI sceKernelAddUserEvent(SceKernelEqueue eq, int id); -int PS4_SYSV_ABI sceKernelAddUserEventEdge(SceKernelEqueue eq, int id); -s32 PS4_SYSV_ABI sceKernelAddHRTimerEvent(SceKernelEqueue eq, int id, timespec* ts, void* udata); -s16 PS4_SYSV_ABI sceKernelGetEventFilter(const SceKernelEvent* ev); - -} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index e6b657c3a..2a65255fb 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -6,14 +6,47 @@ #include "common/scope_exit.h" #include "common/singleton.h" #include "core/file_sys/fs.h" -#include "core/libraries/error_codes.h" #include "core/libraries/kernel/file_system.h" +#include "core/libraries/kernel/orbis_error.h" #include "core/libraries/libs.h" -#include "libkernel.h" +#include "kernel.h" + +#include +#include + +#include "core/devices/logger.h" +#include "core/devices/nop_device.h" + +namespace D = Core::Devices; +using FactoryDevice = std::function(u32, const char*, int, u16)>; + +#define GET_DEVICE_FD(fd) \ + [](u32, const char*, int, u16) { \ + return Common::Singleton::Instance()->GetFile(fd)->device; \ + } + +// prefix path, only dev devices +static std::map available_device = { + // clang-format off + {"/dev/stdin", GET_DEVICE_FD(0)}, + {"/dev/stdout", GET_DEVICE_FD(1)}, + {"/dev/stderr", GET_DEVICE_FD(2)}, + + {"/dev/fd/0", GET_DEVICE_FD(0)}, + {"/dev/fd/1", GET_DEVICE_FD(1)}, + {"/dev/fd/2", GET_DEVICE_FD(2)}, + + {"/dev/deci_stdin", GET_DEVICE_FD(0)}, + {"/dev/deci_stdout", GET_DEVICE_FD(1)}, + {"/dev/deci_stderr", GET_DEVICE_FD(2)}, + + {"/dev/null", GET_DEVICE_FD(0)}, // fd0 (stdin) is a nop device + // clang-format on +}; namespace Libraries::Kernel { -std::vector GetDirectoryEntries(const std::filesystem::path& path) { +auto GetDirectoryEntries(const std::filesystem::path& path) { std::vector files; for (const auto& entry : std::filesystem::directory_iterator(path)) { auto& dir_entry = files.emplace_back(); @@ -24,8 +57,8 @@ std::vector GetDirectoryEntries(const std::filesystem:: return files; } -int PS4_SYSV_ABI sceKernelOpen(const char* path, int flags, u16 mode) { - LOG_INFO(Kernel_Fs, "path = {} flags = {:#x} mode = {}", path, flags, mode); +int PS4_SYSV_ABI sceKernelOpen(const char* raw_path, int flags, u16 mode) { + LOG_INFO(Kernel_Fs, "path = {} flags = {:#x} mode = {}", raw_path, flags, mode); auto* h = Common::Singleton::Instance(); auto* mnt = Common::Singleton::Instance(); @@ -44,22 +77,35 @@ int PS4_SYSV_ABI sceKernelOpen(const char* path, int flags, u16 mode) { bool direct = (flags & ORBIS_KERNEL_O_DIRECT) != 0; bool directory = (flags & ORBIS_KERNEL_O_DIRECTORY) != 0; - if (std::string_view{path} == "/dev/console") { + std::string_view path{raw_path}; + + if (path == "/dev/console") { return 2000; } - if (std::string_view{path} == "/dev/deci_tty6") { + if (path == "/dev/deci_tty6") { return 2001; } - if (std::string_view{path} == "/dev/stdout") { - return 2002; - } - if (std::string_view{path} == "/dev/urandom") { + if (path == "/dev/urandom") { return 2003; } + u32 handle = h->CreateHandle(); auto* file = h->GetFile(handle); + + if (path.starts_with("/dev/")) { + for (const auto& [prefix, factory] : available_device) { + if (path.starts_with(prefix)) { + file->is_opened = true; + file->type = Core::FileSys::FileType::Device; + file->m_guest_name = path; + file->device = factory(handle, path.data(), flags, mode); + return handle; + } + } + } + if (directory) { - file->is_directory = true; + 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 @@ -128,20 +174,21 @@ int PS4_SYSV_ABI sceKernelClose(int d) { return ORBIS_KERNEL_ERROR_EPERM; } if (d == 2003) { // dev/urandom case - return SCE_OK; + return ORBIS_OK; } auto* h = Common::Singleton::Instance(); auto* file = h->GetFile(d); if (file == nullptr) { - return SCE_KERNEL_ERROR_EBADF; + return ORBIS_KERNEL_ERROR_EBADF; } - if (!file->is_directory) { + if (file->type == Core::FileSys::FileType::Regular) { file->f.Close(); } file->is_opened = false; LOG_INFO(Kernel_Fs, "Closing {}", file->m_guest_name); + // FIXME: Lock file mutex before deleting it? h->DeleteHandle(d); - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI posix_close(int d) { @@ -155,27 +202,22 @@ int PS4_SYSV_ABI posix_close(int d) { } size_t PS4_SYSV_ABI sceKernelWrite(int d, const void* buf, size_t nbytes) { - if (d <= 2) { // stdin,stdout,stderr - char* str = strdup((const char*)buf); - if (str[nbytes - 1] == '\n') - str[nbytes - 1] = 0; - LOG_INFO(Tty, "{}", str); - free(str); - return nbytes; - } auto* h = Common::Singleton::Instance(); auto* file = h->GetFile(d); if (file == nullptr) { - return SCE_KERNEL_ERROR_EBADF; + return ORBIS_KERNEL_ERROR_EBADF; } std::scoped_lock lk{file->m_mutex}; + if (file->type == Core::FileSys::FileType::Device) { + return file->device->write(buf, nbytes); + } return file->f.WriteRaw(buf, nbytes); } int PS4_SYSV_ABI sceKernelUnlink(const char* path) { if (path == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } auto* h = Common::Singleton::Instance(); @@ -184,15 +226,15 @@ int PS4_SYSV_ABI sceKernelUnlink(const char* path) { bool ro = false; const auto host_path = mnt->GetHostPath(path, &ro); if (host_path.empty()) { - return SCE_KERNEL_ERROR_EACCES; + return ORBIS_KERNEL_ERROR_EACCES; } if (ro) { - return SCE_KERNEL_ERROR_EROFS; + return ORBIS_KERNEL_ERROR_EROFS; } if (std::filesystem::is_directory(host_path)) { - return SCE_KERNEL_ERROR_EPERM; + return ORBIS_KERNEL_ERROR_EPERM; } auto* file = h->GetFile(host_path); @@ -201,23 +243,69 @@ int PS4_SYSV_ABI sceKernelUnlink(const char* path) { } LOG_INFO(Kernel_Fs, "Unlinked {}", path); - return SCE_OK; + return ORBIS_OK; } size_t PS4_SYSV_ABI _readv(int d, const SceKernelIovec* iov, int iovcnt) { auto* h = Common::Singleton::Instance(); auto* file = h->GetFile(d); - size_t total_read = 0; + if (file == nullptr) { + return ORBIS_KERNEL_ERROR_EBADF; + } + std::scoped_lock lk{file->m_mutex}; + if (file->type == Core::FileSys::FileType::Device) { + int r = file->device->readv(iov, iovcnt); + if (r < 0) { + ErrSceToPosix(r); + return -1; + } + return r; + } + size_t total_read = 0; for (int i = 0; i < iovcnt; i++) { total_read += file->f.ReadRaw(iov[i].iov_base, iov[i].iov_len); } return total_read; } +size_t PS4_SYSV_ABI _writev(int fd, const SceKernelIovec* iov, int iovcn) { + if (fd == 1) { + size_t total_written = 0; + for (int i = 0; i < iovcn; i++) { + total_written += ::fwrite(iov[i].iov_base, 1, iov[i].iov_len, stdout); + } + return total_written; + } + auto* h = Common::Singleton::Instance(); + auto* file = h->GetFile(fd); + if (file == nullptr) { + return ORBIS_KERNEL_ERROR_EBADF; + } + + std::scoped_lock lk{file->m_mutex}; + + if (file->type == Core::FileSys::FileType::Device) { + return file->device->writev(iov, iovcn); + } + size_t total_written = 0; + for (int i = 0; i < iovcn; i++) { + total_written += file->f.WriteRaw(iov[i].iov_base, iov[i].iov_len); + } + return total_written; +} + s64 PS4_SYSV_ABI sceKernelLseek(int d, s64 offset, int whence) { auto* h = Common::Singleton::Instance(); auto* file = h->GetFile(d); + if (file == nullptr) { + return ORBIS_KERNEL_ERROR_EBADF; + } + + std::scoped_lock lk{file->m_mutex}; + if (file->type == Core::FileSys::FileType::Device) { + return file->device->lseek(offset, whence); + } Common::FS::SeekOrigin origin{}; if (whence == 0) { @@ -228,10 +316,9 @@ s64 PS4_SYSV_ABI sceKernelLseek(int d, s64 offset, int whence) { origin = Common::FS::SeekOrigin::End; } - std::scoped_lock lk{file->m_mutex}; if (!file->f.Seek(offset, origin)) { LOG_CRITICAL(Kernel_Fs, "sceKernelLseek: failed to seek"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } return file->f.Tell(); } @@ -257,10 +344,13 @@ s64 PS4_SYSV_ABI sceKernelRead(int d, void* buf, size_t nbytes) { auto* h = Common::Singleton::Instance(); auto* file = h->GetFile(d); if (file == nullptr) { - return SCE_KERNEL_ERROR_EBADF; + return ORBIS_KERNEL_ERROR_EBADF; } std::scoped_lock lk{file->m_mutex}; + if (file->type == Core::FileSys::FileType::Device) { + return file->device->read(buf, nbytes); + } return file->f.ReadRaw(buf, nbytes); } @@ -277,7 +367,7 @@ int PS4_SYSV_ABI posix_read(int d, void* buf, size_t nbytes) { int PS4_SYSV_ABI sceKernelMkdir(const char* path, u16 mode) { LOG_INFO(Kernel_Fs, "path = {} mode = {}", path, mode); if (path == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } auto* mnt = Common::Singleton::Instance(); @@ -285,21 +375,21 @@ int PS4_SYSV_ABI sceKernelMkdir(const char* path, u16 mode) { const auto dir_name = mnt->GetHostPath(path, &ro); if (std::filesystem::exists(dir_name)) { - return SCE_KERNEL_ERROR_EEXIST; + return ORBIS_KERNEL_ERROR_EEXIST; } if (ro) { - return SCE_KERNEL_ERROR_EROFS; + return ORBIS_KERNEL_ERROR_EROFS; } // CUSA02456: path = /aotl after sceSaveDataMount(mode = 1) std::error_code ec; if (dir_name.empty() || !std::filesystem::create_directory(dir_name, ec)) { - return SCE_KERNEL_ERROR_EIO; + return ORBIS_KERNEL_ERROR_EIO; } if (!std::filesystem::exists(dir_name)) { - return SCE_KERNEL_ERROR_ENOENT; + return ORBIS_KERNEL_ERROR_ENOENT; } return ORBIS_OK; } @@ -323,13 +413,13 @@ int PS4_SYSV_ABI sceKernelRmdir(const char* path) { if (dir_name.empty()) { LOG_ERROR(Kernel_Fs, "Failed to remove directory: {}, permission denied", fmt::UTF(dir_name.u8string())); - return SCE_KERNEL_ERROR_EACCES; + return ORBIS_KERNEL_ERROR_EACCES; } if (ro) { LOG_ERROR(Kernel_Fs, "Failed to remove directory: {}, directory is read only", fmt::UTF(dir_name.u8string())); - return SCE_KERNEL_ERROR_EROFS; + return ORBIS_KERNEL_ERROR_EROFS; } if (!std::filesystem::is_directory(dir_name)) { @@ -409,14 +499,20 @@ int PS4_SYSV_ABI posix_stat(const char* path, OrbisKernelStat* sb) { int PS4_SYSV_ABI sceKernelCheckReachability(const char* path) { auto* mnt = Common::Singleton::Instance(); - const auto path_name = mnt->GetHostPath(path); + std::string_view guest_path{path}; + for (const auto& prefix : available_device | std::views::keys) { + if (guest_path.starts_with(prefix)) { + return ORBIS_OK; + } + } + const auto path_name = mnt->GetHostPath(guest_path); if (!std::filesystem::exists(path_name)) { - return SCE_KERNEL_ERROR_ENOENT; + return ORBIS_KERNEL_ERROR_ENOENT; } return ORBIS_OK; } -s64 PS4_SYSV_ABI sceKernelPread(int d, void* buf, size_t nbytes, s64 offset) { +s64 PS4_SYSV_ABI sceKernelPreadv(int d, SceKernelIovec* iov, int iovcnt, s64 offset) { if (d < 3) { return ORBIS_KERNEL_ERROR_EPERM; } @@ -431,15 +527,28 @@ s64 PS4_SYSV_ABI sceKernelPread(int d, void* buf, size_t nbytes, s64 offset) { } std::scoped_lock lk{file->m_mutex}; + if (file->type == Core::FileSys::FileType::Device) { + return file->device->preadv(iov, iovcnt, offset); + } + const s64 pos = file->f.Tell(); SCOPE_EXIT { file->f.Seek(pos); }; if (!file->f.Seek(offset)) { - LOG_CRITICAL(Kernel_Fs, "sceKernelPread: failed to seek"); + LOG_CRITICAL(Kernel_Fs, "failed to seek"); return ORBIS_KERNEL_ERROR_EINVAL; } - return file->f.ReadRaw(buf, nbytes); + size_t total_read = 0; + for (int i = 0; i < iovcnt; i++) { + total_read += file->f.ReadRaw(iov[i].iov_base, iov[i].iov_len); + } + return total_read; +} + +s64 PS4_SYSV_ABI sceKernelPread(int d, void* buf, size_t nbytes, s64 offset) { + SceKernelIovec iovec{buf, nbytes}; + return sceKernelPreadv(d, &iovec, 1, offset); } int PS4_SYSV_ABI sceKernelFStat(int fd, OrbisKernelStat* sb) { @@ -457,18 +566,25 @@ int PS4_SYSV_ABI sceKernelFStat(int fd, OrbisKernelStat* sb) { } std::memset(sb, 0, sizeof(OrbisKernelStat)); - if (file->is_directory) { - sb->st_mode = 0000777u | 0040000u; - sb->st_size = 0; - sb->st_blksize = 512; - sb->st_blocks = 0; - // TODO incomplete - } else { + switch (file->type) { + case Core::FileSys::FileType::Device: + return file->device->fstat(sb); + case Core::FileSys::FileType::Regular: sb->st_mode = 0000777u | 0100000u; sb->st_size = file->f.GetSize(); sb->st_blksize = 512; sb->st_blocks = (sb->st_size + 511) / 512; // TODO incomplete + break; + case Core::FileSys::FileType::Directory: + sb->st_mode = 0000777u | 0040000u; + sb->st_size = 0; + sb->st_blksize = 512; + sb->st_blocks = 0; + // TODO incomplete + break; + default: + UNREACHABLE(); } return ORBIS_OK; } @@ -486,6 +602,13 @@ int PS4_SYSV_ABI posix_fstat(int fd, OrbisKernelStat* sb) { s32 PS4_SYSV_ABI sceKernelFsync(int fd) { auto* h = Common::Singleton::Instance(); auto* file = h->GetFile(fd); + if (file == nullptr) { + return ORBIS_KERNEL_ERROR_EBADF; + } + + if (file->type == Core::FileSys::FileType::Device) { + return file->device->fsync(); + } file->f.Flush(); return ORBIS_OK; } @@ -493,7 +616,7 @@ s32 PS4_SYSV_ABI sceKernelFsync(int fd) { s32 PS4_SYSV_ABI posix_fsync(int fd) { s32 result = sceKernelFsync(fd); if (result < 0) { - LOG_ERROR(Kernel_Pthread, "posix_fstat: error = {}", result); + LOG_ERROR(Kernel_Pthread, "posix_fsync: error = {}", result); ErrSceToPosix(result); return -1; } @@ -505,15 +628,19 @@ int PS4_SYSV_ABI sceKernelFtruncate(int fd, s64 length) { auto* file = h->GetFile(fd); if (file == nullptr) { - return SCE_KERNEL_ERROR_EBADF; + return ORBIS_KERNEL_ERROR_EBADF; + } + + if (file->type == Core::FileSys::FileType::Device) { + return file->device->ftruncate(length); } if (file->m_host_name.empty()) { - return SCE_KERNEL_ERROR_EACCES; + return ORBIS_KERNEL_ERROR_EACCES; } file->f.SetSize(length); - return SCE_OK; + return ORBIS_OK; } static int GetDents(int fd, char* buf, int nbytes, s64* basep) { @@ -529,22 +656,26 @@ static int GetDents(int fd, char* buf, int nbytes, s64* basep) { if (file == nullptr) { return ORBIS_KERNEL_ERROR_EBADF; } + if (file->type == Core::FileSys::FileType::Device) { + return file->device->getdents(buf, nbytes, basep); + } + if (file->dirents_index == file->dirents.size()) { return ORBIS_OK; } - if (!file->is_directory || nbytes < 512 || file->dirents_index > file->dirents.size()) { + if (file->type != Core::FileSys::FileType::Directory || nbytes < 512 || + file->dirents_index > file->dirents.size()) { return ORBIS_KERNEL_ERROR_EINVAL; } const auto& entry = file->dirents.at(file->dirents_index++); auto str = entry.name; - auto str_size = str.size() - 1; static int fileno = 1000; // random OrbisKernelDirent* sce_ent = (OrbisKernelDirent*)buf; sce_ent->d_fileno = fileno++; // TODO this should be unique but atm it changes maybe switch to a // hash or something? sce_ent->d_reclen = sizeof(OrbisKernelDirent); sce_ent->d_type = (entry.isFile ? 8 : 4); - sce_ent->d_namlen = str_size; + sce_ent->d_namlen = str.size(); strncpy(sce_ent->d_name, str.c_str(), ORBIS_MAX_PATH); sce_ent->d_name[ORBIS_MAX_PATH] = '\0'; @@ -578,6 +709,10 @@ s64 PS4_SYSV_ABI sceKernelPwrite(int d, void* buf, size_t nbytes, s64 offset) { } std::scoped_lock lk{file->m_mutex}; + + if (file->type == Core::FileSys::FileType::Device) { + return file->device->pwrite(buf, nbytes, offset); + } const s64 pos = file->f.Tell(); SCOPE_EXIT { file->f.Seek(pos); @@ -597,11 +732,11 @@ s32 PS4_SYSV_ABI sceKernelRename(const char* from, const char* to) { return ORBIS_KERNEL_ERROR_ENOENT; } if (ro) { - return SCE_KERNEL_ERROR_EROFS; + return ORBIS_KERNEL_ERROR_EROFS; } const auto dst_path = mnt->GetHostPath(to, &ro); if (ro) { - return SCE_KERNEL_ERROR_EROFS; + return ORBIS_KERNEL_ERROR_EROFS; } const bool src_is_dir = std::filesystem::is_directory(src_path); const bool dst_is_dir = std::filesystem::is_directory(dst_path); @@ -618,7 +753,7 @@ s32 PS4_SYSV_ABI sceKernelRename(const char* from, const char* to) { return ORBIS_OK; } -void fileSystemSymbolsRegister(Core::Loader::SymbolsResolver* sym) { +void RegisterFileSystem(Core::Loader::SymbolsResolver* sym) { std::srand(std::time(nullptr)); LIB_FUNCTION("1G3lF1Gg1k8", "libkernel", 1, "libkernel", 1, 1, sceKernelOpen); LIB_FUNCTION("wuCroIGjt2g", "libScePosix", 1, "libkernel", 1, 1, posix_open); @@ -629,6 +764,7 @@ void fileSystemSymbolsRegister(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("4wSze92BhLI", "libkernel", 1, "libkernel", 1, 1, sceKernelWrite); LIB_FUNCTION("+WRlkKjZvag", "libkernel", 1, "libkernel", 1, 1, _readv); + LIB_FUNCTION("YSHRBRLn2pI", "libkernel", 1, "libkernel", 1, 1, _writev); LIB_FUNCTION("Oy6IpwgtYOk", "libkernel", 1, "libkernel", 1, 1, posix_lseek); LIB_FUNCTION("Oy6IpwgtYOk", "libScePosix", 1, "libkernel", 1, 1, posix_lseek); LIB_FUNCTION("oib76F-12fk", "libkernel", 1, "libkernel", 1, 1, sceKernelLseek); @@ -650,6 +786,7 @@ void fileSystemSymbolsRegister(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("E6ao34wPw+U", "libScePosix", 1, "libkernel", 1, 1, posix_stat); LIB_FUNCTION("E6ao34wPw+U", "libkernel", 1, "libkernel", 1, 1, posix_stat); LIB_FUNCTION("+r3rMFwItV4", "libkernel", 1, "libkernel", 1, 1, sceKernelPread); + LIB_FUNCTION("yTj62I7kw4s", "libkernel", 1, "libkernel", 1, 1, sceKernelPreadv); LIB_FUNCTION("uWyW3v98sU4", "libkernel", 1, "libkernel", 1, 1, sceKernelCheckReachability); LIB_FUNCTION("fTx66l5iWIA", "libkernel", 1, "libkernel", 1, 1, sceKernelFsync); LIB_FUNCTION("juWbTNM+8hw", "libkernel", 1, "libkernel", 1, 1, posix_fsync); diff --git a/src/core/libraries/kernel/file_system.h b/src/core/libraries/kernel/file_system.h index 1174dd86d..dcbb3957d 100644 --- a/src/core/libraries/kernel/file_system.h +++ b/src/core/libraries/kernel/file_system.h @@ -4,7 +4,7 @@ #pragma once #include "common/types.h" -#include "core/libraries/kernel/time_management.h" +#include "core/libraries/kernel/time.h" namespace Core::Loader { class SymbolsResolver; @@ -37,8 +37,8 @@ struct OrbisKernelStat { u32 st_gen; s32 st_lspare; OrbisKernelTimespec st_birthtim; - unsigned int : (8 / 2) * (16 - static_cast(sizeof(OrbisKernelTimespec))); - unsigned int : (8 / 2) * (16 - static_cast(sizeof(OrbisKernelTimespec))); + u32 : (8 / 2) * (16 - static_cast(sizeof(OrbisKernelTimespec))); + u32 : (8 / 2) * (16 - static_cast(sizeof(OrbisKernelTimespec))); }; struct OrbisKernelDirent { @@ -65,11 +65,6 @@ constexpr int ORBIS_KERNEL_O_DSYNC = 0x1000; constexpr int ORBIS_KERNEL_O_DIRECT = 0x00010000; constexpr int ORBIS_KERNEL_O_DIRECTORY = 0x00020000; -int PS4_SYSV_ABI sceKernelOpen(const char* path, int flags, /* SceKernelMode*/ u16 mode); - -int PS4_SYSV_ABI posix_open(const char* path, int flags, /* SceKernelMode*/ u16 mode); -s64 PS4_SYSV_ABI lseek(int d, s64 offset, int whence); - -void fileSystemSymbolsRegister(Core::Loader::SymbolsResolver* sym); +void RegisterFileSystem(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/kernel.cpp b/src/core/libraries/kernel/kernel.cpp new file mode 100644 index 000000000..bda446257 --- /dev/null +++ b/src/core/libraries/kernel/kernel.cpp @@ -0,0 +1,261 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include + +#include "common/assert.h" +#include "common/debug.h" +#include "common/logging/log.h" +#include "common/polyfill_thread.h" +#include "common/thread.h" +#include "common/va_ctx.h" +#include "core/file_sys/fs.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/kernel/equeue.h" +#include "core/libraries/kernel/file_system.h" +#include "core/libraries/kernel/kernel.h" +#include "core/libraries/kernel/memory.h" +#include "core/libraries/kernel/orbis_error.h" +#include "core/libraries/kernel/posix_error.h" +#include "core/libraries/kernel/process.h" +#include "core/libraries/kernel/threads.h" +#include "core/libraries/kernel/threads/exception.h" +#include "core/libraries/kernel/time.h" +#include "core/libraries/libs.h" + +#ifdef _WIN64 +#include +#endif +#include + +namespace Libraries::Kernel { + +static u64 g_stack_chk_guard = 0xDEADBEEF54321ABC; // dummy return + +boost::asio::io_context io_context; +static std::mutex m_asio_req; +static std::condition_variable_any cv_asio_req; +static std::atomic asio_requests; +static std::jthread service_thread; + +void KernelSignalRequest() { + std::unique_lock lock{m_asio_req}; + ++asio_requests; + cv_asio_req.notify_one(); +} + +static void KernelServiceThread(std::stop_token stoken) { + Common::SetCurrentThreadName("shadPS4:KernelServiceThread"); + + while (!stoken.stop_requested()) { + HLE_TRACE; + { + std::unique_lock lock{m_asio_req}; + Common::CondvarWait(cv_asio_req, lock, stoken, [] { return asio_requests != 0; }); + } + if (stoken.stop_requested()) { + break; + } + + io_context.run(); + io_context.reset(); + + asio_requests = 0; + } +} + +static PS4_SYSV_ABI void stack_chk_fail() { + UNREACHABLE(); +} + +static thread_local int g_posix_errno = 0; + +int* PS4_SYSV_ABI __Error() { + return &g_posix_errno; +} + +void ErrSceToPosix(int error) { + g_posix_errno = error - ORBIS_KERNEL_ERROR_UNKNOWN; +} + +int ErrnoToSceKernelError(int error) { + return error + ORBIS_KERNEL_ERROR_UNKNOWN; +} + +void SetPosixErrno(int e) { + // Some error numbers are different between supported OSes or the PS4 + switch (e) { + case EPERM: + g_posix_errno = POSIX_EPERM; + break; + case EAGAIN: + g_posix_errno = POSIX_EAGAIN; + break; + case ENOMEM: + g_posix_errno = POSIX_ENOMEM; + break; + case EINVAL: + g_posix_errno = POSIX_EINVAL; + break; + case ENOSPC: + g_posix_errno = POSIX_ENOSPC; + break; + case ERANGE: + g_posix_errno = POSIX_ERANGE; + break; + case EDEADLK: + g_posix_errno = POSIX_EDEADLK; + break; + case ETIMEDOUT: + g_posix_errno = POSIX_ETIMEDOUT; + break; + default: + g_posix_errno = e; + } +} + +static uint64_t g_mspace_atomic_id_mask = 0; +static uint64_t g_mstate_table[64] = {0}; + +struct HeapInfoInfo { + uint64_t size = sizeof(HeapInfoInfo); + uint32_t flag; + uint32_t getSegmentInfo; + uint64_t* mspace_atomic_id_mask; + uint64_t* mstate_table; +}; + +void PS4_SYSV_ABI sceLibcHeapGetTraceInfo(HeapInfoInfo* info) { + info->mspace_atomic_id_mask = &g_mspace_atomic_id_mask; + info->mstate_table = g_mstate_table; + info->getSegmentInfo = 0; +} + +s64 PS4_SYSV_ABI ps4__write(int d, const char* buf, std::size_t nbytes) { + auto* h = Common::Singleton::Instance(); + auto* file = h->GetFile(d); + if (file == nullptr) { + return ORBIS_KERNEL_ERROR_EBADF; + } + std::scoped_lock lk{file->m_mutex}; + if (file->type == Core::FileSys::FileType::Device) { + return file->device->write(buf, nbytes); + } + return file->f.WriteRaw(buf, nbytes); +} + +s64 PS4_SYSV_ABI ps4__read(int d, void* buf, u64 nbytes) { + if (d == 0) { + return static_cast( + strlen(std::fgets(static_cast(buf), static_cast(nbytes), stdin))); + } + auto* h = Common::Singleton::Instance(); + auto* file = h->GetFile(d); + if (file == nullptr) { + return ORBIS_KERNEL_ERROR_EBADF; + } + std::scoped_lock lk{file->m_mutex}; + if (file->type == Core::FileSys::FileType::Device) { + return file->device->read(buf, nbytes); + } + return file->f.ReadRaw(buf, nbytes); +} + +struct OrbisKernelUuid { + u32 timeLow; + u16 timeMid; + u16 timeHiAndVersion; + u8 clockSeqHiAndReserved; + u8 clockSeqLow; + u8 node[6]; +}; + +int PS4_SYSV_ABI sceKernelUuidCreate(OrbisKernelUuid* orbisUuid) { +#ifdef _WIN64 + UUID uuid; + UuidCreate(&uuid); + orbisUuid->timeLow = uuid.Data1; + orbisUuid->timeMid = uuid.Data2; + orbisUuid->timeHiAndVersion = uuid.Data3; + orbisUuid->clockSeqHiAndReserved = uuid.Data4[0]; + orbisUuid->clockSeqLow = uuid.Data4[1]; + for (int i = 0; i < 6; i++) { + orbisUuid->node[i] = uuid.Data4[2 + i]; + } +#else + LOG_ERROR(Kernel, "sceKernelUuidCreate: Add linux"); +#endif + return 0; +} + +int PS4_SYSV_ABI kernel_ioctl(int fd, u64 cmd, VA_ARGS) { + auto* h = Common::Singleton::Instance(); + auto* file = h->GetFile(fd); + if (file == nullptr) { + LOG_INFO(Lib_Kernel, "ioctl: fd = {:X} cmd = {:X} file == nullptr", fd, cmd); + g_posix_errno = POSIX_EBADF; + return -1; + } + if (file->type != Core::FileSys::FileType::Device) { + LOG_WARNING(Lib_Kernel, "ioctl: fd = {:X} cmd = {:X} file->type != Device", fd, cmd); + g_posix_errno = ENOTTY; + return -1; + } + VA_CTX(ctx); + int result = file->device->ioctl(cmd, &ctx); + LOG_TRACE(Lib_Kernel, "ioctl: fd = {:X} cmd = {:X} result = {}", fd, cmd, result); + if (result < 0) { + ErrSceToPosix(result); + return -1; + } + return result; +} + +const char* PS4_SYSV_ABI sceKernelGetFsSandboxRandomWord() { + const char* path = "sys"; + return path; +} + +int PS4_SYSV_ABI posix_connect() { + return -1; +} + +int PS4_SYSV_ABI _sigprocmask() { + return ORBIS_OK; +} + +int PS4_SYSV_ABI posix_getpagesize() { + return 16_KB; +} + +void RegisterKernel(Core::Loader::SymbolsResolver* sym) { + service_thread = std::jthread{KernelServiceThread}; + + Libraries::Kernel::RegisterFileSystem(sym); + Libraries::Kernel::RegisterTime(sym); + Libraries::Kernel::RegisterThreads(sym); + Libraries::Kernel::RegisterKernelEventFlag(sym); + Libraries::Kernel::RegisterMemory(sym); + Libraries::Kernel::RegisterEventQueue(sym); + Libraries::Kernel::RegisterProcess(sym); + Libraries::Kernel::RegisterException(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); + LIB_FUNCTION("9BcDykPmo1I", "libkernel", 1, "libkernel", 1, 1, __Error); + LIB_FUNCTION("DRuBt2pvICk", "libkernel", 1, "libkernel", 1, 1, ps4__read); + LIB_FUNCTION("k+AXqu2-eBc", "libkernel", 1, "libkernel", 1, 1, posix_getpagesize); + LIB_FUNCTION("k+AXqu2-eBc", "libScePosix", 1, "libkernel", 1, 1, posix_getpagesize); + LIB_FUNCTION("NWtTN10cJzE", "libSceLibcInternalExt", 1, "libSceLibcInternal", 1, 1, + sceLibcHeapGetTraceInfo); + LIB_FUNCTION("FxVZqBAA7ks", "libkernel", 1, "libkernel", 1, 1, ps4__write); + LIB_FUNCTION("FN4gaPmuFV8", "libScePosix", 1, "libkernel", 1, 1, ps4__write); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/kernel.h b/src/core/libraries/kernel/kernel.h new file mode 100644 index 000000000..8e7f475ad --- /dev/null +++ b/src/core/libraries/kernel/kernel.h @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include "common/types.h" +#include "core/libraries/kernel/orbis_error.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::Kernel { + +void ErrSceToPosix(int result); +int ErrnoToSceKernelError(int e); +void SetPosixErrno(int e); + +template +struct StringLiteral { + constexpr StringLiteral(const char (&str)[N]) { + std::copy_n(str, N, value); + } + + char value[N]; +}; + +template +struct WrapperImpl; + +template +struct WrapperImpl { + static constexpr StringLiteral Name{name}; + static R PS4_SYSV_ABI wrap(Args... args) { + u32 ret = f(args...); + if (ret != 0) { + // LOG_ERROR(Lib_Kernel, "Function {} returned {}", std::string_view{name.value}, ret); + ret += ORBIS_KERNEL_ERROR_UNKNOWN; + } + return ret; + } +}; + +template +constexpr auto OrbisWrapper = WrapperImpl::wrap; + +#define ORBIS(func) WrapperImpl<#func, decltype(&func), func>::wrap + +int* PS4_SYSV_ABI __Error(); + +void RegisterKernel(Core::Loader::SymbolsResolver* sym); + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/libkernel.cpp b/src/core/libraries/kernel/libkernel.cpp deleted file mode 100644 index b3eb81ec8..000000000 --- a/src/core/libraries/kernel/libkernel.cpp +++ /dev/null @@ -1,509 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include - -#include - -#include "common/assert.h" -#include "common/debug.h" -#include "common/elf_info.h" -#include "common/logging/log.h" -#include "common/polyfill_thread.h" -#include "common/singleton.h" -#include "common/thread.h" -#include "core/file_format/psf.h" -#include "core/file_sys/fs.h" -#include "core/libraries/error_codes.h" -#include "core/libraries/kernel/cpu_management.h" -#include "core/libraries/kernel/event_flag/event_flag.h" -#include "core/libraries/kernel/event_queues.h" -#include "core/libraries/kernel/file_system.h" -#include "core/libraries/kernel/libkernel.h" -#include "core/libraries/kernel/memory_management.h" -#include "core/libraries/kernel/thread_management.h" -#include "core/libraries/kernel/time_management.h" -#include "core/libraries/libs.h" -#include "core/linker.h" -#include "core/memory.h" - -#ifdef _WIN64 -#include -#include -#include -#else -#include -#ifdef __APPLE__ -#include -#endif -#endif - -namespace Libraries::Kernel { - -static u64 g_stack_chk_guard = 0xDEADBEEF54321ABC; // dummy return - -boost::asio::io_context io_context; -std::mutex m_asio_req; -std::condition_variable_any cv_asio_req; -std::atomic asio_requests; -std::jthread service_thread; - -void KernelSignalRequest() { - std::unique_lock lock{m_asio_req}; - ++asio_requests; - cv_asio_req.notify_one(); -} - -static void KernelServiceThread(std::stop_token stoken) { - Common::SetCurrentThreadName("shadPS4:Kernel_ServiceThread"); - - while (!stoken.stop_requested()) { - HLE_TRACE; - { - std::unique_lock lock{m_asio_req}; - Common::CondvarWait(cv_asio_req, lock, stoken, [] { return asio_requests != 0; }); - } - if (stoken.stop_requested()) { - break; - } - - io_context.run(); - io_context.reset(); - - asio_requests = 0; - } -} - -static void* PS4_SYSV_ABI sceKernelGetProcParam() { - auto* linker = Common::Singleton::Instance(); - return reinterpret_cast(linker->GetProcParam()); -} - -static PS4_SYSV_ABI void stack_chk_fail() { - UNREACHABLE(); -} - -int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len) { - LOG_INFO(Kernel_Vmm, "addr = {}, len = {:#x}", fmt::ptr(addr), len); - if (len == 0) { - return ORBIS_OK; - } - auto* memory = Core::Memory::Instance(); - memory->UnmapMemory(std::bit_cast(addr), len); - return SCE_OK; -} - -struct iovec { - void* iov_base; /* Base address. */ - size_t iov_len; /* Length. */ -}; - -size_t PS4_SYSV_ABI _writev(int fd, const struct iovec* iov, int iovcn) { - // weird it gives fd ==0 and writes to stdout , i am not sure if it that is valid (found in - // openorbis) - size_t total_written = 0; - for (int i = 0; i < iovcn; i++) { - total_written += ::fwrite(iov[i].iov_base, 1, iov[i].iov_len, stdout); - } - return total_written; -} - -static thread_local int g_posix_errno = 0; -int* PS4_SYSV_ABI __Error() { - return &g_posix_errno; -} - -void ErrSceToPosix(int result) { - const int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - g_posix_errno = rt; -} - -int ErrnoToSceKernelError(int e) { - const auto res = SCE_KERNEL_ERROR_UNKNOWN + e; - return res > SCE_KERNEL_ERROR_ESTOP ? SCE_KERNEL_ERROR_UNKNOWN : res; -} - -void SetPosixErrno(int e) { - // Some error numbers are different between supported OSes or the PS4 - switch (e) { - case EPERM: - g_posix_errno = POSIX_EPERM; - break; - case EAGAIN: - g_posix_errno = POSIX_EAGAIN; - break; - case ENOMEM: - g_posix_errno = POSIX_ENOMEM; - break; - case EINVAL: - g_posix_errno = POSIX_EINVAL; - break; - case ENOSPC: - g_posix_errno = POSIX_ENOSPC; - break; - case ERANGE: - g_posix_errno = POSIX_ERANGE; - break; - case EDEADLK: - g_posix_errno = POSIX_EDEADLK; - break; - case ETIMEDOUT: - g_posix_errno = POSIX_ETIMEDOUT; - break; - default: - g_posix_errno = e; - } -} - -int PS4_SYSV_ABI sceKernelMmap(void* addr, u64 len, int prot, int flags, int fd, size_t offset, - void** res) { - LOG_INFO(Kernel_Vmm, "called addr = {}, len = {}, prot = {}, flags = {}, fd = {}, offset = {}", - fmt::ptr(addr), len, prot, flags, fd, offset); - auto* h = Common::Singleton::Instance(); - auto* memory = Core::Memory::Instance(); - const auto mem_prot = static_cast(prot); - const auto mem_flags = static_cast(flags); - if (fd == -1) { - return memory->MapMemory(res, std::bit_cast(addr), len, mem_prot, mem_flags, - Core::VMAType::Flexible); - } else { - const uintptr_t handle = h->GetFile(fd)->f.GetFileMapping(); - return memory->MapFile(res, std::bit_cast(addr), len, mem_prot, mem_flags, handle, - offset); - } -} - -void* PS4_SYSV_ABI posix_mmap(void* addr, u64 len, int prot, int flags, int fd, u64 offset) { - void* ptr; - LOG_INFO(Kernel_Vmm, "posix mmap redirect to sceKernelMmap"); - // posix call the difference is that there is a different behaviour when it doesn't return 0 or - // SCE_OK - const VAddr ret_addr = (VAddr)__builtin_return_address(0); - int result = sceKernelMmap(addr, len, prot, flags, fd, offset, &ptr); - ASSERT(result == 0); - return ptr; -} - -s32 PS4_SYSV_ABI sceKernelConfiguredFlexibleMemorySize(u64* sizeOut) { - if (sizeOut == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - - auto* memory = Core::Memory::Instance(); - *sizeOut = memory->GetTotalFlexibleSize(); - return ORBIS_OK; -} - -static uint64_t g_mspace_atomic_id_mask = 0; -static uint64_t g_mstate_table[64] = {0}; - -struct HeapInfoInfo { - uint64_t size = sizeof(HeapInfoInfo); - uint32_t flag; - uint32_t getSegmentInfo; - uint64_t* mspace_atomic_id_mask; - uint64_t* mstate_table; -}; - -void PS4_SYSV_ABI sceLibcHeapGetTraceInfo(HeapInfoInfo* info) { - info->mspace_atomic_id_mask = &g_mspace_atomic_id_mask; - info->mstate_table = g_mstate_table; - info->getSegmentInfo = 0; -} - -s64 PS4_SYSV_ABI ps4__write(int d, const void* buf, std::size_t nbytes) { - if (d <= 2) { // stdin,stdout,stderr - char* str = strdup((const char*)buf); - if (str[nbytes - 1] == '\n') - str[nbytes - 1] = 0; - LOG_INFO(Tty, "{}", str); - free(str); - return nbytes; - } - LOG_ERROR(Kernel, "(STUBBED) called d = {} nbytes = {} ", d, nbytes); - UNREACHABLE(); // normal write , is it a posix call?? - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time, - struct OrbisTimesec* st, unsigned long* dst_sec) { - LOG_TRACE(Kernel, "Called"); -#ifdef __APPLE__ - // std::chrono::current_zone() not available yet. - const auto* time_zone = date::current_zone(); -#else - const auto* time_zone = std::chrono::current_zone(); -#endif - auto info = time_zone->get_info(std::chrono::system_clock::now()); - - *local_time = info.offset.count() + info.save.count() * 60 + time; - - if (st != nullptr) { - st->t = time; - st->west_sec = info.offset.count() * 60; - st->dst_sec = info.save.count() * 60; - } - - if (dst_sec != nullptr) { - *dst_sec = info.save.count() * 60; - } - - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver) { - int version = Common::ElfInfo::Instance().RawFirmwareVer(); - LOG_DEBUG(Kernel, "returned system version = {:#x}", version); - *ver = version; - return (version > 0) ? ORBIS_OK : ORBIS_KERNEL_ERROR_EINVAL; -} - -s64 PS4_SYSV_ABI ps4__read(int d, void* buf, u64 nbytes) { - ASSERT_MSG(d == 0, "d is not 0!"); - - return static_cast( - strlen(std::fgets(static_cast(buf), static_cast(nbytes), stdin))); -} - -s32 PS4_SYSV_ABI sceKernelLoadStartModule(const char* moduleFileName, size_t args, const void* argp, - u32 flags, const void* pOpt, int* pRes) { - LOG_INFO(Lib_Kernel, "called filename = {}, args = {}", moduleFileName, args); - - if (flags != 0) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - - auto* mnt = Common::Singleton::Instance(); - const auto path = mnt->GetHostPath(moduleFileName); - - // Load PRX module and relocate any modules that import it. - auto* linker = Common::Singleton::Instance(); - u32 handle = linker->LoadModule(path, true); - if (handle == -1) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - auto* module = linker->GetModule(handle); - linker->RelocateAnyImports(module); - - // If the new module has a TLS image, trigger its load when TlsGetAddr is called. - if (module->tls.image_size != 0) { - linker->AdvanceGenerationCounter(); - } - - // Retrieve and verify proc param according to libkernel. - u64* param = module->GetProcParam(); - ASSERT_MSG(!param || param[0] >= 0x18, "Invalid module param size: {}", param[0]); - module->Start(args, argp, param); - - return handle; -} - -s32 PS4_SYSV_ABI sceKernelDlsym(s32 handle, const char* symbol, void** addrp) { - auto* linker = Common::Singleton::Instance(); - auto* module = linker->GetModule(handle); - *addrp = module->FindByName(symbol); - if (*addrp == nullptr) { - return ORBIS_KERNEL_ERROR_ESRCH; - } - return ORBIS_OK; -} - -static constexpr size_t ORBIS_DBG_MAX_NAME_LENGTH = 256; - -struct OrbisModuleInfoForUnwind { - u64 st_size; - std::array name; - VAddr eh_frame_hdr_addr; - VAddr eh_frame_addr; - u64 eh_frame_size; - VAddr seg0_addr; - u64 seg0_size; -}; - -s32 PS4_SYSV_ABI sceKernelGetModuleInfoForUnwind(VAddr addr, int flags, - OrbisModuleInfoForUnwind* info) { - if (flags >= 3) { - std::memset(info, 0, sizeof(OrbisModuleInfoForUnwind)); - return SCE_KERNEL_ERROR_EINVAL; - } - if (!info) { - return ORBIS_KERNEL_ERROR_EFAULT; - } - if (info->st_size <= sizeof(OrbisModuleInfoForUnwind)) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - - // Find module that contains specified address. - LOG_INFO(Lib_Kernel, "called addr = {:#x}, flags = {:#x}", addr, flags); - auto* linker = Common::Singleton::Instance(); - auto* module = linker->FindByAddress(addr); - const auto mod_info = module->GetModuleInfoEx(); - - // Fill in module info. - info->name = mod_info.name; - info->eh_frame_hdr_addr = mod_info.eh_frame_hdr_addr; - info->eh_frame_addr = mod_info.eh_frame_addr; - info->eh_frame_size = mod_info.eh_frame_size; - info->seg0_addr = mod_info.segments[0].address; - info->seg0_size = mod_info.segments[0].size; - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceKernelGetModuleInfoFromAddr(VAddr addr, int flags, - Core::OrbisKernelModuleInfoEx* info) { - LOG_INFO(Lib_Kernel, "called addr = {:#x}, flags = {:#x}", addr, flags); - auto* linker = Common::Singleton::Instance(); - auto* module = linker->FindByAddress(addr); - *info = module->GetModuleInfoEx(); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceKernelDebugRaiseException() { - UNREACHABLE(); - return 0; -} - -int PS4_SYSV_ABI sceKernelGetCpumode() { - return 0; -} - -void PS4_SYSV_ABI sched_yield() { - return std::this_thread::yield(); -} - -int PS4_SYSV_ABI sceKernelUuidCreate(OrbisKernelUuid* orbisUuid) { -#ifdef _WIN64 - UUID uuid; - UuidCreate(&uuid); - orbisUuid->timeLow = uuid.Data1; - orbisUuid->timeMid = uuid.Data2; - orbisUuid->timeHiAndVersion = uuid.Data3; - orbisUuid->clockSeqHiAndReserved = uuid.Data4[0]; - orbisUuid->clockSeqLow = uuid.Data4[1]; - for (int i = 0; i < 6; i++) { - orbisUuid->node[i] = uuid.Data4[2 + i]; - } -#else - LOG_ERROR(Kernel, "sceKernelUuidCreate: Add linux"); -#endif - return 0; -} - -const char* PS4_SYSV_ABI sceKernelGetFsSandboxRandomWord() { - const char* path = "sys"; - return path; -} - -int PS4_SYSV_ABI posix_connect() { - return -1; -} - -int PS4_SYSV_ABI _sigprocmask() { - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_getpagesize() { - return 4096; -} - -void LibKernel_Register(Core::Loader::SymbolsResolver* sym) { - service_thread = std::jthread{KernelServiceThread}; - - // obj - LIB_OBJ("f7uOxY9mM1U", "libkernel", 1, "libkernel", 1, 1, &g_stack_chk_guard); - - // misc - 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); - - // memory - LIB_FUNCTION("OMDRKKAZ8I4", "libkernel", 1, "libkernel", 1, 1, sceKernelDebugRaiseException); - LIB_FUNCTION("rTXw65xmLIA", "libkernel", 1, "libkernel", 1, 1, sceKernelAllocateDirectMemory); - LIB_FUNCTION("B+vc2AO2Zrc", "libkernel", 1, "libkernel", 1, 1, - sceKernelAllocateMainDirectMemory); - LIB_FUNCTION("C0f7TJcbfac", "libkernel", 1, "libkernel", 1, 1, - sceKernelAvailableDirectMemorySize); - LIB_FUNCTION("hwVSPCmp5tM", "libkernel", 1, "libkernel", 1, 1, - sceKernelCheckedReleaseDirectMemory); - LIB_FUNCTION("rVjRvHJ0X6c", "libkernel", 1, "libkernel", 1, 1, sceKernelVirtualQuery); - LIB_FUNCTION("7oxv3PPCumo", "libkernel", 1, "libkernel", 1, 1, sceKernelReserveVirtualRange); - LIB_FUNCTION("BC+OG5m9+bw", "libkernel", 1, "libkernel", 1, 1, sceKernelGetDirectMemoryType); - LIB_FUNCTION("pO96TwzOm5E", "libkernel", 1, "libkernel", 1, 1, sceKernelGetDirectMemorySize); - LIB_FUNCTION("NcaWUxfMNIQ", "libkernel", 1, "libkernel", 1, 1, sceKernelMapNamedDirectMemory); - LIB_FUNCTION("L-Q3LEjIbgA", "libkernel", 1, "libkernel", 1, 1, sceKernelMapDirectMemory); - LIB_FUNCTION("WFcfL2lzido", "libkernel", 1, "libkernel", 1, 1, sceKernelQueryMemoryProtection); - LIB_FUNCTION("BHouLQzh0X0", "libkernel", 1, "libkernel", 1, 1, sceKernelDirectMemoryQuery); - LIB_FUNCTION("MBuItvba6z8", "libkernel", 1, "libkernel", 1, 1, sceKernelReleaseDirectMemory); - LIB_FUNCTION("PGhQHd-dzv8", "libkernel", 1, "libkernel", 1, 1, sceKernelMmap); - LIB_FUNCTION("cQke9UuBQOk", "libkernel", 1, "libkernel", 1, 1, sceKernelMunmap); - LIB_FUNCTION("mL8NDH86iQI", "libkernel", 1, "libkernel", 1, 1, sceKernelMapNamedFlexibleMemory); - LIB_FUNCTION("aNz11fnnzi4", "libkernel", 1, "libkernel", 1, 1, - sceKernelAvailableFlexibleMemorySize); - LIB_FUNCTION("IWIBBdTHit4", "libkernel", 1, "libkernel", 1, 1, sceKernelMapFlexibleMemory); - LIB_FUNCTION("p5EcQeEeJAE", "libkernel", 1, "libkernel", 1, 1, - _sceKernelRtldSetApplicationHeapAPI); - LIB_FUNCTION("wzvqT4UqKX8", "libkernel", 1, "libkernel", 1, 1, sceKernelLoadStartModule); - 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("VOx8NGmHXTs", "libkernel", 1, "libkernel", 1, 1, sceKernelGetCpumode); - LIB_FUNCTION("Xjoosiw+XPI", "libkernel", 1, "libkernel", 1, 1, sceKernelUuidCreate); - - LIB_FUNCTION("2SKEx6bSq-4", "libkernel", 1, "libkernel", 1, 1, sceKernelBatchMap); - LIB_FUNCTION("kBJzF8x4SyE", "libkernel", 1, "libkernel", 1, 1, sceKernelBatchMap2); - LIB_FUNCTION("DGMG3JshrZU", "libkernel", 1, "libkernel", 1, 1, sceKernelSetVirtualRangeName); - LIB_FUNCTION("n1-v6FgU7MQ", "libkernel", 1, "libkernel", 1, 1, - sceKernelConfiguredFlexibleMemorySize); - - // Memory pool - LIB_FUNCTION("qCSfqDILlns", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolExpand); - LIB_FUNCTION("pU-QydtGcGY", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolReserve); - LIB_FUNCTION("Vzl66WmfLvk", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolCommit); - LIB_FUNCTION("LXo1tpFqJGs", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolDecommit); - - // equeue - LIB_FUNCTION("D0OdFMjp46I", "libkernel", 1, "libkernel", 1, 1, sceKernelCreateEqueue); - LIB_FUNCTION("jpFjmgAC5AE", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteEqueue); - LIB_FUNCTION("fzyMKs9kim0", "libkernel", 1, "libkernel", 1, 1, sceKernelWaitEqueue); - LIB_FUNCTION("vz+pg2zdopI", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventUserData); - LIB_FUNCTION("4R6-OvI2cEA", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEvent); - LIB_FUNCTION("WDszmSbWuDk", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEventEdge); - LIB_FUNCTION("R74tt43xP6k", "libkernel", 1, "libkernel", 1, 1, sceKernelAddHRTimerEvent); - LIB_FUNCTION("F6e0kwo4cnk", "libkernel", 1, "libkernel", 1, 1, sceKernelTriggerUserEvent); - LIB_FUNCTION("LJDwdSNTnDg", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteUserEvent); - LIB_FUNCTION("mJ7aghmgvfc", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventId); - LIB_FUNCTION("9bfdLIyuwCY", "libkernel", 1, "libkernel", 1, 1, sceKernelMTypeProtect); - LIB_FUNCTION("vSMAm3cxYTY", "libkernel", 1, "libkernel", 1, 1, sceKernelMProtect); - LIB_FUNCTION("23CPPI1tyBY", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventFilter); - - // misc - LIB_FUNCTION("WslcK1FQcGI", "libkernel", 1, "libkernel", 1, 1, sceKernelIsNeoMode); - LIB_FUNCTION("Ou3iL1abvng", "libkernel", 1, "libkernel", 1, 1, stack_chk_fail); - LIB_FUNCTION("9BcDykPmo1I", "libkernel", 1, "libkernel", 1, 1, __Error); - LIB_FUNCTION("BPE9s9vQQXo", "libkernel", 1, "libkernel", 1, 1, posix_mmap); - LIB_FUNCTION("BPE9s9vQQXo", "libScePosix", 1, "libkernel", 1, 1, posix_mmap); - LIB_FUNCTION("YSHRBRLn2pI", "libkernel", 1, "libkernel", 1, 1, _writev); - LIB_FUNCTION("959qrazPIrg", "libkernel", 1, "libkernel", 1, 1, sceKernelGetProcParam); - LIB_FUNCTION("-o5uEDpN+oY", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertUtcToLocaltime); - LIB_FUNCTION("WB66evu8bsU", "libkernel", 1, "libkernel", 1, 1, sceKernelGetCompiledSdkVersion); - LIB_FUNCTION("DRuBt2pvICk", "libkernel", 1, "libkernel", 1, 1, ps4__read); - LIB_FUNCTION("k+AXqu2-eBc", "libkernel", 1, "libkernel", 1, 1, posix_getpagesize); - LIB_FUNCTION("k+AXqu2-eBc", "libScePosix", 1, "libkernel", 1, 1, posix_getpagesize); - - Libraries::Kernel::fileSystemSymbolsRegister(sym); - Libraries::Kernel::timeSymbolsRegister(sym); - Libraries::Kernel::pthreadSymbolsRegister(sym); - Libraries::Kernel::RegisterKernelEventFlag(sym); - - // temp - LIB_FUNCTION("NWtTN10cJzE", "libSceLibcInternalExt", 1, "libSceLibcInternal", 1, 1, - sceLibcHeapGetTraceInfo); - LIB_FUNCTION("FxVZqBAA7ks", "libkernel", 1, "libkernel", 1, 1, ps4__write); - LIB_FUNCTION("6XG4B33N09g", "libScePosix", 1, "libkernel", 1, 1, sched_yield); -} - -} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/libkernel.h b/src/core/libraries/kernel/libkernel.h deleted file mode 100644 index 73705cdc2..000000000 --- a/src/core/libraries/kernel/libkernel.h +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include - -#include "common/types.h" - -namespace Core::Loader { -class SymbolsResolver; -} - -namespace Libraries::Kernel { - -void ErrSceToPosix(int result); -int ErrnoToSceKernelError(int e); -void SetPosixErrno(int e); - -struct OrbisTimesec { - time_t t; - u32 west_sec; - u32 dst_sec; -}; - -typedef struct { - uint32_t timeLow; - uint16_t timeMid; - uint16_t timeHiAndVersion; - uint8_t clockSeqHiAndReserved; - uint8_t clockSeqLow; - uint8_t node[6]; -} OrbisKernelUuid; - -int* PS4_SYSV_ABI __Error(); -int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time, - struct OrbisTimesec* st, unsigned long* dst_sec); -int PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver); - -void LibKernel_Register(Core::Loader::SymbolsResolver* sym); - -} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/memory_management.cpp b/src/core/libraries/kernel/memory.cpp similarity index 69% rename from src/core/libraries/kernel/memory_management.cpp rename to src/core/libraries/kernel/memory.cpp index 5331f47f2..7d326cbbf 100644 --- a/src/core/libraries/kernel/memory_management.cpp +++ b/src/core/libraries/kernel/memory.cpp @@ -6,10 +6,13 @@ #include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" +#include "common/scope_exit.h" #include "common/singleton.h" -#include "core/address_space.h" -#include "core/libraries/error_codes.h" -#include "core/libraries/kernel/memory_management.h" +#include "core/file_sys/fs.h" +#include "core/libraries/kernel/kernel.h" +#include "core/libraries/kernel/memory.h" +#include "core/libraries/kernel/orbis_error.h" +#include "core/libraries/libs.h" #include "core/linker.h" #include "core/memory.h" @@ -25,20 +28,20 @@ int PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u u64 alignment, int memoryType, s64* physAddrOut) { if (searchStart < 0 || searchEnd <= searchStart) { LOG_ERROR(Kernel_Vmm, "Provided address range is invalid!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } const bool is_in_range = searchEnd - searchStart >= len; if (len <= 0 || !Common::Is16KBAligned(len) || !is_in_range) { LOG_ERROR(Kernel_Vmm, "Provided address range is invalid!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } if (alignment != 0 && !Common::Is16KBAligned(alignment)) { LOG_ERROR(Kernel_Vmm, "Alignment value is invalid!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } if (physAddrOut == nullptr) { LOG_ERROR(Kernel_Vmm, "Result physical address pointer is null!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } auto* memory = Core::Memory::Instance(); @@ -50,7 +53,7 @@ int PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u "alignment = {:#x}, memoryType = {:#x}, physAddrOut = {:#x}", searchStart, searchEnd, len, alignment, memoryType, phys_addr); - return SCE_OK; + return ORBIS_OK; } s32 PS4_SYSV_ABI sceKernelAllocateMainDirectMemory(size_t len, size_t alignment, int memoryType, @@ -108,7 +111,7 @@ s32 PS4_SYSV_ABI sceKernelVirtualQuery(const void* addr, int flags, OrbisVirtual size_t infoSize) { LOG_INFO(Kernel_Vmm, "called addr = {}, flags = {:#x}", fmt::ptr(addr), flags); if (!addr) { - return SCE_KERNEL_ERROR_EACCES; + return ORBIS_KERNEL_ERROR_EACCES; } auto* memory = Core::Memory::Instance(); return memory->VirtualQuery(std::bit_cast(addr), flags, info); @@ -120,16 +123,16 @@ s32 PS4_SYSV_ABI sceKernelReserveVirtualRange(void** addr, u64 len, int flags, u if (addr == nullptr) { LOG_ERROR(Kernel_Vmm, "Address is invalid!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } if (len == 0 || !Common::Is16KBAligned(len)) { LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 16KB aligned!"); - return SCE_KERNEL_ERROR_EINVAL; + 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!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } } @@ -138,35 +141,38 @@ s32 PS4_SYSV_ABI sceKernelReserveVirtualRange(void** addr, u64 len, int flags, u const auto map_flags = static_cast(flags); memory->Reserve(addr, in_addr, len, map_flags, alignment); - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceKernelMapNamedDirectMemory(void** addr, u64 len, int prot, int flags, s64 directMemoryStart, u64 alignment, const char* name) { - LOG_INFO(Kernel_Vmm, - "addr = {}, len = {:#x}, prot = {:#x}, flags = {:#x}, directMemoryStart = {:#x}, " - "alignment = {:#x}", - fmt::ptr(*addr), len, prot, flags, directMemoryStart, alignment); - if (len == 0 || !Common::Is16KBAligned(len)) { LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 16KB aligned!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } if (!Common::Is16KBAligned(directMemoryStart)) { LOG_ERROR(Kernel_Vmm, "Start address is not 16KB aligned!"); - return SCE_KERNEL_ERROR_EINVAL; + 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!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } } const VAddr in_addr = reinterpret_cast(*addr); const auto mem_prot = static_cast(prot); const auto map_flags = static_cast(flags); + SCOPE_EXIT { + LOG_INFO(Kernel_Vmm, + "in_addr = {:#x}, out_addr = {}, len = {:#x}, prot = {:#x}, flags = {:#x}, " + "directMemoryStart = {:#x}, " + "alignment = {:#x}", + in_addr, fmt::ptr(*addr), len, prot, flags, directMemoryStart, alignment); + }; + auto* memory = Core::Memory::Instance(); return memory->MapMemory(addr, in_addr, len, mem_prot, map_flags, Core::VMAType::Direct, "", false, directMemoryStart, alignment); @@ -200,13 +206,14 @@ s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, std::size_t const VAddr in_addr = reinterpret_cast(*addr_in_out); const auto mem_prot = static_cast(prot); const auto map_flags = static_cast(flags); + SCOPE_EXIT { + LOG_INFO(Kernel_Vmm, + "in_addr = {:#x}, out_addr = {}, len = {:#x}, prot = {:#x}, flags = {:#x}", + in_addr, fmt::ptr(*addr_in_out), len, prot, flags); + }; auto* memory = Core::Memory::Instance(); - const int ret = memory->MapMemory(addr_in_out, in_addr, len, mem_prot, map_flags, - Core::VMAType::Flexible, name); - - LOG_INFO(Kernel_Vmm, "addr = {}, len = {:#x}, prot = {:#x}, flags = {:#x}", - fmt::ptr(*addr_in_out), len, prot, flags); - return ret; + return memory->MapMemory(addr_in_out, in_addr, len, mem_prot, map_flags, + Core::VMAType::Flexible, name); } s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, std::size_t len, int prot, @@ -265,8 +272,6 @@ s32 PS4_SYSV_ABI sceKernelBatchMap(OrbisKernelBatchMapEntry* entries, int numEnt MemoryFlags::SCE_KERNEL_MAP_FIXED); // 0x10, 0x410? } -int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len); - s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEntries, int* numEntriesOut, int flags) { int result = ORBIS_OK; @@ -352,20 +357,20 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, size_ size_t alignment, u64* physAddrOut) { if (searchStart < 0 || searchEnd <= searchStart) { LOG_ERROR(Kernel_Vmm, "Provided address range is invalid!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } const bool is_in_range = searchEnd - searchStart >= len; if (len <= 0 || !Common::Is64KBAligned(len) || !is_in_range) { LOG_ERROR(Kernel_Vmm, "Provided address range is invalid!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } if (alignment != 0 && !Common::Is64KBAligned(alignment)) { LOG_ERROR(Kernel_Vmm, "Alignment value is invalid!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } if (physAddrOut == nullptr) { LOG_ERROR(Kernel_Vmm, "Result physical address pointer is null!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } auto* memory = Core::Memory::Instance(); @@ -386,16 +391,16 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addrIn, size_t len, size_t ali if (addrIn == nullptr) { LOG_ERROR(Kernel_Vmm, "Address is invalid!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } if (len == 0 || !Common::Is2MBAligned(len)) { LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 2MB aligned!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } if (alignment != 0) { if ((!std::has_single_bit(alignment) && !Common::Is2MBAligned(alignment))) { LOG_ERROR(Kernel_Vmm, "Alignment value is invalid!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } } @@ -410,11 +415,11 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addrIn, size_t len, size_t ali s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, size_t len, int type, int prot, int flags) { if (addr == nullptr) { LOG_ERROR(Kernel_Vmm, "Address is invalid!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } if (len == 0 || !Common::Is64KBAligned(len)) { LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 64KB aligned!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } LOG_INFO(Kernel_Vmm, "addr = {}, len = {:#x}, type = {:#x}, prot = {:#x}, flags = {:#x}", @@ -429,11 +434,11 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, size_t len, int type, int s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, size_t len, int flags) { if (addr == nullptr) { LOG_ERROR(Kernel_Vmm, "Address is invalid!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } if (len == 0 || !Common::Is64KBAligned(len)) { LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 64KB aligned!"); - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } LOG_INFO(Kernel_Vmm, "addr = {}, len = {:#x}, flags = {:#x}", fmt::ptr(addr), len, flags); @@ -445,4 +450,107 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, size_t len, int flags) return ORBIS_OK; } +int PS4_SYSV_ABI sceKernelMmap(void* addr, u64 len, int prot, int flags, int fd, size_t offset, + void** res) { + LOG_INFO(Kernel_Vmm, "called addr = {}, len = {}, prot = {}, flags = {}, fd = {}, offset = {}", + fmt::ptr(addr), len, prot, flags, fd, offset); + auto* h = Common::Singleton::Instance(); + auto* memory = Core::Memory::Instance(); + const auto mem_prot = static_cast(prot); + const auto mem_flags = static_cast(flags); + if (fd == -1) { + return memory->MapMemory(res, std::bit_cast(addr), len, mem_prot, mem_flags, + Core::VMAType::Flexible); + } else { + const uintptr_t handle = h->GetFile(fd)->f.GetFileMapping(); + return memory->MapFile(res, std::bit_cast(addr), len, mem_prot, mem_flags, handle, + offset); + } +} + +void* PS4_SYSV_ABI posix_mmap(void* addr, u64 len, int prot, int flags, int fd, u64 offset) { + void* ptr; + LOG_INFO(Kernel_Vmm, "posix mmap redirect to sceKernelMmap"); + int result = sceKernelMmap(addr, len, prot, flags, fd, offset, &ptr); + ASSERT(result == 0); + return ptr; +} + +s32 PS4_SYSV_ABI sceKernelConfiguredFlexibleMemorySize(u64* sizeOut) { + if (sizeOut == nullptr) { + return ORBIS_KERNEL_ERROR_EINVAL; + } + + auto* memory = Core::Memory::Instance(); + *sizeOut = memory->GetTotalFlexibleSize(); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len) { + LOG_INFO(Kernel_Vmm, "addr = {}, len = {:#x}", fmt::ptr(addr), len); + if (len == 0) { + return ORBIS_OK; + } + auto* memory = Core::Memory::Instance(); + return memory->UnmapMemory(std::bit_cast(addr), len); +} + +int PS4_SYSV_ABI posix_munmap(void* addr, size_t len) { + int result = sceKernelMunmap(addr, len); + if (result < 0) { + LOG_ERROR(Kernel_Pthread, "posix_munmap: error = {}", result); + ErrSceToPosix(result); + return -1; + } + return result; +} + +void RegisterMemory(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("rTXw65xmLIA", "libkernel", 1, "libkernel", 1, 1, sceKernelAllocateDirectMemory); + LIB_FUNCTION("B+vc2AO2Zrc", "libkernel", 1, "libkernel", 1, 1, + sceKernelAllocateMainDirectMemory); + LIB_FUNCTION("C0f7TJcbfac", "libkernel", 1, "libkernel", 1, 1, + sceKernelAvailableDirectMemorySize); + LIB_FUNCTION("hwVSPCmp5tM", "libkernel", 1, "libkernel", 1, 1, + sceKernelCheckedReleaseDirectMemory); + LIB_FUNCTION("rVjRvHJ0X6c", "libkernel", 1, "libkernel", 1, 1, sceKernelVirtualQuery); + LIB_FUNCTION("7oxv3PPCumo", "libkernel", 1, "libkernel", 1, 1, sceKernelReserveVirtualRange); + LIB_FUNCTION("BC+OG5m9+bw", "libkernel", 1, "libkernel", 1, 1, sceKernelGetDirectMemoryType); + LIB_FUNCTION("pO96TwzOm5E", "libkernel", 1, "libkernel", 1, 1, sceKernelGetDirectMemorySize); + LIB_FUNCTION("NcaWUxfMNIQ", "libkernel", 1, "libkernel", 1, 1, sceKernelMapNamedDirectMemory); + LIB_FUNCTION("L-Q3LEjIbgA", "libkernel", 1, "libkernel", 1, 1, sceKernelMapDirectMemory); + LIB_FUNCTION("WFcfL2lzido", "libkernel", 1, "libkernel", 1, 1, sceKernelQueryMemoryProtection); + LIB_FUNCTION("BHouLQzh0X0", "libkernel", 1, "libkernel", 1, 1, sceKernelDirectMemoryQuery); + LIB_FUNCTION("MBuItvba6z8", "libkernel", 1, "libkernel", 1, 1, sceKernelReleaseDirectMemory); + LIB_FUNCTION("PGhQHd-dzv8", "libkernel", 1, "libkernel", 1, 1, sceKernelMmap); + LIB_FUNCTION("cQke9UuBQOk", "libkernel", 1, "libkernel", 1, 1, sceKernelMunmap); + LIB_FUNCTION("mL8NDH86iQI", "libkernel", 1, "libkernel", 1, 1, sceKernelMapNamedFlexibleMemory); + LIB_FUNCTION("aNz11fnnzi4", "libkernel", 1, "libkernel", 1, 1, + sceKernelAvailableFlexibleMemorySize); + LIB_FUNCTION("aNz11fnnzi4", "libkernel_avlfmem", 1, "libkernel", 1, 1, + sceKernelAvailableFlexibleMemorySize); + LIB_FUNCTION("IWIBBdTHit4", "libkernel", 1, "libkernel", 1, 1, sceKernelMapFlexibleMemory); + LIB_FUNCTION("p5EcQeEeJAE", "libkernel", 1, "libkernel", 1, 1, + _sceKernelRtldSetApplicationHeapAPI); + LIB_FUNCTION("2SKEx6bSq-4", "libkernel", 1, "libkernel", 1, 1, sceKernelBatchMap); + LIB_FUNCTION("kBJzF8x4SyE", "libkernel", 1, "libkernel", 1, 1, sceKernelBatchMap2); + LIB_FUNCTION("DGMG3JshrZU", "libkernel", 1, "libkernel", 1, 1, sceKernelSetVirtualRangeName); + LIB_FUNCTION("n1-v6FgU7MQ", "libkernel", 1, "libkernel", 1, 1, + sceKernelConfiguredFlexibleMemorySize); + + LIB_FUNCTION("9bfdLIyuwCY", "libkernel", 1, "libkernel", 1, 1, sceKernelMTypeProtect); + LIB_FUNCTION("vSMAm3cxYTY", "libkernel", 1, "libkernel", 1, 1, sceKernelMProtect); + + // Memory pool + LIB_FUNCTION("qCSfqDILlns", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolExpand); + LIB_FUNCTION("pU-QydtGcGY", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolReserve); + LIB_FUNCTION("Vzl66WmfLvk", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolCommit); + LIB_FUNCTION("LXo1tpFqJGs", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolDecommit); + + LIB_FUNCTION("BPE9s9vQQXo", "libkernel", 1, "libkernel", 1, 1, posix_mmap); + LIB_FUNCTION("BPE9s9vQQXo", "libScePosix", 1, "libkernel", 1, 1, posix_mmap); + LIB_FUNCTION("UqDGjXA5yUM", "libkernel", 1, "libkernel", 1, 1, posix_munmap); + LIB_FUNCTION("UqDGjXA5yUM", "libScePosix", 1, "libkernel", 1, 1, posix_munmap); +} + } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/memory_management.h b/src/core/libraries/kernel/memory.h similarity index 96% rename from src/core/libraries/kernel/memory_management.h rename to src/core/libraries/kernel/memory.h index 6e90204cf..2d19ceb49 100644 --- a/src/core/libraries/kernel/memory_management.h +++ b/src/core/libraries/kernel/memory.h @@ -10,6 +10,10 @@ constexpr u64 SCE_KERNEL_MAIN_DMEM_SIZE = 5056_MB; // ~ 5GB // TODO: Confirm this value on hardware. constexpr u64 SCE_KERNEL_MAIN_DMEM_SIZE_PRO = 5568_MB; // ~ 5.5GB +namespace Core::Loader { +class SymbolsResolver; +} + namespace Libraries::Kernel { enum MemoryTypes : u32 { @@ -123,4 +127,12 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addrIn, size_t len, size_t ali s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, size_t len, int type, int prot, int flags); s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, size_t len, int flags); +int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len); + +void* Malloc(size_t size); + +void Free(void* ptr); + +void RegisterMemory(Core::Loader::SymbolsResolver* sym); + } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/orbis_error.h b/src/core/libraries/kernel/orbis_error.h new file mode 100644 index 000000000..d19b3f3f1 --- /dev/null +++ b/src/core/libraries/kernel/orbis_error.h @@ -0,0 +1,108 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// Libkernel library +constexpr int ORBIS_KERNEL_ERROR_UNKNOWN = 0x80020000; +constexpr int ORBIS_KERNEL_ERROR_EPERM = 0x80020001; +constexpr int ORBIS_KERNEL_ERROR_ENOENT = 0x80020002; +constexpr int ORBIS_KERNEL_ERROR_ESRCH = 0x80020003; +constexpr int ORBIS_KERNEL_ERROR_EINTR = 0x80020004; +constexpr int ORBIS_KERNEL_ERROR_EIO = 0x80020005; +constexpr int ORBIS_KERNEL_ERROR_ENXIO = 0x80020006; +constexpr int ORBIS_KERNEL_ERROR_E2BIG = 0x80020007; +constexpr int ORBIS_KERNEL_ERROR_ENOEXEC = 0x80020008; +constexpr int ORBIS_KERNEL_ERROR_EBADF = 0x80020009; +constexpr int ORBIS_KERNEL_ERROR_ECHILD = 0x8002000A; +constexpr int ORBIS_KERNEL_ERROR_EDEADLK = 0x8002000B; +constexpr int ORBIS_KERNEL_ERROR_ENOMEM = 0x8002000C; +constexpr int ORBIS_KERNEL_ERROR_EACCES = 0x8002000D; +constexpr int ORBIS_KERNEL_ERROR_EFAULT = 0x8002000E; +constexpr int ORBIS_KERNEL_ERROR_ENOTBLK = 0x8002000F; +constexpr int ORBIS_KERNEL_ERROR_EBUSY = 0x80020010; +constexpr int ORBIS_KERNEL_ERROR_EEXIST = 0x80020011; +constexpr int ORBIS_KERNEL_ERROR_EXDEV = 0x80020012; +constexpr int ORBIS_KERNEL_ERROR_ENODEV = 0x80020013; +constexpr int ORBIS_KERNEL_ERROR_ENOTDIR = 0x80020014; +constexpr int ORBIS_KERNEL_ERROR_EISDIR = 0x80020015; +constexpr int ORBIS_KERNEL_ERROR_EINVAL = 0x80020016; +constexpr int ORBIS_KERNEL_ERROR_ENFILE = 0x80020017; +constexpr int ORBIS_KERNEL_ERROR_EMFILE = 0x80020018; +constexpr int ORBIS_KERNEL_ERROR_ENOTTY = 0x80020019; +constexpr int ORBIS_KERNEL_ERROR_ETXTBSY = 0x8002001A; +constexpr int ORBIS_KERNEL_ERROR_EFBIG = 0x8002001B; +constexpr int ORBIS_KERNEL_ERROR_ENOSPC = 0x8002001C; +constexpr int ORBIS_KERNEL_ERROR_ESPIPE = 0x8002001D; +constexpr int ORBIS_KERNEL_ERROR_EROFS = 0x8002001E; +constexpr int ORBIS_KERNEL_ERROR_EMLINK = 0x8002001F; +constexpr int ORBIS_KERNEL_ERROR_EPIPE = 0x80020020; +constexpr int ORBIS_KERNEL_ERROR_EDOM = 0x80020021; +constexpr int ORBIS_KERNEL_ERROR_ERANGE = 0x80020022; +constexpr int ORBIS_KERNEL_ERROR_EAGAIN = 0x80020023; +constexpr int ORBIS_KERNEL_ERROR_EWOULDBLOCK = 0x80020023; +constexpr int ORBIS_KERNEL_ERROR_EINPROGRESS = 0x80020024; +constexpr int ORBIS_KERNEL_ERROR_EALREADY = 0x80020025; +constexpr int ORBIS_KERNEL_ERROR_ENOTSOCK = 0x80020026; +constexpr int ORBIS_KERNEL_ERROR_EDESTADDRREQ = 0x80020027; +constexpr int ORBIS_KERNEL_ERROR_EMSGSIZE = 0x80020028; +constexpr int ORBIS_KERNEL_ERROR_EPROTOTYPE = 0x80020029; +constexpr int ORBIS_KERNEL_ERROR_ENOPROTOOPT = 0x8002002A; +constexpr int ORBIS_KERNEL_ERROR_EPROTONOSUPPORT = 0x8002002B; +constexpr int ORBIS_KERNEL_ERROR_ESOCKTNOSUPPORT = 0x8002002C; +constexpr int ORBIS_KERNEL_ERROR_ENOTSUP = 0x8002002D; +constexpr int ORBIS_KERNEL_ERROR_EOPNOTSUPP = 0x8002002D; +constexpr int ORBIS_KERNEL_ERROR_EPFNOSUPPORT = 0x8002002E; +constexpr int ORBIS_KERNEL_ERROR_EAFNOSUPPORT = 0x8002002F; +constexpr int ORBIS_KERNEL_ERROR_EADDRINUSE = 0x80020030; +constexpr int ORBIS_KERNEL_ERROR_EADDRNOTAVAIL = 0x80020031; +constexpr int ORBIS_KERNEL_ERROR_ENETDOWN = 0x80020032; +constexpr int ORBIS_KERNEL_ERROR_ENETUNREACH = 0x80020033; +constexpr int ORBIS_KERNEL_ERROR_ENETRESET = 0x80020034; +constexpr int ORBIS_KERNEL_ERROR_ECONNABORTED = 0x80020035; +constexpr int ORBIS_KERNEL_ERROR_ECONNRESET = 0x80020036; +constexpr int ORBIS_KERNEL_ERROR_ENOBUFS = 0x80020037; +constexpr int ORBIS_KERNEL_ERROR_EISCONN = 0x80020038; +constexpr int ORBIS_KERNEL_ERROR_ENOTCONN = 0x80020039; +constexpr int ORBIS_KERNEL_ERROR_ESHUTDOWN = 0x8002003A; +constexpr int ORBIS_KERNEL_ERROR_ETOOMANYREFS = 0x8002003B; +constexpr int ORBIS_KERNEL_ERROR_ETIMEDOUT = 0x8002003C; +constexpr int ORBIS_KERNEL_ERROR_ECONNREFUSED = 0x8002003D; +constexpr int ORBIS_KERNEL_ERROR_ELOOP = 0x8002003E; +constexpr int ORBIS_KERNEL_ERROR_ENAMETOOLONG = 0x8002003F; +constexpr int ORBIS_KERNEL_ERROR_EHOSTDOWN = 0x80020040; +constexpr int ORBIS_KERNEL_ERROR_EHOSTUNREACH = 0x80020041; +constexpr int ORBIS_KERNEL_ERROR_ENOTEMPTY = 0x80020042; +constexpr int ORBIS_KERNEL_ERROR_EPROCLIM = 0x80020043; +constexpr int ORBIS_KERNEL_ERROR_EUSERS = 0x80020044; +constexpr int ORBIS_KERNEL_ERROR_EDQUOT = 0x80020045; +constexpr int ORBIS_KERNEL_ERROR_ESTALE = 0x80020046; +constexpr int ORBIS_KERNEL_ERROR_EREMOTE = 0x80020047; +constexpr int ORBIS_KERNEL_ERROR_EBADRPC = 0x80020048; +constexpr int ORBIS_KERNEL_ERROR_ERPCMISMATCH = 0x80020049; +constexpr int ORBIS_KERNEL_ERROR_EPROGUNAVAIL = 0x8002004A; +constexpr int ORBIS_KERNEL_ERROR_EPROGMISMATCH = 0x8002004B; +constexpr int ORBIS_KERNEL_ERROR_EPROCUNAVAIL = 0x8002004C; +constexpr int ORBIS_KERNEL_ERROR_ENOLCK = 0x8002004D; +constexpr int ORBIS_KERNEL_ERROR_ENOSYS = 0x8002004E; +constexpr int ORBIS_KERNEL_ERROR_EFTYPE = 0x8002004F; +constexpr int ORBIS_KERNEL_ERROR_EAUTH = 0x80020050; +constexpr int ORBIS_KERNEL_ERROR_ENEEDAUTH = 0x80020051; +constexpr int ORBIS_KERNEL_ERROR_EIDRM = 0x80020052; +constexpr int ORBIS_KERNEL_ERROR_ENOMSG = 0x80020053; +constexpr int ORBIS_KERNEL_ERROR_EOVERFLOW = 0x80020054; +constexpr int ORBIS_KERNEL_ERROR_ECANCELED = 0x80020055; +constexpr int ORBIS_KERNEL_ERROR_EILSEQ = 0x80020056; +constexpr int ORBIS_KERNEL_ERROR_ENOATTR = 0x80020057; +constexpr int ORBIS_KERNEL_ERROR_EDOOFUS = 0x80020058; +constexpr int ORBIS_KERNEL_ERROR_EBADMSG = 0x80020059; +constexpr int ORBIS_KERNEL_ERROR_EMULTIHOP = 0x8002005A; +constexpr int ORBIS_KERNEL_ERROR_ENOLINK = 0x8002005B; +constexpr int ORBIS_KERNEL_ERROR_EPROTO = 0x8002005C; +constexpr int ORBIS_KERNEL_ERROR_ENOTCAPABLE = 0x8002005D; +constexpr int ORBIS_KERNEL_ERROR_ECAPMODE = 0x8002005E; +constexpr int ORBIS_KERNEL_ERROR_ENOBLK = 0x8002005F; +constexpr int ORBIS_KERNEL_ERROR_EICV = 0x80020060; +constexpr int ORBIS_KERNEL_ERROR_ENOPLAYGOENT = 0x80020061; diff --git a/src/core/libraries/kernel/posix_error.h b/src/core/libraries/kernel/posix_error.h new file mode 100644 index 000000000..0f7cf3e50 --- /dev/null +++ b/src/core/libraries/kernel/posix_error.h @@ -0,0 +1,128 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// Posix error codes +constexpr int POSIX_EPERM = 1; +constexpr int POSIX_ENOENT = 2; +constexpr int POSIX_ESRCH = 3; +constexpr int POSIX_EINTR = 4; +constexpr int POSIX_EIO = 5; +constexpr int POSIX_ENXIO = 6; +constexpr int POSIX_E2BIG = 7; +constexpr int POSIX_ENOEXEC = 8; +constexpr int POSIX_EBADF = 9; +constexpr int POSIX_ECHILD = 10; +constexpr int POSIX_EDEADLK = 11; +constexpr int POSIX_ENOMEM = 12; +constexpr int POSIX_EACCES = 13; +constexpr int POSIX_EFAULT = 14; +constexpr int POSIX_ENOTBLK = 15; +constexpr int POSIX_EBUSY = 16; +constexpr int POSIX_EEXIST = 17; +constexpr int POSIX_EXDEV = 18; +constexpr int POSIX_ENODEV = 19; +constexpr int POSIX_ENOTDIR = 20; +constexpr int POSIX_EISDIR = 21; +constexpr int POSIX_EINVAL = 22; +constexpr int POSIX_ENFILE = 23; +constexpr int POSIX_EMFILE = 24; +constexpr int POSIX_ENOTTY = 25; +constexpr int POSIX_ETXTBSY = 26; +constexpr int POSIX_EFBIG = 27; +constexpr int POSIX_ENOSPC = 28; +constexpr int POSIX_ESPIPE = 29; +constexpr int POSIX_EROFS = 30; +constexpr int POSIX_EMLINK = 31; +constexpr int POSIX_EPIPE = 32; +constexpr int POSIX_EDOM = 33; +constexpr int POSIX_ERANGE = 34; +constexpr int POSIX_EAGAIN = 35; +constexpr int POSIX_EWOULDBLOCK = 35; +constexpr int POSIX_EINPROGRESS = 36; +constexpr int POSIX_EALREADY = 37; +constexpr int POSIX_ENOTSOCK = 38; +constexpr int POSIX_EDESTADDRREQ = 39; +constexpr int POSIX_EMSGSIZE = 40; +constexpr int POSIX_EPROTOTYPE = 41; +constexpr int POSIX_ENOPROTOOPT = 42; +constexpr int POSIX_EPROTONOSUPPORT = 43; +constexpr int POSIX_ESOCKTNOSUPPORT = 44; +constexpr int POSIX_EOPNOTSUPP = 45; +constexpr int POSIX_ENOTSUP = 45; +constexpr int POSIX_EPFNOSUPPORT = 46; +constexpr int POSIX_EAFNOSUPPORT = 47; +constexpr int POSIX_EADDRINUSE = 48; +constexpr int POSIX_EADDRNOTAVAIL = 49; +constexpr int POSIX_ENETDOWN = 50; +constexpr int POSIX_ENETUNREACH = 51; +constexpr int POSIX_ENETRESET = 52; +constexpr int POSIX_ECONNABORTED = 53; +constexpr int POSIX_ECONNRESET = 54; +constexpr int POSIX_ENOBUFS = 55; +constexpr int POSIX_EISCONN = 56; +constexpr int POSIX_ENOTCONN = 57; +constexpr int POSIX_ESHUTDOWN = 58; +constexpr int POSIX_ETOOMANYREFS = 59; +constexpr int POSIX_ETIMEDOUT = 60; +constexpr int POSIX_ECONNREFUSED = 61; +constexpr int POSIX_ELOOP = 62; +constexpr int POSIX_ENAMETOOLONG = 63; +constexpr int POSIX_EHOSTDOWN = 64; +constexpr int POSIX_EHOSTUNREACH = 65; +constexpr int POSIX_ENOTEMPTY = 66; +constexpr int POSIX_EPROCLIM = 67; +constexpr int POSIX_EUSERS = 68; +constexpr int POSIX_EDQUOT = 69; +constexpr int POSIX_ESTALE = 70; +constexpr int POSIX_EREMOTE = 71; +constexpr int POSIX_EBADRPC = 72; +constexpr int POSIX_ERPCMISMATCH = 73; +constexpr int POSIX_EPROGUNAVAIL = 74; +constexpr int POSIX_EPROGMISMATCH = 75; +constexpr int POSIX_EPROCUNAVAIL = 76; +constexpr int POSIX_ENOLCK = 77; +constexpr int POSIX_ENOSYS = 78; +constexpr int POSIX_EFTYPE = 79; +constexpr int POSIX_EAUTH = 80; +constexpr int POSIX_ENEEDAUTH = 81; +constexpr int POSIX_EIDRM = 82; +constexpr int POSIX_ENOMSG = 83; +constexpr int POSIX_EOVERFLOW = 84; +constexpr int POSIX_ECANCELED = 85; +constexpr int POSIX_EILSEQ = 86; +constexpr int POSIX_ENOATTR = 87; +constexpr int POSIX_EDOOFUS = 88; +constexpr int POSIX_EBADMSG = 89; +constexpr int POSIX_EMULTIHOP = 90; +constexpr int POSIX_ENOLINK = 91; +constexpr int POSIX_EPROTO = 92; +constexpr int POSIX_ENOTCAPABLE = 93; +constexpr int POSIX_ECAPMODE = 94; +constexpr int POSIX_ENOBLK = 95; +constexpr int POSIX_EICV = 96; +constexpr int POSIX_ENOPLAYGOENT = 97; +constexpr int POSIX_EREVOKE = 98; +constexpr int POSIX_ESDKVERSION = 99; +constexpr int POSIX_ESTART = 100; +constexpr int POSIX_ESTOP = 101; +constexpr int POSIX_EINVALID2MB = 102; +constexpr int POSIX_ELAST = 102; +constexpr int POSIX_EADHOC = 160; +constexpr int POSIX_EINACTIVEDISABLED = 163; +constexpr int POSIX_ENETNODATA = 164; +constexpr int POSIX_ENETDESC = 165; +constexpr int POSIX_ENETDESCTIMEDOUT = 166; +constexpr int POSIX_ENETINTR = 167; +constexpr int POSIX_ERETURN = 205; +constexpr int POSIX_EFPOS = 152; +constexpr int POSIX_ENODATA = 1040; +constexpr int POSIX_ENOSR = 1050; +constexpr int POSIX_ENOSTR = 1051; +constexpr int POSIX_ENOTRECOVERABLE = 1056; +constexpr int POSIX_EOTHER = 1062; +constexpr int POSIX_EOWNERDEAD = 1064; +constexpr int POSIX_ETIME = 1074; diff --git a/src/core/libraries/kernel/process.cpp b/src/core/libraries/kernel/process.cpp new file mode 100644 index 000000000..6c29d9305 --- /dev/null +++ b/src/core/libraries/kernel/process.cpp @@ -0,0 +1,147 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/config.h" +#include "common/elf_info.h" +#include "common/logging/log.h" +#include "core/file_sys/fs.h" +#include "core/libraries/kernel/orbis_error.h" +#include "core/libraries/kernel/process.h" +#include "core/libraries/libs.h" +#include "core/linker.h" + +namespace Libraries::Kernel { + +int PS4_SYSV_ABI sceKernelIsNeoMode() { + LOG_DEBUG(Kernel_Sce, "called"); + return Config::isNeoMode(); +} + +int PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver) { + int version = Common::ElfInfo::Instance().RawFirmwareVer(); + *ver = version; + return (version >= 0) ? ORBIS_OK : ORBIS_KERNEL_ERROR_EINVAL; +} + +int PS4_SYSV_ABI sceKernelGetCpumode() { + return 0; +} + +void* PS4_SYSV_ABI sceKernelGetProcParam() { + auto* linker = Common::Singleton::Instance(); + return linker->GetProcParam(); +} + +s32 PS4_SYSV_ABI sceKernelLoadStartModule(const char* moduleFileName, size_t args, const void* argp, + u32 flags, const void* pOpt, int* pRes) { + LOG_INFO(Lib_Kernel, "called filename = {}, args = {}", moduleFileName, args); + + if (flags != 0) { + return ORBIS_KERNEL_ERROR_EINVAL; + } + + auto* mnt = Common::Singleton::Instance(); + const auto path = mnt->GetHostPath(moduleFileName); + + // Load PRX module and relocate any modules that import it. + auto* linker = Common::Singleton::Instance(); + u32 handle = linker->FindByName(path); + if (handle != -1) { + return handle; + } + handle = linker->LoadModule(path, true); + auto* module = linker->GetModule(handle); + linker->RelocateAnyImports(module); + + // If the new module has a TLS image, trigger its load when TlsGetAddr is called. + if (module->tls.image_size != 0) { + linker->AdvanceGenerationCounter(); + } + + // Retrieve and verify proc param according to libkernel. + u64* param = module->GetProcParam(); + ASSERT_MSG(!param || param[0] >= 0x18, "Invalid module param size: {}", param[0]); + s32 ret = module->Start(args, argp, param); + if (pRes) { + *pRes = ret; + } + + return handle; +} + +s32 PS4_SYSV_ABI sceKernelDlsym(s32 handle, const char* symbol, void** addrp) { + auto* linker = Common::Singleton::Instance(); + auto* module = linker->GetModule(handle); + *addrp = module->FindByName(symbol); + if (*addrp == nullptr) { + return ORBIS_KERNEL_ERROR_ESRCH; + } + return ORBIS_OK; +} + +static constexpr size_t ORBIS_DBG_MAX_NAME_LENGTH = 256; + +struct OrbisModuleInfoForUnwind { + u64 st_size; + std::array name; + VAddr eh_frame_hdr_addr; + VAddr eh_frame_addr; + u64 eh_frame_size; + VAddr seg0_addr; + u64 seg0_size; +}; + +s32 PS4_SYSV_ABI sceKernelGetModuleInfoForUnwind(VAddr addr, int flags, + OrbisModuleInfoForUnwind* info) { + if (flags >= 3) { + std::memset(info, 0, sizeof(OrbisModuleInfoForUnwind)); + return ORBIS_KERNEL_ERROR_EINVAL; + } + if (!info) { + return ORBIS_KERNEL_ERROR_EFAULT; + } + if (info->st_size < sizeof(OrbisModuleInfoForUnwind)) { + return ORBIS_KERNEL_ERROR_EINVAL; + } + + // Find module that contains specified address. + LOG_INFO(Lib_Kernel, "called addr = {:#x}, flags = {:#x}", addr, flags); + auto* linker = Common::Singleton::Instance(); + auto* module = linker->FindByAddress(addr); + if (!module) { + return ORBIS_KERNEL_ERROR_EFAULT; + } + const auto mod_info = module->GetModuleInfoEx(); + + // Fill in module info. + std::memset(info, 0, sizeof(OrbisModuleInfoForUnwind)); + info->name = mod_info.name; + info->eh_frame_hdr_addr = mod_info.eh_frame_hdr_addr; + info->eh_frame_addr = mod_info.eh_frame_addr; + info->eh_frame_size = mod_info.eh_frame_size; + info->seg0_addr = mod_info.segments[0].address; + info->seg0_size = mod_info.segments[0].size; + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceKernelGetModuleInfoFromAddr(VAddr addr, int flags, + Core::OrbisKernelModuleInfoEx* info) { + LOG_INFO(Lib_Kernel, "called addr = {:#x}, flags = {:#x}", addr, flags); + auto* linker = Common::Singleton::Instance(); + auto* module = linker->FindByAddress(addr); + *info = module->GetModuleInfoEx(); + return ORBIS_OK; +} + +void RegisterProcess(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("WB66evu8bsU", "libkernel", 1, "libkernel", 1, 1, sceKernelGetCompiledSdkVersion); + LIB_FUNCTION("WslcK1FQcGI", "libkernel", 1, "libkernel", 1, 1, sceKernelIsNeoMode); + LIB_FUNCTION("VOx8NGmHXTs", "libkernel", 1, "libkernel", 1, 1, sceKernelGetCpumode); + LIB_FUNCTION("959qrazPIrg", "libkernel", 1, "libkernel", 1, 1, sceKernelGetProcParam); + LIB_FUNCTION("wzvqT4UqKX8", "libkernel", 1, "libkernel", 1, 1, sceKernelLoadStartModule); + 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); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/cpu_management.h b/src/core/libraries/kernel/process.h similarity index 60% rename from src/core/libraries/kernel/cpu_management.h rename to src/core/libraries/kernel/process.h index 814ea51df..0340a9793 100644 --- a/src/core/libraries/kernel/cpu_management.h +++ b/src/core/libraries/kernel/process.h @@ -5,8 +5,16 @@ #include "common/types.h" +namespace Core::Loader { +class SymbolsResolver; +} + namespace Libraries::Kernel { int PS4_SYSV_ABI sceKernelIsNeoMode(); +int PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver); + +void RegisterProcess(Core::Loader::SymbolsResolver* sym); + } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/sync/mutex.cpp b/src/core/libraries/kernel/sync/mutex.cpp new file mode 100644 index 000000000..c5e3eba1d --- /dev/null +++ b/src/core/libraries/kernel/sync/mutex.cpp @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "mutex.h" + +#include "common/assert.h" + +namespace Libraries::Kernel { + +TimedMutex::TimedMutex() { +#ifdef _WIN64 + mtx = CreateMutex(nullptr, false, nullptr); + ASSERT(mtx); +#endif +} + +TimedMutex::~TimedMutex() { +#ifdef _WIN64 + CloseHandle(mtx); +#endif +} + +void TimedMutex::lock() { +#ifdef _WIN64 + for (;;) { + u64 res = WaitForSingleObjectEx(mtx, INFINITE, true); + if (res == WAIT_OBJECT_0) { + return; + } + } +#else + mtx.lock(); +#endif +} + +bool TimedMutex::try_lock() { +#ifdef _WIN64 + return WaitForSingleObjectEx(mtx, 0, true) == WAIT_OBJECT_0; +#else + return mtx.try_lock(); +#endif +} + +void TimedMutex::unlock() { +#ifdef _WIN64 + ReleaseMutex(mtx); +#else + mtx.unlock(); +#endif +} + +} // namespace Libraries::Kernel \ No newline at end of file diff --git a/src/core/libraries/kernel/sync/mutex.h b/src/core/libraries/kernel/sync/mutex.h new file mode 100644 index 000000000..f14a920b4 --- /dev/null +++ b/src/core/libraries/kernel/sync/mutex.h @@ -0,0 +1,80 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "common/types.h" + +#ifdef _WIN64 +#include +#else +#include +#endif + +namespace Libraries::Kernel { + +class TimedMutex { +public: + TimedMutex(); + ~TimedMutex(); + + void lock(); + bool try_lock(); + + void unlock(); + + template + bool try_lock_for(const std::chrono::duration& rel_time) { +#ifdef _WIN64 + constexpr auto zero = std::chrono::duration::zero(); + const auto now = std::chrono::steady_clock::now(); + + std::chrono::steady_clock::time_point abs_time = now; + if (rel_time > zero) { + constexpr auto max = (std::chrono::steady_clock::time_point::max)(); + if (abs_time < max - rel_time) { + abs_time += rel_time; + } else { + abs_time = max; + } + } + + return try_lock_until(abs_time); +#else + return mtx.try_lock_for(rel_time); +#endif + } + + template + bool try_lock_until(const std::chrono::time_point& abs_time) { +#ifdef _WIN64 + for (;;) { + const auto now = Clock::now(); + if (abs_time <= now) { + return false; + } + + const auto rel_ms = std::chrono::ceil(abs_time - now); + u64 res = WaitForSingleObjectEx(mtx, static_cast(rel_ms.count()), true); + if (res == WAIT_OBJECT_0) { + return true; + } else if (res == WAIT_TIMEOUT) { + return false; + } + } +#else + return mtx.try_lock_until(abs_time); +#endif + } + +private: +#ifdef _WIN64 + HANDLE mtx; +#else + std::timed_mutex mtx; +#endif +}; + +} // namespace Libraries::Kernel \ No newline at end of file diff --git a/src/core/libraries/kernel/sync/semaphore.h b/src/core/libraries/kernel/sync/semaphore.h new file mode 100644 index 000000000..48a5dc0d8 --- /dev/null +++ b/src/core/libraries/kernel/sync/semaphore.h @@ -0,0 +1,167 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#include "common/assert.h" +#include "common/types.h" + +#ifdef _WIN64 +#include +#elif defined(__APPLE__) +#include +#else +#include +#endif + +namespace Libraries::Kernel { + +template +class Semaphore { +public: + Semaphore(s32 initialCount) +#if !defined(_WIN64) && !defined(__APPLE__) + : sem{initialCount} +#endif + { +#ifdef _WIN64 + sem = CreateSemaphore(nullptr, initialCount, max, nullptr); + ASSERT(sem); +#elif defined(__APPLE__) + sem = dispatch_semaphore_create(initialCount); + ASSERT(sem); +#endif + } + + ~Semaphore() { +#ifdef _WIN64 + CloseHandle(sem); +#elif defined(__APPLE__) + dispatch_release(sem); +#endif + } + + void release() { +#ifdef _WIN64 + ReleaseSemaphore(sem, 1, nullptr); +#elif defined(__APPLE__) + dispatch_semaphore_signal(sem); +#else + sem.release(); +#endif + } + + void acquire() { +#ifdef _WIN64 + for (;;) { + u64 res = WaitForSingleObjectEx(sem, INFINITE, true); + if (res == WAIT_OBJECT_0) { + return; + } + } +#elif defined(__APPLE__) + for (;;) { + const auto res = dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + if (res == 0) { + return; + } + } +#else + sem.acquire(); +#endif + } + + bool try_acquire() { +#ifdef _WIN64 + return WaitForSingleObjectEx(sem, 0, true) == WAIT_OBJECT_0; +#elif defined(__APPLE__) + return dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW) == 0; +#else + return sem.try_acquire(); +#endif + } + + template + bool try_acquire_for(const std::chrono::duration& rel_time) { +#ifdef _WIN64 + const auto start_time = std::chrono::high_resolution_clock::now(); + auto rel_time_ms = std::chrono::ceil(rel_time); + + while (rel_time_ms.count() > 0) { + u64 timeout_ms = static_cast(rel_time_ms.count()); + u64 res = WaitForSingleObjectEx(sem, timeout_ms, true); + if (res == WAIT_OBJECT_0) { + return true; + } else if (res == WAIT_IO_COMPLETION) { + auto elapsed_time = std::chrono::high_resolution_clock::now() - start_time; + rel_time_ms -= std::chrono::duration_cast(elapsed_time); + } else { + return false; + } + } + + return false; +#elif defined(__APPLE__) + const auto rel_time_ns = std::chrono::ceil(rel_time).count(); + const auto timeout = dispatch_time(DISPATCH_TIME_NOW, rel_time_ns); + return dispatch_semaphore_wait(sem, timeout) == 0; +#else + return sem.try_acquire_for(rel_time); +#endif + } + + template + bool try_acquire_until(const std::chrono::time_point& abs_time) { +#ifdef _WIN64 + const auto start_time = Clock::now(); + if (start_time >= abs_time) { + return false; + } + + auto rel_time = std::chrono::ceil(abs_time - start_time); + while (rel_time.count() > 0) { + u64 timeout_ms = static_cast(rel_time.count()); + u64 res = WaitForSingleObjectEx(sem, timeout_ms, true); + if (res == WAIT_OBJECT_0) { + return true; + } else if (res == WAIT_IO_COMPLETION) { + auto elapsed_time = Clock::now() - start_time; + rel_time -= std::chrono::duration_cast(elapsed_time); + } else { + return false; + } + } + + return false; +#elif defined(__APPLE__) + auto abs_s = std::chrono::time_point_cast(abs_time); + auto abs_ns = std::chrono::time_point_cast(abs_time) - + std::chrono::time_point_cast(abs_s); + const timespec abs_timespec = { + .tv_sec = abs_s.time_since_epoch().count(), + .tv_nsec = abs_ns.count(), + }; + const auto timeout = dispatch_walltime(&abs_timespec, 0); + return dispatch_semaphore_wait(sem, timeout) == 0; +#else + return sem.try_acquire_until(abs_time); +#endif + } + +private: +#ifdef _WIN64 + HANDLE sem; +#elif defined(__APPLE__) + dispatch_semaphore_t sem; +#else + std::counting_semaphore sem; +#endif +}; + +using BinarySemaphore = Semaphore<1>; +using CountingSemaphore = Semaphore<0x7FFFFFFF /*ORBIS_KERNEL_SEM_VALUE_MAX*/>; + +} // namespace Libraries::Kernel \ No newline at end of file diff --git a/src/core/libraries/kernel/thread_management.cpp b/src/core/libraries/kernel/thread_management.cpp deleted file mode 100644 index 85f618090..000000000 --- a/src/core/libraries/kernel/thread_management.cpp +++ /dev/null @@ -1,1716 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include - -#include "common/alignment.h" -#include "common/assert.h" -#include "common/error.h" -#include "common/logging/log.h" -#include "common/singleton.h" -#include "common/thread.h" -#include "core/debug_state.h" -#include "core/libraries/error_codes.h" -#include "core/libraries/kernel/libkernel.h" -#include "core/libraries/kernel/thread_management.h" -#include "core/libraries/kernel/threads/threads.h" -#include "core/libraries/libs.h" -#include "core/linker.h" -#include "core/tls.h" -#ifdef _WIN64 -#include -#else -#include -#endif - -namespace Libraries::Kernel { - -thread_local ScePthread g_pthread_self{}; -PThreadCxt* g_pthread_cxt = nullptr; - -void init_pthreads() { - g_pthread_cxt = new PThreadCxt{}; - // default mutex init - ScePthreadMutexattr default_mutexattr = nullptr; - scePthreadMutexattrInit(&default_mutexattr); - g_pthread_cxt->setDefaultMutexattr(default_mutexattr); - ScePthreadMutexattr adaptive_mutexattr = nullptr; - scePthreadMutexattrInit(&adaptive_mutexattr); - scePthreadMutexattrSettype(&adaptive_mutexattr, ORBIS_PTHREAD_MUTEX_ADAPTIVE); - g_pthread_cxt->setAdaptiveMutexattr(adaptive_mutexattr); - // default cond init - ScePthreadCondattr default_condattr = nullptr; - scePthreadCondattrInit(&default_condattr); - g_pthread_cxt->setDefaultCondattr(default_condattr); - // default attr init - ScePthreadAttr default_attr = nullptr; - scePthreadAttrInit(&default_attr); - g_pthread_cxt->SetDefaultAttr(default_attr); - // default rw init - OrbisPthreadRwlockattr default_rwattr = nullptr; - scePthreadRwlockattrInit(&default_rwattr); - g_pthread_cxt->setDefaultRwattr(default_rwattr); - - g_pthread_cxt->SetPthreadPool(new PThreadPool); -} - -void pthreadInitSelfMainThread() { - const char* name = "Main_Thread"; - auto* pthread_pool = g_pthread_cxt->GetPthreadPool(); - g_pthread_self = pthread_pool->Create(name); - scePthreadAttrInit(&g_pthread_self->attr); - g_pthread_self->pth = pthread_self(); - g_pthread_self->name = name; -} - -int PS4_SYSV_ABI scePthreadAttrInit(ScePthreadAttr* attr) { - *attr = new PthreadAttrInternal{}; - - int result = pthread_attr_init(&(*attr)->pth_attr); - - (*attr)->affinity = 0x7f; - (*attr)->guard_size = 0x1000; - - SceKernelSchedParam param{}; - param.sched_priority = 700; - - result = (result == 0 ? scePthreadAttrSetinheritsched(attr, 4) : result); - result = (result == 0 ? scePthreadAttrSetschedparam(attr, ¶m) : result); - result = (result == 0 ? scePthreadAttrSetschedpolicy(attr, SCHED_OTHER) : result); - result = (result == 0 ? scePthreadAttrSetdetachstate(attr, PTHREAD_CREATE_JOINABLE) : result); - - switch (result) { - case 0: - return SCE_OK; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadAttrDestroy(ScePthreadAttr* attr) { - - int result = pthread_attr_destroy(&(*attr)->pth_attr); - - delete *attr; - *attr = nullptr; - - if (result == 0) { - return SCE_OK; - } - return SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrSetguardsize(ScePthreadAttr* attr, size_t guard_size) { - if (attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - (*attr)->guard_size = guard_size; - - return SCE_OK; -} - -int PS4_SYSV_ABI scePthreadAttrGetguardsize(const ScePthreadAttr* attr, size_t* guard_size) { - if (guard_size == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - *guard_size = (*attr)->guard_size; - - return SCE_OK; -} - -int PS4_SYSV_ABI scePthreadAttrGetinheritsched(const ScePthreadAttr* attr, int* inherit_sched) { - - if (inherit_sched == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_attr_getinheritsched(&(*attr)->pth_attr, inherit_sched); - - switch (*inherit_sched) { - case PTHREAD_EXPLICIT_SCHED: - *inherit_sched = 0; - break; - case PTHREAD_INHERIT_SCHED: - *inherit_sched = 4; - break; - default: - UNREACHABLE(); - } - - return (result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL); -} - -int PS4_SYSV_ABI scePthreadAttrGetdetachstate(const ScePthreadAttr* attr, int* state) { - if (state == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - // int result = pthread_attr_getdetachstate(&(*attr)->pth_attr, state); - int result = 0; - *state = ((*attr)->detached ? PTHREAD_CREATE_DETACHED : PTHREAD_CREATE_JOINABLE); - - switch (*state) { - case PTHREAD_CREATE_JOINABLE: - *state = 0; - break; - case PTHREAD_CREATE_DETACHED: - *state = 1; - break; - default: - UNREACHABLE(); - } - - return (result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL); -} - -int PS4_SYSV_ABI scePthreadAttrSetdetachstate(ScePthreadAttr* attr, int detachstate) { - if (attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int pstate = PTHREAD_CREATE_JOINABLE; - switch (detachstate) { - case 0: - pstate = PTHREAD_CREATE_JOINABLE; - break; - case 1: - pstate = PTHREAD_CREATE_DETACHED; - break; - default: - UNREACHABLE_MSG("Invalid detachstate: {}", detachstate); - } - - // int result = pthread_attr_setdetachstate(&(*attr)->pth_attr, pstate); - int result = 0; - (*attr)->detached = (pstate == PTHREAD_CREATE_DETACHED); - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrSetinheritsched(ScePthreadAttr* attr, int inheritSched) { - if (attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int pinherit_sched = PTHREAD_INHERIT_SCHED; - switch (inheritSched) { - case 0: - pinherit_sched = PTHREAD_EXPLICIT_SCHED; - break; - case 4: - pinherit_sched = PTHREAD_INHERIT_SCHED; - break; - default: - UNREACHABLE_MSG("Invalid inheritSched: {}", inheritSched); - } - - int result = pthread_attr_setinheritsched(&(*attr)->pth_attr, pinherit_sched); - - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrGetschedparam(const ScePthreadAttr* attr, - SceKernelSchedParam* param) { - - if (param == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_attr_getschedparam(&(*attr)->pth_attr, param); - - if (param->sched_priority <= -2) { - param->sched_priority = 767; - } else if (param->sched_priority >= +2) { - param->sched_priority = 256; - } else { - param->sched_priority = 700; - } - - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrSetschedparam(ScePthreadAttr* attr, - const SceKernelSchedParam* param) { - if (param == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - SceKernelSchedParam pparam{}; - if (param->sched_priority <= 478) { - pparam.sched_priority = +2; - } else if (param->sched_priority >= 733) { - pparam.sched_priority = -2; - } else { - pparam.sched_priority = 0; - } - - // We always use SCHED_OTHER for now, so don't call this for now. - // int result = pthread_attr_setschedparam(&(*attr)->pth_attr, &pparam); - int result = 0; - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrGetschedpolicy(const ScePthreadAttr* attr, int* policy) { - if (policy == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_attr_getschedpolicy(&(*attr)->pth_attr, policy); - - switch (*policy) { - case SCHED_OTHER: - *policy = (*attr)->policy; - break; - case SCHED_FIFO: - *policy = 1; - break; - case SCHED_RR: - *policy = 3; - break; - default: - UNREACHABLE(); - } - - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrSetschedpolicy(ScePthreadAttr* attr, int policy) { - if (attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int ppolicy = SCHED_OTHER; // winpthreads only supports SCHED_OTHER - if (policy != SCHED_OTHER) { - LOG_ERROR(Kernel_Pthread, "policy={} not supported by winpthreads", policy); - } - - (*attr)->policy = policy; - int result = pthread_attr_setschedpolicy(&(*attr)->pth_attr, ppolicy); - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -ScePthread PS4_SYSV_ABI scePthreadSelf() { - return g_pthread_self; -} - -int PS4_SYSV_ABI scePthreadAttrSetaffinity(ScePthreadAttr* pattr, - const /*SceKernelCpumask*/ u64 mask) { - LOG_DEBUG(Kernel_Pthread, "called"); - - if (pattr == nullptr || *pattr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - (*pattr)->affinity = mask; - return SCE_OK; -} - -int PS4_SYSV_ABI scePthreadAttrGetaffinity(const ScePthreadAttr* pattr, - /* SceKernelCpumask*/ u64* mask) { - if (pattr == nullptr || *pattr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - *mask = (*pattr)->affinity; - - return SCE_OK; -} - -int PS4_SYSV_ABI scePthreadAttrGetstackaddr(const ScePthreadAttr* attr, void** stack_addr) { - - if (stack_addr == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - size_t stack_size = 0; - int result = pthread_attr_getstack(&(*attr)->pth_attr, stack_addr, &stack_size); - - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrGetstacksize(const ScePthreadAttr* attr, size_t* stack_size) { - - if (stack_size == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_attr_getstacksize(&(*attr)->pth_attr, stack_size); - - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrSetstackaddr(ScePthreadAttr* attr, void* addr) { - - if (addr == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - size_t stack_size = 0; - pthread_attr_getstacksize(&(*attr)->pth_attr, &stack_size); - - int result = pthread_attr_setstack(&(*attr)->pth_attr, addr, stack_size); - - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrSetstacksize(ScePthreadAttr* attr, size_t stack_size) { - - if (stack_size == 0 || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_attr_setstacksize(&(*attr)->pth_attr, stack_size); - - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI posix_pthread_attr_init(ScePthreadAttr* attr) { - int result = scePthreadAttrInit(attr); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_attr_setstacksize(ScePthreadAttr* attr, size_t stacksize) { - int result = scePthreadAttrSetstacksize(attr, stacksize); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI scePthreadSetaffinity(ScePthread thread, const /*SceKernelCpumask*/ u64 mask) { - LOG_DEBUG(Kernel_Pthread, "called"); - - if (thread == nullptr) { - return SCE_KERNEL_ERROR_ESRCH; - } - - auto result = scePthreadAttrSetaffinity(&thread->attr, mask); - - return result; -} - -int PS4_SYSV_ABI scePthreadGetaffinity(ScePthread thread, /*SceKernelCpumask*/ u64* mask) { - LOG_INFO(Kernel_Pthread, "called"); - - if (thread == nullptr) { - return SCE_KERNEL_ERROR_ESRCH; - } - - auto result = scePthreadAttrGetaffinity(&thread->attr, mask); - - return result; -} - -ScePthreadMutex* createMutex(ScePthreadMutex* addr) { - if (addr == nullptr || - (*addr != nullptr && *addr != ORBIS_PTHREAD_MUTEX_ADAPTIVE_INITIALIZER)) { - return addr; - } - - const VAddr vaddr = reinterpret_cast(addr); - std::string name = fmt::format("mutex{:#x}", vaddr); - scePthreadMutexInit(addr, nullptr, name.c_str()); - return addr; -} - -int PS4_SYSV_ABI scePthreadMutexInit(ScePthreadMutex* mutex, const ScePthreadMutexattr* mutex_attr, - const char* name) { - const ScePthreadMutexattr* attr; - - if (mutex == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - if (mutex_attr == nullptr || *mutex_attr == nullptr) { - if (*mutex == ORBIS_PTHREAD_MUTEX_ADAPTIVE_INITIALIZER) { - attr = g_pthread_cxt->getAdaptiveMutexattr(); - } else { - attr = g_pthread_cxt->getDefaultMutexattr(); - } - } else { - attr = mutex_attr; - } - - *mutex = new PthreadMutexInternal{}; - if (name != nullptr) { - (*mutex)->name = name; - } else { - (*mutex)->name = "nonameMutex"; - } - - int result = pthread_mutex_init(&(*mutex)->pth_mutex, &(*attr)->pth_mutex_attr); - - if (name != nullptr) { - LOG_INFO(Kernel_Pthread, "name={}, result={}", name, result); - } - - switch (result) { - case 0: - return SCE_OK; - case EAGAIN: - return SCE_KERNEL_ERROR_EAGAIN; - case EINVAL: - return SCE_KERNEL_ERROR_EINVAL; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadMutexDestroy(ScePthreadMutex* mutex) { - - if (mutex == nullptr || *mutex == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - if (*mutex == ORBIS_PTHREAD_MUTEX_ADAPTIVE_INITIALIZER) { - return ORBIS_OK; - } - - int result = pthread_mutex_destroy(&(*mutex)->pth_mutex); - - LOG_DEBUG(Kernel_Pthread, "name={}, result={}", (*mutex)->name, result); - - delete *mutex; - *mutex = nullptr; - - switch (result) { - case 0: - return SCE_OK; - case EBUSY: - return SCE_KERNEL_ERROR_EBUSY; - case EINVAL: - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} -int PS4_SYSV_ABI scePthreadMutexattrInit(ScePthreadMutexattr* attr) { - *attr = new PthreadMutexattrInternal{}; - - int result = pthread_mutexattr_init(&(*attr)->pth_mutex_attr); - - result = (result == 0 ? scePthreadMutexattrSettype(attr, 1) : result); - result = (result == 0 ? scePthreadMutexattrSetprotocol(attr, 0) : result); - - switch (result) { - case 0: - return SCE_OK; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadMutexattrSettype(ScePthreadMutexattr* attr, int type) { - int ptype = PTHREAD_MUTEX_DEFAULT; - switch (type) { - case ORBIS_PTHREAD_MUTEX_ERRORCHECK: - ptype = PTHREAD_MUTEX_ERRORCHECK; - break; - case ORBIS_PTHREAD_MUTEX_RECURSIVE: - ptype = PTHREAD_MUTEX_RECURSIVE; - break; - case ORBIS_PTHREAD_MUTEX_NORMAL: - ptype = PTHREAD_MUTEX_NORMAL; - break; - case ORBIS_PTHREAD_MUTEX_ADAPTIVE: - LOG_ERROR(Kernel_Pthread, "Unimplemented adaptive mutex"); - ptype = PTHREAD_MUTEX_ERRORCHECK; - break; - default: - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_mutexattr_settype(&(*attr)->pth_mutex_attr, ptype); - ASSERT(result == 0); - - return SCE_OK; -} - -int PS4_SYSV_ABI scePthreadMutexattrSetprotocol(ScePthreadMutexattr* attr, int protocol) { - int pprotocol = PTHREAD_PRIO_NONE; - switch (protocol) { - case 0: - pprotocol = PTHREAD_PRIO_NONE; - break; - case 1: - pprotocol = PTHREAD_PRIO_INHERIT; - break; - case 2: - pprotocol = PTHREAD_PRIO_PROTECT; - break; - default: - UNREACHABLE_MSG("Invalid protocol: {}", protocol); - } - -#if _WIN64 - int result = 0; -#else - int result = pthread_mutexattr_setprotocol(&(*attr)->pth_mutex_attr, pprotocol); -#endif - (*attr)->pprotocol = pprotocol; - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadMutexLock(ScePthreadMutex* mutex) { - mutex = createMutex(mutex); - if (mutex == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_mutex_lock(&(*mutex)->pth_mutex); - if (result != 0) { - LOG_TRACE(Kernel_Pthread, "Locked name={}, result={}", (*mutex)->name, result); - } - - switch (result) { - case 0: - return SCE_OK; - case EAGAIN: - return SCE_KERNEL_ERROR_EAGAIN; - case EINVAL: - return SCE_KERNEL_ERROR_EINVAL; - case EDEADLK: - return SCE_KERNEL_ERROR_EDEADLK; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadMutexUnlock(ScePthreadMutex* mutex) { - if (mutex == nullptr || *mutex == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_mutex_unlock(&(*mutex)->pth_mutex); - if (result != 0) { - LOG_TRACE(Kernel_Pthread, "Unlocking name={}, result={}", (*mutex)->name, result); - } - - switch (result) { - case 0: - return SCE_OK; - case EINVAL: - return SCE_KERNEL_ERROR_EINVAL; - case EPERM: - return SCE_KERNEL_ERROR_EPERM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadMutexattrDestroy(ScePthreadMutexattr* attr) { - int result = pthread_mutexattr_destroy(&(*attr)->pth_mutex_attr); - - delete *attr; - *attr = nullptr; - - switch (result) { - case 0: - return SCE_OK; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -ScePthreadCond* createCond(ScePthreadCond* addr) { - if (addr == nullptr || *addr != nullptr) { - return addr; - } - static std::mutex mutex; - std::scoped_lock lk{mutex}; - if (*addr != nullptr) { - return addr; - } - const VAddr vaddr = reinterpret_cast(addr); - std::string name = fmt::format("cond{:#x}", vaddr); - scePthreadCondInit(static_cast(addr), nullptr, name.c_str()); - return addr; -} - -int PS4_SYSV_ABI scePthreadCondInit(ScePthreadCond* cond, const ScePthreadCondattr* attr, - const char* name) { - if (cond == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - if (attr == nullptr) { - attr = g_pthread_cxt->getDefaultCondattr(); - } - - *cond = new PthreadCondInternal{}; - - if (name != nullptr) { - (*cond)->name = name; - } else { - (*cond)->name = "nonameCond"; - } - - int result = pthread_cond_init(&(*cond)->cond, &(*attr)->cond_attr); - - if (name != nullptr) { - LOG_TRACE(Kernel_Pthread, "name={}, result={}", (*cond)->name, result); - } - - switch (result) { - case 0: - return SCE_OK; - case EAGAIN: - return SCE_KERNEL_ERROR_EAGAIN; - case EINVAL: - return SCE_KERNEL_ERROR_EINVAL; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadCondattrInit(ScePthreadCondattr* attr) { - *attr = new PthreadCondAttrInternal{}; - - int result = pthread_condattr_init(&(*attr)->cond_attr); - - switch (result) { - case 0: - return SCE_OK; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadCondBroadcast(ScePthreadCond* cond) { - cond = createCond(cond); - if (cond == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_cond_broadcast(&(*cond)->cond); - - LOG_TRACE(Kernel_Pthread, "called name={}, result={}", (*cond)->name, result); - - return (result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL); -} - -int PS4_SYSV_ABI scePthreadCondTimedwait(ScePthreadCond* cond, ScePthreadMutex* mutex, u64 usec) { - cond = createCond(cond); - if (cond == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - if (mutex == nullptr || *mutex == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - timespec time{}; - time.tv_sec = usec / 1000000; - time.tv_nsec = ((usec % 1000000) * 1000); - int result = pthread_cond_timedwait(&(*cond)->cond, &(*mutex)->pth_mutex, &time); - - // LOG_INFO(Kernel_Pthread, "scePthreadCondTimedwait, result={}", result); - - switch (result) { - case 0: - return SCE_OK; - case ETIMEDOUT: - return SCE_KERNEL_ERROR_ETIMEDOUT; - case EINTR: - return SCE_KERNEL_ERROR_EINTR; - case EAGAIN: - return SCE_KERNEL_ERROR_EAGAIN; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadCondDestroy(ScePthreadCond* cond) { - if (cond == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - int result = pthread_cond_destroy(&(*cond)->cond); - - LOG_DEBUG(Kernel_Pthread, "scePthreadCondDestroy, result={}", result); - - delete *cond; - *cond = nullptr; - - switch (result) { - case 0: - return SCE_OK; - case EBUSY: - return SCE_KERNEL_ERROR_EBUSY; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI posix_pthread_mutex_init(ScePthreadMutex* mutex, const ScePthreadMutexattr* attr) { - // LOG_INFO(Kernel_Pthread, "posix pthread_mutex_init redirect to scePthreadMutexInit"); - int result = scePthreadMutexInit(mutex, attr, nullptr); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_mutex_lock(ScePthreadMutex* mutex) { - // LOG_INFO(Kernel_Pthread, "posix pthread_mutex_lock redirect to scePthreadMutexLock"); - int result = scePthreadMutexLock(mutex); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_mutex_unlock(ScePthreadMutex* mutex) { - // LOG_INFO(Kernel_Pthread, "posix pthread_mutex_unlock redirect to scePthreadMutexUnlock"); - int result = scePthreadMutexUnlock(mutex); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_mutex_destroy(ScePthreadMutex* mutex) { - int result = scePthreadMutexDestroy(mutex); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_cond_wait(ScePthreadCond* cond, ScePthreadMutex* mutex) { - int result = scePthreadCondWait(cond, mutex); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_cond_timedwait(ScePthreadCond* cond, ScePthreadMutex* mutex, - u64 usec) { - int result = scePthreadCondTimedwait(cond, mutex, usec); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_cond_broadcast(ScePthreadCond* cond) { - int result = scePthreadCondBroadcast(cond); - if (result != 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_mutexattr_init(ScePthreadMutexattr* attr) { - int result = scePthreadMutexattrInit(attr); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_mutexattr_settype(ScePthreadMutexattr* attr, int type) { - int result = scePthreadMutexattrSettype(attr, type); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_mutexattr_destroy(ScePthreadMutexattr* attr) { - int result = scePthreadMutexattrDestroy(attr); - if (result < 0) { - UNREACHABLE(); - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_once(pthread_once_t* once_control, void (*init_routine)(void)) { - return pthread_once(once_control, init_routine); -} - -int PS4_SYSV_ABI posix_pthread_mutexattr_setprotocol(ScePthreadMutexattr* attr, int protocol) { - int result = scePthreadMutexattrSetprotocol(attr, protocol); - if (result < 0) { - UNREACHABLE(); - } - return result; -} - -#ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK -static int pthread_mutex_timedlock(pthread_mutex_t* mutex, const struct timespec* abstime) { - int rc; - while ((rc = pthread_mutex_trylock(mutex)) == EBUSY) { - struct timespec curr_time; - clock_gettime(CLOCK_REALTIME, &curr_time); - - s64 remaining_ns = 0; - remaining_ns += - (static_cast(abstime->tv_sec) - static_cast(curr_time.tv_sec)) * 1000000000L; - remaining_ns += static_cast(abstime->tv_nsec) - static_cast(curr_time.tv_nsec); - - if (remaining_ns <= 0) { - return ETIMEDOUT; - } - - struct timespec sleep_time; - sleep_time.tv_sec = 0; - if (remaining_ns < 5000000L) { - sleep_time.tv_nsec = remaining_ns; - } else { - sleep_time.tv_nsec = 5000000; - } - - nanosleep(&sleep_time, nullptr); - } - - return rc; -} -#endif - -int PS4_SYSV_ABI scePthreadMutexTimedlock(ScePthreadMutex* mutex, u64 usec) { - mutex = createMutex(mutex); - if (mutex == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - timespec time{}; - time.tv_sec = usec / 1000000; - time.tv_nsec = ((usec % 1000000) * 1000); - int result = pthread_mutex_timedlock(&(*mutex)->pth_mutex, &time); - - switch (result) { - case 0: - return SCE_OK; - case ETIMEDOUT: - return SCE_KERNEL_ERROR_ETIMEDOUT; - case EINTR: - return SCE_KERNEL_ERROR_EINTR; - case EAGAIN: - return SCE_KERNEL_ERROR_EAGAIN; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -static int pthread_copy_attributes(ScePthreadAttr* dst, const ScePthreadAttr* src) { - if (dst == nullptr || *dst == nullptr || src == nullptr || *src == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - u64 mask = 0; - int state = 0; - size_t guard_size = 0; - int inherit_sched = 0; - SceKernelSchedParam param = {}; - int policy = 0; - void* stack_addr = nullptr; - size_t stack_size = 0; - - int result = 0; - - result = (result == 0 ? scePthreadAttrGetaffinity(src, &mask) : result); - result = (result == 0 ? scePthreadAttrGetdetachstate(src, &state) : result); - result = (result == 0 ? scePthreadAttrGetguardsize(src, &guard_size) : result); - result = (result == 0 ? scePthreadAttrGetinheritsched(src, &inherit_sched) : result); - result = (result == 0 ? scePthreadAttrGetschedparam(src, ¶m) : result); - result = (result == 0 ? scePthreadAttrGetschedpolicy(src, &policy) : result); - result = (result == 0 ? scePthreadAttrGetstackaddr(src, &stack_addr) : result); - result = (result == 0 ? scePthreadAttrGetstacksize(src, &stack_size) : result); - - result = (result == 0 ? scePthreadAttrSetaffinity(dst, mask) : result); - result = (result == 0 ? scePthreadAttrSetdetachstate(dst, state) : result); - result = (result == 0 ? scePthreadAttrSetguardsize(dst, guard_size) : result); - result = (result == 0 ? scePthreadAttrSetinheritsched(dst, inherit_sched) : result); - result = (result == 0 ? scePthreadAttrSetschedparam(dst, ¶m) : result); - result = (result == 0 ? scePthreadAttrSetschedpolicy(dst, policy) : result); - if (stack_addr != nullptr) { - result = (result == 0 ? scePthreadAttrSetstackaddr(dst, stack_addr) : result); - } - if (stack_size != 0) { - result = (result == 0 ? scePthreadAttrSetstacksize(dst, stack_size) : result); - } - - return result; -} - -int PS4_SYSV_ABI scePthreadAttrGet(ScePthread thread, ScePthreadAttr* attr) { - if (thread == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - return pthread_copy_attributes(attr, &thread->attr); -} - -static void cleanup_thread(void* arg) { - auto* thread = static_cast(arg); - for (const auto& [key, destructor] : thread->key_destructors) { - if (void* value = pthread_getspecific(key); value != nullptr) { - destructor(value); - } - } - Core::SetTcbBase(nullptr); - thread->is_almost_done = true; - DebugState.RemoveCurrentThreadFromGuestList(); -} - -static void* run_thread(void* arg) { - auto* thread = static_cast(arg); - Common::SetCurrentThreadName(thread->name.c_str()); - const auto* linker = Common::Singleton::Instance(); - void* ret = nullptr; - g_pthread_self = thread; - pthread_cleanup_push(cleanup_thread, thread); - thread->is_started = true; - DebugState.AddCurrentThreadToGuestList(); - ret = linker->ExecuteGuest(thread->entry, thread->arg); - pthread_cleanup_pop(1); - return ret; -} - -int PS4_SYSV_ABI scePthreadCreate(ScePthread* thread, const ScePthreadAttr* attr, - PthreadEntryFunc start_routine, void* arg, const char* name) { - if (thread == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - auto* pthread_pool = g_pthread_cxt->GetPthreadPool(); - - if (attr == nullptr) { - attr = g_pthread_cxt->GetDefaultAttr(); - } - - *thread = pthread_pool->Create(name); - - if ((*thread)->attr != nullptr) { - scePthreadAttrDestroy(&(*thread)->attr); - } - scePthreadAttrInit(&(*thread)->attr); - - int result = pthread_copy_attributes(&(*thread)->attr, attr); - ASSERT(result == 0); - - if (name != NULL) { - (*thread)->name = name; - } else { - (*thread)->name = "no-name"; - } - (*thread)->entry = start_routine; - (*thread)->arg = arg; - (*thread)->is_almost_done = false; - (*thread)->is_detached = (*attr)->detached; - (*thread)->is_started = false; - - pthread_attr_setstacksize(&(*attr)->pth_attr, 2_MB); - result = pthread_create(&(*thread)->pth, &(*attr)->pth_attr, run_thread, *thread); - - LOG_INFO(Kernel_Pthread, "thread create name = {}", (*thread)->name); - - switch (result) { - case 0: - return SCE_OK; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - case EAGAIN: - return SCE_KERNEL_ERROR_EAGAIN; - case EDEADLK: - return SCE_KERNEL_ERROR_EDEADLK; - case EPERM: - return SCE_KERNEL_ERROR_EPERM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -ScePthread PThreadPool::Create(const char* name) { - std::scoped_lock lock{m_mutex}; - - for (auto* p : m_threads) { - if (p->is_free && name != nullptr && p->name == name) { - p->is_free = false; - return p; - } - } - - auto* ret = new PthreadInternal{}; - ret->is_free = false; - ret->is_detached = false; - ret->is_almost_done = false; - ret->attr = nullptr; - - m_threads.push_back(ret); - - return ret; -} - -void PS4_SYSV_ABI scePthreadYield() { - sched_yield(); -} - -void PS4_SYSV_ABI posix_pthread_yield() { - sched_yield(); -} - -int PS4_SYSV_ABI scePthreadAttrGetstack(ScePthreadAttr* attr, void** addr, size_t* size) { - - int result = pthread_attr_getstack(&(*attr)->pth_attr, addr, size); - LOG_INFO(Kernel_Pthread, "scePthreadAttrGetstack: result = {}", result); - - if (result == 0) { - return SCE_OK; - } - return SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrSetstack(ScePthreadAttr* attr, void* addr, size_t size) { - if (attr == nullptr || *attr == nullptr || addr == nullptr || size < 0x4000) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - int result = pthread_attr_setstack(&(*attr)->pth_attr, addr, size); - LOG_INFO(Kernel_Pthread, "scePthreadAttrSetstack: result = {}", result); - - if (result == 0) { - return ORBIS_OK; - } - return ORBIS_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadJoin(ScePthread thread, void** res) { - int result = pthread_join(thread->pth, res); - LOG_INFO(Kernel_Pthread, "scePthreadJoin result = {}", result); - thread->is_detached = false; - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_join(ScePthread thread, void** res) { - int result = pthread_join(thread->pth, res); - LOG_INFO(Kernel_Pthread, "posix_pthread_join result = {}", result); - thread->is_detached = false; - return ORBIS_OK; -} - -int PS4_SYSV_ABI scePthreadDetach(ScePthread thread) { - thread->is_detached = true; - return ORBIS_OK; -} - -ScePthread PS4_SYSV_ABI posix_pthread_self() { - return g_pthread_self; -} - -int PS4_SYSV_ABI scePthreadCondSignal(ScePthreadCond* cond) { - cond = createCond(cond); - if (cond == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_cond_signal(&(*cond)->cond); - - // LOG_INFO(Kernel_Pthread, "scePthreadCondSignal, result={}", result); - - switch (result) { - case 0: - return SCE_OK; - case EBUSY: - return SCE_KERNEL_ERROR_EBUSY; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadCondWait(ScePthreadCond* cond, ScePthreadMutex* mutex) { - cond = createCond(cond); - if (cond == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - if (mutex == nullptr || *mutex == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - int result = pthread_cond_wait(&(*cond)->cond, &(*mutex)->pth_mutex); - - LOG_DEBUG(Kernel_Pthread, "scePthreadCondWait, result={}", result); - - switch (result) { - case 0: - return SCE_OK; - case EINTR: - return SCE_KERNEL_ERROR_EINTR; - case EAGAIN: - return SCE_KERNEL_ERROR_EAGAIN; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadCondattrDestroy(ScePthreadCondattr* attr) { - if (attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - int result = pthread_condattr_destroy(&(*attr)->cond_attr); - - LOG_DEBUG(Kernel_Pthread, "scePthreadCondattrDestroy: result = {} ", result); - delete *attr; - - switch (result) { - case 0: - return SCE_OK; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadMutexTrylock(ScePthreadMutex* mutex) { - mutex = createMutex(mutex); - if (mutex == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - - int result = pthread_mutex_trylock(&(*mutex)->pth_mutex); - if (result != 0) { - LOG_TRACE(Kernel_Pthread, "name={}, result={}", (*mutex)->name, result); - } - - switch (result) { - case 0: - return ORBIS_OK; - case EAGAIN: - return ORBIS_KERNEL_ERROR_EAGAIN; - case EBUSY: - return ORBIS_KERNEL_ERROR_EBUSY; - case EINVAL: - default: - return ORBIS_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadEqual(ScePthread thread1, ScePthread thread2) { - return (thread1 == thread2 ? 1 : 0); -} - -int PS4_SYSV_ABI posix_pthread_equal(ScePthread thread1, ScePthread thread2) { - return (thread1 == thread2 ? 1 : 0); -} - -struct TlsIndex { - u64 ti_module; - u64 ti_offset; -}; - -void* PS4_SYSV_ABI __tls_get_addr(TlsIndex* index) { - auto* linker = Common::Singleton::Instance(); - return linker->TlsGetAddr(index->ti_module, index->ti_offset); -} - -int PS4_SYSV_ABI posix_sched_get_priority_max() { - return ORBIS_KERNEL_PRIO_FIFO_HIGHEST; -} - -int PS4_SYSV_ABI posix_sched_get_priority_min() { - return ORBIS_KERNEL_PRIO_FIFO_LOWEST; -} - -int PS4_SYSV_ABI posix_pthread_mutex_trylock(ScePthreadMutex* mutex) { - int result = scePthreadMutexTrylock(mutex); - if (result < 0) { - // UNREACHABLE(); - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_attr_destroy(ScePthreadAttr* attr) { - int result = scePthreadAttrDestroy(attr); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_attr_setschedparam(ScePthreadAttr* attr, - const SceKernelSchedParam* param) { - int result = scePthreadAttrSetschedparam(attr, param); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_attr_setinheritsched(ScePthreadAttr* attr, int inheritSched) { - int result = scePthreadAttrSetinheritsched(attr, inheritSched); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_setprio(ScePthread thread, int prio) { - int result = scePthreadSetprio(thread, prio); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_attr_setdetachstate(ScePthreadAttr* attr, int detachstate) { - // LOG_INFO(Kernel_Pthread, "posix pthread_mutexattr_init redirect to scePthreadMutexattrInit"); - int result = scePthreadAttrSetdetachstate(attr, detachstate); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_create_name_np(ScePthread* thread, const ScePthreadAttr* attr, - PthreadEntryFunc start_routine, void* arg, - const char* name) { - int result = scePthreadCreate(thread, attr, start_routine, arg, name); - if (result != 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_create(ScePthread* thread, const ScePthreadAttr* attr, - PthreadEntryFunc start_routine, void* arg) { - return posix_pthread_create_name_np(thread, attr, start_routine, arg, "NoName"); -} - -using Destructor = void (*)(void*); - -int PS4_SYSV_ABI posix_pthread_key_create(u32* key, Destructor func) { - pthread_key_t thread_key; - int rc = pthread_key_create(&thread_key, func); - *key = static_cast(thread_key); - return rc; -} - -int PS4_SYSV_ABI posix_pthread_setspecific(int key, const void* value) { - return pthread_setspecific(key, value); -} - -void* PS4_SYSV_ABI posix_pthread_getspecific(int key) { - return pthread_getspecific(key); -} - -int PS4_SYSV_ABI posix_pthread_cond_init(ScePthreadCond* cond, const ScePthreadCondattr* attr) { - // LOG_INFO(Kernel_Pthread, "posix pthread_mutex_init redirect to scePthreadMutexInit"); - int result = scePthreadCondInit(cond, attr, "NoName"); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_cond_signal(ScePthreadCond* cond) { - int result = scePthreadCondSignal(cond); - return result; -} - -int PS4_SYSV_ABI posix_pthread_cond_destroy(ScePthreadCond* cond) { - int result = scePthreadCondDestroy(cond); - return result; -} - -int PS4_SYSV_ABI posix_pthread_setcancelstate(int state, int* oldstate) { - return pthread_setcancelstate(state, oldstate); -} - -int PS4_SYSV_ABI posix_pthread_detach(ScePthread thread) { - return pthread_detach(thread->pth); -} - -int PS4_SYSV_ABI posix_sem_init(PthreadSemInternal** sem, int pshared, unsigned int value) { - if (value > ORBIS_KERNEL_SEM_VALUE_MAX) { - SetPosixErrno(EINVAL); - return -1; - } - if (sem != nullptr) { - *sem = new PthreadSemInternal{ - .semaphore = std::counting_semaphore{value}, - .value = {static_cast(value)}, - }; - } - return 0; -} - -int PS4_SYSV_ABI posix_sem_wait(PthreadSemInternal** sem) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; - } - (*sem)->semaphore.acquire(); - --(*sem)->value; - return 0; -} - -int PS4_SYSV_ABI posix_sem_trywait(PthreadSemInternal** sem) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; - } - if (!(*sem)->semaphore.try_acquire()) { - SetPosixErrno(EAGAIN); - return -1; - } - --(*sem)->value; - return 0; -} - -int PS4_SYSV_ABI posix_sem_timedwait(PthreadSemInternal** sem, const timespec* t) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; - } - - using std::chrono::duration_cast; - using std::chrono::nanoseconds; - using std::chrono::seconds; - using std::chrono::system_clock; - - const system_clock::time_point time{ - duration_cast(seconds{t->tv_sec} + nanoseconds{t->tv_nsec})}; - if (!(*sem)->semaphore.try_acquire_until(time)) { - SetPosixErrno(ETIMEDOUT); - return -1; - } - --(*sem)->value; - return 0; -} - -int PS4_SYSV_ABI posix_sem_post(PthreadSemInternal** sem) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; - } - if ((*sem)->value == ORBIS_KERNEL_SEM_VALUE_MAX) { - SetPosixErrno(EOVERFLOW); - return -1; - } - ++(*sem)->value; - (*sem)->semaphore.release(); - return 0; -} - -int PS4_SYSV_ABI posix_sem_destroy(PthreadSemInternal** sem) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; - } - delete *sem; - *sem = nullptr; - return 0; -} - -int PS4_SYSV_ABI posix_sem_getvalue(PthreadSemInternal** sem, int* sval) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; - } - if (sval) { - *sval = (*sem)->value; - } - return 0; -} - -int PS4_SYSV_ABI posix_pthread_attr_getstacksize(const pthread_attr_t* attr, size_t* size) { - return pthread_attr_getstacksize(attr, size); -} - -int PS4_SYSV_ABI scePthreadGetschedparam(ScePthread thread, int* policy, - SceKernelSchedParam* param) { - return pthread_getschedparam(thread->pth, policy, param); -} - -int PS4_SYSV_ABI scePthreadSetschedparam(ScePthread thread, int policy, - const SceKernelSchedParam* param) { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called policy={}, sched_priority={}", policy, - param->sched_priority); - return ORBIS_OK; -} - -int PS4_SYSV_ABI scePthreadOnce(int* once_control, void (*init_routine)(void)) { - return pthread_once(reinterpret_cast(once_control), init_routine); -} - -[[noreturn]] void PS4_SYSV_ABI scePthreadExit(void* value_ptr) { - g_pthread_self->is_free = true; - - pthread_exit(value_ptr); - UNREACHABLE(); -} - -[[noreturn]] void PS4_SYSV_ABI posix_pthread_exit(void* value_ptr) { - pthread_exit(value_ptr); - UNREACHABLE(); -} - -int PS4_SYSV_ABI scePthreadGetthreadid() { - return (int)(size_t)g_pthread_self; -} - -int PS4_SYSV_ABI scePthreadGetprio(ScePthread thread, int* prio) { - *prio = thread->prio; - return ORBIS_OK; -} -int PS4_SYSV_ABI scePthreadSetprio(ScePthread thread, int prio) { - if (thread == nullptr) { - LOG_ERROR(Kernel_Pthread, "scePthreadSetprio: thread is nullptr"); - return ORBIS_KERNEL_ERROR_EINVAL; - } - thread->prio = prio; - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_condattr_init(ScePthreadCondattr* attr) { - int result = scePthreadCondattrInit(attr); - LOG_INFO(Kernel_Pthread, - "posix_pthread_condattr_init redirect to scePthreadCondattrInit, result = {}", result); - return result; -} - -int PS4_SYSV_ABI posix_pthread_condattr_destroy(ScePthreadCondattr* attr) { - int result = scePthreadCondattrDestroy(attr); - LOG_INFO(Kernel_Pthread, - "posix_pthread_condattr_destroy redirect to scePthreadCondattrDestroy, result = {}", - result); - return result; -} - -int PS4_SYSV_ABI posix_pthread_condattr_setclock(ScePthreadCondattr* attr, clockid_t clock) { - (*attr)->clock = clock; - return SCE_OK; -} - -int PS4_SYSV_ABI posix_pthread_getschedparam(ScePthread thread, int* policy, - SceKernelSchedParam* param) { - return scePthreadGetschedparam(thread, policy, param); -} - -int PS4_SYSV_ABI posix_pthread_setschedparam(ScePthread thread, int policy, - const SceKernelSchedParam* param) { - return scePthreadSetschedparam(thread, policy, param); -} - -int PS4_SYSV_ABI posix_pthread_attr_getschedpolicy(const ScePthreadAttr* attr, int* policy) { - return scePthreadAttrGetschedpolicy(attr, policy); -} - -int PS4_SYSV_ABI scePthreadRename(ScePthread thread, const char* name) { - thread->name = name; - LOG_INFO(Kernel_Pthread, "scePthreadRename: name = {}", thread->name); - return SCE_OK; -} - -void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym) { - LIB_FUNCTION("lZzFeSxPl08", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setcancelstate); - LIB_FUNCTION("0TyVk4MSLt0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_init); - LIB_FUNCTION("2MOy+rUfuhQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_signal); - LIB_FUNCTION("RXXqi4CtF8w", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_destroy); - LIB_FUNCTION("mqULNdimTn0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_key_create); - LIB_FUNCTION("0-KXaS70xy4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_getspecific); - LIB_FUNCTION("WrOLvHU0yQM", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setspecific); - LIB_FUNCTION("4+h9EzwKF4I", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetschedpolicy); - LIB_FUNCTION("-Wreprtu0Qs", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetdetachstate); - LIB_FUNCTION("JaRMy+QcpeU", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetdetachstate); - LIB_FUNCTION("eXbUSpEaTsA", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetinheritsched); - LIB_FUNCTION("DzES9hQF4f4", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetschedparam); - LIB_FUNCTION("nsYoNRywwNg", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrInit); - LIB_FUNCTION("62KCwEMmzcM", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrDestroy); - LIB_FUNCTION("onNY9Byn-W8", "libkernel", 1, "libkernel", 1, 1, scePthreadJoin); - LIB_FUNCTION("4qGrR6eoP9Y", "libkernel", 1, "libkernel", 1, 1, scePthreadDetach); - LIB_FUNCTION("3PtV6p3QNX4", "libkernel", 1, "libkernel", 1, 1, scePthreadEqual); - LIB_FUNCTION("3kg7rT0NQIs", "libkernel", 1, "libkernel", 1, 1, scePthreadExit); - LIB_FUNCTION("FJrT5LuUBAU", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_exit); - LIB_FUNCTION("7Xl257M4VNI", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_equal); - LIB_FUNCTION("h9CcP3J0oVM", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_join); - LIB_FUNCTION("EI-5-jlq2dE", "libkernel", 1, "libkernel", 1, 1, scePthreadGetthreadid); - LIB_FUNCTION("1tKyG7RlMJo", "libkernel", 1, "libkernel", 1, 1, scePthreadGetprio); - LIB_FUNCTION("W0Hpm2X0uPE", "libkernel", 1, "libkernel", 1, 1, scePthreadSetprio); - LIB_FUNCTION("GBUY7ywdULE", "libkernel", 1, "libkernel", 1, 1, scePthreadRename); - LIB_FUNCTION("F+yfmduIBB8", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetstackaddr); - LIB_FUNCTION("El+cQ20DynU", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetguardsize); - - LIB_FUNCTION("aI+OeCz8xrQ", "libkernel", 1, "libkernel", 1, 1, scePthreadSelf); - LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self); - LIB_FUNCTION("EotR8a3ASf4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_self); - LIB_FUNCTION("3qxgM4ezETA", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetaffinity); - LIB_FUNCTION("8+s5BzZjxSg", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetaffinity); - LIB_FUNCTION("x1X76arYMxU", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGet); - LIB_FUNCTION("FXPWHNk8Of0", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetschedparam); - LIB_FUNCTION("P41kTWUS3EI", "libkernel", 1, "libkernel", 1, 1, scePthreadGetschedparam); - LIB_FUNCTION("oIRFTjoILbg", "libkernel", 1, "libkernel", 1, 1, scePthreadSetschedparam); - LIB_FUNCTION("UTXzJbWhhTE", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetstacksize); - LIB_FUNCTION("vNe1w4diLCs", "libkernel", 1, "libkernel", 1, 1, __tls_get_addr); - LIB_FUNCTION("OxhIB8LB-PQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_create); - LIB_FUNCTION("OxhIB8LB-PQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create); - LIB_FUNCTION("bt3CTBKmGyI", "libkernel", 1, "libkernel", 1, 1, scePthreadSetaffinity); - LIB_FUNCTION("rcrVFJsQWRY", "libkernel", 1, "libkernel", 1, 1, scePthreadGetaffinity); - LIB_FUNCTION("6UgtwV+0zb4", "libkernel", 1, "libkernel", 1, 1, scePthreadCreate); - LIB_FUNCTION("T72hz6ffq08", "libkernel", 1, "libkernel", 1, 1, scePthreadYield); - LIB_FUNCTION("B5GmVDKwpn0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_yield); - - LIB_FUNCTION("-quPa4SEJUw", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetstack); - LIB_FUNCTION("Bvn74vj6oLo", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetstack); - LIB_FUNCTION("Ru36fiTtJzA", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetstackaddr); - LIB_FUNCTION("-fA+7ZlGDQs", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetstacksize); - LIB_FUNCTION("14bOACANTBo", "libkernel", 1, "libkernel", 1, 1, scePthreadOnce); - - // mutex calls - LIB_FUNCTION("cmo1RIYva9o", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexInit); - LIB_FUNCTION("2Of0f+3mhhE", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexDestroy); - LIB_FUNCTION("F8bUHwAG284", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexattrInit); - LIB_FUNCTION("smWEktiyyG0", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexattrDestroy); - LIB_FUNCTION("iMp8QpE+XO4", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexattrSettype); - LIB_FUNCTION("1FGvU0i9saQ", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexattrSetprotocol); - LIB_FUNCTION("9UK1vLZQft4", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexLock); - LIB_FUNCTION("tn3VlD0hG60", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexUnlock); - LIB_FUNCTION("upoVrzMHFeE", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexTrylock); - LIB_FUNCTION("IafI2PxcPnQ", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexTimedlock); - - // scePthreadMutexInitForInternalLibc, scePthreadMutexattrInitForInternalLibc - LIB_FUNCTION("qH1gXoq71RY", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexInit); - LIB_FUNCTION("n2MMpvU8igI", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexattrInit); - - // cond calls - LIB_FUNCTION("2Tb92quprl0", "libkernel", 1, "libkernel", 1, 1, scePthreadCondInit); - LIB_FUNCTION("m5-2bsNfv7s", "libkernel", 1, "libkernel", 1, 1, scePthreadCondattrInit); - LIB_FUNCTION("JGgj7Uvrl+A", "libkernel", 1, "libkernel", 1, 1, scePthreadCondBroadcast); - LIB_FUNCTION("WKAXJ4XBPQ4", "libkernel", 1, "libkernel", 1, 1, scePthreadCondWait); - LIB_FUNCTION("waPcxYiR3WA", "libkernel", 1, "libkernel", 1, 1, scePthreadCondattrDestroy); - LIB_FUNCTION("kDh-NfxgMtE", "libkernel", 1, "libkernel", 1, 1, scePthreadCondSignal); - LIB_FUNCTION("BmMjYxmew1w", "libkernel", 1, "libkernel", 1, 1, scePthreadCondTimedwait); - LIB_FUNCTION("g+PZd2hiacg", "libkernel", 1, "libkernel", 1, 1, scePthreadCondDestroy); - - // posix calls - LIB_FUNCTION("wtkt-teR1so", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_attr_init); - LIB_FUNCTION("2Q0z6rnBrTE", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_attr_setstacksize); - LIB_FUNCTION("ttHNfU+qDBU", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_init); - LIB_FUNCTION("7H0iTOciTLo", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_lock); - LIB_FUNCTION("2Z+PpY6CaJg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_unlock); - LIB_FUNCTION("ltCfaGr2JGE", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_destroy); - LIB_FUNCTION("Op8TBGY5KHg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_cond_wait); - LIB_FUNCTION("Op8TBGY5KHg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_wait); - LIB_FUNCTION("27bAgiJmOh0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_timedwait); - LIB_FUNCTION("mkx2fVhNMsg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_broadcast); - LIB_FUNCTION("dQHWEsJtoE4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutexattr_init); - LIB_FUNCTION("mDmgMOGVUqg", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_mutexattr_settype); - LIB_FUNCTION("5txKfcMUAok", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_mutexattr_setprotocol); - LIB_FUNCTION("HF7lK46xzjY", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_mutexattr_destroy); - LIB_FUNCTION("mKoTx03HRWA", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_condattr_init); - LIB_FUNCTION("dJcuQVn6-Iw", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_condattr_destroy); - LIB_FUNCTION("EjllaAqAPZo", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_condattr_setclock); - LIB_FUNCTION("Z4QosVuAsA0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_once); - LIB_FUNCTION("RtLRV-pBTTY", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_attr_getschedpolicy); - - // openorbis weird functions - LIB_FUNCTION("7H0iTOciTLo", "libkernel", 1, "libkernel", 1, 1, posix_pthread_mutex_lock); - LIB_FUNCTION("2Z+PpY6CaJg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_mutex_unlock); - LIB_FUNCTION("mkx2fVhNMsg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_cond_broadcast); - LIB_FUNCTION("K-jXhbt2gn4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_trylock); - LIB_FUNCTION("E+tyo3lp5Lw", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_attr_setdetachstate); - LIB_FUNCTION("zHchY8ft5pk", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_attr_destroy); - LIB_FUNCTION("euKRgm0Vn2M", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_attr_setschedparam); - LIB_FUNCTION("7ZlAakEf0Qg", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_attr_setinheritsched); - LIB_FUNCTION("a2P9wYGeZvc", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setprio); - LIB_FUNCTION("Jmi+9w9u0E4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create_name_np); - LIB_FUNCTION("OxhIB8LB-PQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create); - LIB_FUNCTION("+U1R4WtXvoc", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_detach); - LIB_FUNCTION("CBNtXOoef-E", "libScePosix", 1, "libkernel", 1, 1, posix_sched_get_priority_max); - LIB_FUNCTION("m0iS6jNsXds", "libScePosix", 1, "libkernel", 1, 1, posix_sched_get_priority_min); - LIB_FUNCTION("FIs3-UQT9sg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_getschedparam); - LIB_FUNCTION("Xs9hdiD7sAA", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setschedparam); - LIB_FUNCTION("pDuPEf3m4fI", "libScePosix", 1, "libkernel", 1, 1, posix_sem_init); - LIB_FUNCTION("YCV5dGGBcCo", "libScePosix", 1, "libkernel", 1, 1, posix_sem_wait); - LIB_FUNCTION("WBWzsRifCEA", "libScePosix", 1, "libkernel", 1, 1, posix_sem_trywait); - LIB_FUNCTION("w5IHyvahg-o", "libScePosix", 1, "libkernel", 1, 1, posix_sem_timedwait); - LIB_FUNCTION("IKP8typ0QUk", "libScePosix", 1, "libkernel", 1, 1, posix_sem_post); - LIB_FUNCTION("cDW233RAwWo", "libScePosix", 1, "libkernel", 1, 1, posix_sem_destroy); - LIB_FUNCTION("Bq+LRV-N6Hk", "libScePosix", 1, "libkernel", 1, 1, posix_sem_getvalue); - LIB_FUNCTION("0qOtCR-ZHck", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_attr_getstacksize); - // libs - RwlockSymbolsRegister(sym); - SemaphoreSymbolsRegister(sym); - KeySymbolsRegister(sym); -} - -} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/thread_management.h b/src/core/libraries/kernel/thread_management.h deleted file mode 100644 index 3cdb300f7..000000000 --- a/src/core/libraries/kernel/thread_management.h +++ /dev/null @@ -1,225 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include "common/types.h" - -#define ORBIS_PTHREAD_MUTEX_ADAPTIVE_INITIALIZER (reinterpret_cast(1)) - -namespace Core::Loader { -class SymbolsResolver; -} - -namespace Libraries::Kernel { -constexpr int ORBIS_KERNEL_PRIO_FIFO_DEFAULT = 700; -constexpr int ORBIS_KERNEL_PRIO_FIFO_HIGHEST = 256; -constexpr int ORBIS_KERNEL_PRIO_FIFO_LOWEST = 767; -constexpr int ORBIS_KERNEL_SEM_VALUE_MAX = 0x7FFFFFFF; - -constexpr int ORBIS_PTHREAD_MUTEX_ERRORCHECK = 1; -constexpr int ORBIS_PTHREAD_MUTEX_RECURSIVE = 2; -constexpr int ORBIS_PTHREAD_MUTEX_NORMAL = 3; -constexpr int ORBIS_PTHREAD_MUTEX_ADAPTIVE = 4; - -struct PthreadInternal; -struct PthreadAttrInternal; -struct PthreadMutexInternal; -struct PthreadMutexattrInternal; -struct PthreadCondInternal; -struct PthreadCondAttrInternal; -struct PthreadRwInternal; -struct PthreadRwLockAttrInternal; -class PthreadKeys; - -using SceKernelSchedParam = ::sched_param; -using ScePthread = PthreadInternal*; -using ScePthreadAttr = PthreadAttrInternal*; -using ScePthreadMutex = PthreadMutexInternal*; -using ScePthreadMutexattr = PthreadMutexattrInternal*; -using ScePthreadCond = PthreadCondInternal*; -using ScePthreadCondattr = PthreadCondAttrInternal*; -using OrbisPthreadRwlock = PthreadRwInternal*; -using OrbisPthreadRwlockattr = PthreadRwLockAttrInternal*; -using OrbisPthreadKey = u32; - -using PthreadKeyDestructor = PS4_SYSV_ABI void (*)(void*); -using PthreadEntryFunc = PS4_SYSV_ABI void* (*)(void*); - -struct PthreadInternal { - u8 reserved[4096]; - std::string name; - pthread_t pth; - ScePthreadAttr attr; - PthreadEntryFunc entry; - void* arg; - std::atomic_bool is_started; - std::atomic_bool is_detached; - std::atomic_bool is_almost_done; - std::atomic_bool is_free; - using Destructor = std::pair; - std::vector key_destructors; - int prio; -}; - -struct PthreadAttrInternal { - u8 reserved[64]; - u64 affinity; - size_t guard_size; - int policy; - bool detached; - pthread_attr_t pth_attr; -}; - -struct PthreadMutexInternal { - u8 reserved[256]; - std::string name; - pthread_mutex_t pth_mutex; -}; - -struct PthreadMutexattrInternal { - u8 reserved[64]; - pthread_mutexattr_t pth_mutex_attr; - int pprotocol; -}; - -struct PthreadCondInternal { - u8 reserved[256]; - std::string name; - pthread_cond_t cond; -}; - -struct PthreadCondAttrInternal { - u8 reserved[64]; - pthread_condattr_t cond_attr; - clockid_t clock; -}; - -struct PthreadRwLockAttrInternal { - u8 reserved[64]; - pthread_rwlockattr_t attr_rwlock; - int type; -}; - -struct PthreadRwInternal { - pthread_rwlock_t pth_rwlock; - std::string name; -}; - -struct PthreadSemInternal { - std::counting_semaphore semaphore; - std::atomic value; -}; - -class PThreadPool { -public: - ScePthread Create(const char* name); - -private: - std::vector m_threads; - std::mutex m_mutex; -}; - -class PThreadCxt { -public: - ScePthreadMutexattr* getDefaultMutexattr() { - return &m_default_mutexattr; - } - void setDefaultMutexattr(ScePthreadMutexattr attr) { - m_default_mutexattr = attr; - } - ScePthreadMutexattr* getAdaptiveMutexattr() { - return &m_adaptive_mutexattr; - } - void setAdaptiveMutexattr(ScePthreadMutexattr attr) { - m_adaptive_mutexattr = attr; - } - ScePthreadCondattr* getDefaultCondattr() { - return &m_default_condattr; - } - void setDefaultCondattr(ScePthreadCondattr attr) { - m_default_condattr = attr; - } - ScePthreadAttr* GetDefaultAttr() { - return &m_default_attr; - } - void SetDefaultAttr(ScePthreadAttr attr) { - m_default_attr = attr; - } - PThreadPool* GetPthreadPool() { - return m_pthread_pool; - } - void SetPthreadPool(PThreadPool* pool) { - m_pthread_pool = pool; - } - OrbisPthreadRwlockattr* getDefaultRwattr() { - return &m_default_Rwattr; - } - void setDefaultRwattr(OrbisPthreadRwlockattr attr) { - m_default_Rwattr = attr; - } - -private: - ScePthreadMutexattr m_default_mutexattr = nullptr; - ScePthreadMutexattr m_adaptive_mutexattr = nullptr; - ScePthreadCondattr m_default_condattr = nullptr; - ScePthreadAttr m_default_attr = nullptr; - PThreadPool* m_pthread_pool = nullptr; - OrbisPthreadRwlockattr m_default_Rwattr = nullptr; -}; - -void init_pthreads(); -void pthreadInitSelfMainThread(); - -int PS4_SYSV_ABI scePthreadAttrInit(ScePthreadAttr* attr); -int PS4_SYSV_ABI scePthreadAttrSetdetachstate(ScePthreadAttr* attr, int detachstate); -int PS4_SYSV_ABI scePthreadAttrSetinheritsched(ScePthreadAttr* attr, int inheritSched); -int PS4_SYSV_ABI scePthreadAttrSetschedparam(ScePthreadAttr* attr, - const SceKernelSchedParam* param); -int PS4_SYSV_ABI scePthreadAttrSetschedpolicy(ScePthreadAttr* attr, int policy); -ScePthread PS4_SYSV_ABI scePthreadSelf(); -int PS4_SYSV_ABI scePthreadAttrSetaffinity(ScePthreadAttr* pattr, - const /*SceKernelCpumask*/ u64 mask); -int PS4_SYSV_ABI scePthreadSetaffinity(ScePthread thread, const /*SceKernelCpumask*/ u64 mask); -int PS4_SYSV_ABI scePthreadGetaffinity(ScePthread thread, /*SceKernelCpumask*/ u64* mask); -int PS4_SYSV_ABI scePthreadCreate(ScePthread* thread, const ScePthreadAttr* attr, - PthreadEntryFunc start_routine, void* arg, const char* name); - -int PS4_SYSV_ABI scePthreadSetprio(ScePthread thread, int prio); - -/*** - * Mutex calls - */ -int PS4_SYSV_ABI scePthreadMutexInit(ScePthreadMutex* mutex, const ScePthreadMutexattr* attr, - const char* name); -int PS4_SYSV_ABI scePthreadMutexattrInit(ScePthreadMutexattr* attr); -int PS4_SYSV_ABI scePthreadMutexattrSettype(ScePthreadMutexattr* attr, int type); -int PS4_SYSV_ABI scePthreadMutexattrSetprotocol(ScePthreadMutexattr* attr, int protocol); -int PS4_SYSV_ABI scePthreadMutexLock(ScePthreadMutex* mutex); -int PS4_SYSV_ABI scePthreadMutexUnlock(ScePthreadMutex* mutex); -/**** - * Cond calls - */ -int PS4_SYSV_ABI scePthreadCondInit(ScePthreadCond* cond, const ScePthreadCondattr* attr, - const char* name); -int PS4_SYSV_ABI scePthreadCondattrInit(ScePthreadCondattr* attr); -int PS4_SYSV_ABI scePthreadCondBroadcast(ScePthreadCond* cond); -int PS4_SYSV_ABI scePthreadCondWait(ScePthreadCond* cond, ScePthreadMutex* mutex); -/**** - * Posix calls - */ -int PS4_SYSV_ABI posix_pthread_mutex_init(ScePthreadMutex* mutex, const ScePthreadMutexattr* attr); -int PS4_SYSV_ABI posix_pthread_mutex_lock(ScePthreadMutex* mutex); -int PS4_SYSV_ABI posix_pthread_mutex_unlock(ScePthreadMutex* mutex); -int PS4_SYSV_ABI posix_pthread_cond_broadcast(ScePthreadCond* cond); - -void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym); -} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads.cpp b/src/core/libraries/kernel/threads.cpp new file mode 100644 index 000000000..082a52b67 --- /dev/null +++ b/src/core/libraries/kernel/threads.cpp @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/libraries/kernel/kernel.h" +#include "core/libraries/kernel/threads.h" +#include "core/libraries/kernel/threads/pthread.h" + +namespace Libraries::Kernel { + +void RegisterThreads(Core::Loader::SymbolsResolver* sym) { + RegisterMutex(sym); + RegisterCond(sym); + RegisterRwlock(sym); + RegisterSemaphore(sym); + RegisterSpec(sym); + RegisterThreadAttr(sym); + RegisterThread(sym); + RegisterRtld(sym); + RegisterPthreadClean(sym); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads.h b/src/core/libraries/kernel/threads.h new file mode 100644 index 000000000..ad1393599 --- /dev/null +++ b/src/core/libraries/kernel/threads.h @@ -0,0 +1,72 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "common/polyfill_thread.h" +#include "core/libraries/kernel/threads/pthread.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::Kernel { + +int PS4_SYSV_ABI posix_pthread_attr_init(PthreadAttrT* attr); + +int PS4_SYSV_ABI posix_pthread_attr_destroy(PthreadAttrT* attr); + +int PS4_SYSV_ABI posix_pthread_create(PthreadT* thread, const PthreadAttrT* attr, + PthreadEntryFunc start_routine, void* arg); + +int PS4_SYSV_ABI posix_pthread_join(PthreadT pthread, void** thread_return); + +void RegisterThreads(Core::Loader::SymbolsResolver* sym); + +class Thread { +public: + explicit Thread() = default; + ~Thread() { + Stop(); + } + + void Run(std::function&& func) { + this->func = std::move(func); + PthreadAttrT attr{}; + posix_pthread_attr_init(&attr); + posix_pthread_create(&thread, &attr, RunWrapper, this); + posix_pthread_attr_destroy(&attr); + } + + void Join() { + if (thread) { + posix_pthread_join(thread, nullptr); + thread = nullptr; + } + } + + bool Joinable() const { + return thread != nullptr; + } + + void Stop() { + if (Joinable()) { + stop.request_stop(); + Join(); + } + } + + static void* PS4_SYSV_ABI RunWrapper(void* arg) { + Thread* thr = (Thread*)arg; + thr->func(thr->stop.get_token()); + return nullptr; + } + +private: + PthreadT thread{}; + std::function func; + std::stop_source stop; +}; + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/condvar.cpp b/src/core/libraries/kernel/threads/condvar.cpp new file mode 100644 index 000000000..2927899d9 --- /dev/null +++ b/src/core/libraries/kernel/threads/condvar.cpp @@ -0,0 +1,363 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include "common/assert.h" +#include "core/libraries/kernel/kernel.h" +#include "core/libraries/kernel/posix_error.h" +#include "core/libraries/kernel/threads/pthread.h" +#include "core/libraries/kernel/threads/sleepq.h" +#include "core/libraries/libs.h" + +namespace Libraries::Kernel { + +static std::mutex CondStaticLock; + +#define THR_COND_INITIALIZER ((PthreadCond*)NULL) +#define THR_COND_DESTROYED ((PthreadCond*)1) + +static constexpr PthreadCondAttr PhreadCondattrDefault = { + .c_pshared = 0, + .c_clockid = ClockId::Realtime, +}; + +static int CondInit(PthreadCondT* cond, const PthreadCondAttrT* cond_attr, const char* name) { + auto* cvp = new PthreadCond{}; + if (cvp == nullptr) { + return POSIX_ENOMEM; + } + + if (name) { + cvp->name = name; + } else { + static int CondId = 0; + cvp->name = fmt::format("Cond{}", CondId++); + } + + if (cond_attr == nullptr || *cond_attr == nullptr) { + cvp->clock_id = ClockId::Realtime; + } else { + // if ((*cond_attr)->c_pshared) { + // cvp->flags |= USYNC_PROCESS_SHARED; + // } + cvp->clock_id = (*cond_attr)->c_clockid; + } + *cond = cvp; + return 0; +} + +static int InitStatic(Pthread* thread, PthreadCondT* cond) { + std::scoped_lock lk{CondStaticLock}; + if (*cond == nullptr) { + return CondInit(cond, nullptr, nullptr); + } + return 0; +} + +#define CHECK_AND_INIT_COND \ + if (cvp = *cond; cvp <= THR_COND_DESTROYED) [[unlikely]] { \ + if (cvp == THR_COND_INITIALIZER) { \ + int ret; \ + ret = InitStatic(g_curthread, cond); \ + if (ret) \ + return (ret); \ + } else if (cvp == THR_COND_DESTROYED) { \ + return POSIX_EINVAL; \ + } \ + cvp = *cond; \ + } + +int PS4_SYSV_ABI posix_pthread_cond_init(PthreadCondT* cond, const PthreadCondAttrT* cond_attr) { + *cond = nullptr; + return CondInit(cond, cond_attr, nullptr); +} + +int PS4_SYSV_ABI scePthreadCondInit(PthreadCondT* cond, const PthreadCondAttrT* cond_attr, + const char* name) { + *cond = nullptr; + return CondInit(cond, cond_attr, name); +} + +int PS4_SYSV_ABI posix_pthread_cond_destroy(PthreadCondT* cond) { + PthreadCond* cvp = *cond; + if (cvp == THR_COND_INITIALIZER) { + return 0; + } + if (cvp == THR_COND_DESTROYED) { + return POSIX_EINVAL; + } + cvp = *cond; + *cond = THR_COND_DESTROYED; + delete cvp; + return 0; +} + +int PthreadCond::Wait(PthreadMutexT* mutex, const OrbisKernelTimespec* abstime, u64 usec) { + PthreadMutex* mp = *mutex; + if (int error = mp->IsOwned(g_curthread); error != 0) { + return error; + } + + Pthread* curthread = g_curthread; + ASSERT_MSG(curthread->wchan == nullptr, "Thread was already on queue."); + // _thr_testcancel(curthread); + SleepqLock(this); + + /* + * set __has_user_waiters before unlocking mutex, this allows + * us to check it without locking in pthread_cond_signal(). + */ + has_user_waiters = 1; + curthread->will_sleep = 1; + + int recurse; + mp->CvUnlock(&recurse); + + curthread->mutex_obj = mp; + SleepqAdd(this, curthread); + + int error = 0; + for (;;) { + void(curthread->wake_sema.try_acquire()); + SleepqUnlock(this); + + //_thr_cancel_enter2(curthread, 0); + error = curthread->Sleep(abstime, usec) ? 0 : POSIX_ETIMEDOUT; + //_thr_cancel_leave(curthread, 0); + + SleepqLock(this); + if (curthread->wchan == nullptr) { + error = 0; + break; + } else if (curthread->ShouldCancel()) { + SleepQueue* sq = SleepqLookup(this); + has_user_waiters = SleepqRemove(sq, curthread); + SleepqUnlock(this); + curthread->mutex_obj = nullptr; + mp->CvLock(recurse); + return 0; + } else if (error == POSIX_ETIMEDOUT) { + SleepQueue* sq = SleepqLookup(this); + has_user_waiters = SleepqRemove(sq, curthread); + break; + } + UNREACHABLE(); + } + SleepqUnlock(this); + curthread->mutex_obj = nullptr; + int error2 = mp->CvLock(recurse); + if (error == 0) { + error = error2; + } + return error; +} + +int PS4_SYSV_ABI posix_pthread_cond_wait(PthreadCondT* cond, PthreadMutexT* mutex) { + PthreadCond* cvp{}; + CHECK_AND_INIT_COND + return cvp->Wait(mutex, nullptr); +} + +int PS4_SYSV_ABI posix_pthread_cond_timedwait(PthreadCondT* cond, PthreadMutexT* mutex, + const OrbisKernelTimespec* abstime) { + if (abstime == nullptr || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || + abstime->tv_nsec >= 1000000000) { + return POSIX_EINVAL; + } + + PthreadCond* cvp{}; + CHECK_AND_INIT_COND + return cvp->Wait(mutex, abstime); +} + +int PS4_SYSV_ABI posix_pthread_cond_reltimedwait_np(PthreadCondT* cond, PthreadMutexT* mutex, + u64 usec) { + PthreadCond* cvp{}; + CHECK_AND_INIT_COND + return cvp->Wait(mutex, THR_RELTIME, usec); +} + +int PthreadCond::Signal() { + Pthread* curthread = g_curthread; + + SleepqLock(this); + SleepQueue* sq = SleepqLookup(this); + if (sq == nullptr) { + SleepqUnlock(this); + return 0; + } + + Pthread* td = sq->sq_blocked.front(); + PthreadMutex* mp = td->mutex_obj; + has_user_waiters = SleepqRemove(sq, td); + + BinarySemaphore* waddr = nullptr; + if (mp->m_owner == curthread) { + if (curthread->nwaiter_defer >= Pthread::MaxDeferWaiters) { + curthread->WakeAll(); + } + curthread->defer_waiters[curthread->nwaiter_defer++] = &td->wake_sema; + mp->m_flags |= PthreadMutexFlags::Defered; + } else { + waddr = &td->wake_sema; + } + + SleepqUnlock(this); + if (waddr != nullptr) { + waddr->release(); + } + return 0; +} + +struct BroadcastArg { + Pthread* curthread; + BinarySemaphore* waddrs[Pthread::MaxDeferWaiters]; + int count; +}; + +int PthreadCond::Broadcast() { + BroadcastArg ba; + ba.curthread = g_curthread; + ba.count = 0; + + const auto drop_cb = [](Pthread* td, void* arg) { + BroadcastArg* ba = reinterpret_cast(arg); + Pthread* curthread = ba->curthread; + PthreadMutex* mp = td->mutex_obj; + + if (mp->m_owner == curthread) { + if (curthread->nwaiter_defer >= Pthread::MaxDeferWaiters) { + curthread->WakeAll(); + } + curthread->defer_waiters[curthread->nwaiter_defer++] = &td->wake_sema; + mp->m_flags |= PthreadMutexFlags::Defered; + } else { + if (ba->count >= Pthread::MaxDeferWaiters) { + for (int i = 0; i < ba->count; i++) { + ba->waddrs[i]->release(); + } + ba->count = 0; + } + ba->waddrs[ba->count++] = &td->wake_sema; + } + }; + + SleepqLock(this); + SleepQueue* sq = SleepqLookup(this); + if (sq == nullptr) { + SleepqUnlock(this); + return 0; + } + + SleepqDrop(sq, drop_cb, &ba); + has_user_waiters = 0; + SleepqUnlock(this); + + for (int i = 0; i < ba.count; i++) { + ba.waddrs[i]->release(); + } + return 0; +} + +int PS4_SYSV_ABI posix_pthread_cond_signal(PthreadCondT* cond) { + PthreadCond* cvp{}; + CHECK_AND_INIT_COND + return cvp->Signal(); +} + +int PS4_SYSV_ABI posix_pthread_cond_broadcast(PthreadCondT* cond) { + PthreadCond* cvp{}; + CHECK_AND_INIT_COND + cvp->Broadcast(); + return 0; +} + +int PS4_SYSV_ABI posix_pthread_condattr_init(PthreadCondAttrT* attr) { + PthreadCondAttr* pattr = new PthreadCondAttr{}; + if (pattr == nullptr) { + return POSIX_ENOMEM; + } + memcpy(pattr, &PhreadCondattrDefault, sizeof(PthreadCondAttr)); + *attr = pattr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_condattr_destroy(PthreadCondAttrT* attr) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + delete *attr; + *attr = nullptr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_condattr_getclock(const PthreadCondAttrT* attr, ClockId* clock_id) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + *clock_id = static_cast((*attr)->c_clockid); + return 0; +} + +int PS4_SYSV_ABI posix_pthread_condattr_setclock(PthreadCondAttrT* attr, ClockId clock_id) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + if (clock_id != ClockId::Realtime && clock_id != ClockId::Virtual && + clock_id != ClockId::Prof && clock_id != ClockId::Monotonic) { + return POSIX_EINVAL; + } + (*attr)->c_clockid = clock_id; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_condattr_getpshared(const PthreadCondAttrT* attr, int* pshared) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + *pshared = 0; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_condattr_setpshared(PthreadCondAttrT* attr, int pshared) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + if (pshared != 0) { + return POSIX_EINVAL; + } + return 0; +} + +void RegisterCond(Core::Loader::SymbolsResolver* sym) { + // Posix + LIB_FUNCTION("mKoTx03HRWA", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_condattr_init); + LIB_FUNCTION("0TyVk4MSLt0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_init); + LIB_FUNCTION("2MOy+rUfuhQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_signal); + LIB_FUNCTION("RXXqi4CtF8w", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_destroy); + LIB_FUNCTION("Op8TBGY5KHg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_wait); + LIB_FUNCTION("27bAgiJmOh0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_timedwait); + LIB_FUNCTION("mkx2fVhNMsg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_broadcast); + + // Posix-Kernel + LIB_FUNCTION("Op8TBGY5KHg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_cond_wait); + LIB_FUNCTION("mkx2fVhNMsg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_cond_broadcast); + + // Orbis + LIB_FUNCTION("2Tb92quprl0", "libkernel", 1, "libkernel", 1, 1, ORBIS(scePthreadCondInit)); + LIB_FUNCTION("m5-2bsNfv7s", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_condattr_init)); + LIB_FUNCTION("JGgj7Uvrl+A", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_cond_broadcast)); + LIB_FUNCTION("WKAXJ4XBPQ4", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_cond_wait)); + LIB_FUNCTION("waPcxYiR3WA", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_condattr_destroy)); + LIB_FUNCTION("kDh-NfxgMtE", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_cond_signal)); + LIB_FUNCTION("BmMjYxmew1w", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_cond_reltimedwait_np)); + LIB_FUNCTION("g+PZd2hiacg", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_cond_destroy)); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/event_flag/event_flag.cpp b/src/core/libraries/kernel/threads/event_flag.cpp similarity index 51% rename from src/core/libraries/kernel/event_flag/event_flag.cpp rename to src/core/libraries/kernel/threads/event_flag.cpp index f83ae0a7f..24ddcb927 100644 --- a/src/core/libraries/kernel/event_flag/event_flag.cpp +++ b/src/core/libraries/kernel/threads/event_flag.cpp @@ -1,13 +1,184 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include + #include "common/assert.h" #include "common/logging/log.h" -#include "core/libraries/error_codes.h" +#include "core/libraries/kernel/orbis_error.h" #include "core/libraries/libs.h" -#include "event_flag.h" namespace Libraries::Kernel { + +constexpr int ORBIS_KERNEL_EVF_ATTR_TH_FIFO = 0x01; +constexpr int ORBIS_KERNEL_EVF_ATTR_TH_PRIO = 0x02; +constexpr int ORBIS_KERNEL_EVF_ATTR_SINGLE = 0x10; +constexpr int ORBIS_KERNEL_EVF_ATTR_MULTI = 0x20; + +constexpr int ORBIS_KERNEL_EVF_WAITMODE_AND = 0x01; +constexpr int ORBIS_KERNEL_EVF_WAITMODE_OR = 0x02; +constexpr int ORBIS_KERNEL_EVF_WAITMODE_CLEAR_ALL = 0x10; +constexpr int ORBIS_KERNEL_EVF_WAITMODE_CLEAR_PAT = 0x20; + +class EventFlagInternal { +public: + enum class ClearMode { None, All, Bits }; + enum class WaitMode { And, Or }; + enum class ThreadMode { Single, Multi }; + enum class QueueMode { Fifo, ThreadPrio }; + + EventFlagInternal(const std::string& name, ThreadMode thread_mode, QueueMode queue_mode, + uint64_t bits) + : m_name(name), m_thread_mode(thread_mode), m_queue_mode(queue_mode), m_bits(bits) {}; + + int Wait(u64 bits, WaitMode wait_mode, ClearMode clear_mode, u64* result, u32* ptr_micros) { + std::unique_lock lock{m_mutex}; + + uint32_t micros = 0; + bool infinitely = true; + if (ptr_micros != nullptr) { + micros = *ptr_micros; + infinitely = false; + } + + if (m_thread_mode == ThreadMode::Single && m_waiting_threads > 0) { + return ORBIS_KERNEL_ERROR_EPERM; + } + + auto const start = std::chrono::system_clock::now(); + m_waiting_threads++; + auto waitFunc = [this, wait_mode, bits] { + return (m_status == Status::Canceled || m_status == Status::Deleted || + (wait_mode == WaitMode::And && (m_bits & bits) == bits) || + (wait_mode == WaitMode::Or && (m_bits & bits) != 0)); + }; + + if (infinitely) { + m_cond_var.wait(lock, waitFunc); + } else { + if (!m_cond_var.wait_for(lock, std::chrono::microseconds(micros), waitFunc)) { + if (result != nullptr) { + *result = m_bits; + } + *ptr_micros = 0; + --m_waiting_threads; + return ORBIS_KERNEL_ERROR_ETIMEDOUT; + } + } + --m_waiting_threads; + if (result != nullptr) { + *result = m_bits; + } + + auto elapsed = std::chrono::duration_cast( + std::chrono::system_clock::now() - start) + .count(); + if (result != nullptr) { + *result = m_bits; + } + + if (ptr_micros != nullptr) { + *ptr_micros = (elapsed >= micros ? 0 : micros - elapsed); + } + + if (m_status == Status::Canceled) { + return ORBIS_KERNEL_ERROR_ECANCELED; + } else if (m_status == Status::Deleted) { + return ORBIS_KERNEL_ERROR_EACCES; + } + + if (clear_mode == ClearMode::All) { + m_bits = 0; + } else if (clear_mode == ClearMode::Bits) { + m_bits &= ~bits; + } + + return ORBIS_OK; + } + + int Poll(u64 bits, WaitMode wait_mode, ClearMode clear_mode, u64* result) { + u32 micros = 0; + auto ret = Wait(bits, wait_mode, clear_mode, result, µs); + if (ret == ORBIS_KERNEL_ERROR_ETIMEDOUT) { + // Poll returns EBUSY instead. + ret = ORBIS_KERNEL_ERROR_EBUSY; + } + return ret; + } + + void Set(u64 bits) { + std::unique_lock lock{m_mutex}; + + while (m_status != Status::Set) { + m_mutex.unlock(); + std::this_thread::sleep_for(std::chrono::microseconds(10)); + m_mutex.lock(); + } + + m_bits |= bits; + m_cond_var.notify_all(); + } + + void Clear(u64 bits) { + std::unique_lock lock{m_mutex}; + while (m_status != Status::Set) { + m_mutex.unlock(); + std::this_thread::sleep_for(std::chrono::microseconds(10)); + m_mutex.lock(); + } + + m_bits &= bits; + } + + void Cancel(u64 setPattern, int* numWaitThreads) { + std::unique_lock lock{m_mutex}; + + while (m_status != Status::Set) { + m_mutex.unlock(); + std::this_thread::sleep_for(std::chrono::microseconds(10)); + m_mutex.lock(); + } + + if (numWaitThreads) { + *numWaitThreads = m_waiting_threads; + } + + m_status = Status::Canceled; + m_bits = setPattern; + + m_cond_var.notify_all(); + + while (m_waiting_threads > 0) { + m_mutex.unlock(); + std::this_thread::sleep_for(std::chrono::microseconds(10)); + m_mutex.lock(); + } + + m_status = Status::Set; + } + +private: + enum class Status { Set, Canceled, Deleted }; + + std::mutex m_mutex; + std::condition_variable m_cond_var; + Status m_status = Status::Set; + int m_waiting_threads = 0; + std::string m_name; + ThreadMode m_thread_mode = ThreadMode::Single; + QueueMode m_queue_mode = QueueMode::Fifo; + u64 m_bits = 0; +}; + +using OrbisKernelUseconds = u32; +using OrbisKernelEventFlag = EventFlagInternal*; + +struct OrbisKernelEventFlagOptParam { + size_t size; +}; + int PS4_SYSV_ABI sceKernelCreateEventFlag(OrbisKernelEventFlag* ef, const char* pName, u32 attr, u64 initPattern, const OrbisKernelEventFlagOptParam* pOptParam) { @@ -25,9 +196,8 @@ int PS4_SYSV_ABI sceKernelCreateEventFlag(OrbisKernelEventFlag* ef, const char* return ORBIS_KERNEL_ERROR_ENAMETOOLONG; } - EventFlagInternal::ThreadMode thread_mode = EventFlagInternal::ThreadMode::Single; - EventFlagInternal::QueueMode queue_mode = EventFlagInternal::QueueMode::Fifo; - + auto thread_mode = EventFlagInternal::ThreadMode::Single; + auto queue_mode = EventFlagInternal::QueueMode::Fifo; switch (attr & 0xfu) { case 0x01: queue_mode = EventFlagInternal::QueueMode::Fifo; @@ -61,6 +231,7 @@ int PS4_SYSV_ABI sceKernelCreateEventFlag(OrbisKernelEventFlag* ef, const char* *ef = new EventFlagInternal(std::string(pName), thread_mode, queue_mode, initPattern); return ORBIS_OK; } + int PS4_SYSV_ABI sceKernelDeleteEventFlag(OrbisKernelEventFlag ef) { if (ef == nullptr) { return ORBIS_KERNEL_ERROR_ESRCH; @@ -69,24 +240,30 @@ int PS4_SYSV_ABI sceKernelDeleteEventFlag(OrbisKernelEventFlag ef) { delete ef; return ORBIS_OK; } + int PS4_SYSV_ABI sceKernelOpenEventFlag() { LOG_ERROR(Kernel_Event, "(STUBBED) called"); return ORBIS_OK; } + int PS4_SYSV_ABI sceKernelCloseEventFlag() { LOG_ERROR(Kernel_Event, "(STUBBED) called"); return ORBIS_OK; } + int PS4_SYSV_ABI sceKernelClearEventFlag(OrbisKernelEventFlag ef, u64 bitPattern) { LOG_DEBUG(Kernel_Event, "called"); ef->Clear(bitPattern); return ORBIS_OK; } + int PS4_SYSV_ABI sceKernelCancelEventFlag(OrbisKernelEventFlag ef, u64 setPattern, int* pNumWaitThreads) { - LOG_ERROR(Kernel_Event, "(STUBBED) called"); + LOG_DEBUG(Kernel_Event, "called"); + ef->Cancel(setPattern, pNumWaitThreads); return ORBIS_OK; } + int PS4_SYSV_ABI sceKernelSetEventFlag(OrbisKernelEventFlag ef, u64 bitPattern) { LOG_TRACE(Kernel_Event, "called"); if (ef == nullptr) { @@ -95,6 +272,7 @@ int PS4_SYSV_ABI sceKernelSetEventFlag(OrbisKernelEventFlag ef, u64 bitPattern) ef->Set(bitPattern); return ORBIS_OK; } + int PS4_SYSV_ABI sceKernelPollEventFlag(OrbisKernelEventFlag ef, u64 bitPattern, u32 waitMode, u64* pResultPat) { LOG_DEBUG(Kernel_Event, "called bitPattern = {:#x} waitMode = {:#x}", bitPattern, waitMode); @@ -107,9 +285,8 @@ int PS4_SYSV_ABI sceKernelPollEventFlag(OrbisKernelEventFlag ef, u64 bitPattern, return ORBIS_KERNEL_ERROR_EINVAL; } - EventFlagInternal::WaitMode wait = EventFlagInternal::WaitMode::And; - EventFlagInternal::ClearMode clear = EventFlagInternal::ClearMode::None; - + auto wait = EventFlagInternal::WaitMode::And; + auto clear = EventFlagInternal::ClearMode::None; switch (waitMode & 0xf) { case 0x01: wait = EventFlagInternal::WaitMode::And; @@ -154,9 +331,8 @@ int PS4_SYSV_ABI sceKernelWaitEventFlag(OrbisKernelEventFlag ef, u64 bitPattern, return ORBIS_KERNEL_ERROR_EINVAL; } - EventFlagInternal::WaitMode wait = EventFlagInternal::WaitMode::And; - EventFlagInternal::ClearMode clear = EventFlagInternal::ClearMode::None; - + auto wait = EventFlagInternal::WaitMode::And; + auto clear = EventFlagInternal::ClearMode::None; switch (waitMode & 0xf) { case 0x01: wait = EventFlagInternal::WaitMode::And; @@ -190,6 +366,7 @@ int PS4_SYSV_ABI sceKernelWaitEventFlag(OrbisKernelEventFlag ef, u64 bitPattern, return result; } + void RegisterKernelEventFlag(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("PZku4ZrXJqg", "libkernel", 1, "libkernel", 1, 1, sceKernelCancelEventFlag); LIB_FUNCTION("7uhBFWRAS60", "libkernel", 1, "libkernel", 1, 1, sceKernelClearEventFlag); @@ -201,4 +378,5 @@ void RegisterKernelEventFlag(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("IOnSvHzqu6A", "libkernel", 1, "libkernel", 1, 1, sceKernelSetEventFlag); LIB_FUNCTION("JTvBflhYazQ", "libkernel", 1, "libkernel", 1, 1, sceKernelWaitEventFlag); } + } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/exception.cpp b/src/core/libraries/kernel/threads/exception.cpp new file mode 100644 index 000000000..017984e0d --- /dev/null +++ b/src/core/libraries/kernel/threads/exception.cpp @@ -0,0 +1,161 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "core/libraries/kernel/threads/exception.h" +#include "core/libraries/kernel/threads/pthread.h" +#include "core/libraries/libs.h" + +#ifdef _WIN64 +#include "common/ntapi.h" +#else +#include +#endif + +namespace Libraries::Kernel { + +static std::array Handlers{}; + +#ifndef _WIN64 +void SigactionHandler(int signum, siginfo_t* inf, ucontext_t* raw_context) { + const auto handler = Handlers[POSIX_SIGUSR1]; + if (handler) { + auto ctx = Ucontext{}; +#ifdef __APPLE__ + auto& regs = raw_context->uc_mcontext->__ss; + ctx.uc_mcontext.mc_r8 = regs.__r8; + ctx.uc_mcontext.mc_r9 = regs.__r9; + ctx.uc_mcontext.mc_r10 = regs.__r10; + ctx.uc_mcontext.mc_r11 = regs.__r11; + ctx.uc_mcontext.mc_r12 = regs.__r12; + ctx.uc_mcontext.mc_r13 = regs.__r13; + ctx.uc_mcontext.mc_r14 = regs.__r14; + ctx.uc_mcontext.mc_r15 = regs.__r15; + ctx.uc_mcontext.mc_rdi = regs.__rdi; + ctx.uc_mcontext.mc_rsi = regs.__rsi; + ctx.uc_mcontext.mc_rbp = regs.__rbp; + ctx.uc_mcontext.mc_rbx = regs.__rbx; + ctx.uc_mcontext.mc_rdx = regs.__rdx; + ctx.uc_mcontext.mc_rax = regs.__rax; + ctx.uc_mcontext.mc_rcx = regs.__rcx; + ctx.uc_mcontext.mc_rsp = regs.__rsp; + ctx.uc_mcontext.mc_fs = regs.__fs; + ctx.uc_mcontext.mc_gs = regs.__gs; +#else + auto& regs = raw_context->uc_mcontext.gregs; + ctx.uc_mcontext.mc_r8 = regs[REG_R8]; + ctx.uc_mcontext.mc_r9 = regs[REG_R9]; + ctx.uc_mcontext.mc_r10 = regs[REG_R10]; + ctx.uc_mcontext.mc_r11 = regs[REG_R11]; + ctx.uc_mcontext.mc_r12 = regs[REG_R12]; + ctx.uc_mcontext.mc_r13 = regs[REG_R13]; + ctx.uc_mcontext.mc_r14 = regs[REG_R14]; + ctx.uc_mcontext.mc_r15 = regs[REG_R15]; + ctx.uc_mcontext.mc_rdi = regs[REG_RDI]; + ctx.uc_mcontext.mc_rsi = regs[REG_RSI]; + ctx.uc_mcontext.mc_rbp = regs[REG_RBP]; + ctx.uc_mcontext.mc_rbx = regs[REG_RBX]; + ctx.uc_mcontext.mc_rdx = regs[REG_RDX]; + ctx.uc_mcontext.mc_rax = regs[REG_RAX]; + ctx.uc_mcontext.mc_rcx = regs[REG_RCX]; + ctx.uc_mcontext.mc_rsp = regs[REG_RSP]; + ctx.uc_mcontext.mc_fs = (regs[REG_CSGSFS] >> 32) & 0xFFFF; + ctx.uc_mcontext.mc_gs = (regs[REG_CSGSFS] >> 16) & 0xFFFF; +#endif + handler(POSIX_SIGUSR1, &ctx); + } +} +#else +void ExceptionHandler(void* arg1, void* arg2, void* arg3, PCONTEXT context) { + const char* thrName = (char*)arg1; + LOG_INFO(Lib_Kernel, "Exception raised successfully on thread '{}'", thrName); + const auto handler = Handlers[POSIX_SIGUSR1]; + if (handler) { + auto ctx = Ucontext{}; + ctx.uc_mcontext.mc_r8 = context->R8; + ctx.uc_mcontext.mc_r9 = context->R9; + ctx.uc_mcontext.mc_r10 = context->R10; + ctx.uc_mcontext.mc_r11 = context->R11; + ctx.uc_mcontext.mc_r12 = context->R12; + ctx.uc_mcontext.mc_r13 = context->R13; + ctx.uc_mcontext.mc_r14 = context->R14; + ctx.uc_mcontext.mc_r15 = context->R15; + ctx.uc_mcontext.mc_rdi = context->Rdi; + ctx.uc_mcontext.mc_rsi = context->Rsi; + ctx.uc_mcontext.mc_rbp = context->Rbp; + ctx.uc_mcontext.mc_rbx = context->Rbx; + ctx.uc_mcontext.mc_rdx = context->Rdx; + ctx.uc_mcontext.mc_rax = context->Rax; + ctx.uc_mcontext.mc_rcx = context->Rcx; + ctx.uc_mcontext.mc_rsp = context->Rsp; + ctx.uc_mcontext.mc_fs = context->SegFs; + ctx.uc_mcontext.mc_gs = context->SegGs; + handler(POSIX_SIGUSR1, &ctx); + } +} +#endif + +int PS4_SYSV_ABI sceKernelInstallExceptionHandler(s32 signum, SceKernelExceptionHandler handler) { + if (signum != POSIX_SIGUSR1) { + LOG_ERROR(Lib_Kernel, "Installing non-supported exception handler for signal {}", signum); + return 0; + } + ASSERT_MSG(!Handlers[POSIX_SIGUSR1], "Invalid parameters"); + Handlers[POSIX_SIGUSR1] = handler; +#ifndef _WIN64 + struct sigaction act = {}; + act.sa_flags = SA_SIGINFO | SA_RESTART; + act.sa_sigaction = reinterpret_cast(SigactionHandler); + sigaction(SIGUSR2, &act, nullptr); +#endif + return 0; +} + +int PS4_SYSV_ABI sceKernelRemoveExceptionHandler(s32 signum) { + if (signum != POSIX_SIGUSR1) { + LOG_ERROR(Lib_Kernel, "Installing non-supported exception handler for signal {}", signum); + return 0; + } + ASSERT_MSG(Handlers[POSIX_SIGUSR1], "Invalid parameters"); + Handlers[POSIX_SIGUSR1] = nullptr; +#ifndef _WIN64 + struct sigaction act = {}; + act.sa_flags = SA_SIGINFO | SA_RESTART; + act.sa_sigaction = nullptr; + sigaction(SIGUSR2, &act, nullptr); +#endif + return 0; +} + +int PS4_SYSV_ABI sceKernelRaiseException(PthreadT thread, int signum) { + LOG_WARNING(Lib_Kernel, "Raising exception on thread '{}'", thread->name); + ASSERT_MSG(signum == POSIX_SIGUSR1, "Attempting to raise non user defined signal!"); +#ifndef _WIN64 + pthread_t pthr = *reinterpret_cast(thread->native_thr.GetHandle()); + pthread_kill(pthr, SIGUSR2); +#else + USER_APC_OPTION option; + option.UserApcFlags = QueueUserApcFlagsSpecialUserApc; + + u64 res = NtQueueApcThreadEx(reinterpret_cast(thread->native_thr.GetHandle()), option, + ExceptionHandler, (void*)thread->name.c_str(), nullptr, nullptr); + ASSERT(res == 0); +#endif + return 0; +} + +int PS4_SYSV_ABI sceKernelDebugRaiseException() { + UNREACHABLE(); + return 0; +} + +void RegisterException(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("il03nluKfMk", "libkernel_unity", 1, "libkernel", 1, 1, sceKernelRaiseException); + LIB_FUNCTION("WkwEd3N7w0Y", "libkernel_unity", 1, "libkernel", 1, 1, + sceKernelInstallExceptionHandler); + LIB_FUNCTION("Qhv5ARAoOEc", "libkernel_unity", 1, "libkernel", 1, 1, + sceKernelRemoveExceptionHandler) + LIB_FUNCTION("OMDRKKAZ8I4", "libkernel", 1, "libkernel", 1, 1, sceKernelDebugRaiseException); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/exception.h b/src/core/libraries/kernel/threads/exception.h new file mode 100644 index 000000000..985a7f56b --- /dev/null +++ b/src/core/libraries/kernel/threads/exception.h @@ -0,0 +1,86 @@ +// 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; +} + +namespace Libraries::Kernel { + +using SceKernelExceptionHandler = PS4_SYSV_ABI void (*)(int, void*); + +constexpr int POSIX_SIGSEGV = 11; +constexpr int POSIX_SIGUSR1 = 30; + +struct Mcontext { + u64 mc_onstack; + u64 mc_rdi; + u64 mc_rsi; + u64 mc_rdx; + u64 mc_rcx; + u64 mc_r8; + u64 mc_r9; + u64 mc_rax; + u64 mc_rbx; + u64 mc_rbp; + u64 mc_r10; + u64 mc_r11; + u64 mc_r12; + u64 mc_r13; + u64 mc_r14; + u64 mc_r15; + int mc_trapno; + u16 mc_fs; + u16 mc_gs; + u64 mc_addr; + int mc_flags; + u16 mc_es; + u16 mc_ds; + u64 mc_err; + u64 mc_rip; + u64 mc_cs; + u64 mc_rflags; + u64 mc_rsp; + u64 mc_ss; + u64 mc_len; + u64 mc_fpformat; + u64 mc_ownedfp; + u64 mc_lbrfrom; + u64 mc_lbrto; + u64 mc_aux1; + u64 mc_aux2; + u64 mc_fpstate[104]; + u64 mc_fsbase; + u64 mc_gsbase; + u64 mc_spare[6]; +}; + +struct Stack { + void* ss_sp; + std::size_t ss_size; + int ss_flags; + int _align; +}; + +struct Sigset { + u64 bits[2]; +}; + +struct Ucontext { + struct Sigset uc_sigmask; + int field1_0x10[12]; + struct Mcontext uc_mcontext; + struct Ucontext* uc_link; + struct Stack uc_stack; + int uc_flags; + int __spare[4]; + int field7_0x4f4[3]; +}; + +void RegisterException(Core::Loader::SymbolsResolver* sym); + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/keys.cpp b/src/core/libraries/kernel/threads/keys.cpp deleted file mode 100644 index cf5104d21..000000000 --- a/src/core/libraries/kernel/threads/keys.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/libraries/error_codes.h" -#include "core/libraries/kernel/thread_management.h" -#include "core/libraries/libs.h" - -namespace Libraries::Kernel { - -int PS4_SYSV_ABI scePthreadKeyCreate(OrbisPthreadKey* key, PthreadKeyDestructor destructor) { - if (key == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - - pthread_key_t thread_key; - int result = pthread_key_create(&thread_key, nullptr); - *key = static_cast(thread_key); - - if (destructor) { - auto thread = scePthreadSelf(); - thread->key_destructors.emplace_back(*key, destructor); - } - - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadKeyCreate: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; -} - -void* PS4_SYSV_ABI scePthreadGetspecific(OrbisPthreadKey key) { - return pthread_getspecific(key); -} - -int PS4_SYSV_ABI scePthreadSetspecific(OrbisPthreadKey key, /* const*/ void* value) { - int result = pthread_setspecific(key, value); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadSetspecific: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; -} - -void KeySymbolsRegister(Core::Loader::SymbolsResolver* sym) { - LIB_FUNCTION("geDaqgH9lTg", "libkernel", 1, "libkernel", 1, 1, scePthreadKeyCreate); - LIB_FUNCTION("eoht7mQOCmo", "libkernel", 1, "libkernel", 1, 1, scePthreadGetspecific); - LIB_FUNCTION("+BzXYkqYeLE", "libkernel", 1, "libkernel", 1, 1, scePthreadSetspecific); -} - -} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/mutex.cpp b/src/core/libraries/kernel/threads/mutex.cpp new file mode 100644 index 000000000..4f11e32da --- /dev/null +++ b/src/core/libraries/kernel/threads/mutex.cpp @@ -0,0 +1,468 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include "common/assert.h" +#include "common/types.h" +#include "core/libraries/kernel/kernel.h" +#include "core/libraries/kernel/posix_error.h" +#include "core/libraries/kernel/threads/pthread.h" +#include "core/libraries/libs.h" + +namespace Libraries::Kernel { + +static constexpr u32 MUTEX_ADAPTIVE_SPINS = 2000; +static std::mutex MutxStaticLock; + +#define THR_MUTEX_INITIALIZER ((PthreadMutex*)NULL) +#define THR_ADAPTIVE_MUTEX_INITIALIZER ((PthreadMutex*)1) +#define THR_MUTEX_DESTROYED ((PthreadMutex*)2) + +#define CPU_SPINWAIT __asm__ volatile("pause") + +#define CHECK_AND_INIT_MUTEX \ + if (PthreadMutex* m = *mutex; m <= THR_MUTEX_DESTROYED) [[unlikely]] { \ + if (m == THR_MUTEX_DESTROYED) { \ + return POSIX_EINVAL; \ + } \ + if (s32 ret = InitStatic(g_curthread, mutex); ret) { \ + return ret; \ + } \ + m = *mutex; \ + } + +static constexpr PthreadMutexAttr PthreadMutexattrDefault = { + .m_type = PthreadMutexType::ErrorCheck, .m_protocol = PthreadMutexProt::None, .m_ceiling = 0}; + +static constexpr PthreadMutexAttr PthreadMutexattrAdaptiveDefault = { + .m_type = PthreadMutexType::AdaptiveNp, .m_protocol = PthreadMutexProt::None, .m_ceiling = 0}; + +using CallocFun = void* (*)(size_t, size_t); + +static int MutexInit(PthreadMutexT* mutex, const PthreadMutexAttr* mutex_attr, const char* name) { + const PthreadMutexAttr* attr; + if (mutex_attr == NULL) { + attr = &PthreadMutexattrDefault; + } else { + attr = mutex_attr; + if (attr->m_type < PthreadMutexType::ErrorCheck || attr->m_type >= PthreadMutexType::Max) { + return POSIX_EINVAL; + } + if (attr->m_protocol > PthreadMutexProt::Protect) { + return POSIX_EINVAL; + } + } + auto* pmutex = new PthreadMutex{}; + if (pmutex == nullptr) { + return POSIX_ENOMEM; + } + + if (name) { + pmutex->name = name; + } else { + static int MutexId = 0; + pmutex->name = fmt::format("Mutex{}", MutexId++); + } + + pmutex->m_flags = PthreadMutexFlags(attr->m_type); + pmutex->m_owner = nullptr; + pmutex->m_count = 0; + pmutex->m_spinloops = 0; + pmutex->m_yieldloops = 0; + pmutex->m_protocol = attr->m_protocol; + if (attr->m_type == PthreadMutexType::AdaptiveNp) { + pmutex->m_spinloops = MUTEX_ADAPTIVE_SPINS; + // pmutex->m_yieldloops = _thr_yieldloops; + } + + *mutex = pmutex; + return 0; +} + +static int InitStatic(Pthread* thread, PthreadMutexT* mutex) { + std::scoped_lock lk{MutxStaticLock}; + + if (*mutex == THR_MUTEX_INITIALIZER) { + return MutexInit(mutex, &PthreadMutexattrDefault, nullptr); + } else if (*mutex == THR_ADAPTIVE_MUTEX_INITIALIZER) { + return MutexInit(mutex, &PthreadMutexattrAdaptiveDefault, nullptr); + } + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutex_init(PthreadMutexT* mutex, + const PthreadMutexAttrT* mutex_attr) { + return MutexInit(mutex, mutex_attr ? *mutex_attr : nullptr, nullptr); +} + +int PS4_SYSV_ABI scePthreadMutexInit(PthreadMutexT* mutex, const PthreadMutexAttrT* mutex_attr, + const char* name) { + return MutexInit(mutex, mutex_attr ? *mutex_attr : nullptr, name); +} + +int PS4_SYSV_ABI posix_pthread_mutex_destroy(PthreadMutexT* mutex) { + PthreadMutexT m = *mutex; + if (m < THR_MUTEX_DESTROYED) { + return 0; + } + if (m == THR_MUTEX_DESTROYED) { + return POSIX_EINVAL; + } + if (m->m_owner != nullptr) { + return POSIX_EBUSY; + } + *mutex = THR_MUTEX_DESTROYED; + delete m; + return 0; +} + +int PthreadMutex::SelfTryLock() { + switch (Type()) { + case PthreadMutexType::ErrorCheck: + case PthreadMutexType::Normal: + case PthreadMutexType::AdaptiveNp: + return POSIX_EBUSY; + case PthreadMutexType::Recursive: { + /* Increment the lock count: */ + if (m_count + 1 > 0) { + m_count++; + return 0; + } + return POSIX_EAGAIN; + } + default: + return POSIX_EINVAL; + } +} + +int PthreadMutex::SelfLock(const OrbisKernelTimespec* abstime, u64 usec) { + const auto DoSleep = [&] { + if (abstime == THR_RELTIME) { + std::this_thread::sleep_for(std::chrono::microseconds(usec)); + return POSIX_ETIMEDOUT; + } else { + if (abstime->tv_sec < 0 || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) { + return POSIX_EINVAL; + } else { + std::this_thread::sleep_until(abstime->TimePoint()); + return POSIX_ETIMEDOUT; + } + } + }; + switch (Type()) { + case PthreadMutexType::ErrorCheck: + case PthreadMutexType::AdaptiveNp: { + if (abstime) { + return DoSleep(); + } + /* + * POSIX specifies that mutexes should return + * EDEADLK if a recursive lock is detected. + */ + return POSIX_EDEADLK; + } + case PthreadMutexType::Normal: { + /* + * What SS2 define as a 'normal' mutex. Intentionally + * deadlock on attempts to get a lock you already own. + */ + if (abstime) { + return DoSleep(); + } + UNREACHABLE_MSG("Mutex deadlock occured"); + return 0; + } + case PthreadMutexType::Recursive: { + /* Increment the lock count: */ + if (m_count + 1 > 0) { + m_count++; + return 0; + } + return POSIX_EAGAIN; + } + default: + return POSIX_EINVAL; + } +} + +int PthreadMutex::Lock(const OrbisKernelTimespec* abstime, u64 usec) { + Pthread* curthread = g_curthread; + if (m_owner == curthread) { + return SelfLock(abstime, usec); + } + + /* + * For adaptive mutexes, spin for a bit in the expectation + * that if the application requests this mutex type then + * the lock is likely to be released quickly and it is + * faster than entering the kernel + */ + if (m_protocol == PthreadMutexProt::None) [[likely]] { + int count = m_spinloops; + while (count--) { + if (m_lock.try_lock()) { + m_owner = curthread; + return 0; + } + CPU_SPINWAIT; + } + + count = m_yieldloops; + while (count--) { + std::this_thread::yield(); + if (m_lock.try_lock()) { + m_owner = curthread; + return 0; + } + } + } + + int ret = 0; + if (abstime == nullptr) { + m_lock.lock(); + } else if (abstime != THR_RELTIME && (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)) + [[unlikely]] { + ret = POSIX_EINVAL; + } else { + if (abstime == THR_RELTIME) { + ret = m_lock.try_lock_for(std::chrono::microseconds(usec)) ? 0 : POSIX_ETIMEDOUT; + } else { + ret = m_lock.try_lock_until(abstime->TimePoint()) ? 0 : POSIX_ETIMEDOUT; + } + } + if (ret == 0) { + m_owner = curthread; + } + return ret; +} + +int PthreadMutex::TryLock() { + Pthread* curthread = g_curthread; + if (m_owner == curthread) { + return SelfTryLock(); + } + const int ret = m_lock.try_lock() ? 0 : POSIX_EBUSY; + if (ret == 0) { + m_owner = curthread; + } + return ret; +} + +int PS4_SYSV_ABI posix_pthread_mutex_trylock(PthreadMutexT* mutex) { + CHECK_AND_INIT_MUTEX + return (*mutex)->TryLock(); +} + +int PS4_SYSV_ABI posix_pthread_mutex_lock(PthreadMutexT* mutex) { + CHECK_AND_INIT_MUTEX + return (*mutex)->Lock(nullptr); +} + +int PS4_SYSV_ABI posix_pthread_mutex_timedlock(PthreadMutexT* mutex, + const OrbisKernelTimespec* abstime) { + CHECK_AND_INIT_MUTEX + UNREACHABLE(); + return (*mutex)->Lock(abstime); +} + +int PS4_SYSV_ABI posix_pthread_mutex_reltimedlock_np(PthreadMutexT* mutex, u64 usec) { + CHECK_AND_INIT_MUTEX + return (*mutex)->Lock(THR_RELTIME, usec); +} + +int PthreadMutex::Unlock() { + Pthread* curthread = g_curthread; + /* + * Check if the running thread is not the owner of the mutex. + */ + if (m_owner != curthread) [[unlikely]] { + return POSIX_EPERM; + } + + if (Type() == PthreadMutexType::Recursive && m_count > 0) [[unlikely]] { + m_count--; + } else { + int defered = True(m_flags & PthreadMutexFlags::Defered); + m_flags &= ~PthreadMutexFlags::Defered; + + m_owner = nullptr; + m_lock.unlock(); + + if (curthread->will_sleep == 0 && defered) { + curthread->WakeAll(); + } + } + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutex_unlock(PthreadMutexT* mutex) { + PthreadMutex* mp = *mutex; + if (mp <= THR_MUTEX_DESTROYED) [[unlikely]] { + if (mp == THR_MUTEX_DESTROYED) { + return POSIX_EINVAL; + } + return POSIX_EPERM; + } + return mp->Unlock(); +} + +int PS4_SYSV_ABI posix_pthread_mutex_getspinloops_np(PthreadMutexT* mutex, int* count) { + CHECK_AND_INIT_MUTEX + *count = (*mutex)->m_spinloops; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutex_setspinloops_np(PthreadMutexT* mutex, int count) { + CHECK_AND_INIT_MUTEX(*mutex)->m_spinloops = count; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutex_getyieldloops_np(PthreadMutexT* mutex, int* count) { + CHECK_AND_INIT_MUTEX + *count = (*mutex)->m_yieldloops; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutex_setyieldloops_np(PthreadMutexT* mutex, int count) { + CHECK_AND_INIT_MUTEX(*mutex)->m_yieldloops = count; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutex_isowned_np(PthreadMutexT* mutex) { + PthreadMutex* m = *mutex; + if (m <= THR_MUTEX_DESTROYED) { + return 0; + } + return m->m_owner == g_curthread; +} + +int PthreadMutex::IsOwned(Pthread* curthread) const { + if (this <= THR_MUTEX_DESTROYED) [[unlikely]] { + if (this == THR_MUTEX_DESTROYED) { + return POSIX_EINVAL; + } + return POSIX_EPERM; + } + if (m_owner != curthread) { + return POSIX_EPERM; + } + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutexattr_init(PthreadMutexAttrT* attr) { + PthreadMutexAttrT pattr = new PthreadMutexAttr{}; + if (pattr == nullptr) { + return POSIX_ENOMEM; + } + memcpy(pattr, &PthreadMutexattrDefault, sizeof(PthreadMutexAttr)); + *attr = pattr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutexattr_setkind_np(PthreadMutexAttrT* attr, + PthreadMutexType kind) { + if (attr == nullptr || *attr == nullptr) { + *__Error() = POSIX_EINVAL; + return -1; + } + (*attr)->m_type = kind; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutexattr_getkind_np(PthreadMutexAttrT attr) { + if (attr == nullptr) { + *__Error() = POSIX_EINVAL; + return -1; + } + return static_cast(attr->m_type); +} + +int PS4_SYSV_ABI posix_pthread_mutexattr_settype(PthreadMutexAttrT* attr, PthreadMutexType type) { + if (attr == nullptr || *attr == nullptr || type >= PthreadMutexType::Max) { + return POSIX_EINVAL; + } + (*attr)->m_type = type; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutexattr_gettype(PthreadMutexAttrT* attr, PthreadMutexType* type) { + if (attr == nullptr || *attr == nullptr || (*attr)->m_type >= PthreadMutexType::Max) { + return POSIX_EINVAL; + } + *type = (*attr)->m_type; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutexattr_destroy(PthreadMutexAttrT* attr) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + delete *attr; + *attr = nullptr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutexattr_getprotocol(PthreadMutexAttrT* mattr, + PthreadMutexProt* protocol) { + if (mattr == nullptr || *mattr == nullptr) { + return POSIX_EINVAL; + } + *protocol = (*mattr)->m_protocol; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutexattr_setprotocol(PthreadMutexAttrT* mattr, + PthreadMutexProt protocol) { + if (mattr == nullptr || *mattr == nullptr || (protocol < PthreadMutexProt::None) || + (protocol > PthreadMutexProt::Protect)) { + return POSIX_EINVAL; + } + (*mattr)->m_protocol = protocol; + //(*mattr)->m_ceiling = THR_MAX_RR_PRIORITY; + return 0; +} + +void RegisterMutex(Core::Loader::SymbolsResolver* sym) { + // Posix + LIB_FUNCTION("ttHNfU+qDBU", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_init); + LIB_FUNCTION("7H0iTOciTLo", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_lock); + LIB_FUNCTION("2Z+PpY6CaJg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_unlock); + LIB_FUNCTION("ltCfaGr2JGE", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_destroy); + LIB_FUNCTION("dQHWEsJtoE4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutexattr_init); + LIB_FUNCTION("mDmgMOGVUqg", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_mutexattr_settype); + LIB_FUNCTION("5txKfcMUAok", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_mutexattr_setprotocol); + LIB_FUNCTION("HF7lK46xzjY", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_mutexattr_destroy); + LIB_FUNCTION("K-jXhbt2gn4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_trylock); + + // Posix-Kernel + LIB_FUNCTION("7H0iTOciTLo", "libkernel", 1, "libkernel", 1, 1, posix_pthread_mutex_lock); + LIB_FUNCTION("2Z+PpY6CaJg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_mutex_unlock); + + // Orbis + LIB_FUNCTION("cmo1RIYva9o", "libkernel", 1, "libkernel", 1, 1, ORBIS(scePthreadMutexInit)); + LIB_FUNCTION("2Of0f+3mhhE", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_mutex_destroy)); + LIB_FUNCTION("F8bUHwAG284", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_mutexattr_init)); + LIB_FUNCTION("smWEktiyyG0", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_mutexattr_destroy)); + LIB_FUNCTION("iMp8QpE+XO4", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_mutexattr_settype)); + LIB_FUNCTION("1FGvU0i9saQ", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_mutexattr_setprotocol)); + LIB_FUNCTION("9UK1vLZQft4", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_mutex_lock)); + LIB_FUNCTION("tn3VlD0hG60", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_mutex_unlock)); + LIB_FUNCTION("upoVrzMHFeE", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_mutex_trylock)); + LIB_FUNCTION("IafI2PxcPnQ", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_mutex_reltimedlock_np)); + LIB_FUNCTION("qH1gXoq71RY", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_mutex_init)); + LIB_FUNCTION("n2MMpvU8igI", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_mutexattr_init)); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp new file mode 100644 index 000000000..c83af86d0 --- /dev/null +++ b/src/core/libraries/kernel/threads/pthread.cpp @@ -0,0 +1,557 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "common/thread.h" +#include "core/debug_state.h" +#include "core/libraries/kernel/kernel.h" +#include "core/libraries/kernel/posix_error.h" +#include "core/libraries/kernel/threads/pthread.h" +#include "core/libraries/kernel/threads/thread_state.h" +#include "core/libraries/libs.h" +#include "core/memory.h" + +namespace Libraries::Kernel { + +constexpr int PthreadInheritSched = 4; + +constexpr int ORBIS_KERNEL_PRIO_FIFO_DEFAULT = 700; +constexpr int ORBIS_KERNEL_PRIO_FIFO_HIGHEST = 256; +constexpr int ORBIS_KERNEL_PRIO_FIFO_LOWEST = 767; + +extern PthreadAttr PthreadAttrDefault; + +void _thread_cleanupspecific(); + +using ThreadDtor = void (*)(); +static ThreadDtor* ThreadDtors{}; + +void PS4_SYSV_ABI _sceKernelSetThreadDtors(ThreadDtor* dtor) { + ThreadDtors = dtor; +} + +static void ExitThread() { + Pthread* curthread = g_curthread; + + /* Check if there is thread specific data: */ + if (curthread->specific != nullptr) { + /* Run the thread-specific data destructors: */ + _thread_cleanupspecific(); + } + + auto* thread_state = ThrState::Instance(); + ASSERT(thread_state->active_threads.fetch_sub(1) != 1); + + curthread->lock.lock(); + curthread->state = PthreadState::Dead; + ASSERT(False(curthread->flags & ThreadFlags::NeedSuspend)); + + /* + * Thread was created with initial refcount 1, we drop the + * reference count to allow it to be garbage collected. + */ + curthread->refcount--; + thread_state->TryCollect(curthread); /* thread lock released */ + + /* + * Kernel will do wakeup at the address, so joiner thread + * will be resumed if it is sleeping at the address. + */ + curthread->tid.store(TidTerminated); + curthread->tid.notify_all(); + + curthread->native_thr.Exit(); + UNREACHABLE(); + /* Never reach! */ +} + +void PS4_SYSV_ABI posix_pthread_exit(void* status) { + Pthread* curthread = g_curthread; + + /* Check if this thread is already in the process of exiting: */ + ASSERT_MSG(!curthread->cancelling, "Thread {} has called pthread_exit from a destructor", + fmt::ptr(curthread)); + + /* Flag this thread as exiting. */ + curthread->cancelling = 1; + curthread->no_cancel = 1; + curthread->cancel_async = 0; + curthread->cancel_point = 0; + + /* Save the return value: */ + curthread->ret = status; + while (!curthread->cleanup.empty()) { + PthreadCleanup* old = curthread->cleanup.front(); + curthread->cleanup.pop_front(); + old->routine(old->routine_arg); + if (old->onheap) { + delete old; + } + } + /*if (ThreadDtors && *ThreadDtors) { + (*ThreadDtors)(); + }*/ + ExitThread(); +} + +static int JoinThread(PthreadT pthread, void** thread_return, const OrbisKernelTimespec* abstime) { + Pthread* curthread = g_curthread; + + if (pthread == nullptr) { + return POSIX_EINVAL; + } + + if (pthread == curthread) { + return POSIX_EDEADLK; + } + + auto* thread_state = ThrState::Instance(); + if (int ret = thread_state->FindThread(pthread, 1); ret != 0) { + return POSIX_ESRCH; + } + + int ret = 0; + if (True(pthread->flags & ThreadFlags::Detached)) { + ret = POSIX_EINVAL; + } else if (pthread->joiner != nullptr) { + /* Multiple joiners are not supported. */ + ret = POSIX_ENOTSUP; + } + if (ret) { + pthread->lock.unlock(); + return ret; + } + /* Set the running thread to be the joiner: */ + pthread->joiner = curthread; + pthread->lock.unlock(); + + const auto backout_join = [](void* arg) PS4_SYSV_ABI { + Pthread* pthread = (Pthread*)arg; + std::scoped_lock lk{pthread->lock}; + pthread->joiner = nullptr; + }; + + PthreadCleanup cup{backout_join, pthread, 0}; + curthread->cleanup.push_front(&cup); + + //_thr_cancel_enter(curthread); + + const int tid = pthread->tid; + while (pthread->tid.load() != TidTerminated) { + //_thr_testcancel(curthread); + ASSERT(abstime == nullptr); + pthread->tid.wait(tid); + } + + //_thr_cancel_leave(curthread, 0); + curthread->cleanup.pop_front(); + + if (ret == POSIX_ETIMEDOUT) { + backout_join(pthread); + return ret; + } + + void* tmp = pthread->ret; + pthread->lock.lock(); + pthread->flags |= ThreadFlags::Detached; + pthread->joiner = nullptr; + thread_state->TryCollect(pthread); /* thread lock released */ + if (thread_return != nullptr) { + *thread_return = tmp; + } + + return 0; +} + +int PS4_SYSV_ABI posix_pthread_join(PthreadT pthread, void** thread_return) { + return JoinThread(pthread, thread_return, NULL); +} + +int PS4_SYSV_ABI posix_pthread_timedjoin_np(PthreadT pthread, void** thread_return, + const OrbisKernelTimespec* abstime) { + if (abstime == nullptr || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || + abstime->tv_nsec >= 1000000000) { + return POSIX_EINVAL; + } + + return JoinThread(pthread, thread_return, abstime); +} + +int PS4_SYSV_ABI posix_pthread_detach(PthreadT pthread) { + if (pthread == nullptr) { + return POSIX_EINVAL; + } + + auto* thread_state = ThrState::Instance(); + if (int ret = thread_state->FindThread(pthread, 1); ret != 0) { + return ret; + } + + /* Check if the thread is already detached or has a joiner. */ + if (True(pthread->flags & ThreadFlags::Detached) || (pthread->joiner != NULL)) { + pthread->lock.unlock(); + return POSIX_EINVAL; + } + + /* Flag the thread as detached. */ + pthread->flags |= ThreadFlags::Detached; + thread_state->TryCollect(pthread); /* thread lock released */ + return 0; +} + +static void RunThread(void* arg) { + Pthread* curthread = (Pthread*)arg; + g_curthread = curthread; + Common::SetCurrentThreadName(curthread->name.c_str()); + DebugState.AddCurrentThreadToGuestList(); + + /* Run the current thread's start routine with argument: */ + curthread->native_thr.Initialize(); + void* ret = Core::ExecuteGuest(curthread->start_routine, curthread->arg); + + /* Remove thread from tracking */ + DebugState.RemoveCurrentThreadFromGuestList(); + posix_pthread_exit(ret); +} + +int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAttrT* attr, + PthreadEntryFunc start_routine, void* arg, + const char* name) { + Pthread* curthread = g_curthread; + auto* thread_state = ThrState::Instance(); + Pthread* new_thread = thread_state->Alloc(curthread); + if (new_thread == nullptr) { + return POSIX_EAGAIN; + } + + if (attr == nullptr || *attr == nullptr) { + new_thread->attr = PthreadAttrDefault; + } else { + new_thread->attr = *(*attr); + new_thread->attr.cpusetsize = 0; + } + if (new_thread->attr.sched_inherit == PthreadInheritSched) { + if (True(curthread->attr.flags & PthreadAttrFlags::ScopeSystem)) { + new_thread->attr.flags |= PthreadAttrFlags::ScopeSystem; + } else { + new_thread->attr.flags &= ~PthreadAttrFlags::ScopeSystem; + } + new_thread->attr.prio = curthread->attr.prio; + new_thread->attr.sched_policy = curthread->attr.sched_policy; + } + + static int TidCounter = 1; + new_thread->tid = ++TidCounter; + + if (thread_state->CreateStack(&new_thread->attr) != 0) { + /* Insufficient memory to create a stack: */ + thread_state->Free(curthread, new_thread); + return POSIX_EAGAIN; + } + + /* + * Write a magic value to the thread structure + * to help identify valid ones: + */ + new_thread->magic = Pthread::ThrMagic; + new_thread->start_routine = start_routine; + new_thread->arg = arg; + new_thread->cancel_enable = 1; + new_thread->cancel_async = 0; + + auto* memory = Core::Memory::Instance(); + if (name && memory->IsValidAddress(name)) { + new_thread->name = name; + } else { + new_thread->name = fmt::format("Thread{}", new_thread->tid.load()); + } + + ASSERT(new_thread->attr.suspend == 0); + new_thread->state = PthreadState::Running; + + if (True(new_thread->attr.flags & PthreadAttrFlags::Detached)) { + new_thread->flags |= ThreadFlags::Detached; + } + + /* Add the new thread. */ + new_thread->refcount = 1; + thread_state->Link(curthread, new_thread); + + /* Return thread pointer eariler so that new thread can use it. */ + (*thread) = new_thread; + + /* 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 (ret) { + *thread = nullptr; + } + return ret; +} + +int PS4_SYSV_ABI posix_pthread_create(PthreadT* thread, const PthreadAttrT* attr, + PthreadEntryFunc start_routine, void* arg) { + return posix_pthread_create_name_np(thread, attr, start_routine, arg, nullptr); +} + +int PS4_SYSV_ABI posix_pthread_getthreadid_np() { + return g_curthread->tid; +} + +int PS4_SYSV_ABI posix_pthread_getname_np(PthreadT thread, char* name) { + std::memcpy(name, thread->name.data(), std::min(thread->name.size(), 32)); + return 0; +} + +int PS4_SYSV_ABI posix_pthread_equal(PthreadT thread1, PthreadT thread2) { + return (thread1 == thread2 ? 1 : 0); +} + +PthreadT PS4_SYSV_ABI posix_pthread_self() { + return g_curthread; +} + +void PS4_SYSV_ABI posix_pthread_yield() { + std::this_thread::yield(); +} + +void PS4_SYSV_ABI sched_yield() { + std::this_thread::yield(); +} + +int PS4_SYSV_ABI posix_pthread_once(PthreadOnce* once_control, void (*init_routine)()) { + for (;;) { + auto state = once_control->state.load(); + if (state == PthreadOnceState::Done) { + return 0; + } + if (state == PthreadOnceState::NeverDone) { + if (once_control->state.compare_exchange_strong(state, PthreadOnceState::InProgress, + std::memory_order_acquire)) { + break; + } + } else if (state == PthreadOnceState::InProgress) { + if (once_control->state.compare_exchange_strong(state, PthreadOnceState::Wait, + std::memory_order_acquire)) { + once_control->state.wait(PthreadOnceState::Wait); + } + } else if (state == PthreadOnceState::Wait) { + once_control->state.wait(state); + } else { + return POSIX_EINVAL; + } + } + + const auto once_cancel_handler = [](void* arg) PS4_SYSV_ABI { + PthreadOnce* once_control = (PthreadOnce*)arg; + auto state = PthreadOnceState::InProgress; + if (once_control->state.compare_exchange_strong(state, PthreadOnceState::NeverDone, + std::memory_order_release)) { + return; + } + + once_control->state.store(PthreadOnceState::NeverDone, std::memory_order_release); + once_control->state.notify_all(); + }; + + PthreadCleanup cup{once_cancel_handler, once_control, 0}; + g_curthread->cleanup.push_front(&cup); + init_routine(); + g_curthread->cleanup.pop_front(); + + auto state = PthreadOnceState::InProgress; + if (once_control->state.compare_exchange_strong(state, PthreadOnceState::Done, + std::memory_order_release)) { + return 0; + } + once_control->state.store(PthreadOnceState::Done); + once_control->state.notify_all(); + return 0; +} + +int PS4_SYSV_ABI posix_sched_get_priority_max() { + return ORBIS_KERNEL_PRIO_FIFO_HIGHEST; +} + +int PS4_SYSV_ABI posix_sched_get_priority_min() { + return ORBIS_KERNEL_PRIO_FIFO_LOWEST; +} + +int PS4_SYSV_ABI posix_pthread_rename_np(PthreadT thread, const char* name) { + LOG_INFO(Kernel_Pthread, "name = {}", name); + Common::SetThreadName(reinterpret_cast(thread->native_thr.GetHandle()), name); + thread->name = name; + return ORBIS_OK; +} + +int PS4_SYSV_ABI posix_pthread_getschedparam(PthreadT pthread, SchedPolicy* policy, + SchedParam* param) { + if (policy == nullptr || param == nullptr) { + return POSIX_EINVAL; + } + + if (pthread == g_curthread) { + /* + * Avoid searching the thread list when it is the current + * thread. + */ + std::scoped_lock lk{g_curthread->lock}; + *policy = g_curthread->attr.sched_policy; + param->sched_priority = g_curthread->attr.prio; + return 0; + } + auto* thread_state = ThrState::Instance(); + /* Find the thread in the list of active threads. */ + if (int ret = thread_state->RefAdd(pthread, /*include dead*/ 0); ret != 0) { + return ret; + } + pthread->lock.lock(); + *policy = pthread->attr.sched_policy; + param->sched_priority = pthread->attr.prio; + pthread->lock.unlock(); + thread_state->RefDelete(pthread); + return 0; +} + +int PS4_SYSV_ABI posix_pthread_setschedparam(PthreadT pthread, SchedPolicy policy, + const SchedParam* param) { + if (pthread == nullptr || param == nullptr) { + return POSIX_EINVAL; + } + + auto* thread_state = ThrState::Instance(); + if (pthread == g_curthread) { + g_curthread->lock.lock(); + } else if (int ret = thread_state->FindThread(pthread, /*include dead*/ 0); ret != 0) { + return ret; + } + + if (pthread->attr.sched_policy == policy && + (policy == SchedPolicy::Other || pthread->attr.prio == param->sched_priority)) { + pthread->attr.prio = param->sched_priority; + pthread->lock.unlock(); + return 0; + } + + // TODO: _thr_setscheduler + pthread->attr.sched_policy = policy; + pthread->attr.prio = param->sched_priority; + pthread->lock.unlock(); + return 0; +} + +int PS4_SYSV_ABI scePthreadGetprio(PthreadT thread, int* priority) { + SchedParam param; + SchedPolicy policy; + + posix_pthread_getschedparam(thread, &policy, ¶m); + *priority = param.sched_priority; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_setprio(PthreadT thread, int prio) { + SchedParam param; + + param.sched_priority = prio; + + auto* thread_state = ThrState::Instance(); + if (thread == g_curthread) { + g_curthread->lock.lock(); + } else if (int ret = thread_state->FindThread(thread, /*include dead*/ 0); ret != 0) { + return ret; + } + + if (thread->attr.sched_policy == SchedPolicy::Other || thread->attr.prio == prio) { + thread->attr.prio = prio; + } else { + // TODO: _thr_setscheduler + thread->attr.prio = prio; + } + + thread->lock.unlock(); + return 0; +} + +enum class PthreadCancelState : u32 { + Enable = 0, + Disable = 1, +}; + +#define POSIX_PTHREAD_CANCELED ((void*)1) + +static inline void TestCancel(Pthread* curthread) { + if (curthread->ShouldCancel() && !curthread->InCritical()) [[unlikely]] { + posix_pthread_exit(POSIX_PTHREAD_CANCELED); + } +} + +int PS4_SYSV_ABI posix_pthread_setcancelstate(PthreadCancelState state, + PthreadCancelState* oldstate) { + Pthread* curthread = g_curthread; + int oldval = curthread->cancel_enable; + switch (state) { + case PthreadCancelState::Disable: + curthread->cancel_enable = 0; + break; + case PthreadCancelState::Enable: + curthread->cancel_enable = 1; + TestCancel(curthread); + break; + default: + return POSIX_EINVAL; + } + + if (oldstate) { + *oldstate = oldval ? PthreadCancelState::Enable : PthreadCancelState::Disable; + } + return 0; +} + +void RegisterThread(Core::Loader::SymbolsResolver* sym) { + // Posix + LIB_FUNCTION("Z4QosVuAsA0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_once); + LIB_FUNCTION("7Xl257M4VNI", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_equal); + LIB_FUNCTION("CBNtXOoef-E", "libScePosix", 1, "libkernel", 1, 1, posix_sched_get_priority_max); + LIB_FUNCTION("m0iS6jNsXds", "libScePosix", 1, "libkernel", 1, 1, posix_sched_get_priority_min); + LIB_FUNCTION("EotR8a3ASf4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_self); + LIB_FUNCTION("B5GmVDKwpn0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_yield); + LIB_FUNCTION("+U1R4WtXvoc", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_detach); + LIB_FUNCTION("FJrT5LuUBAU", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_exit); + LIB_FUNCTION("h9CcP3J0oVM", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_join); + LIB_FUNCTION("OxhIB8LB-PQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create); + LIB_FUNCTION("Jmi+9w9u0E4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create_name_np); + LIB_FUNCTION("lZzFeSxPl08", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setcancelstate); + LIB_FUNCTION("a2P9wYGeZvc", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setprio); + LIB_FUNCTION("FIs3-UQT9sg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_getschedparam); + LIB_FUNCTION("Xs9hdiD7sAA", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setschedparam); + LIB_FUNCTION("6XG4B33N09g", "libScePosix", 1, "libkernel", 1, 1, sched_yield); + + // Posix-Kernel + LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self); + LIB_FUNCTION("OxhIB8LB-PQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_create); + + // Orbis + LIB_FUNCTION("14bOACANTBo", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_once)); + LIB_FUNCTION("GBUY7ywdULE", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_rename_np)); + LIB_FUNCTION("6UgtwV+0zb4", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_create_name_np)); + LIB_FUNCTION("4qGrR6eoP9Y", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_detach)); + LIB_FUNCTION("onNY9Byn-W8", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_join)); + LIB_FUNCTION("P41kTWUS3EI", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_getschedparam)); + LIB_FUNCTION("oIRFTjoILbg", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_setschedparam)); + LIB_FUNCTION("How7B8Oet6k", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_getname_np)); + LIB_FUNCTION("3kg7rT0NQIs", "libkernel", 1, "libkernel", 1, 1, posix_pthread_exit); + LIB_FUNCTION("aI+OeCz8xrQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self); + LIB_FUNCTION("3PtV6p3QNX4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_equal); + LIB_FUNCTION("T72hz6ffq08", "libkernel", 1, "libkernel", 1, 1, posix_pthread_yield); + LIB_FUNCTION("EI-5-jlq2dE", "libkernel", 1, "libkernel", 1, 1, posix_pthread_getthreadid_np); + LIB_FUNCTION("1tKyG7RlMJo", "libkernel", 1, "libkernel", 1, 1, scePthreadGetprio); + 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); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/pthread.h b/src/core/libraries/kernel/threads/pthread.h new file mode 100644 index 000000000..456c2ef37 --- /dev/null +++ b/src/core/libraries/kernel/threads/pthread.h @@ -0,0 +1,351 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "common/enum.h" +#include "core/libraries/kernel/sync/mutex.h" +#include "core/libraries/kernel/sync/semaphore.h" +#include "core/libraries/kernel/time.h" +#include "core/thread.h" +#include "core/tls.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::Kernel { + +struct Pthread; + +enum class PthreadMutexFlags : u32 { + TypeMask = 0xff, + Defered = 0x200, +}; +DECLARE_ENUM_FLAG_OPERATORS(PthreadMutexFlags) + +enum class PthreadMutexType : u32 { + ErrorCheck = 1, + Recursive = 2, + Normal = 3, + AdaptiveNp = 4, + Max +}; + +enum class PthreadMutexProt : u32 { + None = 0, + Inherit = 1, + Protect = 2, +}; + +struct PthreadMutex { + TimedMutex m_lock; + PthreadMutexFlags m_flags; + Pthread* m_owner; + int m_count; + int m_spinloops; + int m_yieldloops; + PthreadMutexProt m_protocol; + std::string name; + + PthreadMutexType Type() const noexcept { + return static_cast(m_flags & PthreadMutexFlags::TypeMask); + } + + int SelfTryLock(); + int SelfLock(const OrbisKernelTimespec* abstime, u64 usec); + + int TryLock(); + int Lock(const OrbisKernelTimespec* abstime, u64 usec = 0); + + int CvLock(int recurse) { + const int error = Lock(nullptr); + if (error == 0) { + m_count = recurse; + } + return error; + } + + int Unlock(); + + int CvUnlock(int* recurse) { + *recurse = m_count; + m_count = 0; + return Unlock(); + } + + int IsOwned(Pthread* curthread) const; +}; +using PthreadMutexT = PthreadMutex*; + +struct PthreadMutexAttr { + PthreadMutexType m_type; + PthreadMutexProt m_protocol; + int m_ceiling; +}; +using PthreadMutexAttrT = PthreadMutexAttr*; + +enum class PthreadCondFlags : u32 { + Private = 1, + Inited = 2, + Busy = 4, +}; + +enum class ClockId : u32 { + Realtime = 0, + Virtual = 1, + Prof = 2, + Monotonic = 4, + Uptime = 5, + UptimePrecise = 7, + UptimeFast = 8, + RealtimePrecise = 9, + RealtimeFast = 10, + MonotonicPrecise = 11, + MonotonicFast = 12, + Second = 13, + ThreadCputimeID = 14, +}; + +struct PthreadCond { + u32 has_user_waiters; + u32 has_kern_waiters; + u32 flags; + ClockId clock_id; + std::string name; + + int Wait(PthreadMutexT* mutex, const OrbisKernelTimespec* abstime, u64 usec = 0); + + int Signal(); + int Broadcast(); +}; +using PthreadCondT = PthreadCond*; + +struct PthreadCondAttr { + int c_pshared; + ClockId c_clockid; +}; +using PthreadCondAttrT = PthreadCondAttr*; + +using PthreadCleanupFunc = void PS4_SYSV_ABI (*)(void*); + +struct PthreadCleanup { + PthreadCleanupFunc routine; + void* routine_arg; + int onheap; +}; + +enum class PthreadAttrFlags : u32 { + Detached = 1, + ScopeSystem = 2, + InheritSched = 4, + NoFloat = 8, + StackUser = 0x100, +}; +DECLARE_ENUM_FLAG_OPERATORS(PthreadAttrFlags) + +enum class SchedPolicy : u32 { + Fifo = 0, + Other = 2, + RoundRobin = 3, +}; + +struct Cpuset { + u64 bits; +}; + +struct PthreadAttr { + SchedPolicy sched_policy; + int sched_inherit; + int prio; + int suspend; + PthreadAttrFlags flags; + void* stackaddr_attr; + size_t stacksize_attr; + size_t guardsize_attr; + size_t cpusetsize; + Cpuset* cpuset; +}; +using PthreadAttrT = PthreadAttr*; + +static constexpr u32 ThrStackDefault = 1_MB; +static constexpr u32 ThrStackInitial = 2_MB; +static constexpr u32 ThrPageSize = 16_KB; +static constexpr u32 ThrGuardDefault = ThrPageSize; + +struct PthreadRwlockAttr { + int pshared; +}; +using PthreadRwlockAttrT = PthreadRwlockAttr*; + +struct PthreadRwlock { + std::shared_timed_mutex lock; + Pthread* owner; + + int Wrlock(const OrbisKernelTimespec* abstime); + int Rdlock(const OrbisKernelTimespec* abstime); +}; +using PthreadRwlockT = PthreadRwlock*; + +enum class PthreadState : u32 { Running, Dead }; + +struct PthreadSpecificElem { + const void* data; + int seqno; +}; + +using PthreadKeyDestructor = void PS4_SYSV_ABI (*)(const void*); + +struct PthreadKey { + int allocated; + int seqno; + PthreadKeyDestructor destructor; +}; +using PthreadKeyT = s32; + +enum class PthreadOnceState : u32 { + NeverDone = 0, + Done = 1, + InProgress = 2, + Wait = 3, +}; + +struct PthreadOnce { + std::atomic state; + std::mutex mutex; +}; + +enum class ThreadFlags : u32 { + Private = 1, + NeedSuspend = 2, + Suspended = 4, + Detached = 8, +}; +DECLARE_ENUM_FLAG_OPERATORS(ThreadFlags) + +enum class ThreadListFlags : u32 { + GcSafe = 1, + InTdList = 2, + InGcList = 4, +}; + +using PthreadEntryFunc = void* PS4_SYSV_ABI (*)(void*); + +constexpr u32 TidTerminated = 1; + +struct SleepQueue; + +struct SchedParam { + int sched_priority; +}; + +#define THR_RELTIME (const OrbisKernelTimespec*)-1 + +struct Pthread { + static constexpr u32 ThrMagic = 0xd09ba115U; + static constexpr u32 MaxDeferWaiters = 50; + + std::atomic tid; + std::mutex lock; + u32 cycle; + int locklevel; + int critical_count; + int sigblock; + int refcount; + PthreadEntryFunc start_routine; + void* arg; + Core::NativeThread native_thr; + PthreadAttr attr; + bool cancel_enable; + bool cancel_pending; + bool cancel_point; + bool no_cancel; + bool cancel_async; + bool cancelling; + Cpuset sigmask; + bool unblock_sigcancel; + bool in_sigsuspend; + bool force_exit; + PthreadState state; + int error; + Pthread* joiner; + ThreadFlags flags; + ThreadListFlags tlflags; + void* ret; + PthreadSpecificElem* specific; + int specific_data_count; + int rdlock_count; + int rtld_bits; + Core::Tcb* tcb; + std::forward_list cleanup; + u32 pad[27]; + u32 magic; + int report_events; + int event_mask; + std::string name; + BinarySemaphore wake_sema{0}; + SleepQueue* sleepqueue; + void* wchan; + PthreadMutex* mutex_obj; + bool will_sleep; + bool has_user_waiters; + int nwaiter_defer; + BinarySemaphore* defer_waiters[MaxDeferWaiters]; + + bool InCritical() const noexcept { + return locklevel > 0 || critical_count > 0; + } + + bool ShouldCollect() const noexcept { + return refcount == 0 && state == PthreadState::Dead && True(flags & ThreadFlags::Detached); + } + + bool ShouldCancel() const noexcept { + return cancel_pending && cancel_enable && no_cancel == 0; + } + + void WakeAll() { + for (int i = 0; i < nwaiter_defer; i++) { + defer_waiters[i]->release(); + } + nwaiter_defer = 0; + } + + bool Sleep(const OrbisKernelTimespec* abstime, u64 usec) { + will_sleep = 0; + if (nwaiter_defer > 0) { + WakeAll(); + } + if (abstime == THR_RELTIME) { + return wake_sema.try_acquire_for(std::chrono::microseconds(usec)); + } else if (abstime != nullptr) { + return wake_sema.try_acquire_until(abstime->TimePoint()); + } else { + wake_sema.acquire(); + return true; + } + } +}; +using PthreadT = Pthread*; + +extern thread_local Pthread* g_curthread; + +void RegisterMutex(Core::Loader::SymbolsResolver* sym); +void RegisterCond(Core::Loader::SymbolsResolver* sym); +void RegisterRwlock(Core::Loader::SymbolsResolver* sym); +void RegisterSemaphore(Core::Loader::SymbolsResolver* sym); +void RegisterSpec(Core::Loader::SymbolsResolver* sym); +void RegisterThreadAttr(Core::Loader::SymbolsResolver* sym); +void RegisterThread(Core::Loader::SymbolsResolver* sym); +void RegisterRtld(Core::Loader::SymbolsResolver* sym); +void RegisterKernelEventFlag(Core::Loader::SymbolsResolver* sym); +void RegisterPthreadClean(Core::Loader::SymbolsResolver* sym); + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/pthread_attr.cpp b/src/core/libraries/kernel/threads/pthread_attr.cpp new file mode 100644 index 000000000..a8e60ccf8 --- /dev/null +++ b/src/core/libraries/kernel/threads/pthread_attr.cpp @@ -0,0 +1,347 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/libraries/kernel/kernel.h" +#include "core/libraries/kernel/posix_error.h" +#include "core/libraries/kernel/threads/pthread.h" +#include "core/libraries/kernel/threads/thread_state.h" +#include "core/libraries/libs.h" + +namespace Libraries::Kernel { + +static constexpr u32 PthreadStackMin = 16_KB; + +struct PthreadPrio { + s32 pri_min; + s32 pri_max; + s32 pri_default; +}; + +static constexpr std::array ThrPriorities = {{ + {0x100, 0x2FF, 0x2BC}, // Fifo + {0x300, 0x3BF, 0x384}, // Other + {0x100, 0x2FF, 0x2BC}, // Round-Robin +}}; + +PthreadAttr PthreadAttrDefault = { + .sched_policy = SchedPolicy::Fifo, + .sched_inherit = 0, + .prio = 0, + .suspend = false, + .flags = PthreadAttrFlags::ScopeSystem, + .stackaddr_attr = NULL, + .stacksize_attr = ThrStackDefault, + .guardsize_attr = 0, + .cpusetsize = 0, + .cpuset = nullptr, +}; + +int PS4_SYSV_ABI posix_pthread_attr_destroy(PthreadAttrT* attr) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + delete *attr; + *attr = nullptr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_getdetachstate(const PthreadAttrT* attr, int* detachstate) { + if (attr == nullptr || *attr == nullptr || detachstate == nullptr) { + return POSIX_EINVAL; + } + *detachstate = True((*attr)->flags & PthreadAttrFlags::Detached); + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_getguardsize(const PthreadAttrT* attr, size_t* guardsize) { + if (attr == nullptr || *attr == nullptr || guardsize == nullptr) { + return POSIX_EINVAL; + } + *guardsize = (*attr)->guardsize_attr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_getinheritsched(const PthreadAttrT* attr, int* sched_inherit) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + *sched_inherit = (*attr)->sched_inherit; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_getschedparam(const PthreadAttrT* attr, SchedParam* param) { + if (attr == nullptr || *attr == nullptr || param == nullptr) { + return POSIX_EINVAL; + } + param->sched_priority = (*attr)->prio; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_getschedpolicy(const PthreadAttrT* attr, SchedPolicy* policy) { + if (attr == nullptr || *attr == nullptr || policy == nullptr) { + return POSIX_EINVAL; + } + *policy = (*attr)->sched_policy; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_getstack(const PthreadAttrT* attr, void** stackaddr, + size_t* stacksize) { + if (attr == nullptr || *attr == nullptr || stackaddr == nullptr || stacksize == nullptr) { + return POSIX_EINVAL; + } + *stackaddr = (*attr)->stackaddr_attr; + *stacksize = (*attr)->stacksize_attr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_getstackaddr(const PthreadAttrT* attr, void** stackaddr) { + if (attr == nullptr || *attr == nullptr || stackaddr == nullptr) { + return POSIX_EINVAL; + } + *stackaddr = (*attr)->stackaddr_attr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_getstacksize(const PthreadAttrT* attr, size_t* stacksize) { + if (attr == nullptr || *attr == nullptr || stacksize == nullptr) { + return POSIX_EINVAL; + } + *stacksize = (*attr)->stacksize_attr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_init(PthreadAttrT* attr) { + PthreadAttrT pattr = new PthreadAttr{}; + if (pattr == nullptr) { + return POSIX_ENOMEM; + } + memcpy(pattr, &PthreadAttrDefault, sizeof(PthreadAttr)); + *attr = pattr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_setschedpolicy(PthreadAttrT* attr, SchedPolicy policy) { + if (attr == NULL || *attr == NULL) { + return POSIX_EINVAL; + } else if ((policy < SchedPolicy::Fifo) || (policy > SchedPolicy::RoundRobin)) { + return POSIX_ENOTSUP; + } + (*attr)->sched_policy = policy; + (*attr)->prio = ThrPriorities[u32(policy) - 1].pri_default; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_setstack(PthreadAttrT* attr, void* stackaddr, + size_t stacksize) { + if (attr == nullptr || *attr == nullptr || stackaddr == nullptr || + stacksize < PthreadStackMin) { + return POSIX_EINVAL; + } + (*attr)->stackaddr_attr = stackaddr; + (*attr)->stacksize_attr = stacksize; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_setstackaddr(PthreadAttrT* attr, void* stackaddr) { + if (attr == nullptr || *attr == nullptr || stackaddr == nullptr) { + return POSIX_EINVAL; + } + (*attr)->stackaddr_attr = stackaddr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_setstacksize(PthreadAttrT* attr, size_t stacksize) { + if (attr == nullptr || *attr == nullptr || stacksize < PthreadStackMin) { + return POSIX_EINVAL; + } + (*attr)->stacksize_attr = stacksize; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_setdetachstate(PthreadAttrT* attr, int detachstate) { + if (attr == nullptr || *attr == nullptr || (detachstate != 1 && detachstate != 0)) { + return POSIX_EINVAL; + } + if (detachstate) { + (*attr)->flags |= PthreadAttrFlags::Detached; + } else { + (*attr)->flags &= ~PthreadAttrFlags::Detached; + } + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_setschedparam(PthreadAttrT* attr, SchedParam* param) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + if (param == nullptr) { + return POSIX_ENOTSUP; + } + + const auto policy = (*attr)->sched_policy; + if (policy == SchedPolicy::RoundRobin) { + if (param->sched_priority < ThrPriorities[u32(policy) - 1].pri_min || + param->sched_priority > ThrPriorities[u32(policy) - 1].pri_max) { + return POSIX_ENOTSUP; + } + } + (*attr)->prio = param->sched_priority; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_setinheritsched(PthreadAttrT* attr, int sched_inherit) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + if (sched_inherit != 4 && sched_inherit != 0) { + return POSIX_ENOTSUP; + } + + (*attr)->sched_inherit = sched_inherit; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_setguardsize(PthreadAttrT* attr, size_t guardsize) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + (*attr)->guardsize_attr = guardsize; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_get_np(PthreadT pthread, PthreadAttrT* dstattr) { + PthreadAttr* dst; + if (pthread == nullptr || dstattr == nullptr || (dst = *dstattr) == nullptr) { + return POSIX_EINVAL; + } + auto* thread_state = ThrState::Instance(); + int ret = thread_state->FindThread(pthread, /*include dead*/ 0); + if (ret != 0) { + return ret; + } + PthreadAttr attr = pthread->attr; + if (True(pthread->flags & ThreadFlags::Detached)) { + attr.flags |= PthreadAttrFlags::Detached; + } + pthread->lock.unlock(); + if (ret == 0) { + memcpy(dst, &attr, sizeof(PthreadAttr)); + } + return ret; +} + +int PS4_SYSV_ABI posix_pthread_attr_getaffinity_np(const PthreadAttrT* pattr, size_t cpusetsize, + Cpuset* cpusetp) { + if (pattr == nullptr) { + return POSIX_EINVAL; + } + PthreadAttrT attr = *pattr; + if (attr == nullptr) { + return POSIX_EINVAL; + } + if (attr->cpuset != nullptr) + memcpy(cpusetp, attr->cpuset, std::min(cpusetsize, attr->cpusetsize)); + else + memset(cpusetp, -1, sizeof(Cpuset)); + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_setaffinity_np(PthreadAttrT* pattr, size_t cpusetsize, + const Cpuset* cpusetp) { + if (pattr == nullptr) { + return POSIX_EINVAL; + } + PthreadAttrT attr = *pattr; + if (attr == nullptr) { + return POSIX_EINVAL; + } + if (cpusetsize == 0 || cpusetp == nullptr) { + if (attr->cpuset != nullptr) { + free(attr->cpuset); + attr->cpuset = NULL; + attr->cpusetsize = 0; + } + return 0; + } + if (attr->cpuset == nullptr) { + attr->cpuset = (Cpuset*)calloc(1, sizeof(Cpuset)); + attr->cpusetsize = sizeof(Cpuset); + } + memcpy(attr->cpuset, cpusetp, sizeof(Cpuset)); + return 0; +} + +int PS4_SYSV_ABI scePthreadAttrGetaffinity(PthreadAttrT* param_1, Cpuset* mask) { + Cpuset cpuset; + const int ret = posix_pthread_attr_getaffinity_np(param_1, 0x10, &cpuset); + if (ret == 0) { + *mask = cpuset; + } + return ret; +} + +int PS4_SYSV_ABI scePthreadAttrSetaffinity(PthreadAttrT* attr, const Cpuset mask) { + return posix_pthread_attr_setaffinity_np(attr, 0x10, &mask); +} + +void RegisterThreadAttr(Core::Loader::SymbolsResolver* sym) { + // Posix + LIB_FUNCTION("wtkt-teR1so", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_attr_init); + LIB_FUNCTION("2Q0z6rnBrTE", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_attr_setstacksize); + LIB_FUNCTION("RtLRV-pBTTY", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_attr_getschedpolicy); + LIB_FUNCTION("E+tyo3lp5Lw", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_attr_setdetachstate); + LIB_FUNCTION("zHchY8ft5pk", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_attr_destroy); + LIB_FUNCTION("euKRgm0Vn2M", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_attr_setschedparam); + LIB_FUNCTION("7ZlAakEf0Qg", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_attr_setinheritsched); + LIB_FUNCTION("0qOtCR-ZHck", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_attr_getstacksize); + LIB_FUNCTION("VUT1ZSrHT0I", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_attr_getdetachstate); + LIB_FUNCTION("JKyG3SWyA10", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_attr_setguardsize); + + // Orbis + LIB_FUNCTION("4+h9EzwKF4I", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_setschedpolicy)); + LIB_FUNCTION("-Wreprtu0Qs", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_setdetachstate)); + LIB_FUNCTION("JaRMy+QcpeU", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_getdetachstate)); + LIB_FUNCTION("eXbUSpEaTsA", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_setinheritsched)); + LIB_FUNCTION("DzES9hQF4f4", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_setschedparam)); + LIB_FUNCTION("nsYoNRywwNg", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_attr_init)); + LIB_FUNCTION("62KCwEMmzcM", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_destroy)); + LIB_FUNCTION("-quPa4SEJUw", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_getstack)); + LIB_FUNCTION("Bvn74vj6oLo", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_setstack)); + LIB_FUNCTION("Ru36fiTtJzA", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_getstackaddr)); + LIB_FUNCTION("-fA+7ZlGDQs", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_getstacksize)); + LIB_FUNCTION("x1X76arYMxU", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_get_np)); + LIB_FUNCTION("FXPWHNk8Of0", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_getschedparam)); + LIB_FUNCTION("UTXzJbWhhTE", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_setstacksize)); + LIB_FUNCTION("F+yfmduIBB8", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_setstackaddr)); + LIB_FUNCTION("El+cQ20DynU", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_setguardsize)); + LIB_FUNCTION("8+s5BzZjxSg", "libkernel", 1, "libkernel", 1, 1, + ORBIS(scePthreadAttrGetaffinity)); + LIB_FUNCTION("3qxgM4ezETA", "libkernel", 1, "libkernel", 1, 1, + ORBIS(scePthreadAttrSetaffinity)); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/pthread_clean.cpp b/src/core/libraries/kernel/threads/pthread_clean.cpp new file mode 100644 index 000000000..4ed15f7a3 --- /dev/null +++ b/src/core/libraries/kernel/threads/pthread_clean.cpp @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/libraries/kernel/threads/pthread.h" +#include "core/libraries/libs.h" + +namespace Libraries::Kernel { + +void PS4_SYSV_ABI __pthread_cleanup_push_imp(PthreadCleanupFunc routine, void* arg, + PthreadCleanup* newbuf) { + newbuf->routine = routine; + newbuf->routine_arg = arg; + newbuf->onheap = 0; + g_curthread->cleanup.push_front(newbuf); +} + +void PS4_SYSV_ABI posix_pthread_cleanup_push(PthreadCleanupFunc routine, void* arg) { + Pthread* curthread = g_curthread; + PthreadCleanup* newbuf = new PthreadCleanup{}; + if (newbuf == nullptr) { + return; + } + + newbuf->routine = routine; + newbuf->routine_arg = arg; + newbuf->onheap = 1; + curthread->cleanup.push_front(newbuf); +} + +void PS4_SYSV_ABI posix_pthread_cleanup_pop(int execute) { + Pthread* curthread = g_curthread; + if (!curthread->cleanup.empty()) { + PthreadCleanup* old = curthread->cleanup.front(); + curthread->cleanup.pop_front(); + if (execute) { + old->routine(old->routine_arg); + } + if (old->onheap) { + delete old; + } + } +} + +void RegisterPthreadClean(Core::Loader::SymbolsResolver* sym) { + // Posix + LIB_FUNCTION("4ZeZWcMsAV0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cleanup_push); + LIB_FUNCTION("RVxb0Ssa5t0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cleanup_pop); + + // Posix-Kernel + LIB_FUNCTION("1xvtUVx1-Sg", "libkernel", 1, "libkernel", 1, 1, __pthread_cleanup_push_imp); + LIB_FUNCTION("iWsFlYMf3Kw", "libkernel", 1, "libkernel", 1, 1, posix_pthread_cleanup_pop); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/pthread_spec.cpp b/src/core/libraries/kernel/threads/pthread_spec.cpp new file mode 100644 index 000000000..9e625da32 --- /dev/null +++ b/src/core/libraries/kernel/threads/pthread_spec.cpp @@ -0,0 +1,158 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/libraries/kernel/kernel.h" +#include "core/libraries/kernel/posix_error.h" +#include "core/libraries/kernel/threads/pthread.h" +#include "core/libraries/libs.h" + +namespace Libraries::Kernel { + +static constexpr u32 PthreadKeysMax = 256; +static constexpr u32 PthreadDestructorIterations = 4; + +static std::array ThreadKeytable{}; +static std::mutex KeytableLock; + +int PS4_SYSV_ABI posix_pthread_key_create(PthreadKeyT* key, PthreadKeyDestructor destructor) { + std::scoped_lock lk{KeytableLock}; + const auto it = std::ranges::find(ThreadKeytable, 0, &PthreadKey::allocated); + if (it != ThreadKeytable.end()) { + it->allocated = 1; + it->destructor = destructor; + it->seqno++; + *key = std::distance(ThreadKeytable.begin(), it); + return 0; + } + return POSIX_EAGAIN; +} + +int PS4_SYSV_ABI posix_pthread_key_delete(PthreadKeyT key) { + if (key >= PthreadKeysMax) { + return POSIX_EINVAL; + } + + std::scoped_lock lk{KeytableLock}; + if (!ThreadKeytable[key].allocated) { + return POSIX_EINVAL; + } + + ThreadKeytable[key].allocated = 0; + return 0; +} + +void _thread_cleanupspecific() { + Pthread* curthread = g_curthread; + PthreadKeyDestructor destructor; + const void* data = NULL; + + if (curthread->specific == nullptr) { + return; + } + + std::unique_lock lk{KeytableLock}; + for (int i = 0; (i < PthreadDestructorIterations) && (curthread->specific_data_count > 0); + i++) { + for (int key = 0; (key < PthreadKeysMax) && (curthread->specific_data_count > 0); key++) { + destructor = nullptr; + + if (ThreadKeytable[key].allocated && (curthread->specific[key].data != nullptr)) { + if (curthread->specific[key].seqno == ThreadKeytable[key].seqno) { + data = curthread->specific[key].data; + destructor = ThreadKeytable[key].destructor; + } + curthread->specific[key].data = nullptr; + curthread->specific_data_count--; + } else if (curthread->specific[key].data != NULL) { + /* + * This can happen if the key is deleted via + * pthread_key_delete without first setting the value + * to NULL in all threads. POSIX says that the + * destructor is not invoked in this case. + */ + curthread->specific[key].data = nullptr; + curthread->specific_data_count--; + } + + /* + * If there is a destructor, call it + * with the key table entry unlocked: + */ + if (destructor != nullptr) { + /* + * Don't hold the lock while calling the + * destructor: + */ + lk.unlock(); + Core::ExecuteGuest(destructor, data); + lk.lock(); + } + } + } + delete[] curthread->specific; + curthread->specific = nullptr; + if (curthread->specific_data_count > 0) { + LOG_WARNING(Lib_Kernel, "Thread has exited with leftover thread-specific data"); + } +} + +int PS4_SYSV_ABI posix_pthread_setspecific(PthreadKeyT key, const void* value) { + int ret = 0; + Pthread* pthread = g_curthread; + + if (!pthread->specific) { + pthread->specific = new PthreadSpecificElem[PthreadKeysMax]{}; + if (!pthread->specific) { + return POSIX_ENOMEM; + } + } + if (key >= PthreadKeysMax) { + return POSIX_EINVAL; + } + if (!ThreadKeytable[key].allocated) { + return POSIX_EINVAL; + } + + if (pthread->specific[key].data == nullptr) { + if (value != nullptr) { + pthread->specific_data_count++; + } + } else if (value == nullptr) { + pthread->specific_data_count--; + } + pthread->specific[key].data = value; + pthread->specific[key].seqno = ThreadKeytable[key].seqno; + return 0; +} + +const void* PS4_SYSV_ABI posix_pthread_getspecific(PthreadKeyT key) { + Pthread* pthread = g_curthread; + + if (!pthread->specific || key >= PthreadKeysMax) { + return nullptr; + } + + if (ThreadKeytable[key].allocated && + (pthread->specific[key].seqno == ThreadKeytable[key].seqno)) { + return pthread->specific[key].data; + } + + return nullptr; +} + +void RegisterSpec(Core::Loader::SymbolsResolver* sym) { + // Posix + LIB_FUNCTION("mqULNdimTn0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_key_create); + LIB_FUNCTION("6BpEZuDT7YI", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_key_delete); + LIB_FUNCTION("0-KXaS70xy4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_getspecific); + LIB_FUNCTION("WrOLvHU0yQM", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setspecific); + + // Orbis + LIB_FUNCTION("geDaqgH9lTg", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_key_create)); + LIB_FUNCTION("PrdHuuDekhY", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_key_delete)); + LIB_FUNCTION("eoht7mQOCmo", "libkernel", 1, "libkernel", 1, 1, posix_pthread_getspecific); + LIB_FUNCTION("+BzXYkqYeLE", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_setspecific)); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/rwlock.cpp b/src/core/libraries/kernel/threads/rwlock.cpp index 87271fe21..affaaf994 100644 --- a/src/core/libraries/kernel/threads/rwlock.cpp +++ b/src/core/libraries/kernel/threads/rwlock.cpp @@ -1,326 +1,243 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "common/logging/log.h" -#include "core/libraries/error_codes.h" +#include "core/libraries/kernel/kernel.h" +#include "core/libraries/kernel/posix_error.h" +#include "core/libraries/kernel/threads/pthread.h" #include "core/libraries/libs.h" -#include "threads.h" namespace Libraries::Kernel { -extern PThreadCxt* g_pthread_cxt; +static std::mutex RwlockStaticLock; -int PS4_SYSV_ABI posix_pthread_rwlock_destroy(OrbisPthreadRwlock* rwlock) { - int result = pthread_rwlock_destroy(&(*rwlock)->pth_rwlock); - delete *rwlock; +#define THR_RWLOCK_INITIALIZER ((PthreadRwlock*)NULL) +#define THR_RWLOCK_DESTROYED ((PthreadRwlock*)1) + +#define CHECK_AND_INIT_RWLOCK \ + if (prwlock = (*rwlock); prwlock <= THR_RWLOCK_DESTROYED) [[unlikely]] { \ + if (prwlock == THR_RWLOCK_INITIALIZER) { \ + int ret; \ + ret = InitStatic(g_curthread, rwlock); \ + if (ret) \ + return (ret); \ + } else if (prwlock == THR_RWLOCK_DESTROYED) { \ + return POSIX_EINVAL; \ + } \ + prwlock = *rwlock; \ + } + +static int RwlockInit(PthreadRwlockT* rwlock, const PthreadRwlockAttrT* attr) { + PthreadRwlock* prwlock = new PthreadRwlock{}; + if (prwlock == nullptr) { + return POSIX_ENOMEM; + } + *rwlock = prwlock; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_rwlock_destroy(PthreadRwlockT* rwlock) { + PthreadRwlockT prwlock = *rwlock; + if (prwlock == THR_RWLOCK_INITIALIZER) { + return 0; + } + if (prwlock == THR_RWLOCK_DESTROYED) { + return POSIX_EINVAL; + } + *rwlock = THR_RWLOCK_DESTROYED; + delete prwlock; + return 0; +} + +static int InitStatic(Pthread* thread, PthreadRwlockT* rwlock) { + std::scoped_lock lk{RwlockStaticLock}; + if (*rwlock == THR_RWLOCK_INITIALIZER) { + return RwlockInit(rwlock, nullptr); + } + return 0; +} + +int PS4_SYSV_ABI posix_pthread_rwlock_init(PthreadRwlockT* rwlock, const PthreadRwlockAttrT* attr) { *rwlock = nullptr; - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlock_destroy: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; + return RwlockInit(rwlock, attr); +} + +int PthreadRwlock::Rdlock(const OrbisKernelTimespec* abstime) { + Pthread* curthread = g_curthread; + + /* + * POSIX said the validity of the abstimeout parameter need + * not be checked if the lock can be immediately acquired. + */ + if (lock.try_lock_shared()) { + curthread->rdlock_count++; + return 0; } - return result; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_init(OrbisPthreadRwlock* rwlock, - const OrbisPthreadRwlockattr* attr, const char* name) { - *rwlock = new PthreadRwInternal{}; - if (attr == nullptr || *attr == nullptr) { - attr = g_pthread_cxt->getDefaultRwattr(); - } - int result = pthread_rwlock_init(&(*rwlock)->pth_rwlock, &(*attr)->attr_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlock_init: error = {}", result); - } - return ORBIS_OK; -} - -OrbisPthreadRwlock* createRwlock(OrbisPthreadRwlock* rwlock) { - if (rwlock == nullptr || *rwlock != nullptr) { - return rwlock; - } - static std::mutex mutex; - std::scoped_lock lk{mutex}; - if (*rwlock != nullptr) { - return rwlock; - } - const VAddr addr = std::bit_cast(rwlock); - const auto name = fmt::format("rwlock{:#x}", addr); - posix_pthread_rwlock_init(rwlock, nullptr, name.c_str()); - return rwlock; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_rdlock(OrbisPthreadRwlock* rwlock) { - rwlock = createRwlock(rwlock); - int result = pthread_rwlock_rdlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlock_rdlock: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_reltimedrdlock_np() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_reltimedwrlock_np() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_setname_np() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_timedrdlock() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_timedwrlock() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_tryrdlock(OrbisPthreadRwlock* rwlock) { - rwlock = createRwlock(rwlock); - if (rwlock == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - int result = pthread_rwlock_tryrdlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlock_tryrdlock: error = {}", result); - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_trywrlock(OrbisPthreadRwlock* rwlock) { - rwlock = createRwlock(rwlock); - if (rwlock == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - int result = pthread_rwlock_trywrlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlock_trywrlock: error = {}", result); - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_unlock(OrbisPthreadRwlock* rwlock) { - rwlock = createRwlock(rwlock); - if (rwlock == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - int result = pthread_rwlock_unlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlock_unlock: error = {}", result); - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_wrlock(OrbisPthreadRwlock* rwlock) { - rwlock = createRwlock(rwlock); - if (rwlock == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - int result = pthread_rwlock_wrlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlock_wrlock: error = {}", result); - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_rwlockattr_destroy(OrbisPthreadRwlockattr* attr) { - int result = pthread_rwlockattr_destroy(&(*attr)->attr_rwlock); - delete *attr; - *attr = nullptr; - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlockattr_destroy: error = {}", result); - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_rwlockattr_getpshared() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_rwlockattr_gettype_np() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_rwlockattr_init(OrbisPthreadRwlockattr* attr) { - *attr = new PthreadRwLockAttrInternal{}; - int result = pthread_rwlockattr_init(&(*attr)->attr_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlockattr_init: error = {}", result); - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_rwlockattr_setpshared() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_rwlockattr_settype_np() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI scePthreadRwlockattrDestroy(OrbisPthreadRwlockattr* attr) { - int result = pthread_rwlockattr_destroy(&(*attr)->attr_rwlock); - delete *attr; - *attr = nullptr; - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadRwlockattrDestroy: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; -} - -int PS4_SYSV_ABI scePthreadRwlockattrGetpshared() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI scePthreadRwlockattrGettype() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI scePthreadRwlockattrInit(OrbisPthreadRwlockattr* attr) { - *attr = new PthreadRwLockAttrInternal{}; - int result = pthread_rwlockattr_init(&(*attr)->attr_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadRwlockattrInit: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; -} - -int PS4_SYSV_ABI scePthreadRwlockattrSetpshared() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI scePthreadRwlockattrSettype() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI scePthreadRwlockDestroy(OrbisPthreadRwlock* rwlock) { - int result = pthread_rwlock_destroy(&(*rwlock)->pth_rwlock); - delete *rwlock; - *rwlock = nullptr; - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadRwlockDestroy: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; -} - -int PS4_SYSV_ABI scePthreadRwlockInit(OrbisPthreadRwlock* rwlock, - const OrbisPthreadRwlockattr* attr, const char* name) { - *rwlock = new PthreadRwInternal{}; - if (rwlock == nullptr || *rwlock == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; + if (abstime && (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0)) [[unlikely]] { + return POSIX_EINVAL; } - if (attr == nullptr || *attr == nullptr) { - attr = g_pthread_cxt->getDefaultRwattr(); + // Note: On interruption an attempt to relock the mutex is made. + if (abstime != nullptr) { + if (!lock.try_lock_shared_until(abstime->TimePoint())) { + return POSIX_ETIMEDOUT; + } + } else { + lock.lock_shared(); } - if (name != nullptr) { - (*rwlock)->name = name; - } - int result = pthread_rwlock_init(&(*rwlock)->pth_rwlock, &(*attr)->attr_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadRwlockInit: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return ORBIS_OK; + + curthread->rdlock_count++; + return 0; } -int PS4_SYSV_ABI scePthreadRwlockRdlock(OrbisPthreadRwlock* rwlock) { - if (rwlock == nullptr || *rwlock == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; +int PthreadRwlock::Wrlock(const OrbisKernelTimespec* abstime) { + Pthread* curthread = g_curthread; + + /* + * POSIX said the validity of the abstimeout parameter need + * not be checked if the lock can be immediately acquired. + */ + if (lock.try_lock()) { + owner = curthread; + return 0; } - int result = pthread_rwlock_rdlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadRwlockRdlock: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; + + if (abstime && (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0)) { + return POSIX_EINVAL; } - return result; + + // Note: On interruption an attempt to relock the mutex is made. + if (abstime != nullptr) { + if (!lock.try_lock_until(abstime->TimePoint())) { + return POSIX_ETIMEDOUT; + } + } else { + lock.lock(); + } + + owner = curthread; + return 0; } -int PS4_SYSV_ABI scePthreadRwlockTimedrdlock() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI posix_pthread_rwlock_rdlock(PthreadRwlockT* rwlock) { + PthreadRwlockT prwlock{}; + CHECK_AND_INIT_RWLOCK + return prwlock->Rdlock(nullptr); } -int PS4_SYSV_ABI scePthreadRwlockTimedwrlock() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI posix_pthread_rwlock_timedrdlock(PthreadRwlockT* rwlock, + const OrbisKernelTimespec* abstime) { + PthreadRwlockT prwlock{}; + CHECK_AND_INIT_RWLOCK + return prwlock->Rdlock(abstime); } -int PS4_SYSV_ABI scePthreadRwlockTryrdlock(OrbisPthreadRwlock* rwlock) { - if (rwlock == nullptr || *rwlock == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; +int PS4_SYSV_ABI posix_pthread_rwlock_tryrdlock(PthreadRwlockT* rwlock) { + Pthread* curthread = g_curthread; + PthreadRwlockT prwlock{}; + CHECK_AND_INIT_RWLOCK + + if (!prwlock->lock.try_lock_shared()) { + return POSIX_EBUSY; } - int result = pthread_rwlock_tryrdlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadRwlockTryrdlock: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; + + curthread->rdlock_count++; + return 0; } -int PS4_SYSV_ABI scePthreadRwlockTrywrlock(OrbisPthreadRwlock* rwlock) { - if (rwlock == nullptr || *rwlock == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; +int PS4_SYSV_ABI posix_pthread_rwlock_trywrlock(PthreadRwlockT* rwlock) { + Pthread* curthread = g_curthread; + PthreadRwlockT prwlock{}; + CHECK_AND_INIT_RWLOCK + + if (!prwlock->lock.try_lock()) { + return POSIX_EBUSY; } - int result = pthread_rwlock_trywrlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadRwlockTrywrlock: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; + prwlock->owner = curthread; + return 0; } -int PS4_SYSV_ABI scePthreadRwlockUnlock(OrbisPthreadRwlock* rwlock) { - if (rwlock == nullptr || *rwlock == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - int result = pthread_rwlock_unlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadRwlockUnlock: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; +int PS4_SYSV_ABI posix_pthread_rwlock_wrlock(PthreadRwlockT* rwlock) { + PthreadRwlockT prwlock{}; + CHECK_AND_INIT_RWLOCK + return prwlock->Wrlock(nullptr); } -int PS4_SYSV_ABI scePthreadRwlockWrlock(OrbisPthreadRwlock* rwlock) { - rwlock = createRwlock(rwlock); - int result = pthread_rwlock_wrlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadRwlockWrlock: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; +int PS4_SYSV_ABI posix_pthread_rwlock_timedwrlock(PthreadRwlockT* rwlock, + const OrbisKernelTimespec* abstime) { + PthreadRwlockT prwlock{}; + CHECK_AND_INIT_RWLOCK + return prwlock->Wrlock(abstime); } -void RwlockSymbolsRegister(Core::Loader::SymbolsResolver* sym) { +int PS4_SYSV_ABI posix_pthread_rwlock_unlock(PthreadRwlockT* rwlock) { + Pthread* curthread = g_curthread; + PthreadRwlockT prwlock = *rwlock; + if (prwlock <= THR_RWLOCK_DESTROYED) [[unlikely]] { + return POSIX_EINVAL; + } + + if (prwlock->owner == curthread) { + prwlock->lock.unlock(); + prwlock->owner = nullptr; + } else { + prwlock->lock.unlock_shared(); + if (prwlock->owner == nullptr) { + curthread->rdlock_count--; + } + } + + return 0; +} + +int PS4_SYSV_ABI posix_pthread_rwlockattr_destroy(PthreadRwlockAttrT* rwlockattr) { + if (rwlockattr == nullptr) { + return POSIX_EINVAL; + } + PthreadRwlockAttrT prwlockattr = *rwlockattr; + if (prwlockattr == nullptr) { + return POSIX_EINVAL; + } + + delete prwlockattr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_rwlockattr_getpshared(const PthreadRwlockAttrT* rwlockattr, + int* pshared) { + *pshared = (*rwlockattr)->pshared; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_rwlockattr_init(PthreadRwlockAttrT* rwlockattr) { + if (rwlockattr == nullptr) { + return POSIX_EINVAL; + } + + PthreadRwlockAttrT prwlockattr = new PthreadRwlockAttr{}; + if (prwlockattr == nullptr) { + return POSIX_ENOMEM; + } + + prwlockattr->pshared = 0; + *rwlockattr = prwlockattr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_rwlockattr_setpshared(PthreadRwlockAttrT* rwlockattr, int pshared) { + /* Only PTHREAD_PROCESS_PRIVATE is supported. */ + if (pshared != 0) { + return POSIX_EINVAL; + } + + (*rwlockattr)->pshared = pshared; + return 0; +} + +void RegisterRwlock(Core::Loader::SymbolsResolver* sym) { + // Posix-Kernel LIB_FUNCTION("1471ajPzxh0", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_destroy); LIB_FUNCTION("ytQULN-nhL4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_init); LIB_FUNCTION("iGjsr1WAtI0", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_rdlock); - LIB_FUNCTION("dYv-+If2GPk", "libkernel", 1, "libkernel", 1, 1, - posix_pthread_rwlock_reltimedrdlock_np); - LIB_FUNCTION("RRnSj8h8VR4", "libkernel", 1, "libkernel", 1, 1, - posix_pthread_rwlock_reltimedwrlock_np); - LIB_FUNCTION("Uwxgnsi3xeM", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_setname_np); LIB_FUNCTION("lb8lnYo-o7k", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_timedrdlock); LIB_FUNCTION("9zklzAl9CGM", "libkernel", 1, "libkernel", 1, 1, @@ -333,13 +250,11 @@ void RwlockSymbolsRegister(Core::Loader::SymbolsResolver* sym) { posix_pthread_rwlockattr_destroy); LIB_FUNCTION("VqEMuCv-qHY", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlockattr_getpshared); - LIB_FUNCTION("l+bG5fsYkhg", "libkernel", 1, "libkernel", 1, 1, - posix_pthread_rwlockattr_gettype_np); LIB_FUNCTION("xFebsA4YsFI", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlockattr_init); LIB_FUNCTION("OuKg+kRDD7U", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlockattr_setpshared); - LIB_FUNCTION("8NuOHiTr1Vw", "libkernel", 1, "libkernel", 1, 1, - posix_pthread_rwlockattr_settype_np); + + // Posix LIB_FUNCTION("1471ajPzxh0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_rwlock_destroy); LIB_FUNCTION("ytQULN-nhL4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_rwlock_init); LIB_FUNCTION("iGjsr1WAtI0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_rwlock_rdlock); @@ -357,27 +272,37 @@ void RwlockSymbolsRegister(Core::Loader::SymbolsResolver* sym) { posix_pthread_rwlockattr_destroy); LIB_FUNCTION("VqEMuCv-qHY", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_rwlockattr_getpshared); - LIB_FUNCTION("l+bG5fsYkhg", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_rwlockattr_gettype_np); LIB_FUNCTION("xFebsA4YsFI", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_rwlockattr_init); LIB_FUNCTION("OuKg+kRDD7U", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_rwlockattr_setpshared); - LIB_FUNCTION("8NuOHiTr1Vw", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_rwlockattr_settype_np); - LIB_FUNCTION("i2ifZ3fS2fo", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockattrDestroy); - LIB_FUNCTION("LcOZBHGqbFk", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockattrGetpshared); - LIB_FUNCTION("Kyls1ChFyrc", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockattrGettype); - LIB_FUNCTION("yOfGg-I1ZII", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockattrInit); - LIB_FUNCTION("-ZvQH18j10c", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockattrSetpshared); - LIB_FUNCTION("h-OifiouBd8", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockattrSettype); - LIB_FUNCTION("BB+kb08Tl9A", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockDestroy); - LIB_FUNCTION("6ULAa0fq4jA", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockInit); - LIB_FUNCTION("Ox9i0c7L5w0", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockRdlock); - LIB_FUNCTION("iPtZRWICjrM", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockTimedrdlock); - LIB_FUNCTION("adh--6nIqTk", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockTimedwrlock); - LIB_FUNCTION("XD3mDeybCnk", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockTryrdlock); - LIB_FUNCTION("bIHoZCTomsI", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockTrywrlock); - LIB_FUNCTION("+L98PIbGttk", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockUnlock); - LIB_FUNCTION("mqdNorrB+gI", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockWrlock); + + // Orbis + LIB_FUNCTION("i2ifZ3fS2fo", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlockattr_destroy)); + LIB_FUNCTION("LcOZBHGqbFk", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlockattr_getpshared)); + LIB_FUNCTION("yOfGg-I1ZII", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlockattr_init)); + LIB_FUNCTION("-ZvQH18j10c", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlockattr_setpshared)); + LIB_FUNCTION("BB+kb08Tl9A", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlock_destroy)); + LIB_FUNCTION("6ULAa0fq4jA", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlock_init)); + LIB_FUNCTION("Ox9i0c7L5w0", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlock_rdlock)); + LIB_FUNCTION("iPtZRWICjrM", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlock_timedrdlock)); + LIB_FUNCTION("adh--6nIqTk", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlock_timedwrlock)); + LIB_FUNCTION("XD3mDeybCnk", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlock_tryrdlock)); + LIB_FUNCTION("bIHoZCTomsI", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlock_trywrlock)); + LIB_FUNCTION("+L98PIbGttk", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlock_unlock)); + LIB_FUNCTION("mqdNorrB+gI", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlock_wrlock)); } + } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/semaphore.cpp b/src/core/libraries/kernel/threads/semaphore.cpp index 59099c1b8..39c1a0233 100644 --- a/src/core/libraries/kernel/threads/semaphore.cpp +++ b/src/core/libraries/kernel/threads/semaphore.cpp @@ -4,23 +4,35 @@ #include #include #include -#include +#include + +#include "core/libraries/kernel/sync/semaphore.h" -#include "common/assert.h" #include "common/logging/log.h" -#include "core/libraries/error_codes.h" +#include "core/libraries/kernel/kernel.h" +#include "core/libraries/kernel/orbis_error.h" +#include "core/libraries/kernel/posix_error.h" +#include "core/libraries/kernel/threads/pthread.h" +#include "core/libraries/kernel/time.h" #include "core/libraries/libs.h" namespace Libraries::Kernel { -class Semaphore { +constexpr int ORBIS_KERNEL_SEM_VALUE_MAX = 0x7FFFFFFF; + +struct PthreadSem { + explicit PthreadSem(s32 value_) : semaphore{value_}, value{value_} {} + + CountingSemaphore semaphore; + std::atomic value; +}; + +class OrbisSem { public: - Semaphore(s32 init_count, s32 max_count, std::string_view name, bool is_fifo) + OrbisSem(s32 init_count, s32 max_count, std::string_view name, bool is_fifo) : name{name}, token_count{init_count}, max_count{max_count}, init_count{init_count}, is_fifo{is_fifo} {} - ~Semaphore() { - ASSERT(wait_list.empty()); - } + ~OrbisSem() = default; int Wait(bool can_block, s32 need_count, u32* timeout) { std::unique_lock lk{mutex}; @@ -33,7 +45,7 @@ public: } if (timeout && *timeout == 0) { - return SCE_KERNEL_ERROR_ETIMEDOUT; + return ORBIS_KERNEL_ERROR_ETIMEDOUT; } // Create waiting thread object and add it into the list of waiters. @@ -42,7 +54,7 @@ public: // Perform the wait. const s32 result = waiter.Wait(lk, timeout); - if (result == SCE_KERNEL_ERROR_ETIMEDOUT) { + if (result == ORBIS_KERNEL_ERROR_ETIMEDOUT) { wait_list.erase(it); } return result; @@ -64,7 +76,8 @@ public: } it = wait_list.erase(it); token_count -= waiter->need_count; - waiter->cv.notify_one(); + waiter->was_signaled = true; + waiter->sem.release(); } return true; @@ -77,63 +90,76 @@ public: } for (auto* waiter : wait_list) { waiter->was_cancled = true; - waiter->cv.notify_one(); + waiter->sem.release(); } wait_list.clear(); token_count = set_count < 0 ? init_count : set_count; return ORBIS_OK; } + void Delete() { + std::scoped_lock lk{mutex}; + for (auto* waiter : wait_list) { + waiter->was_deleted = true; + waiter->sem.release(); + } + wait_list.clear(); + } + public: struct WaitingThread { - std::condition_variable cv; + BinarySemaphore sem; u32 priority; s32 need_count; + std::string thr_name; + bool was_signaled{}; bool was_deleted{}; bool was_cancled{}; - explicit WaitingThread(s32 need_count, bool is_fifo) : need_count{need_count} { - if (is_fifo) { - return; - } + explicit WaitingThread(s32 need_count, bool is_fifo) + : sem{0}, priority{0}, need_count{need_count} { // Retrieve calling thread priority for sorting into waiting threads list. - s32 policy; - sched_param param; - pthread_getschedparam(pthread_self(), &policy, ¶m); - priority = param.sched_priority; + if (!is_fifo) { + priority = g_curthread->attr.prio; + } + + thr_name = g_curthread->name; } int GetResult(bool timed_out) { if (timed_out) { - return SCE_KERNEL_ERROR_ETIMEDOUT; + return ORBIS_KERNEL_ERROR_ETIMEDOUT; } if (was_deleted) { - return SCE_KERNEL_ERROR_EACCES; + return ORBIS_KERNEL_ERROR_EACCES; } if (was_cancled) { - return SCE_KERNEL_ERROR_ECANCELED; + return ORBIS_KERNEL_ERROR_ECANCELED; } - return SCE_OK; + return ORBIS_OK; } int Wait(std::unique_lock& lk, u32* timeout) { + lk.unlock(); if (!timeout) { // Wait indefinitely until we are woken up. - cv.wait(lk); + sem.acquire(); + lk.lock(); return GetResult(false); } // Wait until timeout runs out, recording how much remaining time there was. const auto start = std::chrono::high_resolution_clock::now(); - const auto status = cv.wait_for(lk, std::chrono::microseconds(*timeout)); + sem.try_acquire_for(std::chrono::microseconds(*timeout)); const auto end = std::chrono::high_resolution_clock::now(); const auto time = std::chrono::duration_cast(end - start).count(); - if (status == std::cv_status::timeout) { - *timeout = 0; - } else { + lk.lock(); + if (was_signaled) { *timeout -= time; + } else { + *timeout = 0; } - return GetResult(status == std::cv_status::timeout); + return GetResult(!was_signaled); } }; @@ -150,8 +176,7 @@ public: while (it != wait_list.end() && (*it)->priority > waiter->priority) { ++it; } - wait_list.insert(it, waiter); - return it; + return wait_list.insert(it, waiter); } WaitList wait_list; @@ -163,7 +188,7 @@ public: bool is_fifo; }; -using OrbisKernelSema = Semaphore*; +using OrbisKernelSema = OrbisSem*; s32 PS4_SYSV_ABI sceKernelCreateSema(OrbisKernelSema* sem, const char* pName, u32 attr, s32 initCount, s32 maxCount, const void* pOptParam) { @@ -171,7 +196,7 @@ s32 PS4_SYSV_ABI sceKernelCreateSema(OrbisKernelSema* sem, const char* pName, u3 LOG_ERROR(Lib_Kernel, "Semaphore creation parameters are invalid!"); return ORBIS_KERNEL_ERROR_EINVAL; } - *sem = new Semaphore(initCount, maxCount, pName, attr == 1); + *sem = new OrbisSem(initCount, maxCount, pName, attr == 1); return ORBIS_OK; } @@ -208,19 +233,191 @@ int PS4_SYSV_ABI sceKernelCancelSema(OrbisKernelSema sem, s32 setCount, s32* pNu int PS4_SYSV_ABI sceKernelDeleteSema(OrbisKernelSema sem) { if (!sem) { - return SCE_KERNEL_ERROR_ESRCH; + return ORBIS_KERNEL_ERROR_ESRCH; } + sem->Delete(); delete sem; return ORBIS_OK; } -void SemaphoreSymbolsRegister(Core::Loader::SymbolsResolver* sym) { +int PS4_SYSV_ABI posix_sem_init(PthreadSem** sem, int pshared, u32 value) { + if (value > ORBIS_KERNEL_SEM_VALUE_MAX) { + *__Error() = POSIX_EINVAL; + return -1; + } + if (sem != nullptr) { + *sem = new PthreadSem(value); + } + return 0; +} + +int PS4_SYSV_ABI posix_sem_destroy(PthreadSem** sem) { + if (sem == nullptr || *sem == nullptr) { + *__Error() = POSIX_EINVAL; + return -1; + } + delete *sem; + *sem = nullptr; + return 0; +} + +int PS4_SYSV_ABI posix_sem_wait(PthreadSem** sem) { + if (sem == nullptr || *sem == nullptr) { + *__Error() = POSIX_EINVAL; + return -1; + } + (*sem)->semaphore.acquire(); + --(*sem)->value; + return 0; +} + +int PS4_SYSV_ABI posix_sem_trywait(PthreadSem** sem) { + if (sem == nullptr || *sem == nullptr) { + *__Error() = POSIX_EINVAL; + return -1; + } + if (!(*sem)->semaphore.try_acquire()) { + *__Error() = POSIX_EAGAIN; + return -1; + } + --(*sem)->value; + return 0; +} + +int PS4_SYSV_ABI posix_sem_timedwait(PthreadSem** sem, const OrbisKernelTimespec* t) { + if (sem == nullptr || *sem == nullptr) { + *__Error() = POSIX_EINVAL; + return -1; + } + if (!(*sem)->semaphore.try_acquire_until(t->TimePoint())) { + *__Error() = POSIX_ETIMEDOUT; + return -1; + } + --(*sem)->value; + return 0; +} + +int PS4_SYSV_ABI posix_sem_post(PthreadSem** sem) { + if (sem == nullptr || *sem == nullptr) { + *__Error() = POSIX_EINVAL; + return -1; + } + if ((*sem)->value == ORBIS_KERNEL_SEM_VALUE_MAX) { + *__Error() = POSIX_EOVERFLOW; + return -1; + } + ++(*sem)->value; + (*sem)->semaphore.release(); + return 0; +} + +int PS4_SYSV_ABI posix_sem_getvalue(PthreadSem** sem, int* sval) { + if (sem == nullptr || *sem == nullptr) { + *__Error() = POSIX_EINVAL; + return -1; + } + if (sval) { + *sval = (*sem)->value; + } + return 0; +} + +s32 PS4_SYSV_ABI scePthreadSemInit(PthreadSem** sem, int flag, u32 value, const char* name) { + if (flag != 0) { + return ORBIS_KERNEL_ERROR_EINVAL; + } + + s32 ret = posix_sem_init(sem, 0, value); + if (ret != 0) { + return ErrnoToSceKernelError(*__Error()); + } + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI scePthreadSemDestroy(PthreadSem** sem) { + s32 ret = posix_sem_destroy(sem); + if (ret != 0) { + return ErrnoToSceKernelError(*__Error()); + } + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI scePthreadSemWait(PthreadSem** sem) { + s32 ret = posix_sem_wait(sem); + if (ret != 0) { + return ErrnoToSceKernelError(*__Error()); + } + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI scePthreadSemTrywait(PthreadSem** sem) { + s32 ret = posix_sem_trywait(sem); + if (ret != 0) { + return ErrnoToSceKernelError(*__Error()); + } + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI scePthreadSemTimedwait(PthreadSem** sem, u32 usec) { + OrbisKernelTimespec time{}; + time.tv_sec = usec / 1000000; + time.tv_nsec = (usec % 1000000) * 1000; + + s32 ret = posix_sem_timedwait(sem, &time); + if (ret != 0) { + return ErrnoToSceKernelError(*__Error()); + } + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI scePthreadSemPost(PthreadSem** sem) { + s32 ret = posix_sem_post(sem); + if (ret != 0) { + return ErrnoToSceKernelError(*__Error()); + } + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI scePthreadSemGetvalue(PthreadSem** sem, int* sval) { + s32 ret = posix_sem_getvalue(sem, sval); + if (ret != 0) { + return ErrnoToSceKernelError(*__Error()); + } + + return ORBIS_OK; +} + +void RegisterSemaphore(Core::Loader::SymbolsResolver* sym) { + // Orbis LIB_FUNCTION("188x57JYp0g", "libkernel", 1, "libkernel", 1, 1, sceKernelCreateSema); LIB_FUNCTION("Zxa0VhQVTsk", "libkernel", 1, "libkernel", 1, 1, sceKernelWaitSema); LIB_FUNCTION("4czppHBiriw", "libkernel", 1, "libkernel", 1, 1, sceKernelSignalSema); LIB_FUNCTION("12wOHk8ywb0", "libkernel", 1, "libkernel", 1, 1, sceKernelPollSema); LIB_FUNCTION("4DM06U2BNEY", "libkernel", 1, "libkernel", 1, 1, sceKernelCancelSema); LIB_FUNCTION("R1Jvn8bSCW8", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteSema); + + // Posix + LIB_FUNCTION("pDuPEf3m4fI", "libScePosix", 1, "libkernel", 1, 1, posix_sem_init); + LIB_FUNCTION("cDW233RAwWo", "libScePosix", 1, "libkernel", 1, 1, posix_sem_destroy); + LIB_FUNCTION("YCV5dGGBcCo", "libScePosix", 1, "libkernel", 1, 1, posix_sem_wait); + LIB_FUNCTION("WBWzsRifCEA", "libScePosix", 1, "libkernel", 1, 1, posix_sem_trywait); + LIB_FUNCTION("w5IHyvahg-o", "libScePosix", 1, "libkernel", 1, 1, posix_sem_timedwait); + LIB_FUNCTION("IKP8typ0QUk", "libScePosix", 1, "libkernel", 1, 1, posix_sem_post); + LIB_FUNCTION("Bq+LRV-N6Hk", "libScePosix", 1, "libkernel", 1, 1, posix_sem_getvalue); + + LIB_FUNCTION("GEnUkDZoUwY", "libkernel", 1, "libkernel", 1, 1, scePthreadSemInit); + LIB_FUNCTION("Vwc+L05e6oE", "libkernel", 1, "libkernel", 1, 1, scePthreadSemDestroy); + LIB_FUNCTION("C36iRE0F5sE", "libkernel", 1, "libkernel", 1, 1, scePthreadSemWait); + LIB_FUNCTION("H2a+IN9TP0E", "libkernel", 1, "libkernel", 1, 1, scePthreadSemTrywait); + LIB_FUNCTION("fjN6NQHhK8k", "libkernel", 1, "libkernel", 1, 1, scePthreadSemTimedwait); + LIB_FUNCTION("aishVAiFaYM", "libkernel", 1, "libkernel", 1, 1, scePthreadSemPost); + LIB_FUNCTION("DjpBvGlaWbQ", "libkernel", 1, "libkernel", 1, 1, scePthreadSemGetvalue); } } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/sleepq.cpp b/src/core/libraries/kernel/threads/sleepq.cpp new file mode 100644 index 000000000..d998141d0 --- /dev/null +++ b/src/core/libraries/kernel/threads/sleepq.cpp @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include "common/spin_lock.h" +#include "core/libraries/kernel/threads/pthread.h" +#include "core/libraries/kernel/threads/sleepq.h" + +namespace Libraries::Kernel { + +static constexpr int HASHSHIFT = 9; +static constexpr int HASHSIZE = (1 << HASHSHIFT); +#define SC_HASH(wchan) \ + ((u32)((((uintptr_t)(wchan) >> 3) ^ ((uintptr_t)(wchan) >> (HASHSHIFT + 3))) & (HASHSIZE - 1))) +#define SC_LOOKUP(wc) &sc_table[SC_HASH(wc)] + +struct SleepQueueChain { + Common::SpinLock sc_lock; + SleepqList sc_queues; + int sc_type; +}; + +static std::array sc_table{}; + +void SleepqLock(void* wchan) { + SleepQueueChain* sc = SC_LOOKUP(wchan); + sc->sc_lock.lock(); +} + +void SleepqUnlock(void* wchan) { + SleepQueueChain* sc = SC_LOOKUP(wchan); + sc->sc_lock.unlock(); +} + +SleepQueue* SleepqLookup(void* wchan) { + SleepQueueChain* sc = SC_LOOKUP(wchan); + for (auto& sq : sc->sc_queues) { + if (sq.sq_wchan == wchan) { + return std::addressof(sq); + } + } + return nullptr; +} + +void SleepqAdd(void* wchan, Pthread* td) { + SleepQueue* sq = SleepqLookup(wchan); + if (sq != nullptr) { + sq->sq_freeq.push_front(*td->sleepqueue); + } else { + SleepQueueChain* sc = SC_LOOKUP(wchan); + sq = td->sleepqueue; + sc->sc_queues.push_front(*sq); + sq->sq_wchan = wchan; + /* sq->sq_type = type; */ + } + td->sleepqueue = NULL; + td->wchan = wchan; + sq->sq_blocked.push_front(td); +} + +int SleepqRemove(SleepQueue* sq, Pthread* td) { + std::erase(sq->sq_blocked, td); + if (sq->sq_blocked.empty()) { + td->sleepqueue = sq; + sq->unlink(); + td->wchan = nullptr; + return 0; + } else { + td->sleepqueue = std::addressof(sq->sq_freeq.front()); + sq->sq_freeq.pop_front(); + td->wchan = nullptr; + return 1; + } +} + +void SleepqDrop(SleepQueue* sq, void (*callback)(Pthread*, void*), void* arg) { + if (sq->sq_blocked.empty()) { + return; + } + + sq->unlink(); + Pthread* td = sq->sq_blocked.front(); + sq->sq_blocked.pop_front(); + + callback(td, arg); + + td->sleepqueue = sq; + td->wchan = nullptr; + + auto sq2 = sq->sq_freeq.begin(); + for (Pthread* td : sq->sq_blocked) { + callback(td, arg); + td->sleepqueue = std::addressof(*sq2); + td->wchan = nullptr; + ++sq2; + } + sq->sq_blocked.clear(); + sq->sq_freeq.clear(); +} + +} // namespace Libraries::Kernel \ No newline at end of file diff --git a/src/core/libraries/kernel/threads/sleepq.h b/src/core/libraries/kernel/threads/sleepq.h new file mode 100644 index 000000000..5dc37645d --- /dev/null +++ b/src/core/libraries/kernel/threads/sleepq.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include + +namespace Libraries::Kernel { + +struct Pthread; +struct SleepQueue; + +using ListBaseHook = + boost::intrusive::list_base_hook>; + +using SleepqList = boost::intrusive::list>; + +struct SleepQueue : public ListBaseHook { + std::forward_list sq_blocked; + SleepqList sq_freeq; + void* sq_wchan; + int sq_type; +}; + +void SleepqLock(void* wchan); + +void SleepqUnlock(void* wchan); + +SleepQueue* SleepqLookup(void* wchan); + +void SleepqAdd(void* wchan, Pthread* td); + +int SleepqRemove(SleepQueue* sq, Pthread* td); + +void SleepqDrop(SleepQueue* sq, void (*callback)(Pthread*, void*), void* arg); + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/stack.cpp b/src/core/libraries/kernel/threads/stack.cpp new file mode 100644 index 000000000..45715482a --- /dev/null +++ b/src/core/libraries/kernel/threads/stack.cpp @@ -0,0 +1,141 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "core/libraries/kernel/threads/pthread.h" +#include "core/libraries/kernel/threads/thread_state.h" +#include "core/memory.h" + +namespace Libraries::Kernel { + +static constexpr size_t RoundUp(size_t size) { + if (size % ThrPageSize != 0) { + size = ((size / ThrPageSize) + 1) * ThrPageSize; + } + return size; +} + +int ThreadState::CreateStack(PthreadAttr* attr) { + if ((attr->stackaddr_attr) != NULL) { + attr->guardsize_attr = 0; + attr->flags |= PthreadAttrFlags::StackUser; + return 0; + } + + /* + * Round up stack size to nearest multiple of _thr_page_size so + * that mmap() * will work. If the stack size is not an even + * multiple, we end up initializing things such that there is + * unused space above the beginning of the stack, so the stack + * sits snugly against its guard. + */ + size_t stacksize = RoundUp(attr->stacksize_attr); + size_t guardsize = RoundUp(attr->guardsize_attr); + + attr->stackaddr_attr = NULL; + attr->flags &= ~PthreadAttrFlags::StackUser; + + /* + * Use the garbage collector lock for synchronization of the + * spare stack lists and allocations from usrstack. + */ + thread_list_lock.lock(); + + /* + * If the stack and guard sizes are default, try to allocate a stack + * from the default-size stack cache: + */ + if (stacksize == ThrStackDefault && guardsize == ThrGuardDefault) { + if (!dstackq.empty()) { + /* Use the spare stack. */ + Stack* spare_stack = dstackq.top(); + dstackq.pop(); + attr->stackaddr_attr = spare_stack->stackaddr; + } + } + /* + * The user specified a non-default stack and/or guard size, so try to + * allocate a stack from the non-default size stack cache, using the + * rounded up stack size (stack_size) in the search: + */ + else { + const auto it = std::ranges::find_if(mstackq, [&](Stack* stack) { + return stack->stacksize == stacksize && stack->guardsize == guardsize; + }); + if (it != mstackq.end()) { + attr->stackaddr_attr = (*it)->stackaddr; + mstackq.erase(it); + } + } + + /* A cached stack was found. Release the lock. */ + if (attr->stackaddr_attr != NULL) { + thread_list_lock.unlock(); + return 0; + } + + /* Allocate a stack from usrstack. */ + if (last_stack == 0) { + static constexpr VAddr UsrStack = 0x7EFFF8000ULL; + last_stack = UsrStack - ThrStackInitial - ThrGuardDefault; + } + + /* Allocate a new stack. */ + VAddr stackaddr = last_stack - stacksize - guardsize; + + /* + * Even if stack allocation fails, we don't want to try to + * use this location again, so unconditionally decrement + * last_stack. Under normal operating conditions, the most + * likely reason for an mmap() error is a stack overflow of + * the adjacent thread stack. + */ + last_stack -= (stacksize + guardsize); + + /* Release the lock before mmap'ing it. */ + thread_list_lock.unlock(); + + /* Map the stack and guard page together, and split guard + page from allocated space: */ + auto* memory = Core::Memory::Instance(); + int ret = memory->MapMemory(reinterpret_cast(&stackaddr), stackaddr, + stacksize + guardsize, Core::MemoryProt::CpuReadWrite, + Core::MemoryMapFlags::NoFlags, Core::VMAType::Stack); + ASSERT_MSG(ret == 0, "Unable to map stack memory"); + + if (guardsize != 0) { + ret = memory->Protect(stackaddr, guardsize, Core::MemoryProt::NoAccess); + ASSERT_MSG(ret == 0, "Unable to protect guard page"); + } + + stackaddr += guardsize; + attr->stackaddr_attr = (void*)stackaddr; + + if (attr->stackaddr_attr != nullptr) { + return 0; + } + return -1; +} + +void ThreadState::FreeStack(PthreadAttr* attr) { + if (!attr || True(attr->flags & PthreadAttrFlags::StackUser) || !attr->stackaddr_attr) { + return; + } + + char* stack_base = (char*)attr->stackaddr_attr; + Stack* spare_stack = (Stack*)(stack_base + attr->stacksize_attr - sizeof(Stack)); + spare_stack->stacksize = RoundUp(attr->stacksize_attr); + spare_stack->guardsize = RoundUp(attr->guardsize_attr); + spare_stack->stackaddr = attr->stackaddr_attr; + + if (spare_stack->stacksize == ThrStackDefault && spare_stack->guardsize == ThrGuardDefault) { + /* Default stack/guard size. */ + dstackq.push(spare_stack); + } else { + /* Non-default stack/guard size. */ + mstackq.push_back(spare_stack); + } + attr->stackaddr_attr = nullptr; +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/tcb.cpp b/src/core/libraries/kernel/threads/tcb.cpp new file mode 100644 index 000000000..e5a158216 --- /dev/null +++ b/src/core/libraries/kernel/threads/tcb.cpp @@ -0,0 +1,96 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "common/singleton.h" +#include "core/libraries/kernel/threads/pthread.h" +#include "core/libraries/libs.h" +#include "core/linker.h" +#include "core/tls.h" + +namespace Libraries::Kernel { + +static constexpr size_t TlsTcbSize = 0x40; +static constexpr size_t TlsTcbAlign = 0x20; + +static std::shared_mutex RtldLock; + +Core::Tcb* TcbCtor(Pthread* thread, int initial) { + std::scoped_lock lk{RtldLock}; + + auto* linker = Common::Singleton::Instance(); + auto* addr_out = linker->AllocateTlsForThread(initial); + ASSERT_MSG(addr_out, "Unable to allocate guest TCB"); + + // Initialize allocated memory and allocate DTV table. + const u32 num_dtvs = linker->MaxTlsIndex(); + const auto static_tls_size = linker->StaticTlsSize(); + auto* dtv_table = new Core::DtvEntry[num_dtvs + 2]{}; + + // Initialize thread control block + u8* addr = reinterpret_cast(addr_out); + auto* tcb = reinterpret_cast(addr + static_tls_size); + memset(addr_out, 0, static_tls_size); + tcb->tcb_self = tcb; + tcb->tcb_dtv = dtv_table; + + // Dtv[0] is the generation counter. libkernel puts their number into dtv[1] + dtv_table[0].counter = linker->GenerationCounter(); + dtv_table[1].counter = num_dtvs; + + // Copy init image of main module. + auto* module = linker->GetModule(0); + u8* dest = reinterpret_cast(addr + static_tls_size - module->tls.offset); + + if (module->tls.image_size != 0) { + if (module->tls.image_virtual_addr != 0) { + const u8* src = reinterpret_cast(module->tls.image_virtual_addr); + memcpy(dest, src, module->tls.init_image_size); + } + ASSERT_MSG(module->tls.modid > 0 && module->tls.modid <= num_dtvs); + tcb->tcb_dtv[module->tls.modid + 1].pointer = dest; + } + + if (tcb) { + tcb->tcb_thread = thread; + } + return tcb; +} + +void TcbDtor(Core::Tcb* oldtls) { + std::scoped_lock lk{RtldLock}; + auto* dtv_table = oldtls->tcb_dtv; + + auto* linker = Common::Singleton::Instance(); + const u32 max_tls_index = linker->MaxTlsIndex(); + const u32 num_dtvs = dtv_table[1].counter; + ASSERT_MSG(num_dtvs <= max_tls_index, "Out of bounds DTV access"); + + const u32 static_tls_size = linker->StaticTlsSize(); + const u8* tls_base = (const u8*)oldtls - static_tls_size; + + for (int i = 1; i < num_dtvs; i++) { + u8* dtv_ptr = dtv_table[i + 1].pointer; + if (dtv_ptr && (dtv_ptr < tls_base || (const u8*)oldtls < dtv_ptr)) { + linker->FreeTlsForNonPrimaryThread(dtv_ptr); + } + } + + delete[] dtv_table; +} + +struct TlsIndex { + u64 ti_module; + u64 ti_offset; +}; + +void* PS4_SYSV_ABI __tls_get_addr(TlsIndex* index) { + auto* linker = Common::Singleton::Instance(); + return linker->TlsGetAddr(index->ti_module, index->ti_offset); +} + +void RegisterRtld(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("vNe1w4diLCs", "libkernel", 1, "libkernel", 1, 1, __tls_get_addr); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/thread_state.cpp b/src/core/libraries/kernel/threads/thread_state.cpp new file mode 100644 index 000000000..6c2d4d7ef --- /dev/null +++ b/src/core/libraries/kernel/threads/thread_state.cpp @@ -0,0 +1,172 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include "common/alignment.h" +#include "common/scope_exit.h" +#include "core/libraries/kernel/posix_error.h" +#include "core/libraries/kernel/threads/pthread.h" +#include "core/libraries/kernel/threads/sleepq.h" +#include "core/libraries/kernel/threads/thread_state.h" +#include "core/memory.h" +#include "core/tls.h" + +namespace Libraries::Kernel { + +thread_local Pthread* g_curthread{}; + +Core::Tcb* TcbCtor(Pthread* thread, int initial); +void TcbDtor(Core::Tcb* oldtls); + +ThreadState::ThreadState() { + // Reserve memory for maximum amount of threads allowed. + auto* memory = Core::Memory::Instance(); + static constexpr u32 ThrHeapSize = Common::AlignUp(sizeof(Pthread) * MaxThreads, 16_KB); + void* heap_addr{}; + const int ret = memory->MapMemory(&heap_addr, Core::SYSTEM_RESERVED_MIN, ThrHeapSize, + Core::MemoryProt::CpuReadWrite, Core::MemoryMapFlags::NoFlags, + Core::VMAType::File, "ThrHeap"); + ASSERT_MSG(ret == 0, "Unable to allocate thread heap memory {}", ret); + thread_heap.Initialize(heap_addr, ThrHeapSize); +} + +void ThreadState::Collect(Pthread* curthread) { + boost::container::small_vector work_list; + { + std::scoped_lock lk{thread_list_lock}; + for (auto it = gc_list.begin(); it != gc_list.end();) { + Pthread* td = *it; + if (td->tid != TidTerminated) { + it++; + continue; + } + FreeStack(&td->attr); + work_list.push_back(td); + it = gc_list.erase(it); + } + } + for (Pthread* td : work_list) { + Free(curthread, td); + } +} + +void ThreadState::TryCollect(Pthread* thread) { + SCOPE_EXIT { + thread->lock.unlock(); + }; + if (!thread->ShouldCollect()) { + return; + } + + thread->refcount++; + thread->lock.unlock(); + std::scoped_lock lk{thread_list_lock}; + thread->lock.lock(); + thread->refcount--; + if (thread->ShouldCollect()) { + threads.erase(thread); + gc_list.push_back(thread); + } +} + +Pthread* ThreadState::Alloc(Pthread* curthread) { + Pthread* thread = nullptr; + if (curthread != nullptr) { + if (GcNeeded()) { + Collect(curthread); + } + if (!free_threads.empty()) { + std::scoped_lock lk{free_thread_lock}; + thread = free_threads.back(); + free_threads.pop_back(); + } + } + if (thread == nullptr) { + if (total_threads > MaxThreads) { + return nullptr; + } + total_threads.fetch_add(1); + thread = thread_heap.Allocate(); + if (thread == nullptr) { + total_threads.fetch_sub(1); + return nullptr; + } + } + Core::Tcb* tcb = nullptr; + if (curthread != nullptr) { + std::scoped_lock lk{tcb_lock}; + tcb = TcbCtor(thread, 0 /* not initial tls */); + } else { + tcb = TcbCtor(thread, 1 /* initial tls */); + } + if (tcb != nullptr) { + memset(thread, 0, sizeof(Pthread)); + std::construct_at(thread); + thread->tcb = tcb; + thread->sleepqueue = new SleepQueue{}; + } else { + thread_heap.Free(thread); + total_threads.fetch_sub(1); + thread = nullptr; + } + return thread; +} + +void ThreadState::Free(Pthread* curthread, Pthread* thread) { + if (curthread != nullptr) { + std::scoped_lock lk{tcb_lock}; + TcbDtor(thread->tcb); + } else { + TcbDtor(thread->tcb); + } + thread->tcb = nullptr; + std::destroy_at(thread); + if (free_threads.size() >= MaxCachedThreads) { + delete thread->sleepqueue; + thread_heap.Free(thread); + total_threads.fetch_sub(1); + } else { + std::scoped_lock lk{free_thread_lock}; + free_threads.push_back(thread); + } +} + +int ThreadState::FindThread(Pthread* thread, bool include_dead) { + if (thread == nullptr) { + return POSIX_EINVAL; + } + std::scoped_lock lk{thread_list_lock}; + const auto it = threads.find(thread); + if (it == threads.end()) { + return POSIX_ESRCH; + } + thread->lock.lock(); + if (!include_dead && thread->state == PthreadState::Dead) { + thread->lock.unlock(); + return POSIX_ESRCH; + } + return 0; +} + +int ThreadState::RefAdd(Pthread* thread, bool include_dead) { + if (thread == nullptr) { + /* Invalid thread: */ + return POSIX_EINVAL; + } + + if (int ret = FindThread(thread, include_dead); ret != 0) { + return ret; + } + + thread->refcount++; + thread->lock.unlock(); + return 0; +} + +void ThreadState::RefDelete(Pthread* thread) { + thread->lock.lock(); + thread->refcount--; + TryCollect(thread); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/thread_state.h b/src/core/libraries/kernel/threads/thread_state.h new file mode 100644 index 000000000..c98f2083e --- /dev/null +++ b/src/core/libraries/kernel/threads/thread_state.h @@ -0,0 +1,87 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include "common/singleton.h" +#include "common/slab_heap.h" +#include "common/types.h" + +namespace Libraries::Kernel { + +struct Pthread; +struct PthreadAttr; + +struct Stack { + size_t stacksize; /* Stack size (rounded up). */ + size_t guardsize; /* Guard size. */ + void* stackaddr; /* Stack address. */ +}; + +struct ThreadState { + static constexpr size_t GcThreshold = 5; + static constexpr size_t MaxThreads = 100000; + static constexpr size_t MaxCachedThreads = 100; + + explicit ThreadState(); + + bool GcNeeded() const noexcept { + return gc_list.size() >= GcThreshold; + } + + void Collect(Pthread* curthread); + + void TryCollect(Pthread* thread); + + Pthread* Alloc(Pthread* curthread); + + void Free(Pthread* curthread, Pthread* thread); + + int FindThread(Pthread* thread, bool include_dead); + + int RefAdd(Pthread* thread, bool include_dead); + + void RefDelete(Pthread* thread); + + int CreateStack(PthreadAttr* attr); + + void FreeStack(PthreadAttr* attr); + + void Link(Pthread* curthread, Pthread* thread) { + { + std::scoped_lock lk{thread_list_lock}; + threads.insert(thread); + } + active_threads.fetch_add(1); + } + + void Unlink(Pthread* curthread, Pthread* thread) { + { + std::scoped_lock lk{thread_list_lock}; + threads.erase(thread); + } + active_threads.fetch_sub(1); + } + + Common::SlabHeap thread_heap; + std::set threads; + std::list free_threads; + std::list gc_list; + std::mutex free_thread_lock; + std::mutex tcb_lock; + std::mutex thread_list_lock; + std::atomic total_threads{}; + std::atomic active_threads{}; + std::stack dstackq; + std::list mstackq; + VAddr last_stack = 0; +}; + +using ThrState = Common::Singleton; + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/threads.h b/src/core/libraries/kernel/threads/threads.h deleted file mode 100644 index a3fd354b0..000000000 --- a/src/core/libraries/kernel/threads/threads.h +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/libraries/kernel/thread_management.h" - -namespace Core::Loader { -class SymbolsResolver; -} - -namespace Libraries::Kernel { - -int PS4_SYSV_ABI scePthreadRwlockattrInit(OrbisPthreadRwlockattr* attr); - -void SemaphoreSymbolsRegister(Core::Loader::SymbolsResolver* sym); -void RwlockSymbolsRegister(Core::Loader::SymbolsResolver* sym); -void KeySymbolsRegister(Core::Loader::SymbolsResolver* sym); - -} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/time_management.cpp b/src/core/libraries/kernel/time.cpp similarity index 79% rename from src/core/libraries/kernel/time_management.cpp rename to src/core/libraries/kernel/time.cpp index 853f8d54c..2565b8078 100644 --- a/src/core/libraries/kernel/time_management.cpp +++ b/src/core/libraries/kernel/time.cpp @@ -4,10 +4,9 @@ #include #include "common/assert.h" -#include "common/debug.h" #include "common/native_clock.h" -#include "core/libraries/error_codes.h" -#include "core/libraries/kernel/time_management.h" +#include "core/libraries/kernel/orbis_error.h" +#include "core/libraries/kernel/time.h" #include "core/libraries/libs.h" #ifdef _WIN64 @@ -17,6 +16,9 @@ #include "common/ntapi.h" #else +#if __APPLE__ +#include +#endif #include #include #include @@ -50,14 +52,22 @@ u64 PS4_SYSV_ABI sceKernelReadTsc() { int PS4_SYSV_ABI sceKernelUsleep(u32 microseconds) { #ifdef _WIN64 - if (microseconds < 1000u) { - LARGE_INTEGER interval{ - .QuadPart = -1 * (microseconds * 10u), - }; - NtDelayExecution(FALSE, &interval); - } else { - std::this_thread::sleep_for(std::chrono::microseconds(microseconds)); + const auto start_time = std::chrono::high_resolution_clock::now(); + auto total_wait_time = std::chrono::microseconds(microseconds); + + while (total_wait_time.count() > 0) { + auto wait_time = std::chrono::ceil(total_wait_time).count(); + u64 res = SleepEx(static_cast(wait_time), true); + if (res == WAIT_IO_COMPLETION) { + auto elapsedTime = std::chrono::high_resolution_clock::now() - start_time; + auto elapsedMicroseconds = + std::chrono::duration_cast(elapsedTime).count(); + total_wait_time = std::chrono::microseconds(microseconds - elapsedMicroseconds); + } else { + break; + } } + return 0; #else timespec start; @@ -85,7 +95,7 @@ u32 PS4_SYSV_ABI sceKernelSleep(u32 seconds) { int PS4_SYSV_ABI sceKernelClockGettime(s32 clock_id, OrbisKernelTimespec* tp) { if (tp == nullptr) { - return SCE_KERNEL_ERROR_EFAULT; + return ORBIS_KERNEL_ERROR_EFAULT; } clockid_t pclock_id = CLOCK_REALTIME; switch (clock_id) { @@ -110,9 +120,9 @@ int PS4_SYSV_ABI sceKernelClockGettime(s32 clock_id, OrbisKernelTimespec* tp) { tp->tv_sec = t.tv_sec; tp->tv_nsec = t.tv_nsec; if (result == 0) { - return SCE_OK; + return ORBIS_OK; } - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } int PS4_SYSV_ABI posix_clock_gettime(s32 clock_id, OrbisKernelTimespec* time) { @@ -131,11 +141,11 @@ int PS4_SYSV_ABI posix_nanosleep(const OrbisKernelTimespec* rqtp, OrbisKernelTim int PS4_SYSV_ABI sceKernelNanosleep(const OrbisKernelTimespec* rqtp, OrbisKernelTimespec* rmtp) { if (!rqtp || !rmtp) { - return SCE_KERNEL_ERROR_EFAULT; + return ORBIS_KERNEL_ERROR_EFAULT; } if (rqtp->tv_sec < 0 || rqtp->tv_nsec < 0) { - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } return posix_nanosleep(rqtp, rmtp); @@ -202,7 +212,7 @@ s32 PS4_SYSV_ABI sceKernelGettimezone(OrbisKernelTimezone* tz) { int PS4_SYSV_ABI posix_clock_getres(u32 clock_id, OrbisKernelTimespec* res) { if (res == nullptr) { - return SCE_KERNEL_ERROR_EFAULT; + return ORBIS_KERNEL_ERROR_EFAULT; } clockid_t pclock_id = CLOCK_REALTIME; switch (clock_id) { @@ -226,9 +236,9 @@ int PS4_SYSV_ABI posix_clock_getres(u32 clock_id, OrbisKernelTimespec* res) { res->tv_sec = t.tv_sec; res->tv_nsec = t.tv_nsec; if (result == 0) { - return SCE_OK; + return ORBIS_OK; } - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } int PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, time_t* seconds, @@ -242,9 +252,9 @@ int PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, if (dst_seconds) *dst_seconds = timezone->tz_dsttime * 60; } else { - return SCE_KERNEL_ERROR_EINVAL; + return ORBIS_KERNEL_ERROR_EINVAL; } - return SCE_OK; + return ORBIS_OK; } namespace Dev { @@ -258,7 +268,33 @@ Common::NativeClock* GetClock() { } // namespace Dev -void timeSymbolsRegister(Core::Loader::SymbolsResolver* sym) { +int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time, + struct OrbisTimesec* st, u64* dst_sec) { + LOG_TRACE(Kernel, "Called"); +#ifdef __APPLE__ + // std::chrono::current_zone() not available yet. + const auto* time_zone = date::current_zone(); +#else + const auto* time_zone = std::chrono::current_zone(); +#endif + auto info = time_zone->get_info(std::chrono::system_clock::now()); + + *local_time = info.offset.count() + info.save.count() * 60 + time; + + if (st != nullptr) { + st->t = time; + st->west_sec = info.offset.count() * 60; + st->dst_sec = info.save.count() * 60; + } + + if (dst_sec != nullptr) { + *dst_sec = info.save.count() * 60; + } + + return ORBIS_OK; +} + +void RegisterTime(Core::Loader::SymbolsResolver* sym) { clock = std::make_unique(); initial_ptc = clock->GetUptime(); LIB_FUNCTION("4J2sUJmuHZQ", "libkernel", 1, "libkernel", 1, 1, sceKernelGetProcessTime); @@ -284,6 +320,7 @@ void timeSymbolsRegister(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("lLMT9vJAck0", "libScePosix", 1, "libkernel", 1, 1, posix_clock_gettime); LIB_FUNCTION("smIj7eqzZE8", "libScePosix", 1, "libkernel", 1, 1, posix_clock_getres); LIB_FUNCTION("0NTHN1NKONI", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertLocaltimeToUtc); + LIB_FUNCTION("-o5uEDpN+oY", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertUtcToLocaltime); } } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/time_management.h b/src/core/libraries/kernel/time.h similarity index 77% rename from src/core/libraries/kernel/time_management.h rename to src/core/libraries/kernel/time.h index f2216f3d3..6aa281aaf 100644 --- a/src/core/libraries/kernel/time_management.h +++ b/src/core/libraries/kernel/time.h @@ -3,8 +3,8 @@ #pragma once +#include #include - #include "common/types.h" namespace Common { @@ -30,6 +30,19 @@ struct OrbisKernelTimezone { struct OrbisKernelTimespec { s64 tv_sec; s64 tv_nsec; + + std::chrono::system_clock::time_point TimePoint() const noexcept { + using namespace std::chrono; + const auto duration = + duration_cast(seconds{tv_sec} + nanoseconds{tv_nsec}); + return system_clock::time_point{duration}; + } +}; + +struct OrbisTimesec { + time_t t; + u32 west_sec; + u32 dst_sec; }; constexpr int ORBIS_CLOCK_REALTIME = 0; @@ -66,6 +79,10 @@ int PS4_SYSV_ABI sceKernelClockGettime(s32 clock_id, OrbisKernelTimespec* tp); s32 PS4_SYSV_ABI sceKernelGettimezone(OrbisKernelTimezone* tz); int PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, time_t* seconds, OrbisKernelTimezone* timezone, int* dst_seconds); -void timeSymbolsRegister(Core::Loader::SymbolsResolver* sym); + +int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time, OrbisTimesec* st, + u64* dst_sec); + +void RegisterTime(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Kernel diff --git a/src/core/libraries/libc_internal/libc_internal.cpp b/src/core/libraries/libc_internal/libc_internal.cpp index 8eea41eb3..eb6046c7a 100644 --- a/src/core/libraries/libc_internal/libc_internal.cpp +++ b/src/core/libraries/libc_internal/libc_internal.cpp @@ -3,8 +3,8 @@ #include +#include "common/assert.h" #include "common/logging/log.h" -#include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "libc_internal.h" @@ -36,28 +36,184 @@ int PS4_SYSV_ABI internal_strcpy_s(char* dest, size_t dest_size, const char* src #endif } +int PS4_SYSV_ABI internal_strcat_s(char* dest, size_t dest_size, const char* src) { +#ifdef _WIN64 + return strcat_s(dest, dest_size, src); +#else + std::strcat(dest, src); + return 0; // ALL OK +#endif +} + int PS4_SYSV_ABI internal_memcmp(const void* s1, const void* s2, size_t n) { return std::memcmp(s1, s2, n); } +int PS4_SYSV_ABI internal_strcmp(const char* str1, const char* str2) { + return std::strcmp(str1, str2); +} + int PS4_SYSV_ABI internal_strncmp(const char* str1, const char* str2, size_t num) { return std::strncmp(str1, str2, num); } -int PS4_SYSV_ABI internal_strlen(const char* str) { +size_t PS4_SYSV_ABI internal_strlen(const char* str) { return std::strlen(str); } +char* PS4_SYSV_ABI internal_strncpy(char* dest, const char* src, std::size_t count) { + return std::strncpy(dest, src, count); +} + +char* PS4_SYSV_ABI internal_strcat(char* dest, const char* src) { + return std::strcat(dest, src); +} + +const char* PS4_SYSV_ABI internal_strchr(const char* str, int c) { + return std::strchr(str, c); +} + +double PS4_SYSV_ABI internal_sin(double x) { + return std::sin(x); +} + +float PS4_SYSV_ABI internal_sinf(float x) { + return std::sinf(x); +} + +double PS4_SYSV_ABI internal_cos(double x) { + return std::cos(x); +} + +float PS4_SYSV_ABI internal_cosf(float x) { + return std::cosf(x); +} + +void PS4_SYSV_ABI internal_sincos(double x, double* sinp, double* cosp) { + *sinp = std::sin(x); + *cosp = std::cos(x); +} + +void PS4_SYSV_ABI internal_sincosf(float x, float* sinp, float* cosp) { + *sinp = std::sinf(x); + *cosp = std::cosf(x); +} + +double PS4_SYSV_ABI internal_tan(double x) { + return std::tan(x); +} + +float PS4_SYSV_ABI internal_tanf(float x) { + return std::tanf(x); +} + +double PS4_SYSV_ABI internal_asin(double x) { + return std::asin(x); +} + +float PS4_SYSV_ABI internal_asinf(float x) { + return std::asinf(x); +} + +double PS4_SYSV_ABI internal_acos(double x) { + return std::acos(x); +} + +float PS4_SYSV_ABI internal_acosf(float x) { + return std::acosf(x); +} + +double PS4_SYSV_ABI internal_atan(double x) { + return std::atan(x); +} + +float PS4_SYSV_ABI internal_atanf(float x) { + return std::atanf(x); +} + +double PS4_SYSV_ABI internal_atan2(double y, double x) { + return std::atan2(y, x); +} + +float PS4_SYSV_ABI internal_atan2f(float y, float x) { + return std::atan2f(y, x); +} + +double PS4_SYSV_ABI internal_exp(double x) { + return std::exp(x); +} + float PS4_SYSV_ABI internal_expf(float x) { - return expf(x); + return std::expf(x); +} + +double PS4_SYSV_ABI internal_exp2(double x) { + return std::exp2(x); +} + +float PS4_SYSV_ABI internal_exp2f(float x) { + return std::exp2f(x); +} + +double PS4_SYSV_ABI internal_pow(double x, double y) { + return std::pow(x, y); +} + +float PS4_SYSV_ABI internal_powf(float x, float y) { + return std::powf(x, y); +} + +double PS4_SYSV_ABI internal_log(double x) { + return std::log(x); +} + +float PS4_SYSV_ABI internal_logf(float x) { + return std::logf(x); +} + +double PS4_SYSV_ABI internal_log10(double x) { + return std::log10(x); +} + +float PS4_SYSV_ABI internal_log10f(float x) { + return std::log10f(x); } void* PS4_SYSV_ABI internal_malloc(size_t size) { return std::malloc(size); } -char* PS4_SYSV_ABI internal_strncpy(char* dest, const char* src, std::size_t count) { - return std::strncpy(dest, src, count); +void PS4_SYSV_ABI internal_free(void* ptr) { + std::free(ptr); +} + +void* PS4_SYSV_ABI internal_operator_new(size_t size) { + if (size == 0) { + // Size of 1 is used if 0 is provided. + size = 1; + } + void* ptr = std::malloc(size); + ASSERT_MSG(ptr, "Failed to allocate new object with size {}", size); + return ptr; +} + +void PS4_SYSV_ABI internal_operator_delete(void* ptr) { + if (ptr) { + std::free(ptr); + } +} + +int PS4_SYSV_ABI internal_posix_memalign(void** ptr, size_t alignment, size_t size) { +#ifdef _WIN64 + void* allocated = _aligned_malloc(size, alignment); + if (!allocated) { + return errno; + } + *ptr = allocated; + return 0; +#else + return posix_memalign(ptr, alignment, size); +#endif } void RegisterlibSceLibcInternal(Core::Loader::SymbolsResolver* sym) { @@ -69,17 +225,71 @@ void RegisterlibSceLibcInternal(Core::Loader::SymbolsResolver* sym) { internal_memset); LIB_FUNCTION("5Xa2ACNECdo", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_strcpy_s); + LIB_FUNCTION("K+gcnFFJKVc", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_strcat_s); LIB_FUNCTION("DfivPArhucg", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_memcmp); - LIB_FUNCTION("8zsu04XNsZ4", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_expf); LIB_FUNCTION("aesyjrHVWy4", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_strcmp); + LIB_FUNCTION("Ovb2dSJOAuE", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_strncmp); LIB_FUNCTION("j4ViWNHEgww", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_strlen); LIB_FUNCTION("6sJWiWSRuqk", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_strncpy); + LIB_FUNCTION("Ls4tzzhimqQ", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_strcat); + LIB_FUNCTION("ob5xAW4ln-0", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_strchr); + LIB_FUNCTION("H8ya2H00jbI", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_sin); + LIB_FUNCTION("Q4rRL34CEeE", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_sinf); + LIB_FUNCTION("2WE3BTYVwKM", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_cos); + LIB_FUNCTION("-P6FNMzk2Kc", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_cosf); + LIB_FUNCTION("jMB7EFyu30Y", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_sincos); + LIB_FUNCTION("pztV4AF18iI", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_sincosf); + LIB_FUNCTION("T7uyNqP7vQA", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_tan); + LIB_FUNCTION("ZE6RNL+eLbk", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_tanf); + LIB_FUNCTION("7Ly52zaL44Q", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_asin); + LIB_FUNCTION("GZWjF-YIFFk", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_asinf); + LIB_FUNCTION("JBcgYuW8lPU", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_acos); + LIB_FUNCTION("QI-x0SL8jhw", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_acosf); + LIB_FUNCTION("OXmauLdQ8kY", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_atan); + LIB_FUNCTION("weDug8QD-lE", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_atanf); + LIB_FUNCTION("HUbZmOnT-Dg", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_atan2); + LIB_FUNCTION("EH-x713A99c", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_atan2f); + LIB_FUNCTION("NVadfnzQhHQ", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_exp); + LIB_FUNCTION("8zsu04XNsZ4", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_expf); + LIB_FUNCTION("dnaeGXbjP6E", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_exp2); + LIB_FUNCTION("wuAQt-j+p4o", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_exp2f); + LIB_FUNCTION("9LCjpWyQ5Zc", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_pow); + LIB_FUNCTION("1D0H2KNjshE", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_powf); + LIB_FUNCTION("rtV7-jWC6Yg", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_log); + LIB_FUNCTION("RQXLbdT2lc4", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_logf); + LIB_FUNCTION("WuMbPBKN1TU", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_log10); + LIB_FUNCTION("lhpd6Wk6ccs", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_log10f); LIB_FUNCTION("gQX+4GDQjpM", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_malloc); + LIB_FUNCTION("tIhsqj0qsFE", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, internal_free); + LIB_FUNCTION("fJnpuVVBbKk", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_operator_new); + LIB_FUNCTION("hdm0YfMa7TQ", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_operator_new); + LIB_FUNCTION("MLWl90SFWNE", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_operator_delete); + LIB_FUNCTION("z+P+xCnWLBk", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_operator_delete); + LIB_FUNCTION("cVSk9y8URbc", "libSceLibcInternal", 1, "libSceLibcInternal", 1, 1, + internal_posix_memalign); }; } // namespace Libraries::LibcInternal diff --git a/src/core/libraries/libpng/pngdec.cpp b/src/core/libraries/libpng/pngdec.cpp index 3a5d1ba71..d9a324fc5 100644 --- a/src/core/libraries/libpng/pngdec.cpp +++ b/src/core/libraries/libpng/pngdec.cpp @@ -1,55 +1,48 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include "common/assert.h" #include "common/logging/log.h" -#include "core/libraries/error_codes.h" +#include "core/libraries/libpng/pngdec.h" +#include "core/libraries/libpng/pngdec_error.h" #include "core/libraries/libs.h" -#include "externals/stb_image.h" -#include "pngdec.h" namespace Libraries::PngDec { -void setImageInfoParams(OrbisPngDecImageInfo* imageInfo, int width, int height, int channels, - bool isInterlaced, bool isTransparent) { - if (imageInfo != nullptr) { - imageInfo->imageWidth = width; - imageInfo->imageHeight = height; - imageInfo->bitDepth = 8; // always 8? - switch (channels) { // clut missing - case 1: - imageInfo->colorSpace = OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_GRAYSCALE; - break; - case 2: - imageInfo->colorSpace = - OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_GRAYSCALE_ALPHA; - break; - case 3: - imageInfo->colorSpace = OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_RGB; - break; - case 4: - imageInfo->colorSpace = OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_RGBA; - break; - default: - imageInfo->colorSpace = OrbisPngDecColorSpace::ORBIS_PNG_DEC_COLOR_SPACE_RGB; - break; - } - imageInfo->imageFlag = 0; - if (isInterlaced) { - imageInfo->imageFlag |= ORBIS_PNG_DEC_IMAGE_FLAG_ADAM7_INTERLACE; - } - if (isTransparent) { - imageInfo->imageFlag |= ORBIS_PNG_DEC_IMAGE_FLAG_TRNS_CHUNK_EXIST; - } +struct PngHandler { + png_structp png_ptr; + png_infop info_ptr; +}; + +struct PngStruct { + const u8* data; + size_t size; + u64 offset; +}; + +static inline OrbisPngDecColorSpace MapPngColor(int color) { + switch (color) { + case PNG_COLOR_TYPE_GRAY: + return OrbisPngDecColorSpace::Grayscale; + case PNG_COLOR_TYPE_GRAY_ALPHA: + return OrbisPngDecColorSpace::GrayscaleAlpha; + case PNG_COLOR_TYPE_PALETTE: + return OrbisPngDecColorSpace::Clut; + case PNG_COLOR_TYPE_RGB: + return OrbisPngDecColorSpace::Rgb; + case PNG_COLOR_TYPE_RGB_ALPHA: + return OrbisPngDecColorSpace::Rgba; } + UNREACHABLE_MSG("unknown png color type"); } -bool checktRNS(const u8* png_raw, int size) { - for (int i = 30; i < size - 4; i += 4) { - if (std::memcmp(png_raw + i, "tRNS", 4) == 0) { - return true; - } - } - return false; +void PngDecError(png_structp png_ptr, png_const_charp error_message) { + LOG_ERROR(Lib_Png, "PNG error {}", error_message); +} + +void PngDecWarning(png_structp png_ptr, png_const_charp error_message) { + LOG_ERROR(Lib_Png, "PNG warning {}", error_message); } s32 PS4_SYSV_ABI scePngDecCreate(const OrbisPngDecCreateParam* param, void* memoryAddress, @@ -62,15 +55,25 @@ s32 PS4_SYSV_ABI scePngDecCreate(const OrbisPngDecCreateParam* param, void* memo LOG_ERROR(Lib_Png, "Invalid memory address!"); return ORBIS_PNG_DEC_ERROR_INVALID_ADDR; } - if (param->maxImageWidth - 1 > 1000000) { - LOG_ERROR(Lib_Png, "Invalid size! width = {}", param->maxImageWidth); + if (param->max_image_width - 1 > 1000000) { + LOG_ERROR(Lib_Png, "Invalid size! width = {}", param->max_image_width); return ORBIS_PNG_DEC_ERROR_INVALID_SIZE; } - const OrbisPngDecCreateParam* nextParam = param + 1; - int ret = (8 << (reinterpret_cast(nextParam) & 0x1f)) * - (param->maxImageWidth + 0x47U & 0xfffffff8) + - 0xd000; - *handle = reinterpret_cast(ret); + auto pngh = (PngHandler*)memoryAddress; + + pngh->png_ptr = + png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, PngDecError, PngDecWarning); + + if (pngh->png_ptr == nullptr) + return ORBIS_PNG_DEC_ERROR_FATAL; + + pngh->info_ptr = png_create_info_struct(pngh->png_ptr); + if (pngh->info_ptr == nullptr) { + png_destroy_read_struct(&pngh->png_ptr, nullptr, nullptr); + return false; + } + + *handle = pngh; return ORBIS_OK; } @@ -84,26 +87,93 @@ s32 PS4_SYSV_ABI scePngDecDecode(OrbisPngDecHandle handle, const OrbisPngDecDeco LOG_ERROR(Lib_Png, "Invalid param!"); return ORBIS_PNG_DEC_ERROR_INVALID_PARAM; } - if (param->pngMemAddr == nullptr || param->pngMemAddr == nullptr) { + if (param->png_mem_addr == nullptr || param->image_mem_addr == nullptr) { LOG_ERROR(Lib_Png, "invalid image address!"); return ORBIS_PNG_DEC_ERROR_INVALID_ADDR; } + LOG_TRACE(Lib_Png, + "pngMemSize = {} , imageMemSize = {} , pixelFormat = {} , alphaValue = {} , " + "imagePitch = {}", + param->png_mem_size, param->image_mem_size, int(param->pixel_format), + param->alpha_value, param->image_pitch); - int width, height, channels; - const u8* png_raw = (const u8*)param->pngMemAddr; - u8* img = stbi_load_from_memory(png_raw, param->pngMemSize, &width, &height, &channels, - STBI_rgb_alpha); // STBI_rgb_alpha? - if (!img) { - LOG_ERROR(Lib_Png, "Decoding failed!"); - return ORBIS_PNG_DEC_ERROR_DECODE_ERROR; + auto pngh = (PngHandler*)handle; + + const auto pngdata = PngStruct{ + .data = param->png_mem_addr, + .size = param->png_mem_size, + .offset = 0, + }; + png_set_read_fn(pngh->png_ptr, (void*)&pngdata, + [](png_structp ps, png_bytep data, png_size_t len) { + if (len == 0) + return; + auto pngdata = (PngStruct*)png_get_io_ptr(ps); + ::memcpy(data, pngdata->data + pngdata->offset, len); + pngdata->offset += len; + }); + + png_read_info(pngh->png_ptr, pngh->info_ptr); + const u32 width = png_get_image_width(pngh->png_ptr, pngh->info_ptr); + const u32 height = png_get_image_height(pngh->png_ptr, pngh->info_ptr); + const auto color_type = MapPngColor(png_get_color_type(pngh->png_ptr, pngh->info_ptr)); + const auto bit_depth = png_get_bit_depth(pngh->png_ptr, pngh->info_ptr); + + if (imageInfo != nullptr) { + imageInfo->bit_depth = bit_depth; + imageInfo->image_width = width; + imageInfo->image_height = height; + imageInfo->color_space = color_type; + imageInfo->image_flag = OrbisPngDecImageFlag::None; + if (png_get_interlace_type(pngh->png_ptr, pngh->info_ptr) == 1) { + imageInfo->image_flag |= OrbisPngDecImageFlag::Adam7Interlace; + } + if (png_get_valid(pngh->png_ptr, pngh->info_ptr, PNG_INFO_tRNS)) { + imageInfo->image_flag |= OrbisPngDecImageFlag::TrnsChunkExist; + } } - bool isInterlaced = (png_raw[28] == 1); - bool isTransparent = checktRNS(png_raw, param->pngMemSize); - setImageInfoParams(imageInfo, width, height, channels, isInterlaced, isTransparent); - u8* imageBuffer = (u8*)(param->imageMemAddr); - memcpy(imageBuffer, img, width * height * 4); // copy/pass decoded data - stbi_image_free(img); - return 0; + + if (bit_depth == 16) { + png_set_strip_16(pngh->png_ptr); + } + if (color_type == OrbisPngDecColorSpace::Clut) { + png_set_palette_to_rgb(pngh->png_ptr); + } + if (color_type == OrbisPngDecColorSpace::Grayscale && bit_depth < 8) { + png_set_expand_gray_1_2_4_to_8(pngh->png_ptr); + } + if (png_get_valid(pngh->png_ptr, pngh->info_ptr, PNG_INFO_tRNS)) { + png_set_tRNS_to_alpha(pngh->png_ptr); + } + if (color_type == OrbisPngDecColorSpace::Grayscale || + color_type == OrbisPngDecColorSpace::GrayscaleAlpha) { + png_set_gray_to_rgb(pngh->png_ptr); + } + if (param->pixel_format == OrbisPngDecPixelFormat::B8G8R8A8) { + png_set_bgr(pngh->png_ptr); + } + if (color_type == OrbisPngDecColorSpace::Rgb || + color_type == OrbisPngDecColorSpace::Grayscale || + color_type == OrbisPngDecColorSpace::Clut) { + png_set_add_alpha(pngh->png_ptr, param->alpha_value, PNG_FILLER_AFTER); + } + + const s32 pass = png_set_interlace_handling(pngh->png_ptr); + png_read_update_info(pngh->png_ptr, pngh->info_ptr); + + const s32 num_channels = png_get_channels(pngh->png_ptr, pngh->info_ptr); + const s32 horizontal_bytes = num_channels * width; + const s32 stride = param->image_pitch > 0 ? param->image_pitch : horizontal_bytes; + + for (int j = 0; j < pass; j++) { + auto ptr = reinterpret_cast(param->image_mem_addr); + for (int y = 0; y < height; y++) { + png_read_row(pngh->png_ptr, ptr, nullptr); + ptr += stride; + } + } + + return (width > 32767 || height > 32767) ? 0 : (width << 16) | height; } s32 PS4_SYSV_ABI scePngDecDecodeWithInputControl() { @@ -112,8 +182,8 @@ s32 PS4_SYSV_ABI scePngDecDecodeWithInputControl() { } s32 PS4_SYSV_ABI scePngDecDelete(OrbisPngDecHandle handle) { - handle = nullptr; // ? - LOG_ERROR(Lib_Png, "(STUBBED)called"); + auto pngh = *(PngHandler**)handle; + png_destroy_read_struct(&pngh->png_ptr, &pngh->info_ptr, nullptr); return ORBIS_OK; } @@ -123,16 +193,55 @@ s32 PS4_SYSV_ABI scePngDecParseHeader(const OrbisPngDecParseParam* param, LOG_ERROR(Lib_Png, "Invalid param!"); return ORBIS_PNG_DEC_ERROR_INVALID_PARAM; } - int width, height, channels; - const u8* png_raw = (const u8*)(param->pngMemAddr); - int img = stbi_info_from_memory(png_raw, param->pngMemSize, &width, &height, &channels); - if (img == 0) { - LOG_ERROR(Lib_Png, "Decoding failed!"); - return ORBIS_PNG_DEC_ERROR_DECODE_ERROR; + + u8 header[8]; + memcpy(header, param->png_mem_addr, 8); + // Check if the header indicates a valid PNG file + if (png_sig_cmp(header, 0, 8)) { + LOG_ERROR(Lib_Png, "Memory doesn't contain a valid png file"); + return ORBIS_PNG_DEC_ERROR_INVALID_DATA; } - bool isInterlaced = (png_raw[28] == 1); - bool isTransparent = checktRNS(png_raw, param->pngMemSize); - setImageInfoParams(imageInfo, width, height, channels, isInterlaced, isTransparent); + // Create a libpng structure, also pass our custom error/warning functions + auto png_ptr = + png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, PngDecError, PngDecWarning); + + // Create a libpng info structure + auto info_ptr = png_create_info_struct(png_ptr); + + const auto pngdata = PngStruct{ + .data = param->png_mem_addr, + .size = param->png_mem_size, + .offset = 0, + }; + + png_set_read_fn(png_ptr, (void*)&pngdata, [](png_structp ps, png_bytep data, png_size_t len) { + auto pngdata = (PngStruct*)png_get_io_ptr(ps); + ::memcpy(data, pngdata->data + pngdata->offset, len); + pngdata->offset += len; + }); + + // Now call png_read_info with our pngPtr as image handle, and infoPtr to receive the file + // info. + png_read_info(png_ptr, info_ptr); + + imageInfo->image_width = png_get_image_width(png_ptr, info_ptr); + imageInfo->image_height = png_get_image_height(png_ptr, info_ptr); + imageInfo->color_space = MapPngColor(png_get_color_type(png_ptr, info_ptr)); + imageInfo->bit_depth = png_get_bit_depth(png_ptr, info_ptr); + imageInfo->image_flag = OrbisPngDecImageFlag::None; + if (png_get_interlace_type(png_ptr, info_ptr) == 1) { + imageInfo->image_flag |= OrbisPngDecImageFlag::Adam7Interlace; + } + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { + imageInfo->image_flag |= OrbisPngDecImageFlag::TrnsChunkExist; + } + + png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); + LOG_TRACE( + Lib_Png, + "imageWidth = {} , imageHeight = {} , colorSpace = {} , bitDepth = {} , imageFlag = {}", + imageInfo->image_width, imageInfo->image_height, int(imageInfo->color_space), + imageInfo->bit_depth, int(imageInfo->image_flag)); return ORBIS_OK; } @@ -145,13 +254,11 @@ s32 PS4_SYSV_ABI scePngDecQueryMemorySize(const OrbisPngDecCreateParam* param) { LOG_ERROR(Lib_Png, "Invalid attribute! attribute = {}", param->attribute); return ORBIS_PNG_DEC_ERROR_INVALID_ADDR; } - if (param->maxImageWidth - 1 > 1000000) { - LOG_ERROR(Lib_Png, "Invalid size! width = {}", param->maxImageWidth); + if (param->max_image_width - 1 > 1000000) { + LOG_ERROR(Lib_Png, "Invalid size! width = {}", param->max_image_width); return ORBIS_PNG_DEC_ERROR_INVALID_SIZE; } - int ret = - (8 << ((u8)param->attribute & 0x1f)) * (param->maxImageWidth + 0x47U & 0xfffffff8) + 0xd090; - return ret; + return sizeof(PngHandler); } void RegisterlibScePngDec(Core::Loader::SymbolsResolver* sym) { @@ -166,4 +273,4 @@ void RegisterlibScePngDec(Core::Loader::SymbolsResolver* sym) { scePngDecDecodeWithInputControl); }; -} // namespace Libraries::PngDec \ No newline at end of file +} // namespace Libraries::PngDec diff --git a/src/core/libraries/libpng/pngdec.h b/src/core/libraries/libpng/pngdec.h index 35034a196..c897d7c95 100644 --- a/src/core/libraries/libpng/pngdec.h +++ b/src/core/libraries/libpng/pngdec.h @@ -3,67 +3,71 @@ #pragma once +#include "common/enum.h" #include "common/types.h" namespace Core::Loader { class SymbolsResolver; } + namespace Libraries::PngDec { -constexpr int ORBIS_PNG_DEC_ERROR_INVALID_ADDR = 0x80690001; -constexpr int ORBIS_PNG_DEC_ERROR_INVALID_SIZE = 0x80690002; -constexpr int ORBIS_PNG_DEC_ERROR_INVALID_PARAM = 0x80690003; -constexpr int ORBIS_PNG_DEC_ERROR_INVALID_HANDLE = 0x80690004; -constexpr int ORBIS_PNG_DEC_ERROR_INVALID_WORK_MEMORY = 0x80690005; -constexpr int ORBIS_PNG_DEC_ERROR_INVALID_DATA = 0x80690010; -constexpr int ORBIS_PNG_DEC_ERROR_UNSUPPORT_DATA = 0x80690011; -constexpr int ORBIS_PNG_DEC_ERROR_DECODE_ERROR = 0x80690012; -constexpr int ORBIS_PNG_DEC_ERROR_FATAL = 0x80690020; +enum class OrbisPngDecColorSpace : u16 { + Grayscale = 2, + Rgb, + Clut, + GrayscaleAlpha = 18, + Rgba, +}; -typedef struct OrbisPngDecParseParam { - const void* pngMemAddr; - u32 pngMemSize; +enum class OrbisPngDecImageFlag : u32 { + None = 0, + Adam7Interlace = 1, + TrnsChunkExist = 2, +}; +DECLARE_ENUM_FLAG_OPERATORS(OrbisPngDecImageFlag) + +enum class OrbisPngDecPixelFormat : u16 { + R8G8B8A8 = 0, + B8G8R8A8, +}; + +enum class OrbisPngDecAttribute { + None = 0, + BitDepth16, +}; + +struct OrbisPngDecParseParam { + const u8* png_mem_addr; + u32 png_mem_size; u32 reserved; -} OrbisPngDecParseParam; +}; -typedef struct OrbisPngDecImageInfo { - u32 imageWidth; - u32 imageHeight; - u16 colorSpace; - u16 bitDepth; - u32 imageFlag; -} OrbisPngDecImageInfo; +struct OrbisPngDecImageInfo { + u32 image_width; + u32 image_height; + OrbisPngDecColorSpace color_space; + u16 bit_depth; + OrbisPngDecImageFlag image_flag; +}; -typedef enum OrbisPngDecColorSpace { - ORBIS_PNG_DEC_COLOR_SPACE_GRAYSCALE = 2, - ORBIS_PNG_DEC_COLOR_SPACE_RGB, - ORBIS_PNG_DEC_COLOR_SPACE_CLUT, - ORBIS_PNG_DEC_COLOR_SPACE_GRAYSCALE_ALPHA = 18, - ORBIS_PNG_DEC_COLOR_SPACE_RGBA -} ScePngDecColorSpace; - -typedef enum OrbisPngDecImageFlag { - ORBIS_PNG_DEC_IMAGE_FLAG_ADAM7_INTERLACE = 1, - ORBIS_PNG_DEC_IMAGE_FLAG_TRNS_CHUNK_EXIST = 2 -} OrbisPngDecImageFlag; - -typedef struct OrbisPngDecCreateParam { - u32 thisSize; +struct OrbisPngDecCreateParam { + u32 this_size; u32 attribute; - u32 maxImageWidth; -} OrbisPngDecCreateParam; + u32 max_image_width; +}; -typedef void* OrbisPngDecHandle; +using OrbisPngDecHandle = void*; -typedef struct OrbisPngDecDecodeParam { - const void* pngMemAddr; - void* imageMemAddr; - u32 pngMemSize; - u32 imageMemSize; - u16 pixelFormat; - u16 alphaValue; - u32 imagePitch; -} OrbisPngDecDecodeParam; +struct OrbisPngDecDecodeParam { + const u8* png_mem_addr; + u8* image_mem_addr; + u32 png_mem_size; + u32 image_mem_size; + OrbisPngDecPixelFormat pixel_format; + u16 alpha_value; + u32 image_pitch; +}; s32 PS4_SYSV_ABI scePngDecCreate(const OrbisPngDecCreateParam* param, void* memoryAddress, u32 memorySize, OrbisPngDecHandle* handle); @@ -76,4 +80,4 @@ s32 PS4_SYSV_ABI scePngDecParseHeader(const OrbisPngDecParseParam* param, s32 PS4_SYSV_ABI scePngDecQueryMemorySize(const OrbisPngDecCreateParam* param); void RegisterlibScePngDec(Core::Loader::SymbolsResolver* sym); -} // namespace Libraries::PngDec \ No newline at end of file +} // namespace Libraries::PngDec diff --git a/src/core/libraries/libpng/pngdec_error.h b/src/core/libraries/libpng/pngdec_error.h new file mode 100644 index 000000000..9745ed5d1 --- /dev/null +++ b/src/core/libraries/libpng/pngdec_error.h @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// PngDec library +constexpr int ORBIS_PNG_DEC_ERROR_INVALID_ADDR = 0x80690001; +constexpr int ORBIS_PNG_DEC_ERROR_INVALID_SIZE = 0x80690002; +constexpr int ORBIS_PNG_DEC_ERROR_INVALID_PARAM = 0x80690003; +constexpr int ORBIS_PNG_DEC_ERROR_INVALID_HANDLE = 0x80690004; +constexpr int ORBIS_PNG_DEC_ERROR_INVALID_WORK_MEMORY = 0x80690005; +constexpr int ORBIS_PNG_DEC_ERROR_INVALID_DATA = 0x80690010; +constexpr int ORBIS_PNG_DEC_ERROR_UNSUPPORT_DATA = 0x80690011; +constexpr int ORBIS_PNG_DEC_ERROR_DECODE_ERROR = 0x80690012; +constexpr int ORBIS_PNG_DEC_ERROR_FATAL = 0x80690020; diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index 23d751622..66cdd5b87 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -14,7 +14,7 @@ #include "core/libraries/ime/error_dialog.h" #include "core/libraries/ime/ime.h" #include "core/libraries/ime/ime_dialog.h" -#include "core/libraries/kernel/libkernel.h" +#include "core/libraries/kernel/kernel.h" #include "core/libraries/libc_internal/libc_internal.h" #include "core/libraries/libpng/pngdec.h" #include "core/libraries/libs.h" @@ -27,7 +27,9 @@ #include "core/libraries/np_trophy/np_trophy.h" #include "core/libraries/pad/pad.h" #include "core/libraries/playgo/playgo.h" +#include "core/libraries/playgo/playgo_dialog.h" #include "core/libraries/random/random.h" +#include "core/libraries/razor_cpu/razor_cpu.h" #include "core/libraries/remote_play/remoteplay.h" #include "core/libraries/rtc/rtc.h" #include "core/libraries/save_data/dialog/savedatadialog.h" @@ -49,11 +51,9 @@ namespace Libraries { void InitHLELibs(Core::Loader::SymbolsResolver* sym) { LOG_INFO(Lib_Kernel, "Initializing HLE libraries"); - Libraries::Kernel::LibKernel_Register(sym); + Libraries::Kernel::RegisterKernel(sym); Libraries::GnmDriver::RegisterlibSceGnmDriver(sym); Libraries::VideoOut::RegisterLib(sym); - - // New libraries folder from autogen Libraries::UserService::RegisterlibSceUserService(sym); Libraries::SystemService::RegisterlibSceSystemService(sym); Libraries::CommonDialog::RegisterlibSceCommonDialog(sym); @@ -75,6 +75,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::AppContent::RegisterlibSceAppContent(sym); Libraries::PngDec::RegisterlibScePngDec(sym); Libraries::PlayGo::RegisterlibScePlayGo(sym); + Libraries::PlayGo::Dialog::RegisterlibScePlayGoDialog(sym); Libraries::Random::RegisterlibSceRandom(sym); Libraries::Usbd::RegisterlibSceUsbd(sym); Libraries::Pad::RegisterlibScePad(sym); @@ -89,6 +90,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::SharePlay::RegisterlibSceSharePlay(sym); Libraries::Remoteplay::RegisterlibSceRemoteplay(sym); Libraries::Videodec::RegisterlibSceVideodec(sym); + Libraries::RazorCpu::RegisterlibSceRazorCpu(sym); } } // namespace Libraries diff --git a/src/core/libraries/libs.h b/src/core/libraries/libs.h index ea928101e..aa5ba4a97 100644 --- a/src/core/libraries/libs.h +++ b/src/core/libraries/libs.h @@ -9,41 +9,6 @@ #include "core/loader/elf.h" #include "core/loader/symbols_resolver.h" -template -struct StringLiteral { - constexpr StringLiteral(const char (&str)[N]) { - std::copy_n(str, N, value); - } - - char value[N]; -}; - -template -struct wrapper_impl; - -template -struct wrapper_impl { - static R PS4_SYSV_ABI wrap(Args... args) { - if (std::string_view(name.value) != "scePthreadEqual" && - std::string_view(name.value) != "sceUserServiceGetEvent") { - // LOG_WARNING(Core_Linker, "Function {} called", name.value); - } - if constexpr (std::is_same_v || std::is_same_v) { - const u32 ret = f(args...); - if (ret != 0 && std::string_view(name.value) != "scePthreadEqual") { - LOG_WARNING(Core_Linker, "Function {} returned {:#x}", name.value, ret); - } - return ret; - } - // stuff - return f(args...); - } -}; - -template -constexpr auto wrapper = wrapper_impl::wrap; - -// #define W(foo) wrapper<#foo, decltype(&foo), foo> #define W(foo) foo #define LIB_FUNCTION(nid, lib, libversion, mod, moduleVersionMajor, moduleVersionMinor, function) \ @@ -56,7 +21,7 @@ constexpr auto wrapper = wrapper_impl::wrap; sr.module_version_major = moduleVersionMajor; \ sr.module_version_minor = moduleVersionMinor; \ sr.type = Core::Loader::SymbolType::Function; \ - auto func = reinterpret_cast(W(function)); \ + auto func = reinterpret_cast(function); \ sym->AddSymbol(sr, func); \ } diff --git a/src/core/libraries/network/net_ctl_obj.cpp b/src/core/libraries/network/net_ctl_obj.cpp index 07381d676..ad944cd9c 100644 --- a/src/core/libraries/network/net_ctl_obj.cpp +++ b/src/core/libraries/network/net_ctl_obj.cpp @@ -1,80 +1,63 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "common/singleton.h" -#include "core/linker.h" -#include "net_ctl_codes.h" -#include "net_ctl_obj.h" +#include +#include "core/libraries/network/net_ctl_codes.h" +#include "core/libraries/network/net_ctl_obj.h" +#include "core/tls.h" -Libraries::NetCtl::NetCtlInternal::NetCtlInternal() { - callbacks.fill({nullptr, nullptr}); - nptoolCallbacks.fill({nullptr, nullptr}); -} +namespace Libraries::NetCtl { -Libraries::NetCtl::NetCtlInternal::~NetCtlInternal() {} +NetCtlInternal::NetCtlInternal() = default; -s32 Libraries::NetCtl::NetCtlInternal::registerCallback(OrbisNetCtlCallback func, void* arg) { - std::unique_lock lock{m_mutex}; +NetCtlInternal::~NetCtlInternal() = default; + +s32 NetCtlInternal::RegisterCallback(OrbisNetCtlCallback func, void* arg) { + std::scoped_lock lock{m_mutex}; // Find the next available slot - int next_id = 0; - for (const auto& callback : callbacks) { - if (callback.func == nullptr) { - break; - } - next_id++; - } - - if (next_id == 8) { + const auto it = std::ranges::find(callbacks, nullptr, &NetCtlCallback::func); + if (it == callbacks.end()) { return ORBIS_NET_CTL_ERROR_CALLBACK_MAX; } + const int next_id = std::distance(callbacks.begin(), it); callbacks[next_id].func = func; callbacks[next_id].arg = arg; return next_id; } -s32 Libraries::NetCtl::NetCtlInternal::registerNpToolkitCallback( - OrbisNetCtlCallbackForNpToolkit func, void* arg) { - - std::unique_lock lock{m_mutex}; +s32 NetCtlInternal::RegisterNpToolkitCallback(OrbisNetCtlCallbackForNpToolkit func, void* arg) { + std::scoped_lock lock{m_mutex}; // Find the next available slot - int next_id = 0; - for (const auto& callback : nptoolCallbacks) { - if (callback.func == nullptr) { - break; - } - next_id++; - } - - if (next_id == 8) { + const auto it = std::ranges::find(nptool_callbacks, nullptr, &NetCtlCallbackForNpToolkit::func); + if (it == nptool_callbacks.end()) { return ORBIS_NET_CTL_ERROR_CALLBACK_MAX; } - nptoolCallbacks[next_id].func = func; - nptoolCallbacks[next_id].arg = arg; + const int next_id = std::distance(nptool_callbacks.begin(), it); + nptool_callbacks[next_id].func = func; + nptool_callbacks[next_id].arg = arg; return next_id; } -void Libraries::NetCtl::NetCtlInternal::checkCallback() { - std::unique_lock lock{m_mutex}; - const auto* linker = Common::Singleton::Instance(); - for (auto& callback : callbacks) { - if (callback.func != nullptr) { - linker->ExecuteGuest(callback.func, ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED, - callback.arg); +void NetCtlInternal::CheckCallback() { + std::scoped_lock lock{m_mutex}; + for (const auto [func, arg] : callbacks) { + if (func != nullptr) { + Core::ExecuteGuest(func, ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED, arg); } } } -void Libraries::NetCtl::NetCtlInternal::checkNpToolkitCallback() { - std::unique_lock lock{m_mutex}; - const auto* linker = Common::Singleton::Instance(); - for (auto& callback : nptoolCallbacks) { - if (callback.func != nullptr) { - linker->ExecuteGuest(callback.func, ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED, - callback.arg); +void NetCtlInternal::CheckNpToolkitCallback() { + std::scoped_lock lock{m_mutex}; + for (const auto [func, arg] : nptool_callbacks) { + if (func != nullptr) { + Core::ExecuteGuest(func, ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED, arg); } } } + +} // namespace Libraries::NetCtl diff --git a/src/core/libraries/network/net_ctl_obj.h b/src/core/libraries/network/net_ctl_obj.h index 3178677f4..cbbb33a4e 100644 --- a/src/core/libraries/network/net_ctl_obj.h +++ b/src/core/libraries/network/net_ctl_obj.h @@ -3,9 +3,7 @@ #pragma once -#include #include - #include "common/types.h" namespace Libraries::NetCtl { @@ -25,16 +23,17 @@ struct NetCtlCallbackForNpToolkit { class NetCtlInternal { public: - NetCtlInternal(); + explicit NetCtlInternal(); ~NetCtlInternal(); - s32 registerCallback(OrbisNetCtlCallback func, void* arg); - s32 registerNpToolkitCallback(OrbisNetCtlCallbackForNpToolkit func, void* arg); - void checkCallback(); - void checkNpToolkitCallback(); + + s32 RegisterCallback(OrbisNetCtlCallback func, void* arg); + s32 RegisterNpToolkitCallback(OrbisNetCtlCallbackForNpToolkit func, void* arg); + void CheckCallback(); + void CheckNpToolkitCallback(); public: - std::array nptoolCallbacks; - std::array callbacks; + std::array nptool_callbacks{}; + std::array callbacks{}; std::mutex m_mutex; }; } // namespace Libraries::NetCtl diff --git a/src/core/libraries/network/netctl.cpp b/src/core/libraries/network/netctl.cpp index d3f83c290..b167d2789 100644 --- a/src/core/libraries/network/netctl.cpp +++ b/src/core/libraries/network/netctl.cpp @@ -13,7 +13,6 @@ #endif #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_ctl_codes.h" @@ -21,6 +20,8 @@ namespace Libraries::NetCtl { +static NetCtlInternal netctl; + int PS4_SYSV_ABI sceNetBweCheckCallbackIpcInt() { LOG_ERROR(Lib_NetCtl, "(STUBBED) called"); return ORBIS_OK; @@ -92,8 +93,7 @@ int PS4_SYSV_ABI sceNetCtlUnregisterCallbackV6() { } int PS4_SYSV_ABI sceNetCtlCheckCallback() { - auto* netctl = Common::Singleton::Instance(); - netctl->checkCallback(); + netctl.CheckCallback(); return ORBIS_OK; } @@ -298,8 +298,7 @@ int PS4_SYSV_ABI sceNetCtlRegisterCallback(OrbisNetCtlCallback func, void* arg, if (!func || !cid) { return ORBIS_NET_CTL_ERROR_INVALID_ADDR; } - auto* netctl = Common::Singleton::Instance(); - s32 result = netctl->registerCallback(func, arg); + s32 result = netctl.RegisterCallback(func, arg); if (result < 0) { return result; } else { @@ -374,8 +373,7 @@ int PS4_SYSV_ABI Func_D8DCB6973537A3DC() { } int PS4_SYSV_ABI sceNetCtlCheckCallbackForNpToolkit() { - auto* netctl = Common::Singleton::Instance(); - netctl->checkNpToolkitCallback(); + netctl.CheckNpToolkitCallback(); return ORBIS_OK; } @@ -389,8 +387,7 @@ int PS4_SYSV_ABI sceNetCtlRegisterCallbackForNpToolkit(OrbisNetCtlCallbackForNpT if (!func || !cid) { return ORBIS_NET_CTL_ERROR_INVALID_ADDR; } - auto* netctl = Common::Singleton::Instance(); - s32 result = netctl->registerNpToolkitCallback(func, arg); + s32 result = netctl.RegisterNpToolkitCallback(func, arg); if (result < 0) { return result; } else { diff --git a/src/core/libraries/network/netctl.h b/src/core/libraries/network/netctl.h index 89ba34c31..4992fffa9 100644 --- a/src/core/libraries/network/netctl.h +++ b/src/core/libraries/network/netctl.h @@ -4,7 +4,7 @@ #pragma once #include "common/types.h" -#include "net_ctl_obj.h" +#include "core/libraries/network/net_ctl_obj.h" namespace Core::Loader { class SymbolsResolver; @@ -23,7 +23,7 @@ constexpr int ORBIS_NET_CTL_HOSTNAME_LEN = 255 + 1; constexpr int ORBIS_NET_CTL_AUTH_NAME_LEN = 127 + 1; constexpr int ORBIS_NET_CTL_IPV4_ADDR_STR_LEN = 16; -typedef union OrbisNetCtlInfo { +union OrbisNetCtlInfo { u32 device; OrbisNetEtherAddr ether_addr; u32 mtu; @@ -45,7 +45,7 @@ typedef union OrbisNetCtlInfo { u32 http_proxy_config; char http_proxy_server[ORBIS_NET_CTL_HOSTNAME_LEN]; u16 http_proxy_port; -} SceNetCtlInfo; +}; // GetInfo codes constexpr int ORBIS_NET_CTL_INFO_DEVICE = 1; diff --git a/src/core/libraries/ngs2/ngs2.cpp b/src/core/libraries/ngs2/ngs2.cpp index b66c9b15b..7eb663413 100644 --- a/src/core/libraries/ngs2/ngs2.cpp +++ b/src/core/libraries/ngs2/ngs2.cpp @@ -1,13 +1,12 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "ngs2.h" -#include "ngs2_error.h" -#include "ngs2_impl.h" - #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" +#include "core/libraries/ngs2/ngs2.h" +#include "core/libraries/ngs2/ngs2_error.h" +#include "core/libraries/ngs2/ngs2_impl.h" namespace Libraries::Ngs2 { @@ -416,4 +415,4 @@ void RegisterlibSceNgs2(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("AbYvTOZ8Pts", "libSceNgs2", 1, "libSceNgs2", 1, 1, sceNgs2VoiceRunCommands); }; -} // namespace Libraries::Ngs2 \ No newline at end of file +} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2.h b/src/core/libraries/ngs2/ngs2.h index 0df83f565..a5f1f52a6 100644 --- a/src/core/libraries/ngs2/ngs2.h +++ b/src/core/libraries/ngs2/ngs2.h @@ -3,10 +3,8 @@ #pragma once -#include "common/types.h" - #include -#include +#include "common/types.h" namespace Core::Loader { class SymbolsResolver; @@ -18,7 +16,9 @@ class Ngs2; using SceNgs2Handle = Ngs2*; -enum SceNgs2HandleType { SCE_NGS2_HANDLE_TYPE_SYSTEM = 0 }; +enum class SceNgs2HandleType : u32 { + System = 0, +}; struct Ngs2Handle { void* selfPointer; @@ -69,4 +69,5 @@ struct StackBuffer { }; void RegisterlibSceNgs2(Core::Loader::SymbolsResolver* sym); -} // namespace Libraries::Ngs2 \ No newline at end of file + +} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_impl.cpp b/src/core/libraries/ngs2/ngs2_impl.cpp index 793435d83..b358a05f7 100644 --- a/src/core/libraries/ngs2/ngs2_impl.cpp +++ b/src/core/libraries/ngs2/ngs2_impl.cpp @@ -6,7 +6,7 @@ #include "common/logging/log.h" #include "core/libraries/error_codes.h" -#include "core/libraries/kernel/libkernel.h" +#include "core/libraries/kernel/kernel.h" using namespace Libraries::Kernel; diff --git a/src/core/libraries/np_manager/np_manager.cpp b/src/core/libraries/np_manager/np_manager.cpp index 4fff59003..ec9cc6bf5 100644 --- a/src/core/libraries/np_manager/np_manager.cpp +++ b/src/core/libraries/np_manager/np_manager.cpp @@ -1,13 +1,12 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include -#include #include "common/config.h" #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" -#include "np_manager.h" +#include "core/libraries/np_manager/np_manager.h" +#include "core/tls.h" namespace Libraries::NpManager { @@ -972,12 +971,11 @@ int PS4_SYSV_ABI sceNpGetGamePresenceStatusA() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNpGetNpId(OrbisUserServiceUserId userId, OrbisNpId* npId) { - LOG_INFO(Lib_NpManager, "userId {}", userId); - std::string name = Config::getUserName(); - // Fill the unused stuffs to 0 - memset(npId, 0, sizeof(*npId)); - strcpy(npId->handle.data, name.c_str()); +int PS4_SYSV_ABI sceNpGetNpId(OrbisUserServiceUserId user_id, OrbisNpId* np_id) { + LOG_INFO(Lib_NpManager, "user_id {}", user_id); + const auto name = Config::getUserName(); + std::memset(np_id, 0, sizeof(OrbisNpId)); + name.copy(np_id->handle.data, sizeof(np_id->handle.data)); return ORBIS_OK; } @@ -986,12 +984,11 @@ int PS4_SYSV_ABI sceNpGetNpReachabilityState() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNpGetOnlineId(s32 userId, OrbisNpOnlineId* onlineId) { - LOG_DEBUG(Lib_NpManager, "userId {}", userId); - std::string name = Config::getUserName(); - // Fill the unused stuffs to 0 - memset(onlineId, 0, sizeof(*onlineId)); - strcpy(onlineId->data, name.c_str()); +int PS4_SYSV_ABI sceNpGetOnlineId(s32 user_id, OrbisNpOnlineId* online_id) { + LOG_DEBUG(Lib_NpManager, "user_id {}", user_id); + const auto name = Config::getUserName(); + std::memset(online_id, 0, sizeof(OrbisNpOnlineId)); + name.copy(online_id->data, sizeof(online_id->data)); return ORBIS_OK; } @@ -1006,7 +1003,7 @@ int PS4_SYSV_ABI sceNpGetParentalControlInfoA() { } int PS4_SYSV_ABI sceNpGetState(s32 userId, OrbisNpState* state) { - *state = ORBIS_NP_STATE_SIGNED_OUT; + *state = OrbisNpState::SignedOut; LOG_DEBUG(Lib_NpManager, "Signed out"); return ORBIS_OK; } @@ -2519,10 +2516,7 @@ struct NpStateCallbackForNpToolkit { NpStateCallbackForNpToolkit NpStateCbForNp; int PS4_SYSV_ABI sceNpCheckCallbackForLib() { - // LOG_ERROR(Lib_NpManager, "(STUBBED) called"); - const auto* linker = Common::Singleton::Instance(); - linker->ExecuteGuest(NpStateCbForNp.func, 1, ORBIS_NP_STATE_SIGNED_OUT, - NpStateCbForNp.userdata); + Core::ExecuteGuest(NpStateCbForNp.func, 1, OrbisNpState::SignedOut, NpStateCbForNp.userdata); return ORBIS_OK; } diff --git a/src/core/libraries/np_manager/np_manager.h b/src/core/libraries/np_manager/np_manager.h index 7e906cdc8..6ba588e5e 100644 --- a/src/core/libraries/np_manager/np_manager.h +++ b/src/core/libraries/np_manager/np_manager.h @@ -13,18 +13,14 @@ namespace Libraries::NpManager { constexpr int ORBIS_NP_ERROR_SIGNED_OUT = 0x80550006; -enum OrbisNpState { - ORBIS_NP_STATE_UNKNOWN = 0, - ORBIS_NP_STATE_SIGNED_OUT, - ORBIS_NP_STATE_SIGNED_IN -}; +enum class OrbisNpState : u32 { Unknown = 0, SignedOut, SignedIn }; using OrbisNpStateCallbackForNpToolkit = PS4_SYSV_ABI void (*)(s32 userId, OrbisNpState state, void* userdata); constexpr int ORBIS_NP_ONLINEID_MAX_LENGTH = 16; -typedef int OrbisUserServiceUserId; +using OrbisUserServiceUserId = s32; struct OrbisNpOnlineId { char data[ORBIS_NP_ONLINEID_MAX_LENGTH]; @@ -542,4 +538,4 @@ int PS4_SYSV_ABI sceNpRegisterStateCallbackForToolkit(OrbisNpStateCallbackForNpT int PS4_SYSV_ABI sceNpUnregisterStateCallbackForToolkit(); void RegisterlibSceNpManager(Core::Loader::SymbolsResolver* sym); -} // namespace Libraries::NpManager \ No newline at end of file +} // namespace Libraries::NpManager diff --git a/src/core/libraries/np_trophy/np_trophy.cpp b/src/core/libraries/np_trophy/np_trophy.cpp index e8fd57ef1..9324ed6bb 100644 --- a/src/core/libraries/np_trophy/np_trophy.cpp +++ b/src/core/libraries/np_trophy/np_trophy.cpp @@ -7,10 +7,10 @@ #include "common/logging/log.h" #include "common/path_util.h" #include "common/slot_vector.h" -#include "core/libraries/error_codes.h" #include "core/libraries/libs.h" -#include "np_trophy.h" -#include "trophy_ui.h" +#include "core/libraries/np_trophy/np_trophy.h" +#include "core/libraries/np_trophy/np_trophy_error.h" +#include "core/libraries/np_trophy/trophy_ui.h" namespace Libraries::NpTrophy { diff --git a/src/core/libraries/np_trophy/np_trophy.h b/src/core/libraries/np_trophy/np_trophy.h index ac13a9ab7..9abc795bc 100644 --- a/src/core/libraries/np_trophy/np_trophy.h +++ b/src/core/libraries/np_trophy/np_trophy.h @@ -29,14 +29,14 @@ constexpr int ORBIS_NP_TROPHY_INVALID_HANDLE = -1; constexpr int ORBIS_NP_TROPHY_INVALID_CONTEXT = -1; constexpr int ORBIS_NP_TROPHY_INVALID_TROPHY_ID = -1; -typedef int32_t OrbisNpTrophyHandle; -typedef int32_t OrbisNpTrophyContext; -typedef int32_t OrbisNpTrophyId; -typedef uint32_t OrbisNpTrophyFlagMask; +using OrbisNpTrophyHandle = s32; +using OrbisNpTrophyContext = s32; +using OrbisNpTrophyId = s32; +using OrbisNpTrophyFlagMask = u32; struct OrbisNpTrophyFlagArray { - OrbisNpTrophyFlagMask - flag_bits[ORBIS_NP_TROPHY_FLAG_SETSIZE >> ORBIS_NP_TROPHY_FLAG_BITS_SHIFT]; + static constexpr int NumMasks = ORBIS_NP_TROPHY_FLAG_SETSIZE >> ORBIS_NP_TROPHY_FLAG_BITS_SHIFT; + std::array flag_bits; }; void ORBIS_NP_TROPHY_FLAG_ZERO(OrbisNpTrophyFlagArray* p); @@ -49,18 +49,18 @@ struct OrbisNpTrophyData { size_t size; OrbisNpTrophyId trophy_id; bool unlocked; - uint8_t reserved[3]; + u8 reserved[3]; Rtc::OrbisRtcTick timestamp; }; -typedef int32_t OrbisNpTrophyGrade; +using OrbisNpTrophyGrade = s32; constexpr int ORBIS_NP_TROPHY_GRADE_UNKNOWN = 0; constexpr int ORBIS_NP_TROPHY_GRADE_PLATINUM = 1; constexpr int ORBIS_NP_TROPHY_GRADE_GOLD = 2; constexpr int ORBIS_NP_TROPHY_GRADE_SILVER = 3; constexpr int ORBIS_NP_TROPHY_GRADE_BRONZE = 4; -typedef int32_t OrbisNpTrophyGroupId; +using OrbisNpTrophyGroupId = s32; constexpr int ORBIS_NP_TROPHY_BASE_GAME_GROUP_ID = -1; constexpr int ORBIS_NP_TROPHY_INVALID_GROUP_ID = -2; @@ -70,29 +70,29 @@ struct OrbisNpTrophyDetails { OrbisNpTrophyGrade trophy_grade; OrbisNpTrophyGroupId group_id; bool hidden; - uint8_t reserved[3]; + u8 reserved[3]; char name[ORBIS_NP_TROPHY_NAME_MAX_SIZE]; char description[ORBIS_NP_TROPHY_DESCR_MAX_SIZE]; }; struct OrbisNpTrophyGameData { size_t size; - uint32_t unlocked_trophies; - uint32_t unlocked_platinum; - uint32_t unlocked_gold; - uint32_t unlocked_silver; - uint32_t unlocked_bronze; - uint32_t progress_percentage; + u32 unlocked_trophies; + u32 unlocked_platinum; + u32 unlocked_gold; + u32 unlocked_silver; + u32 unlocked_bronze; + u32 progress_percentage; }; struct OrbisNpTrophyGameDetails { size_t size; - uint32_t num_groups; - uint32_t num_trophies; - uint32_t num_platinum; - uint32_t num_gold; - uint32_t num_silver; - uint32_t num_bronze; + u32 num_groups; + u32 num_trophies; + u32 num_platinum; + u32 num_gold; + u32 num_silver; + u32 num_bronze; char title[ORBIS_NP_TROPHY_GAME_TITLE_MAX_SIZE]; char description[ORBIS_NP_TROPHY_GAME_DESCR_MAX_SIZE]; }; @@ -100,23 +100,23 @@ struct OrbisNpTrophyGameDetails { struct OrbisNpTrophyGroupData { size_t size; OrbisNpTrophyGroupId group_id; - uint32_t unlocked_trophies; - uint32_t unlocked_platinum; - uint32_t unlocked_gold; - uint32_t unlocked_silver; - uint32_t unlocked_bronze; - uint32_t progress_percentage; + u32 unlocked_trophies; + u32 unlocked_platinum; + u32 unlocked_gold; + u32 unlocked_silver; + u32 unlocked_bronze; + u32 progress_percentage; uint8_t reserved[4]; }; struct OrbisNpTrophyGroupDetails { size_t size; OrbisNpTrophyGroupId group_id; - uint32_t num_trophies; - uint32_t num_platinum; - uint32_t num_gold; - uint32_t num_silver; - uint32_t num_bronze; + u32 num_trophies; + u32 num_platinum; + u32 num_gold; + u32 num_silver; + u32 num_bronze; char title[ORBIS_NP_TROPHY_GROUP_TITLE_MAX_SIZE]; char description[ORBIS_NP_TROPHY_GROUP_DESCR_MAX_SIZE]; }; @@ -133,7 +133,7 @@ int PS4_SYSV_ABI sceNpTrophyConfigGetTrophySetVersion(); int PS4_SYSV_ABI sceNpTrophyConfigGetTrophyTitleDetails(); int PS4_SYSV_ABI sceNpTrophyConfigHasGroupFeature(); s32 PS4_SYSV_ABI sceNpTrophyCreateContext(OrbisNpTrophyContext* context, int32_t user_id, - uint32_t service_label, uint64_t options); + u32 service_label, uint64_t options); s32 PS4_SYSV_ABI sceNpTrophyCreateHandle(OrbisNpTrophyHandle* handle); int PS4_SYSV_ABI sceNpTrophyDestroyContext(OrbisNpTrophyContext context); s32 PS4_SYSV_ABI sceNpTrophyDestroyHandle(OrbisNpTrophyHandle handle); diff --git a/src/core/libraries/np_trophy/np_trophy_error.h b/src/core/libraries/np_trophy/np_trophy_error.h new file mode 100644 index 000000000..9506d9ca4 --- /dev/null +++ b/src/core/libraries/np_trophy/np_trophy_error.h @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// NpTrophy library +constexpr int ORBIS_NP_TROPHY_ERROR_UNKNOWN = 0x80551600; +constexpr int ORBIS_NP_TROPHY_ERROR_NOT_INITIALIZED = 0x80551601; +constexpr int ORBIS_NP_TROPHY_ERROR_ALREADY_INITIALIZED = 0x80551602; +constexpr int ORBIS_NP_TROPHY_ERROR_OUT_OF_MEMORY = 0x80551603; +constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT = 0x80551604; +constexpr int ORBIS_NP_TROPHY_ERROR_INSUFFICIENT_BUFFER = 0x80551605; +constexpr int ORBIS_NP_TROPHY_ERROR_EXCEEDS_MAX = 0x80551606; +constexpr int ORBIS_NP_TROPHY_ERROR_ABORT = 0x80551607; +constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE = 0x80551608; +constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT = 0x80551609; +constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_TROPHY_ID = 0x8055160A; +constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_GROUP_ID = 0x8055160B; +constexpr int ORBIS_NP_TROPHY_ERROR_TROPHY_ALREADY_UNLOCKED = 0x8055160C; +constexpr int ORBIS_NP_TROPHY_ERROR_PLATINUM_CANNOT_UNLOCK = 0x8055160D; +constexpr int ORBIS_NP_TROPHY_ERROR_ACCOUNTID_NOT_MATCH = 0x8055160E; +constexpr int ORBIS_NP_TROPHY_ERROR_NOT_REGISTERED = 0x8055160F; +constexpr int ORBIS_NP_TROPHY_ERROR_ALREADY_REGISTERED = 0x80551610; +constexpr int ORBIS_NP_TROPHY_ERROR_BROKEN_DATA = 0x80551611; +constexpr int ORBIS_NP_TROPHY_ERROR_INSUFFICIENT_SPACE = 0x80551612; +constexpr int ORBIS_NP_TROPHY_ERROR_CONTEXT_ALREADY_EXISTS = 0x80551613; +constexpr int ORBIS_NP_TROPHY_ERROR_ICON_FILE_NOT_FOUND = 0x80551614; +constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_TRP_FILE_FORMAT = 0x80551616; +constexpr int ORBIS_NP_TROPHY_ERROR_UNSUPPORTED_TRP_FILE = 0x80551617; +constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_TROPHY_CONF_FORMAT = 0x80551618; +constexpr int ORBIS_NP_TROPHY_ERROR_UNSUPPORTED_TROPHY_CONF = 0x80551619; +constexpr int ORBIS_NP_TROPHY_ERROR_TROPHY_NOT_UNLOCKED = 0x8055161A; +constexpr int ORBIS_NP_TROPHY_ERROR_USER_NOT_FOUND = 0x8055161C; +constexpr int ORBIS_NP_TROPHY_ERROR_USER_NOT_LOGGED_IN = 0x8055161D; +constexpr int ORBIS_NP_TROPHY_ERROR_CONTEXT_USER_LOGOUT = 0x8055161E; +constexpr int ORBIS_NP_TROPHY_ERROR_USE_TRP_FOR_DEVELOPMENT = 0x8055161F; +constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_NP_SERVICE_LABEL = 0x80551621; +constexpr int ORBIS_NP_TROPHY_ERROR_NOT_SUPPORTED = 0x80551622; +constexpr int ORBIS_NP_TROPHY_ERROR_CONTEXT_EXCEEDS_MAX = 0x80551623; +constexpr int ORBIS_NP_TROPHY_ERROR_HANDLE_EXCEEDS_MAX = 0x80551624; +constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_USER_ID = 0x80551625; +constexpr int ORBIS_NP_TROPHY_ERROR_TITLE_CONF_NOT_INSTALLED = 0x80551626; +constexpr int ORBIS_NP_TROPHY_ERROR_BROKEN_TITLE_CONF = 0x80551627; +constexpr int ORBIS_NP_TROPHY_ERROR_INCONSISTENT_TITLE_CONF = 0x80551628; +constexpr int ORBIS_NP_TROPHY_ERROR_TITLE_BACKGROUND = 0x80551629; +constexpr int ORBIS_NP_TROPHY_ERROR_SCREENSHOT_DISABLED = 0x8055162B; +constexpr int ORBIS_NP_TROPHY_ERROR_SCREENSHOT_DISPLAY_BUFFER_NOT_IN_USE = 0x8055162D; diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index d786647c2..ec4186f11 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -1,12 +1,11 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "common/assert.h" #include "common/config.h" #include "common/logging/log.h" #include "common/singleton.h" -#include "core/libraries/error_codes.h" #include "core/libraries/libs.h" +#include "core/libraries/pad/pad_errors.h" #include "input/controller.h" #include "pad.h" @@ -25,6 +24,7 @@ int PS4_SYSV_ABI scePadConnectPort() { int PS4_SYSV_ABI scePadDeviceClassGetExtendedInformation( s32 handle, OrbisPadDeviceClassExtendedInformation* pExtInfo) { LOG_ERROR(Lib_Pad, "(STUBBED) called"); + std::memset(pExtInfo, 0, sizeof(OrbisPadDeviceClassExtendedInformation)); if (Config::getUseSpecialPad()) { pExtInfo->deviceClass = (OrbisPadDeviceClass)Config::getSpecialPadClass(); } @@ -98,8 +98,8 @@ int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerIn pInfo->connectionType = ORBIS_PAD_PORT_TYPE_STANDARD; pInfo->connectedCount = 1; pInfo->connected = false; - pInfo->deviceClass = ORBIS_PAD_DEVICE_CLASS_STANDARD; - return SCE_OK; + pInfo->deviceClass = OrbisPadDeviceClass::Standard; + return ORBIS_OK; } pInfo->touchPadInfo.pixelDensity = 1; pInfo->touchPadInfo.resolution.x = 1920; @@ -109,12 +109,12 @@ int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerIn pInfo->connectionType = ORBIS_PAD_PORT_TYPE_STANDARD; pInfo->connectedCount = 1; pInfo->connected = true; - pInfo->deviceClass = ORBIS_PAD_DEVICE_CLASS_STANDARD; + pInfo->deviceClass = OrbisPadDeviceClass::Standard; if (Config::getUseSpecialPad()) { pInfo->connectionType = ORBIS_PAD_PORT_TYPE_SPECIAL; pInfo->deviceClass = (OrbisPadDeviceClass)Config::getSpecialPadClass(); } - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI scePadGetDataInternal() { @@ -381,7 +381,7 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) { pData->connectedCount = 1; // connectedCount; pData->deviceUniqueDataLen = 0; - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI scePadReadStateExt() { diff --git a/src/core/libraries/pad/pad.h b/src/core/libraries/pad/pad.h index f94a642cf..68943b460 100644 --- a/src/core/libraries/pad/pad.h +++ b/src/core/libraries/pad/pad.h @@ -3,6 +3,7 @@ #pragma once +#include "common/enum.h" #include "common/types.h" namespace Core::Loader { @@ -18,18 +19,18 @@ constexpr int ORBIS_PAD_PORT_TYPE_STANDARD = 0; constexpr int ORBIS_PAD_PORT_TYPE_SPECIAL = 2; constexpr int ORBIS_PAD_PORT_TYPE_REMOTE_CONTROL = 16; -enum OrbisPadDeviceClass { - ORBIS_PAD_DEVICE_CLASS_INVALID = -1, - ORBIS_PAD_DEVICE_CLASS_STANDARD = 0, - ORBIS_PAD_DEVICE_CLASS_GUITAR = 1, - ORBIS_PAD_DEVICE_CLASS_DRUM = 2, - ORBIS_PAD_DEVICE_CLASS_DJ_TURNTABLE = 3, - ORBIS_PAD_DEVICE_CLASS_DANCEMAT = 4, - ORBIS_PAD_DEVICE_CLASS_NAVIGATION = 5, - ORBIS_PAD_DEVICE_CLASS_STEERING_WHEEL = 6, - ORBIS_PAD_DEVICE_CLASS_STICK = 7, - ORBIS_PAD_DEVICE_CLASS_FLIGHT_STICK = 8, - ORBIS_PAD_DEVICE_CLASS_GUN = 9, +enum class OrbisPadDeviceClass { + Invalid = -1, + Standard = 0, + Guitar = 1, + Drum = 2, + DjTurntable = 3, + Dancemat = 4, + Navigation = 5, + SteeringWheel = 6, + Stick = 7, + FightStick = 8, + Gun = 9, }; struct OrbisPadDeviceClassExtendedInformation { @@ -123,25 +124,27 @@ struct OrbisPadAnalogStick { u8 y; }; -enum OrbisPadButtonDataOffset { - ORBIS_PAD_BUTTON_L3 = 0x00000002, - ORBIS_PAD_BUTTON_R3 = 0x00000004, - ORBIS_PAD_BUTTON_OPTIONS = 0x00000008, - ORBIS_PAD_BUTTON_UP = 0x00000010, - ORBIS_PAD_BUTTON_RIGHT = 0x00000020, - ORBIS_PAD_BUTTON_DOWN = 0x00000040, - ORBIS_PAD_BUTTON_LEFT = 0x00000080, - ORBIS_PAD_BUTTON_L2 = 0x00000100, - ORBIS_PAD_BUTTON_R2 = 0x00000200, - ORBIS_PAD_BUTTON_L1 = 0x00000400, - ORBIS_PAD_BUTTON_R1 = 0x00000800, - ORBIS_PAD_BUTTON_TRIANGLE = 0x00001000, - ORBIS_PAD_BUTTON_CIRCLE = 0x00002000, - ORBIS_PAD_BUTTON_CROSS = 0x00004000, - ORBIS_PAD_BUTTON_SQUARE = 0x00008000, - ORBIS_PAD_BUTTON_TOUCH_PAD = 0x00100000, - ORBIS_PAD_BUTTON_INTERCEPTED = 0x80000000, +enum class OrbisPadButtonDataOffset : u32 { + None = 0, + L3 = 0x2, + R3 = 0x4, + Options = 0x8, + Up = 0x10, + Right = 0x20, + Down = 0x40, + Left = 0x80, + L2 = 0x100, + R2 = 0x200, + L1 = 0x400, + R1 = 0x800, + Triangle = 0x1000, + Circle = 0x2000, + Cross = 0x4000, + Square = 0x8000, + TouchPad = 0x100000, + Intercepted = 0x80000000, }; +DECLARE_ENUM_FLAG_OPERATORS(OrbisPadButtonDataOffset) struct OrbisFQuaternion { float x, y, z, w; @@ -173,7 +176,7 @@ struct OrbisPadExtensionUnitData { }; struct OrbisPadData { - u32 buttons; + OrbisPadButtonDataOffset buttons; OrbisPadAnalogStick leftStick; OrbisPadAnalogStick rightStick; OrbisPadAnalogButtons analogButtons; @@ -346,4 +349,4 @@ int PS4_SYSV_ABI Func_89C9237E393DA243(); int PS4_SYSV_ABI Func_EF103E845B6F0420(); void RegisterlibScePad(Core::Loader::SymbolsResolver* sym); -} // namespace Libraries::Pad \ No newline at end of file +} // namespace Libraries::Pad diff --git a/src/core/libraries/pad/pad_errors.h b/src/core/libraries/pad/pad_errors.h new file mode 100644 index 000000000..182c89219 --- /dev/null +++ b/src/core/libraries/pad/pad_errors.h @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// Pad library +constexpr int ORBIS_PAD_ERROR_INVALID_ARG = 0x80920001; +constexpr int ORBIS_PAD_ERROR_INVALID_PORT = 0x80920002; +constexpr int ORBIS_PAD_ERROR_INVALID_HANDLE = 0x80920003; +constexpr int ORBIS_PAD_ERROR_ALREADY_OPENED = 0x80920004; +constexpr int ORBIS_PAD_ERROR_NOT_INITIALIZED = 0x80920005; +constexpr int ORBIS_PAD_ERROR_INVALID_LIGHTBAR_SETTING = 0x80920006; +constexpr int ORBIS_PAD_ERROR_DEVICE_NOT_CONNECTED = 0x80920007; +constexpr int ORBIS_PAD_ERROR_DEVICE_NO_HANDLE = 0x80920008; +constexpr int ORBIS_PAD_ERROR_FATAL = 0x809200FF; +constexpr int ORBIS_PAD_ERROR_NOT_PERMITTED = 0x80920101; +constexpr int ORBIS_PAD_ERROR_INVALID_BUFFER_LENGTH = 0x80920102; +constexpr int ORBIS_PAD_ERROR_INVALID_REPORT_LENGTH = 0x80920103; +constexpr int ORBIS_PAD_ERROR_INVALID_REPORT_ID = 0x80920104; +constexpr int ORBIS_PAD_ERROR_SEND_AGAIN = 0x80920105; diff --git a/src/core/libraries/playgo/playgo.cpp b/src/core/libraries/playgo/playgo.cpp index d4f5c6b7c..13d6f991f 100644 --- a/src/core/libraries/playgo/playgo.cpp +++ b/src/core/libraries/playgo/playgo.cpp @@ -4,6 +4,7 @@ #include "common/logging/log.h" #include "common/singleton.h" #include "core/file_format/playgo_chunk.h" +#include "core/file_sys/fs.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "core/libraries/system/systemservice.h" @@ -11,6 +12,9 @@ namespace Libraries::PlayGo { +static constexpr OrbisPlayGoHandle PlaygoHandle = 1; +static std::unique_ptr playgo; + s32 PS4_SYSV_ABI sceDbgPlayGoRequestNextChunk() { LOG_ERROR(Lib_PlayGo, "(STUBBED)called"); return ORBIS_OK; @@ -24,57 +28,62 @@ s32 PS4_SYSV_ABI sceDbgPlayGoSnapshot() { s32 PS4_SYSV_ABI scePlayGoClose(OrbisPlayGoHandle handle) { LOG_INFO(Lib_PlayGo, "called"); - auto* playgo = Common::Singleton::Instance(); - - if (handle != 1) + if (handle != PlaygoHandle) { return ORBIS_PLAYGO_ERROR_BAD_HANDLE; - if (!playgo->initialized) + } + if (!playgo) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; + } return ORBIS_OK; } s32 PS4_SYSV_ABI scePlayGoGetChunkId(OrbisPlayGoHandle handle, OrbisPlayGoChunkId* outChunkIdList, u32 numberOfEntries, u32* outEntries) { LOG_INFO(Lib_PlayGo, "called"); - auto* playgo = Common::Singleton::Instance(); - if (handle != 1) + if (handle != PlaygoHandle) { return ORBIS_PLAYGO_ERROR_BAD_HANDLE; - if (outEntries == nullptr) + } + if (outEntries == nullptr) { return ORBIS_PLAYGO_ERROR_BAD_POINTER; - if (outChunkIdList != nullptr && numberOfEntries == 0) + } + if (outChunkIdList != nullptr && numberOfEntries == 0) { return ORBIS_PLAYGO_ERROR_BAD_SIZE; + } if (playgo->GetPlaygoHeader().file_size == 0) { *outEntries = 0; - } else { - if (outChunkIdList == nullptr) { - *outEntries = playgo->chunks.size(); - } else { - if (numberOfEntries > playgo->chunks.size()) { - numberOfEntries = playgo->chunks.size(); - } - - if (numberOfEntries != 0) { - for (u32 i = 0; i < numberOfEntries; i++) { - outChunkIdList[i] = i; - } - *outEntries = numberOfEntries; - } - } + return ORBIS_OK; } + + if (outChunkIdList == nullptr) { + *outEntries = playgo->chunks.size(); + return ORBIS_OK; + } + + if (numberOfEntries > playgo->chunks.size()) { + numberOfEntries = playgo->chunks.size(); + } + + for (u32 i = 0; i < numberOfEntries; i++) { + outChunkIdList[i] = i; + } + *outEntries = numberOfEntries; return ORBIS_OK; } s32 PS4_SYSV_ABI scePlayGoGetEta(OrbisPlayGoHandle handle, const OrbisPlayGoChunkId* chunkIds, u32 numberOfEntries, OrbisPlayGoEta* outEta) { LOG_INFO(Lib_PlayGo, "called"); - if (handle != 1) + if (handle != PlaygoHandle) { return ORBIS_PLAYGO_ERROR_BAD_HANDLE; - if (chunkIds == nullptr || outEta == nullptr) + } + if (chunkIds == nullptr || outEta == nullptr) { return ORBIS_PLAYGO_ERROR_BAD_POINTER; - if (numberOfEntries == 0) + } + if (numberOfEntries == 0) { return ORBIS_PLAYGO_ERROR_BAD_SIZE; + } *outEta = 0; // all is loaded return ORBIS_OK; @@ -84,22 +93,23 @@ s32 PS4_SYSV_ABI scePlayGoGetInstallSpeed(OrbisPlayGoHandle handle, OrbisPlayGoInstallSpeed* outSpeed) { LOG_INFO(Lib_PlayGo, "called"); - auto* playgo = Common::Singleton::Instance(); - - if (handle != 1) + if (handle != PlaygoHandle) { return ORBIS_PLAYGO_ERROR_BAD_HANDLE; - if (outSpeed == nullptr) + } + if (outSpeed == nullptr) { return ORBIS_PLAYGO_ERROR_BAD_POINTER; - if (!playgo->initialized) + } + if (!playgo) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; + } std::scoped_lock lk{playgo->GetSpeedMutex()}; - if (playgo->speed == 0) { + if (playgo->speed == OrbisPlayGoInstallSpeed::Suspended) { using namespace std::chrono; if ((duration_cast(steady_clock::now().time_since_epoch()).count() - playgo->speed_tick) > 30 * 1000) { // 30sec - playgo->speed = ORBIS_PLAYGO_INSTALL_SPEED_TRICKLE; + playgo->speed = OrbisPlayGoInstallSpeed::Trickle; } } *outSpeed = playgo->speed; @@ -111,14 +121,15 @@ s32 PS4_SYSV_ABI scePlayGoGetLanguageMask(OrbisPlayGoHandle handle, OrbisPlayGoLanguageMask* outLanguageMask) { LOG_INFO(Lib_PlayGo, "called"); - auto* playgo = Common::Singleton::Instance(); - - if (handle != 1) + if (handle != PlaygoHandle) { return ORBIS_PLAYGO_ERROR_BAD_HANDLE; - if (outLanguageMask == nullptr) + } + if (outLanguageMask == nullptr) { return ORBIS_PLAYGO_ERROR_BAD_POINTER; - if (!playgo->initialized) + } + if (!playgo) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; + } *outLanguageMask = playgo->langMask; return ORBIS_OK; @@ -129,24 +140,27 @@ s32 PS4_SYSV_ABI scePlayGoGetLocus(OrbisPlayGoHandle handle, const OrbisPlayGoCh LOG_INFO(Lib_PlayGo, "called handle = {}, chunkIds = {}, numberOfEntries = {}", handle, *chunkIds, numberOfEntries); - auto* playgo = Common::Singleton::Instance(); - - if (handle != 1) + if (handle != PlaygoHandle) { return ORBIS_PLAYGO_ERROR_BAD_HANDLE; - if (chunkIds == nullptr || outLoci == nullptr) + } + if (chunkIds == nullptr || outLoci == nullptr) { return ORBIS_PLAYGO_ERROR_BAD_POINTER; - if (numberOfEntries == 0) + } + if (numberOfEntries == 0) { return ORBIS_PLAYGO_ERROR_BAD_SIZE; - if (!playgo->initialized) + } + if (!playgo) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; - if (playgo->GetPlaygoHeader().file_size == 0) + } + if (playgo->GetPlaygoHeader().file_size == 0) { return ORBIS_PLAYGO_ERROR_NOT_SUPPORT_PLAYGO; + } - for (uint32_t i = 0; i < numberOfEntries; i++) { + for (int i = 0; i < numberOfEntries; i++) { if (chunkIds[i] <= playgo->chunks.size()) { - outLoci[i] = OrbisPlayGoLocusValue::ORBIS_PLAYGO_LOCUS_LOCAL_FAST; + outLoci[i] = OrbisPlayGoLocus::LocalFast; } else { - outLoci[i] = ORBIS_PLAYGO_LOCUS_NOT_DOWNLOADED; + outLoci[i] = OrbisPlayGoLocus::NotDownloaded; return ORBIS_PLAYGO_ERROR_BAD_CHUNK_ID; } } @@ -158,18 +172,21 @@ s32 PS4_SYSV_ABI scePlayGoGetProgress(OrbisPlayGoHandle handle, const OrbisPlayG LOG_INFO(Lib_PlayGo, "called handle = {}, chunkIds = {}, numberOfEntries = {}", handle, *chunkIds, numberOfEntries); - auto* playgo = Common::Singleton::Instance(); - - if (handle != 1) + if (handle != PlaygoHandle) { return ORBIS_PLAYGO_ERROR_BAD_HANDLE; - if (chunkIds == nullptr || outProgress == nullptr) + } + if (chunkIds == nullptr || outProgress == nullptr) { return ORBIS_PLAYGO_ERROR_BAD_POINTER; - if (numberOfEntries == 0) + } + if (numberOfEntries == 0) { return ORBIS_PLAYGO_ERROR_BAD_SIZE; - if (!playgo->initialized) + } + if (!playgo) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; - if (playgo->GetPlaygoHeader().file_size == 0) + } + if (playgo->GetPlaygoHeader().file_size == 0) { return ORBIS_PLAYGO_ERROR_BAD_CHUNK_ID; + } outProgress->progressSize = 0; outProgress->totalSize = 0; @@ -194,16 +211,18 @@ s32 PS4_SYSV_ABI scePlayGoGetToDoList(OrbisPlayGoHandle handle, OrbisPlayGoToDo* u32 numberOfEntries, u32* outEntries) { LOG_INFO(Lib_PlayGo, "called handle = {} numberOfEntries = {}", handle, numberOfEntries); - auto* playgo = Common::Singleton::Instance(); - - if (handle != 1) + if (handle != PlaygoHandle) { return ORBIS_PLAYGO_ERROR_BAD_HANDLE; - if (outTodoList == nullptr || outEntries == nullptr) + } + if (outTodoList == nullptr || outEntries == nullptr) { return ORBIS_PLAYGO_ERROR_BAD_POINTER; - if (numberOfEntries == 0) + } + if (numberOfEntries == 0) { return ORBIS_PLAYGO_ERROR_BAD_SIZE; - if (!playgo->initialized) + } + if (!playgo) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; + } *outEntries = 0; // nothing to do return ORBIS_OK; } @@ -218,41 +237,50 @@ int scePlayGoConvertLanguage(int systemLang) { s32 PS4_SYSV_ABI scePlayGoInitialize(OrbisPlayGoInitParams* param) { LOG_INFO(Lib_PlayGo, "called, bufSize = {}", param->bufSize); - if (param->bufAddr == nullptr) + if (param->bufAddr == nullptr) { return ORBIS_PLAYGO_ERROR_BAD_POINTER; - if (param->bufSize < 0x200000) + } + if (param->bufSize < 0x200000) { return ORBIS_PLAYGO_ERROR_BAD_SIZE; - - auto* playgo = Common::Singleton::Instance(); - - if (!playgo->initialized) { - using namespace SystemService; - // get system lang - int systemLang = 0; - sceSystemServiceParamGetInt(ORBIS_SYSTEM_SERVICE_PARAM_ID_LANG, &systemLang); - playgo->langMask = scePlayGoConvertLanguage(systemLang); - playgo->initialized = true; - } else { + } + if (playgo) { return ORBIS_PLAYGO_ERROR_ALREADY_INITIALIZED; } + + using namespace SystemService; + + playgo = std::make_unique(); + + auto* mnt = Common::Singleton::Instance(); + const auto file_path = mnt->GetHostPath("/app0/sce_sys/playgo-chunk.dat"); + if (!playgo->Open(file_path)) { + LOG_WARNING(Lib_PlayGo, "Could not open PlayGo file"); + } + + s32 system_lang = 0; + sceSystemServiceParamGetInt(OrbisSystemServiceParamId::Lang, &system_lang); + playgo->langMask = scePlayGoConvertLanguage(system_lang); + return ORBIS_OK; } s32 PS4_SYSV_ABI scePlayGoOpen(OrbisPlayGoHandle* outHandle, const void* param) { LOG_INFO(Lib_PlayGo, "called"); - auto* playgo = Common::Singleton::Instance(); - - if (outHandle == nullptr) + if (outHandle == nullptr) { return ORBIS_PLAYGO_ERROR_BAD_POINTER; - if (param) + } + if (param) { return ORBIS_PLAYGO_ERROR_INVALID_ARGUMENT; - if (!playgo->initialized) + } + if (!playgo) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; - if (playgo->GetPlaygoHeader().file_size == 0) + } + if (playgo->GetPlaygoHeader().file_size == 0) { return ORBIS_PLAYGO_ERROR_NOT_SUPPORT_PLAYGO; + } - playgo->handle = *outHandle = 1; + playgo->handle = *outHandle = PlaygoHandle; return ORBIS_OK; } @@ -260,21 +288,23 @@ s32 PS4_SYSV_ABI scePlayGoPrefetch(OrbisPlayGoHandle handle, const OrbisPlayGoCh u32 numberOfEntries, OrbisPlayGoLocus minimumLocus) { LOG_INFO(Lib_PlayGo, "called"); - auto* playgo = Common::Singleton::Instance(); - - if (handle != 1) + if (handle != PlaygoHandle) { return ORBIS_PLAYGO_ERROR_BAD_HANDLE; - if (chunkIds == nullptr) + } + if (chunkIds == nullptr) { return ORBIS_PLAYGO_ERROR_BAD_POINTER; - if (numberOfEntries == 0) + } + if (numberOfEntries == 0) { return ORBIS_PLAYGO_ERROR_BAD_SIZE; - if (!playgo->initialized) + } + if (!playgo) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; + } switch (minimumLocus) { - case ORBIS_PLAYGO_LOCUS_NOT_DOWNLOADED: - case ORBIS_PLAYGO_LOCUS_LOCAL_SLOW: - case ORBIS_PLAYGO_LOCUS_LOCAL_FAST: + case OrbisPlayGoLocus::NotDownloaded: + case OrbisPlayGoLocus::LocalSlow: + case OrbisPlayGoLocus::LocalFast: break; default: return ORBIS_PLAYGO_ERROR_BAD_LOCUS; @@ -285,24 +315,23 @@ s32 PS4_SYSV_ABI scePlayGoPrefetch(OrbisPlayGoHandle handle, const OrbisPlayGoCh s32 PS4_SYSV_ABI scePlayGoSetInstallSpeed(OrbisPlayGoHandle handle, OrbisPlayGoInstallSpeed speed) { LOG_INFO(Lib_PlayGo, "called"); - auto* playgo = Common::Singleton::Instance(); - - if (handle != 1) + if (handle != PlaygoHandle) { return ORBIS_PLAYGO_ERROR_BAD_HANDLE; - if (!playgo->initialized) + } + if (!playgo) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; + } switch (speed) { - case ORBIS_PLAYGO_INSTALL_SPEED_SUSPENDED: - case ORBIS_PLAYGO_INSTALL_SPEED_TRICKLE: - case ORBIS_PLAYGO_INSTALL_SPEED_FULL: + case OrbisPlayGoInstallSpeed::Suspended: + case OrbisPlayGoInstallSpeed::Trickle: + case OrbisPlayGoInstallSpeed::Full: break; default: return ORBIS_PLAYGO_ERROR_INVALID_ARGUMENT; } std::scoped_lock lk{playgo->GetSpeedMutex()}; - using namespace std::chrono; playgo->speed = speed; playgo->speed_tick = @@ -314,12 +343,13 @@ s32 PS4_SYSV_ABI scePlayGoSetInstallSpeed(OrbisPlayGoHandle handle, OrbisPlayGoI s32 PS4_SYSV_ABI scePlayGoSetLanguageMask(OrbisPlayGoHandle handle, OrbisPlayGoLanguageMask languageMask) { LOG_INFO(Lib_PlayGo, "called"); - auto* playgo = Common::Singleton::Instance(); - if (handle != 1) + if (handle != 1) { return ORBIS_PLAYGO_ERROR_BAD_HANDLE; - if (!playgo->initialized) + } + if (!playgo) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; + } playgo->langMask = languageMask; return ORBIS_OK; @@ -329,28 +359,28 @@ s32 PS4_SYSV_ABI scePlayGoSetToDoList(OrbisPlayGoHandle handle, const OrbisPlayG uint32_t numberOfEntries) { LOG_INFO(Lib_PlayGo, "called"); - auto* playgo = Common::Singleton::Instance(); - - if (handle != 1) + if (handle != PlaygoHandle) { return ORBIS_PLAYGO_ERROR_BAD_HANDLE; - if (todoList == nullptr) + } + if (todoList == nullptr) { return ORBIS_PLAYGO_ERROR_BAD_POINTER; - if (numberOfEntries == 0) + } + if (numberOfEntries == 0) { return ORBIS_PLAYGO_ERROR_BAD_SIZE; - if (!playgo->initialized) + } + if (!playgo) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; + } return ORBIS_OK; } s32 PS4_SYSV_ABI scePlayGoTerminate() { LOG_INFO(Lib_PlayGo, "called"); - auto* playgo = Common::Singleton::Instance(); - if (playgo->initialized) { - playgo->initialized = false; - } else { + if (!playgo) { return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED; } + playgo.reset(); return ORBIS_OK; } diff --git a/src/core/libraries/playgo/playgo_dialog.cpp b/src/core/libraries/playgo/playgo_dialog.cpp new file mode 100644 index 000000000..16f7aa05d --- /dev/null +++ b/src/core/libraries/playgo/playgo_dialog.cpp @@ -0,0 +1,78 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/libraries/libs.h" +#include "core/libraries/playgo/playgo_dialog.h" +#include "core/libraries/system/commondialog.h" + +namespace Libraries::PlayGo::Dialog { + +using CommonDialog::Error; +using CommonDialog::Result; +using CommonDialog::Status; + +Error PS4_SYSV_ABI scePlayGoDialogClose() { + LOG_ERROR(Lib_PlayGoDialog, "(DUMMY) called"); + return Error::OK; +} + +Error PS4_SYSV_ABI scePlayGoDialogGetResult(OrbisPlayGoDialogResult* result) { + LOG_ERROR(Lib_PlayGoDialog, "(DUMMY) called"); + if (result == nullptr) { + return Error::ARG_NULL; + } + // Result value 3 allows games to proceed. + result->result = static_cast(3); + return Error::OK; +} + +Status PS4_SYSV_ABI scePlayGoDialogGetStatus() { + LOG_ERROR(Lib_PlayGoDialog, "(DUMMY) called"); + return Status::FINISHED; +} + +Error PS4_SYSV_ABI scePlayGoDialogInitialize() { + LOG_ERROR(Lib_PlayGoDialog, "(DUMMY) called"); + return Error::OK; +} + +Error PS4_SYSV_ABI scePlayGoDialogOpen(const OrbisPlayGoDialogParam* param) { + LOG_ERROR(Lib_PlayGoDialog, "(DUMMY) called"); + if (param == nullptr) { + return Error::ARG_NULL; + } + ASSERT(param->size == sizeof(OrbisPlayGoDialogParam)); + ASSERT(param->baseParam.size == sizeof(CommonDialog::BaseParam)); + return Error::OK; +} + +Error PS4_SYSV_ABI scePlayGoDialogTerminate() { + LOG_ERROR(Lib_PlayGoDialog, "(DUMMY) called"); + return Error::OK; +} + +Status PS4_SYSV_ABI scePlayGoDialogUpdateStatus() { + LOG_ERROR(Lib_PlayGoDialog, "(DUMMY) called"); + return Status::FINISHED; +} + +void RegisterlibScePlayGoDialog(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("fbigNQiZpm0", "libScePlayGoDialog", 1, "libScePlayGoDialog", 1, 1, + scePlayGoDialogClose); + LIB_FUNCTION("wx9TDplJKB4", "libScePlayGoDialog", 1, "libScePlayGoDialog", 1, 1, + scePlayGoDialogGetResult); + LIB_FUNCTION("NOAMxY2EGS0", "libScePlayGoDialog", 1, "libScePlayGoDialog", 1, 1, + scePlayGoDialogGetStatus); + LIB_FUNCTION("fECamTJKpsM", "libScePlayGoDialog", 1, "libScePlayGoDialog", 1, 1, + scePlayGoDialogInitialize); + LIB_FUNCTION("kHd72ukqbxw", "libScePlayGoDialog", 1, "libScePlayGoDialog", 1, 1, + scePlayGoDialogOpen); + LIB_FUNCTION("okgIGdr5Iz0", "libScePlayGoDialog", 1, "libScePlayGoDialog", 1, 1, + scePlayGoDialogTerminate); + LIB_FUNCTION("Yb60K7BST48", "libScePlayGoDialog", 1, "libScePlayGoDialog", 1, 1, + scePlayGoDialogUpdateStatus); +}; + +} // namespace Libraries::PlayGo::Dialog diff --git a/src/core/libraries/playgo/playgo_dialog.h b/src/core/libraries/playgo/playgo_dialog.h new file mode 100644 index 000000000..fa9c64680 --- /dev/null +++ b/src/core/libraries/playgo/playgo_dialog.h @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" +#include "core/libraries/system/commondialog.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::PlayGo::Dialog { + +struct OrbisPlayGoDialogParam { + CommonDialog::BaseParam baseParam; + s32 size; + u8 unk[0x30]; +}; +static_assert(sizeof(OrbisPlayGoDialogParam) == 0x68); + +struct OrbisPlayGoDialogResult { + u8 unk1[0x4]; + CommonDialog::Result result; + u8 unk2[0x20]; +}; +static_assert(sizeof(OrbisPlayGoDialogResult) == 0x28); + +CommonDialog::Error PS4_SYSV_ABI scePlayGoDialogClose(); +CommonDialog::Error PS4_SYSV_ABI scePlayGoDialogGetResult(OrbisPlayGoDialogResult* result); +CommonDialog::Status PS4_SYSV_ABI scePlayGoDialogGetStatus(); +CommonDialog::Error PS4_SYSV_ABI scePlayGoDialogInitialize(); +CommonDialog::Error PS4_SYSV_ABI scePlayGoDialogOpen(const OrbisPlayGoDialogParam* param); +CommonDialog::Error PS4_SYSV_ABI scePlayGoDialogTerminate(); +CommonDialog::Status PS4_SYSV_ABI scePlayGoDialogUpdateStatus(); + +void RegisterlibScePlayGoDialog(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::PlayGo::Dialog diff --git a/src/core/libraries/playgo/playgo_types.h b/src/core/libraries/playgo/playgo_types.h index 62dea4f4e..885eee035 100644 --- a/src/core/libraries/playgo/playgo_types.h +++ b/src/core/libraries/playgo/playgo_types.h @@ -5,41 +5,39 @@ #include "common/types.h" -typedef u32 OrbisPlayGoHandle; -typedef u16 OrbisPlayGoChunkId; -typedef s8 OrbisPlayGoLocus; -typedef s32 OrbisPlayGoInstallSpeed; -typedef s64 OrbisPlayGoEta; -typedef u64 OrbisPlayGoLanguageMask; +using OrbisPlayGoHandle = u32; +using OrbisPlayGoChunkId = u16; +using OrbisPlayGoEta = s64; +using OrbisPlayGoLanguageMask = u64; -typedef struct OrbisPlayGoInitParams { +enum class OrbisPlayGoLocus : s8 { + NotDownloaded = 0, + LocalSlow = 2, + LocalFast = 3, +}; + +enum class OrbisPlayGoInstallSpeed : s32 { + Suspended = 0, + Trickle = 1, + Full = 2, +}; + +struct OrbisPlayGoInitParams { const void* bufAddr; u32 bufSize; u32 reserved; -} OrbisPlayGoInitParams; +}; -typedef struct OrbisPlayGoToDo { +struct OrbisPlayGoToDo { OrbisPlayGoChunkId chunkId; OrbisPlayGoLocus locus; s8 reserved; -} OrbisPlayGoToDo; +}; -typedef struct OrbisPlayGoProgress { - uint64_t progressSize; - uint64_t totalSize; -} OrbisPlayGoProgress; - -typedef enum OrbisPlayGoLocusValue { - ORBIS_PLAYGO_LOCUS_NOT_DOWNLOADED = 0, - ORBIS_PLAYGO_LOCUS_LOCAL_SLOW = 2, - ORBIS_PLAYGO_LOCUS_LOCAL_FAST = 3 -} OrbisPlayGoLocusValue; - -typedef enum OrbisPlayGoInstallSpeedValue { - ORBIS_PLAYGO_INSTALL_SPEED_SUSPENDED = 0, - ORBIS_PLAYGO_INSTALL_SPEED_TRICKLE = 1, - ORBIS_PLAYGO_INSTALL_SPEED_FULL = 2 -} OrbisPlayGoInstallSpeedValue; +struct OrbisPlayGoProgress { + u64 progressSize; + u64 totalSize; +}; constexpr int ORBIS_PLAYGO_ERROR_UNKNOWN = -2135818239; /* 0x80B20001 */ constexpr int ORBIS_PLAYGO_ERROR_FATAL = -2135818238; /* 0x80B20002 */ diff --git a/src/core/libraries/random/random.h b/src/core/libraries/random/random.h index b483cf6ed..172494106 100644 --- a/src/core/libraries/random/random.h +++ b/src/core/libraries/random/random.h @@ -10,9 +10,11 @@ class SymbolsResolver; } namespace Libraries::Random { -constexpr int32_t SCE_RANDOM_MAX_SIZE = 64; + +constexpr s32 SCE_RANDOM_MAX_SIZE = 64; s32 PS4_SYSV_ABI sceRandomGetRandomNumber(u8* buf, std::size_t size); void RegisterlibSceRandom(Core::Loader::SymbolsResolver* sym); -} // namespace Libraries::Random \ No newline at end of file + +} // namespace Libraries::Random diff --git a/src/core/libraries/razor_cpu/razor_cpu.cpp b/src/core/libraries/razor_cpu/razor_cpu.cpp new file mode 100644 index 000000000..99707e972 --- /dev/null +++ b/src/core/libraries/razor_cpu/razor_cpu.cpp @@ -0,0 +1,249 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "razor_cpu.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" + +namespace Libraries::RazorCpu { + +s32 PS4_SYSV_ABI sceRazorCpuBeginLogicalFileAccess() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +void PS4_SYSV_ABI sceRazorCpuDisableFiberUserMarkers() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); +} + +s32 PS4_SYSV_ABI sceRazorCpuEndLogicalFileAccess() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuFiberLogNameChange() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuFiberSwitch() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +bool PS4_SYSV_ABI sceRazorCpuFlushOccurred() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return false; +} + +s32 PS4_SYSV_ABI sceRazorCpuGetDataTagStorageSize() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuGpuMarkerSync() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuInitDataTags() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuInitializeGpuMarkerContext() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuInitializeInternal() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +bool PS4_SYSV_ABI sceRazorCpuIsCapturing() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return false; +} + +s32 PS4_SYSV_ABI sceRazorCpuJobManagerDispatch() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuJobManagerJob() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuJobManagerSequence() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuNamedSync() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuPlotValue() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuPopMarker() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuPushMarker() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuPushMarkerStatic() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuResizeTaggedBuffer() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +void PS4_SYSV_ABI sceRazorCpuSetPopMarkerCallback() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); +} + +void PS4_SYSV_ABI sceRazorCpuSetPushMarkerCallback() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); +} + +void PS4_SYSV_ABI sceRazorCpuSetPushMarkerStaticCallback() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); +} + +s32 PS4_SYSV_ABI sceRazorCpuShutdownDataTags() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuStartCaptureInternal() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuStopCaptureInternal() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuSync() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuTagArray() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuTagBuffer() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuUnTagBuffer() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuWorkloadRunBegin() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuWorkloadRunEnd() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuWorkloadSubmit() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceRazorCpuWriteBookmark() { + LOG_DEBUG(Lib_RazorCpu, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceRazorCpu(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("JFzLJBlYIJE", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuBeginLogicalFileAccess); + LIB_FUNCTION("SfRTRZ1Sh+U", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuDisableFiberUserMarkers); + LIB_FUNCTION("gVioM9cbiDs", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuEndLogicalFileAccess); + LIB_FUNCTION("G90IIOtgFQ0", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuFiberLogNameChange); + LIB_FUNCTION("PAytDtFGpqY", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuFiberSwitch); + LIB_FUNCTION("sPhrQD31ClM", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuFlushOccurred); + LIB_FUNCTION("B782NptkGUc", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuGetDataTagStorageSize); + LIB_FUNCTION("EH9Au2RlSrE", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuGpuMarkerSync); + LIB_FUNCTION("A7oRMdaOJP8", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuInitDataTags); + LIB_FUNCTION("NFwh-J-BrI0", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuInitializeGpuMarkerContext); + LIB_FUNCTION("ElNyedXaa4o", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuInitializeInternal); + LIB_FUNCTION("EboejOQvLL4", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuIsCapturing); + LIB_FUNCTION("dnEdyY4+klQ", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuJobManagerDispatch); + LIB_FUNCTION("KP+TBWGHlgs", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuJobManagerJob); + LIB_FUNCTION("9FowWFMEIM8", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuJobManagerSequence); + LIB_FUNCTION("XCuZoBSVFG8", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, sceRazorCpuNamedSync); + LIB_FUNCTION("njGikRrxkC0", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, sceRazorCpuPlotValue); + LIB_FUNCTION("YpkGsMXP3ew", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, sceRazorCpuPopMarker); + LIB_FUNCTION("zw+celG7zSI", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, sceRazorCpuPushMarker); + LIB_FUNCTION("uZrOwuNJX-M", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuPushMarkerStatic); + LIB_FUNCTION("D0yUjM33QqU", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuResizeTaggedBuffer); + LIB_FUNCTION("jqYWaTfgZs0", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuSetPopMarkerCallback); + LIB_FUNCTION("DJsHcEb94n0", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuSetPushMarkerCallback); + LIB_FUNCTION("EZtqozPTS4M", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuSetPushMarkerStaticCallback); + LIB_FUNCTION("emklx7RK-LY", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuShutdownDataTags); + LIB_FUNCTION("TIytAjYeaik", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuStartCaptureInternal); + LIB_FUNCTION("jWpkVWdMrsM", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuStopCaptureInternal); + LIB_FUNCTION("Ax7NjOzctIM", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, sceRazorCpuSync); + LIB_FUNCTION("we3oTKSPSTw", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, sceRazorCpuTagArray); + LIB_FUNCTION("vyjdThnQfQQ", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, sceRazorCpuTagBuffer); + LIB_FUNCTION("0yNHPIkVTmw", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuUnTagBuffer); + LIB_FUNCTION("Crha9LvwvJM", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuWorkloadRunBegin); + LIB_FUNCTION("q1GxBfGHO0s", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuWorkloadRunEnd); + LIB_FUNCTION("6rUvx-6QmYc", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuWorkloadSubmit); + LIB_FUNCTION("G3brhegfyNg", "libSceRazorCpu", 1, "libSceRazorCpu", 1, 1, + sceRazorCpuWriteBookmark); +} + +} // namespace Libraries::RazorCpu \ No newline at end of file diff --git a/src/core/libraries/razor_cpu/razor_cpu.h b/src/core/libraries/razor_cpu/razor_cpu.h new file mode 100644 index 000000000..ec25e44b9 --- /dev/null +++ b/src/core/libraries/razor_cpu/razor_cpu.h @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +#include +#include + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::RazorCpu { +void RegisterlibSceRazorCpu(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::RazorCpu \ No newline at end of file diff --git a/src/core/libraries/rtc/rtc.cpp b/src/core/libraries/rtc/rtc.cpp index 7a46a1e31..f94c00134 100644 --- a/src/core/libraries/rtc/rtc.cpp +++ b/src/core/libraries/rtc/rtc.cpp @@ -4,12 +4,11 @@ #include #include "common/logging/log.h" -#include "core/libraries/error_codes.h" -#include "core/libraries/kernel/libkernel.h" -#include "core/libraries/kernel/time_management.h" +#include "core/libraries/kernel/process.h" +#include "core/libraries/kernel/time.h" #include "core/libraries/libs.h" -#include "rtc.h" -#include "rtc_error.h" +#include "core/libraries/rtc/rtc.h" +#include "core/libraries/rtc/rtc_error.h" namespace Libraries::Rtc { @@ -49,7 +48,7 @@ int PS4_SYSV_ABI sceRtcCheckValid(OrbisRtcDateTime* pTime) { if (pTime->microsecond >= 1000000) return ORBIS_RTC_ERROR_INVALID_MICROSECOND; - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcCompareTick(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2) { @@ -102,7 +101,7 @@ int PS4_SYSV_ABI sceRtcConvertUtcToLocalTime(OrbisRtcTick* pTickUtc, OrbisRtcTic } int PS4_SYSV_ABI sceRtcEnd() { - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcFormatRFC2822(char* pszDateTime, const OrbisRtcTick* pTickUtc, @@ -253,7 +252,7 @@ int PS4_SYSV_ABI sceRtcFormatRFC2822(char* pszDateTime, const OrbisRtcTick* pTic } } - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcFormatRFC2822LocalTime(char* pszDateTime, const OrbisRtcTick* pTickUtc) { @@ -374,7 +373,7 @@ int PS4_SYSV_ABI sceRtcFormatRFC3339Precise(char* pszDateTime, const OrbisRtcTic pszDateTime[i] = formattedString.c_str()[i]; } - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcFormatRFC3339PreciseLocalTime(char* pszDateTime, @@ -397,13 +396,13 @@ int PS4_SYSV_ABI sceRtcGetCurrentAdNetworkTick(OrbisRtcTick* pTick) { Kernel::OrbisKernelTimespec clocktime; int returnValue = Kernel::sceKernelClockGettime(Kernel::ORBIS_CLOCK_REALTIME, &clocktime); - if (returnValue == SCE_OK) { + if (returnValue == ORBIS_OK) { pTick->tick = clocktime.tv_nsec / 1000 + clocktime.tv_sec * 1000000 + UNIX_EPOCH_TICKS; } else { return ORBIS_RTC_ERROR_NOT_INITIALIZED; } - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcGetCurrentClock(OrbisRtcDateTime* pTime, int timeZone) { @@ -415,7 +414,7 @@ int PS4_SYSV_ABI sceRtcGetCurrentClock(OrbisRtcDateTime* pTime, int timeZone) { Kernel::OrbisKernelTimespec clocktime; int returnValue = Kernel::sceKernelClockGettime(Kernel::ORBIS_CLOCK_REALTIME, &clocktime); - if (returnValue == SCE_OK) { + if (returnValue == ORBIS_OK) { OrbisRtcTick clockTick; clockTick.tick = clocktime.tv_nsec / 1000 + clocktime.tv_sec * 1000000 + UNIX_EPOCH_TICKS; @@ -461,13 +460,13 @@ int PS4_SYSV_ABI sceRtcGetCurrentDebugNetworkTick(OrbisRtcTick* pTick) { Kernel::OrbisKernelTimespec clocktime; int returnValue = Kernel::sceKernelClockGettime(Kernel::ORBIS_CLOCK_REALTIME, &clocktime); - if (returnValue == SCE_OK) { + if (returnValue == ORBIS_OK) { pTick->tick = clocktime.tv_nsec / 1000 + clocktime.tv_sec * 1000000 + UNIX_EPOCH_TICKS; } else { return ORBIS_RTC_ERROR_NOT_INITIALIZED; } - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcGetCurrentNetworkTick(OrbisRtcTick* pTick) { @@ -479,13 +478,13 @@ int PS4_SYSV_ABI sceRtcGetCurrentNetworkTick(OrbisRtcTick* pTick) { Kernel::OrbisKernelTimespec clocktime; int returnValue = Kernel::sceKernelClockGettime(Kernel::ORBIS_CLOCK_REALTIME, &clocktime); - if (returnValue == SCE_OK) { + if (returnValue == ORBIS_OK) { pTick->tick = clocktime.tv_nsec / 1000 + clocktime.tv_sec * 1000000 + UNIX_EPOCH_TICKS; } else { return ORBIS_RTC_ERROR_NOT_INITIALIZED; } - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcGetCurrentRawNetworkTick(OrbisRtcTick* pTick) { @@ -497,13 +496,13 @@ int PS4_SYSV_ABI sceRtcGetCurrentRawNetworkTick(OrbisRtcTick* pTick) { Kernel::OrbisKernelTimespec clocktime; int returnValue = Kernel::sceKernelClockGettime(Kernel::ORBIS_CLOCK_REALTIME, &clocktime); - if (returnValue == SCE_OK) { + if (returnValue == ORBIS_OK) { pTick->tick = clocktime.tv_nsec / 1000 + clocktime.tv_sec * 1000000 + UNIX_EPOCH_TICKS; } else { return ORBIS_RTC_ERROR_NOT_INITIALIZED; } - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcGetCurrentTick(OrbisRtcTick* pTick) { @@ -519,7 +518,7 @@ int PS4_SYSV_ABI sceRtcGetCurrentTick(OrbisRtcTick* pTick) { pTick->tick = clocktime.tv_nsec / 1000 + clocktime.tv_sec * 1000000 + UNIX_EPOCH_TICKS; } - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcGetDayOfWeek(int year, int month, int day) { @@ -576,14 +575,14 @@ int PS4_SYSV_ABI sceRtcGetDaysInMonth(int year, int month) { return lastDay; } -int PS4_SYSV_ABI sceRtcGetDosTime(OrbisRtcDateTime* pTime, unsigned int* dosTime) { +int PS4_SYSV_ABI sceRtcGetDosTime(OrbisRtcDateTime* pTime, u32* dosTime) { LOG_TRACE(Lib_Rtc, "called"); if (pTime == nullptr || dosTime == nullptr) return ORBIS_RTC_ERROR_INVALID_POINTER; int isValid = sceRtcCheckValid(pTime); - if (isValid != SCE_OK) { + if (isValid != ORBIS_OK) { return isValid; } @@ -594,7 +593,7 @@ int PS4_SYSV_ABI sceRtcGetDosTime(OrbisRtcDateTime* pTime, unsigned int* dosTime *dosTime |= (pTime->month & 0x0F) << 21; *dosTime |= ((pTime->year - 1980) & 0x7F) << 25; - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcGetTick(OrbisRtcDateTime* pTime, OrbisRtcTick* pTick) { @@ -629,10 +628,10 @@ int PS4_SYSV_ABI sceRtcGetTick(OrbisRtcDateTime* pTime, OrbisRtcTick* pTick) { pTick->tick = days + msec; - return SCE_OK; + return ORBIS_OK; } -unsigned int PS4_SYSV_ABI sceRtcGetTickResolution() { +u32 PS4_SYSV_ABI sceRtcGetTickResolution() { LOG_TRACE(Lib_Rtc, "called"); return 1000000; @@ -645,7 +644,7 @@ int PS4_SYSV_ABI sceRtcGetTime_t(OrbisRtcDateTime* pTime, time_t* llTime) { return ORBIS_RTC_ERROR_INVALID_POINTER; int isValid = sceRtcCheckValid(pTime); - if (isValid != SCE_OK) { + if (isValid != ORBIS_OK) { return isValid; } @@ -658,7 +657,7 @@ int PS4_SYSV_ABI sceRtcGetTime_t(OrbisRtcDateTime* pTime, time_t* llTime) { *llTime = (timeTick.tick - UNIX_EPOCH_TICKS) / 1000000; } - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcGetWin32FileTime(OrbisRtcDateTime* pTime, uint64_t* ulWin32Time) { @@ -668,7 +667,7 @@ int PS4_SYSV_ABI sceRtcGetWin32FileTime(OrbisRtcDateTime* pTime, uint64_t* ulWin return ORBIS_RTC_ERROR_INVALID_POINTER; int isValid = sceRtcCheckValid(pTime); - if (isValid != SCE_OK) { + if (isValid != ORBIS_OK) { return isValid; } @@ -681,11 +680,11 @@ int PS4_SYSV_ABI sceRtcGetWin32FileTime(OrbisRtcDateTime* pTime, uint64_t* ulWin *ulWin32Time = (timeTick.tick - WIN32_FILETIME_EPOCH_TICKS) * 10; } - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcInit() { - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcIsLeapYear(int yearInt) { @@ -790,7 +789,7 @@ int PS4_SYSV_ABI sceRtcParseDateTime(OrbisRtcTick* pTickUtc, const char* pszDate sceRtcGetTick(&dateTime, pTickUtc); } - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcParseRFC3339(OrbisRtcTick* pTickUtc, const char* pszDateTime) { @@ -825,7 +824,7 @@ int PS4_SYSV_ABI sceRtcParseRFC3339(OrbisRtcTick* pTickUtc, const char* pszDateT } } - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcSetConf() { @@ -869,7 +868,7 @@ int PS4_SYSV_ABI sceRtcSetDosTime(OrbisRtcDateTime* pTime, u32 dosTime) { pTime->day = days & 0x1f; pTime->month = (days >> 5) & 0x0f; pTime->year = (days >> 9) + 1980; - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcSetTick(OrbisRtcDateTime* pTime, OrbisRtcTick* pTick) { @@ -918,7 +917,7 @@ int PS4_SYSV_ABI sceRtcSetTick(OrbisRtcDateTime* pTime, OrbisRtcTick* pTick) { msec %= 1000000; pTime->microsecond = msec; - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcSetTime_t(OrbisRtcDateTime* pTime, time_t llTime) { @@ -946,7 +945,7 @@ int PS4_SYSV_ABI sceRtcSetTime_t(OrbisRtcDateTime* pTime, time_t llTime) { newTick.tick += UNIX_EPOCH_TICKS; sceRtcSetTick(pTime, &newTick); - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcSetWin32FileTime(OrbisRtcDateTime* pTime, int64_t ulWin32Time) { @@ -962,7 +961,7 @@ int PS4_SYSV_ABI sceRtcSetWin32FileTime(OrbisRtcDateTime* pTime, int64_t ulWin32 sceRtcSetTick(pTime, &convertedTick); - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcTickAddDays(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, int32_t lAdd) { @@ -973,7 +972,7 @@ int PS4_SYSV_ABI sceRtcTickAddDays(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, i pTick1->tick = (lAdd * 86400000000) + pTick2->tick; - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcTickAddHours(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, int32_t lAdd) { @@ -984,7 +983,7 @@ int PS4_SYSV_ABI sceRtcTickAddHours(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, pTick1->tick = (lAdd * 3600000000) + pTick2->tick; - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcTickAddMicroseconds(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, @@ -996,7 +995,7 @@ int PS4_SYSV_ABI sceRtcTickAddMicroseconds(OrbisRtcTick* pTick1, OrbisRtcTick* p pTick1->tick = lAdd + pTick2->tick; - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcTickAddMinutes(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, int64_t lAdd) { @@ -1007,7 +1006,7 @@ int PS4_SYSV_ABI sceRtcTickAddMinutes(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2 pTick1->tick = (lAdd * 60000000) + pTick2->tick; - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcTickAddMonths(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, int32_t lAdd) { @@ -1018,7 +1017,7 @@ int PS4_SYSV_ABI sceRtcTickAddMonths(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, if (lAdd == 0) { pTick1->tick = pTick2->tick; - return SCE_OK; + return ORBIS_OK; } OrbisRtcDateTime time; @@ -1054,13 +1053,13 @@ int PS4_SYSV_ABI sceRtcTickAddMonths(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, } int timeIsValid = sceRtcCheckValid(&time); - if (timeIsValid == SCE_OK) { + if (timeIsValid == ORBIS_OK) { sceRtcGetTick(&time, pTick1); } else { return timeIsValid; } - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcTickAddSeconds(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, int64_t lAdd) { @@ -1071,7 +1070,7 @@ int PS4_SYSV_ABI sceRtcTickAddSeconds(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2 pTick1->tick = (lAdd * 1000000) + pTick2->tick; - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcTickAddTicks(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, int64_t lAdd) { @@ -1082,7 +1081,7 @@ int PS4_SYSV_ABI sceRtcTickAddTicks(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, pTick1->tick = lAdd + pTick2->tick; - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcTickAddWeeks(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, int32_t lAdd) { @@ -1093,7 +1092,7 @@ int PS4_SYSV_ABI sceRtcTickAddWeeks(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, pTick1->tick = (lAdd * 604800000000) + pTick2->tick; - return SCE_OK; + return ORBIS_OK; } int PS4_SYSV_ABI sceRtcTickAddYears(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, int32_t lAdd) { @@ -1106,7 +1105,7 @@ int PS4_SYSV_ABI sceRtcTickAddYears(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, if (lAdd == 0) { pTick1->tick = pTick2->tick; - return SCE_OK; + return ORBIS_OK; } sceRtcSetTick(&time, pTick1); @@ -1114,13 +1113,13 @@ int PS4_SYSV_ABI sceRtcTickAddYears(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, time.year += lAdd; int timeIsValid = sceRtcCheckValid(&time); - if (timeIsValid == SCE_OK) { + if (timeIsValid == ORBIS_OK) { sceRtcGetTick(&time, pTick1); } else { return timeIsValid; } - return SCE_OK; + return ORBIS_OK; } void RegisterlibSceRtc(Core::Loader::SymbolsResolver* sym) { diff --git a/src/core/libraries/rtc/rtc.h b/src/core/libraries/rtc/rtc.h index c41040863..eb7afa00c 100644 --- a/src/core/libraries/rtc/rtc.h +++ b/src/core/libraries/rtc/rtc.h @@ -3,6 +3,7 @@ #pragma once +#include #include "common/types.h" namespace Core::Loader { @@ -19,21 +20,21 @@ constexpr int ORBIS_RTC_DAYOFWEEK_THURSDAY = 4; constexpr int ORBIS_RTC_DAYOFWEEK_FRIDAY = 5; constexpr int ORBIS_RTC_DAYOFWEEK_SATURDAY = 6; -constexpr int64_t UNIX_EPOCH_TICKS = 0xdcbffeff2bc000; -constexpr int64_t WIN32_FILETIME_EPOCH_TICKS = 0xb36168b6a58000; +constexpr s64 UNIX_EPOCH_TICKS = 0xdcbffeff2bc000; +constexpr s64 WIN32_FILETIME_EPOCH_TICKS = 0xb36168b6a58000; struct OrbisRtcTick { - uint64_t tick; + u64 tick; }; struct OrbisRtcDateTime { - uint16_t year; - uint16_t month; - uint16_t day; - uint16_t hour; - uint16_t minute; - uint16_t second; - uint32_t microsecond; + u16 year; + u16 month; + u16 day; + u16 hour; + u16 minute; + u16 second; + u32 microsecond; }; int PS4_SYSV_ABI sceRtcCheckValid(OrbisRtcDateTime* pTime); @@ -58,9 +59,9 @@ int PS4_SYSV_ABI sceRtcGetCurrentRawNetworkTick(OrbisRtcTick* pTick); int PS4_SYSV_ABI sceRtcGetCurrentTick(OrbisRtcTick* pTick); int PS4_SYSV_ABI sceRtcGetDayOfWeek(int year, int month, int day); int PS4_SYSV_ABI sceRtcGetDaysInMonth(int year, int month); -int PS4_SYSV_ABI sceRtcGetDosTime(OrbisRtcDateTime* pTime, unsigned int* dosTime); +int PS4_SYSV_ABI sceRtcGetDosTime(OrbisRtcDateTime* pTime, u32* dosTime); int PS4_SYSV_ABI sceRtcGetTick(OrbisRtcDateTime* pTime, OrbisRtcTick* pTick); -unsigned int PS4_SYSV_ABI sceRtcGetTickResolution(); +u32 PS4_SYSV_ABI sceRtcGetTickResolution(); int PS4_SYSV_ABI sceRtcGetTime_t(OrbisRtcDateTime* pTime, time_t* llTime); int PS4_SYSV_ABI sceRtcGetWin32FileTime(OrbisRtcDateTime* pTime, uint64_t* ulWin32Time); int PS4_SYSV_ABI sceRtcInit(); @@ -88,4 +89,4 @@ int PS4_SYSV_ABI sceRtcTickAddWeeks(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, int PS4_SYSV_ABI sceRtcTickAddYears(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, int32_t lAdd); void RegisterlibSceRtc(Core::Loader::SymbolsResolver* sym); -} // namespace Libraries::Rtc \ No newline at end of file +} // namespace Libraries::Rtc diff --git a/src/core/libraries/rtc/rtc_error.h b/src/core/libraries/rtc/rtc_error.h index 3af5a68fd..406c6fb56 100644 --- a/src/core/libraries/rtc/rtc_error.h +++ b/src/core/libraries/rtc/rtc_error.h @@ -3,6 +3,8 @@ #pragma once +#include "core/libraries/error_codes.h" + constexpr int ORBIS_RTC_ERROR_DATETIME_UNINITIALIZED = 0x7FFEF9FE; constexpr int ORBIS_RTC_ERROR_INVALID_PARAMETER = 0x80010602; constexpr int ORBIS_RTC_ERROR_INVALID_TICK_PARAMETER = 0x80010603; @@ -29,4 +31,4 @@ constexpr int ORBIS_RTC_ERROR_INVALID_DAY = 0x80B5000A; constexpr int ORBIS_RTC_ERROR_INVALID_HOUR = 0x80B5000B; constexpr int ORBIS_RTC_ERROR_INVALID_MINUTE = 0x80B5000C; constexpr int ORBIS_RTC_ERROR_INVALID_SECOND = 0x80B5000D; -constexpr int ORBIS_RTC_ERROR_INVALID_MICROSECOND = 0x80B5000E; \ No newline at end of file +constexpr int ORBIS_RTC_ERROR_INVALID_MICROSECOND = 0x80B5000E; diff --git a/src/core/libraries/save_data/dialog/savedatadialog.cpp b/src/core/libraries/save_data/dialog/savedatadialog.cpp index 0ad7d7dc0..2f0619165 100644 --- a/src/core/libraries/save_data/dialog/savedatadialog.cpp +++ b/src/core/libraries/save_data/dialog/savedatadialog.cpp @@ -1,11 +1,12 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + #include "common/elf_info.h" #include "common/logging/log.h" #include "core/libraries/libs.h" #include "core/libraries/system/commondialog.h" -#include "magic_enum.hpp" #include "savedatadialog.h" #include "savedatadialog_ui.h" diff --git a/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp b/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp index 01e56f8b8..a6ca8744d 100644 --- a/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp +++ b/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include "common/elf_info.h" #include "common/singleton.h" @@ -98,12 +98,9 @@ SaveDialogState::SaveDialogState(const OrbisSaveDataDialogParam& param) { param_sfo.Open(param_sfo_path); auto last_write = param_sfo.GetLastWrite(); -#if defined(_WIN32) && !defined(__GNUC__) && !defined(__MINGW32__) && !defined(__MINGW64__) - auto utc_time = std::chrono::file_clock::to_utc(last_write); -#else - auto utc_time = std::chrono::file_clock::to_sys(last_write); -#endif - std::string date_str = fmt::format("{:%d %b, %Y %R}", utc_time); + std::string date_str = + fmt::format("{:%d %b, %Y %R}", + fmt::localtime(std::chrono::system_clock::to_time_t(last_write))); size_t size = Common::FS::GetDirectorySize(dir_path); std::string size_str = SpaceSizeToString(size); @@ -592,7 +589,7 @@ void SaveDialogUi::DrawList() { int idx = 0; int max_idx = 0; bool is_min = pos == FocusPos::DATAOLDEST; - std::filesystem::file_time_type max_write{}; + std::chrono::system_clock::time_point max_write{}; if (state->new_item.has_value()) { idx++; } diff --git a/src/core/libraries/save_data/dialog/savedatadialog_ui.h b/src/core/libraries/save_data/dialog/savedatadialog_ui.h index 3f414470f..aa67e1f5f 100644 --- a/src/core/libraries/save_data/dialog/savedatadialog_ui.h +++ b/src/core/libraries/save_data/dialog/savedatadialog_ui.h @@ -248,7 +248,7 @@ public: std::string date{}; std::string size{}; - std::filesystem::file_time_type last_write{}; + std::chrono::system_clock::time_point last_write{}; PSF pfo{}; bool is_corrupted{}; }; diff --git a/src/core/libraries/save_data/save_backup.cpp b/src/core/libraries/save_data/save_backup.cpp index da5172b15..5261cdb11 100644 --- a/src/core/libraries/save_data/save_backup.cpp +++ b/src/core/libraries/save_data/save_backup.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include "save_backup.h" #include "save_instance.h" @@ -79,7 +79,7 @@ static void backup(const std::filesystem::path& dir_name) { } static void BackupThreadBody() { - Common::SetCurrentThreadName("shadPS4:SaveData_BackupThread"); + Common::SetCurrentThreadName("shadPS4:SaveData:BackupThread"); while (g_backup_status != WorkerStatus::Stopping) { g_backup_status = WorkerStatus::Waiting; diff --git a/src/core/libraries/save_data/save_instance.cpp b/src/core/libraries/save_data/save_instance.cpp index 0d6c5173c..99daf83cc 100644 --- a/src/core/libraries/save_data/save_instance.cpp +++ b/src/core/libraries/save_data/save_instance.cpp @@ -3,7 +3,7 @@ #include -#include +#include #include "common/assert.h" #include "common/config.h" diff --git a/src/core/libraries/save_data/save_memory.cpp b/src/core/libraries/save_data/save_memory.cpp index e9ef53761..84179bc27 100644 --- a/src/core/libraries/save_data/save_memory.cpp +++ b/src/core/libraries/save_data/save_memory.cpp @@ -66,7 +66,7 @@ static void SaveFileSafe(void* buf, size_t count, const std::filesystem::path& p } [[noreturn]] void SaveThreadLoop() { - Common::SetCurrentThreadName("shadPS4:SaveData_SaveDataMemoryThread"); + Common::SetCurrentThreadName("shadPS4:SaveData:SaveDataMemoryThread"); std::mutex mtx; while (true) { { diff --git a/src/core/libraries/save_data/savedata.cpp b/src/core/libraries/save_data/savedata.cpp index 93b3c20a9..66899fb34 100644 --- a/src/core/libraries/save_data/savedata.cpp +++ b/src/core/libraries/save_data/savedata.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include "common/assert.h" #include "common/cstring.h" @@ -149,7 +149,7 @@ struct OrbisSaveDataIcon { size_t dataSize; std::array _reserved; - Error LoadIcon(const std::filesystem::path& icon_path) { + Error LoadIcon(const fs::path& icon_path) { try { const Common::FS::IOFile file(icon_path, Common::FS::FileAccessMode::Read); dataSize = file.GetSize(); @@ -345,7 +345,9 @@ static bool match(std::string_view str, std::string_view pattern) { if (*pat_it == '_') { // 1 character wildcard ++str_it; ++pat_it; - } else if (*pat_it != *str_it) { + continue; + } + if (*pat_it != *str_it) { return false; } ++str_it; @@ -1230,7 +1232,7 @@ Error PS4_SYSV_ABI sceSaveDataLoadIcon(const OrbisSaveDataMountPoint* mountPoint return Error::PARAMETER; } LOG_DEBUG(Lib_SaveData, "called"); - std::filesystem::path path; + fs::path path; const std::string_view mount_point_str{mountPoint->data}; for (const auto& instance : g_mount_slots) { if (instance.has_value() && instance->GetMountPoint() == mount_point_str) { @@ -1375,7 +1377,7 @@ Error PS4_SYSV_ABI sceSaveDataSaveIcon(const OrbisSaveDataMountPoint* mountPoint return Error::PARAMETER; } LOG_DEBUG(Lib_SaveData, "called"); - std::filesystem::path path; + fs::path path; const std::string_view mount_point_str{mountPoint->data}; for (const auto& instance : g_mount_slots) { if (instance.has_value() && instance->GetMountPoint() == mount_point_str) { diff --git a/src/core/libraries/system/msgdialog.cpp b/src/core/libraries/system/msgdialog.cpp index 7d924e4ad..8a01f429f 100644 --- a/src/core/libraries/system/msgdialog.cpp +++ b/src/core/libraries/system/msgdialog.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include -#include +#include #include "common/assert.h" #include "common/logging/log.h" diff --git a/src/core/libraries/system/sysmodule.cpp b/src/core/libraries/system/sysmodule.cpp index 596efc0af..350f1317b 100644 --- a/src/core/libraries/system/sysmodule.cpp +++ b/src/core/libraries/system/sysmodule.cpp @@ -3,12 +3,13 @@ #define MAGIC_ENUM_RANGE_MIN 0 #define MAGIC_ENUM_RANGE_MAX 300 -#include +#include #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "core/libraries/system/sysmodule.h" +#include "core/libraries/system/system_error.h" namespace Libraries::SysModule { @@ -32,13 +33,21 @@ int PS4_SYSV_ABI sceSysmoduleIsCameraPreloaded() { return ORBIS_OK; } -int PS4_SYSV_ABI sceSysmoduleIsLoaded() { - LOG_ERROR(Lib_SysModule, "(STUBBED) called"); +int PS4_SYSV_ABI sceSysmoduleIsLoaded(OrbisSysModule id) { + LOG_ERROR(Lib_SysModule, "(DUMMY) called module = {}", magic_enum::enum_name(id)); + if (static_cast(id) == 0) { + LOG_ERROR(Lib_SysModule, "Invalid sysmodule ID: {:#x}", static_cast(id)); + return ORBIS_SYSMODULE_INVALID_ID; + } return ORBIS_OK; } -int PS4_SYSV_ABI sceSysmoduleIsLoadedInternal() { - LOG_ERROR(Lib_SysModule, "(STUBBED) called"); +int PS4_SYSV_ABI sceSysmoduleIsLoadedInternal(OrbisSysModuleInternal id) { + LOG_ERROR(Lib_SysModule, "(DUMMY) called module = {:#x}", static_cast(id)); + if ((static_cast(id) & 0x7FFFFFFF) == 0) { + LOG_ERROR(Lib_SysModule, "Invalid internal sysmodule ID: {:#x}", static_cast(id)); + return ORBIS_SYSMODULE_INVALID_ID; + } return ORBIS_OK; } diff --git a/src/core/libraries/system/sysmodule.h b/src/core/libraries/system/sysmodule.h index d7a0c31b1..3630fb58e 100644 --- a/src/core/libraries/system/sysmodule.h +++ b/src/core/libraries/system/sysmodule.h @@ -147,12 +147,16 @@ enum class OrbisSysModule : u16 { ORBIS_SYSMODULE_KEYBOARD = 0x0106, }; +enum class OrbisSysModuleInternal : u32 { + ORBIS_SYSMODULE_INTERNAL_RAZOR_CPU = 0x80000019, // libSceRazorCpu.sprx +}; + int PS4_SYSV_ABI sceSysmoduleGetModuleHandleInternal(); int PS4_SYSV_ABI sceSysmoduleGetModuleInfoForUnwind(); int PS4_SYSV_ABI sceSysmoduleIsCalledFromSysModule(); int PS4_SYSV_ABI sceSysmoduleIsCameraPreloaded(); -int PS4_SYSV_ABI sceSysmoduleIsLoaded(); -int PS4_SYSV_ABI sceSysmoduleIsLoadedInternal(); +int PS4_SYSV_ABI sceSysmoduleIsLoaded(OrbisSysModule id); +int PS4_SYSV_ABI sceSysmoduleIsLoadedInternal(OrbisSysModuleInternal id); int PS4_SYSV_ABI sceSysmoduleLoadModule(OrbisSysModule id); int PS4_SYSV_ABI sceSysmoduleLoadModuleByNameInternal(); int PS4_SYSV_ABI sceSysmoduleLoadModuleInternal(); diff --git a/src/core/libraries/system/system_error.h b/src/core/libraries/system/system_error.h new file mode 100644 index 000000000..615e4cd5f --- /dev/null +++ b/src/core/libraries/system/system_error.h @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +constexpr int ORBIS_SYSMODULE_INVALID_ID = 0x805A1000; +constexpr int ORBIS_SYSMODULE_NOT_LOADED = 0x805A1001; +constexpr int ORBIS_SYSMODULE_LOCK_FAILED = 0x805A10FF; \ No newline at end of file diff --git a/src/core/libraries/system/systemservice.cpp b/src/core/libraries/system/systemservice.cpp index 9c6fe8f2e..ebb9f4392 100644 --- a/src/core/libraries/system/systemservice.cpp +++ b/src/core/libraries/system/systemservice.cpp @@ -3,9 +3,9 @@ #include "common/config.h" #include "common/logging/log.h" -#include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "core/libraries/system/systemservice.h" +#include "core/libraries/system/systemservice_error.h" namespace Libraries::SystemService { @@ -1772,14 +1772,12 @@ s32 PS4_SYSV_ABI sceSystemServiceGetStatus(OrbisSystemServiceStatus* status) { LOG_ERROR(Lib_SystemService, "OrbisSystemServiceStatus is null"); return ORBIS_SYSTEM_SERVICE_ERROR_PARAMETER; } - OrbisSystemServiceStatus st = {}; - st.eventNum = 0; - st.isSystemUiOverlaid = false; - st.isInBackgroundExecution = false; - st.isCpuMode7CpuNormal = true; - st.isGameLiveStreamingOnAir = false; - st.isOutOfVrPlayArea = false; - *status = st; + status->event_num = 0; + status->is_system_ui_overlaid = false; + status->is_in_background_execution = false; + status->is_cpu_mode7_cpu_normal = true; + status->is_game_live_streaming_on_air = false; + status->is_out_of_vr_play_area = false; return ORBIS_OK; } @@ -1889,39 +1887,38 @@ int PS4_SYSV_ABI sceSystemServiceNavigateToGoHome() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceSystemServiceParamGetInt(int param_id, int* value) { +s32 PS4_SYSV_ABI sceSystemServiceParamGetInt(OrbisSystemServiceParamId param_id, int* value) { // TODO this probably should be stored in config for UI configuration - LOG_INFO(Lib_SystemService, "called param_id {}", param_id); + LOG_INFO(Lib_SystemService, "called param_id {}", u32(param_id)); if (value == nullptr) { LOG_ERROR(Lib_SystemService, "value is null"); return ORBIS_SYSTEM_SERVICE_ERROR_PARAMETER; } switch (param_id) { - case ORBIS_SYSTEM_SERVICE_PARAM_ID_LANG: + case OrbisSystemServiceParamId::Lang: *value = Config::GetLanguage(); break; - case ORBIS_SYSTEM_SERVICE_PARAM_ID_DATE_FORMAT: - *value = ORBIS_SYSTEM_PARAM_DATE_FORMAT_DDMMYYYY; + case OrbisSystemServiceParamId::DateFormat: + *value = u32(OrbisSystemParamDateFormat::FmtDDMMYYYY); break; - case ORBIS_SYSTEM_SERVICE_PARAM_ID_TIME_FORMAT: - *value = ORBIS_SYSTEM_PARAM_TIME_FORMAT_24HOUR; + case OrbisSystemServiceParamId::TimeFormat: + *value = u32(OrbisSystemParamTimeFormat::Fmt24Hour); break; - case ORBIS_SYSTEM_SERVICE_PARAM_ID_TIME_ZONE: + case OrbisSystemServiceParamId::TimeZone: *value = +120; break; - case ORBIS_SYSTEM_SERVICE_PARAM_ID_SUMMERTIME: + case OrbisSystemServiceParamId::Summertime: *value = 1; break; - case ORBIS_SYSTEM_SERVICE_PARAM_ID_GAME_PARENTAL_LEVEL: - *value = ORBIS_SYSTEM_PARAM_GAME_PARENTAL_OFF; + case OrbisSystemServiceParamId::GameParentalLevel: + *value = u32(OrbisSystemParamGameParentalLevel::Off); break; - case ORBIS_SYSTEM_SERVICE_PARAM_ID_ENTER_BUTTON_ASSIGN: - *value = ORBIS_SYSTEM_PARAM_ENTER_BUTTON_ASSIGN_CROSS; + case OrbisSystemServiceParamId::EnterButtonAssign: + *value = u32(OrbisSystemParamEnterButtonAssign::Cross); break; default: - LOG_ERROR(Lib_SystemService, "param_id {} unsupported!", - param_id); // shouldn't go there but log it - *value = 0; // return a dummy value + LOG_ERROR(Lib_SystemService, "param_id {} unsupported!", u32(param_id)); + *value = 0; } return ORBIS_OK; @@ -1943,7 +1940,7 @@ int PS4_SYSV_ABI sceSystemServiceRaiseExceptionLocalProcess() { } s32 PS4_SYSV_ABI sceSystemServiceReceiveEvent(OrbisSystemServiceEvent* event) { - LOG_ERROR(Lib_SystemService, "(STUBBED) called, event type = {:#x}", (int)event->eventType); + LOG_ERROR(Lib_SystemService, "(STUBBED) called"); if (event == nullptr) { return ORBIS_SYSTEM_SERVICE_ERROR_PARAMETER; } diff --git a/src/core/libraries/system/systemservice.h b/src/core/libraries/system/systemservice.h index 56583c97e..cdd3c15e8 100644 --- a/src/core/libraries/system/systemservice.h +++ b/src/core/libraries/system/systemservice.h @@ -12,114 +12,81 @@ class SymbolsResolver; namespace Libraries::SystemService { -enum OrbisSystemServiceParamId { - ORBIS_SYSTEM_SERVICE_PARAM_ID_LANG = 1, - ORBIS_SYSTEM_SERVICE_PARAM_ID_DATE_FORMAT = 2, - ORBIS_SYSTEM_SERVICE_PARAM_ID_TIME_FORMAT = 3, - ORBIS_SYSTEM_SERVICE_PARAM_ID_TIME_ZONE = 4, - ORBIS_SYSTEM_SERVICE_PARAM_ID_SUMMERTIME = 5, - ORBIS_SYSTEM_SERVICE_PARAM_ID_SYSTEM_NAME = 6, - ORBIS_SYSTEM_SERVICE_PARAM_ID_GAME_PARENTAL_LEVEL = 7, - ORBIS_SYSTEM_SERVICE_PARAM_ID_ENTER_BUTTON_ASSIGN = 1000 +enum class OrbisSystemServiceParamId { + Lang = 1, + DateFormat = 2, + TimeFormat = 3, + TimeZone = 4, + Summertime = 5, + SystemName = 6, + GameParentalLevel = 7, + EnterButtonAssign = 1000, }; -enum OrbisSystemParamDateFormat { - ORBIS_SYSTEM_PARAM_DATE_FORMAT_YYYYMMDD = 0, - ORBIS_SYSTEM_PARAM_DATE_FORMAT_DDMMYYYY = 1, - ORBIS_SYSTEM_PARAM_DATE_FORMAT_MMDDYYYY = 2 +enum class OrbisSystemParamDateFormat { + FmtYYYYMMDD = 0, + FmtDDMMYYYY = 1, + FmtMMDDYYYY = 2, }; -enum OrbisSystemParamTimeFormat { - ORBIS_SYSTEM_PARAM_TIME_FORMAT_12HOUR = 0, - ORBIS_SYSTEM_PARAM_TIME_FORMAT_24HOUR = 1 +enum class OrbisSystemParamTimeFormat { + Fmt12Hour = 0, + Fmt24Hour = 1, }; -enum OrbisSystemParamGameParentalLevel { - ORBIS_SYSTEM_PARAM_GAME_PARENTAL_OFF = 0, - ORBIS_SYSTEM_PARAM_GAME_PARENTAL_LEVEL01 = 1, - ORBIS_SYSTEM_PARAM_GAME_PARENTAL_LEVEL02 = 2, - ORBIS_SYSTEM_PARAM_GAME_PARENTAL_LEVEL03 = 3, - ORBIS_SYSTEM_PARAM_GAME_PARENTAL_LEVEL04 = 4, - ORBIS_SYSTEM_PARAM_GAME_PARENTAL_LEVEL05 = 5, - ORBIS_SYSTEM_PARAM_GAME_PARENTAL_LEVEL06 = 6, - ORBIS_SYSTEM_PARAM_GAME_PARENTAL_LEVEL07 = 7, - ORBIS_SYSTEM_PARAM_GAME_PARENTAL_LEVEL08 = 8, - ORBIS_SYSTEM_PARAM_GAME_PARENTAL_LEVEL09 = 9, - ORBIS_SYSTEM_PARAM_GAME_PARENTAL_LEVEL10 = 10, - ORBIS_SYSTEM_PARAM_GAME_PARENTAL_LEVEL11 = 11 +enum class OrbisSystemParamGameParentalLevel { + Off = 0, + Level01 = 1, + Level02 = 2, + Level03 = 3, + Level04 = 4, + Level05 = 5, + Level06 = 6, + Level07 = 7, + Level08 = 8, + Level09 = 9, + Level10 = 10, + Level11 = 11, }; -enum OrbisSystemParamEnterButtonAssign { - ORBIS_SYSTEM_PARAM_ENTER_BUTTON_ASSIGN_CIRCLE = 0, - ORBIS_SYSTEM_PARAM_ENTER_BUTTON_ASSIGN_CROSS = 1 +enum class OrbisSystemParamEnterButtonAssign { + Circle = 0, + Cross = 1, }; -enum OrbisSystemParamLanguage { - ORBIS_SYSTEM_PARAM_LANG_JAPANESE = 0, - ORBIS_SYSTEM_PARAM_LANG_ENGLISH_US = 1, - ORBIS_SYSTEM_PARAM_LANG_FRENCH = 2, - ORBIS_SYSTEM_PARAM_LANG_SPANISH = 3, - ORBIS_SYSTEM_PARAM_LANG_GERMAN = 4, - ORBIS_SYSTEM_PARAM_LANG_ITALIAN = 5, - ORBIS_SYSTEM_PARAM_LANG_DUTCH = 6, - ORBIS_SYSTEM_PARAM_LANG_PORTUGUESE_PT = 7, - ORBIS_SYSTEM_PARAM_LANG_RUSSIAN = 8, - ORBIS_SYSTEM_PARAM_LANG_KOREAN = 9, - ORBIS_SYSTEM_PARAM_LANG_CHINESE_T = 10, - ORBIS_SYSTEM_PARAM_LANG_CHINESE_S = 11, - ORBIS_SYSTEM_PARAM_LANG_FINNISH = 12, - ORBIS_SYSTEM_PARAM_LANG_SWEDISH = 13, - ORBIS_SYSTEM_PARAM_LANG_DANISH = 14, - ORBIS_SYSTEM_PARAM_LANG_NORWEGIAN = 15, - ORBIS_SYSTEM_PARAM_LANG_POLISH = 16, - ORBIS_SYSTEM_PARAM_LANG_PORTUGUESE_BR = 17, - ORBIS_SYSTEM_PARAM_LANG_ENGLISH_GB = 18, - ORBIS_SYSTEM_PARAM_LANG_TURKISH = 19, - ORBIS_SYSTEM_PARAM_LANG_SPANISH_LA = 20, - ORBIS_SYSTEM_PARAM_LANG_ARABIC = 21, - ORBIS_SYSTEM_PARAM_LANG_FRENCH_CA = 22, - ORBIS_SYSTEM_PARAM_LANG_CZECH = 23, - ORBIS_SYSTEM_PARAM_LANG_HUNGARIAN = 24, - ORBIS_SYSTEM_PARAM_LANG_GREEK = 25, - ORBIS_SYSTEM_PARAM_LANG_ROMANIAN = 26, - ORBIS_SYSTEM_PARAM_LANG_THAI = 27, - ORBIS_SYSTEM_PARAM_LANG_VIETNAMESE = 28, - ORBIS_SYSTEM_PARAM_LANG_INDONESIAN = 29 -}; - -enum OrbisSystemServiceEventType { - ORBIS_SYSTEM_SERVICE_EVENT_INVALID = -1, - ORBIS_SYSTEM_SERVICE_EVENT_ON_RESUME = 0x10000000, - ORBIS_SYSTEM_SERVICE_EVENT_GAME_LIVE_STREAMING_STATUS_UPDATE = 0x10000001, - ORBIS_SYSTEM_SERVICE_EVENT_SESSION_INVITATION = 0x10000002, - ORBIS_SYSTEM_SERVICE_EVENT_ENTITLEMENT_UPDATE = 0x10000003, - ORBIS_SYSTEM_SERVICE_EVENT_GAME_CUSTOM_DATA = 0x10000004, - ORBIS_SYSTEM_SERVICE_EVENT_DISPLAY_SAFE_AREA_UPDATE = 0x10000005, - ORBIS_SYSTEM_SERVICE_EVENT_URL_OPEN = 0x10000006, - ORBIS_SYSTEM_SERVICE_EVENT_LAUNCH_APP = 0x10000007, - ORBIS_SYSTEM_SERVICE_EVENT_APP_LAUNCH_LINK = 0x10000008, - ORBIS_SYSTEM_SERVICE_EVENT_ADDCONTENT_INSTALL = 0x10000009, - ORBIS_SYSTEM_SERVICE_EVENT_RESET_VR_POSITION = 0x1000000a, - ORBIS_SYSTEM_SERVICE_EVENT_JOIN_EVENT = 0x1000000b, - ORBIS_SYSTEM_SERVICE_EVENT_PLAYGO_LOCUS_UPDATE = 0x1000000c, - ORBIS_SYSTEM_SERVICE_EVENT_PLAY_TOGETHER_HOST = 0x1000000d, - ORBIS_SYSTEM_SERVICE_EVENT_SERVICE_ENTITLEMENT_UPDATE = 0x1000000e, - ORBIS_SYSTEM_SERVICE_EVENT_EYE_TO_EYE_DISTANCE_UPDATE = 0x1000000f, - ORBIS_SYSTEM_SERVICE_EVENT_JOIN_MATCH_EVENT = 0x10000010, - ORBIS_SYSTEM_SERVICE_EVENT_PLAY_TOGETHER_HOST_A = 0x10000011, - ORBIS_SYSTEM_SERVICE_EVENT_WEBBROWSER_CLOSED = 0x10000012, - ORBIS_SYSTEM_SERVICE_EVENT_CONTROLLER_SETTINGS_CLOSED = 0x10000013, - ORBIS_SYSTEM_SERVICE_EVENT_JOIN_TEAM_ON_TEAM_MATCH_EVENT = 0x10000014, - ORBIS_SYSTEM_SERVICE_EVENT_OPEN_SHARE_MENU = 0x30000000 +enum class OrbisSystemServiceEventType { + Invalid = -1, + OnResume = 0x10000000, + GameLiveStreamingStatusUpdate = 0x10000001, + SessionInvitation = 0x10000002, + EntitlementUpdate = 0x10000003, + GameCustomData = 0x10000004, + DisplaySafeAreaUpdate = 0x10000005, + UrlOpen = 0x10000006, + LaunchApp = 0x10000007, + AppLaunchLink = 0x10000008, + AddcontentInstall = 0x10000009, + ResetVrPosition = 0x1000000a, + JoinEvent = 0x1000000b, + PlaygoLocusUpdate = 0x1000000c, + PlayTogetherHost = 0x1000000d, + ServiceEntitlementUpdate = 0x1000000e, + EyeToEyeDistanceUpdate = 0x1000000f, + JoinMatchEvent = 0x10000010, + PlayTogetherHostA = 0x10000011, + WebBrowserClosed = 0x10000012, + ControllerSettingsClosed = 0x10000013, + JoinTeamOnTeamMatchEvent = 0x10000014, + OpenShareMenu = 0x30000000 }; struct OrbisSystemServiceStatus { - s32 eventNum; - bool isSystemUiOverlaid; - bool isInBackgroundExecution; - bool isCpuMode7CpuNormal; - bool isGameLiveStreamingOnAir; - bool isOutOfVrPlayArea; + s32 event_num; + bool is_system_ui_overlaid; + bool is_in_background_execution; + bool is_cpu_mode7_cpu_normal; + bool is_game_live_streaming_on_air; + bool is_out_of_vr_play_area; u8 reserved[]; }; @@ -129,36 +96,36 @@ struct OrbisSystemServiceDisplaySafeAreaInfo { }; struct OrbisSystemServiceEvent { - OrbisSystemServiceEventType eventType; + OrbisSystemServiceEventType event_type; union { char param[8192]; struct { char source[1024]; char url[4096]; - } urlOpen; + } url_open; struct { u32 size; u8 arg[8188]; - } launchApp; + } launch_app; struct { u32 size; u8 arg[2020]; - } appLaunchLink; + } app_launch_link; struct { - s32 userId; - char eventId[37]; - char bootArgument[7169]; - } joinEvent; + s32 user_id; + char event_id[37]; + char boot_argument[7169]; + } join_event; struct { - s32 userId; - u32 npServiceLabel; + s32 user_id; + u32 np_service_label; u8 reserved[8184]; - } serviceEntitlementUpdate; + } service_entitlement_update; struct { - s32 userId; - u32 npServiceLabel; + s32 user_id; + u32 np_service_label; u8 reserved[8184]; - } unifiedEntitlementUpdate; + } unified_entitlement_update; u8 reserved[8192]; }; }; @@ -537,7 +504,7 @@ int PS4_SYSV_ABI sceSystemServiceNavigateToAnotherApp(); int PS4_SYSV_ABI sceSystemServiceNavigateToGoBack(); int PS4_SYSV_ABI sceSystemServiceNavigateToGoBackWithValue(); int PS4_SYSV_ABI sceSystemServiceNavigateToGoHome(); -s32 PS4_SYSV_ABI sceSystemServiceParamGetInt(int param_id, int* value); +s32 PS4_SYSV_ABI sceSystemServiceParamGetInt(OrbisSystemServiceParamId param_id, int* value); int PS4_SYSV_ABI sceSystemServiceParamGetString(); int PS4_SYSV_ABI sceSystemServicePowerTick(); int PS4_SYSV_ABI sceSystemServiceRaiseExceptionLocalProcess(); diff --git a/src/core/libraries/system/systemservice_error.h b/src/core/libraries/system/systemservice_error.h new file mode 100644 index 000000000..ca59fab65 --- /dev/null +++ b/src/core/libraries/system/systemservice_error.h @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// SystemService library +constexpr int ORBIS_SYSTEM_SERVICE_ERROR_PARAMETER = 0x80A10003; +constexpr int ORBIS_SYSTEM_SERVICE_ERROR_NO_EVENT = 0x80A10004; diff --git a/src/core/libraries/system/userservice.cpp b/src/core/libraries/system/userservice.cpp index 140ca8921..e1f9f75e8 100644 --- a/src/core/libraries/system/userservice.cpp +++ b/src/core/libraries/system/userservice.cpp @@ -4,9 +4,9 @@ #include "common/config.h" #include "common/logging/log.h" -#include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "core/libraries/system/userservice.h" +#include "core/libraries/system/userservice_error.h" namespace Libraries::UserService { @@ -112,7 +112,7 @@ s32 PS4_SYSV_ABI sceUserServiceGetEvent(OrbisUserServiceEvent* event) { if (!logged_in) { logged_in = true; - event->event = SCE_USER_SERVICE_EVENT_TYPE_LOGIN; + event->event = OrbisUserServiceEventType::Login; event->userId = 1; return ORBIS_OK; } @@ -1041,14 +1041,14 @@ int PS4_SYSV_ABI sceUserServiceGetTraditionalChineseInputType() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceUserServiceGetUserColor(int user_id, int* color) { +s32 PS4_SYSV_ABI sceUserServiceGetUserColor(int user_id, OrbisUserServiceUserColor* color) { // TODO fix me better LOG_INFO(Lib_UserService, "called user_id = {}", user_id); if (color == nullptr) { LOG_ERROR(Lib_UserService, "color is null"); return ORBIS_USER_SERVICE_ERROR_INVALID_ARGUMENT; } - *color = ORBIS_USER_SERVICE_USER_COLOR_BLUE; + *color = OrbisUserServiceUserColor::Blue; return ORBIS_OK; } diff --git a/src/core/libraries/system/userservice.h b/src/core/libraries/system/userservice.h index 5bb1fd043..66ac2b69d 100644 --- a/src/core/libraries/system/userservice.h +++ b/src/core/libraries/system/userservice.h @@ -40,16 +40,16 @@ struct OrbisUserServiceRegisteredUserIdList { OrbisUserServiceUserId userId[ORBIS_USER_SERVICE_MAX_REGISTER_USERS]; }; -enum OrbisUserServiceUserColor { - ORBIS_USER_SERVICE_USER_COLOR_BLUE = 0, - ORBIS_USER_SERVICE_USER_COLOR_RED = 1, - ORBIS_USER_SERVICE_USER_COLOR_GREEN = 2, - ORBIS_USER_SERVICE_USER_COLOR_PINK = 3, +enum class OrbisUserServiceUserColor { + Blue = 0, + Red = 1, + Green = 2, + Pink = 3, }; -enum OrbisUserServiceEventType { - SCE_USER_SERVICE_EVENT_TYPE_LOGIN = 0, // Login event - SCE_USER_SERVICE_EVENT_TYPE_LOGOUT = 1, // Logout event +enum class OrbisUserServiceEventType { + Login = 0, // Login event + Logout = 1, // Logout event }; struct OrbisUserServiceEvent { @@ -258,7 +258,7 @@ int PS4_SYSV_ABI sceUserServiceGetTopMenuLimitItem(); int PS4_SYSV_ABI sceUserServiceGetTopMenuNotificationFlag(); int PS4_SYSV_ABI sceUserServiceGetTopMenuTutorialFlag(); int PS4_SYSV_ABI sceUserServiceGetTraditionalChineseInputType(); -s32 PS4_SYSV_ABI sceUserServiceGetUserColor(int user_id, int* color); +s32 PS4_SYSV_ABI sceUserServiceGetUserColor(int user_id, OrbisUserServiceUserColor* color); int PS4_SYSV_ABI sceUserServiceGetUserGroupName(); int PS4_SYSV_ABI sceUserServiceGetUserGroupNameList(); int PS4_SYSV_ABI sceUserServiceGetUserGroupNum(); diff --git a/src/core/libraries/system/userservice_error.h b/src/core/libraries/system/userservice_error.h new file mode 100644 index 000000000..40921df9f --- /dev/null +++ b/src/core/libraries/system/userservice_error.h @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// UserService library +constexpr int ORBIS_USER_SERVICE_ERROR_INTERNAL = 0x80960001; +constexpr int ORBIS_USER_SERVICE_ERROR_NOT_INITIALIZED = 0x80960002; +constexpr int ORBIS_USER_SERVICE_ERROR_ALREADY_INITIALIZED = 0x80960003; +constexpr int ORBIS_USER_SERVICE_ERROR_NO_MEMORY = 0x80960004; +constexpr int ORBIS_USER_SERVICE_ERROR_INVALID_ARGUMENT = 0x80960005; +constexpr int ORBIS_USER_SERVICE_ERROR_OPERATION_NOT_SUPPORTED = 0x80960006; +constexpr int ORBIS_USER_SERVICE_ERROR_NO_EVENT = 0x80960007; +constexpr int ORBIS_USER_SERVICE_ERROR_NOT_LOGGED_IN = 0x80960009; +constexpr int ORBIS_USER_SERVICE_ERROR_BUFFER_TOO_SHORT = 0x8096000A; diff --git a/src/core/libraries/videodec/videodec.cpp b/src/core/libraries/videodec/videodec.cpp index 8d6ea4988..6c9a9b8c0 100644 --- a/src/core/libraries/videodec/videodec.cpp +++ b/src/core/libraries/videodec/videodec.cpp @@ -1,12 +1,11 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "videodec.h" - #include "common/logging/log.h" -#include "core/libraries/error_codes.h" #include "core/libraries/libs.h" -#include "videodec_impl.h" +#include "core/libraries/videodec/videodec.h" +#include "core/libraries/videodec/videodec_error.h" +#include "core/libraries/videodec/videodec_impl.h" namespace Libraries::Videodec { @@ -134,4 +133,4 @@ void RegisterlibSceVideodec(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("f8AgDv-1X8A", "libSceVideodec", 1, "libSceVideodec", 1, 1, sceVideodecReset); }; -} // namespace Libraries::Videodec \ No newline at end of file +} // namespace Libraries::Videodec diff --git a/src/core/libraries/videodec/videodec2.cpp b/src/core/libraries/videodec/videodec2.cpp index fe2b82bea..0bd68afbd 100644 --- a/src/core/libraries/videodec/videodec2.cpp +++ b/src/core/libraries/videodec/videodec2.cpp @@ -1,13 +1,12 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "videodec2.h" -#include "videodec2_impl.h" - #include "common/assert.h" #include "common/logging/log.h" -#include "core/libraries/error_codes.h" #include "core/libraries/libs.h" +#include "core/libraries/videodec/videodec2.h" +#include "core/libraries/videodec/videodec2_impl.h" +#include "core/libraries/videodec/videodec_error.h" namespace Libraries::Vdec2 { @@ -197,4 +196,4 @@ void RegisterlibSceVdec2(Core::Loader::SymbolsResolver* sym) { sceVideodec2GetPictureInfo); } -} // namespace Libraries::Vdec2 \ No newline at end of file +} // namespace Libraries::Vdec2 diff --git a/src/core/libraries/videodec/videodec2.h b/src/core/libraries/videodec/videodec2.h index 9b230cc54..4617e1c20 100644 --- a/src/core/libraries/videodec/videodec2.h +++ b/src/core/libraries/videodec/videodec2.h @@ -15,7 +15,7 @@ namespace Libraries::Vdec2 { class VdecDecoder; using OrbisVideodec2Decoder = VdecDecoder*; -typedef void* OrbisVideodec2ComputeQueue; +using OrbisVideodec2ComputeQueue = void*; struct OrbisVideodec2DecoderConfigInfo { u64 thisSize; diff --git a/src/core/libraries/videodec/videodec2_impl.cpp b/src/core/libraries/videodec/videodec2_impl.cpp index 37270fa8c..138d78af3 100644 --- a/src/core/libraries/videodec/videodec2_impl.cpp +++ b/src/core/libraries/videodec/videodec2_impl.cpp @@ -3,21 +3,11 @@ #include "videodec2_impl.h" -#include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" -#include "core/libraries/error_codes.h" +#include "core/libraries/videodec/videodec_error.h" -// The av_err2str macro in libavutil/error.h does not play nice with C++ -#ifdef av_err2str -#undef av_err2str -#include -av_always_inline std::string av_err2string(int errnum) { - char errbuf[AV_ERROR_MAX_STRING_SIZE]; - return av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, errnum); -} -#define av_err2str(err) av_err2string(err).c_str() -#endif // av_err2str +#include "common/support/avdec.h" namespace Libraries::Vdec2 { diff --git a/src/core/libraries/videodec/videodec_error.h b/src/core/libraries/videodec/videodec_error.h new file mode 100644 index 000000000..4e0066e5e --- /dev/null +++ b/src/core/libraries/videodec/videodec_error.h @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// Videodec library +constexpr int ORBIS_VIDEODEC_ERROR_API_FAIL = 0x80C10000; +constexpr int ORBIS_VIDEODEC_ERROR_CODEC_TYPE = 0x80C10001; +constexpr int ORBIS_VIDEODEC_ERROR_STRUCT_SIZE = 0x80C10002; +constexpr int ORBIS_VIDEODEC_ERROR_HANDLE = 0x80C10003; +constexpr int ORBIS_VIDEODEC_ERROR_CPU_MEMORY_SIZE = 0x80C10004; +constexpr int ORBIS_VIDEODEC_ERROR_CPU_MEMORY_POINTER = 0x80C10005; +constexpr int ORBIS_VIDEODEC_ERROR_CPU_GPU_MEMORY_SIZE = 0x80C10006; +constexpr int ORBIS_VIDEODEC_ERROR_CPU_GPU_MEMORY_POINTER = 0x80C10007; +constexpr int ORBIS_VIDEODEC_ERROR_SHADER_CONTEXT_POINTER = 0x80C10008; +constexpr int ORBIS_VIDEODEC_ERROR_AU_SIZE = 0x80C10009; +constexpr int ORBIS_VIDEODEC_ERROR_AU_POINTER = 0x80C1000A; +constexpr int ORBIS_VIDEODEC_ERROR_FRAME_BUFFER_SIZE = 0x80C1000B; +constexpr int ORBIS_VIDEODEC_ERROR_FRAME_BUFFER_POINTER = 0x80C1000C; +constexpr int ORBIS_VIDEODEC_ERROR_FRAME_BUFFER_ALIGNMENT = 0x80C1000D; +constexpr int ORBIS_VIDEODEC_ERROR_CONFIG_INFO = 0x80C1000E; +constexpr int ORBIS_VIDEODEC_ERROR_ARGUMENT_POINTER = 0x80C1000F; +constexpr int ORBIS_VIDEODEC_ERROR_NEW_SEQUENCE = 0x80C10010; +constexpr int ORBIS_VIDEODEC_ERROR_DECODE_AU = 0x80C10011; +constexpr int ORBIS_VIDEODEC_ERROR_MISMATCH_SPEC = 0x80C10012; +constexpr int ORBIS_VIDEODEC_ERROR_INVALID_SEQUENCE = 0x80C10013; +constexpr int ORBIS_VIDEODEC_ERROR_FATAL_STREAM = 0x80C10014; +constexpr int ORBIS_VIDEODEC_ERROR_FATAL_STATE = 0x80C10015; + +// Videodec2 library +constexpr int ORBIS_VIDEODEC2_ERROR_API_FAIL = 0x811D0100; +constexpr int ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE = 0x811D0101; +constexpr int ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER = 0x811D0102; +constexpr int ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE = 0x811D0103; +constexpr int ORBIS_VIDEODEC2_ERROR_MEMORY_SIZE = 0x811D0104; +constexpr int ORBIS_VIDEODEC2_ERROR_MEMORY_POINTER = 0x811D0105; +constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_BUFFER_SIZE = 0x811D0106; +constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_BUFFER_POINTER = 0x811D0107; +constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_BUFFER_ALIGNMENT = 0x811D0108; +constexpr int ORBIS_VIDEODEC2_ERROR_NOT_ONION_MEMORY = 0x811D0109; +constexpr int ORBIS_VIDEODEC2_ERROR_NOT_GARLIC_MEMORY = 0x811D010A; +constexpr int ORBIS_VIDEODEC2_ERROR_NOT_DIRECT_MEMORY = 0x811D010B; +constexpr int ORBIS_VIDEODEC2_ERROR_MEMORY_INFO = 0x811D010C; +constexpr int ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_SIZE = 0x811D010D; +constexpr int ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_POINTER = 0x811D010E; +constexpr int ORBIS_VIDEODEC2_ERROR_OUTPUT_INFO = 0x811D010F; +constexpr int ORBIS_VIDEODEC2_ERROR_COMPUTE_QUEUE = 0x811D0110; +constexpr int ORBIS_VIDEODEC2_ERROR_FATAL_STATE = 0x811D0111; +constexpr int ORBIS_VIDEODEC2_ERROR_PRESET_VALUE = 0x811D0112; +constexpr int ORBIS_VIDEODEC2_ERROR_CONFIG_INFO = 0x811D0200; +constexpr int ORBIS_VIDEODEC2_ERROR_COMPUTE_PIPE_ID = 0x811D0201; +constexpr int ORBIS_VIDEODEC2_ERROR_COMPUTE_QUEUE_ID = 0x811D0202; +constexpr int ORBIS_VIDEODEC2_ERROR_RESOURCE_TYPE = 0x811D0203; +constexpr int ORBIS_VIDEODEC2_ERROR_CODEC_TYPE = 0x811D0204; +constexpr int ORBIS_VIDEODEC2_ERROR_PROFILE_LEVEL = 0x811D0205; +constexpr int ORBIS_VIDEODEC2_ERROR_PIPELINE_DEPTH = 0x811D0206; +constexpr int ORBIS_VIDEODEC2_ERROR_AFFINITY_MASK = 0x811D0207; +constexpr int ORBIS_VIDEODEC2_ERROR_THREAD_PRIORITY = 0x811D0208; +constexpr int ORBIS_VIDEODEC2_ERROR_DPB_FRAME_COUNT = 0x811D0209; +constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_WIDTH_HEIGHT = 0x811D020A; +constexpr int ORBIS_VIDEODEC2_ERROR_EXTRA_CONFIG_INFO = 0x811D020B; +constexpr int ORBIS_VIDEODEC2_ERROR_NEW_SEQUENCE = 0x811D0300; +constexpr int ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT = 0x811D0301; +constexpr int ORBIS_VIDEODEC2_ERROR_OVERSIZE_DECODE = 0x811D0302; +constexpr int ORBIS_VIDEODEC2_ERROR_INVALID_SEQUENCE = 0x811D0303; +constexpr int ORBIS_VIDEODEC2_ERROR_FATAL_STREAM = 0x811D0304; diff --git a/src/core/libraries/videodec/videodec_impl.cpp b/src/core/libraries/videodec/videodec_impl.cpp index a244c4a61..b5f72e9ce 100644 --- a/src/core/libraries/videodec/videodec_impl.cpp +++ b/src/core/libraries/videodec/videodec_impl.cpp @@ -6,18 +6,9 @@ #include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" -#include "core/libraries/error_codes.h" +#include "core/libraries/videodec/videodec_error.h" -// The av_err2str macro in libavutil/error.h does not play nice with C++ -#ifdef av_err2str -#undef av_err2str -#include -av_always_inline std::string av_err2string(int errnum) { - char errbuf[AV_ERROR_MAX_STRING_SIZE]; - return av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, errnum); -} -#define av_err2str(err) av_err2string(err).c_str() -#endif // av_err2str +#include "common/support/avdec.h" namespace Libraries::Videodec { diff --git a/src/core/libraries/videoout/driver.cpp b/src/core/libraries/videoout/driver.cpp index e2fd00028..f6c25afe3 100644 --- a/src/core/libraries/videoout/driver.cpp +++ b/src/core/libraries/videoout/driver.cpp @@ -2,20 +2,18 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include -#include #include "common/assert.h" #include "common/config.h" #include "common/debug.h" #include "common/thread.h" #include "core/debug_state.h" -#include "core/libraries/error_codes.h" -#include "core/libraries/kernel/time_management.h" +#include "core/libraries/kernel/time.h" #include "core/libraries/videoout/driver.h" -#include "core/platform.h" -#include "video_core/renderer_vulkan/renderer_vulkan.h" +#include "core/libraries/videoout/videoout_error.h" +#include "video_core/renderer_vulkan/vk_presenter.h" -extern std::unique_ptr renderer; +extern std::unique_ptr presenter; extern std::unique_ptr liverpool; namespace Libraries::VideoOut { @@ -43,10 +41,10 @@ constexpr u32 PixelFormatBpp(PixelFormat pixel_format) { } VideoOutDriver::VideoOutDriver(u32 width, u32 height) { - main_port.resolution.fullWidth = width; - main_port.resolution.fullHeight = height; - main_port.resolution.paneWidth = width; - main_port.resolution.paneHeight = height; + main_port.resolution.full_width = width; + main_port.resolution.full_height = height; + main_port.resolution.pane_width = width; + main_port.resolution.pane_height = height; present_thread = std::jthread([&](std::stop_token token) { PresentThread(token); }); } @@ -136,7 +134,7 @@ int VideoOutDriver::RegisterBuffers(VideoOutPort* port, s32 startIndex, void* co .address_right = 0, }; - renderer->RegisterVideoOutSurface(group, address); + presenter->RegisterVideoOutSurface(group, address); LOG_INFO(Lib_VideoOut, "buffers[{}] = {:#x}", i + startIndex, address); } @@ -164,9 +162,9 @@ int VideoOutDriver::UnregisterBuffers(VideoOutPort* port, s32 attributeIndex) { void VideoOutDriver::Flip(const Request& req) { // Whatever the game is rendering show splash if it is active - if (!renderer->ShowSplash(req.frame)) { + if (!presenter->ShowSplash(req.frame)) { // Present the frame. - renderer->Present(req.frame); + presenter->Present(req.frame); } // Update flip status. @@ -175,20 +173,21 @@ void VideoOutDriver::Flip(const Request& req) { std::unique_lock lock{port->port_mutex}; auto& flip_status = port->flip_status; flip_status.count++; - flip_status.processTime = Libraries::Kernel::sceKernelGetProcessTime(); + flip_status.process_time = Libraries::Kernel::sceKernelGetProcessTime(); flip_status.tsc = Libraries::Kernel::sceKernelReadTsc(); - flip_status.flipArg = req.flip_arg; - flip_status.currentBuffer = req.index; + flip_status.flip_arg = req.flip_arg; + flip_status.current_buffer = req.index; if (req.eop) { - --flip_status.gcQueueNum; + --flip_status.gc_queue_num; } - --flip_status.flipPendingNum; + --flip_status.flip_pending_num; } // Trigger flip events for the port. for (auto& event : port->flip_events) { if (event != nullptr) { - event->TriggerEvent(SCE_VIDEO_OUT_EVENT_FLIP, Kernel::SceKernelEvent::Filter::VideoOut, + event->TriggerEvent(u64(OrbisVideoOutEventId::Flip), + Kernel::SceKernelEvent::Filter::VideoOut, reinterpret_cast(req.flip_arg)); } } @@ -201,24 +200,27 @@ void VideoOutDriver::Flip(const Request& req) { } void VideoOutDriver::DrawBlankFrame() { - const auto empty_frame = renderer->PrepareBlankFrame(false); - renderer->Present(empty_frame); + if (presenter->ShowSplash(nullptr)) { + return; + } + const auto empty_frame = presenter->PrepareBlankFrame(false); + presenter->Present(empty_frame); } bool VideoOutDriver::SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg, bool is_eop /*= false*/) { { std::unique_lock lock{port->port_mutex}; - if (index != -1 && port->flip_status.flipPendingNum >= port->NumRegisteredBuffers()) { + if (index != -1 && port->flip_status.flip_pending_num >= port->NumRegisteredBuffers()) { LOG_ERROR(Lib_VideoOut, "Flip queue is full"); return false; } if (is_eop) { - ++port->flip_status.gcQueueNum; + ++port->flip_status.gc_queue_num; } - ++port->flip_status.flipPendingNum; // integral GPU and CPU pending flips counter - port->flip_status.submitTsc = Libraries::Kernel::sceKernelReadTsc(); + ++port->flip_status.flip_pending_num; // integral GPU and CPU pending flips counter + port->flip_status.submit_tsc = Libraries::Kernel::sceKernelReadTsc(); } if (!is_eop) { @@ -226,7 +228,7 @@ bool VideoOutDriver::SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg, // point VO surface is ready to be presented, and we will need have an actual state of // Vulkan image at the time of frame presentation. liverpool->SendCommand([=, this]() { - renderer->FlushDraw(); + presenter->FlushDraw(); SubmitFlipInternal(port, index, flip_arg, is_eop); }); } else { @@ -240,11 +242,11 @@ void VideoOutDriver::SubmitFlipInternal(VideoOutPort* port, s32 index, s64 flip_ bool is_eop /*= false*/) { Vulkan::Frame* frame; if (index == -1) { - frame = renderer->PrepareBlankFrame(is_eop); + frame = presenter->PrepareBlankFrame(is_eop); } else { const auto& buffer = port->buffer_slots[index]; const auto& group = port->groups[buffer.group_index]; - frame = renderer->PrepareFrame(group, buffer.address_left, is_eop); + frame = presenter->PrepareFrame(group, buffer.address_left, is_eop); } std::scoped_lock lock{mutex}; @@ -296,9 +298,9 @@ void VideoOutDriver::PresentThread(std::stop_token token) { { // Needs lock here as can be concurrently read by `sceVideoOutGetVblankStatus` - std::unique_lock lock{main_port.vo_mutex}; + std::scoped_lock lock{main_port.vo_mutex}; vblank_status.count++; - vblank_status.processTime = Libraries::Kernel::sceKernelGetProcessTime(); + vblank_status.process_time = Libraries::Kernel::sceKernelGetProcessTime(); vblank_status.tsc = Libraries::Kernel::sceKernelReadTsc(); main_port.vblank_cv.notify_all(); } @@ -306,7 +308,7 @@ void VideoOutDriver::PresentThread(std::stop_token token) { // Trigger flip events for the port. for (auto& event : main_port.vblank_events) { if (event != nullptr) { - event->TriggerEvent(SCE_VIDEO_OUT_EVENT_VBLANK, + event->TriggerEvent(u64(OrbisVideoOutEventId::Vblank), Kernel::SceKernelEvent::Filter::VideoOut, nullptr); } } diff --git a/src/core/libraries/videoout/driver.h b/src/core/libraries/videoout/driver.h index 2e478b9ee..ec01b621f 100644 --- a/src/core/libraries/videoout/driver.h +++ b/src/core/libraries/videoout/driver.h @@ -74,7 +74,7 @@ struct ServiceThreadParams { class VideoOutDriver { public: - explicit VideoOutDriver(u32 width, u32 height); + VideoOutDriver(u32 width, u32 height); ~VideoOutDriver(); int Open(const ServiceThreadParams* params); diff --git a/src/core/libraries/videoout/video_out.cpp b/src/core/libraries/videoout/video_out.cpp index 31b8a21ca..f36de6ade 100644 --- a/src/core/libraries/videoout/video_out.cpp +++ b/src/core/libraries/videoout/video_out.cpp @@ -4,13 +4,15 @@ #include "common/assert.h" #include "common/config.h" #include "common/logging/log.h" -#include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "core/libraries/system/userservice.h" #include "core/libraries/videoout/driver.h" #include "core/libraries/videoout/video_out.h" -#include "core/loader/symbols_resolver.h" +#include "core/libraries/videoout/videoout_error.h" #include "core/platform.h" +#include "video_core/renderer_vulkan/vk_presenter.h" + +extern std::unique_ptr presenter; namespace Libraries::VideoOut { @@ -48,7 +50,7 @@ s32 PS4_SYSV_ABI sceVideoOutAddFlipEvent(Kernel::SceKernelEqueue eq, s32 handle, } Kernel::EqueueEvent event{}; - event.event.ident = SCE_VIDEO_OUT_EVENT_FLIP; + event.event.ident = u64(OrbisVideoOutEventId::Flip); event.event.filter = Kernel::SceKernelEvent::Filter::VideoOut; // The library only sets EV_ADD but kernel driver forces EV_CLEAR event.event.flags = Kernel::SceKernelEvent::Flags::Clear; @@ -75,7 +77,7 @@ s32 PS4_SYSV_ABI sceVideoOutAddVblankEvent(Kernel::SceKernelEqueue eq, s32 handl } Kernel::EqueueEvent event{}; - event.event.ident = SCE_VIDEO_OUT_EVENT_VBLANK; + event.event.ident = u64(OrbisVideoOutEventId::Vblank); event.event.filter = Kernel::SceKernelEvent::Filter::VideoOut; // The library only sets EV_ADD but kernel driver forces EV_CLEAR event.event.flags = Kernel::SceKernelEvent::Flags::Clear; @@ -115,7 +117,7 @@ s32 PS4_SYSV_ABI sceVideoOutIsFlipPending(s32 handle) { LOG_TRACE(Lib_VideoOut, "called"); auto* port = driver->GetPort(handle); std::unique_lock lock{port->port_mutex}; - s32 pending = port->flip_status.flipPendingNum; + s32 pending = port->flip_status.flip_pending_num; return pending; } @@ -153,7 +155,7 @@ s32 PS4_SYSV_ABI sceVideoOutSubmitFlip(s32 handle, s32 bufferIndex, s32 flipMode int PS4_SYSV_ABI sceVideoOutGetEventId(const Kernel::SceKernelEvent* ev) { if (ev == nullptr) { - return SCE_VIDEO_OUT_ERROR_INVALID_ADDRESS; + return ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS; } if (ev->filter != Kernel::SceKernelEvent::Filter::VideoOut) { return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE; @@ -163,7 +165,7 @@ int PS4_SYSV_ABI sceVideoOutGetEventId(const Kernel::SceKernelEvent* ev) { int PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::SceKernelEvent* ev, int64_t* data) { if (ev == nullptr || data == nullptr) { - return SCE_VIDEO_OUT_ERROR_INVALID_ADDRESS; + return ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS; } if (ev->filter != Kernel::SceKernelEvent::Filter::VideoOut) { return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE; @@ -193,15 +195,16 @@ s32 PS4_SYSV_ABI sceVideoOutGetFlipStatus(s32 handle, FlipStatus* status) { LOG_TRACE(Lib_VideoOut, "count = {}, processTime = {}, tsc = {}, submitTsc = {}, flipArg = {}, gcQueueNum = " "{}, flipPendingNum = {}, currentBuffer = {}", - status->count, status->processTime, status->tsc, status->submitTsc, status->flipArg, - status->gcQueueNum, status->flipPendingNum, status->currentBuffer); + status->count, status->process_time, status->tsc, status->submit_tsc, + status->flip_arg, status->gc_queue_num, status->flip_pending_num, + status->current_buffer); return ORBIS_OK; } s32 PS4_SYSV_ABI sceVideoOutGetVblankStatus(int handle, SceVideoOutVblankStatus* status) { if (status == nullptr) { - return SCE_VIDEO_OUT_ERROR_INVALID_ADDRESS; + return ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS; } auto* port = driver->GetPort(handle); @@ -297,6 +300,28 @@ s32 PS4_SYSV_ABI sceVideoOutWaitVblank(s32 handle) { return ORBIS_OK; } +s32 PS4_SYSV_ABI sceVideoOutColorSettingsSetGamma(SceVideoOutColorSettings* settings, float gamma) { + if (gamma < 0.1f || gamma > 2.0f) { + return ORBIS_VIDEO_OUT_ERROR_INVALID_VALUE; + } + settings->gamma = gamma; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVideoOutAdjustColor(s32 handle, const SceVideoOutColorSettings* settings) { + if (settings == nullptr) { + return ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS; + } + + auto* port = driver->GetPort(handle); + if (!port) { + return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE; + } + + presenter->GetGammaRef() = settings->gamma; + return ORBIS_OK; +} + void RegisterLib(Core::Loader::SymbolsResolver* sym) { driver = std::make_unique(Config::getScreenWidth(), Config::getScreenHeight()); @@ -329,6 +354,10 @@ void RegisterLib(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("U2JJtSqNKZI", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutGetEventId); LIB_FUNCTION("rWUTcKdkUzQ", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutGetEventData); + LIB_FUNCTION("DYhhWbJSeRg", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, + sceVideoOutColorSettingsSetGamma); + LIB_FUNCTION("pv9CI5VC+R0", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, + sceVideoOutAdjustColor); // openOrbis appears to have libSceVideoOut_v1 module libSceVideoOut_v1.1 LIB_FUNCTION("Up36PTk687E", "libSceVideoOut", 1, "libSceVideoOut", 1, 1, sceVideoOutOpen); diff --git a/src/core/libraries/videoout/video_out.h b/src/core/libraries/videoout/video_out.h index 63cd8fede..5af9d550d 100644 --- a/src/core/libraries/videoout/video_out.h +++ b/src/core/libraries/videoout/video_out.h @@ -3,7 +3,7 @@ #pragma once -#include "core/libraries/kernel/event_queues.h" +#include "core/libraries/kernel/equeue.h" #include "core/libraries/videoout/buffer.h" namespace Core::Loader { @@ -40,36 +40,32 @@ constexpr int SCE_VIDEO_OUT_BUFFER_ATTRIBUTE_OPTION_NONE = 0; constexpr int SCE_VIDEO_OUT_BUFFER_ATTRIBUTE_OPTION_VR = 7; constexpr int SCE_VIDEO_OUT_BUFFER_ATTRIBUTE_OPTION_STRICT_COLORIMETRY = 8; -enum SceVideoOutEventId : s16 { - SCE_VIDEO_OUT_EVENT_FLIP = 0, - SCE_VIDEO_OUT_EVENT_VBLANK = 1, - SCE_VIDEO_OUT_EVENT_PRE_VBLANK_START = 2 -}; +enum class OrbisVideoOutEventId : s16 { Flip = 0, Vblank = 1, PreVblankStart = 2 }; -enum AspectRatioMode : s32 { - SCE_VIDEO_OUT_ASPECT_RATIO_16_9 = 0, +enum class AspectRatioMode : s32 { + Ratio16_9 = 0, }; struct FlipStatus { u64 count = 0; - u64 processTime = 0; + u64 process_time = 0; u64 tsc = 0; - s64 flipArg = -1; - u64 submitTsc = 0; + s64 flip_arg = -1; + u64 submit_tsc = 0; u64 reserved0 = 0; - s32 gcQueueNum = 0; - s32 flipPendingNum = 0; - s32 currentBuffer = -1; + s32 gc_queue_num = 0; + s32 flip_pending_num = 0; + s32 current_buffer = -1; u32 reserved1 = 0; }; struct SceVideoOutResolutionStatus { - s32 fullWidth = 1280; - s32 fullHeight = 720; - s32 paneWidth = 1280; - s32 paneHeight = 720; - u64 refreshRate = SCE_VIDEO_OUT_REFRESH_RATE_59_94HZ; - float screenSizeInInch = 50; + s32 full_width = 1280; + s32 full_height = 720; + s32 pane_width = 1280; + s32 pane_height = 720; + u64 refresh_rate = SCE_VIDEO_OUT_REFRESH_RATE_59_94HZ; + float screen_size_in_inch = 50; u16 flags = 0; u16 reserved0 = 0; u32 reserved1[3] = {0}; @@ -77,7 +73,7 @@ struct SceVideoOutResolutionStatus { struct SceVideoOutVblankStatus { u64 count = 0; - u64 processTime = 0; + u64 process_time = 0; u64 tsc = 0; u64 reserved[1] = {0}; u8 flags = 0; @@ -88,6 +84,11 @@ struct SceVideoOutDeviceCapabilityInfo { u64 capability; }; +struct SceVideoOutColorSettings { + float gamma; + u32 reserved[3]; +}; + void PS4_SYSV_ABI sceVideoOutSetBufferAttribute(BufferAttribute* attribute, PixelFormat pixelFormat, u32 tilingMode, u32 aspectRatio, u32 width, u32 height, u32 pitchInPixel); @@ -106,6 +107,8 @@ s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 i 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 sceVideoOutColorSettingsSetGamma(SceVideoOutColorSettings* settings, float gamma); +s32 PS4_SYSV_ABI sceVideoOutAdjustColor(s32 handle, const SceVideoOutColorSettings* settings); // Internal system functions void sceVideoOutGetBufferLabelAddress(s32 handle, uintptr_t* label_addr); diff --git a/src/core/libraries/videoout/videoout_error.h b/src/core/libraries/videoout/videoout_error.h new file mode 100644 index 000000000..b1ed18c92 --- /dev/null +++ b/src/core/libraries/videoout/videoout_error.h @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +// VideoOut library +constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_VALUE = 0x80290001; +constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS = 0x80290002; +constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_PIXEL_FORMAT = 0x80290003; +constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_PITCH = 0x80290004; +constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_RESOLUTION = 0x80290005; +constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_FLIP_MODE = 0x80290006; +constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_TILING_MODE = 0x80290007; +constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_ASPECT_RATIO = 0x80290008; +constexpr int ORBIS_VIDEO_OUT_ERROR_RESOURCE_BUSY = 0x80290009; +constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_INDEX = 0x8029000A; +constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE = 0x8029000B; +constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE = 0x8029000C; +constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT = 0x8029000D; +constexpr int ORBIS_VIDEO_OUT_ERROR_NO_EMPTY_SLOT = 0x8029000F; +constexpr int ORBIS_VIDEO_OUT_ERROR_SLOT_OCCUPIED = 0x80290010; +constexpr int ORBIS_VIDEO_OUT_ERROR_FLIP_QUEUE_FULL = 0x80290012; +constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_MEMORY = 0x80290013; +constexpr int ORBIS_VIDEO_OUT_ERROR_MEMORY_NOT_PHYSICALLY_CONTIGUOUS = 0x80290014; +constexpr int ORBIS_VIDEO_OUT_ERROR_MEMORY_INVALID_ALIGNMENT = 0x80290015; +constexpr int ORBIS_VIDEO_OUT_ERROR_UNSUPPORTED_OUTPUT_MODE = 0x80290016; +constexpr int ORBIS_VIDEO_OUT_ERROR_OVERFLOW = 0x80290017; +constexpr int ORBIS_VIDEO_OUT_ERROR_NO_DEVICE = 0x80290018; +constexpr int ORBIS_VIDEO_OUT_ERROR_UNAVAILABLE_OUTPUT_MODE = 0x80290019; +constexpr int ORBIS_VIDEO_OUT_ERROR_INVALID_OPTION = 0x8029001A; +constexpr int ORBIS_VIDEO_OUT_ERROR_UNKNOWN = 0x802900FE; +constexpr int ORBIS_VIDEO_OUT_ERROR_FATAL = 0x802900FF; +constexpr int ORBIS_VIDEO_OUT_ERROR_ENOMEM = 0x8029100C; diff --git a/src/core/linker.cpp b/src/core/linker.cpp index 2d7865c33..9cf4198ae 100644 --- a/src/core/linker.cpp +++ b/src/core/linker.cpp @@ -11,27 +11,21 @@ #include "common/thread.h" #include "core/aerolib/aerolib.h" #include "core/aerolib/stubs.h" -#include "core/cpu_patches.h" -#include "core/libraries/kernel/memory_management.h" -#include "core/libraries/kernel/thread_management.h" +#include "core/libraries/kernel/memory.h" +#include "core/libraries/kernel/threads.h" #include "core/linker.h" #include "core/memory.h" #include "core/tls.h" -#include "core/virtual_memory.h" -#include "debug_state.h" namespace Core { -using ExitFunc = PS4_SYSV_ABI void (*)(); - static PS4_SYSV_ABI void ProgramExitFunc() { - fmt::print("exit function called\n"); + LOG_ERROR(Core_Linker, "Exit function called"); } #ifdef ARCH_X86_64 -static PS4_SYSV_ABI void RunMainEntry(VAddr addr, EntryParams* params, ExitFunc exit_func) { - // reinterpret_cast(addr)(params, exit_func); // can't be used, stack has to have - // a specific layout +static PS4_SYSV_ABI void* RunMainEntry [[noreturn]] (EntryParams* params) { + // Start shared library modules asm volatile("andq $-16, %%rsp\n" // Align to 16 bytes "subq $8, %%rsp\n" // videoout_basic expects the stack to be misaligned @@ -47,8 +41,9 @@ static PS4_SYSV_ABI void RunMainEntry(VAddr addr, EntryParams* params, ExitFunc "jmp *%0\n" // can't use call here, as that would mangle the prepared stack. // there's no coming back : - : "r"(addr), "r"(params), "r"(exit_func) + : "r"(params->entry_addr), "r"(params), "r"(ProgramExitFunc) : "rax", "rsi", "rdi"); + UNREACHABLE(); } #endif @@ -62,10 +57,8 @@ void Linker::Execute() { } // Calculate static TLS size. - for (const auto& module : m_modules) { - static_tls_size += module->tls.image_size; - module->tls.offset = static_tls_size; - } + Module* module = m_modules[0].get(); + static_tls_size = module->tls.offset = module->tls.image_size; // Relocate all modules for (const auto& m : m_modules) { @@ -87,36 +80,17 @@ void Linker::Execute() { } } - // Init primary thread. - Common::SetCurrentThreadName("GAME_MainThread"); - DebugState.AddCurrentThreadToGuestList(); - Libraries::Kernel::pthreadInitSelfMainThread(); - EnsureThreadInitialized(true); + main_thread.Run([this, module](std::stop_token) { + Common::SetCurrentThreadName("GAME_MainThread"); + LoadSharedLibraries(); - // Start shared library modules - for (auto& m : m_modules) { - if (m->IsSharedLib()) { - m->Start(0, nullptr, nullptr); - } - } - - // Start main module. - EntryParams p{}; - p.argc = 1; - p.argv[0] = "eboot.bin"; - - for (auto& m : m_modules) { - if (!m->IsSharedLib()) { -#ifdef ARCH_X86_64 - ExecuteGuest(RunMainEntry, m->GetEntryAddress(), &p, ProgramExitFunc); -#else - UNIMPLEMENTED_MSG( - "Missing guest entrypoint implementation for target CPU architecture."); -#endif - } - } - - SetTcbBase(nullptr); + // Start main module. + EntryParams params{}; + params.argc = 1; + params.argv[0] = "eboot.bin"; + params.entry_addr = module->GetEntryAddress(); + RunMainEntry(¶ms); + }); } s32 Linker::LoadModule(const std::filesystem::path& elf_name, bool is_dynamic) { @@ -149,10 +123,9 @@ Module* Linker::FindByAddress(VAddr address) { } void Linker::Relocate(Module* module) { - module->ForEachRelocation([&](elf_relocation* rel, u32 i, bool isJmpRel) { - const u32 bit_idx = - (isJmpRel ? module->dynamic_info.relocation_table_size / sizeof(elf_relocation) : 0) + - i; + module->ForEachRelocation([&](elf_relocation* rel, u32 i, bool is_jmp_rel) { + const u32 num_relocs = module->dynamic_info.relocation_table_size / sizeof(elf_relocation); + const u32 bit_idx = (is_jmp_rel ? num_relocs : 0) + i; if (module->TestRelaBit(bit_idx)) { return; } @@ -160,7 +133,7 @@ void Linker::Relocate(Module* module) { auto symbol = rel->GetSymbol(); auto addend = rel->rel_addend; auto* symbol_table = module->dynamic_info.symbol_table; - auto* namesTlb = module->dynamic_info.str_table; + auto* names_tlb = module->dynamic_info.str_table; const VAddr rel_base_virtual_addr = module->GetBaseAddress(); const VAddr rel_virtual_addr = rel_base_virtual_addr + rel->rel_offset; @@ -216,7 +189,7 @@ void Linker::Relocate(Module* module) { break; case STB_GLOBAL: case STB_WEAK: { - rel_name = namesTlb + sym.st_name; + rel_name = names_tlb + sym.st_name; if (Resolve(rel_name, rel_sym_type, module, &symrec)) { // Only set the rela bit if the symbol was actually resolved and not stubbed. module->SetRelaBit(bit_idx); @@ -225,7 +198,7 @@ void Linker::Relocate(Module* module) { break; } default: - ASSERT_MSG(0, "unknown bind type {}", sym_bind); + UNREACHABLE_MSG("Unknown bind type {}", sym_bind); } rel_is_resolved = (symbol_virtual_addr != 0); rel_value = (rel_is_resolved ? symbol_virtual_addr + addend : 0); @@ -237,9 +210,9 @@ void Linker::Relocate(Module* module) { } if (rel_is_resolved) { - VirtualMemory::memory_patch(rel_virtual_addr, rel_value); + std::memcpy(reinterpret_cast(rel_virtual_addr), &rel_value, sizeof(rel_value)); } else { - LOG_INFO(Core_Linker, "function not patched! {}", rel_name); + LOG_INFO(Core_Linker, "Function not patched! {}", rel_name); } }); } @@ -310,7 +283,7 @@ void* Linker::TlsGetAddr(u64 module_index, u64 offset) { const u32 old_num_dtvs = dtv_table[1].counter; ASSERT_MSG(max_tls_index > old_num_dtvs, "Module unloading unsupported"); // Module was loaded, increase DTV table size. - DtvEntry* new_dtv_table = new DtvEntry[max_tls_index + 2]; + DtvEntry* new_dtv_table = new DtvEntry[max_tls_index + 2]{}; std::memcpy(new_dtv_table + 2, dtv_table + 2, old_num_dtvs * sizeof(DtvEntry)); new_dtv_table[0].counter = dtv_generation_counter; new_dtv_table[1].counter = max_tls_index; @@ -322,13 +295,11 @@ void* Linker::TlsGetAddr(u64 module_index, u64 offset) { } u8* addr = dtv_table[module_index + 1].pointer; + Module* module = m_modules[module_index - 1].get(); if (!addr) { // Module was just loaded by above code. Allocate TLS block for it. - Module* module = m_modules[module_index - 1].get(); const u32 init_image_size = module->tls.init_image_size; - // TODO: Determine if Windows will crash from this - u8* dest = - reinterpret_cast(ExecuteGuest(heap_api->heap_malloc, module->tls.image_size)); + u8* dest = reinterpret_cast(heap_api->heap_malloc(module->tls.image_size)); const u8* src = reinterpret_cast(module->tls.image_virtual_addr); std::memcpy(dest, src, init_image_size); std::memset(dest + init_image_size, 0, module->tls.image_size - init_image_size); @@ -338,18 +309,7 @@ void* Linker::TlsGetAddr(u64 module_index, u64 offset) { return addr + offset; } -thread_local std::once_flag init_tls_flag; - -void Linker::EnsureThreadInitialized(bool is_primary) const { - std::call_once(init_tls_flag, [this, is_primary] { -#ifdef ARCH_X86_64 - InitializeThreadPatchStack(); -#endif - InitTlsForThread(is_primary); - }); -} - -void Linker::InitTlsForThread(bool is_primary) const { +void* Linker::AllocateTlsForThread(bool is_primary) { static constexpr size_t TcbSize = 0x40; static constexpr size_t TlsAllocAlign = 0x20; const size_t total_tls_size = Common::AlignUp(static_tls_size, TlsAllocAlign) + TcbSize; @@ -370,54 +330,20 @@ void Linker::InitTlsForThread(bool is_primary) const { ASSERT_MSG(ret == 0, "Unable to allocate TLS+TCB for the primary thread"); } else { if (heap_api) { -#ifndef WIN32 - addr_out = ExecuteGuestWithoutTls(heap_api->heap_malloc, total_tls_size); + addr_out = Core::ExecuteGuest(heap_api->heap_malloc, total_tls_size); } else { addr_out = std::malloc(total_tls_size); -#else - // TODO: Windows tls malloc replacement, refer to rtld_tls_block_malloc - LOG_ERROR(Core_Linker, "TLS user malloc called, using std::malloc"); - addr_out = std::malloc(total_tls_size); - if (!addr_out) { - auto pth_id = pthread_self(); - auto handle = pthread_gethandle(pth_id); - ASSERT_MSG(addr_out, - "Cannot allocate TLS block defined for handle=%x, index=%d size=%d", - handle, pth_id, total_tls_size); - } -#endif } } + return addr_out; +} - // Initialize allocated memory and allocate DTV table. - const u32 num_dtvs = max_tls_index; - std::memset(addr_out, 0, total_tls_size); - DtvEntry* dtv_table = new DtvEntry[num_dtvs + 2]; - - // Initialize thread control block - u8* addr = reinterpret_cast(addr_out); - Tcb* tcb = reinterpret_cast(addr + static_tls_size); - tcb->tcb_self = tcb; - tcb->tcb_dtv = dtv_table; - - // Dtv[0] is the generation counter. libkernel puts their number into dtv[1] (why?) - dtv_table[0].counter = dtv_generation_counter; - dtv_table[1].counter = num_dtvs; - - // Copy init images to TLS thread blocks and map them to DTV slots. - for (u32 i = 0; i < num_static_modules; i++) { - auto* module = m_modules[i].get(); - if (module->tls.image_size == 0) { - continue; - } - u8* dest = reinterpret_cast(addr + static_tls_size - module->tls.offset); - const u8* src = reinterpret_cast(module->tls.image_virtual_addr); - std::memcpy(dest, src, module->tls.init_image_size); - tcb->tcb_dtv[module->tls.modid + 1].pointer = dest; +void Linker::FreeTlsForNonPrimaryThread(void* pointer) { + if (heap_api) { + Core::ExecuteGuest(heap_api->heap_free, pointer); + } else { + std::free(pointer); } - - // Set pointer to FS base - SetTcbBase(tcb); } void Linker::DebugDump() { @@ -425,17 +351,18 @@ void Linker::DebugDump() { const std::filesystem::path debug(log_dir / "debugdump"); std::filesystem::create_directory(debug); for (const auto& m : m_modules) { - // TODO make a folder with game id for being more unique? - const std::filesystem::path filepath(debug / m.get()->file.stem()); + Module* module = m.get(); + auto& elf = module->elf; + const std::filesystem::path filepath(debug / module->file.stem()); std::filesystem::create_directory(filepath); - m.get()->import_sym.DebugDump(filepath / "imports.txt"); - m.get()->export_sym.DebugDump(filepath / "exports.txt"); - if (m.get()->elf.IsSelfFile()) { - m.get()->elf.SelfHeaderDebugDump(filepath / "selfHeader.txt"); - m.get()->elf.SelfSegHeaderDebugDump(filepath / "selfSegHeaders.txt"); + module->import_sym.DebugDump(filepath / "imports.txt"); + module->export_sym.DebugDump(filepath / "exports.txt"); + if (elf.IsSelfFile()) { + elf.SelfHeaderDebugDump(filepath / "selfHeader.txt"); + elf.SelfSegHeaderDebugDump(filepath / "selfSegHeaders.txt"); } - m.get()->elf.ElfHeaderDebugDump(filepath / "elfHeader.txt"); - m.get()->elf.PHeaderDebugDump(filepath / "elfPHeaders.txt"); + elf.ElfHeaderDebugDump(filepath / "elfHeader.txt"); + elf.PHeaderDebugDump(filepath / "elfPHeaders.txt"); } } diff --git a/src/core/linker.h b/src/core/linker.h index fe1278d00..d6b5d648a 100644 --- a/src/core/linker.h +++ b/src/core/linker.h @@ -6,6 +6,7 @@ #include #include #include +#include "core/libraries/kernel/threads.h" #include "core/module.h" namespace Core { @@ -40,10 +41,15 @@ struct OrbisProcParam { u64 unknown1; }; +using ExitFunc = PS4_SYSV_ABI void (*)(); + +class Linker; + struct EntryParams { int argc; u32 padding; const char* argv[3]; + VAddr entry_addr; }; struct HeapAPI { @@ -79,6 +85,27 @@ public: return m_modules.at(index).get(); } + u32 FindByName(const std::filesystem::path& name) const { + for (u32 i = 0; i < m_modules.size(); i++) { + if (name == m_modules[i]->file) { + return i; + } + } + return -1; + } + + u32 MaxTlsIndex() const { + return max_tls_index; + } + + u32 GenerationCounter() const { + return dtv_generation_counter; + } + + size_t StaticTlsSize() const noexcept { + return static_tls_size; + } + void RelocateAnyImports(Module* m) { Relocate(m); for (auto& module : m_modules) { @@ -89,6 +116,14 @@ public: } } + void LoadSharedLibraries() { + for (auto& module : m_modules) { + if (module->IsSharedLib()) { + module->Start(0, nullptr, nullptr); + } + } + } + void SetHeapAPI(void* func[]) { heap_api = reinterpret_cast(func); } @@ -98,6 +133,8 @@ public: } void* TlsGetAddr(u64 module_index, u64 offset); + void* AllocateTlsForThread(bool is_primary); + void FreeTlsForNonPrimaryThread(void* pointer); s32 LoadModule(const std::filesystem::path& elf_name, bool is_dynamic = false); Module* FindByAddress(VAddr address); @@ -108,26 +145,11 @@ public: void Execute(); void DebugDump(); - template - ReturnType ExecuteGuest(PS4_SYSV_ABI ReturnType (*func)(FuncArgs...), - CallArgs&&... args) const { - // Make sure TLS is initialized for the thread before entering guest. - EnsureThreadInitialized(); - return ExecuteGuestWithoutTls(func, args...); - } - private: const Module* FindExportedModule(const ModuleInfo& m, const LibraryInfo& l); - void EnsureThreadInitialized(bool is_primary = false) const; - void InitTlsForThread(bool is_primary) const; - - template - ReturnType ExecuteGuestWithoutTls(PS4_SYSV_ABI ReturnType (*func)(FuncArgs...), - CallArgs&&... args) const { - return func(std::forward(args)...); - } MemoryManager* memory; + Libraries::Kernel::Thread main_thread; std::mutex mutex; u32 dtv_generation_counter{1}; size_t static_tls_size{}; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 61eb421e5..82e4b7ad3 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -5,10 +5,9 @@ #include "common/assert.h" #include "common/config.h" #include "common/debug.h" -#include "core/libraries/error_codes.h" -#include "core/libraries/kernel/memory_management.h" +#include "core/libraries/kernel/memory.h" +#include "core/libraries/kernel/orbis_error.h" #include "core/memory.h" -#include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_rasterizer.h" namespace Core { @@ -268,7 +267,7 @@ int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, M // Certain games perform flexible mappings on loop to determine // the available flexible memory size. Questionable but we need to handle this. if (type == VMAType::Flexible && flexible_usage + size > total_flexible_size) { - return SCE_KERNEL_ERROR_ENOMEM; + return ORBIS_KERNEL_ERROR_ENOMEM; } // When virtual addr is zero, force it to virtual_base. The guest cannot pass Fixed @@ -329,7 +328,7 @@ int MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, size_t size, Mem } // Map the file. - impl.MapFile(mapped_addr, size, offset, std::bit_cast(prot), fd); + impl.MapFile(mapped_addr, size_aligned, offset, std::bit_cast(prot), fd); // Add virtual memory area auto& new_vma = CarveVMA(mapped_addr, size_aligned)->second; @@ -376,12 +375,12 @@ void MemoryManager::PoolDecommit(VAddr virtual_addr, size_t size) { TRACK_FREE(virtual_addr, "VMEM"); } -void MemoryManager::UnmapMemory(VAddr virtual_addr, size_t size) { +s32 MemoryManager::UnmapMemory(VAddr virtual_addr, size_t size) { std::scoped_lock lk{mutex}; - UnmapMemoryImpl(virtual_addr, size); + return UnmapMemoryImpl(virtual_addr, size); } -void MemoryManager::UnmapMemoryImpl(VAddr virtual_addr, size_t size) { +s32 MemoryManager::UnmapMemoryImpl(VAddr virtual_addr, size_t size) { const auto it = FindVMA(virtual_addr); const auto& vma_base = it->second; ASSERT_MSG(vma_base.Contains(virtual_addr, size), @@ -416,6 +415,8 @@ void MemoryManager::UnmapMemoryImpl(VAddr virtual_addr, size_t size) { impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + size, phys_base, is_exec, has_backing, readonly_file); TRACK_FREE(virtual_addr, "VMEM"); + + return ORBIS_OK; } int MemoryManager::QueryProtection(VAddr addr, void** start, void** end, u32* prot) { @@ -513,9 +514,8 @@ int MemoryManager::VirtualQuery(VAddr addr, int flags, 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::Pooled); - info->is_committed.Assign(vma.type != VMAType::Free && vma.type != VMAType::Reserved && - vma.type != VMAType::PoolReserved); + info->is_pooled.Assign(vma.type == VMAType::PoolReserved); + info->is_committed.Assign(vma.type == VMAType::Pooled); vma.name.copy(info->name.data(), std::min(info->name.size(), vma.name.size())); if (vma.type == VMAType::Direct) { const auto dmem_it = FindDmemArea(vma.phys_base); @@ -586,6 +586,7 @@ void MemoryManager::NameVirtualRange(VAddr virtual_addr, size_t size, std::strin "Range provided is not fully contained in vma"); it->second.name = name; } + VAddr MemoryManager::SearchFree(VAddr virtual_addr, size_t size, u32 alignment) { // If the requested address is below the mapped range, start search from the lowest address auto min_search_address = impl.SystemManagedVirtualBase(); @@ -692,7 +693,7 @@ MemoryManager::DMemHandle MemoryManager::Split(DMemHandle dmem_handle, size_t of new_area.size -= offset_in_area; return dmem_map.emplace_hint(std::next(dmem_handle), new_area.base, new_area); -}; +} int MemoryManager::GetDirectMemoryType(PAddr addr, int* directMemoryTypeOut, void** directMemoryStartOut, void** directMemoryEndOut) { diff --git a/src/core/memory.h b/src/core/memory.h index 286f1c979..364609451 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -10,7 +10,7 @@ #include "common/singleton.h" #include "common/types.h" #include "core/address_space.h" -#include "core/libraries/kernel/memory_management.h" +#include "core/libraries/kernel/memory.h" namespace Vulkan { class Rasterizer; @@ -20,6 +20,10 @@ namespace Libraries::Kernel { struct OrbisQueryInfo; } +namespace Core::Devtools::Widget { +class MemoryMapViewer; +} + namespace Core { enum class MemoryProt : u32 { @@ -133,6 +137,10 @@ public: rasterizer = rasterizer_; } + AddressSpace& GetAddressSpace() { + return impl; + } + u64 GetTotalDirectSize() const { return total_direct_size; } @@ -149,6 +157,13 @@ public: return impl.SystemReservedVirtualBase(); } + bool IsValidAddress(const void* addr) const noexcept { + const VAddr virtual_addr = reinterpret_cast(addr); + const auto end_it = std::prev(vma_map.end()); + const VAddr end_addr = end_it->first + end_it->second.size; + return virtual_addr >= vma_map.begin()->first && virtual_addr < end_addr; + } + bool TryWriteBacking(void* address, const void* data, u32 num_bytes); void SetupMemoryRegions(u64 flexible_size); @@ -177,7 +192,7 @@ public: void PoolDecommit(VAddr virtual_addr, size_t size); - void UnmapMemory(VAddr virtual_addr, size_t size); + s32 UnmapMemory(VAddr virtual_addr, size_t size); int QueryProtection(VAddr addr, void** start, void** end, u32* prot); @@ -235,7 +250,7 @@ private: DMemHandle Split(DMemHandle dmem_handle, size_t offset_in_area); - void UnmapMemoryImpl(VAddr virtual_addr, size_t size); + s32 UnmapMemoryImpl(VAddr virtual_addr, size_t size); private: AddressSpace impl; @@ -246,6 +261,8 @@ private: size_t total_flexible_size{}; size_t flexible_usage{}; Vulkan::Rasterizer* rasterizer{}; + + friend class ::Core::Devtools::Widget::MemoryMapViewer; }; using Memory = Common::Singleton; diff --git a/src/core/module.cpp b/src/core/module.cpp index 5d3b40577..70afb932c 100644 --- a/src/core/module.cpp +++ b/src/core/module.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + #include "common/alignment.h" #include "common/arch.h" #include "common/assert.h" @@ -9,10 +11,10 @@ #include "common/string_util.h" #include "core/aerolib/aerolib.h" #include "core/cpu_patches.h" -#include "core/linker.h" #include "core/loader/dwarf.h" #include "core/memory.h" #include "core/module.h" +#include "core/tls.h" namespace Core { @@ -56,6 +58,30 @@ static std::string EncodeId(u64 nVal) { return enc; } +static std::string StringToNid(std::string_view symbol) { + static constexpr std::array Salt = {0x51, 0x8D, 0x64, 0xA6, 0x35, 0xDE, 0xD8, 0xC1, + 0xE6, 0xB0, 0x39, 0xB1, 0xC3, 0xE5, 0x52, 0x30}; + std::vector input(symbol.size() + Salt.size()); + std::memcpy(input.data(), symbol.data(), symbol.size()); + std::memcpy(input.data() + symbol.size(), Salt.data(), Salt.size()); + + std::array hash; + CryptoPP::SHA1().CalculateDigest(hash.data(), input.data(), input.size()); + + u64 digest; + std::memcpy(&digest, hash.data(), sizeof(digest)); + + static constexpr std::string_view codes = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-"; + std::string dst(11, '\0'); + + for (int i = 0; i < 10; i++) { + dst[i] = codes[(digest >> (58 - i * 6)) & 0x3f]; + } + dst[10] = codes[(digest & 0xf) * 4]; + return dst; +} + Module::Module(Core::MemoryManager* memory_, const std::filesystem::path& file_, u32& max_tls_index) : memory{memory_}, file{file_}, name{file.stem().string()} { elf.Open(file); @@ -70,9 +96,8 @@ Module::~Module() = default; s32 Module::Start(size_t args, const void* argp, void* param) { LOG_INFO(Core_Linker, "Module started : {}", name); - const auto* linker = Common::Singleton::Instance(); const VAddr addr = dynamic_info.init_virtual_addr + GetBaseAddress(); - return linker->ExecuteGuest(reinterpret_cast(addr), args, argp, param); + return ExecuteGuest(reinterpret_cast(addr), args, argp, param); } void Module::LoadModuleToMemory(u32& max_tls_index) { @@ -167,9 +192,7 @@ void Module::LoadModuleToMemory(u32& max_tls_index) { tls.align = elf_pheader[i].p_align; tls.image_virtual_addr = elf_pheader[i].p_vaddr + base_virtual_addr; tls.image_size = GetAlignedSize(elf_pheader[i]); - if (tls.image_size != 0) { - tls.modid = ++max_tls_index; - } + tls.modid = ++max_tls_index; LOG_INFO(Core_Linker, "TLS virtual address = {:#x}", tls.image_virtual_addr); LOG_INFO(Core_Linker, "TLS image size = {}", tls.image_size); break; @@ -447,8 +470,8 @@ OrbisKernelModuleInfoEx Module::GetModuleInfoEx() const { .tls_align = tls.align, .init_proc_addr = base_virtual_addr + dynamic_info.init_virtual_addr, .fini_proc_addr = base_virtual_addr + dynamic_info.fini_virtual_addr, - .eh_frame_hdr_addr = eh_frame_hdr_addr, - .eh_frame_addr = eh_frame_addr, + .eh_frame_hdr_addr = base_virtual_addr + eh_frame_hdr_addr, + .eh_frame_addr = base_virtual_addr + eh_frame_addr, .eh_frame_hdr_size = eh_frame_hdr_size, .eh_frame_size = eh_frame_size, .segments = info.segments, @@ -492,4 +515,15 @@ const LibraryInfo* Module::FindLibrary(std::string_view id) { return nullptr; } +void* Module::FindByName(std::string_view name) { + const auto nid_str = StringToNid(name); + const auto symbols = export_sym.GetSymbols(); + const auto it = std::ranges::find_if( + symbols, [&](const Loader::SymbolRecord& record) { return record.name.contains(nid_str); }); + if (it != symbols.end()) { + return reinterpret_cast(it->virtual_address); + } + return nullptr; +} + } // namespace Core diff --git a/src/core/module.h b/src/core/module.h index 007501f08..630c5d583 100644 --- a/src/core/module.h +++ b/src/core/module.h @@ -165,15 +165,6 @@ public: return elf.IsSharedLib(); } - void* FindByName(std::string_view name) { - const auto symbols = export_sym.GetSymbols(); - const auto it = std::ranges::find(symbols, name, &Loader::SymbolRecord::nid_name); - if (it != symbols.end()) { - return reinterpret_cast(it->virtual_address); - } - return nullptr; - } - template T GetProcParam() const noexcept { return reinterpret_cast(proc_param_virtual_addr); @@ -217,6 +208,8 @@ public: void LoadDynamicInfo(); void LoadSymbols(); + void* FindByName(std::string_view name); + OrbisKernelModuleInfoEx GetModuleInfoEx() const; const ModuleInfo* FindModule(std::string_view id); const LibraryInfo* FindLibrary(std::string_view id); diff --git a/src/core/platform.h b/src/core/platform.h index 03bd79e86..bdb50701b 100644 --- a/src/core/platform.h +++ b/src/core/platform.h @@ -7,7 +7,8 @@ #include "common/logging/log.h" #include "common/singleton.h" #include "common/types.h" -#include "magic_enum.hpp" + +#include #include #include diff --git a/src/core/signals.cpp b/src/core/signals.cpp index 8faf794ed..89844ae25 100644 --- a/src/core/signals.cpp +++ b/src/core/signals.cpp @@ -41,6 +41,14 @@ static LONG WINAPI SignalHandler(EXCEPTION_POINTERS* pExp) noexcept { #else +static std::string GetThreadName() { + char name[256]; + if (pthread_getname_np(pthread_self(), name, sizeof(name)) != 0) { + return ""; + } + return std::string{name}; +} + static std::string DisassembleInstruction(void* code_address) { char buffer[256] = ""; @@ -71,16 +79,18 @@ static void SignalHandler(int sig, siginfo_t* info, void* raw_context) { case SIGBUS: { const bool is_write = Common::IsWriteError(raw_context); if (!signals->DispatchAccessViolation(raw_context, info->si_addr)) { - UNREACHABLE_MSG("Unhandled access violation at code address {}: {} address {}", - fmt::ptr(code_address), is_write ? "Write to" : "Read from", - fmt::ptr(info->si_addr)); + UNREACHABLE_MSG( + "Unhandled access violation in thread '{}' at code address {}: {} address {}", + GetThreadName(), fmt::ptr(code_address), is_write ? "Write to" : "Read from", + fmt::ptr(info->si_addr)); } break; } case SIGILL: if (!signals->DispatchIllegalInstruction(raw_context)) { - UNREACHABLE_MSG("Unhandled illegal instruction at code address {}: {}", - fmt::ptr(code_address), DisassembleInstruction(code_address)); + UNREACHABLE_MSG("Unhandled illegal instruction in thread '{}' at code address {}: {}", + GetThreadName(), fmt::ptr(code_address), + DisassembleInstruction(code_address)); } break; case SIGUSR1: { // Sleep thread until signal is received diff --git a/src/core/thread.cpp b/src/core/thread.cpp new file mode 100644 index 000000000..f87e3c8dc --- /dev/null +++ b/src/core/thread.cpp @@ -0,0 +1,128 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "libraries/kernel/threads/pthread.h" +#include "thread.h" + +#include "core/libraries/kernel/threads/pthread.h" + +#ifdef _WIN64 +#include +#include "common/ntapi.h" +#else +#include +#endif + +namespace Core { + +#ifdef _WIN64 +#define KGDT64_R3_DATA (0x28) +#define KGDT64_R3_CODE (0x30) +#define KGDT64_R3_CMTEB (0x50) +#define RPL_MASK (0x03) + +#define INITIAL_FPUCW (0x037f) +#define INITIAL_MXCSR_MASK (0xffbf) +#define EFLAGS_INTERRUPT_MASK (0x200) + +void InitializeTeb(INITIAL_TEB* teb, const ::Libraries::Kernel::PthreadAttr* attr) { + teb->StackBase = (void*)((u64)attr->stackaddr_attr + attr->stacksize_attr); + teb->StackLimit = nullptr; + teb->StackAllocationBase = attr->stackaddr_attr; +} + +void InitializeContext(CONTEXT* ctx, ThreadFunc func, void* arg, + const ::Libraries::Kernel::PthreadAttr* attr) { + /* Note: The stack has to be reversed */ + ctx->Rsp = (u64)attr->stackaddr_attr + attr->stacksize_attr; + ctx->Rbp = (u64)attr->stackaddr_attr + attr->stacksize_attr; + ctx->Rcx = (u64)arg; + ctx->Rip = (u64)func; + + ctx->SegGs = KGDT64_R3_DATA | RPL_MASK; + ctx->SegEs = KGDT64_R3_DATA | RPL_MASK; + ctx->SegDs = KGDT64_R3_DATA | RPL_MASK; + ctx->SegCs = KGDT64_R3_CODE | RPL_MASK; + ctx->SegSs = KGDT64_R3_DATA | RPL_MASK; + ctx->SegFs = KGDT64_R3_CMTEB | RPL_MASK; + + ctx->EFlags = 0x3000 | EFLAGS_INTERRUPT_MASK; + ctx->MxCsr = INITIAL_MXCSR; + + ctx->FltSave.ControlWord = INITIAL_FPUCW; + ctx->FltSave.MxCsr = INITIAL_MXCSR; + ctx->FltSave.MxCsr_Mask = INITIAL_MXCSR_MASK; + + ctx->ContextFlags = + CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT; +} +#endif + +NativeThread::NativeThread() : native_handle{0} {} + +NativeThread::~NativeThread() {} + +int NativeThread::Create(ThreadFunc func, void* arg, const ::Libraries::Kernel::PthreadAttr* attr) { +#ifndef _WIN64 + pthread_t* pthr = reinterpret_cast(&native_handle); + pthread_attr_t pattr; + pthread_attr_init(&pattr); + pthread_attr_setstack(&pattr, attr->stackaddr_attr, attr->stacksize_attr); + return pthread_create(pthr, &pattr, (PthreadFunc)func, arg); +#else + CLIENT_ID clientId{}; + INITIAL_TEB teb{}; + CONTEXT ctx{}; + + clientId.UniqueProcess = GetCurrentProcess(); + clientId.UniqueThread = GetCurrentThread(); + + InitializeTeb(&teb, attr); + InitializeContext(&ctx, func, arg, attr); + + return NtCreateThread(&native_handle, THREAD_ALL_ACCESS, nullptr, GetCurrentProcess(), + &clientId, &ctx, &teb, false); +#endif +} + +void NativeThread::Exit() { + if (!native_handle) { + return; + } + + tid = 0; + +#ifdef _WIN64 + NtClose(native_handle); + native_handle = nullptr; + + /* The Windows kernel will free the stack + given at thread creation via INITIAL_TEB + (StackAllocationBase) upon thread termination. + + In earlier Windows versions (NT4 to Windows Server 2003), + you could get around this via disabling FreeStackOnTermination + on the TEB. This has been removed since then. + + To avoid this, we must forcefully set the TEB + deallocation stack pointer to NULL so ZwFreeVirtualMemory fails + in the kernel and our stack is not freed. + */ + auto* teb = reinterpret_cast(NtCurrentTeb()); + teb->DeallocationStack = nullptr; + + NtTerminateThread(nullptr, 0); +#else + pthread_exit(nullptr); +#endif +} + +void NativeThread::Initialize() { +#if _WIN64 + tid = GetCurrentThreadId(); +#else + tid = (u64)pthread_self(); +#endif +} + +} // namespace Core \ No newline at end of file diff --git a/src/core/thread.h b/src/core/thread.h new file mode 100644 index 000000000..3bac0e699 --- /dev/null +++ b/src/core/thread.h @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Libraries::Kernel { +struct PthreadAttr; +} // namespace Libraries::Kernel + +namespace Core { + +using ThreadFunc = void (*)(void*); +using PthreadFunc = void* (*)(void*); + +class NativeThread { +public: + NativeThread(); + ~NativeThread(); + + int Create(ThreadFunc func, void* arg, const ::Libraries::Kernel::PthreadAttr* attr); + void Exit(); + + void Initialize(); + + uintptr_t GetHandle() { + return reinterpret_cast(native_handle); + } + + u64 GetTid() { + return tid; + } + +private: +#ifdef _WIN64 + void* native_handle; +#else + uintptr_t native_handle; +#endif + u64 tid; +}; + +} // namespace Core \ No newline at end of file diff --git a/src/core/tls.cpp b/src/core/tls.cpp index eb07e7a72..9b3178171 100644 --- a/src/core/tls.cpp +++ b/src/core/tls.cpp @@ -5,6 +5,8 @@ #include "common/arch.h" #include "common/assert.h" #include "common/types.h" +#include "core/cpu_patches.h" +#include "core/libraries/kernel/threads/pthread.h" #include "core/tls.h" #ifdef _WIN32 @@ -52,8 +54,13 @@ Tcb* GetTcbBase() { // Reserve space in the 32-bit address range for allocating TCB pages. asm(".zerofill TCB_SPACE,TCB_SPACE,__guest_system,0x3FC000"); -static constexpr u64 ldt_region_base = 0x4000; -static constexpr u64 ldt_region_size = 0x3FC000; +struct LdtPage { + void* tcb; + u16 index; +}; + +static constexpr uintptr_t ldt_region_base = 0x4000; +static constexpr size_t ldt_region_size = 0x3FC000; static constexpr u16 ldt_block_size = 0x1000; static constexpr u16 ldt_index_base = 8; static constexpr u16 ldt_index_total = (ldt_region_size - ldt_region_base) / ldt_block_size; @@ -61,11 +68,13 @@ static constexpr u16 ldt_index_total = (ldt_region_size - ldt_region_base) / ldt static boost::icl::interval_set free_ldts{}; static std::mutex free_ldts_lock; static std::once_flag ldt_region_init_flag; +static pthread_key_t ldt_page_slot = 0; -static u16 GetLdtIndex() { - sel_t selector; - asm volatile("mov %%fs, %0" : "=r"(selector)); - return selector.index; +static void FreeLdtPage(void* raw) { + const auto* ldt_page = static_cast(raw); + + std::unique_lock lock{free_ldts_lock}; + free_ldts += ldt_page->index; } static void InitLdtRegion() { @@ -76,11 +85,20 @@ static void InitLdtRegion() { free_ldts += boost::icl::interval::right_open(ldt_index_base, ldt_index_base + ldt_index_total); + ASSERT_MSG(pthread_key_create(&ldt_page_slot, FreeLdtPage) == 0, + "Failed to create thread LDT page key: {}", errno); } -static void** SetupThreadLdt() { +void SetTcbBase(void* image_address) { std::call_once(ldt_region_init_flag, InitLdtRegion); + auto* ldt_page = static_cast(pthread_getspecific(ldt_page_slot)); + if (ldt_page != nullptr) { + // Update TCB pointer in existing page. + ldt_page->tcb = image_address; + return; + } + // Allocate a new LDT index for the current thread. u16 ldt_index; { @@ -89,10 +107,12 @@ static void** SetupThreadLdt() { ldt_index = first(*free_ldts.begin()); free_ldts -= ldt_index; } - const u64 addr = ldt_region_base + (ldt_index - ldt_index_base) * ldt_block_size; + + const uintptr_t addr = ldt_region_base + (ldt_index - ldt_index_base) * ldt_block_size; // Create an LDT entry for the TCB. - const ldt_entry ldt{.data{ + ldt_entry ldt{}; + ldt.data = { .base00 = static_cast(addr), .base16 = static_cast(addr >> 16), .base24 = static_cast(addr >> 24), @@ -103,34 +123,27 @@ static void** SetupThreadLdt() { .present = 1, // Segment present .stksz = DESC_DATA_32B, .granular = DESC_GRAN_BYTE, - }}; + }; int ret = i386_set_ldt(ldt_index, &ldt, 1); ASSERT_MSG(ret == ldt_index, - "Failed to set LDT for TLS area: expected {}, but syscall returned {}", ldt_index, - ret); + "Failed to set LDT {} at {:#x} for TLS area: syscall returned {}, errno {}", + ldt_index, addr, ret, errno); // Set the FS segment to the created LDT. - const sel_t sel{ + const sel_t new_selector{ .rpl = USER_PRIV, .ti = SEL_LDT, .index = ldt_index, }; - asm volatile("mov %0, %%fs" ::"r"(sel)); + asm volatile("mov %0, %%fs" ::"r"(new_selector)); - return reinterpret_cast(addr); -} + // Store the TCB base pointer and index in the created LDT area. + ldt_page = reinterpret_cast(addr); + ldt_page->tcb = image_address; + ldt_page->index = ldt_index; -static void FreeThreadLdt() { - std::unique_lock lock{free_ldts_lock}; - free_ldts += GetLdtIndex(); -} - -void SetTcbBase(void* image_address) { - if (image_address != nullptr) { - *SetupThreadLdt() = image_address; - } else { - FreeThreadLdt(); - } + ASSERT_MSG(pthread_setspecific(ldt_page_slot, ldt_page) == 0, + "Failed to store thread LDT page pointer: {}", errno); } Tcb* GetTcbBase() { @@ -181,4 +194,15 @@ Tcb* GetTcbBase() { #endif +thread_local std::once_flag init_tls_flag; + +void EnsureThreadInitialized() { + std::call_once(init_tls_flag, [] { +#ifdef ARCH_X86_64 + InitializeThreadPatchStack(); +#endif + SetTcbBase(Libraries::Kernel::g_curthread->tcb); + }); +} + } // namespace Core diff --git a/src/core/tls.h b/src/core/tls.h index f5bf33184..4df9e4ace 100644 --- a/src/core/tls.h +++ b/src/core/tls.h @@ -12,7 +12,7 @@ class CodeGenerator; namespace Core { union DtvEntry { - size_t counter; + std::size_t counter; u8* pointer; }; @@ -33,4 +33,13 @@ void SetTcbBase(void* image_address); /// Retrieves Tcb structure for the calling thread. Tcb* GetTcbBase(); +/// Makes sure TLS is initialized for the thread before entering guest. +void EnsureThreadInitialized(); + +template +ReturnType ExecuteGuest(PS4_SYSV_ABI ReturnType (*func)(FuncArgs...), CallArgs&&... args) { + EnsureThreadInitialized(); + return func(std::forward(args)...); +} + } // namespace Core diff --git a/src/core/virtual_memory.cpp b/src/core/virtual_memory.cpp deleted file mode 100644 index 8907622a4..000000000 --- a/src/core/virtual_memory.cpp +++ /dev/null @@ -1,180 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/assert.h" -#include "common/error.h" -#include "common/logging/log.h" -#include "core/virtual_memory.h" - -#ifdef _WIN64 -#include -#else -#include -#endif - -#if !defined(_WIN64) -enum PosixPageProtection { - PAGE_NOACCESS = 0, - PAGE_READONLY = PROT_READ, - PAGE_READWRITE = PROT_READ | PROT_WRITE, - PAGE_EXECUTE = PROT_EXEC, - PAGE_EXECUTE_READ = PROT_EXEC | PROT_READ, - PAGE_EXECUTE_READWRITE = PROT_EXEC | PROT_READ | PROT_WRITE -}; -#endif - -namespace VirtualMemory { -static u32 convertMemoryMode(MemoryMode mode) { - switch (mode) { - case MemoryMode::Read: - return PAGE_READONLY; - case MemoryMode::Write: - case MemoryMode::ReadWrite: - return PAGE_READWRITE; - - case MemoryMode::Execute: - return PAGE_EXECUTE; - case MemoryMode::ExecuteRead: - return PAGE_EXECUTE_READ; - case MemoryMode::ExecuteWrite: - case MemoryMode::ExecuteReadWrite: - return PAGE_EXECUTE_READWRITE; - - case MemoryMode::NoAccess: - return PAGE_NOACCESS; - default: - return PAGE_NOACCESS; - } -} -static MemoryMode convertMemoryMode(u32 mode) { - switch (mode) { - case PAGE_NOACCESS: - return MemoryMode::NoAccess; - case PAGE_READONLY: - return MemoryMode::Read; - case PAGE_READWRITE: - return MemoryMode::ReadWrite; - case PAGE_EXECUTE: - return MemoryMode::Execute; - case PAGE_EXECUTE_READ: - return MemoryMode::ExecuteRead; - case PAGE_EXECUTE_READWRITE: - return MemoryMode::ExecuteReadWrite; - default: - return MemoryMode::NoAccess; - } -} - -u64 memory_alloc(u64 address, u64 size, MemoryMode mode) { -#ifdef _WIN64 - auto ptr = reinterpret_cast(VirtualAlloc( - reinterpret_cast(static_cast(address)), size, - static_cast(MEM_COMMIT) | static_cast(MEM_RESERVE), convertMemoryMode(mode))); - - if (ptr == 0) { - auto err = static_cast(GetLastError()); - LOG_ERROR(Common_Memory, "VirtualAlloc() failed: 0x{:X}", err); - } -#else - auto ptr = reinterpret_cast( - mmap(reinterpret_cast(static_cast(address)), size, - PROT_EXEC | PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)); - - if (ptr == reinterpret_cast MAP_FAILED) { - LOG_ERROR(Common_Memory, "mmap() failed: {}", std::strerror(errno)); - } -#endif - return ptr; -} -bool memory_protect(u64 address, u64 size, MemoryMode mode, MemoryMode* old_mode) { -#ifdef _WIN64 - DWORD old_protect = 0; - if (VirtualProtect(reinterpret_cast(static_cast(address)), size, - convertMemoryMode(mode), &old_protect) == 0) { - auto err = static_cast(GetLastError()); - LOG_ERROR(Common_Memory, "VirtualProtect() failed: 0x{:X}", err); - return false; - } - if (old_mode != nullptr) { - *old_mode = convertMemoryMode(old_protect); - } - return true; -#else - int ret = mprotect(reinterpret_cast(address), size, convertMemoryMode(mode)); - if (ret != 0) { - const auto error = Common::GetLastErrorMsg(); - ASSERT(false); - } - return true; -#endif -} - -bool memory_flush(u64 address, u64 size) { -#ifdef _WIN64 - if (::FlushInstructionCache(GetCurrentProcess(), - reinterpret_cast(static_cast(address)), - size) == 0) { - auto err = static_cast(GetLastError()); - LOG_ERROR(Common_Memory, "FlushInstructionCache() failed: 0x{:X}", err); - return false; - } - return true; -#else // linux probably doesn't have something similar - return true; -#endif -} -bool memory_patch(u64 vaddr, u64 value) { - MemoryMode old_mode{}; - // memory_protect(vaddr, 8, MemoryMode::ReadWrite, &old_mode); - - auto* ptr = reinterpret_cast(vaddr); - - bool ret = (*ptr != value); - - *ptr = value; - - // memory_protect(vaddr, 8, old_mode, nullptr); - - // if mode is executable flush it so insure that cpu finds it - if (containsExecuteMode(old_mode)) { - memory_flush(vaddr, 8); - } - - return ret; -} -static u64 AlignUp(u64 pos, u64 align) { - return (align != 0 ? (pos + (align - 1)) & ~(align - 1) : pos); -} - -u64 memory_alloc_aligned(u64 address, u64 size, MemoryMode mode, u64 alignment) { -#ifdef _WIN64 - // try allocate aligned address inside user area - MEM_ADDRESS_REQUIREMENTS req{}; - MEM_EXTENDED_PARAMETER param{}; - req.LowestStartingAddress = - (address == 0 ? reinterpret_cast(USER_MIN) - : reinterpret_cast(AlignUp(address, alignment))); - req.HighestEndingAddress = reinterpret_cast(USER_MAX); - req.Alignment = alignment; - param.Type = MemExtendedParameterAddressRequirements; - param.Pointer = &req; - - auto ptr = reinterpret_cast( - VirtualAlloc2(GetCurrentProcess(), nullptr, size, - static_cast(MEM_COMMIT) | static_cast(MEM_RESERVE), - convertMemoryMode(mode), ¶m, 1)); - - if (ptr == 0) { - auto err = static_cast(GetLastError()); - LOG_ERROR(Common_Memory, "VirtualAlloc2() failed: 0x{:X}", err); - } - return ptr; -#else - void* hint_address = address == 0 ? reinterpret_cast(USER_MIN) - : reinterpret_cast(AlignUp(address, alignment)); - void* ptr = mmap(hint_address, size, convertMemoryMode(mode), MAP_ANON | MAP_PRIVATE, -1, 0); - ASSERT(ptr != MAP_FAILED); - return reinterpret_cast(ptr); -#endif -} -} // namespace VirtualMemory diff --git a/src/core/virtual_memory.h b/src/core/virtual_memory.h deleted file mode 100644 index 953f35f79..000000000 --- a/src/core/virtual_memory.h +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/types.h" - -constexpr u64 SYSTEM_RESERVED = 0x800000000u; -constexpr u64 CODE_BASE_OFFSET = 0x100000000u; -constexpr u64 SYSTEM_MANAGED_MIN = 0x0000040000u; -constexpr u64 SYSTEM_MANAGED_MAX = 0x07FFFFBFFFu; -constexpr u64 USER_MIN = 0x1000000000u; -constexpr u64 USER_MAX = 0xFBFFFFFFFFu; - -namespace VirtualMemory { -enum class MemoryMode : u32 { - NoAccess = 0, - Read = 1, - Write = 2, - ReadWrite = 3, - Execute = 4, - ExecuteRead = 5, - ExecuteWrite = 6, - ExecuteReadWrite = 7, -}; -u64 memory_alloc(u64 address, u64 size, MemoryMode mode); -u64 memory_alloc_aligned(u64 address, u64 size, MemoryMode mode, u64 alignment); -bool memory_protect(u64 address, u64 size, MemoryMode mode, MemoryMode* old_mode); -bool memory_flush(u64 address, u64 size); -bool memory_patch(u64 vaddr, u64 value); - -inline bool containsExecuteMode(MemoryMode mode) { - switch (mode) { - case MemoryMode::Execute: - return true; - case MemoryMode::ExecuteRead: - return true; - case MemoryMode::ExecuteWrite: - return true; - case MemoryMode::ExecuteReadWrite: - return true; - default: - return false; - } -} - -} // namespace VirtualMemory diff --git a/src/emulator.cpp b/src/emulator.cpp index d9d32ec1d..60d6e18d7 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -22,14 +22,13 @@ #include "common/scm_rev.h" #include "common/singleton.h" #include "common/version.h" -#include "core/file_format/playgo_chunk.h" #include "core/file_format/psf.h" #include "core/file_format/splash.h" #include "core/file_format/trp.h" #include "core/file_sys/fs.h" #include "core/libraries/disc_map/disc_map.h" #include "core/libraries/fiber/fiber.h" -#include "core/libraries/kernel/thread_management.h" +#include "core/libraries/jpeg/jpegenc.h" #include "core/libraries/libc_internal/libc_internal.h" #include "core/libraries/libs.h" #include "core/libraries/ngs2/ngs2.h" @@ -76,6 +75,9 @@ Emulator::Emulator() { LOG_INFO(Config, "Vulkan rdocMarkersEnable: {}", Config::vkMarkersEnabled()); LOG_INFO(Config, "Vulkan crashDiagnostics: {}", Config::vkCrashDiagnosticEnabled()); + // Create stdin/stdout/stderr + Common::Singleton::Instance()->CreateStdHandles(); + // Defer until after logging is initialized. memory = Core::Memory::Instance(); controller = Common::Singleton::Instance(); @@ -157,14 +159,7 @@ void Emulator::Run(const std::filesystem::path& file) { 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); - } else if (entry.path().filename() == "playgo-chunk.dat") { - auto* playgo = Common::Singleton::Instance(); - auto filepath = sce_sys_folder / "playgo-chunk.dat"; - if (!playgo->Open(filepath)) { - LOG_ERROR(Loader, "PlayGo: unable to open file"); - } - } else if (entry.path().filename() == "pic0.png" || - entry.path().filename() == "pic1.png") { + } else if (entry.path().filename() == "pic1.png") { auto* splash = Common::Singleton::Instance(); if (splash->IsLoaded()) { continue; @@ -222,7 +217,6 @@ void Emulator::Run(const std::filesystem::path& file) { VideoCore::SetOutputDir(mount_captures_dir, id); // Initialize kernel and library facilities. - Libraries::Kernel::init_pthreads(); Libraries::InitHLELibs(&linker->GetHLESymbols()); // Load the module with the linker @@ -257,13 +251,11 @@ void Emulator::Run(const std::filesystem::path& file) { } #endif - // start execution - std::jthread mainthread = - std::jthread([this](std::stop_token stop_token) { linker->Execute(); }); + linker->Execute(); - window->initTimers(); - while (window->isOpen()) { - window->waitEvent(); + window->InitTimers(); + while (window->IsOpen()) { + window->WaitEvent(); } #ifdef ENABLE_QT_GUI @@ -274,7 +266,7 @@ void Emulator::Run(const std::filesystem::path& file) { } void Emulator::LoadSystemModules(const std::filesystem::path& file, std::string game_serial) { - constexpr std::array ModulesToLoad{ + constexpr std::array ModulesToLoad{ {{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterlibSceNgs2}, {"libSceFiber.sprx", &Libraries::Fiber::RegisterlibSceFiber}, {"libSceUlt.sprx", nullptr}, @@ -283,8 +275,7 @@ void Emulator::LoadSystemModules(const std::filesystem::path& file, std::string {"libSceLibcInternal.sprx", &Libraries::LibcInternal::RegisterlibSceLibcInternal}, {"libSceDiscMap.sprx", &Libraries::DiscMap::RegisterlibSceDiscMap}, {"libSceRtc.sprx", &Libraries::Rtc::RegisterlibSceRtc}, - {"libSceJpegEnc.sprx", nullptr}, - {"libSceRazorCpu.sprx", nullptr}, + {"libSceJpegEnc.sprx", &Libraries::JpegEnc::RegisterlibSceJpegEnc}, {"libSceCesCs.sprx", nullptr}}}; std::vector found_modules; diff --git a/src/images/utils_icon.png b/src/images/utils_icon.png new file mode 100644 index 000000000..7dfa3aa00 Binary files /dev/null and b/src/images/utils_icon.png differ diff --git a/src/imgui/renderer/imgui_core.cpp b/src/imgui/renderer/imgui_core.cpp index 311e86a3c..46391faef 100644 --- a/src/imgui/renderer/imgui_core.cpp +++ b/src/imgui/renderer/imgui_core.cpp @@ -14,7 +14,7 @@ #include "imgui_internal.h" #include "sdl_window.h" #include "texture_manager.h" -#include "video_core/renderer_vulkan/renderer_vulkan.h" +#include "video_core/renderer_vulkan/vk_presenter.h" #include "imgui_fonts/notosansjp_regular.ttf.g.cpp" #include "imgui_fonts/proggyvector_regular.ttf.g.cpp" @@ -49,7 +49,7 @@ void Initialize(const ::Vulkan::Instance& instance, const Frontend::WindowSDL& w io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; - io.DisplaySize = ImVec2((float)window.getWidth(), (float)window.getHeight()); + io.DisplaySize = ImVec2((float)window.GetWidth(), (float)window.GetHeight()); PushStyleVar(ImGuiStyleVar_WindowRounding, 6.0f); // Makes the window edges rounded auto path = config_path.u8string(); @@ -83,7 +83,7 @@ void Initialize(const ::Vulkan::Instance& instance, const Frontend::WindowSDL& w StyleColorsDark(); ::Core::Devtools::Layer::SetupSettings(); - Sdl::Init(window.GetSdlWindow()); + Sdl::Init(window.GetSDLWindow()); const Vulkan::InitInfo vk_info{ .instance = instance.GetInstance(), @@ -108,7 +108,7 @@ void Initialize(const ::Vulkan::Instance& instance, const Frontend::WindowSDL& w ImFormatString(label, IM_ARRAYSIZE(label), "WindowOverViewport_%08X", GetMainViewport()->ID); dock_id = ImHashStr(label); - if (const auto dpi = SDL_GetWindowDisplayScale(window.GetSdlWindow()); dpi > 0.0f) { + if (const auto dpi = SDL_GetWindowDisplayScale(window.GetSDLWindow()); dpi > 0.0f) { GetIO().FontGlobalScale = dpi; } } diff --git a/src/imgui/renderer/texture_manager.cpp b/src/imgui/renderer/texture_manager.cpp index 7f9c69d49..f13c995be 100644 --- a/src/imgui/renderer/texture_manager.cpp +++ b/src/imgui/renderer/texture_manager.cpp @@ -4,12 +4,12 @@ #include #include -#include - #include "common/assert.h" #include "common/config.h" #include "common/io_file.h" #include "common/polyfill_thread.h" +#include "common/stb.h" +#include "common/thread.h" #include "imgui_impl_vulkan.h" #include "texture_manager.h" @@ -82,6 +82,7 @@ RefCountedTexture::~RefCountedTexture() { } } } + RefCountedTexture::Image RefCountedTexture::GetTexture() const { if (inner == nullptr) { return {}; @@ -92,6 +93,7 @@ RefCountedTexture::Image RefCountedTexture::GetTexture() const { .height = inner->height, }; } + RefCountedTexture::operator bool() const { return inner != nullptr && inner->texture_id != nullptr; } @@ -131,6 +133,7 @@ Inner::~Inner() { } void WorkerLoop() { + Common::SetCurrentThreadName("shadPS4:ImGuiTextureManager"); std::mutex mtx; while (g_is_worker_running) { std::unique_lock lk{mtx}; diff --git a/src/input/controller.cpp b/src/input/controller.cpp index 2187608e5..3927b096f 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -1,13 +1,10 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "controller.h" - -#include "common/assert.h" -#include "core/libraries/kernel/time_management.h" -#include "core/libraries/pad/pad.h" - #include +#include "core/libraries/kernel/time.h" +#include "core/libraries/pad/pad.h" +#include "input/controller.h" namespace Input { @@ -59,9 +56,7 @@ State GameController::GetLastState() const { if (m_states_num == 0) { return m_last_state; } - - auto last = (m_first_state + m_states_num - 1) % MAX_STATES; - + const u32 last = (m_first_state + m_states_num - 1) % MAX_STATES; return m_states[last]; } @@ -71,19 +66,19 @@ void GameController::AddState(const State& state) { m_first_state = (m_first_state + 1) % MAX_STATES; } - auto index = (m_first_state + m_states_num) % MAX_STATES; - + const u32 index = (m_first_state + m_states_num) % MAX_STATES; m_states[index] = state; m_last_state = state; m_private[index].obtained = false; m_states_num++; } -void GameController::CheckButton(int id, u32 button, bool isPressed) { +void GameController::CheckButton(int id, Libraries::Pad::OrbisPadButtonDataOffset button, + bool is_pressed) { std::scoped_lock lock{m_mutex}; auto state = GetLastState(); state.time = Libraries::Kernel::sceKernelGetProcessTime(); - if (isPressed) { + if (is_pressed) { state.buttonsState |= button; } else { state.buttonsState &= ~button; @@ -93,28 +88,28 @@ void GameController::CheckButton(int id, u32 button, bool isPressed) { } void GameController::Axis(int id, Input::Axis axis, int value) { + using Libraries::Pad::OrbisPadButtonDataOffset; + std::scoped_lock lock{m_mutex}; auto state = GetLastState(); state.time = Libraries::Kernel::sceKernelGetProcessTime(); - int axis_id = static_cast(axis); - state.axes[axis_id] = value; if (axis == Input::Axis::TriggerLeft) { if (value > 0) { - state.buttonsState |= Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L2; + state.buttonsState |= OrbisPadButtonDataOffset::L2; } else { - state.buttonsState &= ~Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L2; + state.buttonsState &= ~OrbisPadButtonDataOffset::L2; } } if (axis == Input::Axis::TriggerRight) { if (value > 0) { - state.buttonsState |= Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R2; + state.buttonsState |= OrbisPadButtonDataOffset::R2; } else { - state.buttonsState &= ~Libraries::Pad::OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R2; + state.buttonsState &= ~OrbisPadButtonDataOffset::R2; } } diff --git a/src/input/controller.h b/src/input/controller.h index 01ea21c0c..d425fb46c 100644 --- a/src/input/controller.h +++ b/src/input/controller.h @@ -5,6 +5,7 @@ #include #include "common/types.h" +#include "core/libraries/pad/pad.h" struct SDL_Gamepad; @@ -28,7 +29,7 @@ struct TouchpadEntry { }; struct State { - u32 buttonsState = 0; + Libraries::Pad::OrbisPadButtonDataOffset buttonsState{}; u64 time = 0; int axes[static_cast(Axis::AxisMax)] = {128, 128, 128, 128, 0, 0}; TouchpadEntry touchpad[2] = {{false, 0, 0}, {false, 0, 0}}; @@ -49,7 +50,7 @@ public: void ReadState(State* state, bool* isConnected, int* connectedCount); int ReadStates(State* states, int states_num, bool* isConnected, int* connectedCount); State GetLastState() const; - void CheckButton(int id, u32 button, bool isPressed); + void CheckButton(int id, Libraries::Pad::OrbisPadButtonDataOffset button, bool isPressed); void AddState(const State& state); void Axis(int id, Input::Axis axis, int value); void SetLightBarRGB(u8 r, u8 g, u8 b); diff --git a/src/main.cpp b/src/main.cpp index de1d92326..17b5c11fe 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,13 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "functional" +#include "iostream" +#include "string" +#include "unordered_map" + #include +#include "common/config.h" #include "common/memory_patcher.h" #include "emulator.h" @@ -14,26 +20,105 @@ int main(int argc, char* argv[]) { SetConsoleOutputCP(CP_UTF8); #endif + bool has_game_argument = false; + std::string game_path; + + // Map of argument strings to lambda functions + std::unordered_map> arg_map = { + {"-h", + [&](int&) { + std::cout << "Usage: shadps4 [options] \n" + "Options:\n" + " -g, --game Specify game path to launch\n" + " -p, --patch Apply specified patch file\n" + " -f, --fullscreen Specify window initial fullscreen " + "state. Does not overwrite the config file.\n" + " -h, --help Display this help message\n"; + exit(0); + }}, + {"--help", [&](int& i) { arg_map["-h"](i); }}, + + {"-g", + [&](int& i) { + if (i + 1 < argc) { + game_path = argv[++i]; + has_game_argument = true; + } else { + std::cerr << "Error: Missing argument for -g/--game\n"; + exit(1); + } + }}, + {"--game", [&](int& i) { arg_map["-g"](i); }}, + + {"-p", + [&](int& i) { + if (i + 1 < argc) { + MemoryPatcher::patchFile = argv[++i]; + } else { + std::cerr << "Error: Missing argument for -p/--patch\n"; + exit(1); + } + }}, + {"--patch", [&](int& i) { arg_map["-p"](i); }}, + {"-f", + [&](int& i) { + if (++i >= argc) { + std::cerr << "Error: Missing argument for -f/--fullscreen\n"; + exit(1); + } + std::string f_param(argv[i]); + bool is_fullscreen; + if (f_param == "true") { + is_fullscreen = true; + } else if (f_param == "false") { + is_fullscreen = false; + } else { + std::cerr + << "Error: Invalid argument for -f/--fullscreen. Use 'true' or 'false'.\n"; + exit(1); + } + // Set fullscreen mode without saving it to config file + Config::setFullscreenMode(is_fullscreen); + }}, + {"--fullscreen", [&](int& i) { arg_map["-f"](i); }}, + }; + if (argc == 1) { - fmt::print("Usage: {} \n", argv[0]); - return -1; - } - // check if eboot file exists - if (!std::filesystem::exists(argv[1])) { - fmt::print("Eboot.bin file not found\n"); + int dummy = 0; // one does not simply pass 0 directly + arg_map.at("-h")(dummy); return -1; } - for (int i = 0; i < argc; i++) { - std::string curArg = argv[i]; - if (curArg == "-p") { - std::string patchFile = argv[i + 1]; - MemoryPatcher::patchFile = patchFile; + // Parse command-line arguments using the map + for (int i = 1; i < argc; ++i) { + std::string cur_arg = argv[i]; + auto it = arg_map.find(cur_arg); + if (it != arg_map.end()) { + it->second(i); // Call the associated lambda function + } else if (i == argc - 1 && !has_game_argument) { + // Assume the last argument is the game file if not specified via -g/--game + game_path = argv[i]; + has_game_argument = true; + } else { + std::cerr << "Unknown argument: " << cur_arg << ", see --help for info.\n"; + return 1; } } + if (!has_game_argument) { + std::cerr << "Error: Please provide a game path or ID.\n"; + exit(1); + } + + // Check if the game path or ID exists + if (!std::filesystem::exists(game_path)) { + std::cerr << "Error: Game file not found\n"; + return -1; + } + + // Run the emulator with the specified game Core::Emulator emulator; - emulator.Run(argv[1]); + emulator.Run(game_path); return 0; } diff --git a/src/qt_gui/cheats_patches.cpp b/src/qt_gui/cheats_patches.cpp index a35136f12..3e7c22451 100644 --- a/src/qt_gui/cheats_patches.cpp +++ b/src/qt_gui/cheats_patches.cpp @@ -39,7 +39,7 @@ CheatsPatches::CheatsPatches(const QString& gameName, const QString& gameSerial, m_gameSize(gameSize), m_gameImage(gameImage), manager(new QNetworkAccessManager(this)) { setupUI(); resize(500, 400); - setWindowTitle(tr("Cheats / Patches")); + setWindowTitle(tr("Cheats / Patches for ") + m_gameName); } CheatsPatches::~CheatsPatches() {} diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index 823ad921c..7da7341da 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -451,7 +451,7 @@ private: pShellLink->SetWorkingDirectory((LPCWSTR)QFileInfo(exePath).absolutePath().utf16()); // Set arguments, eboot.bin file location - QString arguments = QString("\"%1\"").arg(targetPath); + QString arguments = QString("-g \"%1\"").arg(targetPath); pShellLink->SetArguments((LPCWSTR)arguments.utf16()); // Set the icon for the shortcut diff --git a/src/qt_gui/main.cpp b/src/qt_gui/main.cpp index da8804f69..318245053 100644 --- a/src/qt_gui/main.cpp +++ b/src/qt_gui/main.cpp @@ -1,6 +1,9 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "iostream" +#include "unordered_map" + #include "common/config.h" #include "common/memory_patcher.h" #include "core/file_sys/fs.h" @@ -26,10 +29,90 @@ int main(int argc, char* argv[]) { const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); Config::load(user_dir / "config.toml"); - // Check if elf or eboot.bin path was passed as a command line argument bool has_command_line_argument = argc > 1; + bool show_gui = false, has_game_argument = false; + std::string gamePath; - // Check if the game install directory is set + // Map of argument strings to lambda functions + std::unordered_map> arg_map = { + {"-h", + [&](int&) { + std::cout << "Usage: shadps4 [options]\n" + "Options:\n" + " No arguments: Opens the GUI.\n" + " -g, --game Specify or " + " to launch\n" + " -p, --patch Apply specified patch file\n" + " -s, --show-gui Show the GUI\n" + " -f, --fullscreen Specify window initial fullscreen " + "state. Does not overwrite the config file.\n" + " -h, --help Display this help message\n"; + exit(0); + }}, + {"--help", [&](int& i) { arg_map["-h"](i); }}, // Redirect --help to -h + + {"-s", [&](int&) { show_gui = true; }}, + {"--show-gui", [&](int& i) { arg_map["-s"](i); }}, + + {"-g", + [&](int& i) { + if (i + 1 < argc) { + gamePath = argv[++i]; + has_game_argument = true; + } else { + std::cerr << "Error: Missing argument for -g/--game\n"; + exit(1); + } + }}, + {"--game", [&](int& i) { arg_map["-g"](i); }}, + + {"-p", + [&](int& i) { + if (i + 1 < argc) { + MemoryPatcher::patchFile = argv[++i]; + } else { + std::cerr << "Error: Missing argument for -p\n"; + exit(1); + } + }}, + {"--patch", [&](int& i) { arg_map["-p"](i); }}, + {"-f", + [&](int& i) { + if (++i >= argc) { + std::cerr + << "Error: Invalid argument for -f/--fullscreen. Use 'true' or 'false'.\n"; + exit(1); + } + std::string f_param(argv[i]); + bool is_fullscreen; + if (f_param == "true") { + is_fullscreen = true; + } else if (f_param == "false") { + is_fullscreen = false; + } else { + std::cerr + << "Error: Invalid argument for -f/--fullscreen. Use 'true' or 'false'.\n"; + exit(1); + } + // Set fullscreen mode without saving it to config file + Config::setFullscreenMode(is_fullscreen); + }}, + {"--fullscreen", [&](int& i) { arg_map["-f"](i); }}, + }; + + // Parse command-line arguments using the map + for (int i = 1; i < argc; ++i) { + std::string cur_arg = argv[i]; + auto it = arg_map.find(cur_arg); + if (it != arg_map.end()) { + it->second(i); // Call the associated lambda function + } else { + std::cerr << "Unknown argument: " << cur_arg << ", see --help for info.\n"; + return 1; + } + } + + // If no game directory is set and no command line argument, prompt for it if (Config::getGameInstallDirs().empty() && !has_command_line_argument) { GameInstallDialog dlg; dlg.exec(); @@ -40,21 +123,46 @@ int main(int argc, char* argv[]) { // Initialize the main window MainWindow* m_main_window = new MainWindow(nullptr); - m_main_window->Init(); - - // Check for command line arguments - if (has_command_line_argument) { - Core::Emulator emulator; - for (int i = 0; i < argc; i++) { - std::string curArg = argv[i]; - if (curArg == "-p") { - std::string patchFile = argv[i + 1]; - MemoryPatcher::patchFile = patchFile; - } - } - emulator.Run(argv[1]); + if ((has_command_line_argument && show_gui) || !has_command_line_argument) { + m_main_window->Init(); } - // Run the Qt application + if (has_command_line_argument && !has_game_argument) { + std::cerr << "Error: Please provide a game path or ID.\n"; + exit(1); + } + + // Process game path or ID if provided + if (has_game_argument) { + std::filesystem::path game_file_path(gamePath); + + // Check if the provided path is a valid file + if (!std::filesystem::exists(game_file_path)) { + // If not a file, treat it as a game ID and search in install directories + bool game_found = false; + for (const auto& install_dir : Config::getGameInstallDirs()) { + auto potential_game_path = install_dir / gamePath / "eboot.bin"; + if (std::filesystem::exists(potential_game_path)) { + game_file_path = potential_game_path; + game_found = true; + break; + } + } + if (!game_found) { + std::cerr << "Error: Game ID or file path not found: " << gamePath << std::endl; + return 1; + } + } + + // Run the emulator with the resolved game path + Core::Emulator emulator; + emulator.Run(game_file_path.string()); + if (!show_gui) { + return 0; // Exit after running the emulator without showing the GUI + } + } + + // Show the main window and run the Qt application + m_main_window->show(); return a.exec(); -} +} \ No newline at end of file diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index e1f683678..7b4e7ece6 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -23,6 +23,9 @@ #include "main_window.h" #include "settings_dialog.h" #include "video_core/renderer_vulkan/vk_instance.h" +#ifdef ENABLE_DISCORD_RPC +#include "common/discord_rpc_handler.h" +#endif MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); @@ -77,12 +80,13 @@ bool MainWindow::Init() { "Games: " + QString::number(numGames) + " (" + QString::number(duration.count()) + "ms)"; statusBar->showMessage(statusMessage); - // Initialize Discord RPC +#ifdef ENABLE_DISCORD_RPC if (Config::getEnableDiscordRPC()) { auto* rpc = Common::Singleton::Instance(); rpc->init(); rpc->setStatusIdling(); } +#endif return true; } @@ -99,6 +103,7 @@ void MainWindow::CreateActions() { m_list_mode_act_group = new QActionGroup(this); m_list_mode_act_group->addAction(ui->setlistModeListAct); m_list_mode_act_group->addAction(ui->setlistModeGridAct); + m_list_mode_act_group->addAction(ui->setlistElfAct); // create action group for themes m_theme_act_group = new QActionGroup(this); @@ -364,7 +369,7 @@ void MainWindow::CreateConnects() { ui->sizeSlider->setEnabled(true); ui->sizeSlider->setSliderPosition(slider_pos_grid); }); - // Elf + // Elf Viewer connect(ui->setlistElfAct, &QAction::triggered, m_dock_widget.data(), [this]() { BackgroundMusicPlayer::getInstance().stopMusic(); m_dock_widget->setWidget(m_elf_viewer.data()); @@ -624,10 +629,12 @@ void MainWindow::ConfigureGuiFromSettings() { Config::getMainWindowGeometryW(), Config::getMainWindowGeometryH()); ui->showGameListAct->setChecked(true); - if (isTableList) { + if (Config::getTableMode() == 0) { ui->setlistModeListAct->setChecked(true); - } else { + } else if (Config::getTableMode() == 1) { ui->setlistModeGridAct->setChecked(true); + } else if (Config::getTableMode() == 2) { + ui->setlistElfAct->setChecked(true); } BackgroundMusicPlayer::getInstance().setVolume(Config::getBGMvolume()); } @@ -960,6 +967,7 @@ void MainWindow::SetUiIcons(bool isWhite) { ui->gameInstallPathAct->setIcon(RecolorIcon(ui->gameInstallPathAct->icon(), isWhite)); ui->menuThemes->setIcon(RecolorIcon(ui->menuThemes->icon(), isWhite)); ui->menuGame_List_Icons->setIcon(RecolorIcon(ui->menuGame_List_Icons->icon(), isWhite)); + ui->menuUtils->setIcon(RecolorIcon(ui->menuUtils->icon(), isWhite)); ui->playButton->setIcon(RecolorIcon(ui->playButton->icon(), isWhite)); ui->pauseButton->setIcon(RecolorIcon(ui->pauseButton->icon(), isWhite)); ui->stopButton->setIcon(RecolorIcon(ui->stopButton->icon(), isWhite)); diff --git a/src/qt_gui/main_window.h b/src/qt_gui/main_window.h index 7dc85f0a7..5ae2540ec 100644 --- a/src/qt_gui/main_window.h +++ b/src/qt_gui/main_window.h @@ -9,7 +9,6 @@ #include "background_music_player.h" #include "common/config.h" -#include "common/discord_rpc_handler.h" #include "common/path_util.h" #include "core/file_format/psf.h" #include "core/file_sys/fs.h" diff --git a/src/qt_gui/main_window_ui.h b/src/qt_gui/main_window_ui.h index edfb4b5b0..2518601c9 100644 --- a/src/qt_gui/main_window_ui.h +++ b/src/qt_gui/main_window_ui.h @@ -111,15 +111,14 @@ public: setIconSizeLargeAct->setCheckable(true); setlistModeListAct = new QAction(MainWindow); setlistModeListAct->setObjectName("setlistModeListAct"); - setlistModeListAct->setCheckable(true); - setlistModeListAct->setChecked(true); setlistModeListAct->setIcon(QIcon(":images/list_icon.png")); + setlistModeListAct->setCheckable(true); setlistModeGridAct = new QAction(MainWindow); setlistModeGridAct->setObjectName("setlistModeGridAct"); - setlistModeGridAct->setCheckable(true); setlistModeGridAct->setIcon(QIcon(":images/grid_icon.png")); + setlistModeGridAct->setCheckable(true); setlistElfAct = new QAction(MainWindow); - setlistElfAct->setObjectName("setlistModeGridAct"); + setlistElfAct->setObjectName("setlistElfAct"); setlistElfAct->setCheckable(true); gameInstallPathAct = new QAction(MainWindow); gameInstallPathAct->setObjectName("gameInstallPathAct"); @@ -254,6 +253,7 @@ public: menuSettings->setObjectName("menuSettings"); menuUtils = new QMenu(menuSettings); menuUtils->setObjectName("menuUtils"); + menuUtils->setIcon(QIcon(":images/utils_icon.png")); menuThemes = new QMenu(menuView); menuThemes->setObjectName("menuThemes"); menuThemes->setIcon(QIcon(":images/themes_icon.png")); diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 83582663a..1fd4b6e8b 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -6,6 +6,9 @@ #include #include +#ifdef ENABLE_DISCORD_RPC +#include "common/discord_rpc_handler.h" +#endif #ifdef ENABLE_UPDATER #include "check_update.h" #endif @@ -173,6 +176,7 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge BackgroundMusicPlayer::getInstance().setVolume(val); }); +#ifdef ENABLE_DISCORD_RPC connect(ui->discordRPCCheckbox, &QCheckBox::stateChanged, this, [](int val) { Config::setEnableDiscordRPC(val); auto* rpc = Common::Singleton::Instance(); @@ -183,6 +187,7 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge rpc->shutdown(); } }); +#endif } // Input TAB diff --git a/src/qt_gui/translations/ar.ts b/src/qt_gui/translations/ar.ts index 25e215183..45a030062 100644 --- a/src/qt_gui/translations/ar.ts +++ b/src/qt_gui/translations/ar.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - الغش / التصحيحات + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index 14c42f1d9..fa7e9c50b 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Snyd / Patches + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/de.ts b/src/qt_gui/translations/de.ts index 64a6c6480..0fa534bb9 100644 --- a/src/qt_gui/translations/de.ts +++ b/src/qt_gui/translations/de.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Cheats / Patches + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/el.ts b/src/qt_gui/translations/el.ts index e064f8c26..5233454b9 100644 --- a/src/qt_gui/translations/el.ts +++ b/src/qt_gui/translations/el.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Cheats / Patches + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index 0fbfc31e5..36d530a50 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Cheats / Patches + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index 5d637249e..0598e04f3 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Trucos / Parches + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index 55a2fdf53..3cd72cac3 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - چیت / پچ ها + Cheats / Patches for + Cheats / Patches for ا diff --git a/src/qt_gui/translations/fi.ts b/src/qt_gui/translations/fi.ts index 4d160bf6b..8c9518d0f 100644 --- a/src/qt_gui/translations/fi.ts +++ b/src/qt_gui/translations/fi.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Huijaukset / Korjaukset + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index 39cd11bf6..a9903d43c 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -62,7 +62,7 @@ Select which directory you want to install to. - Select which directory you want to install to. + Sélectionnez le répertoire où vous souhaitez effectuer l'installation. @@ -158,22 +158,22 @@ Delete... - Delete... + Supprimer... Delete Game - Delete Game + Supprimer jeu Delete Update - Delete Update + Supprimer MÀJ Delete DLC - Delete DLC + Supprimer DLC @@ -203,7 +203,7 @@ Game - Game + Jeu @@ -213,17 +213,17 @@ This game has no update to delete! - This game has no update to delete! + Ce jeu n'a pas de mise à jour à supprimer! Update - Update + Mise à jour This game has no DLC to delete! - This game has no DLC to delete! + Ce jeu n'a pas de DLC à supprimer! @@ -233,12 +233,12 @@ Delete %1 - Delete %1 + Supprime %1 Are you sure you want to delete %1's %2 directory? - Are you sure you want to delete %1's %2 directory? + Êtes vous sûr de vouloir supprimer le répertoire %1 %2 ? @@ -495,7 +495,7 @@ Enable Separate Update Folder - Enable Separate Update Folder + Dossier séparé pour les mises à jours @@ -510,7 +510,7 @@ Enable Discord Rich Presence - Activer Discord Rich Presence + Activer la présence Discord @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Cheats/Patches + Cheats / Patches for + Cheats/Patchs pour @@ -1159,7 +1159,7 @@ separateUpdatesCheckBox - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. + Dossier séparé pour les mises à jours:\nInstalle les mises à jours des jeux dans un dossier séparé pour une gestion plus facile. @@ -1169,7 +1169,7 @@ ps4proCheckBox - Est-ce un PS4 Pro:\nFait en sorte que l'émulateur se comporte comme un PS4 PRO, ce qui peut activer des fonctionnalités spéciales dans les jeux qui le prennent en charge. + Mode PS4 Pro:\nFait en sorte que l'émulateur se comporte comme un PS4 PRO, ce qui peut activer des fonctionnalités spéciales dans les jeux qui le prennent en charge. diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index a43b8d371..135fc4231 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Csalások / Javítások + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/id.ts b/src/qt_gui/translations/id.ts index d616f1cf3..5c6148b86 100644 --- a/src/qt_gui/translations/id.ts +++ b/src/qt_gui/translations/id.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Cheat / Patch + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts index c59289314..b496cc330 100644 --- a/src/qt_gui/translations/it.ts +++ b/src/qt_gui/translations/it.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Trucchi / Patch + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index f4a4b15ad..3cee79951 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - チート / パッチ + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index 2fa3ee153..ea449ebc1 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Cheats / Patches + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index 16aaf5d86..dd467283f 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Sukčiavimai / Pataisos + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/nb_NO.ts b/src/qt_gui/translations/nb_NO.ts index e02f24182..de0a88b73 100644 --- a/src/qt_gui/translations/nb_NO.ts +++ b/src/qt_gui/translations/nb_NO.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Juks / Programrettelse + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/nl.ts b/src/qt_gui/translations/nl.ts index b0cfaff5e..399aef8be 100644 --- a/src/qt_gui/translations/nl.ts +++ b/src/qt_gui/translations/nl.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Cheats / Patches + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index 4d11c13f6..730b37124 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Kody / poprawki + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index f1d3631d8..7ea63b9fb 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Cheats / Patches + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index fff0bcddb..d73b1be6c 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Cheats / Patches + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index 052623235..ab7e2c40e 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Читы и патчи + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index 5715371bf..bc8f24162 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -118,22 +118,22 @@ Open Folder... - Hapni Dosjen... + Hap Dosjen... Open Game Folder - Hapni Dosjen e Lojës + Hap Dosjen e Lojës Open Save Data Folder - Hapni Dosjen e të Dhënave të Ruajtura + Hap Dosjen e të Dhënave të Ruajtura Open Log Folder - Hapni Dosjen e Regjistrimeve + Hap Dosjen e Ditarit @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Mashtrime / Arna + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index 335465778..5944f980f 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Hileler / Yamalar + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/uk_UA.ts b/src/qt_gui/translations/uk_UA.ts index 31bfe9dba..b28035335 100644 --- a/src/qt_gui/translations/uk_UA.ts +++ b/src/qt_gui/translations/uk_UA.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Чити та Патчі + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index 223cb9ed0..b5ae8bd39 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - Cheat / Bản vá + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index 4fe1f7c42..989e71071 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - 作弊码 / 补丁 + Cheats / Patches for + Cheats / Patches for diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index 4db00775d..b650a74ea 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -840,8 +840,8 @@ CheatsPatches - Cheats / Patches - 作弊碼 / 修補檔 + Cheats / Patches for + Cheats / Patches for diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index ad7d1b4a6..d95e8d634 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -6,9 +6,9 @@ #include #include #include + #include "common/assert.h" #include "common/config.h" -#include "common/version.h" #include "core/libraries/pad/pad.h" #include "imgui/renderer/imgui_core.h" #include "input/controller.h" @@ -21,6 +21,45 @@ namespace Frontend { +using namespace Libraries::Pad; + +static OrbisPadButtonDataOffset SDLGamepadToOrbisButton(u8 button) { + switch (button) { + case SDL_GAMEPAD_BUTTON_DPAD_DOWN: + return OrbisPadButtonDataOffset::Down; + case SDL_GAMEPAD_BUTTON_DPAD_UP: + return OrbisPadButtonDataOffset::Up; + case SDL_GAMEPAD_BUTTON_DPAD_LEFT: + return OrbisPadButtonDataOffset::Left; + case SDL_GAMEPAD_BUTTON_DPAD_RIGHT: + return OrbisPadButtonDataOffset::Right; + case SDL_GAMEPAD_BUTTON_SOUTH: + return OrbisPadButtonDataOffset::Cross; + case SDL_GAMEPAD_BUTTON_NORTH: + return OrbisPadButtonDataOffset::Triangle; + case SDL_GAMEPAD_BUTTON_WEST: + return OrbisPadButtonDataOffset::Square; + case SDL_GAMEPAD_BUTTON_EAST: + return OrbisPadButtonDataOffset::Circle; + case SDL_GAMEPAD_BUTTON_START: + return OrbisPadButtonDataOffset::Options; + case SDL_GAMEPAD_BUTTON_TOUCHPAD: + return OrbisPadButtonDataOffset::TouchPad; + case SDL_GAMEPAD_BUTTON_BACK: + return OrbisPadButtonDataOffset::TouchPad; + case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER: + return OrbisPadButtonDataOffset::L1; + case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER: + return OrbisPadButtonDataOffset::R1; + case SDL_GAMEPAD_BUTTON_LEFT_STICK: + return OrbisPadButtonDataOffset::L3; + case SDL_GAMEPAD_BUTTON_RIGHT_STICK: + return OrbisPadButtonDataOffset::R3; + default: + return OrbisPadButtonDataOffset::None; + } +} + static Uint32 SDLCALL PollController(void* userdata, SDL_TimerID timer_id, Uint32 interval) { auto* controller = reinterpret_cast(userdata); return controller->Poll(); @@ -80,7 +119,7 @@ WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameController* controller_ WindowSDL::~WindowSDL() = default; -void WindowSDL::waitEvent() { +void WindowSDL::WaitEvent() { // Called on main thread SDL_Event event; @@ -96,16 +135,16 @@ void WindowSDL::waitEvent() { case SDL_EVENT_WINDOW_RESIZED: case SDL_EVENT_WINDOW_MAXIMIZED: case SDL_EVENT_WINDOW_RESTORED: - onResize(); + OnResize(); break; case SDL_EVENT_WINDOW_MINIMIZED: case SDL_EVENT_WINDOW_EXPOSED: is_shown = event.type == SDL_EVENT_WINDOW_EXPOSED; - onResize(); + OnResize(); break; case SDL_EVENT_KEY_DOWN: case SDL_EVENT_KEY_UP: - onKeyPress(&event); + OnKeyPress(&event); break; case SDL_EVENT_GAMEPAD_BUTTON_DOWN: case SDL_EVENT_GAMEPAD_BUTTON_UP: @@ -115,7 +154,7 @@ void WindowSDL::waitEvent() { case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN: case SDL_EVENT_GAMEPAD_TOUCHPAD_UP: case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION: - onGamepadEvent(&event); + OnGamepadEvent(&event); break; case SDL_EVENT_QUIT: is_open = false; @@ -125,18 +164,16 @@ void WindowSDL::waitEvent() { } } -void WindowSDL::initTimers() { +void WindowSDL::InitTimers() { SDL_AddTimer(100, &PollController, controller); } -void WindowSDL::onResize() { +void WindowSDL::OnResize() { SDL_GetWindowSizeInPixels(window, &width, &height); ImGui::Core::OnResize(); } -void WindowSDL::onKeyPress(const SDL_Event* event) { - using Libraries::Pad::OrbisPadButtonDataOffset; - +void WindowSDL::OnKeyPress(const SDL_Event* event) { #ifdef __APPLE__ // Use keys that are more friendly for keyboards without a keypad. // Once there are key binding options this won't be necessary. @@ -151,38 +188,38 @@ void WindowSDL::onKeyPress(const SDL_Event* event) { constexpr SDL_Keycode TriangleKey = SDLK_KP_8; #endif - u32 button = 0; + auto button = OrbisPadButtonDataOffset::None; Input::Axis axis = Input::Axis::AxisMax; int axisvalue = 0; int ax = 0; std::string backButtonBehavior = Config::getBackButtonBehavior(); switch (event->key.key) { case SDLK_UP: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_UP; + button = OrbisPadButtonDataOffset::Up; break; case SDLK_DOWN: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_DOWN; + button = OrbisPadButtonDataOffset::Down; break; case SDLK_LEFT: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_LEFT; + button = OrbisPadButtonDataOffset::Left; break; case SDLK_RIGHT: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_RIGHT; + button = OrbisPadButtonDataOffset::Right; break; case TriangleKey: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TRIANGLE; + button = OrbisPadButtonDataOffset::Triangle; break; case CircleKey: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CIRCLE; + button = OrbisPadButtonDataOffset::Circle; break; case CrossKey: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CROSS; + button = OrbisPadButtonDataOffset::Cross; break; case SquareKey: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_SQUARE; + button = OrbisPadButtonDataOffset::Square; break; case SDLK_RETURN: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_OPTIONS; + button = OrbisPadButtonDataOffset::Options; break; case SDLK_A: axis = Input::Axis::LeftX; @@ -257,19 +294,19 @@ void WindowSDL::onKeyPress(const SDL_Event* event) { ax = Input::GetAxis(-0x80, 0x80, axisvalue); break; case SDLK_X: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L3; + button = OrbisPadButtonDataOffset::L3; break; case SDLK_M: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R3; + button = OrbisPadButtonDataOffset::R3; break; case SDLK_Q: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L1; + button = OrbisPadButtonDataOffset::L1; break; case SDLK_U: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R1; + button = OrbisPadButtonDataOffset::R1; break; case SDLK_E: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L2; + button = OrbisPadButtonDataOffset::L2; axis = Input::Axis::TriggerLeft; if (event->type == SDL_EVENT_KEY_DOWN) { axisvalue += 255; @@ -279,7 +316,7 @@ void WindowSDL::onKeyPress(const SDL_Event* event) { ax = Input::GetAxis(0, 0x80, axisvalue); break; case SDLK_O: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R2; + button = OrbisPadButtonDataOffset::R2; axis = Input::Axis::TriggerRight; if (event->type == SDL_EVENT_KEY_DOWN) { axisvalue += 255; @@ -294,9 +331,9 @@ void WindowSDL::onKeyPress(const SDL_Event* event) { : (backButtonBehavior == "right" ? 0.75f : 0.5f); // trigger a touchpad event so that the touchpad emulation for back button works controller->SetTouchpadState(0, true, x, 0.5f); - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TOUCH_PAD; + button = OrbisPadButtonDataOffset::TouchPad; } else { - button = 0; + button = {}; } break; case SDLK_F11: @@ -317,7 +354,7 @@ void WindowSDL::onKeyPress(const SDL_Event* event) { default: break; } - if (button != 0) { + if (button != OrbisPadButtonDataOffset::None) { controller->CheckButton(0, button, event->type == SDL_EVENT_KEY_DOWN); } if (axis != Input::Axis::AxisMax) { @@ -325,10 +362,8 @@ void WindowSDL::onKeyPress(const SDL_Event* event) { } } -void WindowSDL::onGamepadEvent(const SDL_Event* event) { - using Libraries::Pad::OrbisPadButtonDataOffset; - - u32 button = 0; +void WindowSDL::OnGamepadEvent(const SDL_Event* event) { + auto button = OrbisPadButtonDataOffset::None; Input::Axis axis = Input::Axis::AxisMax; switch (event->type) { case SDL_EVENT_GAMEPAD_ADDED: @@ -343,25 +378,25 @@ void WindowSDL::onGamepadEvent(const SDL_Event* event) { event->gtouchpad.x, event->gtouchpad.y); break; case SDL_EVENT_GAMEPAD_BUTTON_DOWN: - case SDL_EVENT_GAMEPAD_BUTTON_UP: - button = sdlGamepadToOrbisButton(event->gbutton.button); - if (button != 0) { - if (event->gbutton.button == SDL_GAMEPAD_BUTTON_BACK) { - std::string backButtonBehavior = Config::getBackButtonBehavior(); - if (backButtonBehavior != "none") { - float x = backButtonBehavior == "left" - ? 0.25f - : (backButtonBehavior == "right" ? 0.75f : 0.5f); - // trigger a touchpad event so that the touchpad emulation for back button works - controller->SetTouchpadState(0, true, x, 0.5f); - controller->CheckButton(0, button, - event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN); - } - } else { - controller->CheckButton(0, button, event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN); - } + case SDL_EVENT_GAMEPAD_BUTTON_UP: { + button = SDLGamepadToOrbisButton(event->gbutton.button); + if (button == OrbisPadButtonDataOffset::None) { + break; + } + if (event->gbutton.button != SDL_GAMEPAD_BUTTON_BACK) { + controller->CheckButton(0, button, event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN); + break; + } + const auto backButtonBehavior = Config::getBackButtonBehavior(); + if (backButtonBehavior != "none") { + float x = backButtonBehavior == "left" ? 0.25f + : (backButtonBehavior == "right" ? 0.75f : 0.5f); + // trigger a touchpad event so that the touchpad emulation for back button works + controller->SetTouchpadState(0, true, x, 0.5f); + controller->CheckButton(0, button, event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN); } break; + } case SDL_EVENT_GAMEPAD_AXIS_MOTION: axis = event->gaxis.axis == SDL_GAMEPAD_AXIS_LEFTX ? Input::Axis::LeftX : event->gaxis.axis == SDL_GAMEPAD_AXIS_LEFTY ? Input::Axis::LeftY @@ -383,43 +418,4 @@ void WindowSDL::onGamepadEvent(const SDL_Event* event) { } } -int WindowSDL::sdlGamepadToOrbisButton(u8 button) { - using Libraries::Pad::OrbisPadButtonDataOffset; - - switch (button) { - case SDL_GAMEPAD_BUTTON_DPAD_DOWN: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_DOWN; - case SDL_GAMEPAD_BUTTON_DPAD_UP: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_UP; - case SDL_GAMEPAD_BUTTON_DPAD_LEFT: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_LEFT; - case SDL_GAMEPAD_BUTTON_DPAD_RIGHT: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_RIGHT; - case SDL_GAMEPAD_BUTTON_SOUTH: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CROSS; - case SDL_GAMEPAD_BUTTON_NORTH: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TRIANGLE; - case SDL_GAMEPAD_BUTTON_WEST: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_SQUARE; - case SDL_GAMEPAD_BUTTON_EAST: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CIRCLE; - case SDL_GAMEPAD_BUTTON_START: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_OPTIONS; - case SDL_GAMEPAD_BUTTON_TOUCHPAD: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TOUCH_PAD; - case SDL_GAMEPAD_BUTTON_BACK: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TOUCH_PAD; - case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L1; - case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R1; - case SDL_GAMEPAD_BUTTON_LEFT_STICK: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L3; - case SDL_GAMEPAD_BUTTON_RIGHT_STICK: - return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R3; - default: - return 0; - } -} - } // namespace Frontend diff --git a/src/sdl_window.h b/src/sdl_window.h index ec8de354b..78d0e582f 100644 --- a/src/sdl_window.h +++ b/src/sdl_window.h @@ -46,36 +46,33 @@ public: std::string_view window_title); ~WindowSDL(); - s32 getWidth() const { + s32 GetWidth() const { return width; } - s32 getHeight() const { + s32 GetHeight() const { return height; } - bool isOpen() const { + bool IsOpen() const { return is_open; } - [[nodiscard]] SDL_Window* GetSdlWindow() const { + [[nodiscard]] SDL_Window* GetSDLWindow() const { return window; } - WindowSystemInfo getWindowInfo() const { + WindowSystemInfo GetWindowInfo() const { return window_info; } - void waitEvent(); - - void initTimers(); + void WaitEvent(); + void InitTimers(); private: - void onResize(); - void onKeyPress(const SDL_Event* event); - void onGamepadEvent(const SDL_Event* event); - - int sdlGamepadToOrbisButton(u8 button); + void OnResize(); + void OnKeyPress(const SDL_Event* event); + void OnGamepadEvent(const SDL_Event* event); private: s32 width; diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index e84908a57..23800fc49 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -206,7 +206,7 @@ Id DefineMain(EmitContext& ctx, const IR::Program& program) { return main; } -void SetupCapabilities(const Info& info, EmitContext& ctx) { +void SetupCapabilities(const Info& info, const Profile& profile, EmitContext& ctx) { ctx.AddCapability(spv::Capability::Image1D); ctx.AddCapability(spv::Capability::Sampled1D); ctx.AddCapability(spv::Capability::ImageQuery); @@ -251,6 +251,10 @@ void SetupCapabilities(const Info& info, EmitContext& ctx) { if (info.stage == Stage::Geometry) { ctx.AddCapability(spv::Capability::Geometry); } + if (info.stage == Stage::Fragment && profile.needs_manual_interpolation) { + ctx.AddExtension("SPV_KHR_fragment_shader_barycentric"); + ctx.AddCapability(spv::Capability::FragmentBarycentricKHR); + } } void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) { @@ -280,7 +284,7 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) { ctx.AddExtension("SPV_EXT_demote_to_helper_invocation"); ctx.AddCapability(spv::Capability::DemoteToHelperInvocationEXT); } - if (info.stores.Get(IR::Attribute::Depth)) { + if (info.stores.GetAny(IR::Attribute::Depth)) { ctx.AddExecutionMode(main, spv::ExecutionMode::DepthReplacing); } break; @@ -342,7 +346,7 @@ std::vector EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_in EmitContext ctx{profile, runtime_info, program.info, binding}; const Id main{DefineMain(ctx, program)}; DefineEntryPoint(program, ctx, main); - SetupCapabilities(program.info, ctx); + SetupCapabilities(program.info, profile, ctx); SetupFloatMode(ctx, profile, runtime_info, main); PatchPhiNodes(program, ctx); binding.user_data += program.info.ud_mask.NumRegs(); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp index a58b2778f..ce65a5ccb 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp @@ -60,6 +60,18 @@ Id EmitSharedAtomicSMin32(EmitContext& ctx, Id offset, Id value) { return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicSMin); } +Id EmitSharedAtomicAnd32(EmitContext& ctx, Id offset, Id value) { + return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicAnd); +} + +Id EmitSharedAtomicOr32(EmitContext& ctx, Id offset, Id value) { + return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicOr); +} + +Id EmitSharedAtomicXor32(EmitContext& ctx, Id offset, Id value) { + return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicXor); +} + Id EmitBufferAtomicIAdd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) { return BufferAtomicU32(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicIAdd); } 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 064200d99..d005169c4 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 @@ -5,7 +5,7 @@ #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" #include "shader_recompiler/backend/spirv/spirv_emit_context.h" -#include +#include namespace Shader::Backend::SPIRV { namespace { @@ -171,54 +171,38 @@ Id EmitReadStepRate(EmitContext& ctx, int rate_idx) { rate_idx == 0 ? ctx.u32_zero_value : ctx.u32_one_value)); } +Id EmitGetAttributeForGeometry(EmitContext& ctx, IR::Attribute attr, u32 comp, u32 index) { + if (IR::IsPosition(attr)) { + ASSERT(attr == IR::Attribute::Position0); + const auto position_arr_ptr = ctx.TypePointer(spv::StorageClass::Input, ctx.F32[4]); + const auto pointer{ + ctx.OpAccessChain(position_arr_ptr, ctx.gl_in, ctx.ConstU32(index), ctx.ConstU32(0u))}; + const auto position_comp_ptr = ctx.TypePointer(spv::StorageClass::Input, ctx.F32[1]); + return ctx.OpLoad(ctx.F32[1], + ctx.OpAccessChain(position_comp_ptr, pointer, ctx.ConstU32(comp))); + } + + if (IR::IsParam(attr)) { + const u32 param_id{u32(attr) - u32(IR::Attribute::Param0)}; + const auto param = ctx.input_params.at(param_id).id; + const auto param_arr_ptr = ctx.TypePointer(spv::StorageClass::Input, ctx.F32[4]); + const auto pointer{ctx.OpAccessChain(param_arr_ptr, param, ctx.ConstU32(index))}; + const auto position_comp_ptr = ctx.TypePointer(spv::StorageClass::Input, ctx.F32[1]); + return ctx.OpLoad(ctx.F32[1], + ctx.OpAccessChain(position_comp_ptr, pointer, ctx.ConstU32(comp))); + } + UNREACHABLE(); +} + Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp, u32 index) { if (ctx.info.stage == Stage::Geometry) { - if (IR::IsPosition(attr)) { - ASSERT(attr == IR::Attribute::Position0); - const auto position_arr_ptr = ctx.TypePointer(spv::StorageClass::Input, ctx.F32[4]); - const auto pointer{ctx.OpAccessChain(position_arr_ptr, ctx.gl_in, ctx.ConstU32(index), - ctx.ConstU32(0u))}; - const auto position_comp_ptr = ctx.TypePointer(spv::StorageClass::Input, ctx.F32[1]); - return ctx.OpLoad(ctx.F32[1], - ctx.OpAccessChain(position_comp_ptr, pointer, ctx.ConstU32(comp))); - } - - if (IR::IsParam(attr)) { - const u32 param_id{u32(attr) - u32(IR::Attribute::Param0)}; - const auto param = ctx.input_params.at(param_id).id; - const auto param_arr_ptr = ctx.TypePointer(spv::StorageClass::Input, ctx.F32[4]); - const auto pointer{ctx.OpAccessChain(param_arr_ptr, param, ctx.ConstU32(index))}; - const auto position_comp_ptr = ctx.TypePointer(spv::StorageClass::Input, ctx.F32[1]); - return ctx.OpLoad(ctx.F32[1], - ctx.OpAccessChain(position_comp_ptr, pointer, ctx.ConstU32(comp))); - } - UNREACHABLE(); + return EmitGetAttributeForGeometry(ctx, attr, comp, index); } if (IR::IsParam(attr)) { const u32 index{u32(attr) - u32(IR::Attribute::Param0)}; const auto& param{ctx.input_params.at(index)}; - if (param.buffer_handle < 0) { - if (!ValidId(param.id)) { - // Attribute is disabled or varying component is not written - return ctx.ConstF32(comp == 3 ? 1.0f : 0.0f); - } - - Id result; - if (param.is_default) { - result = ctx.OpCompositeExtract(param.component_type, param.id, comp); - } else if (param.num_components > 1) { - const Id pointer{ - ctx.OpAccessChain(param.pointer_type, param.id, ctx.ConstU32(comp))}; - result = ctx.OpLoad(param.component_type, pointer); - } else { - result = ctx.OpLoad(param.component_type, param.id); - } - if (param.is_integer) { - result = ctx.OpBitcast(ctx.F32[1], result); - } - return result; - } else { + if (param.buffer_handle >= 0) { const auto step_rate = EmitReadStepRate(ctx, param.id.value); const auto offset = ctx.OpIAdd( ctx.U32[1], @@ -229,7 +213,26 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp, u32 index) { ctx.ConstU32(comp)); return EmitReadConstBuffer(ctx, param.buffer_handle, offset); } + + Id result; + if (param.is_loaded) { + // Attribute is either default or manually interpolated. The id points to an already + // loaded vector. + result = ctx.OpCompositeExtract(param.component_type, param.id, comp); + } else if (param.num_components > 1) { + // Attribute is a vector and we need to access a specific component. + const Id pointer{ctx.OpAccessChain(param.pointer_type, param.id, ctx.ConstU32(comp))}; + result = ctx.OpLoad(param.component_type, pointer); + } else { + // Attribute is a single float or interger, simply load it. + result = ctx.OpLoad(param.component_type, param.id); + } + if (param.is_integer) { + result = ctx.OpBitcast(ctx.F32[1], result); + } + return result; } + switch (attr) { case IR::Attribute::FragCoord: { const Id coord = ctx.OpLoad( @@ -323,7 +326,9 @@ Id EmitLoadBufferU32x4(EmitContext& ctx, IR::Inst*, u32 handle, Id address) { Id EmitLoadBufferFormatF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) { const auto& buffer = ctx.texture_buffers[handle]; const Id tex_buffer = ctx.OpLoad(buffer.image_type, buffer.id); - const Id coord = ctx.OpIAdd(ctx.U32[1], address, buffer.coord_offset); + const Id coord = + ctx.OpIAdd(ctx.U32[1], ctx.OpShiftLeftLogical(ctx.U32[1], address, buffer.coord_shift), + buffer.coord_offset); Id texel = buffer.is_storage ? ctx.OpImageRead(buffer.result_type, tex_buffer, coord) : ctx.OpImageFetch(buffer.result_type, tex_buffer, coord); if (buffer.is_integer) { @@ -369,7 +374,9 @@ void EmitStoreBufferU32x4(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addre void EmitStoreBufferFormatF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) { const auto& buffer = ctx.texture_buffers[handle]; const Id tex_buffer = ctx.OpLoad(buffer.image_type, buffer.id); - const Id coord = ctx.OpIAdd(ctx.U32[1], address, buffer.coord_offset); + const Id coord = + ctx.OpIAdd(ctx.U32[1], ctx.OpShiftLeftLogical(ctx.U32[1], address, buffer.coord_shift), + buffer.coord_offset); if (buffer.is_integer) { value = ctx.OpBitcast(buffer.result_type, value); } diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index 40e5ea8b9..fe2660705 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp @@ -187,7 +187,8 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, const Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, u32 handle, Id lod, bool has_mips) { const auto& texture = ctx.images[handle & 0xFFFF]; const Id image = ctx.OpLoad(texture.image_type, texture.id); - const auto type = ctx.info.images[handle & 0xFFFF].type; + const auto sharp = ctx.info.images[handle & 0xFFFF].GetSharp(ctx.info); + const auto type = sharp.GetBoundType(); const Id zero = ctx.u32_zero_value; const auto mips{[&] { return has_mips ? ctx.OpImageQueryLevels(ctx.U32[1], image) : zero; }}; const bool uses_lod{type != AmdGpu::ImageType::Color2DMsaa && !texture.is_storage}; diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index 12361991a..cc3db880c 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -112,6 +112,9 @@ Id EmitSharedAtomicUMax32(EmitContext& ctx, Id offset, Id value); Id EmitSharedAtomicSMax32(EmitContext& ctx, Id offset, Id value); Id EmitSharedAtomicUMin32(EmitContext& ctx, Id offset, Id value); Id EmitSharedAtomicSMin32(EmitContext& ctx, Id offset, Id value); +Id EmitSharedAtomicAnd32(EmitContext& ctx, Id offset, Id value); +Id EmitSharedAtomicOr32(EmitContext& ctx, Id offset, Id value); +Id EmitSharedAtomicXor32(EmitContext& ctx, Id offset, Id value); Id EmitCompositeConstructU32x2(EmitContext& ctx, Id e1, Id e2); Id EmitCompositeConstructU32x3(EmitContext& ctx, Id e1, Id e2, Id e3); Id EmitCompositeConstructU32x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp index e9ffdcce8..4a22ba09f 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp @@ -8,6 +8,9 @@ namespace Shader::Backend::SPIRV { void EmitPrologue(EmitContext& ctx) { + if (ctx.stage == Stage::Fragment) { + ctx.DefineInterpolatedAttribs(); + } ctx.DefineBufferOffsets(); } diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index dc404b121..5c7278c6b 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -4,6 +4,7 @@ #include "common/assert.h" #include "common/div_ceil.h" #include "shader_recompiler/backend/spirv/spirv_emit_context.h" +#include "shader_recompiler/frontend/fetch_shader.h" #include "shader_recompiler/ir/passes/srt.h" #include "video_core/amdgpu/types.h" @@ -155,18 +156,12 @@ void EmitContext::DefineInterfaces() { } const VectorIds& GetAttributeType(EmitContext& ctx, AmdGpu::NumberFormat fmt) { - switch (fmt) { - case AmdGpu::NumberFormat::Float: - case AmdGpu::NumberFormat::Unorm: - case AmdGpu::NumberFormat::Snorm: - case AmdGpu::NumberFormat::SnormNz: - case AmdGpu::NumberFormat::Sscaled: - case AmdGpu::NumberFormat::Uscaled: - case AmdGpu::NumberFormat::Srgb: + switch (GetNumberClass(fmt)) { + case AmdGpu::NumberClass::Float: return ctx.F32; - case AmdGpu::NumberFormat::Sint: + case AmdGpu::NumberClass::Sint: return ctx.S32; - case AmdGpu::NumberFormat::Uint: + case AmdGpu::NumberClass::Uint: return ctx.U32; default: break; @@ -176,18 +171,12 @@ const VectorIds& GetAttributeType(EmitContext& ctx, AmdGpu::NumberFormat fmt) { EmitContext::SpirvAttribute EmitContext::GetAttributeInfo(AmdGpu::NumberFormat fmt, Id id, u32 num_components, bool output) { - switch (fmt) { - case AmdGpu::NumberFormat::Float: - case AmdGpu::NumberFormat::Unorm: - case AmdGpu::NumberFormat::Snorm: - case AmdGpu::NumberFormat::SnormNz: - case AmdGpu::NumberFormat::Sscaled: - case AmdGpu::NumberFormat::Uscaled: - case AmdGpu::NumberFormat::Srgb: + switch (GetNumberClass(fmt)) { + case AmdGpu::NumberClass::Float: return {id, output ? output_f32 : input_f32, F32[1], num_components, false}; - case AmdGpu::NumberFormat::Uint: + case AmdGpu::NumberClass::Uint: return {id, output ? output_u32 : input_u32, U32[1], num_components, true}; - case AmdGpu::NumberFormat::Sint: + case AmdGpu::NumberClass::Sint: return {id, output ? output_s32 : input_s32, S32[1], num_components, true}; default: break; @@ -218,10 +207,42 @@ void EmitContext::DefineBufferOffsets() { push_data_block, ConstU32(half), ConstU32(comp))}; const Id value{OpLoad(U32[1], ptr)}; tex_buffer.coord_offset = OpBitFieldUExtract(U32[1], value, ConstU32(offset), ConstU32(6U)); + tex_buffer.coord_shift = + OpBitFieldUExtract(U32[1], value, ConstU32(offset + 6U), ConstU32(2U)); Name(tex_buffer.coord_offset, fmt::format("texbuf{}_off", binding)); } } +void EmitContext::DefineInterpolatedAttribs() { + if (!profile.needs_manual_interpolation) { + return; + } + // Iterate all input attributes, load them and manually interpolate with barycentric + // coordinates. + for (s32 i = 0; i < runtime_info.fs_info.num_inputs; i++) { + const auto& input = runtime_info.fs_info.inputs[i]; + const u32 semantic = input.param_index; + auto& params = input_params[semantic]; + if (input.is_flat || params.is_loaded) { + continue; + } + const Id p_array{OpLoad(TypeArray(F32[4], ConstU32(3U)), params.id)}; + const Id p0{OpCompositeExtract(F32[4], p_array, 0U)}; + const Id p1{OpCompositeExtract(F32[4], p_array, 1U)}; + const Id p2{OpCompositeExtract(F32[4], p_array, 2U)}; + const Id p10{OpFSub(F32[4], p1, p0)}; + const Id p20{OpFSub(F32[4], p2, p0)}; + const Id bary_coord{OpLoad(F32[3], gl_bary_coord_id)}; + const Id bary_coord_y{OpCompositeExtract(F32[1], bary_coord, 1)}; + const Id bary_coord_z{OpCompositeExtract(F32[1], bary_coord, 2)}; + const Id p10_y{OpVectorTimesScalar(F32[4], p10, bary_coord_y)}; + const Id p20_z{OpVectorTimesScalar(F32[4], p20, bary_coord_z)}; + params.id = OpFAdd(F32[4], p0, OpFAdd(F32[4], p10_y, p20_z)); + Name(params.id, fmt::format("fs_in_attr{}", semantic)); + params.is_loaded = true; + } +} + Id MakeDefaultValue(EmitContext& ctx, u32 default_value) { switch (default_value) { case 0: @@ -250,33 +271,42 @@ void EmitContext::DefineInputs() { base_vertex = DefineVariable(U32[1], spv::BuiltIn::BaseVertex, spv::StorageClass::Input); instance_id = DefineVariable(U32[1], spv::BuiltIn::InstanceIndex, spv::StorageClass::Input); - for (const auto& input : info.vs_inputs) { - ASSERT(input.binding < IR::NumParams); - const Id type{GetAttributeType(*this, input.fmt)[4]}; - if (input.instance_step_rate == Info::VsInput::InstanceIdType::OverStepRate0 || - input.instance_step_rate == Info::VsInput::InstanceIdType::OverStepRate1) { - + const auto fetch_shader = Gcn::ParseFetchShader(info); + if (!fetch_shader) { + break; + } + for (const auto& attrib : fetch_shader->attributes) { + ASSERT(attrib.semantic < IR::NumParams); + const auto sharp = attrib.GetSharp(info); + const Id type{GetAttributeType(*this, sharp.GetNumberFmt())[4]}; + if (attrib.UsesStepRates()) { const u32 rate_idx = - input.instance_step_rate == Info::VsInput::InstanceIdType::OverStepRate0 ? 0 - : 1; + attrib.GetStepRate() == Gcn::VertexAttribute::InstanceIdType::OverStepRate0 ? 0 + : 1; + const u32 num_components = AmdGpu::NumComponents(sharp.GetDataFmt()); + const auto buffer = + std::ranges::find_if(info.buffers, [&attrib](const auto& buffer) { + return buffer.instance_attrib == attrib.semantic; + }); // Note that we pass index rather than Id - input_params[input.binding] = { - rate_idx, - input_u32, - U32[1], - input.num_components, - true, - false, - input.instance_data_buf, + input_params[attrib.semantic] = SpirvAttribute{ + .id = rate_idx, + .pointer_type = input_u32, + .component_type = U32[1], + .num_components = std::min(attrib.num_elements, num_components), + .is_integer = true, + .is_loaded = false, + .buffer_handle = int(buffer - info.buffers.begin()), }; } else { - Id id{DefineInput(type, input.binding)}; - if (input.instance_step_rate == Info::VsInput::InstanceIdType::Plain) { - Name(id, fmt::format("vs_instance_attr{}", input.binding)); + Id id{DefineInput(type, attrib.semantic)}; + if (attrib.GetStepRate() == Gcn::VertexAttribute::InstanceIdType::Plain) { + Name(id, fmt::format("vs_instance_attr{}", attrib.semantic)); } else { - Name(id, fmt::format("vs_in_attr{}", input.binding)); + Name(id, fmt::format("vs_in_attr{}", attrib.semantic)); } - input_params[input.binding] = GetAttributeInfo(input.fmt, id, 4, false); + input_params[attrib.semantic] = + GetAttributeInfo(sharp.GetNumberFmt(), id, 4, false); interfaces.push_back(id); } } @@ -286,6 +316,10 @@ void EmitContext::DefineInputs() { frag_coord = DefineVariable(F32[4], spv::BuiltIn::FragCoord, spv::StorageClass::Input); frag_depth = DefineVariable(F32[1], spv::BuiltIn::FragDepth, spv::StorageClass::Output); front_facing = DefineVariable(U1[1], spv::BuiltIn::FrontFacing, spv::StorageClass::Input); + if (profile.needs_manual_interpolation) { + gl_bary_coord_id = + DefineVariable(F32[3], spv::BuiltIn::BaryCoordKHR, spv::StorageClass::Input); + } for (s32 i = 0; i < runtime_info.fs_info.num_inputs; i++) { const auto& input = runtime_info.fs_info.inputs[i]; const u32 semantic = input.param_index; @@ -299,14 +333,21 @@ void EmitContext::DefineInputs() { const IR::Attribute param{IR::Attribute::Param0 + input.param_index}; const u32 num_components = info.loads.NumComponents(param); const Id type{F32[num_components]}; - const Id id{DefineInput(type, semantic)}; - if (input.is_flat) { - Decorate(id, spv::Decoration::Flat); + Id attr_id{}; + if (profile.needs_manual_interpolation && !input.is_flat) { + attr_id = DefineInput(TypeArray(type, ConstU32(3U)), semantic); + Decorate(attr_id, spv::Decoration::PerVertexKHR); + Name(attr_id, fmt::format("fs_in_attr{}_p", semantic)); + } else { + attr_id = DefineInput(type, semantic); + Name(attr_id, fmt::format("fs_in_attr{}", semantic)); + } + if (input.is_flat) { + Decorate(attr_id, spv::Decoration::Flat); } - Name(id, fmt::format("fs_in_attr{}", semantic)); input_params[semantic] = - GetAttributeInfo(AmdGpu::NumberFormat::Float, id, num_components, false); - interfaces.push_back(id); + GetAttributeInfo(AmdGpu::NumberFormat::Float, attr_id, num_components, false); + interfaces.push_back(attr_id); } break; case Stage::Compute: @@ -512,9 +553,10 @@ void EmitContext::DefineBuffers() { void EmitContext::DefineTextureBuffers() { for (const auto& desc : info.texture_buffers) { - const bool is_integer = - desc.nfmt == AmdGpu::NumberFormat::Uint || desc.nfmt == AmdGpu::NumberFormat::Sint; - const VectorIds& sampled_type{GetAttributeType(*this, desc.nfmt)}; + const auto sharp = desc.GetSharp(info); + const auto nfmt = sharp.GetNumberFmt(); + const bool is_integer = AmdGpu::IsInteger(nfmt); + const VectorIds& sampled_type{GetAttributeType(*this, nfmt)}; const u32 sampled = desc.is_written ? 2 : 1; const Id image_type{TypeImage(sampled_type[1], spv::Dim::Buffer, false, false, false, sampled, spv::ImageFormat::Unknown)}; @@ -609,10 +651,11 @@ spv::ImageFormat GetFormat(const AmdGpu::Image& image) { } Id ImageType(EmitContext& ctx, const ImageResource& desc, Id sampled_type) { - const auto image = ctx.info.ReadUdSharp(desc.sharp_idx); + const auto image = desc.GetSharp(ctx.info); const auto format = desc.is_atomic ? GetFormat(image) : spv::ImageFormat::Unknown; + const auto type = image.GetBoundType(); const u32 sampled = desc.is_storage ? 2 : 1; - switch (desc.type) { + switch (type) { case AmdGpu::ImageType::Color1D: return ctx.TypeImage(sampled_type, spv::Dim::Dim1D, false, false, false, sampled, format); case AmdGpu::ImageType::Color1DArray: @@ -631,14 +674,15 @@ Id ImageType(EmitContext& ctx, const ImageResource& desc, Id sampled_type) { default: break; } - throw InvalidArgument("Invalid texture type {}", desc.type); + throw InvalidArgument("Invalid texture type {}", type); } void EmitContext::DefineImagesAndSamplers() { for (const auto& image_desc : info.images) { - const bool is_integer = image_desc.nfmt == AmdGpu::NumberFormat::Uint || - image_desc.nfmt == AmdGpu::NumberFormat::Sint; - const VectorIds& data_types = GetAttributeType(*this, image_desc.nfmt); + const auto sharp = image_desc.GetSharp(info); + const auto nfmt = sharp.GetNumberFmt(); + const bool is_integer = AmdGpu::IsInteger(nfmt); + const VectorIds& data_types = GetAttributeType(*this, nfmt); const Id sampled_type = data_types[1]; const Id image_type{ImageType(*this, image_desc, sampled_type)}; const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)}; diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index fb30a5dd6..4e5e7dd3b 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -42,7 +42,9 @@ public: ~EmitContext(); Id Def(const IR::Value& value); + void DefineBufferOffsets(); + void DefineInterpolatedAttribs(); [[nodiscard]] Id DefineInput(Id type, u32 location) { const Id input_id{DefineVar(type, spv::StorageClass::Input)}; @@ -197,6 +199,9 @@ public: Id shared_memory_u32_type{}; + Id interpolate_func{}; + Id gl_bary_coord_id{}; + struct TextureDefinition { const VectorIds* data_types; Id id; @@ -218,6 +223,7 @@ public: struct TextureBufferDefinition { Id id; Id coord_offset; + Id coord_shift; u32 binding; Id image_type; Id result_type; @@ -241,7 +247,7 @@ public: Id component_type; u32 num_components; bool is_integer{}; - bool is_default{}; + bool is_loaded{}; s32 buffer_handle{-1}; }; std::array input_params{}; diff --git a/src/shader_recompiler/frontend/control_flow_graph.cpp b/src/shader_recompiler/frontend/control_flow_graph.cpp index 354196d31..8c3122b28 100644 --- a/src/shader_recompiler/frontend/control_flow_graph.cpp +++ b/src/shader_recompiler/frontend/control_flow_graph.cpp @@ -47,6 +47,15 @@ static IR::Condition MakeCondition(const GcnInst& inst) { } } +static bool IgnoresExecMask(Opcode opcode) { + switch (opcode) { + case Opcode::V_WRITELANE_B32: + return true; + default: + return false; + } +} + static constexpr size_t LabelReserveSize = 32; CFG::CFG(Common::ObjectPool& block_pool_, std::span inst_list_) @@ -133,20 +142,26 @@ void CFG::EmitDivergenceLabels() { curr_begin = -1; continue; } - // Add a label to the instruction right after the open scope call. - // It is the start of a new basic block. - const auto& save_inst = inst_list[curr_begin]; - const Label label = index_to_pc[curr_begin] + save_inst.length; - AddLabel(label); - // Add a label to the close scope instruction. - // There are 3 cases where we need to close a scope. - // * Close scope instruction inside the block - // * Close scope instruction at the end of the block (cbranch or endpgm) - // * Normal instruction at the end of the block - // For the last case we must NOT add a label as that would cause - // the instruction to be separated into its own basic block. - if (is_close) { - AddLabel(index_to_pc[index]); + // If all instructions in the scope ignore exec masking, we shouldn't insert a + // scope. + const auto start = inst_list.begin() + curr_begin + 1; + if (!std::ranges::all_of(start, inst_list.begin() + index, IgnoresExecMask, + &GcnInst::opcode)) { + // Add a label to the instruction right after the open scope call. + // It is the start of a new basic block. + const auto& save_inst = inst_list[curr_begin]; + const Label label = index_to_pc[curr_begin] + save_inst.length; + AddLabel(label); + // Add a label to the close scope instruction. + // There are 3 cases where we need to close a scope. + // * Close scope instruction inside the block + // * Close scope instruction at the end of the block (cbranch or endpgm) + // * Normal instruction at the end of the block + // For the last case we must NOT add a label as that would cause + // the instruction to be separated into its own basic block. + if (is_close) { + AddLabel(index_to_pc[index]); + } } // Reset scope begin. curr_begin = -1; diff --git a/src/shader_recompiler/frontend/decode.cpp b/src/shader_recompiler/frontend/decode.cpp index 796bed127..a5187aebd 100644 --- a/src/shader_recompiler/frontend/decode.cpp +++ b/src/shader_recompiler/frontend/decode.cpp @@ -5,7 +5,7 @@ #include "common/assert.h" #include "shader_recompiler/frontend/decode.h" -#include "magic_enum.hpp" +#include namespace Shader::Gcn { diff --git a/src/shader_recompiler/frontend/fetch_shader.cpp b/src/shader_recompiler/frontend/fetch_shader.cpp index 16938410c..8ae664d79 100644 --- a/src/shader_recompiler/frontend/fetch_shader.cpp +++ b/src/shader_recompiler/frontend/fetch_shader.cpp @@ -34,8 +34,14 @@ namespace Shader::Gcn { * We take the reverse way, extract the original input semantics from these instructions. **/ -FetchShaderData ParseFetchShader(const u32* code, u32* out_size) { - FetchShaderData data{}; +std::optional ParseFetchShader(const Shader::Info& info) { + if (!info.has_fetch_shader) { + return std::nullopt; + } + const u32* code; + std::memcpy(&code, &info.user_data[info.fetch_shader_sgpr_base], sizeof(code)); + + FetchShaderData data{.code = code}; GcnCodeSlice code_slice(code, code + std::numeric_limits::max()); GcnDecodeContext decoder; @@ -49,7 +55,7 @@ FetchShaderData ParseFetchShader(const u32* code, u32* out_size) { u32 semantic_index = 0; while (!code_slice.atEnd()) { const auto inst = decoder.decodeInstruction(code_slice); - *out_size += inst.length; + data.size += inst.length; if (inst.opcode == Opcode::S_SETPC_B64) { break; diff --git a/src/shader_recompiler/frontend/fetch_shader.h b/src/shader_recompiler/frontend/fetch_shader.h index 0e5d15419..080b0eb22 100644 --- a/src/shader_recompiler/frontend/fetch_shader.h +++ b/src/shader_recompiler/frontend/fetch_shader.h @@ -3,26 +3,67 @@ #pragma once +#include #include #include "common/types.h" +#include "shader_recompiler/info.h" namespace Shader::Gcn { struct VertexAttribute { + enum InstanceIdType : u8 { + None = 0, + OverStepRate0 = 1, + OverStepRate1 = 2, + Plain = 3, + }; + u8 semantic; ///< Semantic index of the attribute u8 dest_vgpr; ///< Destination VGPR to load first component. u8 num_elements; ///< Number of components to load u8 sgpr_base; ///< SGPR that contains the pointer to the list of vertex V# u8 dword_offset; ///< The dword offset of the V# that describes this attribute. u8 instance_data; ///< Indicates that the buffer will be accessed in instance rate + + [[nodiscard]] InstanceIdType GetStepRate() const { + return static_cast(instance_data); + } + + [[nodiscard]] bool UsesStepRates() const { + const auto step_rate = GetStepRate(); + return step_rate == OverStepRate0 || step_rate == OverStepRate1; + } + + [[nodiscard]] constexpr AmdGpu::Buffer GetSharp(const Shader::Info& info) const noexcept { + return info.ReadUdReg(sgpr_base, dword_offset); + } + + bool operator==(const VertexAttribute& other) const { + return semantic == other.semantic && dest_vgpr == other.dest_vgpr && + num_elements == other.num_elements && sgpr_base == other.sgpr_base && + dword_offset == other.dword_offset && instance_data == other.instance_data; + } }; struct FetchShaderData { + const u32* code; + u32 size = 0; std::vector attributes; s8 vertex_offset_sgpr = -1; ///< SGPR of vertex offset from VADDR s8 instance_offset_sgpr = -1; ///< SGPR of instance offset from VADDR + + [[nodiscard]] bool UsesStepRates() const { + return std::ranges::find_if(attributes, [](const VertexAttribute& attribute) { + return attribute.UsesStepRates(); + }) != attributes.end(); + } + + bool operator==(const FetchShaderData& other) const { + return attributes == other.attributes && vertex_offset_sgpr == other.vertex_offset_sgpr && + instance_offset_sgpr == other.instance_offset_sgpr; + } }; -FetchShaderData ParseFetchShader(const u32* code, u32* out_size); +std::optional ParseFetchShader(const Shader::Info& info); } // namespace Shader::Gcn diff --git a/src/shader_recompiler/frontend/translate/data_share.cpp b/src/shader_recompiler/frontend/translate/data_share.cpp index a453023fc..5914f9fe3 100644 --- a/src/shader_recompiler/frontend/translate/data_share.cpp +++ b/src/shader_recompiler/frontend/translate/data_share.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "shader_recompiler/frontend/translate/translate.h" +#include "shader_recompiler/ir/reg.h" namespace Shader::Gcn { @@ -18,6 +19,12 @@ void Translator::EmitDataShare(const GcnInst& inst) { return DS_MIN_U32(inst, false, false); case Opcode::DS_MAX_U32: return DS_MAX_U32(inst, false, false); + case Opcode::DS_AND_B32: + return DS_AND_B32(inst, false); + case Opcode::DS_OR_B32: + return DS_OR_B32(inst, false); + case Opcode::DS_XOR_B32: + return DS_XOR_B32(inst, false); case Opcode::DS_WRITE_B32: return DS_WRITE(32, false, false, false, inst); case Opcode::DS_WRITE2_B32: @@ -30,6 +37,12 @@ void Translator::EmitDataShare(const GcnInst& inst) { return DS_MIN_U32(inst, false, true); case Opcode::DS_MAX_RTN_U32: return DS_MAX_U32(inst, false, true); + case Opcode::DS_AND_RTN_B32: + return DS_AND_B32(inst, true); + case Opcode::DS_OR_RTN_B32: + return DS_OR_B32(inst, true); + case Opcode::DS_XOR_RTN_B32: + return DS_XOR_B32(inst, true); case Opcode::DS_SWIZZLE_B32: return DS_SWIZZLE_B32(inst); case Opcode::DS_READ_B32: @@ -68,10 +81,9 @@ void Translator::V_READFIRSTLANE_B32(const GcnInst& inst) { } void Translator::V_READLANE_B32(const GcnInst& inst) { - const IR::ScalarReg dst{inst.dst[0].code}; const IR::U32 value{GetSrc(inst.src[0])}; const IR::U32 lane{GetSrc(inst.src[1])}; - ir.SetScalarReg(dst, ir.ReadLane(value, lane)); + SetDst(inst.dst[0], ir.ReadLane(value, lane)); } void Translator::V_WRITELANE_B32(const GcnInst& inst) { @@ -87,7 +99,8 @@ void Translator::V_WRITELANE_B32(const GcnInst& inst) { void Translator::DS_ADD_U32(const GcnInst& inst, bool rtn) { const IR::U32 addr{GetSrc(inst.src[0])}; const IR::U32 data{GetSrc(inst.src[1])}; - const IR::U32 offset = ir.Imm32(u32(inst.control.ds.offset0)); + const IR::U32 offset = + ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0)); const IR::U32 addr_offset = ir.IAdd(addr, offset); const IR::Value original_val = ir.SharedAtomicIAdd(addr_offset, data); if (rtn) { @@ -98,7 +111,8 @@ void Translator::DS_ADD_U32(const GcnInst& inst, bool rtn) { void Translator::DS_MIN_U32(const GcnInst& inst, bool is_signed, bool rtn) { const IR::U32 addr{GetSrc(inst.src[0])}; const IR::U32 data{GetSrc(inst.src[1])}; - const IR::U32 offset = ir.Imm32(u32(inst.control.ds.offset0)); + const IR::U32 offset = + ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0)); const IR::U32 addr_offset = ir.IAdd(addr, offset); const IR::Value original_val = ir.SharedAtomicIMin(addr_offset, data, is_signed); if (rtn) { @@ -109,7 +123,8 @@ void Translator::DS_MIN_U32(const GcnInst& inst, bool is_signed, bool rtn) { void Translator::DS_MAX_U32(const GcnInst& inst, bool is_signed, bool rtn) { const IR::U32 addr{GetSrc(inst.src[0])}; const IR::U32 data{GetSrc(inst.src[1])}; - const IR::U32 offset = ir.Imm32(u32(inst.control.ds.offset0)); + const IR::U32 offset = + ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0)); const IR::U32 addr_offset = ir.IAdd(addr, offset); const IR::Value original_val = ir.SharedAtomicIMax(addr_offset, data, is_signed); if (rtn) { @@ -117,6 +132,42 @@ void Translator::DS_MAX_U32(const GcnInst& inst, bool is_signed, bool rtn) { } } +void Translator::DS_AND_B32(const GcnInst& inst, bool rtn) { + const IR::U32 addr{GetSrc(inst.src[0])}; + const IR::U32 data{GetSrc(inst.src[1])}; + const IR::U32 offset = + ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0)); + const IR::U32 addr_offset = ir.IAdd(addr, offset); + const IR::Value original_val = ir.SharedAtomicAnd(addr_offset, data); + if (rtn) { + SetDst(inst.dst[0], IR::U32{original_val}); + } +} + +void Translator::DS_OR_B32(const GcnInst& inst, bool rtn) { + const IR::U32 addr{GetSrc(inst.src[0])}; + const IR::U32 data{GetSrc(inst.src[1])}; + const IR::U32 offset = + ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0)); + const IR::U32 addr_offset = ir.IAdd(addr, offset); + const IR::Value original_val = ir.SharedAtomicOr(addr_offset, data); + if (rtn) { + SetDst(inst.dst[0], IR::U32{original_val}); + } +} + +void Translator::DS_XOR_B32(const GcnInst& inst, bool rtn) { + const IR::U32 addr{GetSrc(inst.src[0])}; + const IR::U32 data{GetSrc(inst.src[1])}; + const IR::U32 offset = + ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0)); + const IR::U32 addr_offset = ir.IAdd(addr, offset); + const IR::Value original_val = ir.SharedAtomicXor(addr_offset, data); + if (rtn) { + SetDst(inst.dst[0], IR::U32{original_val}); + } +} + void Translator::DS_WRITE(int bit_size, bool is_signed, bool is_pair, bool stride64, const GcnInst& inst) { const IR::U32 addr{ir.GetVectorReg(IR::VectorReg(inst.src[0].code))}; @@ -141,12 +192,14 @@ void Translator::DS_WRITE(int bit_size, bool is_signed, bool is_pair, bool strid addr1); } } else if (bit_size == 64) { - const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(u32(inst.control.ds.offset0))); + const IR::U32 addr0 = ir.IAdd( + addr, ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0))); const IR::Value data = ir.CompositeConstruct(ir.GetVectorReg(data0), ir.GetVectorReg(data0 + 1)); ir.WriteShared(bit_size, data, addr0); } else { - const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(u32(inst.control.ds.offset0))); + const IR::U32 addr0 = ir.IAdd( + addr, ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0))); ir.WriteShared(bit_size, ir.GetVectorReg(data0), addr0); } } @@ -154,13 +207,12 @@ void Translator::DS_WRITE(int bit_size, bool is_signed, bool is_pair, bool strid void Translator::DS_SWIZZLE_B32(const GcnInst& inst) { const u8 offset0 = inst.control.ds.offset0; const u8 offset1 = inst.control.ds.offset1; - const IR::U32 src{GetSrc(inst.src[1])}; - ASSERT(offset1 & 0x80); + const IR::U32 src{GetSrc(inst.src[0])}; + // ASSERT(offset1 & 0x80); const IR::U32 lane_id = ir.LaneId(); const IR::U32 id_in_group = ir.BitwiseAnd(lane_id, ir.Imm32(0b11)); const IR::U32 base = ir.ShiftLeftLogical(id_in_group, ir.Imm32(1)); - const IR::U32 index = - ir.IAdd(lane_id, ir.BitFieldExtract(ir.Imm32(offset0), base, ir.Imm32(2))); + const IR::U32 index = ir.BitFieldExtract(ir.Imm32(offset0), base, ir.Imm32(2)); SetDst(inst.dst[0], ir.QuadShuffle(src, index)); } @@ -188,26 +240,28 @@ void Translator::DS_READ(int bit_size, bool is_signed, bool is_pair, bool stride ir.SetVectorReg(dst_reg++, IR::U32{ir.CompositeExtract(data1, 1)}); } } else if (bit_size == 64) { - const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(u32(inst.control.ds.offset0))); + const IR::U32 addr0 = ir.IAdd( + addr, ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0))); const IR::Value data = ir.LoadShared(bit_size, is_signed, addr0); ir.SetVectorReg(dst_reg, IR::U32{ir.CompositeExtract(data, 0)}); ir.SetVectorReg(dst_reg + 1, IR::U32{ir.CompositeExtract(data, 1)}); } else { - const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(u32(inst.control.ds.offset0))); + const IR::U32 addr0 = ir.IAdd( + addr, ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0))); const IR::U32 data = IR::U32{ir.LoadShared(bit_size, is_signed, addr0)}; ir.SetVectorReg(dst_reg, data); } } void Translator::DS_APPEND(const GcnInst& inst) { - const u32 inst_offset = inst.control.ds.offset0; + const u32 inst_offset = (u32(inst.control.ds.offset1) << 8u) + inst.control.ds.offset0; const IR::U32 gds_offset = ir.IAdd(ir.GetM0(), ir.Imm32(inst_offset)); const IR::U32 prev = ir.DataAppend(gds_offset); SetDst(inst.dst[0], prev); } void Translator::DS_CONSUME(const GcnInst& inst) { - const u32 inst_offset = inst.control.ds.offset0; + const u32 inst_offset = (u32(inst.control.ds.offset1) << 8u) + inst.control.ds.offset0; const IR::U32 gds_offset = ir.IAdd(ir.GetM0(), ir.Imm32(inst_offset)); const IR::U32 prev = ir.DataConsume(gds_offset); SetDst(inst.dst[0], prev); diff --git a/src/shader_recompiler/frontend/translate/scalar_alu.cpp b/src/shader_recompiler/frontend/translate/scalar_alu.cpp index 1e627d95c..c320e00a7 100644 --- a/src/shader_recompiler/frontend/translate/scalar_alu.cpp +++ b/src/shader_recompiler/frontend/translate/scalar_alu.cpp @@ -50,6 +50,8 @@ void Translator::EmitScalarAlu(const GcnInst& inst) { return S_OR_B64(NegateMode::None, false, inst); case Opcode::S_XOR_B32: return S_XOR_B32(inst); + case Opcode::S_NOT_B32: + return S_NOT_B32(inst); case Opcode::S_XOR_B64: return S_OR_B64(NegateMode::None, true, inst); case Opcode::S_ANDN2_B32: @@ -94,6 +96,8 @@ void Translator::EmitScalarAlu(const GcnInst& inst) { return S_BREV_B32(inst); case Opcode::S_BCNT1_I32_B64: return S_BCNT1_I32_B64(inst); + case Opcode::S_FF1_I32_B32: + return S_FF1_I32_B32(inst); case Opcode::S_AND_SAVEEXEC_B64: return S_SAVEEXEC_B64(NegateMode::None, false, inst); case Opcode::S_ORN2_SAVEEXEC_B64: @@ -132,6 +136,16 @@ void Translator::EmitSOPC(const GcnInst& inst) { return S_CMP(ConditionOp::LT, false, inst); case Opcode::S_CMP_LE_U32: return S_CMP(ConditionOp::LE, false, inst); + + case Opcode::S_BITCMP0_B32: + return S_BITCMP(false, 32, inst); + case Opcode::S_BITCMP1_B32: + return S_BITCMP(true, 32, inst); + case Opcode::S_BITCMP0_B64: + return S_BITCMP(false, 64, inst); + case Opcode::S_BITCMP1_B64: + return S_BITCMP(true, 64, inst); + default: LogMissingOpcode(inst); } @@ -291,6 +305,10 @@ void Translator::S_AND_B64(NegateMode negate, const GcnInst& inst) { ASSERT_MSG(-s32(operand.code) + SignedConstIntNegMin - 1 == -1, "SignedConstIntNeg must be -1"); return ir.Imm1(true); + case OperandField::LiteralConst: + ASSERT_MSG(operand.code == 0 || operand.code == std::numeric_limits::max(), + "Unsupported literal {:#x}", operand.code); + return ir.Imm1(operand.code & 1); default: UNREACHABLE(); } @@ -372,6 +390,13 @@ void Translator::S_XOR_B32(const GcnInst& inst) { ir.SetScc(ir.INotEqual(result, ir.Imm32(0))); } +void Translator::S_NOT_B32(const GcnInst& inst) { + const IR::U32 src0{GetSrc(inst.src[0])}; + const IR::U32 result{ir.BitwiseNot(src0)}; + SetDst(inst.dst[0], result); + ir.SetScc(ir.INotEqual(result, ir.Imm32(0))); +} + void Translator::S_LSHL_B32(const GcnInst& inst) { const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 src1{GetSrc(inst.src[1])}; @@ -550,6 +575,12 @@ void Translator::S_BCNT1_I32_B64(const GcnInst& inst) { ir.SetScc(ir.INotEqual(result, ir.Imm32(0))); } +void Translator::S_FF1_I32_B32(const GcnInst& inst) { + const IR::U32 src0{GetSrc(inst.src[0])}; + const IR::U32 result{ir.Select(ir.IEqual(src0, ir.Imm32(0U)), ir.Imm32(-1), ir.FindILsb(src0))}; + SetDst(inst.dst[0], result); +} + void Translator::S_SAVEEXEC_B64(NegateMode negate, bool is_or, const GcnInst& inst) { // This instruction normally operates on 64-bit data (EXEC, VCC, SGPRs) // However here we flatten it to 1-bit EXEC and 1-bit VCC. For the destination @@ -615,4 +646,33 @@ void Translator::S_CMP(ConditionOp cond, bool is_signed, const GcnInst& inst) { ir.SetScc(result); } +void Translator::S_BITCMP(bool compare_mode, u32 bits, const GcnInst& inst) { + const IR::U1 result = [&] { + const IR::U32 src0 = GetSrc(inst.src[0]); + const IR::U32 src1 = GetSrc(inst.src[1]); + + IR::U32 mask; + switch (bits) { + case 32: + mask = ir.Imm32(0x1f); + break; + case 64: + mask = ir.Imm32(0x3f); + break; + default: + UNREACHABLE(); + } + + const IR::U32 bitpos{ir.BitwiseAnd(src1, mask)}; + const IR::U32 bittest{ir.BitwiseAnd(ir.ShiftRightLogical(src0, bitpos), ir.Imm32(1))}; + + if (!compare_mode) { + return ir.IEqual(bittest, ir.Imm32(0)); + } else { + return ir.IEqual(bittest, ir.Imm32(1)); + } + }(); + ir.SetScc(result); +} + } // namespace Shader::Gcn diff --git a/src/shader_recompiler/frontend/translate/translate.cpp b/src/shader_recompiler/frontend/translate/translate.cpp index ccce31a24..97978ff6b 100644 --- a/src/shader_recompiler/frontend/translate/translate.cpp +++ b/src/shader_recompiler/frontend/translate/translate.cpp @@ -14,7 +14,7 @@ #define MAGIC_ENUM_RANGE_MIN 0 #define MAGIC_ENUM_RANGE_MAX 1515 -#include "magic_enum.hpp" +#include namespace Shader::Gcn { @@ -53,15 +53,74 @@ void Translator::EmitPrologue() { } break; case Stage::Fragment: - // https://github.com/chaotic-cx/mesa-mirror/blob/72326e15/src/amd/vulkan/radv_shader_args.c#L258 - // The first two VGPRs are used for i/j barycentric coordinates. In the vast majority of - // cases it will be only those two, but if shader is using both e.g linear and perspective - // inputs it can be more For now assume that this isn't the case. - dst_vreg = IR::VectorReg::V2; - for (u32 i = 0; i < 4; i++) { - ir.SetVectorReg(dst_vreg++, ir.GetAttribute(IR::Attribute::FragCoord, i)); + dst_vreg = IR::VectorReg::V0; + if (runtime_info.fs_info.addr_flags.persp_sample_ena) { + ++dst_vreg; // I + ++dst_vreg; // J + } + if (runtime_info.fs_info.addr_flags.persp_center_ena) { + ++dst_vreg; // I + ++dst_vreg; // J + } + if (runtime_info.fs_info.addr_flags.persp_centroid_ena) { + ++dst_vreg; // I + ++dst_vreg; // J + } + if (runtime_info.fs_info.addr_flags.persp_pull_model_ena) { + ++dst_vreg; // I/W + ++dst_vreg; // J/W + ++dst_vreg; // 1/W + } + if (runtime_info.fs_info.addr_flags.linear_sample_ena) { + ++dst_vreg; // I + ++dst_vreg; // J + } + if (runtime_info.fs_info.addr_flags.linear_center_ena) { + ++dst_vreg; // I + ++dst_vreg; // J + } + if (runtime_info.fs_info.addr_flags.linear_centroid_ena) { + ++dst_vreg; // I + ++dst_vreg; // J + } + if (runtime_info.fs_info.addr_flags.line_stipple_tex_ena) { + ++dst_vreg; + } + if (runtime_info.fs_info.addr_flags.pos_x_float_ena) { + if (runtime_info.fs_info.en_flags.pos_x_float_ena) { + ir.SetVectorReg(dst_vreg++, ir.GetAttribute(IR::Attribute::FragCoord, 0)); + } else { + ir.SetVectorReg(dst_vreg++, ir.Imm32(0.0f)); + } + } + if (runtime_info.fs_info.addr_flags.pos_y_float_ena) { + if (runtime_info.fs_info.en_flags.pos_y_float_ena) { + ir.SetVectorReg(dst_vreg++, ir.GetAttribute(IR::Attribute::FragCoord, 1)); + } else { + ir.SetVectorReg(dst_vreg++, ir.Imm32(0.0f)); + } + } + if (runtime_info.fs_info.addr_flags.pos_z_float_ena) { + if (runtime_info.fs_info.en_flags.pos_z_float_ena) { + ir.SetVectorReg(dst_vreg++, ir.GetAttribute(IR::Attribute::FragCoord, 2)); + } else { + ir.SetVectorReg(dst_vreg++, ir.Imm32(0.0f)); + } + } + if (runtime_info.fs_info.addr_flags.pos_w_float_ena) { + if (runtime_info.fs_info.en_flags.pos_w_float_ena) { + ir.SetVectorReg(dst_vreg++, ir.GetAttribute(IR::Attribute::FragCoord, 3)); + } else { + ir.SetVectorReg(dst_vreg++, ir.Imm32(0.0f)); + } + } + if (runtime_info.fs_info.addr_flags.front_face_ena) { + if (runtime_info.fs_info.en_flags.front_face_ena) { + ir.SetVectorReg(dst_vreg++, ir.GetAttributeU32(IR::Attribute::IsFrontFace)); + } else { + ir.SetVectorReg(dst_vreg++, ir.Imm32(0)); + } } - ir.SetVectorReg(dst_vreg++, ir.GetAttributeU32(IR::Attribute::IsFrontFace)); break; case Stage::Compute: ir.SetVectorReg(dst_vreg++, ir.GetAttributeU32(IR::Attribute::LocalInvocationId, 0)); @@ -174,6 +233,13 @@ T Translator::GetSrc(const InstOperand& operand) { value = ir.GetM0(); } break; + case OperandField::Scc: + if constexpr (is_float) { + UNREACHABLE(); + } else { + value = ir.BitCast(ir.GetScc()); + } + break; default: UNREACHABLE(); } @@ -361,13 +427,11 @@ void Translator::SetDst64(const InstOperand& operand, const IR::U64F64& value_ra void Translator::EmitFetch(const GcnInst& inst) { // Read the pointer to the fetch shader assembly. - const u32 sgpr_base = inst.src[0].code; - const u32* code; - std::memcpy(&code, &info.user_data[sgpr_base], sizeof(code)); + info.has_fetch_shader = true; + info.fetch_shader_sgpr_base = inst.src[0].code; - // Parse the assembly to generate a list of attributes. - u32 fetch_size{}; - const auto fetch_data = ParseFetchShader(code, &fetch_size); + const auto fetch_data = ParseFetchShader(info); + ASSERT(fetch_data.has_value()); if (Config::dumpShaders()) { using namespace Common::FS; @@ -377,13 +441,10 @@ void Translator::EmitFetch(const GcnInst& inst) { } const auto filename = fmt::format("vs_{:#018x}.fetch.bin", info.pgm_hash); const auto file = IOFile{dump_dir / filename, FileAccessMode::Write}; - file.WriteRaw(code, fetch_size); + file.WriteRaw(fetch_data->code, fetch_data->size); } - info.vertex_offset_sgpr = fetch_data.vertex_offset_sgpr; - info.instance_offset_sgpr = fetch_data.instance_offset_sgpr; - - for (const auto& attrib : fetch_data.attributes) { + for (const auto& attrib : fetch_data->attributes) { const IR::Attribute attr{IR::Attribute::Param0 + attrib.semantic}; IR::VectorReg dst_reg{attrib.dest_vgpr}; @@ -413,29 +474,14 @@ void Translator::EmitFetch(const GcnInst& inst) { // In case of programmable step rates we need to fallback to instance data pulling in // shader, so VBs should be bound as regular data buffers - s32 instance_buf_handle = -1; - const auto step_rate = static_cast(attrib.instance_data); - if (step_rate == Info::VsInput::OverStepRate0 || - step_rate == Info::VsInput::OverStepRate1) { + if (attrib.UsesStepRates()) { info.buffers.push_back({ .sharp_idx = info.srt_info.ReserveSharp(attrib.sgpr_base, attrib.dword_offset, 4), .used_types = IR::Type::F32, .is_instance_data = true, + .instance_attrib = attrib.semantic, }); - instance_buf_handle = s32(info.buffers.size() - 1); - info.uses_step_rates = true; } - - const u32 num_components = AmdGpu::NumComponents(buffer.GetDataFmt()); - info.vs_inputs.push_back({ - .fmt = buffer.GetNumberFmt(), - .binding = attrib.semantic, - .num_components = std::min(attrib.num_elements, num_components), - .sgpr_base = attrib.sgpr_base, - .dword_offset = attrib.dword_offset, - .instance_step_rate = step_rate, - .instance_data_buf = instance_buf_handle, - }); } } diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index 79bc33f0c..00cdd8d55 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -96,6 +96,7 @@ public: void S_MUL_I32(const GcnInst& inst); void S_BFE_U32(const GcnInst& inst); void S_ABSDIFF_I32(const GcnInst& inst); + void S_NOT_B32(const GcnInst& inst); // SOPK void S_MOVK(const GcnInst& inst); @@ -109,11 +110,13 @@ public: void S_NOT_B64(const GcnInst& inst); void S_BREV_B32(const GcnInst& inst); void S_BCNT1_I32_B64(const GcnInst& inst); + void S_FF1_I32_B32(const GcnInst& inst); void S_GETPC_B64(u32 pc, const GcnInst& inst); void S_SAVEEXEC_B64(NegateMode negate, bool is_or, const GcnInst& inst); // SOPC void S_CMP(ConditionOp cond, bool is_signed, const GcnInst& inst); + void S_BITCMP(bool compare_mode, u32 bits, const GcnInst& inst); // SOPP void S_BARRIER(); @@ -220,10 +223,12 @@ public: void V_FMA_F64(const GcnInst& inst); void V_MIN3_F32(const GcnInst& inst); void V_MIN3_I32(const GcnInst& inst); + void V_MIN3_U32(const GcnInst& inst); void V_MAX3_F32(const GcnInst& inst); void V_MAX3_U32(bool is_signed, const GcnInst& inst); void V_MED3_F32(const GcnInst& inst); void V_MED3_I32(const GcnInst& inst); + void V_MED3_U32(const GcnInst& inst); void V_SAD(const GcnInst& inst); void V_SAD_U32(const GcnInst& inst); void V_CVT_PK_U16_U32(const GcnInst& inst); @@ -247,6 +252,9 @@ public: void DS_MAX_U32(const GcnInst& inst, bool is_signed, bool rtn); void DS_WRITE(int bit_size, bool is_signed, bool is_pair, bool stride64, const GcnInst& inst); void DS_SWIZZLE_B32(const GcnInst& inst); + void DS_AND_B32(const GcnInst& inst, bool rtn); + void DS_OR_B32(const GcnInst& inst, bool rtn); + void DS_XOR_B32(const GcnInst& inst, bool rtn); void DS_READ(int bit_size, bool is_signed, bool is_pair, bool stride64, const GcnInst& inst); void DS_APPEND(const GcnInst& inst); void DS_CONSUME(const GcnInst& inst); diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index 433f9dce7..8149230db 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -347,6 +347,8 @@ void Translator::EmitVectorAlu(const GcnInst& inst) { return V_MIN3_F32(inst); case Opcode::V_MIN3_I32: return V_MIN3_I32(inst); + case Opcode::V_MIN3_U32: + return V_MIN3_U32(inst); case Opcode::V_MAX3_F32: return V_MAX3_F32(inst); case Opcode::V_MAX3_I32: @@ -357,6 +359,8 @@ void Translator::EmitVectorAlu(const GcnInst& inst) { return V_MED3_F32(inst); case Opcode::V_MED3_I32: return V_MED3_I32(inst); + case Opcode::V_MED3_U32: + return V_MED3_U32(inst); case Opcode::V_SAD_U32: return V_SAD_U32(inst); case Opcode::V_CVT_PK_U16_U32: @@ -1062,6 +1066,13 @@ void Translator::V_MIN3_I32(const GcnInst& inst) { SetDst(inst.dst[0], ir.SMin(src0, ir.SMin(src1, src2))); } +void Translator::V_MIN3_U32(const GcnInst& inst) { + const IR::U32 src0{GetSrc(inst.src[0])}; + const IR::U32 src1{GetSrc(inst.src[1])}; + const IR::U32 src2{GetSrc(inst.src[2])}; + SetDst(inst.dst[0], ir.UMin(src0, ir.UMin(src1, src2))); +} + void Translator::V_MAX3_F32(const GcnInst& inst) { const IR::F32 src0{GetSrc(inst.src[0])}; const IR::F32 src1{GetSrc(inst.src[1])}; @@ -1092,6 +1103,14 @@ void Translator::V_MED3_I32(const GcnInst& inst) { SetDst(inst.dst[0], ir.SMax(ir.SMin(src0, src1), mmx)); } +void Translator::V_MED3_U32(const GcnInst& inst) { + const IR::U32 src0{GetSrc(inst.src[0])}; + const IR::U32 src1{GetSrc(inst.src[1])}; + const IR::U32 src2{GetSrc(inst.src[2])}; + const IR::U32 mmx = ir.UMin(ir.UMax(src0, src1), src2); + SetDst(inst.dst[0], ir.UMax(ir.UMin(src0, src1), mmx)); +} + void Translator::V_SAD(const GcnInst& inst) { const IR::U32 abs_diff = ir.IAbs(ir.ISub(GetSrc(inst.src[0]), GetSrc(inst.src[1]))); SetDst(inst.dst[0], ir.IAdd(abs_diff, GetSrc(inst.src[2]))); @@ -1136,15 +1155,23 @@ void Translator::V_LSHL_B64(const GcnInst& inst) { const IR::U64 src0{GetSrc64(inst.src[0])}; const IR::U64 src1{GetSrc64(inst.src[1])}; const IR::VectorReg dst_reg{inst.dst[0].code}; - if (src0.IsImmediate() && src0.U64() == -1) { - ir.SetVectorReg(dst_reg, ir.Imm32(0xFFFFFFFF)); - ir.SetVectorReg(dst_reg + 1, ir.Imm32(0xFFFFFFFF)); - return; + if (src0.IsImmediate()) { + if (src0.U64() == -1) { + // If src0 is a fixed -1, the result will always be -1. + ir.SetVectorReg(dst_reg, ir.Imm32(0xFFFFFFFF)); + ir.SetVectorReg(dst_reg + 1, ir.Imm32(0xFFFFFFFF)); + return; + } + if (src1.IsImmediate()) { + // If both src0 and src1 are immediates, we can calculate the result now. + // Note that according to the manual, only bits 4:0 are used from src1. + const u64 result = src0.U64() << (src1.U64() & 0x1F); + ir.SetVectorReg(dst_reg, ir.Imm32(static_cast(result))); + ir.SetVectorReg(dst_reg + 1, ir.Imm32(static_cast(result >> 32))); + return; + } } - ASSERT_MSG(src0.IsImmediate() && src0.U64() == 0 && src1.IsImmediate() && src1.U64() == 0, - "V_LSHL_B64 with non-zero src0 or src1 is not supported"); - ir.SetVectorReg(dst_reg, ir.Imm32(0)); - ir.SetVectorReg(dst_reg + 1, ir.Imm32(0)); + UNREACHABLE_MSG("Unimplemented V_LSHL_B64 arguments"); } void Translator::V_MUL_F64(const GcnInst& inst) { diff --git a/src/shader_recompiler/info.h b/src/shader_recompiler/info.h index f9cbacaf2..494bbb4bb 100644 --- a/src/shader_recompiler/info.h +++ b/src/shader_recompiler/info.h @@ -17,6 +17,7 @@ #include "shader_recompiler/ir/type.h" #include "shader_recompiler/params.h" #include "shader_recompiler/runtime_info.h" +#include "video_core/amdgpu/liverpool.h" #include "video_core/amdgpu/resource.h" namespace Shader { @@ -44,6 +45,7 @@ struct BufferResource { AmdGpu::Buffer inline_cbuf; bool is_gds_buffer{}; bool is_instance_data{}; + u8 instance_attrib{}; bool is_written{}; bool IsStorage(AmdGpu::Buffer buffer) const noexcept { @@ -56,7 +58,6 @@ using BufferResourceList = boost::container::small_vector; struct TextureBufferResource { u32 sharp_idx; - AmdGpu::NumberFormat nfmt; bool is_written{}; constexpr AmdGpu::Buffer GetSharp(const Info& info) const noexcept; @@ -65,8 +66,6 @@ using TextureBufferResourceList = boost::container::small_vector vs_inputs{}; - struct AttributeFlags { bool Get(IR::Attribute attrib, u32 comp = 0) const { return flags[Index(attrib)] & (1 << comp); @@ -178,9 +164,6 @@ struct Info { CopyShaderData gs_copy_data; - s8 vertex_offset_sgpr = -1; - s8 instance_offset_sgpr = -1; - BufferResourceList buffers; TextureBufferResourceList texture_buffers; ImageResourceList images; @@ -207,10 +190,11 @@ struct Info { bool uses_shared{}; bool uses_fp16{}; bool uses_fp64{}; - bool uses_step_rates{}; bool translation_failed{}; // indicates that shader has unsupported instructions bool has_readconst{}; u8 mrt_mask{0u}; + bool has_fetch_shader{false}; + u32 fetch_shader_sgpr_base{0u}; explicit Info(Stage stage_, ShaderParams params) : stage{stage_}, pgm_hash{params.hash}, pgm_base{params.Base()}, @@ -251,18 +235,6 @@ struct Info { bnd.user_data += ud_mask.NumRegs(); } - [[nodiscard]] std::pair GetDrawOffsets() const { - u32 vertex_offset = 0; - u32 instance_offset = 0; - if (vertex_offset_sgpr != -1) { - vertex_offset = user_data[vertex_offset_sgpr]; - } - if (instance_offset_sgpr != -1) { - instance_offset = user_data[instance_offset_sgpr]; - } - return {vertex_offset, instance_offset}; - } - void RefreshFlatBuf() { flattened_ud_buf.resize(srt_info.flattened_bufsize_dw); ASSERT(user_data.size() <= NumUserDataRegs); @@ -283,7 +255,12 @@ constexpr AmdGpu::Buffer TextureBufferResource::GetSharp(const Info& info) const } constexpr AmdGpu::Image ImageResource::GetSharp(const Info& info) const noexcept { - return info.ReadUdSharp(sharp_idx); + const auto image = info.ReadUdSharp(sharp_idx); + if (!image.Valid()) { + // Fall back to null image if unbound. + return AmdGpu::Image::Null(); + } + return image; } constexpr AmdGpu::Sampler SamplerResource::GetSharp(const Info& info) const noexcept { diff --git a/src/shader_recompiler/ir/basic_block.cpp b/src/shader_recompiler/ir/basic_block.cpp index 426acb2b8..b4d1a78c7 100644 --- a/src/shader_recompiler/ir/basic_block.cpp +++ b/src/shader_recompiler/ir/basic_block.cpp @@ -19,12 +19,14 @@ void Block::AppendNewInst(Opcode op, std::initializer_list args) { Block::iterator Block::PrependNewInst(iterator insertion_point, const Inst& base_inst) { Inst* const inst{inst_pool->Create(base_inst)}; + inst->SetParent(this); return instructions.insert(insertion_point, *inst); } Block::iterator Block::PrependNewInst(iterator insertion_point, Opcode op, std::initializer_list args, u32 flags) { Inst* const inst{inst_pool->Create(op, flags)}; + inst->SetParent(this); const auto result_it{instructions.insert(insertion_point, *inst)}; if (inst->NumArgs() != args.size()) { diff --git a/src/shader_recompiler/ir/breadth_first_search.h b/src/shader_recompiler/ir/breadth_first_search.h index b042ae3d6..b3ed87204 100644 --- a/src/shader_recompiler/ir/breadth_first_search.h +++ b/src/shader_recompiler/ir/breadth_first_search.h @@ -14,8 +14,8 @@ namespace Shader::IR { // Use typename Instruction so the function can be used to return either const or mutable // Insts depending on the context. template -auto BreadthFirstSearch(Instruction* inst, Pred&& pred) - -> std::invoke_result_t { +auto BreadthFirstSearch(Instruction* inst, + Pred&& pred) -> std::invoke_result_t { // Most often case the instruction is the desired already. if (std::optional result = pred(inst)) { return result; @@ -53,8 +53,8 @@ auto BreadthFirstSearch(Instruction* inst, Pred&& pred) } template -auto BreadthFirstSearch(const Value& value, Pred&& pred) - -> std::invoke_result_t { +auto BreadthFirstSearch(const Value& value, + Pred&& pred) -> std::invoke_result_t { if (value.IsImmediate()) { // Nothing to do with immediates return std::nullopt; diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp index cfd044f9e..78e7f2289 100644 --- a/src/shader_recompiler/ir/ir_emitter.cpp +++ b/src/shader_recompiler/ir/ir_emitter.cpp @@ -59,6 +59,11 @@ F64 IREmitter::Imm64(f64 value) const { return F64{Value{value}}; } +template <> +IR::U32 IREmitter::BitCast(const IR::U1& value) { + return IR::U32{Select(value, Imm32(1), Imm32(0))}; +} + template <> IR::U32 IREmitter::BitCast(const IR::F32& value) { return Inst(Opcode::BitCastU32F32, value); @@ -321,6 +326,18 @@ U32 IREmitter::SharedAtomicIMax(const U32& address, const U32& data, bool is_sig : Inst(Opcode::SharedAtomicUMax32, address, data); } +U32 IREmitter::SharedAtomicAnd(const U32& address, const U32& data) { + return Inst(Opcode::SharedAtomicAnd32, address, data); +} + +U32 IREmitter::SharedAtomicOr(const U32& address, const U32& data) { + return Inst(Opcode::SharedAtomicOr32, address, data); +} + +U32 IREmitter::SharedAtomicXor(const U32& address, const U32& data) { + return Inst(Opcode::SharedAtomicXor32, address, data); +} + U32 IREmitter::ReadConst(const Value& base, const U32& offset) { return Inst(Opcode::ReadConst, base, offset); } diff --git a/src/shader_recompiler/ir/ir_emitter.h b/src/shader_recompiler/ir/ir_emitter.h index b3f513085..cbd3780de 100644 --- a/src/shader_recompiler/ir/ir_emitter.h +++ b/src/shader_recompiler/ir/ir_emitter.h @@ -90,6 +90,9 @@ public: [[nodiscard]] U32F32 SharedAtomicIAdd(const U32& address, const U32F32& data); [[nodiscard]] U32 SharedAtomicIMin(const U32& address, const U32& data, bool is_signed); [[nodiscard]] U32 SharedAtomicIMax(const U32& address, const U32& data, bool is_signed); + [[nodiscard]] U32 SharedAtomicAnd(const U32& address, const U32& data); + [[nodiscard]] U32 SharedAtomicOr(const U32& address, const U32& data); + [[nodiscard]] U32 SharedAtomicXor(const U32& address, const U32& data); [[nodiscard]] U32 ReadConst(const Value& base, const U32& offset); [[nodiscard]] U32 ReadConstBuffer(const Value& handle, const U32& index); diff --git a/src/shader_recompiler/ir/microinstruction.cpp b/src/shader_recompiler/ir/microinstruction.cpp index f0b4882b3..9b4ad63d2 100644 --- a/src/shader_recompiler/ir/microinstruction.cpp +++ b/src/shader_recompiler/ir/microinstruction.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include "shader_recompiler/exception.h" @@ -77,6 +78,9 @@ bool Inst::MayHaveSideEffects() const noexcept { case Opcode::SharedAtomicUMin32: case Opcode::SharedAtomicSMax32: case Opcode::SharedAtomicUMax32: + case Opcode::SharedAtomicAnd32: + case Opcode::SharedAtomicOr32: + case Opcode::SharedAtomicXor32: case Opcode::ImageWrite: case Opcode::ImageAtomicIAdd32: case Opcode::ImageAtomicSMin32: @@ -116,10 +120,10 @@ void Inst::SetArg(size_t index, Value value) { } const IR::Value arg{Arg(index)}; if (!arg.IsImmediate()) { - UndoUse(arg); + UndoUse(arg.Inst(), index); } if (!value.IsImmediate()) { - Use(value); + Use(value.Inst(), index); } if (op == Opcode::Phi) { phi_args[index].second = value; @@ -140,7 +144,7 @@ Block* Inst::PhiBlock(size_t index) const { void Inst::AddPhiOperand(Block* predecessor, const Value& value) { if (!value.IsImmediate()) { - Use(value); + Use(value.Inst(), phi_args.size()); } phi_args.emplace_back(predecessor, value); } @@ -152,17 +156,19 @@ void Inst::Invalidate() { void Inst::ClearArgs() { if (op == Opcode::Phi) { - for (auto& pair : phi_args) { + for (auto i = 0; i < phi_args.size(); i++) { + auto& pair = phi_args[i]; IR::Value& value{pair.second}; if (!value.IsImmediate()) { - UndoUse(value); + UndoUse(value.Inst(), i); } } phi_args.clear(); } else { - for (auto& value : args) { + for (auto i = 0; i < args.size(); i++) { + auto& value = args[i]; if (!value.IsImmediate()) { - UndoUse(value); + UndoUse(value.Inst(), i); } } // Reset arguments to null @@ -171,13 +177,21 @@ void Inst::ClearArgs() { } } -void Inst::ReplaceUsesWith(Value replacement) { - Invalidate(); - ReplaceOpcode(Opcode::Identity); - if (!replacement.IsImmediate()) { - Use(replacement); +void Inst::ReplaceUsesWith(Value replacement, bool preserve) { + // Copy since user->SetArg will mutate this->uses + // Could also do temp_uses = std::move(uses) but more readable + const auto temp_uses = uses; + for (const auto& [user, operand] : temp_uses) { + DEBUG_ASSERT(user->Arg(operand).Inst() == this); + user->SetArg(operand, replacement); + } + Invalidate(); + if (preserve) { + // Still useful to have Identity for indirection. + // SSA pass would be more complicated without it + ReplaceOpcode(Opcode::Identity); + SetArg(0, replacement); } - args[0] = replacement; } void Inst::ReplaceOpcode(IR::Opcode opcode) { @@ -192,14 +206,15 @@ void Inst::ReplaceOpcode(IR::Opcode opcode) { op = opcode; } -void Inst::Use(const Value& value) { - Inst* const inst{value.Inst()}; - ++inst->use_count; +void Inst::Use(Inst* used, u32 operand) { + DEBUG_ASSERT(0 == std::count(used->uses.begin(), used->uses.end(), IR::Use(this, operand))); + used->uses.emplace_front(this, operand); } -void Inst::UndoUse(const Value& value) { - Inst* const inst{value.Inst()}; - --inst->use_count; +void Inst::UndoUse(Inst* used, u32 operand) { + IR::Use use(this, operand); + DEBUG_ASSERT(1 == std::count(used->uses.begin(), used->uses.end(), use)); + used->uses.remove(use); } } // namespace Shader::IR diff --git a/src/shader_recompiler/ir/opcodes.h b/src/shader_recompiler/ir/opcodes.h index 200d7f421..be640297a 100644 --- a/src/shader_recompiler/ir/opcodes.h +++ b/src/shader_recompiler/ir/opcodes.h @@ -53,7 +53,7 @@ constexpr Type F64x3{Type::F64x3}; constexpr Type F64x4{Type::F64x4}; constexpr Type StringLiteral{Type::StringLiteral}; -constexpr OpcodeMeta META_TABLE[]{ +constexpr OpcodeMeta META_TABLE[] { #define OPCODE(name_token, type_token, ...) \ { \ .name{#name_token}, \ diff --git a/src/shader_recompiler/ir/opcodes.inc b/src/shader_recompiler/ir/opcodes.inc index 51e10fb38..0283ccd0f 100644 --- a/src/shader_recompiler/ir/opcodes.inc +++ b/src/shader_recompiler/ir/opcodes.inc @@ -43,6 +43,9 @@ OPCODE(SharedAtomicSMin32, U32, U32, OPCODE(SharedAtomicUMin32, U32, U32, U32, ) OPCODE(SharedAtomicSMax32, U32, U32, U32, ) OPCODE(SharedAtomicUMax32, U32, U32, U32, ) +OPCODE(SharedAtomicAnd32, U32, U32, U32, ) +OPCODE(SharedAtomicOr32, U32, U32, U32, ) +OPCODE(SharedAtomicXor32, U32, U32, U32, ) // Context getters/setters OPCODE(GetUserData, U32, ScalarReg, ) diff --git a/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp b/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp index a03fe051c..9624ce6a5 100644 --- a/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp +++ b/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp @@ -43,7 +43,7 @@ bool FoldCommutative(IR::Inst& inst, ImmFn&& imm_fn) { if (is_lhs_immediate && is_rhs_immediate) { const auto result{imm_fn(Arg(lhs), Arg(rhs))}; - inst.ReplaceUsesWith(IR::Value{result}); + inst.ReplaceUsesWithAndRemove(IR::Value{result}); return false; } if (is_lhs_immediate && !is_rhs_immediate) { @@ -75,7 +75,7 @@ bool FoldWhenAllImmediates(IR::Inst& inst, Func&& func) { return false; } using Indices = std::make_index_sequence::NUM_ARGS>; - inst.ReplaceUsesWith(EvalImmediates(inst, func, Indices{})); + inst.ReplaceUsesWithAndRemove(EvalImmediates(inst, func, Indices{})); return true; } @@ -83,12 +83,12 @@ template void FoldBitCast(IR::Inst& inst, IR::Opcode reverse) { const IR::Value value{inst.Arg(0)}; if (value.IsImmediate()) { - inst.ReplaceUsesWith(IR::Value{std::bit_cast(Arg(value))}); + inst.ReplaceUsesWithAndRemove(IR::Value{std::bit_cast(Arg(value))}); return; } IR::Inst* const arg_inst{value.InstRecursive()}; if (arg_inst->GetOpcode() == reverse) { - inst.ReplaceUsesWith(arg_inst->Arg(0)); + inst.ReplaceUsesWithAndRemove(arg_inst->Arg(0)); return; } } @@ -131,7 +131,7 @@ void FoldCompositeExtract(IR::Inst& inst, IR::Opcode construct, IR::Opcode inser if (!result) { return; } - inst.ReplaceUsesWith(*result); + inst.ReplaceUsesWithAndRemove(*result); } void FoldConvert(IR::Inst& inst, IR::Opcode opposite) { @@ -141,7 +141,7 @@ void FoldConvert(IR::Inst& inst, IR::Opcode opposite) { } IR::Inst* const producer{value.InstRecursive()}; if (producer->GetOpcode() == opposite) { - inst.ReplaceUsesWith(producer->Arg(0)); + inst.ReplaceUsesWithAndRemove(producer->Arg(0)); } } @@ -152,9 +152,9 @@ void FoldLogicalAnd(IR::Inst& inst) { const IR::Value rhs{inst.Arg(1)}; if (rhs.IsImmediate()) { if (rhs.U1()) { - inst.ReplaceUsesWith(inst.Arg(0)); + inst.ReplaceUsesWithAndRemove(inst.Arg(0)); } else { - inst.ReplaceUsesWith(IR::Value{false}); + inst.ReplaceUsesWithAndRemove(IR::Value{false}); } } } @@ -162,7 +162,7 @@ void FoldLogicalAnd(IR::Inst& inst) { void FoldSelect(IR::Inst& inst) { const IR::Value cond{inst.Arg(0)}; if (cond.IsImmediate()) { - inst.ReplaceUsesWith(cond.U1() ? inst.Arg(1) : inst.Arg(2)); + inst.ReplaceUsesWithAndRemove(cond.U1() ? inst.Arg(1) : inst.Arg(2)); } } @@ -173,9 +173,9 @@ void FoldLogicalOr(IR::Inst& inst) { const IR::Value rhs{inst.Arg(1)}; if (rhs.IsImmediate()) { if (rhs.U1()) { - inst.ReplaceUsesWith(IR::Value{true}); + inst.ReplaceUsesWithAndRemove(IR::Value{true}); } else { - inst.ReplaceUsesWith(inst.Arg(0)); + inst.ReplaceUsesWithAndRemove(inst.Arg(0)); } } } @@ -183,12 +183,12 @@ void FoldLogicalOr(IR::Inst& inst) { void FoldLogicalNot(IR::Inst& inst) { const IR::U1 value{inst.Arg(0)}; if (value.IsImmediate()) { - inst.ReplaceUsesWith(IR::Value{!value.U1()}); + inst.ReplaceUsesWithAndRemove(IR::Value{!value.U1()}); return; } IR::Inst* const arg{value.InstRecursive()}; if (arg->GetOpcode() == IR::Opcode::LogicalNot) { - inst.ReplaceUsesWith(arg->Arg(0)); + inst.ReplaceUsesWithAndRemove(arg->Arg(0)); } } @@ -199,7 +199,7 @@ void FoldInverseFunc(IR::Inst& inst, IR::Opcode reverse) { } IR::Inst* const arg_inst{value.InstRecursive()}; if (arg_inst->GetOpcode() == reverse) { - inst.ReplaceUsesWith(arg_inst->Arg(0)); + inst.ReplaceUsesWithAndRemove(arg_inst->Arg(0)); return; } } @@ -211,7 +211,7 @@ void FoldAdd(IR::Block& block, IR::Inst& inst) { } const IR::Value rhs{inst.Arg(1)}; if (rhs.IsImmediate() && Arg(rhs) == 0) { - inst.ReplaceUsesWith(inst.Arg(0)); + inst.ReplaceUsesWithAndRemove(inst.Arg(0)); return; } } @@ -226,21 +226,58 @@ void FoldCmpClass(IR::Block& block, IR::Inst& inst) { } else if ((class_mask & IR::FloatClassFunc::Finite) == IR::FloatClassFunc::Finite) { IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; const IR::F32 value = IR::F32{inst.Arg(0)}; - inst.ReplaceUsesWith(ir.LogicalNot(ir.LogicalOr(ir.FPIsInf(value), ir.FPIsInf(value)))); + inst.ReplaceUsesWithAndRemove( + ir.LogicalNot(ir.LogicalOr(ir.FPIsInf(value), ir.FPIsInf(value)))); } else { UNREACHABLE(); } } -void FoldReadLane(IR::Inst& inst) { +void FoldReadLane(IR::Block& block, IR::Inst& inst) { const u32 lane = inst.Arg(1).U32(); IR::Inst* prod = inst.Arg(0).InstRecursive(); - while (prod->GetOpcode() == IR::Opcode::WriteLane) { - if (prod->Arg(2).U32() == lane) { - inst.ReplaceUsesWith(prod->Arg(1)); + + const auto search_chain = [lane](const IR::Inst* prod) -> IR::Value { + while (prod->GetOpcode() == IR::Opcode::WriteLane) { + if (prod->Arg(2).U32() == lane) { + return prod->Arg(1); + } + prod = prod->Arg(0).InstRecursive(); + } + return {}; + }; + + if (prod->GetOpcode() == IR::Opcode::WriteLane) { + if (const IR::Value value = search_chain(prod); !value.IsEmpty()) { + inst.ReplaceUsesWith(value); + } + return; + } + + if (prod->GetOpcode() == IR::Opcode::Phi) { + boost::container::small_vector phi_args; + for (size_t arg_index = 0; arg_index < prod->NumArgs(); ++arg_index) { + const IR::Inst* arg{prod->Arg(arg_index).InstRecursive()}; + if (arg->GetOpcode() != IR::Opcode::WriteLane) { + return; + } + const IR::Value value = search_chain(arg); + if (value.IsEmpty()) { + continue; + } + phi_args.emplace_back(value); + } + if (std::ranges::all_of(phi_args, [&](IR::Value value) { return value == phi_args[0]; })) { + inst.ReplaceUsesWith(phi_args[0]); return; } - prod = prod->Arg(0).InstRecursive(); + const auto insert_point = IR::Block::InstructionList::s_iterator_to(*prod); + IR::Inst* const new_phi{&*block.PrependNewInst(insert_point, IR::Opcode::Phi)}; + new_phi->SetFlags(IR::Type::U32); + for (size_t arg_index = 0; arg_index < phi_args.size(); arg_index++) { + new_phi->AddPhiOperand(prod->PhiBlock(arg_index), phi_args[arg_index]); + } + inst.ReplaceUsesWith(IR::Value{new_phi}); } } @@ -290,7 +327,7 @@ void ConstantPropagation(IR::Block& block, IR::Inst& inst) { case IR::Opcode::SelectF64: return FoldSelect(inst); case IR::Opcode::ReadLane: - return FoldReadLane(inst); + return FoldReadLane(block, inst); case IR::Opcode::FPNeg32: FoldWhenAllImmediates(inst, [](f32 a) { return -a; }); return; diff --git a/src/shader_recompiler/ir/passes/flatten_extended_userdata_pass.cpp b/src/shader_recompiler/ir/passes/flatten_extended_userdata_pass.cpp index 6292edfd8..ef9319891 100644 --- a/src/shader_recompiler/ir/passes/flatten_extended_userdata_pass.cpp +++ b/src/shader_recompiler/ir/passes/flatten_extended_userdata_pass.cpp @@ -148,17 +148,21 @@ static void GenerateSrtProgram(Info& info, PassInfo& pass_info) { // Special case for V# step rate buffers in fetch shader for (const auto [sgpr_base, dword_offset, num_dwords] : info.srt_info.srt_reservations) { // get pointer to V# - c.mov(r10d, ptr[rdi + (sgpr_base << 2)]); - + if (sgpr_base != IR::NumScalarRegs) { + PushPtr(c, sgpr_base); + } u32 src_off = dword_offset << 2; for (auto j = 0; j < num_dwords; j++) { - c.mov(r11d, ptr[r10d + src_off]); + c.mov(r11d, ptr[rdi + src_off]); c.mov(ptr[rsi + (pass_info.dst_off_dw << 2)], r11d); src_off += 4; ++pass_info.dst_off_dw; } + if (sgpr_base != IR::NumScalarRegs) { + PopPtr(c); + } } ASSERT(pass_info.dst_off_dw == info.srt_info.flattened_bufsize_dw); diff --git a/src/shader_recompiler/ir/passes/lower_shared_mem_to_registers.cpp b/src/shader_recompiler/ir/passes/lower_shared_mem_to_registers.cpp index 76bfcf911..c109f3595 100644 --- a/src/shader_recompiler/ir/passes/lower_shared_mem_to_registers.cpp +++ b/src/shader_recompiler/ir/passes/lower_shared_mem_to_registers.cpp @@ -25,7 +25,7 @@ void LowerSharedMemToRegisters(IR::Program& program) { }); ASSERT(it != ds_writes.end()); // Replace data read with value written. - inst.ReplaceUsesWith((*it)->Arg(1)); + inst.ReplaceUsesWithAndRemove((*it)->Arg(1)); } } } diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index 7d29c845d..89c5c78a0 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -381,7 +381,6 @@ void PatchTextureBufferInstruction(IR::Block& block, IR::Inst& inst, Info& info, const auto buffer = info.ReadUdSharp(sharp); const s32 binding = descriptors.Add(TextureBufferResource{ .sharp_idx = sharp, - .nfmt = buffer.GetNumberFmt(), .is_written = inst.GetOpcode() == IR::Opcode::StoreBufferFormatF32, }); @@ -597,7 +596,7 @@ void PatchImageSampleInstruction(IR::Block& block, IR::Inst& inst, Info& info, } return ir.ImageSampleImplicitLod(handle, coords, bias, offset, inst_info); }(); - inst.ReplaceUsesWith(new_inst); + inst.ReplaceUsesWithAndRemove(new_inst); } void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descriptors& descriptors) { @@ -660,11 +659,8 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip } } - const auto type = image.IsPartialCubemap() ? AmdGpu::ImageType::Color2DArray : image.GetType(); u32 image_binding = descriptors.Add(ImageResource{ .sharp_idx = tsharp, - .type = type, - .nfmt = image.GetNumberFmt(), .is_storage = is_storage, .is_depth = bool(inst_info.is_depth), .is_atomic = IsImageAtomicInstruction(inst), diff --git a/src/shader_recompiler/ir/passes/ssa_rewrite_pass.cpp b/src/shader_recompiler/ir/passes/ssa_rewrite_pass.cpp index df73c1bc8..1d252bee1 100644 --- a/src/shader_recompiler/ir/passes/ssa_rewrite_pass.cpp +++ b/src/shader_recompiler/ir/passes/ssa_rewrite_pass.cpp @@ -164,7 +164,6 @@ IR::Opcode UndefOpcode(const FlagTag) noexcept { enum class Status { Start, SetValue, - PreparePhiArgument, PushPhiArgument, }; @@ -253,12 +252,10 @@ public: IR::Inst* const phi{stack.back().phi}; phi->AddPhiOperand(*stack.back().pred_it, stack.back().result); ++stack.back().pred_it; - } - [[fallthrough]]; - case Status::PreparePhiArgument: prepare_phi_operand(); break; } + } } while (stack.size() > 1); return stack.back().result; } @@ -266,9 +263,7 @@ public: void SealBlock(IR::Block* block) { const auto it{incomplete_phis.find(block)}; if (it != incomplete_phis.end()) { - for (auto& pair : it->second) { - auto& variant{pair.first}; - auto& phi{pair.second}; + for (auto& [variant, phi] : it->second) { std::visit([&](auto& variable) { AddPhiOperands(variable, *phi, block); }, variant); } } @@ -289,7 +284,7 @@ private: const size_t num_args{phi.NumArgs()}; for (size_t arg_index = 0; arg_index < num_args; ++arg_index) { const IR::Value& op{phi.Arg(arg_index)}; - if (op.Resolve() == same.Resolve() || op == IR::Value{&phi}) { + if (op.Resolve() == same.Resolve() || op.Resolve() == IR::Value{&phi}) { // Unique value or self-reference continue; } @@ -314,9 +309,15 @@ private: ++reinsert_point; } // Reinsert the phi node and reroute all its uses to the "same" value + const auto users = phi.Uses(); list.insert(reinsert_point, phi); phi.ReplaceUsesWith(same); - // TODO: Try to recursively remove all phi users, which might have become trivial + // Try to recursively remove all phi users, which might have become trivial + for (const auto& [user, arg_index] : users) { + if (user->GetOpcode() == IR::Opcode::Phi) { + TryRemoveTrivialPhi(*user, user->GetParent(), undef_opcode); + } + } return same; } diff --git a/src/shader_recompiler/ir/value.h b/src/shader_recompiler/ir/value.h index 7e46747b9..dbe8b5cc4 100644 --- a/src/shader_recompiler/ir/value.h +++ b/src/shader_recompiler/ir/value.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -107,6 +108,16 @@ public: explicit TypedValue(IR::Inst* inst_) : TypedValue(Value(inst_)) {} }; +struct Use { + Inst* user; + u32 operand; + + Use() = default; + Use(Inst* user_, u32 operand_) : user(user_), operand(operand_) {} + Use(const Use&) = default; + bool operator==(const Use&) const noexcept = default; +}; + class Inst : public boost::intrusive::list_base_hook<> { public: explicit Inst(IR::Opcode op_, u32 flags_) noexcept; @@ -118,14 +129,22 @@ public: Inst& operator=(Inst&&) = delete; Inst(Inst&&) = delete; + IR::Block* GetParent() const { + ASSERT(parent); + return parent; + } + void SetParent(IR::Block* block) { + parent = block; + } + /// Get the number of uses this instruction has. [[nodiscard]] int UseCount() const noexcept { - return use_count; + return uses.size(); } /// Determines whether this instruction has uses or not. [[nodiscard]] bool HasUses() const noexcept { - return use_count > 0; + return uses.size() > 0; } /// Get the opcode this microinstruction represents. @@ -167,7 +186,13 @@ public: void Invalidate(); void ClearArgs(); - void ReplaceUsesWith(Value replacement); + void ReplaceUsesWithAndRemove(Value replacement) { + ReplaceUsesWith(replacement, false); + } + + void ReplaceUsesWith(Value replacement) { + ReplaceUsesWith(replacement, true); + } void ReplaceOpcode(IR::Opcode opcode); @@ -197,25 +222,32 @@ public: return std::bit_cast(definition); } + const auto Uses() const { + return uses; + } + private: struct NonTriviallyDummy { NonTriviallyDummy() noexcept {} }; - void Use(const Value& value); - void UndoUse(const Value& value); + void Use(Inst* used, u32 operand); + void UndoUse(Inst* used, u32 operand); + void ReplaceUsesWith(Value replacement, bool preserve); IR::Opcode op{}; - int use_count{}; u32 flags{}; u32 definition{}; + IR::Block* parent{}; union { NonTriviallyDummy dummy{}; boost::container::small_vector, 2> phi_args; std::array args; }; + + boost::container::list uses; }; -static_assert(sizeof(Inst) <= 128, "Inst size unintentionally increased"); +static_assert(sizeof(Inst) <= 160, "Inst size unintentionally increased"); using U1 = TypedValue; using U8 = TypedValue; @@ -373,4 +405,4 @@ template <> struct hash { std::size_t operator()(const Shader::IR::Value& v) const; }; -} // namespace std \ No newline at end of file +} // namespace std diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index bbda731e0..96c458d44 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h @@ -22,8 +22,10 @@ struct Profile { bool support_fp32_denorm_preserve{}; bool support_fp32_denorm_flush{}; bool support_explicit_workgroup_layout{}; + bool support_legacy_vertex_attributes{}; bool has_broken_spirv_clamp{}; bool lower_left_origin_mode{}; + bool needs_manual_interpolation{}; u64 min_ssbo_alignment{}; }; diff --git a/src/shader_recompiler/recompiler.cpp b/src/shader_recompiler/recompiler.cpp index 19579f665..64f842c42 100644 --- a/src/shader_recompiler/recompiler.cpp +++ b/src/shader_recompiler/recompiler.cpp @@ -32,7 +32,9 @@ IR::Program TranslateProgram(std::span code, Pools& pools, Info& info const RuntimeInfo& runtime_info, const Profile& profile) { // Ensure first instruction is expected. constexpr u32 token_mov_vcchi = 0xBEEB03FF; - ASSERT_MSG(code[0] == token_mov_vcchi, "First instruction is not s_mov_b32 vcc_hi, #imm"); + if (code[0] != token_mov_vcchi) { + LOG_WARNING(Render_Recompiler, "First instruction is not s_mov_b32 vcc_hi, #imm"); + } Gcn::GcnCodeSlice slice(code.data(), code.data() + code.size()); Gcn::GcnDecodeContext decoder; diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index 03936f3a8..4c779a368 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -7,6 +7,7 @@ #include #include #include "common/types.h" +#include "video_core/amdgpu/liverpool.h" #include "video_core/amdgpu/types.h" namespace Shader { @@ -20,7 +21,7 @@ enum class Stage : u32 { Local, Compute, }; -constexpr u32 MaxStageTypes = 6; +constexpr u32 MaxStageTypes = 7; [[nodiscard]] constexpr Stage StageFromIndex(size_t index) noexcept { return static_cast(index); @@ -105,6 +106,8 @@ struct FragmentRuntimeInfo { auto operator<=>(const PsInput&) const noexcept = default; }; + AmdGpu::Liverpool::PsInput en_flags; + AmdGpu::Liverpool::PsInput addr_flags; u32 num_inputs; std::array inputs; struct PsColorBuffer { @@ -117,6 +120,7 @@ struct FragmentRuntimeInfo { bool operator==(const FragmentRuntimeInfo& other) const noexcept { return std::ranges::equal(color_buffers, other.color_buffers) && + en_flags.raw == other.en_flags.raw && addr_flags.raw == other.addr_flags.raw && num_inputs == other.num_inputs && std::ranges::equal(inputs.begin(), inputs.begin() + num_inputs, other.inputs.begin(), other.inputs.begin() + num_inputs); diff --git a/src/shader_recompiler/specialization.h b/src/shader_recompiler/specialization.h index 225b164b5..2a3bd62f4 100644 --- a/src/shader_recompiler/specialization.h +++ b/src/shader_recompiler/specialization.h @@ -6,17 +6,27 @@ #include #include "common/types.h" +#include "frontend/fetch_shader.h" #include "shader_recompiler/backend/bindings.h" #include "shader_recompiler/info.h" -#include "shader_recompiler/ir/passes/srt.h" namespace Shader { +struct VsAttribSpecialization { + AmdGpu::NumberClass num_class{}; + + auto operator<=>(const VsAttribSpecialization&) const = default; +}; + struct BufferSpecialization { u16 stride : 14; u16 is_storage : 1; + u32 size = 0; - auto operator<=>(const BufferSpecialization&) const = default; + bool operator==(const BufferSpecialization& other) const { + return stride == other.stride && is_storage == other.is_storage && + (size >= other.is_storage || is_storage); + } }; struct TextureBufferSpecialization { @@ -50,6 +60,8 @@ struct StageSpecialization { const Shader::Info* info; RuntimeInfo runtime_info; + std::optional fetch_shader_data{}; + boost::container::small_vector vs_attribs; std::bitset bitset{}; boost::container::small_vector buffers; boost::container::small_vector tex_buffers; @@ -57,9 +69,18 @@ struct StageSpecialization { boost::container::small_vector fmasks; Backend::Bindings start{}; - explicit StageSpecialization(const Shader::Info& info_, RuntimeInfo runtime_info_, - Backend::Bindings start_) + explicit StageSpecialization(const Info& info_, RuntimeInfo runtime_info_, + const Profile& profile_, Backend::Bindings start_) : info{&info_}, runtime_info{runtime_info_}, start{start_} { + fetch_shader_data = Gcn::ParseFetchShader(info_); + if (info_.stage == Stage::Vertex && fetch_shader_data && + !profile_.support_legacy_vertex_attributes) { + // Specialize shader on VS input number types to follow spec. + ForEachSharp(vs_attribs, fetch_shader_data->attributes, + [](auto& spec, const auto& desc, AmdGpu::Buffer sharp) { + spec.num_class = AmdGpu::GetNumberClass(sharp.GetNumberFmt()); + }); + } u32 binding{}; if (info->has_readconst) { binding++; @@ -68,6 +89,9 @@ struct StageSpecialization { [](auto& spec, const auto& desc, AmdGpu::Buffer sharp) { spec.stride = sharp.GetStride(); spec.is_storage = desc.IsStorage(sharp); + if (!spec.is_storage) { + spec.size = sharp.GetSize(); + } }); ForEachSharp(binding, tex_buffers, info->texture_buffers, [](auto& spec, const auto& desc, AmdGpu::Buffer sharp) { @@ -75,8 +99,7 @@ struct StageSpecialization { }); ForEachSharp(binding, images, info->images, [](auto& spec, const auto& desc, AmdGpu::Image sharp) { - spec.type = sharp.IsPartialCubemap() ? AmdGpu::ImageType::Color2DArray - : sharp.GetType(); + spec.type = sharp.GetBoundType(); spec.is_integer = AmdGpu::IsInteger(sharp.GetNumberFmt()); }); ForEachSharp(binding, fmasks, info->fmasks, @@ -86,6 +109,17 @@ struct StageSpecialization { }); } + void ForEachSharp(auto& spec_list, auto& desc_list, auto&& func) { + for (const auto& desc : desc_list) { + auto& spec = spec_list.emplace_back(); + const auto sharp = desc.GetSharp(*info); + if (!sharp) { + continue; + } + func(spec, desc, sharp); + } + } + void ForEachSharp(u32& binding, auto& spec_list, auto& desc_list, auto&& func) { for (const auto& desc : desc_list) { auto& spec = spec_list.emplace_back(); @@ -106,6 +140,14 @@ struct StageSpecialization { if (runtime_info != other.runtime_info) { return false; } + if (fetch_shader_data != other.fetch_shader_data) { + return false; + } + for (u32 i = 0; i < vs_attribs.size(); i++) { + if (vs_attribs[i] != other.vs_attribs[i]) { + return false; + } + } u32 binding{}; if (info->has_readconst != other.info->has_readconst) { return false; diff --git a/src/shadps4.qrc b/src/shadps4.qrc index fb537a633..f5b6e176d 100644 --- a/src/shadps4.qrc +++ b/src/shadps4.qrc @@ -6,6 +6,7 @@ images/play_icon.png images/pause_icon.png images/stop_icon.png + images/utils_icon.png images/file_icon.png images/folder_icon.png images/keys.png diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 53aab630e..a4eae8e7a 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -46,7 +46,7 @@ Liverpool::~Liverpool() { } void Liverpool::Process(std::stop_token stoken) { - Common::SetCurrentThreadName("shadPS4:GPU_CommandProcessor"); + Common::SetCurrentThreadName("shadPS4:GpuCommandProcessor"); while (!stoken.stop_requested()) { { @@ -161,6 +161,19 @@ Liverpool::Task Liverpool::ProcessCeUpdate(std::span ccb) { } break; } + case PM4ItOpcode::IndirectBufferConst: { + const auto* indirect_buffer = reinterpret_cast(header); + auto task = + ProcessCeUpdate({indirect_buffer->Address(), indirect_buffer->ib_size}); + while (!task.handle.done()) { + task.handle.resume(); + + TracyFiberLeave; + co_yield {}; + TracyFiberEnter(ccb_task_name); + }; + break; + } default: const u32 count = header->type3.NumWords(); UNREACHABLE_MSG("Unknown PM4 type 3 opcode {:#x} with count {}", @@ -410,14 +423,14 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); const auto offset = draw_indirect->data_offset; const auto ib_address = mapped_queues[GfxQueueId].indirect_args_addr; - const auto size = sizeof(PM4CmdDrawIndirect::DrawInstancedArgs); + const auto size = 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("dcb:{}:DrawIndirect", cmd_address)); - rasterizer->DrawIndirect(false, ib_address, offset, size); + rasterizer->DrawIndirect(false, ib_address, offset, size, 1, 0); rasterizer->ScopeMarkerEnd(); } break; @@ -427,7 +440,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); const auto offset = draw_index_indirect->data_offset; const auto ib_address = mapped_queues[GfxQueueId].indirect_args_addr; - const auto size = sizeof(PM4CmdDrawIndexIndirect::DrawIndexInstancedArgs); + const auto size = sizeof(DrawIndexedIndirectArgs); if (DebugState.DumpingCurrentReg()) { DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); } @@ -435,7 +448,26 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); rasterizer->ScopeMarkerBegin( fmt::format("dcb:{}:DrawIndexIndirect", cmd_address)); - rasterizer->DrawIndirect(true, ib_address, offset, size); + rasterizer->DrawIndirect(true, ib_address, offset, size, 1, 0); + rasterizer->ScopeMarkerEnd(); + } + break; + } + case PM4ItOpcode::DrawIndexIndirectCountMulti: { + const auto* draw_index_indirect = + reinterpret_cast(header); + const auto offset = draw_index_indirect->data_offset; + const auto ib_address = mapped_queues[GfxQueueId].indirect_args_addr; + if (DebugState.DumpingCurrentReg()) { + DebugState.PushRegsDump(base_addr, reinterpret_cast(header), regs); + } + if (rasterizer) { + const auto cmd_address = reinterpret_cast(header); + rasterizer->ScopeMarkerBegin( + fmt::format("dcb:{}:DrawIndexIndirectCountMulti", cmd_address)); + rasterizer->DrawIndirect(true, ib_address, offset, draw_index_indirect->stride, + draw_index_indirect->count, + draw_index_indirect->countAddr); rasterizer->ScopeMarkerEnd(); } break; @@ -533,7 +565,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); - if (dma_data->dst_addr_lo == 0x3022C) { + if (dma_data->dst_addr_lo == 0x3022C || !rasterizer) { break; } if (dma_data->src_sel == DmaDataSrc::Data && dma_data->dst_sel == DmaDataDst::Gds) { @@ -550,7 +582,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spansrc_sel == DmaDataSrc::Gds && dma_data->dst_sel == DmaDataDst::Memory) { - LOG_WARNING(Render_Vulkan, "GDS memory read"); + // LOG_WARNING(Render_Vulkan, "GDS memory read"); } else if (dma_data->src_sel == DmaDataSrc::Memory && dma_data->dst_sel == DmaDataDst::Memory) { rasterizer->InlineData(dma_data->DstAddress(), @@ -668,7 +700,7 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, int vqid) { } case PM4ItOpcode::DmaData: { const auto* dma_data = reinterpret_cast(header); - if (dma_data->dst_addr_lo == 0x3022C) { + if (dma_data->dst_addr_lo == 0x3022C || !rasterizer) { break; } if (dma_data->src_sel == DmaDataSrc::Data && dma_data->dst_sel == DmaDataDst::Gds) { @@ -683,7 +715,7 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, int vqid) { false); } else if (dma_data->src_sel == DmaDataSrc::Gds && dma_data->dst_sel == DmaDataDst::Memory) { - LOG_WARNING(Render_Vulkan, "GDS memory read"); + // LOG_WARNING(Render_Vulkan, "GDS memory read"); } else if (dma_data->src_sel == DmaDataSrc::Memory && dma_data->dst_sel == DmaDataDst::Memory) { rasterizer->InlineData(dma_data->DstAddress(), diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index a4cf79334..ca3b01612 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -86,6 +86,32 @@ struct Liverpool { } }; + static const BinaryInfo& SearchBinaryInfo(const u32* code, size_t search_limit = 0x1000) { + constexpr u32 token_mov_vcchi = 0xBEEB03FF; + + if (code[0] == token_mov_vcchi) { + const auto* info = std::bit_cast(code + (code[1] + 1) * 2); + if (info->Valid()) { + return *info; + } + } + + // First instruction is not s_mov_b32 vcc_hi, #imm, + // which means we cannot get the binary info via said instruction. + // The easiest solution is to iterate through each dword and break + // on the first instance of the binary info. + constexpr size_t signature_size = sizeof(BinaryInfo::signature_ref) / sizeof(u8); + const u32* end = code + search_limit; + + for (const u32* it = code; it < end; ++it) { + if (const BinaryInfo* info = std::bit_cast(it); info->Valid()) { + return *info; + } + } + + UNREACHABLE_MSG("Shader binary info not found."); + } + struct ShaderProgram { u32 address_lo; BitField<0, 8, u32> address_hi; @@ -111,8 +137,7 @@ struct Liverpool { std::span Code() const { const u32* code = Address(); - BinaryInfo bininfo; - std::memcpy(&bininfo, code + (code[1] + 1) * 2, sizeof(bininfo)); + const BinaryInfo& bininfo = SearchBinaryInfo(code); const u32 num_dwords = bininfo.length / sizeof(u32); return std::span{code, num_dwords}; } @@ -164,27 +189,24 @@ struct Liverpool { std::span Code() const { const u32* code = Address(); - BinaryInfo bininfo; - std::memcpy(&bininfo, code + (code[1] + 1) * 2, sizeof(bininfo)); + const BinaryInfo& bininfo = SearchBinaryInfo(code); const u32 num_dwords = bininfo.length / sizeof(u32); return std::span{code, num_dwords}; } }; template - static constexpr auto* GetBinaryInfo(const Shader& sh) { + static constexpr const BinaryInfo& GetBinaryInfo(const Shader& sh) { const auto* code = sh.template Address(); - const auto* bininfo = std::bit_cast(code + (code[1] + 1) * 2); - // ASSERT_MSG(bininfo->Valid(), "Invalid shader binary header"); - return bininfo; + return SearchBinaryInfo(code); } static constexpr Shader::ShaderParams GetParams(const auto& sh) { - auto* bininfo = GetBinaryInfo(sh); + auto& bininfo = GetBinaryInfo(sh); return { .user_data = sh.user_data, .code = sh.Code(), - .hash = bininfo->shader_hash, + .hash = bininfo.shader_hash, }; } @@ -1049,6 +1071,28 @@ struct Liverpool { BitField<27, 1, u32> enable_postz_overrasterization; }; + union PsInput { + u32 raw; + struct { + u32 persp_sample_ena : 1; + u32 persp_center_ena : 1; + u32 persp_centroid_ena : 1; + u32 persp_pull_model_ena : 1; + u32 linear_sample_ena : 1; + u32 linear_center_ena : 1; + u32 linear_centroid_ena : 1; + u32 line_stipple_tex_ena : 1; + u32 pos_x_float_ena : 1; + u32 pos_y_float_ena : 1; + u32 pos_z_float_ena : 1; + u32 pos_w_float_ena : 1; + u32 front_face_ena : 1; + u32 ancillary_ena : 1; + u32 sample_coverage_ena : 1; + u32 pos_fixed_pt_ena : 1; + }; + }; + union Regs { struct { INSERT_PADDING_WORDS(0x2C08); @@ -1089,7 +1133,8 @@ struct Liverpool { INSERT_PADDING_WORDS(2); std::array viewport_scissors; std::array viewport_depths; - INSERT_PADDING_WORDS(0xA103 - 0xA0D4); + INSERT_PADDING_WORDS(0xA102 - 0xA0D4); + u32 index_offset; u32 primitive_restart_index; INSERT_PADDING_WORDS(1); BlendConstants blend_constants; @@ -1103,7 +1148,10 @@ struct Liverpool { INSERT_PADDING_WORDS(0xA191 - 0xA187); std::array ps_inputs; VsOutputConfig vs_output_config; - INSERT_PADDING_WORDS(4); + INSERT_PADDING_WORDS(1); + PsInput ps_input_ena; + PsInput ps_input_addr; + INSERT_PADDING_WORDS(1); BitField<0, 6, u32> num_interp; INSERT_PADDING_WORDS(0xA1C3 - 0xA1B6 - 1); ShaderPosFormat shader_pos_format; @@ -1358,12 +1406,15 @@ static_assert(GFX6_3D_REG_INDEX(color_target_mask) == 0xA08E); static_assert(GFX6_3D_REG_INDEX(color_shader_mask) == 0xA08F); static_assert(GFX6_3D_REG_INDEX(generic_scissor) == 0xA090); static_assert(GFX6_3D_REG_INDEX(viewport_scissors) == 0xA094); +static_assert(GFX6_3D_REG_INDEX(index_offset) == 0xA102); static_assert(GFX6_3D_REG_INDEX(primitive_restart_index) == 0xA103); static_assert(GFX6_3D_REG_INDEX(stencil_control) == 0xA10B); static_assert(GFX6_3D_REG_INDEX(viewports) == 0xA10F); static_assert(GFX6_3D_REG_INDEX(clip_user_data) == 0xA16F); static_assert(GFX6_3D_REG_INDEX(ps_inputs) == 0xA191); static_assert(GFX6_3D_REG_INDEX(vs_output_config) == 0xA1B1); +static_assert(GFX6_3D_REG_INDEX(ps_input_ena) == 0xA1B3); +static_assert(GFX6_3D_REG_INDEX(ps_input_addr) == 0xA1B4); static_assert(GFX6_3D_REG_INDEX(num_interp) == 0xA1B6); static_assert(GFX6_3D_REG_INDEX(shader_pos_format) == 0xA1C3); static_assert(GFX6_3D_REG_INDEX(z_export_format) == 0xA1C4); diff --git a/src/video_core/amdgpu/pixel_format.h b/src/video_core/amdgpu/pixel_format.h index e83313ea4..38c81ba5f 100644 --- a/src/video_core/amdgpu/pixel_format.h +++ b/src/video_core/amdgpu/pixel_format.h @@ -10,7 +10,24 @@ namespace AmdGpu { -[[nodiscard]] constexpr bool IsInteger(NumberFormat nfmt) { +enum NumberClass { + Float, + Sint, + Uint, +}; + +[[nodiscard]] constexpr NumberClass GetNumberClass(const NumberFormat nfmt) { + switch (nfmt) { + case NumberFormat::Sint: + return Sint; + case NumberFormat::Uint: + return Uint; + default: + return Float; + } +} + +[[nodiscard]] constexpr bool IsInteger(const NumberFormat nfmt) { return nfmt == AmdGpu::NumberFormat::Sint || nfmt == AmdGpu::NumberFormat::Uint; } diff --git a/src/video_core/amdgpu/pm4_cmds.h b/src/video_core/amdgpu/pm4_cmds.h index a956b030d..be6751285 100644 --- a/src/video_core/amdgpu/pm4_cmds.h +++ b/src/video_core/amdgpu/pm4_cmds.h @@ -778,14 +778,15 @@ struct PM4CmdDispatchIndirect { u32 dispatch_initiator; ///< Dispatch Initiator Register }; -struct PM4CmdDrawIndirect { - struct DrawInstancedArgs { - u32 vertex_count_per_instance; - u32 instance_count; - u32 start_vertex_location; - u32 start_instance_location; - }; +struct DrawIndirectArgs { + u32 vertex_count_per_instance; + u32 instance_count; + u32 start_vertex_location; + u32 start_instance_location; +}; +static_assert(sizeof(DrawIndirectArgs) == 0x10u); +struct PM4CmdDrawIndirect { PM4Type3Header header; ///< header u32 data_offset; ///< Byte aligned offset where the required data structure starts union { @@ -801,15 +802,16 @@ struct PM4CmdDrawIndirect { u32 draw_initiator; ///< Draw Initiator Register }; -struct PM4CmdDrawIndexIndirect { - struct DrawIndexInstancedArgs { - u32 index_count_per_instance; - u32 instance_count; - u32 start_index_location; - u32 base_vertex_location; - u32 start_instance_location; - }; +struct DrawIndexedIndirectArgs { + u32 index_count_per_instance; + u32 instance_count; + u32 start_index_location; + u32 base_vertex_location; + u32 start_instance_location; +}; +static_assert(sizeof(DrawIndexedIndirectArgs) == 0x14u); +struct PM4CmdDrawIndexIndirect { PM4Type3Header header; ///< header u32 data_offset; ///< Byte aligned offset where the required data structure starts union { @@ -817,7 +819,7 @@ struct PM4CmdDrawIndexIndirect { BitField<0, 16, u32> base_vtx_loc; ///< Offset where the CP will write the ///< BaseVertexLocation it fetched from memory }; - union { // NOTE: this one is undocumented in AMD spec, but Gnm driver writes this field + union { u32 dw3; BitField<0, 16, u32> start_inst_loc; ///< Offset where the CP will write the ///< StartInstanceLocation it fetched from memory @@ -825,4 +827,31 @@ struct PM4CmdDrawIndexIndirect { u32 draw_initiator; ///< Draw Initiator Register }; +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 + }; + union { + u32 dw4; + BitField<0, 16, u32> drawIndexLoc; ///< register offset to write the Draw Index count + BitField<30, 1, u32> + countIndirectEnable; ///< Indicates the data structure count is in memory + BitField<31, 1, u32> + drawIndexEnable; ///< 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 + u32 stride; ///< Stride in memory from one data structure to the next + u32 draw_initiator; ///< Draw Initiator Register +}; + } // namespace AmdGpu diff --git a/src/video_core/amdgpu/pm4_opcodes.h b/src/video_core/amdgpu/pm4_opcodes.h index 4b853138b..ce388d1ba 100644 --- a/src/video_core/amdgpu/pm4_opcodes.h +++ b/src/video_core/amdgpu/pm4_opcodes.h @@ -71,6 +71,7 @@ enum class PM4ItOpcode : u32 { IncrementDeCounter = 0x85, WaitOnCeCounter = 0x86, WaitOnDeCounterDiff = 0x88, + DrawIndexIndirectCountMulti = 0x9d, }; } // namespace AmdGpu diff --git a/src/video_core/amdgpu/resource.h b/src/video_core/amdgpu/resource.h index 81fe43f4e..ba87425f2 100644 --- a/src/video_core/amdgpu/resource.h +++ b/src/video_core/amdgpu/resource.h @@ -304,6 +304,10 @@ struct Image { const auto viewed_slice = last_array - base_array + 1; return GetType() == ImageType::Cube && viewed_slice < 6; } + + ImageType GetBoundType() const noexcept { + return IsPartialCubemap() ? ImageType::Color2DArray : GetType(); + } }; static_assert(sizeof(Image) == 32); // 256bits @@ -358,8 +362,8 @@ enum class MipFilter : u64 { }; enum class BorderColor : u64 { - OpaqueBlack = 0, - TransparentBlack = 1, + TransparentBlack = 0, + OpaqueBlack = 1, White = 2, Custom = 3, }; @@ -416,11 +420,11 @@ struct Sampler { } float MinLod() const noexcept { - return static_cast(min_lod); + return static_cast(min_lod.Value()) / 256.0f; } float MaxLod() const noexcept { - return static_cast(max_lod); + return static_cast(max_lod.Value()) / 256.0f; } }; diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index b15eace12..1abdb230b 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -5,6 +5,7 @@ #include "common/alignment.h" #include "common/scope_exit.h" #include "common/types.h" +#include "shader_recompiler/frontend/fetch_shader.h" #include "shader_recompiler/info.h" #include "video_core/amdgpu/liverpool.h" #include "video_core/buffer_cache/buffer_cache.h" @@ -107,7 +108,8 @@ void BufferCache::DownloadBufferMemory(Buffer& buffer, VAddr device_addr, u64 si } } -bool BufferCache::BindVertexBuffers(const Shader::Info& vs_info) { +bool BufferCache::BindVertexBuffers( + const Shader::Info& vs_info, const std::optional& fetch_shader) { boost::container::small_vector attributes; boost::container::small_vector bindings; SCOPE_EXIT { @@ -126,7 +128,7 @@ bool BufferCache::BindVertexBuffers(const Shader::Info& vs_info) { } }; - if (vs_info.vs_inputs.empty()) { + if (!fetch_shader || fetch_shader->attributes.empty()) { return false; } @@ -150,30 +152,29 @@ bool BufferCache::BindVertexBuffers(const Shader::Info& vs_info) { // Calculate buffers memory overlaps bool has_step_rate = false; boost::container::static_vector ranges{}; - for (const auto& input : vs_info.vs_inputs) { - if (input.instance_step_rate == Shader::Info::VsInput::InstanceIdType::OverStepRate0 || - input.instance_step_rate == Shader::Info::VsInput::InstanceIdType::OverStepRate1) { + for (const auto& attrib : fetch_shader->attributes) { + if (attrib.UsesStepRates()) { has_step_rate = true; continue; } - const auto& buffer = vs_info.ReadUdReg(input.sgpr_base, input.dword_offset); + const auto& buffer = attrib.GetSharp(vs_info); if (buffer.GetSize() == 0) { continue; } guest_buffers.emplace_back(buffer); ranges.emplace_back(buffer.base_address, buffer.base_address + buffer.GetSize()); attributes.push_back({ - .location = input.binding, - .binding = input.binding, + .location = attrib.semantic, + .binding = attrib.semantic, .format = Vulkan::LiverpoolToVK::SurfaceFormat(buffer.GetDataFmt(), buffer.GetNumberFmt()), .offset = 0, }); bindings.push_back({ - .binding = input.binding, + .binding = attrib.semantic, .stride = buffer.GetStride(), - .inputRate = input.instance_step_rate == Shader::Info::VsInput::None + .inputRate = attrib.GetStepRate() == Shader::Gcn::VertexAttribute::InstanceIdType::None ? vk::VertexInputRate::eVertex : vk::VertexInputRate::eInstance, .divisor = 1, @@ -236,7 +237,7 @@ bool BufferCache::BindVertexBuffers(const Shader::Info& vs_info) { u32 BufferCache::BindIndexBuffer(bool& is_indexed, u32 index_offset) { // Emulate QuadList primitive type with CPU made index buffer. const auto& regs = liverpool->regs; - if (regs.primitive_type == AmdGpu::PrimitiveType::QuadList) { + if (regs.primitive_type == AmdGpu::PrimitiveType::QuadList && !is_indexed) { is_indexed = true; // Emit indices. @@ -262,6 +263,32 @@ u32 BufferCache::BindIndexBuffer(bool& is_indexed, u32 index_offset) { VAddr index_address = regs.index_base_address.Address(); index_address += index_offset * index_size; + if (regs.primitive_type == AmdGpu::PrimitiveType::QuadList) { + // Convert indices. + const u32 new_index_size = regs.num_indices * index_size * 6 / 4; + const auto [data, offset] = stream_buffer.Map(new_index_size); + const auto index_ptr = reinterpret_cast(index_address); + switch (index_type) { + case vk::IndexType::eUint16: + Vulkan::LiverpoolToVK::ConvertQuadToTriangleListIndices(data, index_ptr, + regs.num_indices); + break; + case vk::IndexType::eUint32: + Vulkan::LiverpoolToVK::ConvertQuadToTriangleListIndices(data, index_ptr, + regs.num_indices); + break; + default: + UNREACHABLE_MSG("Unsupported QuadList index type {}", vk::to_string(index_type)); + break; + } + stream_buffer.Commit(); + + // Bind index buffer. + const auto cmdbuf = scheduler.CommandBuffer(); + cmdbuf.bindIndexBuffer(stream_buffer.Handle(), offset, index_type); + return new_index_size / index_size; + } + // Bind index buffer. const u32 index_buffer_size = regs.num_indices * index_size; const auto [vk_buffer, offset] = ObtainBuffer(index_address, index_buffer_size, false); @@ -362,7 +389,7 @@ bool BufferCache::IsRegionRegistered(VAddr addr, size_t size) { if (buf_start_addr < end_addr && addr < buf_end_addr) { return true; } - page = Common::DivCeil(end_addr, CACHING_PAGESIZE); + page = Common::DivCeil(buf_end_addr, CACHING_PAGESIZE); } return false; } @@ -620,10 +647,10 @@ void BufferCache::SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size, bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, VAddr device_addr, u32 size) { static constexpr FindFlags find_flags = FindFlags::NoCreate | FindFlags::RelaxDim | FindFlags::RelaxFmt | FindFlags::RelaxSize; - ImageInfo info{}; - info.guest_address = device_addr; - info.guest_size_bytes = size; - const ImageId image_id = texture_cache.FindImage(info, find_flags); + TextureCache::BaseDesc desc{}; + desc.info.guest_address = device_addr; + desc.info.guest_size_bytes = size; + const ImageId image_id = texture_cache.FindImage(desc, find_flags); if (!image_id) { return false; } @@ -635,7 +662,7 @@ bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, VAddr device_addr, "Texel buffer aliases image subresources {:x} : {:x}", device_addr, image.info.guest_address); boost::container::small_vector copies; - u32 offset = buffer.Offset(image.cpu_addr); + u32 offset = buffer.Offset(image.info.guest_address); const u32 num_layers = image.info.resources.layers; const u32 max_offset = offset + size; for (u32 m = 0; m < image.info.resources.levels; m++) { diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index e2519e942..3dab95db7 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -20,8 +20,11 @@ struct Liverpool; } namespace Shader { -struct Info; +namespace Gcn { +struct FetchShaderData; } +struct Info; +} // namespace Shader namespace VideoCore { @@ -39,7 +42,7 @@ public: struct Traits { using Entry = BufferId; - static constexpr size_t AddressSpaceBits = 39; + static constexpr size_t AddressSpaceBits = 40; static constexpr size_t FirstLevelBits = 14; static constexpr size_t PageBits = CACHING_PAGEBITS; }; @@ -76,7 +79,8 @@ public: void InvalidateMemory(VAddr device_addr, u64 size); /// Binds host vertex buffers for the current draw. - bool BindVertexBuffers(const Shader::Info& vs_info); + bool BindVertexBuffers(const Shader::Info& vs_info, + const std::optional& fetch_shader); /// Bind host index buffer for the current draw. u32 BindIndexBuffer(bool& is_indexed, u32 index_offset); diff --git a/src/video_core/buffer_cache/memory_tracker_base.h b/src/video_core/buffer_cache/memory_tracker_base.h index 375701c4c..a59bcfff5 100644 --- a/src/video_core/buffer_cache/memory_tracker_base.h +++ b/src/video_core/buffer_cache/memory_tracker_base.h @@ -14,7 +14,7 @@ namespace VideoCore { class MemoryTracker { public: - static constexpr size_t MAX_CPU_PAGE_BITS = 39; + static constexpr size_t MAX_CPU_PAGE_BITS = 40; static constexpr size_t HIGHER_PAGE_BITS = 22; static constexpr size_t HIGHER_PAGE_SIZE = 1ULL << HIGHER_PAGE_BITS; static constexpr size_t HIGHER_PAGE_MASK = HIGHER_PAGE_SIZE - 1ULL; diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index f2b6cc2d0..4ef8bcdba 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -7,6 +7,8 @@ set(SHADER_FILES detile_m32x1.comp detile_m32x2.comp detile_m32x4.comp + fs_tri.vert + post_process.frag ) set(SHADER_INCLUDE ${CMAKE_CURRENT_BINARY_DIR}/include) diff --git a/src/video_core/host_shaders/fs_tri.vert b/src/video_core/host_shaders/fs_tri.vert new file mode 100644 index 000000000..7b82c11a9 --- /dev/null +++ b/src/video_core/host_shaders/fs_tri.vert @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#version 450 + +layout(location = 0) out vec2 uv; + +void main() { + vec2 pos = vec2( + float((gl_VertexIndex & 1u) << 2u), + float((gl_VertexIndex & 2u) << 1u) + ); + gl_Position = vec4(pos - vec2(1.0, 1.0), 0.0, 1.0); + uv = pos * 0.5; +} diff --git a/src/video_core/host_shaders/post_process.frag b/src/video_core/host_shaders/post_process.frag new file mode 100644 index 000000000..fcced3232 --- /dev/null +++ b/src/video_core/host_shaders/post_process.frag @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#version 450 + +layout (location = 0) in vec2 uv; +layout (location = 0) out vec4 color; + +layout (binding = 0) uniform sampler2D texSampler; + +layout(push_constant) uniform settings { + float gamma; +} pp; + +void main() +{ + vec4 color_linear = texture(texSampler, uv); + color = pow(color_linear, vec4(1.0/(2.2 + 1.0 - pp.gamma))); +} diff --git a/src/video_core/page_manager.cpp b/src/video_core/page_manager.cpp index a49fff43a..fefae81f4 100644 --- a/src/video_core/page_manager.cpp +++ b/src/video_core/page_manager.cpp @@ -7,6 +7,7 @@ #include "common/assert.h" #include "common/error.h" #include "common/signal_context.h" +#include "core/memory.h" #include "core/signals.h" #include "video_core/page_manager.h" #include "video_core/renderer_vulkan/vk_rasterizer.h" @@ -28,10 +29,10 @@ namespace VideoCore { constexpr size_t PAGESIZE = 4_KB; constexpr size_t PAGEBITS = 12; -#if ENABLE_USERFAULTFD +#ifdef ENABLE_USERFAULTFD struct PageManager::Impl { Impl(Vulkan::Rasterizer* rasterizer_) : rasterizer{rasterizer_} { - uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); + uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY); ASSERT_MSG(uffd != -1, "{}", Common::GetLastErrorMsg()); // Request uffdio features from kernel. @@ -113,8 +114,8 @@ struct PageManager::Impl { // Notify rasterizer about the fault. const VAddr addr = msg.arg.pagefault.address; - const VAddr addr_page = Common::AlignDown(addr, PAGESIZE); - rasterizer->InvalidateMemory(addr_page, PAGESIZE); + const VAddr addr_page = GetPageAddr(addr); + rasterizer->InvalidateMemory(addr, addr_page, PAGESIZE); } } @@ -145,23 +146,19 @@ struct PageManager::Impl { ASSERT_MSG(owned_ranges.find(address) != owned_ranges.end(), "Attempted to track non-GPU memory at address {:#x}, size {:#x}.", address, size); -#ifdef _WIN32 - DWORD prot = allow_write ? PAGE_READWRITE : PAGE_READONLY; - DWORD old_prot{}; - BOOL result = VirtualProtect(std::bit_cast(address), size, prot, &old_prot); - ASSERT_MSG(result != 0, "Region protection failed"); -#else - mprotect(reinterpret_cast(address), size, - PROT_READ | (allow_write ? PROT_WRITE : 0)); -#endif + auto* memory = Core::Memory::Instance(); + auto& impl = memory->GetAddressSpace(); + impl.Protect(address, size, + allow_write ? Core::MemoryPermission::ReadWrite + : Core::MemoryPermission::Read); } static bool GuestFaultSignalHandler(void* context, void* fault_address) { const auto addr = reinterpret_cast(fault_address); const bool is_write = Common::IsWriteError(context); if (is_write && owned_ranges.find(addr) != owned_ranges.end()) { - const VAddr addr_aligned = Common::AlignDown(addr, PAGESIZE); - rasterizer->InvalidateMemory(addr_aligned, PAGESIZE); + const VAddr addr_aligned = GetPageAddr(addr); + rasterizer->InvalidateMemory(addr, addr_aligned, PAGESIZE); return true; } return false; @@ -177,6 +174,14 @@ PageManager::PageManager(Vulkan::Rasterizer* rasterizer_) PageManager::~PageManager() = default; +VAddr PageManager::GetPageAddr(VAddr addr) { + return Common::AlignDown(addr, PAGESIZE); +} + +VAddr PageManager::GetNextPageAddr(VAddr addr) { + return Common::AlignUp(addr + 1, PAGESIZE); +} + void PageManager::OnGpuMap(VAddr address, size_t size) { impl->OnMap(address, size); } diff --git a/src/video_core/page_manager.h b/src/video_core/page_manager.h index 0dc022aa5..29a946a8f 100644 --- a/src/video_core/page_manager.h +++ b/src/video_core/page_manager.h @@ -28,6 +28,9 @@ public: /// Increase/decrease the number of surface in pages touching the specified region void UpdatePagesCachedCount(VAddr addr, u64 size, s32 delta); + static VAddr GetPageAddr(VAddr addr); + static VAddr GetNextPageAddr(VAddr addr); + private: struct Impl; std::unique_ptr impl; diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index 43dda4064..f0f7d352c 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -6,7 +6,7 @@ #include "video_core/amdgpu/pixel_format.h" #include "video_core/renderer_vulkan/liverpool_to_vk.h" -#include +#include #define INVALID_NUMBER_FORMAT_COMBO \ LOG_ERROR(Render_Vulkan, "Unsupported number type {} for format {}", number_type, format); @@ -285,10 +285,10 @@ vk::SamplerMipmapMode MipFilter(AmdGpu::MipFilter filter) { vk::BorderColor BorderColor(AmdGpu::BorderColor color) { switch (color) { - case AmdGpu::BorderColor::OpaqueBlack: - return vk::BorderColor::eFloatOpaqueBlack; case AmdGpu::BorderColor::TransparentBlack: return vk::BorderColor::eFloatTransparentBlack; + case AmdGpu::BorderColor::OpaqueBlack: + return vk::BorderColor::eFloatOpaqueBlack; case AmdGpu::BorderColor::White: return vk::BorderColor::eFloatOpaqueWhite; case AmdGpu::BorderColor::Custom: @@ -652,7 +652,7 @@ vk::Format SurfaceFormat(AmdGpu::DataFormat data_format, AmdGpu::NumberFormat nu } vk::Format AdjustColorBufferFormat(vk::Format base_format, - Liverpool::ColorBuffer::SwapMode comp_swap, bool is_vo_surface) { + Liverpool::ColorBuffer::SwapMode comp_swap) { const bool comp_swap_alt = comp_swap == Liverpool::ColorBuffer::SwapMode::Alternate; const bool comp_swap_reverse = comp_swap == Liverpool::ColorBuffer::SwapMode::StandardReverse; const bool comp_swap_alt_reverse = @@ -664,9 +664,9 @@ vk::Format AdjustColorBufferFormat(vk::Format base_format, case vk::Format::eB8G8R8A8Unorm: return vk::Format::eR8G8B8A8Unorm; case vk::Format::eR8G8B8A8Srgb: - return is_vo_surface ? vk::Format::eB8G8R8A8Unorm : vk::Format::eB8G8R8A8Srgb; + return vk::Format::eB8G8R8A8Srgb; case vk::Format::eB8G8R8A8Srgb: - return is_vo_surface ? vk::Format::eR8G8B8A8Unorm : vk::Format::eR8G8B8A8Srgb; + return vk::Format::eR8G8B8A8Srgb; case vk::Format::eA2B10G10R10UnormPack32: return vk::Format::eA2R10G10B10UnormPack32; default: @@ -677,20 +677,10 @@ vk::Format AdjustColorBufferFormat(vk::Format base_format, case vk::Format::eR8G8B8A8Unorm: return vk::Format::eA8B8G8R8UnormPack32; case vk::Format::eR8G8B8A8Srgb: - return is_vo_surface ? vk::Format::eA8B8G8R8UnormPack32 - : vk::Format::eA8B8G8R8SrgbPack32; + return vk::Format::eA8B8G8R8SrgbPack32; default: break; } - } else if (comp_swap_alt_reverse) { - return base_format; - } else { - if (is_vo_surface && base_format == vk::Format::eR8G8B8A8Srgb) { - return vk::Format::eR8G8B8A8Unorm; - } - if (is_vo_surface && base_format == vk::Format::eB8G8R8A8Srgb) { - return vk::Format::eB8G8R8A8Unorm; - } } return base_format; } @@ -736,19 +726,6 @@ vk::Format DepthFormat(DepthBuffer::ZFormat z_format, DepthBuffer::StencilFormat return format->vk_format; } -void EmitQuadToTriangleListIndices(u8* out_ptr, u32 num_vertices) { - static constexpr u16 NumVerticesPerQuad = 4; - u16* out_data = reinterpret_cast(out_ptr); - for (u16 i = 0; i < num_vertices; i += NumVerticesPerQuad) { - *out_data++ = i; - *out_data++ = i + 1; - *out_data++ = i + 2; - *out_data++ = i; - *out_data++ = i + 2; - *out_data++ = i + 3; - } -} - vk::ClearValue ColorBufferClearValue(const AmdGpu::Liverpool::ColorBuffer& color_buffer) { const auto comp_swap = color_buffer.info.comp_swap.Value(); const auto format = color_buffer.info.format.Value(); diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.h b/src/video_core/renderer_vulkan/liverpool_to_vk.h index 5fb04e5f5..287ba691e 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.h +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.h @@ -51,7 +51,7 @@ std::span SurfaceFormats(); vk::Format SurfaceFormat(AmdGpu::DataFormat data_format, AmdGpu::NumberFormat num_format); vk::Format AdjustColorBufferFormat(vk::Format base_format, - Liverpool::ColorBuffer::SwapMode comp_swap, bool is_vo_surface); + Liverpool::ColorBuffer::SwapMode comp_swap); struct DepthFormatInfo { Liverpool::DepthBuffer::ZFormat z_format; @@ -68,7 +68,33 @@ vk::ClearValue ColorBufferClearValue(const AmdGpu::Liverpool::ColorBuffer& color vk::SampleCountFlagBits NumSamples(u32 num_samples, vk::SampleCountFlags supported_flags); -void EmitQuadToTriangleListIndices(u8* out_indices, u32 num_vertices); +static constexpr u16 NumVerticesPerQuad = 4; + +inline void EmitQuadToTriangleListIndices(u8* out_ptr, u32 num_vertices) { + u16* out_data = reinterpret_cast(out_ptr); + for (u16 i = 0; i < num_vertices; i += NumVerticesPerQuad) { + *out_data++ = i; + *out_data++ = i + 1; + *out_data++ = i + 2; + *out_data++ = i; + *out_data++ = i + 2; + *out_data++ = i + 3; + } +} + +template +void ConvertQuadToTriangleListIndices(u8* out_ptr, const u8* in_ptr, u32 num_vertices) { + T* out_data = reinterpret_cast(out_ptr); + const T* in_data = reinterpret_cast(in_ptr); + for (u16 i = 0; i < num_vertices; i += NumVerticesPerQuad) { + *out_data++ = in_data[i]; + *out_data++ = in_data[i + 1]; + *out_data++ = in_data[i + 2]; + *out_data++ = in_data[i]; + *out_data++ = in_data[i + 2]; + *out_data++ = in_data[i + 3]; + } +} static inline vk::Format PromoteFormatToDepth(vk::Format fmt) { if (fmt == vk::Format::eR32Sfloat) { diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp deleted file mode 100644 index 64a483654..000000000 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ /dev/null @@ -1,468 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/config.h" -#include "common/debug.h" -#include "common/singleton.h" -#include "core/file_format/splash.h" -#include "core/libraries/system/systemservice.h" -#include "imgui/renderer/imgui_core.h" -#include "sdl_window.h" -#include "video_core/renderer_vulkan/renderer_vulkan.h" -#include "video_core/renderer_vulkan/vk_rasterizer.h" -#include "video_core/texture_cache/image.h" - -#include - -#include "core/debug_state.h" -#include "core/devtools/layer.h" - -namespace Vulkan { - -bool CanBlitToSwapchain(const vk::PhysicalDevice physical_device, vk::Format format) { - const vk::FormatProperties props{physical_device.getFormatProperties(format)}; - return static_cast(props.optimalTilingFeatures & vk::FormatFeatureFlagBits::eBlitDst); -} - -[[nodiscard]] vk::ImageSubresourceLayers MakeImageSubresourceLayers() { - return vk::ImageSubresourceLayers{ - .aspectMask = vk::ImageAspectFlagBits::eColor, - .mipLevel = 0, - .baseArrayLayer = 0, - .layerCount = 1, - }; -} - -[[nodiscard]] vk::ImageBlit MakeImageBlit(s32 frame_width, s32 frame_height, s32 dst_width, - s32 dst_height, s32 offset_x, s32 offset_y) { - return vk::ImageBlit{ - .srcSubresource = MakeImageSubresourceLayers(), - .srcOffsets = - std::array{ - vk::Offset3D{ - .x = 0, - .y = 0, - .z = 0, - }, - vk::Offset3D{ - .x = frame_width, - .y = frame_height, - .z = 1, - }, - }, - .dstSubresource = MakeImageSubresourceLayers(), - .dstOffsets = - std::array{ - vk::Offset3D{ - .x = offset_x, - .y = offset_y, - .z = 0, - }, - vk::Offset3D{ - .x = offset_x + dst_width, - .y = offset_y + dst_height, - .z = 1, - }, - }, - }; -} - -[[nodiscard]] vk::ImageBlit MakeImageBlitStretch(s32 frame_width, s32 frame_height, - s32 swapchain_width, s32 swapchain_height) { - return MakeImageBlit(frame_width, frame_height, swapchain_width, swapchain_height, 0, 0); -} - -[[nodiscard]] vk::ImageBlit MakeImageBlitFit(s32 frame_width, s32 frame_height, s32 swapchain_width, - s32 swapchain_height) { - float frame_aspect = static_cast(frame_width) / frame_height; - float swapchain_aspect = static_cast(swapchain_width) / swapchain_height; - - s32 dst_width = swapchain_width; - s32 dst_height = swapchain_height; - - if (frame_aspect > swapchain_aspect) { - dst_height = static_cast(swapchain_width / frame_aspect); - } else { - dst_width = static_cast(swapchain_height * frame_aspect); - } - - s32 offset_x = (swapchain_width - dst_width) / 2; - s32 offset_y = (swapchain_height - dst_height) / 2; - - return MakeImageBlit(frame_width, frame_height, dst_width, dst_height, offset_x, offset_y); -} - -RendererVulkan::RendererVulkan(Frontend::WindowSDL& window_, AmdGpu::Liverpool* liverpool_) - : window{window_}, liverpool{liverpool_}, - instance{window, Config::getGpuId(), Config::vkValidationEnabled(), - Config::vkCrashDiagnosticEnabled()}, - draw_scheduler{instance}, present_scheduler{instance}, flip_scheduler{instance}, - swapchain{instance, window}, - rasterizer{std::make_unique(instance, draw_scheduler, liverpool)}, - texture_cache{rasterizer->GetTextureCache()} { - const u32 num_images = swapchain.GetImageCount(); - const vk::Device device = instance.GetDevice(); - - // Create presentation frames. - present_frames.resize(num_images); - for (u32 i = 0; i < num_images; i++) { - Frame& frame = present_frames[i]; - auto [fence_result, fence] = - device.createFence({.flags = vk::FenceCreateFlagBits::eSignaled}); - ASSERT_MSG(fence_result == vk::Result::eSuccess, "Failed to create present done fence: {}", - vk::to_string(fence_result)); - frame.present_done = fence; - free_queue.push(&frame); - } - - // Setup ImGui - ImGui::Core::Initialize(instance, window, num_images, swapchain.GetSurfaceFormat().format); - ImGui::Layer::AddLayer(Common::Singleton::Instance()); -} - -RendererVulkan::~RendererVulkan() { - ImGui::Layer::RemoveLayer(Common::Singleton::Instance()); - draw_scheduler.Finish(); - const vk::Device device = instance.GetDevice(); - for (auto& frame : present_frames) { - vmaDestroyImage(instance.GetAllocator(), frame.image, frame.allocation); - device.destroyImageView(frame.image_view); - device.destroyFence(frame.present_done); - } - ImGui::Core::Shutdown(device); -} - -void RendererVulkan::RecreateFrame(Frame* frame, u32 width, u32 height) { - const vk::Device device = instance.GetDevice(); - if (frame->image_view) { - device.destroyImageView(frame->image_view); - } - if (frame->image) { - vmaDestroyImage(instance.GetAllocator(), frame->image, frame->allocation); - } - - const vk::Format format = swapchain.GetSurfaceFormat().format; - const vk::ImageCreateInfo image_info = { - .imageType = vk::ImageType::e2D, - .format = format, - .extent = {width, height, 1}, - .mipLevels = 1, - .arrayLayers = 1, - .samples = vk::SampleCountFlagBits::e1, - .usage = vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferDst | - vk::ImageUsageFlagBits::eTransferSrc, - }; - - const VmaAllocationCreateInfo alloc_info = { - .flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT, - .usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, - .requiredFlags = 0, - .preferredFlags = 0, - .pool = VK_NULL_HANDLE, - .pUserData = nullptr, - }; - - VkImage unsafe_image{}; - VkImageCreateInfo unsafe_image_info = static_cast(image_info); - - VkResult result = vmaCreateImage(instance.GetAllocator(), &unsafe_image_info, &alloc_info, - &unsafe_image, &frame->allocation, nullptr); - if (result != VK_SUCCESS) [[unlikely]] { - LOG_CRITICAL(Render_Vulkan, "Failed allocating texture with error {}", - vk::to_string(vk::Result{result})); - UNREACHABLE(); - } - frame->image = vk::Image{unsafe_image}; - - const vk::ImageViewCreateInfo view_info = { - .image = frame->image, - .viewType = vk::ImageViewType::e2D, - .format = format, - .subresourceRange{ - .aspectMask = vk::ImageAspectFlagBits::eColor, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }, - }; - auto [view_result, view] = device.createImageView(view_info); - ASSERT_MSG(view_result == vk::Result::eSuccess, "Failed to create frame image view: {}", - vk::to_string(view_result)); - frame->image_view = view; - frame->width = width; - frame->height = height; -} - -bool RendererVulkan::ShowSplash(Frame* frame /*= nullptr*/) { - const auto* splash = Common::Singleton::Instance(); - if (splash->GetImageData().empty()) { - return false; - } - - if (!Libraries::SystemService::IsSplashVisible()) { - return false; - } - - if (!frame) { - if (!splash_img.has_value()) { - VideoCore::ImageInfo info{}; - info.pixel_format = vk::Format::eR8G8B8A8Srgb; - info.type = vk::ImageType::e2D; - info.size = - VideoCore::Extent3D{splash->GetImageInfo().width, splash->GetImageInfo().height, 1}; - info.pitch = splash->GetImageInfo().width; - info.guest_address = VAddr(splash->GetImageData().data()); - info.guest_size_bytes = splash->GetImageData().size(); - splash_img.emplace(instance, present_scheduler, info); - texture_cache.RefreshImage(*splash_img); - } - frame = PrepareFrameInternal(*splash_img); - } - Present(frame); - return true; -} - -Frame* RendererVulkan::PrepareFrameInternal(VideoCore::Image& image, bool is_eop) { - // Request a free presentation frame. - Frame* frame = GetRenderFrame(); - - // EOP flips are triggered from GPU thread so use the drawing scheduler to record - // commands. Otherwise we are dealing with a CPU flip which could have arrived - // from any guest thread. Use a separate scheduler for that. - auto& scheduler = is_eop ? draw_scheduler : flip_scheduler; - scheduler.EndRendering(); - const auto cmdbuf = scheduler.CommandBuffer(); - - image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {}, - cmdbuf); - - const auto frame_subresources = vk::ImageSubresourceRange{ - .aspectMask = vk::ImageAspectFlagBits::eColor, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = VK_REMAINING_ARRAY_LAYERS, - }; - const std::array pre_barrier{ - vk::ImageMemoryBarrier{ - .srcAccessMask = vk::AccessFlagBits::eTransferRead, - .dstAccessMask = vk::AccessFlagBits::eTransferWrite, - .oldLayout = vk::ImageLayout::eUndefined, - .newLayout = vk::ImageLayout::eTransferDstOptimal, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = frame->image, - .subresourceRange{frame_subresources}, - }, - }; - cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, - vk::PipelineStageFlagBits::eTransfer, vk::DependencyFlagBits::eByRegion, - {}, {}, pre_barrier); - - // Clear the frame image before blitting to avoid artifacts. - const vk::ClearColorValue clear_color{std::array{0.0f, 0.0f, 0.0f, 1.0f}}; - cmdbuf.clearColorImage(frame->image, vk::ImageLayout::eTransferDstOptimal, clear_color, - frame_subresources); - - const auto blitBarrier = - vk::ImageMemoryBarrier2{.srcStageMask = vk::PipelineStageFlagBits2::eTransfer, - .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, - .dstStageMask = vk::PipelineStageFlagBits2::eTransfer, - .dstAccessMask = vk::AccessFlagBits2::eTransferWrite, - .oldLayout = vk::ImageLayout::eTransferDstOptimal, - .newLayout = vk::ImageLayout::eTransferDstOptimal, - .image = frame->image, - .subresourceRange{frame_subresources}}; - - cmdbuf.pipelineBarrier2(vk::DependencyInfo{ - .imageMemoryBarrierCount = 1, - .pImageMemoryBarriers = &blitBarrier, - }); - - // Post-processing (Anti-aliasing, FSR etc) goes here. For now just blit to the frame image. - cmdbuf.blitImage(image.image, image.last_state.layout, frame->image, - vk::ImageLayout::eTransferDstOptimal, - MakeImageBlitFit(image.info.size.width, image.info.size.height, frame->width, - frame->height), - vk::Filter::eLinear); - - const vk::ImageMemoryBarrier post_barrier{ - .srcAccessMask = vk::AccessFlagBits::eTransferWrite, - .dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite, - .oldLayout = vk::ImageLayout::eTransferDstOptimal, - .newLayout = vk::ImageLayout::eGeneral, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = frame->image, - .subresourceRange{frame_subresources}, - }; - cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands, - vk::PipelineStageFlagBits::eAllCommands, - vk::DependencyFlagBits::eByRegion, {}, {}, post_barrier); - - // Flush frame creation commands. - frame->ready_semaphore = scheduler.GetMasterSemaphore()->Handle(); - frame->ready_tick = scheduler.CurrentTick(); - SubmitInfo info{}; - scheduler.Flush(info); - return frame; -} - -void RendererVulkan::Present(Frame* frame) { - // Recreate the swapchain if the window was resized. - if (window.getWidth() != swapchain.GetExtent().width || - window.getHeight() != swapchain.GetExtent().height) { - swapchain.Recreate(window.getWidth(), window.getHeight()); - } - - ImGui::Core::NewFrame(); - - swapchain.AcquireNextImage(); - - const vk::Image swapchain_image = swapchain.Image(); - - auto& scheduler = present_scheduler; - const auto cmdbuf = scheduler.CommandBuffer(); - - ImGui::Core::Render(cmdbuf, frame); - - { - auto* profiler_ctx = instance.GetProfilerContext(); - TracyVkNamedZoneC(profiler_ctx, renderer_gpu_zone, cmdbuf, "Host frame", - MarkersPalette::GpuMarkerColor, profiler_ctx != nullptr); - - const vk::Extent2D extent = swapchain.GetExtent(); - const std::array pre_barriers{ - vk::ImageMemoryBarrier{ - .srcAccessMask = vk::AccessFlagBits::eNone, - .dstAccessMask = vk::AccessFlagBits::eTransferWrite, - .oldLayout = vk::ImageLayout::eUndefined, - .newLayout = vk::ImageLayout::eTransferDstOptimal, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = swapchain_image, - .subresourceRange{ - .aspectMask = vk::ImageAspectFlagBits::eColor, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = VK_REMAINING_ARRAY_LAYERS, - }, - }, - vk::ImageMemoryBarrier{ - .srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite, - .dstAccessMask = vk::AccessFlagBits::eTransferRead, - .oldLayout = vk::ImageLayout::eGeneral, - .newLayout = vk::ImageLayout::eTransferSrcOptimal, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = frame->image, - .subresourceRange{ - .aspectMask = vk::ImageAspectFlagBits::eColor, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = VK_REMAINING_ARRAY_LAYERS, - }, - }, - }; - const vk::ImageMemoryBarrier post_barrier{ - .srcAccessMask = vk::AccessFlagBits::eTransferWrite, - .dstAccessMask = vk::AccessFlagBits::eMemoryRead, - .oldLayout = vk::ImageLayout::eTransferDstOptimal, - .newLayout = vk::ImageLayout::ePresentSrcKHR, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = swapchain_image, - .subresourceRange{ - .aspectMask = vk::ImageAspectFlagBits::eColor, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = VK_REMAINING_ARRAY_LAYERS, - }, - }; - - cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eColorAttachmentOutput, - vk::PipelineStageFlagBits::eTransfer, - vk::DependencyFlagBits::eByRegion, {}, {}, pre_barriers); - - cmdbuf.blitImage( - frame->image, vk::ImageLayout::eTransferSrcOptimal, swapchain_image, - vk::ImageLayout::eTransferDstOptimal, - MakeImageBlitStretch(frame->width, frame->height, extent.width, extent.height), - vk::Filter::eLinear); - - cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands, - vk::PipelineStageFlagBits::eAllCommands, - vk::DependencyFlagBits::eByRegion, {}, {}, post_barrier); - - if (profiler_ctx) { - TracyVkCollect(profiler_ctx, cmdbuf); - } - } - - // Flush vulkan commands. - SubmitInfo info{}; - info.AddWait(swapchain.GetImageAcquiredSemaphore()); - info.AddWait(frame->ready_semaphore, frame->ready_tick); - info.AddSignal(swapchain.GetPresentReadySemaphore()); - info.AddSignal(frame->present_done); - scheduler.Flush(info); - - // Present to swapchain. - std::scoped_lock submit_lock{Scheduler::submit_mutex}; - swapchain.Present(); - - // Free the frame for reuse - std::scoped_lock fl{free_mutex}; - free_queue.push(frame); - free_cv.notify_one(); - - DebugState.IncFlipFrameNum(); -} - -Frame* RendererVulkan::GetRenderFrame() { - // Wait for free presentation frames - Frame* frame; - { - std::unique_lock lock{free_mutex}; - free_cv.wait(lock, [this] { return !free_queue.empty(); }); - LOG_DEBUG(Render_Vulkan, "Got render frame, remaining {}", free_queue.size() - 1); - - // Take the frame from the queue - frame = free_queue.front(); - free_queue.pop(); - } - - const vk::Device device = instance.GetDevice(); - vk::Result result{}; - - const auto wait = [&]() { - result = device.waitForFences(frame->present_done, false, std::numeric_limits::max()); - return result; - }; - - // Wait for the presentation to be finished so all frame resources are free - while (wait() != vk::Result::eSuccess) { - ASSERT_MSG(result != vk::Result::eErrorDeviceLost, - "Device lost during waiting for a frame"); - // Retry if the waiting times out - if (result == vk::Result::eTimeout) { - continue; - } - } - - // Reset fence for next queue submission. - device.resetFences(frame->present_done); - - // If the window dimensions changed, recreate this frame - if (frame->width != window.getWidth() || frame->height != window.getHeight()) { - RecreateFrame(frame, window.getWidth(), window.getHeight()); - } - - return frame; -} - -} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index 4ab290780..09d4e4195 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -15,8 +15,10 @@ ComputePipeline::ComputePipeline(const Instance& instance_, Scheduler& scheduler DescriptorHeap& desc_heap_, vk::PipelineCache pipeline_cache, u64 compute_key_, const Shader::Info& info_, vk::ShaderModule module) - : Pipeline{instance_, scheduler_, desc_heap_, pipeline_cache}, compute_key{compute_key_}, - info{&info_} { + : Pipeline{instance_, scheduler_, desc_heap_, pipeline_cache, true}, compute_key{compute_key_} { + auto& info = stages[int(Shader::Stage::Compute)]; + info = &info_; + const vk::PipelineShaderStageCreateInfo shader_ci = { .stage = vk::ShaderStageFlagBits::eCompute, .module = module, @@ -118,90 +120,4 @@ ComputePipeline::ComputePipeline(const Instance& instance_, Scheduler& scheduler ComputePipeline::~ComputePipeline() = default; -bool ComputePipeline::BindResources(VideoCore::BufferCache& buffer_cache, - VideoCore::TextureCache& texture_cache) const { - // Bind resource buffers and textures. - boost::container::small_vector set_writes; - BufferBarriers buffer_barriers; - Shader::PushData push_data{}; - Shader::Backend::Bindings binding{}; - - info->PushUd(binding, push_data); - - buffer_infos.clear(); - buffer_views.clear(); - image_infos.clear(); - - // Most of the time when a metadata is updated with a shader it gets cleared. It means - // we can skip the whole dispatch and update the tracked state instead. Also, it is not - // intended to be consumed and in such rare cases (e.g. HTile introspection, CRAA) we - // will need its full emulation anyways. For cases of metadata read a warning will be logged. - const auto IsMetaUpdate = [&](const auto& desc) { - const VAddr address = desc.GetSharp(*info).base_address; - if (desc.is_written) { - if (texture_cache.TouchMeta(address, true)) { - LOG_TRACE(Render_Vulkan, "Metadata update skipped"); - return true; - } - } else { - if (texture_cache.IsMeta(address)) { - LOG_WARNING(Render_Vulkan, "Unexpected metadata read by a CS shader (buffer)"); - } - } - return false; - }; - - for (const auto& desc : info->buffers) { - if (desc.is_gds_buffer) { - continue; - } - if (IsMetaUpdate(desc)) { - return false; - } - } - for (const auto& desc : info->texture_buffers) { - if (IsMetaUpdate(desc)) { - return false; - } - } - - BindBuffers(buffer_cache, texture_cache, *info, binding, push_data, set_writes, - buffer_barriers); - - BindTextures(texture_cache, *info, binding, set_writes); - - if (set_writes.empty()) { - return false; - } - - const auto cmdbuf = scheduler.CommandBuffer(); - if (!buffer_barriers.empty()) { - const auto dependencies = vk::DependencyInfo{ - .dependencyFlags = vk::DependencyFlagBits::eByRegion, - .bufferMemoryBarrierCount = u32(buffer_barriers.size()), - .pBufferMemoryBarriers = buffer_barriers.data(), - }; - scheduler.EndRendering(); - cmdbuf.pipelineBarrier2(dependencies); - } - - cmdbuf.pushConstants(*pipeline_layout, vk::ShaderStageFlagBits::eCompute, 0u, sizeof(push_data), - &push_data); - - // Bind descriptor set. - if (uses_push_descriptors) { - cmdbuf.pushDescriptorSetKHR(vk::PipelineBindPoint::eCompute, *pipeline_layout, 0, - set_writes); - return true; - } - const auto desc_set = desc_heap.Commit(*desc_layout); - for (auto& set_write : set_writes) { - set_write.dstSet = desc_set; - } - instance.GetDevice().updateDescriptorSets(set_writes, {}); - cmdbuf.bindDescriptorSets(vk::PipelineBindPoint::eCompute, *pipeline_layout, 0, desc_set, {}); - - return true; -} - } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.h b/src/video_core/renderer_vulkan/vk_compute_pipeline.h index f1bc7285a..ca429b58d 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.h @@ -24,13 +24,8 @@ public: vk::ShaderModule module); ~ComputePipeline(); - bool BindResources(VideoCore::BufferCache& buffer_cache, - VideoCore::TextureCache& texture_cache) const; - private: u64 compute_key; - const Shader::Info* info; - bool uses_push_descriptors{}; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 32e3bf8f8..d53204c77 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include @@ -10,22 +11,22 @@ #include "video_core/amdgpu/resource.h" #include "video_core/buffer_cache/buffer_cache.h" #include "video_core/renderer_vulkan/vk_graphics_pipeline.h" + +#include "shader_recompiler/frontend/fetch_shader.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/texture_cache/texture_cache.h" namespace Vulkan { -static constexpr auto gp_stage_flags = vk::ShaderStageFlagBits::eVertex | - vk::ShaderStageFlagBits::eGeometry | - vk::ShaderStageFlagBits::eFragment; - GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& scheduler_, DescriptorHeap& desc_heap_, const GraphicsPipelineKey& key_, vk::PipelineCache pipeline_cache, std::span infos, + std::optional fetch_shader_, std::span modules) - : Pipeline{instance_, scheduler_, desc_heap_, pipeline_cache}, key{key_} { + : Pipeline{instance_, scheduler_, desc_heap_, pipeline_cache}, key{key_}, + fetch_shader{std::move(fetch_shader_)} { const vk::Device device = instance.GetDevice(); std::ranges::copy(infos, stages.begin()); BuildDescSetLayout(); @@ -50,32 +51,31 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul boost::container::static_vector vertex_bindings; boost::container::static_vector vertex_attributes; - if (!instance.IsVertexInputDynamicState()) { - const auto& vs_info = stages[u32(Shader::Stage::Vertex)]; - for (const auto& input : vs_info->vs_inputs) { - if (input.instance_step_rate == Shader::Info::VsInput::InstanceIdType::OverStepRate0 || - input.instance_step_rate == Shader::Info::VsInput::InstanceIdType::OverStepRate1) { + if (fetch_shader && !instance.IsVertexInputDynamicState()) { + const auto& vs_info = GetStage(Shader::Stage::Vertex); + for (const auto& attrib : fetch_shader->attributes) { + if (attrib.UsesStepRates()) { // Skip attribute binding as the data will be pulled by shader continue; } - const auto buffer = - vs_info->ReadUdReg(input.sgpr_base, input.dword_offset); + const auto buffer = attrib.GetSharp(vs_info); if (buffer.GetSize() == 0) { continue; } vertex_attributes.push_back({ - .location = input.binding, - .binding = input.binding, + .location = attrib.semantic, + .binding = attrib.semantic, .format = LiverpoolToVK::SurfaceFormat(buffer.GetDataFmt(), buffer.GetNumberFmt()), .offset = 0, }); vertex_bindings.push_back({ - .binding = input.binding, + .binding = attrib.semantic, .stride = buffer.GetStride(), - .inputRate = input.instance_step_rate == Shader::Info::VsInput::None - ? vk::VertexInputRate::eVertex - : vk::VertexInputRate::eInstance, + .inputRate = + attrib.GetStepRate() == Shader::Gcn::VertexAttribute::InstanceIdType::None + ? vk::VertexInputRate::eVertex + : vk::VertexInputRate::eInstance, }); } } @@ -389,67 +389,4 @@ void GraphicsPipeline::BuildDescSetLayout() { desc_layout = std::move(layout); } -void GraphicsPipeline::BindResources(const Liverpool::Regs& regs, - VideoCore::BufferCache& buffer_cache, - VideoCore::TextureCache& texture_cache) const { - // Bind resource buffers and textures. - boost::container::small_vector set_writes; - BufferBarriers buffer_barriers; - Shader::PushData push_data{}; - Shader::Backend::Bindings binding{}; - - buffer_infos.clear(); - buffer_views.clear(); - image_infos.clear(); - - for (const auto* stage : stages) { - if (!stage) { - continue; - } - if (stage->uses_step_rates) { - push_data.step0 = regs.vgt_instance_step_rate_0; - push_data.step1 = regs.vgt_instance_step_rate_1; - } - stage->PushUd(binding, push_data); - - BindBuffers(buffer_cache, texture_cache, *stage, binding, push_data, set_writes, - buffer_barriers); - - BindTextures(texture_cache, *stage, binding, set_writes); - } - - const auto cmdbuf = scheduler.CommandBuffer(); - SCOPE_EXIT { - cmdbuf.pushConstants(*pipeline_layout, gp_stage_flags, 0U, sizeof(push_data), &push_data); - cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, Handle()); - }; - - if (set_writes.empty()) { - return; - } - - if (!buffer_barriers.empty()) { - const auto dependencies = vk::DependencyInfo{ - .dependencyFlags = vk::DependencyFlagBits::eByRegion, - .bufferMemoryBarrierCount = u32(buffer_barriers.size()), - .pBufferMemoryBarriers = buffer_barriers.data(), - }; - scheduler.EndRendering(); - cmdbuf.pipelineBarrier2(dependencies); - } - - // Bind descriptor set. - if (uses_push_descriptors) { - cmdbuf.pushDescriptorSetKHR(vk::PipelineBindPoint::eGraphics, *pipeline_layout, 0, - set_writes); - return; - } - const auto desc_set = desc_heap.Commit(*desc_layout); - for (auto& set_write : set_writes) { - set_write.dstSet = desc_set; - } - instance.GetDevice().updateDescriptorSets(set_writes, {}); - cmdbuf.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, *pipeline_layout, 0, desc_set, {}); -} - } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index f7762eb12..91ffe4ea4 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -2,7 +2,9 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include + #include "common/types.h" +#include "shader_recompiler/frontend/fetch_shader.h" #include "video_core/renderer_vulkan/liverpool_to_vk.h" #include "video_core/renderer_vulkan/vk_common.h" #include "video_core/renderer_vulkan/vk_pipeline_common.h" @@ -14,8 +16,8 @@ class TextureCache; namespace Vulkan { -static constexpr u32 MaxVertexBufferCount = 32; static constexpr u32 MaxShaderStages = 5; +static constexpr u32 MaxVertexBufferCount = 32; class Instance; class Scheduler; @@ -58,14 +60,12 @@ public: GraphicsPipeline(const Instance& instance, Scheduler& scheduler, DescriptorHeap& desc_heap, const GraphicsPipelineKey& key, vk::PipelineCache pipeline_cache, std::span stages, + std::optional fetch_shader, std::span modules); ~GraphicsPipeline(); - void BindResources(const Liverpool::Regs& regs, VideoCore::BufferCache& buffer_cache, - VideoCore::TextureCache& texture_cache) const; - - const Shader::Info& GetStage(Shader::Stage stage) const noexcept { - return *stages[u32(stage)]; + const std::optional& GetFetchShader() const noexcept { + return fetch_shader; } bool IsEmbeddedVs() const noexcept { @@ -99,9 +99,8 @@ private: void BuildDescSetLayout(); private: - std::array stages{}; GraphicsPipelineKey key; - bool uses_push_descriptors{}; + std::optional fetch_shader{}; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 0edc4228a..81784eb60 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -70,8 +70,9 @@ std::unordered_map GetFormatProperties( static constexpr std::array misc_formats = { vk::Format::eA2R10G10B10UnormPack32, vk::Format::eA8B8G8R8UnormPack32, vk::Format::eA8B8G8R8SrgbPack32, vk::Format::eB8G8R8A8Unorm, - vk::Format::eB8G8R8A8Srgb, vk::Format::eR5G6B5UnormPack16, - vk::Format::eD24UnormS8Uint, + vk::Format::eB8G8R8A8Snorm, vk::Format::eB8G8R8A8Uint, + vk::Format::eB8G8R8A8Sint, vk::Format::eB8G8R8A8Srgb, + vk::Format::eR5G6B5UnormPack16, vk::Format::eD24UnormS8Uint, }; for (const auto& format : misc_formats) { if (!format_properties.contains(format)) { @@ -95,7 +96,7 @@ Instance::Instance(bool enable_validation, bool enable_crash_diagnostic) Instance::Instance(Frontend::WindowSDL& window, s32 physical_device_index, bool enable_validation /*= false*/, bool enable_crash_diagnostic /*= false*/) - : instance{CreateInstance(window.getWindowInfo().type, enable_validation, + : instance{CreateInstance(window.GetWindowInfo().type, enable_validation, enable_crash_diagnostic)}, physical_devices{EnumeratePhysicalDevices(instance)} { if (enable_validation) { @@ -256,6 +257,7 @@ bool Instance::CreateDevice() { workgroup_memory_explicit_layout = add_extension(VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME); vertex_input_dynamic_state = add_extension(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); + fragment_shader_barycentric = add_extension(VK_KHR_FRAGMENT_SHADER_BARYCENTRIC_EXTENSION_NAME); // The next two extensions are required to be available together in order to support write masks color_write_en = add_extension(VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME); @@ -264,6 +266,7 @@ bool Instance::CreateDevice() { const bool robustness = add_extension(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME); list_restart = add_extension(VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_EXTENSION_NAME); maintenance5 = add_extension(VK_KHR_MAINTENANCE_5_EXTENSION_NAME); + legacy_vertex_attributes = add_extension(VK_EXT_LEGACY_VERTEX_ATTRIBUTES_EXTENSION_NAME); // These extensions are promoted by Vulkan 1.3, but for greater compatibility we use Vulkan 1.2 // with extensions. @@ -345,6 +348,7 @@ bool Instance::CreateDevice() { }, vk::PhysicalDeviceVulkan12Features{ .samplerMirrorClampToEdge = vk12_features.samplerMirrorClampToEdge, + .drawIndirectCount = vk12_features.drawIndirectCount, .shaderFloat16 = vk12_features.shaderFloat16, .scalarBlockLayout = vk12_features.scalarBlockLayout, .uniformBufferStandardLayout = vk12_features.uniformBufferStandardLayout, @@ -398,6 +402,12 @@ bool Instance::CreateDevice() { vk::PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT{ .primitiveTopologyListRestart = true, }, + vk::PhysicalDeviceFragmentShaderBarycentricFeaturesKHR{ + .fragmentShaderBarycentric = true, + }, + vk::PhysicalDeviceLegacyVertexAttributesFeaturesEXT{ + .legacyVertexAttributes = true, + }, #ifdef __APPLE__ feature_chain.get(), #endif @@ -437,6 +447,12 @@ bool Instance::CreateDevice() { if (!vertex_input_dynamic_state) { device_chain.unlink(); } + if (!fragment_shader_barycentric) { + device_chain.unlink(); + } + if (!legacy_vertex_attributes) { + device_chain.unlink(); + } auto [device_result, dev] = physical_device.createDeviceUnique(device_chain.get()); if (device_result != vk::Result::eSuccess) { diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index 51c2c57c5..81303c9cc 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -143,10 +143,21 @@ public: return maintenance5; } + /// Returns true when VK_KHR_fragment_shader_barycentric is supported. + bool IsFragmentShaderBarycentricSupported() const { + return fragment_shader_barycentric; + } + + /// Returns true when VK_EXT_primitive_topology_list_restart is supported. bool IsListRestartSupported() const { return list_restart; } + /// Returns true when VK_EXT_legacy_vertex_attributes is supported. + bool IsLegacyVertexAttributesSupported() const { + return legacy_vertex_attributes; + } + /// Returns true when geometry shaders are supported by the device bool IsGeometryStageSupported() const { return features.geometryShader; @@ -315,6 +326,7 @@ private: bool null_descriptor{}; bool maintenance5{}; bool list_restart{}; + bool legacy_vertex_attributes{}; u64 min_imported_host_pointer_alignment{}; u32 subgroup_size{}; bool tooling_info{}; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index da098fa37..53bdc79a6 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -7,17 +7,18 @@ #include "common/hash.h" #include "common/io_file.h" #include "common/path_util.h" +#include "core/debug_state.h" #include "shader_recompiler/backend/spirv/emit_spirv.h" #include "shader_recompiler/info.h" #include "shader_recompiler/recompiler.h" #include "shader_recompiler/runtime_info.h" -#include "video_core/renderer_vulkan/renderer_vulkan.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_pipeline_cache.h" +#include "video_core/renderer_vulkan/vk_presenter.h" #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_shader_util.h" -extern std::unique_ptr renderer; +extern std::unique_ptr presenter; namespace Vulkan { @@ -122,6 +123,8 @@ Shader::RuntimeInfo PipelineCache::BuildRuntimeInfo(Shader::Stage stage) { } case Shader::Stage::Fragment: { BuildCommon(regs.ps_program); + info.fs_info.en_flags = regs.ps_input_ena; + info.fs_info.addr_flags = regs.ps_input_addr; const auto& ps_inputs = regs.ps_inputs; info.fs_info.num_inputs = regs.num_interp; for (u32 i = 0; i < regs.num_interp; i++) { @@ -168,6 +171,9 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_, .support_fp32_denorm_preserve = bool(vk12_props.shaderDenormPreserveFloat32), .support_fp32_denorm_flush = bool(vk12_props.shaderDenormFlushToZeroFloat32), .support_explicit_workgroup_layout = true, + .support_legacy_vertex_attributes = instance_.IsLegacyVertexAttributesSupported(), + .needs_manual_interpolation = instance.IsFragmentShaderBarycentricSupported() && + instance.GetDriverID() == vk::DriverId::eNvidiaProprietary, }; auto [cache_result, cache] = instance.GetDevice().createPipelineCacheUnique({}); ASSERT_MSG(cache_result == vk::Result::eSuccess, "Failed to create pipeline cache: {}", @@ -184,7 +190,7 @@ const GraphicsPipeline* PipelineCache::GetGraphicsPipeline() { const auto [it, is_new] = graphics_pipelines.try_emplace(graphics_key); if (is_new) { it.value() = graphics_pipeline_pool.Create(instance, scheduler, desc_heap, graphics_key, - *pipeline_cache, infos, modules); + *pipeline_cache, infos, fetch_shader, modules); } return it->second; } @@ -260,14 +266,13 @@ bool PipelineCache::RefreshGraphicsKey() { // recompiler. for (auto cb = 0u, remapped_cb = 0u; cb < Liverpool::NumColorBuffers; ++cb) { auto const& col_buf = regs.color_buffers[cb]; - if (skip_cb_binding || !col_buf || !regs.color_target_mask.GetMask(cb)) { + if (skip_cb_binding || !col_buf) { continue; } const auto base_format = LiverpoolToVK::SurfaceFormat(col_buf.info.format, col_buf.NumFormat()); - const bool is_vo_surface = renderer->IsVideoOutSurface(col_buf); - key.color_formats[remapped_cb] = LiverpoolToVK::AdjustColorBufferFormat( - base_format, col_buf.info.comp_swap.Value(), false /*is_vo_surface*/); + key.color_formats[remapped_cb] = + LiverpoolToVK::AdjustColorBufferFormat(base_format, col_buf.info.comp_swap.Value()); key.color_num_formats[remapped_cb] = col_buf.NumFormat(); if (base_format == key.color_formats[remapped_cb]) { key.mrt_swizzles[remapped_cb] = col_buf.info.comp_swap.Value(); @@ -276,6 +281,8 @@ bool PipelineCache::RefreshGraphicsKey() { ++remapped_cb; } + fetch_shader = std::nullopt; + Shader::Backend::Bindings binding{}; const auto& TryBindStageRemap = [&](Shader::Stage stage_in, Shader::Stage stage_out) -> bool { const auto stage_in_idx = static_cast(stage_in); @@ -293,8 +300,8 @@ bool PipelineCache::RefreshGraphicsKey() { return false; } - const auto* bininfo = Liverpool::GetBinaryInfo(*pgm); - if (!bininfo->Valid()) { + const auto& bininfo = Liverpool::GetBinaryInfo(*pgm); + if (!bininfo.Valid()) { LOG_WARNING(Render_Vulkan, "Invalid binary info structure!"); key.stage_hashes[stage_out_idx] = 0; infos[stage_out_idx] = nullptr; @@ -302,8 +309,12 @@ bool PipelineCache::RefreshGraphicsKey() { } auto params = Liverpool::GetParams(*pgm); - std::tie(infos[stage_out_idx], modules[stage_out_idx], key.stage_hashes[stage_out_idx]) = - GetProgram(stage_in, params, binding); + std::optional fetch_shader_; + std::tie(infos[stage_out_idx], modules[stage_out_idx], fetch_shader_, + key.stage_hashes[stage_out_idx]) = GetProgram(stage_in, params, binding); + if (fetch_shader_) { + fetch_shader = fetch_shader_; + } return true; }; @@ -339,16 +350,14 @@ bool PipelineCache::RefreshGraphicsKey() { } } - const auto* vs_info = infos[static_cast(Shader::Stage::Vertex)]; - if (vs_info && !instance.IsVertexInputDynamicState()) { + const auto vs_info = infos[static_cast(Shader::Stage::Vertex)]; + if (vs_info && fetch_shader && !instance.IsVertexInputDynamicState()) { u32 vertex_binding = 0; - for (const auto& input : vs_info->vs_inputs) { - if (input.instance_step_rate == Shader::Info::VsInput::InstanceIdType::OverStepRate0 || - input.instance_step_rate == Shader::Info::VsInput::InstanceIdType::OverStepRate1) { + for (const auto& attrib : fetch_shader->attributes) { + if (attrib.UsesStepRates()) { continue; } - const auto& buffer = - vs_info->ReadUdReg(input.sgpr_base, input.dword_offset); + const auto& buffer = attrib.GetSharp(*vs_info); if (buffer.GetSize() == 0) { continue; } @@ -358,11 +367,12 @@ bool PipelineCache::RefreshGraphicsKey() { } } + u32 num_samples = 1u; + // Second pass to fill remain CB pipeline key data for (auto cb = 0u, remapped_cb = 0u; cb < Liverpool::NumColorBuffers; ++cb) { auto const& col_buf = regs.color_buffers[cb]; - if (skip_cb_binding || !col_buf || !regs.color_target_mask.GetMask(cb) || - (key.mrt_mask & (1u << cb)) == 0) { + if (skip_cb_binding || !col_buf || (key.mrt_mask & (1u << cb)) == 0) { key.color_formats[cb] = vk::Format::eUndefined; key.mrt_swizzles[cb] = Liverpool::ColorBuffer::SwapMode::Standard; continue; @@ -374,8 +384,16 @@ bool PipelineCache::RefreshGraphicsKey() { key.write_masks[remapped_cb] = vk::ColorComponentFlags{regs.color_target_mask.GetMask(cb)}; key.cb_shader_mask.SetMask(remapped_cb, regs.color_shader_mask.GetMask(cb)); + num_samples = std::max(num_samples, 1u << col_buf.attrib.num_samples_log2); + ++remapped_cb; } + + // It seems that the number of samples > 1 set in the AA config doesn't mean we're always + // rendering with MSAA, so we need to derive MS ratio from the CB settings. + num_samples = std::max(num_samples, regs.depth_buffer.NumSamples()); + key.num_samples = num_samples; + return true; } @@ -383,7 +401,7 @@ bool PipelineCache::RefreshComputeKey() { Shader::Backend::Bindings binding{}; const auto* cs_pgm = &liverpool->regs.cs_program; const auto cs_params = Liverpool::GetParams(*cs_pgm); - std::tie(infos[0], modules[0], compute_key) = + std::tie(infos[0], modules[0], fetch_shader, compute_key) = GetProgram(Shader::Stage::Compute, cs_params, binding); return true; } @@ -397,33 +415,43 @@ vk::ShaderModule PipelineCache::CompileModule(Shader::Info& info, DumpShader(code, info.pgm_hash, info.stage, perm_idx, "bin"); const auto ir_program = Shader::TranslateProgram(code, pools, info, runtime_info, profile); - const auto spv = Shader::Backend::SPIRV::EmitSPIRV(profile, runtime_info, ir_program, binding); + auto spv = Shader::Backend::SPIRV::EmitSPIRV(profile, runtime_info, ir_program, binding); DumpShader(spv, info.pgm_hash, info.stage, perm_idx, "spv"); + auto patch = GetShaderPatch(info.pgm_hash, info.stage, perm_idx, "spv"); + if (patch) { + spv = *patch; + LOG_INFO(Loader, "Loaded patch for {} shader {:#x}", info.stage, info.pgm_hash); + } const auto module = CompileSPV(spv, instance.GetDevice()); const auto name = fmt::format("{}_{:#x}_{}", info.stage, info.pgm_hash, perm_idx); Vulkan::SetObjectName(instance.GetDevice(), module, name); + if (Config::collectShadersForDebug()) { + DebugState.CollectShader(name, spv, code); + } return module; } -std::tuple PipelineCache::GetProgram( - Shader::Stage stage, Shader::ShaderParams params, Shader::Backend::Bindings& binding) { +std::tuple, u64> +PipelineCache::GetProgram(Shader::Stage stage, Shader::ShaderParams params, + Shader::Backend::Bindings& binding) { const auto runtime_info = BuildRuntimeInfo(stage); auto [it_pgm, new_program] = program_cache.try_emplace(params.hash); if (new_program) { Program* program = program_pool.Create(stage, params); auto start = binding; const auto module = CompileModule(program->info, runtime_info, params.code, 0, binding); - const auto spec = Shader::StageSpecialization(program->info, runtime_info, start); + const auto spec = Shader::StageSpecialization(program->info, runtime_info, profile, start); program->AddPermut(module, std::move(spec)); it_pgm.value() = program; - return std::make_tuple(&program->info, module, HashCombine(params.hash, 0)); + return std::make_tuple(&program->info, module, spec.fetch_shader_data, + HashCombine(params.hash, 0)); } Program* program = it_pgm->second; auto& info = program->info; info.RefreshFlatBuf(); - const auto spec = Shader::StageSpecialization(info, runtime_info, binding); + const auto spec = Shader::StageSpecialization(info, runtime_info, profile, binding); size_t perm_idx = program->modules.size(); vk::ShaderModule module{}; @@ -437,7 +465,8 @@ std::tuple PipelineCache::GetProgram module = it->module; perm_idx = std::distance(program->modules.begin(), it); } - return std::make_tuple(&info, module, HashCombine(params.hash, perm_idx)); + return std::make_tuple(&info, module, spec.fetch_shader_data, + HashCombine(params.hash, perm_idx)); } void PipelineCache::DumpShader(std::span code, u64 hash, Shader::Stage stage, @@ -456,4 +485,27 @@ void PipelineCache::DumpShader(std::span code, u64 hash, Shader::Stag file.WriteSpan(code); } +std::optional> PipelineCache::GetShaderPatch(u64 hash, Shader::Stage stage, + size_t perm_idx, + std::string_view ext) { + if (!Config::patchShaders()) { + return {}; + } + + using namespace Common::FS; + const auto patch_dir = GetUserPath(PathType::ShaderDir) / "patch"; + if (!std::filesystem::exists(patch_dir)) { + std::filesystem::create_directories(patch_dir); + } + const auto filename = fmt::format("{}_{:#018x}_{}.{}", stage, hash, perm_idx, ext); + const auto filepath = patch_dir / filename; + if (!std::filesystem::exists(filepath)) { + return {}; + } + const auto file = IOFile{patch_dir / filename, FileAccessMode::Read}; + std::vector code(file.GetSize() / sizeof(u32)); + file.Read(code); + return code; +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index 7e44bbf09..e4a8abd4f 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -38,8 +38,6 @@ struct Program { }; class PipelineCache { - static constexpr size_t MaxShaderStages = 5; - public: explicit PipelineCache(const Instance& instance, Scheduler& scheduler, AmdGpu::Liverpool* liverpool); @@ -49,8 +47,10 @@ public: const ComputePipeline* GetComputePipeline(); - std::tuple GetProgram( - Shader::Stage stage, Shader::ShaderParams params, Shader::Backend::Bindings& binding); + std::tuple, + u64> + GetProgram(Shader::Stage stage, Shader::ShaderParams params, + Shader::Backend::Bindings& binding); private: bool RefreshGraphicsKey(); @@ -58,6 +58,8 @@ private: void DumpShader(std::span code, u64 hash, Shader::Stage stage, size_t perm_idx, std::string_view ext); + std::optional> GetShaderPatch(u64 hash, Shader::Stage stage, size_t perm_idx, + std::string_view ext); vk::ShaderModule CompileModule(Shader::Info& info, const Shader::RuntimeInfo& runtime_info, std::span code, size_t perm_idx, Shader::Backend::Bindings& binding); @@ -80,6 +82,7 @@ private: tsl::robin_map graphics_pipelines; std::array infos{}; std::array modules{}; + std::optional fetch_shader{}; GraphicsPipelineKey graphics_key{}; u64 compute_key{}; }; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_common.cpp b/src/video_core/renderer_vulkan/vk_pipeline_common.cpp index 4c297cd42..6b48a40a0 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_common.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_common.cpp @@ -12,230 +12,47 @@ namespace Vulkan { -boost::container::static_vector Pipeline::image_infos; -boost::container::static_vector Pipeline::buffer_views; -boost::container::static_vector Pipeline::buffer_infos; - Pipeline::Pipeline(const Instance& instance_, Scheduler& scheduler_, DescriptorHeap& desc_heap_, - vk::PipelineCache pipeline_cache) - : instance{instance_}, scheduler{scheduler_}, desc_heap{desc_heap_} {} + vk::PipelineCache pipeline_cache, bool is_compute_ /*= false*/) + : instance{instance_}, scheduler{scheduler_}, desc_heap{desc_heap_}, is_compute{is_compute_} {} Pipeline::~Pipeline() = default; -void Pipeline::BindBuffers(VideoCore::BufferCache& buffer_cache, - VideoCore::TextureCache& texture_cache, const Shader::Info& stage, - Shader::Backend::Bindings& binding, Shader::PushData& push_data, - DescriptorWrites& set_writes, BufferBarriers& buffer_barriers) const { - using BufferBindingInfo = std::pair; - static boost::container::static_vector buffer_bindings; +void Pipeline::BindResources(DescriptorWrites& set_writes, const BufferBarriers& buffer_barriers, + const Shader::PushData& push_data) const { + const auto cmdbuf = scheduler.CommandBuffer(); + const auto bind_point = + IsCompute() ? vk::PipelineBindPoint::eCompute : vk::PipelineBindPoint::eGraphics; - buffer_bindings.clear(); - - for (const auto& desc : stage.buffers) { - const auto vsharp = desc.GetSharp(stage); - if (!desc.is_gds_buffer && vsharp.base_address != 0 && vsharp.GetSize() > 0) { - const auto buffer_id = buffer_cache.FindBuffer(vsharp.base_address, vsharp.GetSize()); - buffer_bindings.emplace_back(buffer_id, vsharp); - } else { - buffer_bindings.emplace_back(VideoCore::BufferId{}, vsharp); - } + if (!buffer_barriers.empty()) { + const auto dependencies = vk::DependencyInfo{ + .dependencyFlags = vk::DependencyFlagBits::eByRegion, + .bufferMemoryBarrierCount = u32(buffer_barriers.size()), + .pBufferMemoryBarriers = buffer_barriers.data(), + }; + scheduler.EndRendering(); + cmdbuf.pipelineBarrier2(dependencies); } - using TexBufferBindingInfo = std::pair; - static boost::container::static_vector texbuffer_bindings; + const auto stage_flags = IsCompute() ? vk::ShaderStageFlagBits::eCompute : gp_stage_flags; + cmdbuf.pushConstants(*pipeline_layout, stage_flags, 0u, sizeof(push_data), &push_data); - texbuffer_bindings.clear(); - - for (const auto& desc : stage.texture_buffers) { - const auto vsharp = desc.GetSharp(stage); - if (vsharp.base_address != 0 && vsharp.GetSize() > 0 && - vsharp.GetDataFmt() != AmdGpu::DataFormat::FormatInvalid) { - const auto buffer_id = buffer_cache.FindBuffer(vsharp.base_address, vsharp.GetSize()); - texbuffer_bindings.emplace_back(buffer_id, vsharp); - } else { - texbuffer_bindings.emplace_back(VideoCore::BufferId{}, vsharp); - } + // Bind descriptor set. + if (set_writes.empty()) { + return; } - // Bind the flattened user data buffer as a UBO so it's accessible to the shader - if (stage.has_readconst) { - const auto [vk_buffer, offset] = buffer_cache.ObtainHostUBO(stage.flattened_ud_buf); - buffer_infos.emplace_back(vk_buffer->Handle(), offset, - stage.flattened_ud_buf.size() * sizeof(u32)); - set_writes.push_back({ - .dstSet = VK_NULL_HANDLE, - .dstBinding = binding.unified++, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eUniformBuffer, - .pBufferInfo = &buffer_infos.back(), - }); - ++binding.buffer; + if (uses_push_descriptors) { + cmdbuf.pushDescriptorSetKHR(bind_point, *pipeline_layout, 0, set_writes); + return; } - // Second pass to re-bind buffers that were updated after binding - for (u32 i = 0; i < buffer_bindings.size(); i++) { - const auto& [buffer_id, vsharp] = buffer_bindings[i]; - const auto& desc = stage.buffers[i]; - const bool is_storage = desc.IsStorage(vsharp); - if (!buffer_id) { - if (desc.is_gds_buffer) { - const auto* gds_buf = buffer_cache.GetGdsBuffer(); - buffer_infos.emplace_back(gds_buf->Handle(), 0, gds_buf->SizeBytes()); - } else if (instance.IsNullDescriptorSupported()) { - buffer_infos.emplace_back(VK_NULL_HANDLE, 0, VK_WHOLE_SIZE); - } else { - auto& null_buffer = buffer_cache.GetBuffer(VideoCore::NULL_BUFFER_ID); - buffer_infos.emplace_back(null_buffer.Handle(), 0, VK_WHOLE_SIZE); - } - } else { - const auto [vk_buffer, offset] = buffer_cache.ObtainBuffer( - vsharp.base_address, vsharp.GetSize(), desc.is_written, false, buffer_id); - const u32 alignment = - is_storage ? instance.StorageMinAlignment() : instance.UniformMinAlignment(); - const u32 offset_aligned = Common::AlignDown(offset, alignment); - const u32 adjust = offset - offset_aligned; - ASSERT(adjust % 4 == 0); - push_data.AddOffset(binding.buffer, adjust); - buffer_infos.emplace_back(vk_buffer->Handle(), offset_aligned, - vsharp.GetSize() + adjust); - } - - set_writes.push_back({ - .dstSet = VK_NULL_HANDLE, - .dstBinding = binding.unified++, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = is_storage ? vk::DescriptorType::eStorageBuffer - : vk::DescriptorType::eUniformBuffer, - .pBufferInfo = &buffer_infos.back(), - }); - ++binding.buffer; - } - - const auto null_buffer_view = - instance.IsNullDescriptorSupported() ? VK_NULL_HANDLE : buffer_cache.NullBufferView(); - for (u32 i = 0; i < texbuffer_bindings.size(); i++) { - const auto& [buffer_id, vsharp] = texbuffer_bindings[i]; - const auto& desc = stage.texture_buffers[i]; - vk::BufferView& buffer_view = buffer_views.emplace_back(null_buffer_view); - if (buffer_id) { - const u32 alignment = instance.TexelBufferMinAlignment(); - const auto [vk_buffer, offset] = buffer_cache.ObtainBuffer( - vsharp.base_address, vsharp.GetSize(), desc.is_written, true, buffer_id); - const u32 fmt_stride = AmdGpu::NumBits(vsharp.GetDataFmt()) >> 3; - ASSERT_MSG(fmt_stride == vsharp.GetStride(), - "Texel buffer stride must match format stride"); - const u32 offset_aligned = Common::AlignDown(offset, alignment); - const u32 adjust = offset - offset_aligned; - ASSERT(adjust % fmt_stride == 0); - push_data.AddOffset(binding.buffer, adjust / fmt_stride); - buffer_view = - vk_buffer->View(offset_aligned, vsharp.GetSize() + adjust, desc.is_written, - vsharp.GetDataFmt(), vsharp.GetNumberFmt()); - if (auto barrier = - vk_buffer->GetBarrier(desc.is_written ? vk::AccessFlagBits2::eShaderWrite - : vk::AccessFlagBits2::eShaderRead, - vk::PipelineStageFlagBits2::eComputeShader)) { - buffer_barriers.emplace_back(*barrier); - } - if (desc.is_written) { - texture_cache.InvalidateMemoryFromGPU(vsharp.base_address, vsharp.GetSize()); - } - } - - set_writes.push_back({ - .dstSet = VK_NULL_HANDLE, - .dstBinding = binding.unified++, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = desc.is_written ? vk::DescriptorType::eStorageTexelBuffer - : vk::DescriptorType::eUniformTexelBuffer, - .pTexelBufferView = &buffer_view, - }); - ++binding.buffer; - } -} - -void Pipeline::BindTextures(VideoCore::TextureCache& texture_cache, const Shader::Info& stage, - Shader::Backend::Bindings& binding, - DescriptorWrites& set_writes) const { - - using ImageBindingInfo = std::tuple; - static boost::container::static_vector image_bindings; - - image_bindings.clear(); - - for (const auto& image_desc : stage.images) { - const auto tsharp = image_desc.GetSharp(stage); - if (tsharp.GetDataFmt() != AmdGpu::DataFormat::FormatInvalid) { - VideoCore::ImageInfo image_info{tsharp, image_desc}; - const auto image_id = texture_cache.FindImage(image_info); - auto& image = texture_cache.GetImage(image_id); - image.flags |= VideoCore::ImageFlagBits::Bound; - image_bindings.emplace_back(image_id, tsharp, image_desc); - } else { - image_bindings.emplace_back(VideoCore::ImageId{}, tsharp, image_desc); - } - - if (texture_cache.IsMeta(tsharp.Address())) { - LOG_WARNING(Render_Vulkan, "Unexpected metadata read by a PS shader (texture)"); - } - } - - // Second pass to re-bind images that were updated after binding - for (auto [image_id, tsharp, desc] : image_bindings) { - if (!image_id) { - 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, - vk::ImageLayout::eGeneral); - } - } else { - auto& image = texture_cache.GetImage(image_id); - if (True(image.flags & VideoCore::ImageFlagBits::NeedsRebind)) { - image_id = texture_cache.FindImage(image.info); - } - VideoCore::ImageViewInfo view_info{tsharp, desc}; - auto& image_view = texture_cache.FindTexture(image_id, view_info); - image_infos.emplace_back(VK_NULL_HANDLE, *image_view.image_view, - texture_cache.GetImage(image_id).last_state.layout); - image.flags &= - ~(VideoCore::ImageFlagBits::NeedsRebind | VideoCore::ImageFlagBits::Bound); - } - - set_writes.push_back({ - .dstSet = VK_NULL_HANDLE, - .dstBinding = binding.unified++, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = desc.is_storage ? vk::DescriptorType::eStorageImage - : vk::DescriptorType::eSampledImage, - .pImageInfo = &image_infos.back(), - }); - } - - for (const auto& sampler : stage.samplers) { - auto ssharp = sampler.GetSharp(stage); - if (sampler.disable_aniso) { - const auto& tsharp = stage.images[sampler.associated_image].GetSharp(stage); - if (tsharp.base_level == 0 && tsharp.last_level == 0) { - ssharp.max_aniso.Assign(AmdGpu::AnisoRatio::One); - } - } - const auto vk_sampler = texture_cache.GetSampler(ssharp); - image_infos.emplace_back(vk_sampler, VK_NULL_HANDLE, vk::ImageLayout::eGeneral); - set_writes.push_back({ - .dstSet = VK_NULL_HANDLE, - .dstBinding = binding.unified++, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eSampler, - .pImageInfo = &image_infos.back(), - }); + const auto desc_set = desc_heap.Commit(*desc_layout); + for (auto& set_write : set_writes) { + set_write.dstSet = desc_set; } + instance.GetDevice().updateDescriptorSets(set_writes, {}); + cmdbuf.bindDescriptorSets(bind_point, *pipeline_layout, 0, desc_set, {}); } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_pipeline_common.h b/src/video_core/renderer_vulkan/vk_pipeline_common.h index 75764bfa6..8c48c83f7 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_common.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_common.h @@ -6,14 +6,18 @@ #include "shader_recompiler/backend/bindings.h" #include "shader_recompiler/info.h" #include "video_core/renderer_vulkan/vk_common.h" +#include "video_core/texture_cache/texture_cache.h" namespace VideoCore { class BufferCache; -class TextureCache; } // namespace VideoCore namespace Vulkan { +static constexpr auto gp_stage_flags = vk::ShaderStageFlagBits::eVertex | + vk::ShaderStageFlagBits::eGeometry | + vk::ShaderStageFlagBits::eFragment; + class Instance; class Scheduler; class DescriptorHeap; @@ -21,7 +25,7 @@ class DescriptorHeap; class Pipeline { public: Pipeline(const Instance& instance, Scheduler& scheduler, DescriptorHeap& desc_heap, - vk::PipelineCache pipeline_cache); + vk::PipelineCache pipeline_cache, bool is_compute = false); virtual ~Pipeline(); vk::Pipeline Handle() const noexcept { @@ -32,16 +36,27 @@ public: return *pipeline_layout; } + auto GetStages() const { + if (is_compute) { + return std::span{stages.cend() - 1, stages.cend()}; + } else { + return std::span{stages.cbegin(), stages.cend() - 1}; + } + } + + const Shader::Info& GetStage(Shader::Stage stage) const noexcept { + return *stages[u32(stage)]; + } + + bool IsCompute() const { + return is_compute; + } + using DescriptorWrites = boost::container::small_vector; using BufferBarriers = boost::container::small_vector; - void BindBuffers(VideoCore::BufferCache& buffer_cache, VideoCore::TextureCache& texture_cache, - const Shader::Info& stage, Shader::Backend::Bindings& binding, - Shader::PushData& push_data, DescriptorWrites& set_writes, - BufferBarriers& buffer_barriers) const; - - void BindTextures(VideoCore::TextureCache& texture_cache, const Shader::Info& stage, - Shader::Backend::Bindings& binding, DescriptorWrites& set_writes) const; + void BindResources(DescriptorWrites& set_writes, const BufferBarriers& buffer_barriers, + const Shader::PushData& push_data) const; protected: const Instance& instance; @@ -50,9 +65,9 @@ protected: vk::UniquePipeline pipeline; vk::UniquePipelineLayout pipeline_layout; vk::UniqueDescriptorSetLayout desc_layout; - static boost::container::static_vector image_infos; - static boost::container::static_vector buffer_views; - static boost::container::static_vector buffer_infos; + std::array stages{}; + bool uses_push_descriptors{}; + const bool is_compute; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_platform.cpp b/src/video_core/renderer_vulkan/vk_platform.cpp index 0eb7e0759..2e717397b 100644 --- a/src/video_core/renderer_vulkan/vk_platform.cpp +++ b/src/video_core/renderer_vulkan/vk_platform.cpp @@ -22,7 +22,7 @@ #include "video_core/renderer_vulkan/vk_platform.h" #if VULKAN_HPP_ENABLE_DYNAMIC_LOADER_TOOL -static vk::DynamicLoader dl; +static vk::detail::DynamicLoader dl; #else extern "C" { VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr(VkInstance instance, @@ -73,7 +73,7 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL DebugUtilsCallback( } vk::SurfaceKHR CreateSurface(vk::Instance instance, const Frontend::WindowSDL& emu_window) { - const auto& window_info = emu_window.getWindowInfo(); + const auto& window_info = emu_window.GetWindowInfo(); vk::SurfaceKHR surface{}; #if defined(VK_USE_PLATFORM_WIN32_KHR) diff --git a/src/video_core/renderer_vulkan/vk_presenter.cpp b/src/video_core/renderer_vulkan/vk_presenter.cpp new file mode 100644 index 000000000..b7d829316 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_presenter.cpp @@ -0,0 +1,786 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/config.h" +#include "common/debug.h" +#include "common/singleton.h" +#include "core/debug_state.h" +#include "core/devtools/layer.h" +#include "core/file_format/splash.h" +#include "core/libraries/system/systemservice.h" +#include "imgui/renderer/imgui_core.h" +#include "sdl_window.h" +#include "video_core/renderer_vulkan/vk_presenter.h" +#include "video_core/renderer_vulkan/vk_rasterizer.h" +#include "video_core/renderer_vulkan/vk_shader_util.h" +#include "video_core/texture_cache/image.h" + +#include "video_core/host_shaders/fs_tri_vert.h" +#include "video_core/host_shaders/post_process_frag.h" + +#include + +namespace Vulkan { + +bool CanBlitToSwapchain(const vk::PhysicalDevice physical_device, vk::Format format) { + const vk::FormatProperties props{physical_device.getFormatProperties(format)}; + return static_cast(props.optimalTilingFeatures & vk::FormatFeatureFlagBits::eBlitDst); +} + +[[nodiscard]] vk::ImageSubresourceLayers MakeImageSubresourceLayers() { + return vk::ImageSubresourceLayers{ + .aspectMask = vk::ImageAspectFlagBits::eColor, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }; +} + +[[nodiscard]] vk::ImageBlit MakeImageBlit(s32 frame_width, s32 frame_height, s32 dst_width, + s32 dst_height, s32 offset_x, s32 offset_y) { + return vk::ImageBlit{ + .srcSubresource = MakeImageSubresourceLayers(), + .srcOffsets = + std::array{ + vk::Offset3D{ + .x = 0, + .y = 0, + .z = 0, + }, + vk::Offset3D{ + .x = frame_width, + .y = frame_height, + .z = 1, + }, + }, + .dstSubresource = MakeImageSubresourceLayers(), + .dstOffsets = + std::array{ + vk::Offset3D{ + .x = offset_x, + .y = offset_y, + .z = 0, + }, + vk::Offset3D{ + .x = offset_x + dst_width, + .y = offset_y + dst_height, + .z = 1, + }, + }, + }; +} + +[[nodiscard]] vk::ImageBlit MakeImageBlitStretch(s32 frame_width, s32 frame_height, + s32 swapchain_width, s32 swapchain_height) { + return MakeImageBlit(frame_width, frame_height, swapchain_width, swapchain_height, 0, 0); +} + +static vk::Rect2D FitImage(s32 frame_width, s32 frame_height, s32 swapchain_width, + s32 swapchain_height) { + float frame_aspect = static_cast(frame_width) / frame_height; + float swapchain_aspect = static_cast(swapchain_width) / swapchain_height; + + u32 dst_width = swapchain_width; + u32 dst_height = swapchain_height; + + if (frame_aspect > swapchain_aspect) { + dst_height = static_cast(swapchain_width / frame_aspect); + } else { + dst_width = static_cast(swapchain_height * frame_aspect); + } + + const s32 offset_x = (swapchain_width - dst_width) / 2; + const s32 offset_y = (swapchain_height - dst_height) / 2; + + return vk::Rect2D{{offset_x, offset_y}, {dst_width, dst_height}}; +} + +[[nodiscard]] vk::ImageBlit MakeImageBlitFit(s32 frame_width, s32 frame_height, s32 swapchain_width, + s32 swapchain_height) { + const auto& dst_rect = FitImage(frame_width, frame_height, swapchain_width, swapchain_height); + + return MakeImageBlit(frame_width, frame_height, dst_rect.extent.width, dst_rect.extent.height, + dst_rect.offset.x, dst_rect.offset.y); +} + +static vk::Format FormatToUnorm(vk::Format fmt) { + switch (fmt) { + case vk::Format::eR8G8B8A8Srgb: + return vk::Format::eR8G8B8A8Unorm; + case vk::Format::eB8G8R8A8Srgb: + return vk::Format::eB8G8R8A8Unorm; + default: + UNREACHABLE(); + } +} + +void Presenter::CreatePostProcessPipeline() { + static const std::array pp_shaders{ + HostShaders::FS_TRI_VERT, + HostShaders::POST_PROCESS_FRAG, + }; + + boost::container::static_vector bindings{ + { + .binding = 0, + .descriptorType = vk::DescriptorType::eCombinedImageSampler, + .descriptorCount = 1, + .stageFlags = vk::ShaderStageFlagBits::eFragment, + }, + }; + + const vk::DescriptorSetLayoutCreateInfo desc_layout_ci = { + .flags = vk::DescriptorSetLayoutCreateFlagBits::ePushDescriptorKHR, + .bindingCount = static_cast(bindings.size()), + .pBindings = bindings.data(), + }; + auto desc_layout_result = instance.GetDevice().createDescriptorSetLayoutUnique(desc_layout_ci); + ASSERT_MSG(desc_layout_result.result == vk::Result::eSuccess, + "Failed to create descriptor set layout: {}", + vk::to_string(desc_layout_result.result)); + pp_desc_set_layout = std::move(desc_layout_result.value); + + const vk::PushConstantRange push_constants = { + .stageFlags = vk::ShaderStageFlagBits::eFragment, + .offset = 0, + .size = sizeof(PostProcessSettings), + }; + + const auto& vs_module = + Vulkan::Compile(pp_shaders[0], vk::ShaderStageFlagBits::eVertex, instance.GetDevice()); + ASSERT(vs_module); + Vulkan::SetObjectName(instance.GetDevice(), vs_module, "fs_tri.vert"); + + const auto& fs_module = + Vulkan::Compile(pp_shaders[1], vk::ShaderStageFlagBits::eFragment, instance.GetDevice()); + ASSERT(fs_module); + Vulkan::SetObjectName(instance.GetDevice(), vs_module, "post_process.frag"); + + const std::array shaders_ci{ + vk::PipelineShaderStageCreateInfo{ + .stage = vk::ShaderStageFlagBits::eVertex, + .module = vs_module, + .pName = "main", + }, + vk::PipelineShaderStageCreateInfo{ + .stage = vk::ShaderStageFlagBits::eFragment, + .module = fs_module, + .pName = "main", + }, + }; + + const vk::DescriptorSetLayout set_layout = *pp_desc_set_layout; + const vk::PipelineLayoutCreateInfo layout_info = { + .setLayoutCount = 1U, + .pSetLayouts = &set_layout, + .pushConstantRangeCount = 1, + .pPushConstantRanges = &push_constants, + }; + auto [layout_result, layout] = instance.GetDevice().createPipelineLayoutUnique(layout_info); + ASSERT_MSG(layout_result == vk::Result::eSuccess, "Failed to create pipeline layout: {}", + vk::to_string(layout_result)); + pp_pipeline_layout = std::move(layout); + + const std::array pp_color_formats{ + vk::Format::eB8G8R8A8Unorm, // swapchain.GetSurfaceFormat().format, + }; + const vk::PipelineRenderingCreateInfoKHR pipeline_rendering_ci = { + .colorAttachmentCount = 1u, + .pColorAttachmentFormats = pp_color_formats.data(), + }; + + const vk::PipelineVertexInputStateCreateInfo vertex_input_info = { + .vertexBindingDescriptionCount = 0u, + .vertexAttributeDescriptionCount = 0u, + }; + + const vk::PipelineInputAssemblyStateCreateInfo input_assembly = { + .topology = vk::PrimitiveTopology::eTriangleList, + }; + + const vk::Viewport viewport = { + .x = 0.0f, + .y = 0.0f, + .width = 1.0f, + .height = 1.0f, + .minDepth = 0.0f, + .maxDepth = 1.0f, + }; + + const vk::Rect2D scissor = { + .offset = {0, 0}, + .extent = {1, 1}, + }; + + const vk::PipelineViewportStateCreateInfo viewport_info = { + .viewportCount = 1, + .pViewports = &viewport, + .scissorCount = 1, + .pScissors = &scissor, + }; + + const vk::PipelineRasterizationStateCreateInfo raster_state = { + .depthClampEnable = false, + .rasterizerDiscardEnable = false, + .polygonMode = vk::PolygonMode::eFill, + .cullMode = vk::CullModeFlagBits::eBack, + .frontFace = vk::FrontFace::eClockwise, + .depthBiasEnable = false, + .lineWidth = 1.0f, + }; + + const vk::PipelineMultisampleStateCreateInfo multisampling = { + .rasterizationSamples = vk::SampleCountFlagBits::e1, + }; + + const std::array attachments{ + vk::PipelineColorBlendAttachmentState{ + .blendEnable = false, + .colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | + vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA, + }, + }; + + const vk::PipelineColorBlendStateCreateInfo color_blending = { + .logicOpEnable = false, + .logicOp = vk::LogicOp::eCopy, + .attachmentCount = attachments.size(), + .pAttachments = attachments.data(), + .blendConstants = std::array{1.0f, 1.0f, 1.0f, 1.0f}, + }; + + const std::array dynamic_states = { + vk::DynamicState::eViewport, + vk::DynamicState::eScissor, + }; + + const vk::PipelineDynamicStateCreateInfo dynamic_info = { + .dynamicStateCount = static_cast(dynamic_states.size()), + .pDynamicStates = dynamic_states.data(), + }; + + const vk::GraphicsPipelineCreateInfo pipeline_info = { + .pNext = &pipeline_rendering_ci, + .stageCount = static_cast(shaders_ci.size()), + .pStages = shaders_ci.data(), + .pVertexInputState = &vertex_input_info, + .pInputAssemblyState = &input_assembly, + .pViewportState = &viewport_info, + .pRasterizationState = &raster_state, + .pMultisampleState = &multisampling, + .pColorBlendState = &color_blending, + .pDynamicState = &dynamic_info, + .layout = *pp_pipeline_layout, + }; + + auto result = instance.GetDevice().createGraphicsPipelineUnique( + /*pipeline_cache*/ {}, pipeline_info); + if (result.result == vk::Result::eSuccess) { + pp_pipeline = std::move(result.value); + } else { + UNREACHABLE_MSG("Post process pipeline creation failed!"); + } + + // Once pipeline is compiled, we don't need the shader module anymore + instance.GetDevice().destroyShaderModule(vs_module); + instance.GetDevice().destroyShaderModule(fs_module); + + // Create sampler resource + const vk::SamplerCreateInfo sampler_ci = { + .magFilter = vk::Filter::eLinear, + .minFilter = vk::Filter::eLinear, + .mipmapMode = vk::SamplerMipmapMode::eNearest, + .addressModeU = vk::SamplerAddressMode::eClampToEdge, + .addressModeV = vk::SamplerAddressMode::eClampToEdge, + }; + auto [sampler_result, smplr] = instance.GetDevice().createSamplerUnique(sampler_ci); + ASSERT_MSG(sampler_result == vk::Result::eSuccess, "Failed to create sampler: {}", + vk::to_string(sampler_result)); + pp_sampler = std::move(smplr); +} + +Presenter::Presenter(Frontend::WindowSDL& window_, AmdGpu::Liverpool* liverpool_) + : window{window_}, liverpool{liverpool_}, + instance{window, Config::getGpuId(), Config::vkValidationEnabled(), + Config::vkCrashDiagnosticEnabled()}, + draw_scheduler{instance}, present_scheduler{instance}, flip_scheduler{instance}, + swapchain{instance, window}, + rasterizer{std::make_unique(instance, draw_scheduler, liverpool)}, + texture_cache{rasterizer->GetTextureCache()} { + const u32 num_images = swapchain.GetImageCount(); + const vk::Device device = instance.GetDevice(); + + // Create presentation frames. + present_frames.resize(num_images); + for (u32 i = 0; i < num_images; i++) { + Frame& frame = present_frames[i]; + auto [fence_result, fence] = + device.createFence({.flags = vk::FenceCreateFlagBits::eSignaled}); + ASSERT_MSG(fence_result == vk::Result::eSuccess, "Failed to create present done fence: {}", + vk::to_string(fence_result)); + frame.present_done = fence; + free_queue.push(&frame); + } + + CreatePostProcessPipeline(); + + // Setup ImGui + ImGui::Core::Initialize(instance, window, num_images, + FormatToUnorm(swapchain.GetSurfaceFormat().format)); + ImGui::Layer::AddLayer(Common::Singleton::Instance()); +} + +Presenter::~Presenter() { + ImGui::Layer::RemoveLayer(Common::Singleton::Instance()); + draw_scheduler.Finish(); + const vk::Device device = instance.GetDevice(); + for (auto& frame : present_frames) { + vmaDestroyImage(instance.GetAllocator(), frame.image, frame.allocation); + device.destroyImageView(frame.image_view); + device.destroyFence(frame.present_done); + } + ImGui::Core::Shutdown(device); +} + +void Presenter::RecreateFrame(Frame* frame, u32 width, u32 height) { + const vk::Device device = instance.GetDevice(); + if (frame->image_view) { + device.destroyImageView(frame->image_view); + } + if (frame->image) { + vmaDestroyImage(instance.GetAllocator(), frame->image, frame->allocation); + } + + const vk::Format format = swapchain.GetSurfaceFormat().format; + const vk::ImageCreateInfo image_info = { + .flags = vk::ImageCreateFlagBits::eMutableFormat, + .imageType = vk::ImageType::e2D, + .format = format, + .extent = {width, height, 1}, + .mipLevels = 1, + .arrayLayers = 1, + .samples = vk::SampleCountFlagBits::e1, + .usage = vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferDst | + vk::ImageUsageFlagBits::eTransferSrc, + }; + + const VmaAllocationCreateInfo alloc_info = { + .flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT, + .usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, + .requiredFlags = 0, + .preferredFlags = 0, + .pool = VK_NULL_HANDLE, + .pUserData = nullptr, + }; + + VkImage unsafe_image{}; + VkImageCreateInfo unsafe_image_info = static_cast(image_info); + + VkResult result = vmaCreateImage(instance.GetAllocator(), &unsafe_image_info, &alloc_info, + &unsafe_image, &frame->allocation, nullptr); + if (result != VK_SUCCESS) [[unlikely]] { + LOG_CRITICAL(Render_Vulkan, "Failed allocating texture with error {}", + vk::to_string(vk::Result{result})); + UNREACHABLE(); + } + frame->image = vk::Image{unsafe_image}; + + const vk::ImageViewCreateInfo view_info = { + .image = frame->image, + .viewType = vk::ImageViewType::e2D, + .format = FormatToUnorm(format), + .subresourceRange{ + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + auto [view_result, view] = device.createImageView(view_info); + ASSERT_MSG(view_result == vk::Result::eSuccess, "Failed to create frame image view: {}", + vk::to_string(view_result)); + frame->image_view = view; + frame->width = width; + frame->height = height; +} + +bool Presenter::ShowSplash(Frame* frame /*= nullptr*/) { + const auto* splash = Common::Singleton::Instance(); + if (splash->GetImageData().empty()) { + return false; + } + + if (!Libraries::SystemService::IsSplashVisible()) { + return false; + } + + draw_scheduler.EndRendering(); + const auto cmdbuf = draw_scheduler.CommandBuffer(); + + if (!frame) { + if (!splash_img.has_value()) { + VideoCore::ImageInfo info{}; + info.pixel_format = vk::Format::eR8G8B8A8Srgb; + info.type = vk::ImageType::e2D; + info.size = + VideoCore::Extent3D{splash->GetImageInfo().width, splash->GetImageInfo().height, 1}; + info.pitch = splash->GetImageInfo().width; + info.guest_address = VAddr(splash->GetImageData().data()); + info.guest_size_bytes = splash->GetImageData().size(); + info.mips_layout.emplace_back(splash->GetImageData().size(), + splash->GetImageInfo().width, + splash->GetImageInfo().height, 0); + splash_img.emplace(instance, present_scheduler, info); + texture_cache.RefreshImage(*splash_img); + + splash_img->Transit(vk::ImageLayout::eTransferSrcOptimal, + vk::AccessFlagBits2::eTransferRead, {}, cmdbuf); + } + + frame = GetRenderFrame(); + } + + const auto frame_subresources = vk::ImageSubresourceRange{ + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }; + + const auto pre_barrier = + vk::ImageMemoryBarrier2{.srcStageMask = vk::PipelineStageFlagBits2::eTransfer, + .srcAccessMask = vk::AccessFlagBits2::eTransferRead, + .dstStageMask = vk::PipelineStageFlagBits2::eTransfer, + .dstAccessMask = vk::AccessFlagBits2::eTransferWrite, + .oldLayout = vk::ImageLayout::eUndefined, + .newLayout = vk::ImageLayout::eTransferDstOptimal, + .image = frame->image, + .subresourceRange{frame_subresources}}; + + cmdbuf.pipelineBarrier2(vk::DependencyInfo{ + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &pre_barrier, + }); + + cmdbuf.blitImage(splash_img->image, vk::ImageLayout::eTransferSrcOptimal, frame->image, + vk::ImageLayout::eTransferDstOptimal, + MakeImageBlitFit(splash->GetImageInfo().width, splash->GetImageInfo().height, + frame->width, frame->height), + vk::Filter::eLinear); + + const auto post_barrier = + vk::ImageMemoryBarrier2{.srcStageMask = vk::PipelineStageFlagBits2::eTransfer, + .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, + .dstStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput, + .dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite, + .oldLayout = vk::ImageLayout::eTransferDstOptimal, + .newLayout = vk::ImageLayout::eGeneral, + .image = frame->image, + .subresourceRange{frame_subresources}}; + + cmdbuf.pipelineBarrier2(vk::DependencyInfo{ + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &post_barrier, + }); + + // Flush frame creation commands. + frame->ready_semaphore = draw_scheduler.GetMasterSemaphore()->Handle(); + frame->ready_tick = draw_scheduler.CurrentTick(); + SubmitInfo info{}; + draw_scheduler.Flush(info); + + Present(frame); + return true; +} + +Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) { + // Request a free presentation frame. + Frame* frame = GetRenderFrame(); + + // EOP flips are triggered from GPU thread so use the drawing scheduler to record + // commands. Otherwise we are dealing with a CPU flip which could have arrived + // from any guest thread. Use a separate scheduler for that. + auto& scheduler = is_eop ? draw_scheduler : flip_scheduler; + scheduler.EndRendering(); + const auto cmdbuf = scheduler.CommandBuffer(); + + const auto frame_subresources = vk::ImageSubresourceRange{ + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }; + + const auto pre_barrier = + vk::ImageMemoryBarrier2{.srcStageMask = vk::PipelineStageFlagBits2::eTransfer, + .srcAccessMask = vk::AccessFlagBits2::eTransferRead, + .dstStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput, + .dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite, + .oldLayout = vk::ImageLayout::eUndefined, + .newLayout = vk::ImageLayout::eColorAttachmentOptimal, + .image = frame->image, + .subresourceRange{frame_subresources}}; + + cmdbuf.pipelineBarrier2(vk::DependencyInfo{ + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &pre_barrier, + }); + + if (image_id != VideoCore::NULL_IMAGE_ID) { + auto& image = texture_cache.GetImage(image_id); + image.Transit(vk::ImageLayout::eShaderReadOnlyOptimal, vk::AccessFlagBits2::eShaderRead, {}, + cmdbuf); + + static vk::DescriptorImageInfo image_info{ + .sampler = *pp_sampler, + .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal, + }; + + VideoCore::ImageViewInfo info{}; + info.format = image.info.pixel_format; + if (auto view = image.FindView(info)) { + image_info.imageView = *texture_cache.GetImageView(view).image_view; + } else { + image_info.imageView = *texture_cache.RegisterImageView(image_id, info).image_view; + } + + static const std::array set_writes{ + vk::WriteDescriptorSet{ + .dstSet = VK_NULL_HANDLE, + .dstBinding = 0, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eCombinedImageSampler, + .pImageInfo = &image_info, + }, + }; + + cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, *pp_pipeline); + + const auto& dst_rect = + FitImage(image.info.size.width, image.info.size.height, frame->width, frame->height); + + const std::array viewports = { + vk::Viewport{ + .x = 1.0f * dst_rect.offset.x, + .y = 1.0f * dst_rect.offset.y, + .width = 1.0f * dst_rect.extent.width, + .height = 1.0f * dst_rect.extent.height, + .minDepth = 0.0f, + .maxDepth = 1.0f, + }, + }; + + cmdbuf.setViewport(0, viewports); + cmdbuf.setScissor(0, {dst_rect}); + + cmdbuf.pushDescriptorSetKHR(vk::PipelineBindPoint::eGraphics, *pp_pipeline_layout, 0, + set_writes); + cmdbuf.pushConstants(*pp_pipeline_layout, vk::ShaderStageFlagBits::eFragment, 0, + sizeof(PostProcessSettings), &pp_settings); + + const std::array attachments = {vk::RenderingAttachmentInfo{ + .imageView = frame->image_view, + .imageLayout = vk::ImageLayout::eColorAttachmentOptimal, + .loadOp = vk::AttachmentLoadOp::eClear, + .storeOp = vk::AttachmentStoreOp::eStore, + }}; + + vk::RenderingInfo rendering_info{ + .renderArea = + vk::Rect2D{ + .offset = {0, 0}, + .extent = {frame->width, frame->height}, + }, + .layerCount = 1, + .colorAttachmentCount = attachments.size(), + .pColorAttachments = attachments.data(), + }; + cmdbuf.beginRendering(rendering_info); + cmdbuf.draw(3, 1, 0, 0); + cmdbuf.endRendering(); + } + + const auto post_barrier = + vk::ImageMemoryBarrier2{.srcStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput, + .srcAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite, + .dstStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput, + .dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite, + .oldLayout = vk::ImageLayout::eColorAttachmentOptimal, + .newLayout = vk::ImageLayout::eGeneral, + .image = frame->image, + .subresourceRange{frame_subresources}}; + + cmdbuf.pipelineBarrier2(vk::DependencyInfo{ + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &post_barrier, + }); + + // Flush frame creation commands. + frame->ready_semaphore = scheduler.GetMasterSemaphore()->Handle(); + frame->ready_tick = scheduler.CurrentTick(); + SubmitInfo info{}; + scheduler.Flush(info); + return frame; +} + +void Presenter::Present(Frame* frame) { + // Recreate the swapchain if the window was resized. + if (window.GetWidth() != swapchain.GetExtent().width || + window.GetHeight() != swapchain.GetExtent().height) { + swapchain.Recreate(window.GetWidth(), window.GetHeight()); + } + + ImGui::Core::NewFrame(); + + swapchain.AcquireNextImage(); + + const vk::Image swapchain_image = swapchain.Image(); + + auto& scheduler = present_scheduler; + const auto cmdbuf = scheduler.CommandBuffer(); + + ImGui::Core::Render(cmdbuf, frame); + + { + auto* profiler_ctx = instance.GetProfilerContext(); + TracyVkNamedZoneC(profiler_ctx, renderer_gpu_zone, cmdbuf, "Host frame", + MarkersPalette::GpuMarkerColor, profiler_ctx != nullptr); + + const vk::Extent2D extent = swapchain.GetExtent(); + const std::array pre_barriers{ + vk::ImageMemoryBarrier{ + .srcAccessMask = vk::AccessFlagBits::eNone, + .dstAccessMask = vk::AccessFlagBits::eTransferWrite, + .oldLayout = vk::ImageLayout::eUndefined, + .newLayout = vk::ImageLayout::eTransferDstOptimal, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = swapchain_image, + .subresourceRange{ + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }, + vk::ImageMemoryBarrier{ + .srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite, + .dstAccessMask = vk::AccessFlagBits::eTransferRead, + .oldLayout = vk::ImageLayout::eGeneral, + .newLayout = vk::ImageLayout::eTransferSrcOptimal, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = frame->image, + .subresourceRange{ + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }, + }; + const vk::ImageMemoryBarrier post_barrier{ + .srcAccessMask = vk::AccessFlagBits::eTransferWrite, + .dstAccessMask = vk::AccessFlagBits::eMemoryRead, + .oldLayout = vk::ImageLayout::eTransferDstOptimal, + .newLayout = vk::ImageLayout::ePresentSrcKHR, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = swapchain_image, + .subresourceRange{ + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }; + + cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eColorAttachmentOutput, + vk::PipelineStageFlagBits::eTransfer, + vk::DependencyFlagBits::eByRegion, {}, {}, pre_barriers); + + cmdbuf.blitImage( + frame->image, vk::ImageLayout::eTransferSrcOptimal, swapchain_image, + vk::ImageLayout::eTransferDstOptimal, + MakeImageBlitStretch(frame->width, frame->height, extent.width, extent.height), + vk::Filter::eLinear); + + cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands, + vk::PipelineStageFlagBits::eAllCommands, + vk::DependencyFlagBits::eByRegion, {}, {}, post_barrier); + + if (profiler_ctx) { + TracyVkCollect(profiler_ctx, cmdbuf); + } + } + + // Flush vulkan commands. + SubmitInfo info{}; + info.AddWait(swapchain.GetImageAcquiredSemaphore()); + info.AddWait(frame->ready_semaphore, frame->ready_tick); + info.AddSignal(swapchain.GetPresentReadySemaphore()); + info.AddSignal(frame->present_done); + scheduler.Flush(info); + + // Present to swapchain. + std::scoped_lock submit_lock{Scheduler::submit_mutex}; + swapchain.Present(); + + // Free the frame for reuse + std::scoped_lock fl{free_mutex}; + free_queue.push(frame); + free_cv.notify_one(); + + DebugState.IncFlipFrameNum(); +} + +Frame* Presenter::GetRenderFrame() { + // Wait for free presentation frames + Frame* frame; + { + std::unique_lock lock{free_mutex}; + free_cv.wait(lock, [this] { return !free_queue.empty(); }); + LOG_DEBUG(Render_Vulkan, "Got render frame, remaining {}", free_queue.size() - 1); + + // Take the frame from the queue + frame = free_queue.front(); + free_queue.pop(); + } + + const vk::Device device = instance.GetDevice(); + vk::Result result{}; + + const auto wait = [&]() { + result = device.waitForFences(frame->present_done, false, std::numeric_limits::max()); + return result; + }; + + // Wait for the presentation to be finished so all frame resources are free + while (wait() != vk::Result::eSuccess) { + ASSERT_MSG(result != vk::Result::eErrorDeviceLost, + "Device lost during waiting for a frame"); + // Retry if the waiting times out + if (result == vk::Result::eTimeout) { + continue; + } + } + + // Reset fence for next queue submission. + device.resetFences(frame->present_done); + + // If the window dimensions changed, recreate this frame + if (frame->width != window.GetWidth() || frame->height != window.GetHeight()) { + RecreateFrame(frame, window.GetWidth(), window.GetHeight()); + } + + return frame; +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/vk_presenter.h similarity index 70% rename from src/video_core/renderer_vulkan/renderer_vulkan.h rename to src/video_core/renderer_vulkan/vk_presenter.h index a663622fc..4d9226dec 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/vk_presenter.h @@ -40,31 +40,39 @@ enum SchedulerType { class Rasterizer; -class RendererVulkan { +class Presenter { + struct PostProcessSettings { + float gamma = 1.0f; + }; + public: - explicit RendererVulkan(Frontend::WindowSDL& window, AmdGpu::Liverpool* liverpool); - ~RendererVulkan(); + Presenter(Frontend::WindowSDL& window, AmdGpu::Liverpool* liverpool); + ~Presenter(); + + float& GetGammaRef() { + return pp_settings.gamma; + } Frame* PrepareFrame(const Libraries::VideoOut::BufferAttributeGroup& attribute, VAddr cpu_address, bool is_eop) { - const auto info = VideoCore::ImageInfo{attribute, cpu_address}; - const auto image_id = texture_cache.FindImage(info); + 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); - auto& image = texture_cache.GetImage(image_id); - return PrepareFrameInternal(image, is_eop); + return PrepareFrameInternal(image_id, is_eop); } Frame* PrepareBlankFrame(bool is_eop) { - auto& image = texture_cache.GetImage(VideoCore::NULL_IMAGE_ID); - return PrepareFrameInternal(image, is_eop); + return PrepareFrameInternal(VideoCore::NULL_IMAGE_ID, is_eop); } VideoCore::Image& RegisterVideoOutSurface( const Libraries::VideoOut::BufferAttributeGroup& attribute, VAddr cpu_address) { vo_buffers_addr.emplace_back(cpu_address); - const auto info = VideoCore::ImageInfo{attribute, cpu_address}; - const auto image_id = texture_cache.FindImage(info); - return texture_cache.GetImage(image_id); + auto desc = VideoCore::TextureCache::VideoOutDesc{attribute, cpu_address}; + const auto image_id = texture_cache.FindImage(desc); + auto& image = texture_cache.GetImage(image_id); + image.usage.vo_surface = 1u; + return image; } bool IsVideoOutSurface(const AmdGpu::Liverpool::ColorBuffer& color_buffer) { @@ -83,10 +91,16 @@ public: } private: - Frame* PrepareFrameInternal(VideoCore::Image& image, bool is_eop = true); + void CreatePostProcessPipeline(); + Frame* PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop = true); Frame* GetRenderFrame(); private: + PostProcessSettings pp_settings{}; + vk::UniquePipeline pp_pipeline{}; + vk::UniquePipelineLayout pp_pipeline_layout{}; + vk::UniqueDescriptorSetLayout pp_desc_set_layout{}; + vk::UniqueSampler pp_sampler{}; Frontend::WindowSDL& window; AmdGpu::Liverpool* liverpool; Instance instance; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 6d214a18e..0471fdb0a 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -75,6 +75,118 @@ bool Rasterizer::FilterDraw() { return true; } +RenderState Rasterizer::PrepareRenderState(u32 mrt_mask) { + // Prefetch color and depth buffers to let texture cache handle possible overlaps with bound + // textures (e.g. mipgen) + RenderState state; + + cb_descs.clear(); + db_desc.reset(); + + const auto& regs = liverpool->regs; + + if (regs.color_control.degamma_enable) { + LOG_WARNING(Render_Vulkan, "Color buffers require gamma correction"); + } + + for (auto col_buf_id = 0u; col_buf_id < Liverpool::NumColorBuffers; ++col_buf_id) { + const auto& col_buf = regs.color_buffers[col_buf_id]; + if (!col_buf) { + continue; + } + + // Skip stale color buffers if shader doesn't output to them. Otherwise it will perform + // an unnecessary transition and may result in state conflict if the resource is already + // bound for reading. + if ((mrt_mask & (1 << col_buf_id)) == 0) { + continue; + } + + const auto& hint = liverpool->last_cb_extent[col_buf_id]; + auto& [image_id, desc] = cb_descs.emplace_back(std::piecewise_construct, std::tuple{}, + std::tuple{col_buf, hint}); + const auto& image_view = texture_cache.FindRenderTarget(desc); + image_id = bound_images.emplace_back(image_view.image_id); + auto& image = texture_cache.GetImage(image_id); + image.binding.is_target = 1u; + + const auto slice = image_view.info.range.base.layer; + const bool is_clear = texture_cache.IsMetaCleared(col_buf.CmaskAddress(), slice); + texture_cache.TouchMeta(col_buf.CmaskAddress(), slice, false); + + const auto mip = image_view.info.range.base.level; + state.width = std::min(state.width, std::max(image.info.size.width >> mip, 1u)); + state.height = std::min(state.height, std::max(image.info.size.height >> mip, 1u)); + state.color_images[state.num_color_attachments] = image.image; + state.color_attachments[state.num_color_attachments++] = { + .imageView = *image_view.image_view, + .imageLayout = vk::ImageLayout::eUndefined, + .loadOp = is_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, + .storeOp = vk::AttachmentStoreOp::eStore, + .clearValue = + is_clear ? LiverpoolToVK::ColorBufferClearValue(col_buf) : vk::ClearValue{}, + }; + } + + using ZFormat = AmdGpu::Liverpool::DepthBuffer::ZFormat; + using StencilFormat = AmdGpu::Liverpool::DepthBuffer::StencilFormat; + if (regs.depth_buffer.Address() != 0 && + ((regs.depth_control.depth_enable && regs.depth_buffer.z_info.format != ZFormat::Invalid) || + (regs.depth_control.stencil_enable && + regs.depth_buffer.stencil_info.format != StencilFormat::Invalid))) { + const auto htile_address = regs.depth_htile_data_base.GetAddress(); + const auto& hint = liverpool->last_db_extent; + auto& [image_id, desc] = + db_desc.emplace(std::piecewise_construct, std::tuple{}, + std::tuple{regs.depth_buffer, regs.depth_view, regs.depth_control, + htile_address, hint}); + const auto& image_view = texture_cache.FindDepthTarget(desc); + image_id = bound_images.emplace_back(image_view.image_id); + auto& image = texture_cache.GetImage(image_id); + image.binding.is_target = 1u; + + const auto slice = image_view.info.range.base.layer; + const bool is_clear = regs.depth_render_control.depth_clear_enable || + texture_cache.IsMetaCleared(htile_address, slice); + ASSERT(desc.view_info.range.extent.layers == 1); + + state.width = std::min(state.width, image.info.size.width); + state.height = std::min(state.height, image.info.size.height); + state.depth_image = image.image; + state.depth_attachment = { + .imageView = *image_view.image_view, + .imageLayout = vk::ImageLayout::eUndefined, + .loadOp = is_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, + .storeOp = vk::AttachmentStoreOp::eStore, + .clearValue = vk::ClearValue{.depthStencil = {.depth = regs.depth_clear, + .stencil = regs.stencil_clear}}, + }; + texture_cache.TouchMeta(htile_address, slice, false); + state.has_depth = + regs.depth_buffer.z_info.format != AmdGpu::Liverpool::DepthBuffer::ZFormat::Invalid; + state.has_stencil = regs.depth_buffer.stencil_info.format != + AmdGpu::Liverpool::DepthBuffer::StencilFormat::Invalid; + } + + return state; +} + +[[nodiscard]] std::pair GetDrawOffsets( + const AmdGpu::Liverpool::Regs& regs, const Shader::Info& info, + const std::optional& fetch_shader) { + u32 vertex_offset = regs.index_offset; + u32 instance_offset = 0; + if (fetch_shader) { + if (vertex_offset == 0 && fetch_shader->vertex_offset_sgpr != -1) { + vertex_offset = info.user_data[fetch_shader->vertex_offset_sgpr]; + } + if (fetch_shader->instance_offset_sgpr != -1) { + instance_offset = info.user_data[fetch_shader->instance_offset_sgpr]; + } + } + return {vertex_offset, instance_offset}; +} + void Rasterizer::Draw(bool is_indexed, u32 index_offset) { RENDERER_TRACE; @@ -82,27 +194,30 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) { return; } - const auto cmdbuf = scheduler.CommandBuffer(); const auto& regs = liverpool->regs; const GraphicsPipeline* pipeline = pipeline_cache.GetGraphicsPipeline(); if (!pipeline) { return; } - try { - pipeline->BindResources(regs, buffer_cache, texture_cache); - } catch (...) { - UNREACHABLE(); + auto state = PrepareRenderState(pipeline->GetMrtMask()); + + if (!BindResources(pipeline)) { + return; } const auto& vs_info = pipeline->GetStage(Shader::Stage::Vertex); - buffer_cache.BindVertexBuffers(vs_info); + const auto& fetch_shader = pipeline->GetFetchShader(); + buffer_cache.BindVertexBuffers(vs_info, fetch_shader); const u32 num_indices = buffer_cache.BindIndexBuffer(is_indexed, index_offset); - BeginRendering(*pipeline); + BeginRendering(*pipeline, state); UpdateDynamicState(*pipeline); - const auto [vertex_offset, instance_offset] = vs_info.GetDrawOffsets(); + const auto [vertex_offset, instance_offset] = GetDrawOffsets(regs, vs_info, fetch_shader); + + const auto cmdbuf = scheduler.CommandBuffer(); + cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline->Handle()); if (is_indexed) { cmdbuf.drawIndexed(num_indices, regs.num_instances.NumInstances(), 0, s32(vertex_offset), @@ -113,97 +228,131 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) { cmdbuf.draw(num_vertices, regs.num_instances.NumInstances(), vertex_offset, instance_offset); } + + ResetBindings(); } -void Rasterizer::DrawIndirect(bool is_indexed, VAddr address, u32 offset, u32 size) { +void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u32 stride, + u32 max_count, VAddr count_address) { RENDERER_TRACE; if (!FilterDraw()) { return; } - const auto cmdbuf = scheduler.CommandBuffer(); const auto& regs = liverpool->regs; - const GraphicsPipeline* pipeline = pipeline_cache.GetGraphicsPipeline(); - if (!pipeline) { + if (regs.primitive_type == AmdGpu::PrimitiveType::QuadList) { + // For QuadList we use generated index buffer to convert quads to triangles. Since it + // changes type of the draw, arguments are not valid for this case. We need to run a + // conversion pass to repack the indirect arguments buffer first. + LOG_WARNING(Render_Vulkan, "QuadList primitive type is not supported for indirect draw"); return; } ASSERT_MSG(regs.primitive_type != AmdGpu::PrimitiveType::RectList, "Unsupported primitive type for indirect draw"); - try { - pipeline->BindResources(regs, buffer_cache, texture_cache); - } catch (...) { - UNREACHABLE(); + const GraphicsPipeline* pipeline = pipeline_cache.GetGraphicsPipeline(); + if (!pipeline) { + return; + } + + auto state = PrepareRenderState(pipeline->GetMrtMask()); + + if (!BindResources(pipeline)) { + return; } const auto& vs_info = pipeline->GetStage(Shader::Stage::Vertex); - buffer_cache.BindVertexBuffers(vs_info); + const auto& fetch_shader = pipeline->GetFetchShader(); + buffer_cache.BindVertexBuffers(vs_info, fetch_shader); buffer_cache.BindIndexBuffer(is_indexed, 0); - const auto [buffer, base] = buffer_cache.ObtainBuffer(address + offset, size, false); + const auto& [buffer, base] = + buffer_cache.ObtainBuffer(arg_address + offset, stride * max_count, false); - BeginRendering(*pipeline); + VideoCore::Buffer* count_buffer{}; + u32 count_base{}; + if (count_address != 0) { + std::tie(count_buffer, count_base) = buffer_cache.ObtainBuffer(count_address, 4, false); + } + + BeginRendering(*pipeline, state); UpdateDynamicState(*pipeline); // We can safely ignore both SGPR UD indices and results of fetch shader parsing, as vertex and // instance offsets will be automatically applied by Vulkan from indirect args buffer. + const auto cmdbuf = scheduler.CommandBuffer(); + cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline->Handle()); + if (is_indexed) { - cmdbuf.drawIndexedIndirect(buffer->Handle(), base, 1, 0); + ASSERT(sizeof(VkDrawIndexedIndirectCommand) == stride); + + if (count_address != 0) { + cmdbuf.drawIndexedIndirectCount(buffer->Handle(), base, count_buffer->Handle(), + count_base, max_count, stride); + } else { + cmdbuf.drawIndexedIndirect(buffer->Handle(), base, max_count, stride); + } } else { - cmdbuf.drawIndirect(buffer->Handle(), base, 1, 0); + ASSERT(sizeof(VkDrawIndirectCommand) == stride); + + if (count_address != 0) { + cmdbuf.drawIndirectCount(buffer->Handle(), base, count_buffer->Handle(), count_base, + max_count, stride); + } else { + cmdbuf.drawIndirect(buffer->Handle(), base, max_count, stride); + } } + + ResetBindings(); } void Rasterizer::DispatchDirect() { RENDERER_TRACE; - const auto cmdbuf = scheduler.CommandBuffer(); const auto& cs_program = liverpool->regs.cs_program; const ComputePipeline* pipeline = pipeline_cache.GetComputePipeline(); if (!pipeline) { return; } - try { - const auto has_resources = pipeline->BindResources(buffer_cache, texture_cache); - if (!has_resources) { - return; - } - } catch (...) { - UNREACHABLE(); + if (!BindResources(pipeline)) { + return; } scheduler.EndRendering(); + + const auto cmdbuf = scheduler.CommandBuffer(); cmdbuf.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline->Handle()); cmdbuf.dispatch(cs_program.dim_x, cs_program.dim_y, cs_program.dim_z); + + ResetBindings(); } void Rasterizer::DispatchIndirect(VAddr address, u32 offset, u32 size) { RENDERER_TRACE; - const auto cmdbuf = scheduler.CommandBuffer(); const auto& cs_program = liverpool->regs.cs_program; const ComputePipeline* pipeline = pipeline_cache.GetComputePipeline(); if (!pipeline) { return; } - try { - const auto has_resources = pipeline->BindResources(buffer_cache, texture_cache); - if (!has_resources) { - return; - } - } catch (...) { - UNREACHABLE(); + if (!BindResources(pipeline)) { + return; } scheduler.EndRendering(); - cmdbuf.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline->Handle()); + const auto [buffer, base] = buffer_cache.ObtainBuffer(address + offset, size, false); + + const auto cmdbuf = scheduler.CommandBuffer(); + cmdbuf.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline->Handle()); cmdbuf.dispatchIndirect(buffer->Handle(), base); + + ResetBindings(); } u64 Rasterizer::Flush() { @@ -217,86 +366,406 @@ void Rasterizer::Finish() { scheduler.Finish(); } -void Rasterizer::BeginRendering(const GraphicsPipeline& pipeline) { +bool Rasterizer::BindResources(const Pipeline* pipeline) { + buffer_infos.clear(); + buffer_views.clear(); + image_infos.clear(); + const auto& regs = liverpool->regs; - RenderState state; - if (regs.color_control.degamma_enable) { - LOG_WARNING(Render_Vulkan, "Color buffers require gamma correction"); - } + if (pipeline->IsCompute()) { + const auto& info = pipeline->GetStage(Shader::Stage::Compute); - for (auto col_buf_id = 0u; col_buf_id < Liverpool::NumColorBuffers; ++col_buf_id) { - const auto& col_buf = regs.color_buffers[col_buf_id]; - if (!col_buf) { - continue; - } - - // If the color buffer is still bound but rendering to it is disabled by the target mask, - // we need to prevent the render area from being affected by unbound render target extents. - if (!regs.color_target_mask.GetMask(col_buf_id)) { - continue; - } - - // Skip stale color buffers if shader doesn't output to them. Otherwise it will perform - // an unnecessary transition and may result in state conflict if the resource is already - // bound for reading. - if ((pipeline.GetMrtMask() & (1 << col_buf_id)) == 0) { - continue; - } - - const auto& hint = liverpool->last_cb_extent[col_buf_id]; - VideoCore::ImageInfo image_info{col_buf, hint}; - VideoCore::ImageViewInfo view_info{col_buf, false /*!!image.info.usage.vo_buffer*/}; - const auto& image_view = texture_cache.FindRenderTarget(image_info, view_info); - const auto& image = texture_cache.GetImage(image_view.image_id); - state.width = std::min(state.width, image.info.size.width); - state.height = std::min(state.height, image.info.size.height); - - const bool is_clear = texture_cache.IsMetaCleared(col_buf.CmaskAddress()); - state.color_images[state.num_color_attachments] = image.image; - state.color_attachments[state.num_color_attachments++] = { - .imageView = *image_view.image_view, - .imageLayout = vk::ImageLayout::eColorAttachmentOptimal, - .loadOp = is_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, - .storeOp = vk::AttachmentStoreOp::eStore, - .clearValue = - is_clear ? LiverpoolToVK::ColorBufferClearValue(col_buf) : vk::ClearValue{}, + // Most of the time when a metadata is updated with a shader it gets cleared. It means + // we can skip the whole dispatch and update the tracked state instead. Also, it is not + // intended to be consumed and in such rare cases (e.g. HTile introspection, CRAA) we + // will need its full emulation anyways. For cases of metadata read a warning will be + // logged. + const auto IsMetaUpdate = [&](const auto& desc) { + const auto sharp = desc.GetSharp(info); + const VAddr address = sharp.base_address; + if (desc.is_written) { + // Assume all slices were updates + if (texture_cache.ClearMeta(address)) { + LOG_TRACE(Render_Vulkan, "Metadata update skipped"); + return true; + } + } else { + if (texture_cache.IsMeta(address)) { + LOG_WARNING(Render_Vulkan, "Unexpected metadata read by a CS shader (buffer)"); + } + } + return false; }; - texture_cache.TouchMeta(col_buf.CmaskAddress(), false); + + // Assume if a shader reads and writes metas at the same time, it is a copy shader. + bool meta_read = false; + for (const auto& desc : info.buffers) { + if (desc.is_gds_buffer) { + continue; + } + if (!desc.is_written) { + const VAddr address = desc.GetSharp(info).base_address; + meta_read = texture_cache.IsMeta(address); + } + } + + for (const auto& desc : info.texture_buffers) { + if (!desc.is_written) { + const VAddr address = desc.GetSharp(info).base_address; + meta_read = texture_cache.IsMeta(address); + } + } + + if (!meta_read) { + for (const auto& desc : info.buffers) { + if (IsMetaUpdate(desc)) { + return false; + } + } + + for (const auto& desc : info.texture_buffers) { + if (IsMetaUpdate(desc)) { + return false; + } + } + } } - using ZFormat = AmdGpu::Liverpool::DepthBuffer::ZFormat; - using StencilFormat = AmdGpu::Liverpool::DepthBuffer::StencilFormat; - if (regs.depth_buffer.Address() != 0 && - ((regs.depth_control.depth_enable && regs.depth_buffer.z_info.format != ZFormat::Invalid) || - (regs.depth_control.stencil_enable && - regs.depth_buffer.stencil_info.format != StencilFormat::Invalid))) { - const auto htile_address = regs.depth_htile_data_base.GetAddress(); - const bool is_clear = regs.depth_render_control.depth_clear_enable || - texture_cache.IsMetaCleared(htile_address); - const auto& hint = liverpool->last_db_extent; - VideoCore::ImageInfo image_info{regs.depth_buffer, regs.depth_view.NumSlices(), - htile_address, hint}; - VideoCore::ImageViewInfo view_info{regs.depth_buffer, regs.depth_view, regs.depth_control}; - const auto& image_view = texture_cache.FindDepthTarget(image_info, view_info); - const auto& image = texture_cache.GetImage(image_view.image_id); - state.width = std::min(state.width, image.info.size.width); - state.height = std::min(state.height, image.info.size.height); - state.depth_image = image.image; - state.depth_attachment = { - .imageView = *image_view.image_view, - .imageLayout = image.last_state.layout, - .loadOp = is_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, - .storeOp = is_clear ? vk::AttachmentStoreOp::eNone : vk::AttachmentStoreOp::eStore, - .clearValue = vk::ClearValue{.depthStencil = {.depth = regs.depth_clear, - .stencil = regs.stencil_clear}}, - }; - texture_cache.TouchMeta(htile_address, false); - state.has_depth = - regs.depth_buffer.z_info.format != AmdGpu::Liverpool::DepthBuffer::ZFormat::Invalid; - state.has_stencil = regs.depth_buffer.stencil_info.format != - AmdGpu::Liverpool::DepthBuffer::StencilFormat::Invalid; + set_writes.clear(); + buffer_barriers.clear(); + + // Bind resource buffers and textures. + Shader::PushData push_data{}; + Shader::Backend::Bindings binding{}; + + for (const auto* stage : pipeline->GetStages()) { + if (!stage) { + continue; + } + push_data.step0 = regs.vgt_instance_step_rate_0; + push_data.step1 = regs.vgt_instance_step_rate_1; + stage->PushUd(binding, push_data); + + BindBuffers(*stage, binding, push_data, set_writes, buffer_barriers); + BindTextures(*stage, binding, set_writes); } + + pipeline->BindResources(set_writes, buffer_barriers, push_data); + + return true; +} + +void Rasterizer::BindBuffers(const Shader::Info& stage, Shader::Backend::Bindings& binding, + Shader::PushData& push_data, Pipeline::DescriptorWrites& set_writes, + Pipeline::BufferBarriers& buffer_barriers) { + buffer_bindings.clear(); + + for (const auto& desc : stage.buffers) { + const auto vsharp = desc.GetSharp(stage); + if (!desc.is_gds_buffer && vsharp.base_address != 0 && vsharp.GetSize() > 0) { + const auto buffer_id = buffer_cache.FindBuffer(vsharp.base_address, vsharp.GetSize()); + buffer_bindings.emplace_back(buffer_id, vsharp); + } else { + buffer_bindings.emplace_back(VideoCore::BufferId{}, vsharp); + } + } + + texbuffer_bindings.clear(); + + for (const auto& desc : stage.texture_buffers) { + const auto vsharp = desc.GetSharp(stage); + if (vsharp.base_address != 0 && vsharp.GetSize() > 0 && + vsharp.GetDataFmt() != AmdGpu::DataFormat::FormatInvalid) { + const auto buffer_id = buffer_cache.FindBuffer(vsharp.base_address, vsharp.GetSize()); + texbuffer_bindings.emplace_back(buffer_id, vsharp); + } else { + texbuffer_bindings.emplace_back(VideoCore::BufferId{}, vsharp); + } + } + + // Bind the flattened user data buffer as a UBO so it's accessible to the shader + if (stage.has_readconst) { + const auto [vk_buffer, offset] = buffer_cache.ObtainHostUBO(stage.flattened_ud_buf); + buffer_infos.emplace_back(vk_buffer->Handle(), offset, + stage.flattened_ud_buf.size() * sizeof(u32)); + set_writes.push_back({ + .dstSet = VK_NULL_HANDLE, + .dstBinding = binding.unified++, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eUniformBuffer, + .pBufferInfo = &buffer_infos.back(), + }); + ++binding.buffer; + } + + // Second pass to re-bind buffers that were updated after binding + for (u32 i = 0; i < buffer_bindings.size(); i++) { + const auto& [buffer_id, vsharp] = buffer_bindings[i]; + const auto& desc = stage.buffers[i]; + const bool is_storage = desc.IsStorage(vsharp); + if (!buffer_id) { + if (desc.is_gds_buffer) { + const auto* gds_buf = buffer_cache.GetGdsBuffer(); + buffer_infos.emplace_back(gds_buf->Handle(), 0, gds_buf->SizeBytes()); + } else if (instance.IsNullDescriptorSupported()) { + buffer_infos.emplace_back(VK_NULL_HANDLE, 0, VK_WHOLE_SIZE); + } else { + auto& null_buffer = buffer_cache.GetBuffer(VideoCore::NULL_BUFFER_ID); + buffer_infos.emplace_back(null_buffer.Handle(), 0, VK_WHOLE_SIZE); + } + } else { + const auto [vk_buffer, offset] = buffer_cache.ObtainBuffer( + vsharp.base_address, vsharp.GetSize(), desc.is_written, false, buffer_id); + const u32 alignment = + is_storage ? instance.StorageMinAlignment() : instance.UniformMinAlignment(); + const u32 offset_aligned = Common::AlignDown(offset, alignment); + const u32 adjust = offset - offset_aligned; + ASSERT(adjust % 4 == 0); + push_data.AddOffset(binding.buffer, adjust); + buffer_infos.emplace_back(vk_buffer->Handle(), offset_aligned, + vsharp.GetSize() + adjust); + } + + set_writes.push_back({ + .dstSet = VK_NULL_HANDLE, + .dstBinding = binding.unified++, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = is_storage ? vk::DescriptorType::eStorageBuffer + : vk::DescriptorType::eUniformBuffer, + .pBufferInfo = &buffer_infos.back(), + }); + ++binding.buffer; + } + + const auto null_buffer_view = + instance.IsNullDescriptorSupported() ? VK_NULL_HANDLE : buffer_cache.NullBufferView(); + for (u32 i = 0; i < texbuffer_bindings.size(); i++) { + const auto& [buffer_id, vsharp] = texbuffer_bindings[i]; + const auto& desc = stage.texture_buffers[i]; + vk::BufferView& buffer_view = buffer_views.emplace_back(null_buffer_view); + if (buffer_id) { + const u32 alignment = instance.TexelBufferMinAlignment(); + const auto [vk_buffer, offset] = buffer_cache.ObtainBuffer( + vsharp.base_address, vsharp.GetSize(), desc.is_written, true, buffer_id); + const u32 fmt_stride = AmdGpu::NumBits(vsharp.GetDataFmt()) >> 3; + const u32 buf_stride = vsharp.GetStride(); + ASSERT_MSG(buf_stride % fmt_stride == 0, + "Texel buffer stride must match format stride"); + const u32 offset_aligned = Common::AlignDown(offset, alignment); + const u32 adjust = offset - offset_aligned; + ASSERT(adjust % fmt_stride == 0); + push_data.AddTexelOffset(binding.buffer, buf_stride / fmt_stride, adjust / fmt_stride); + buffer_view = + vk_buffer->View(offset_aligned, vsharp.GetSize() + adjust, desc.is_written, + vsharp.GetDataFmt(), vsharp.GetNumberFmt()); + if (auto barrier = + vk_buffer->GetBarrier(desc.is_written ? vk::AccessFlagBits2::eShaderWrite + : vk::AccessFlagBits2::eShaderRead, + vk::PipelineStageFlagBits2::eComputeShader)) { + buffer_barriers.emplace_back(*barrier); + } + if (desc.is_written) { + texture_cache.InvalidateMemoryFromGPU(vsharp.base_address, vsharp.GetSize()); + } + } + + set_writes.push_back({ + .dstSet = VK_NULL_HANDLE, + .dstBinding = binding.unified++, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = desc.is_written ? vk::DescriptorType::eStorageTexelBuffer + : vk::DescriptorType::eUniformTexelBuffer, + .pTexelBufferView = &buffer_view, + }); + ++binding.buffer; + } +} + +void Rasterizer::BindTextures(const Shader::Info& stage, Shader::Backend::Bindings& binding, + Pipeline::DescriptorWrites& set_writes) { + image_bindings.clear(); + + for (const auto& image_desc : stage.images) { + const auto tsharp = image_desc.GetSharp(stage); + if (texture_cache.IsMeta(tsharp.Address())) { + LOG_WARNING(Render_Vulkan, "Unexpected metadata read by a shader (texture)"); + } + + if (tsharp.GetDataFmt() == AmdGpu::DataFormat::FormatInvalid) { + image_bindings.emplace_back(std::piecewise_construct, std::tuple{}, std::tuple{}); + continue; + } + + auto& [image_id, desc] = image_bindings.emplace_back(std::piecewise_construct, std::tuple{}, + std::tuple{tsharp, image_desc}); + image_id = texture_cache.FindImage(desc); + auto& image = texture_cache.GetImage(image_id); + if (image.binding.is_bound) { + // The image is already bound. In case if it is about to be used as storage we need + // to force general layout on it. + image.binding.force_general |= image_desc.is_storage; + } + if (image.binding.is_target) { + // The image is already bound as target. Since we read and output to it need to force + // general layout too. + image.binding.force_general = 1u; + } + image.binding.is_bound = 1u; + } + + // Second pass to re-bind images that were updated after binding + for (auto& [image_id, desc] : image_bindings) { + bool is_storage = desc.type == VideoCore::TextureCache::BindingType::Storage; + if (!image_id) { + 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, + vk::ImageLayout::eGeneral); + } + } else { + if (auto& old_image = texture_cache.GetImage(image_id); + old_image.binding.needs_rebind) { + old_image.binding.Reset(); // clean up previous image binding state + image_id = texture_cache.FindImage(desc); + } + + bound_images.emplace_back(image_id); + + auto& image = texture_cache.GetImage(image_id); + auto& image_view = texture_cache.FindTexture(image_id, desc.view_info); + + if (image.binding.force_general || image.binding.is_target) { + image.Transit(vk::ImageLayout::eGeneral, + vk::AccessFlagBits2::eShaderRead | + (image.info.IsDepthStencil() + ? vk::AccessFlagBits2::eDepthStencilAttachmentWrite + : vk::AccessFlagBits2::eColorAttachmentWrite), + {}); + } else { + if (is_storage) { + image.Transit(vk::ImageLayout::eGeneral, + vk::AccessFlagBits2::eShaderRead | + vk::AccessFlagBits2::eShaderWrite, + desc.view_info.range); + } else { + const auto new_layout = image.info.IsDepthStencil() + ? vk::ImageLayout::eDepthStencilReadOnlyOptimal + : vk::ImageLayout::eShaderReadOnlyOptimal; + image.Transit(new_layout, vk::AccessFlagBits2::eShaderRead, + desc.view_info.range); + } + } + image.usage.storage |= is_storage; + image.usage.texture |= !is_storage; + + image_infos.emplace_back(VK_NULL_HANDLE, *image_view.image_view, + image.last_state.layout); + } + + set_writes.push_back({ + .dstSet = VK_NULL_HANDLE, + .dstBinding = binding.unified++, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = + is_storage ? vk::DescriptorType::eStorageImage : vk::DescriptorType::eSampledImage, + .pImageInfo = &image_infos.back(), + }); + } + + for (const auto& sampler : stage.samplers) { + auto ssharp = sampler.GetSharp(stage); + if (sampler.disable_aniso) { + const auto& tsharp = stage.images[sampler.associated_image].GetSharp(stage); + if (tsharp.base_level == 0 && tsharp.last_level == 0) { + ssharp.max_aniso.Assign(AmdGpu::AnisoRatio::One); + } + } + const auto vk_sampler = texture_cache.GetSampler(ssharp); + image_infos.emplace_back(vk_sampler, VK_NULL_HANDLE, vk::ImageLayout::eGeneral); + set_writes.push_back({ + .dstSet = VK_NULL_HANDLE, + .dstBinding = binding.unified++, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eSampler, + .pImageInfo = &image_infos.back(), + }); + } +} + +void Rasterizer::BeginRendering(const GraphicsPipeline& pipeline, RenderState& state) { + int cb_index = 0; + for (auto& [image_id, desc] : cb_descs) { + 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_images[cb_index] = image.image; + + const auto mip = view.info.range.base.level; + state.width = std::min(state.width, std::max(image.info.size.width >> mip, 1u)); + state.height = std::min(state.height, std::max(image.info.size.height >> mip, 1u)); + ASSERT(old_img.info.size.width == state.width); + ASSERT(old_img.info.size.height == state.height); + } + auto& image = texture_cache.GetImage(image_id); + if (image.binding.force_general) { + image.Transit( + vk::ImageLayout::eGeneral, + vk::AccessFlagBits2::eColorAttachmentWrite | vk::AccessFlagBits2::eShaderRead, {}); + + } else { + image.Transit(vk::ImageLayout::eColorAttachmentOptimal, + vk::AccessFlagBits2::eColorAttachmentWrite | + vk::AccessFlagBits2::eColorAttachmentRead, + desc.view_info.range); + } + image.usage.render_target = 1u; + state.color_attachments[cb_index].imageLayout = image.last_state.layout; + ++cb_index; + } + + if (db_desc) { + const auto& image_id = std::get<0>(*db_desc); + const auto& desc = std::get<1>(*db_desc); + auto& image = texture_cache.GetImage(image_id); + ASSERT(image.binding.needs_rebind == 0); + const bool has_stencil = image.usage.stencil; + if (has_stencil) { + image.aspect_mask |= vk::ImageAspectFlagBits::eStencil; + } + if (image.binding.force_general) { + image.Transit(vk::ImageLayout::eGeneral, + vk::AccessFlagBits2::eDepthStencilAttachmentWrite | + vk::AccessFlagBits2::eShaderRead, + {}); + } else { + const auto new_layout = desc.view_info.is_storage + ? has_stencil + ? vk::ImageLayout::eDepthStencilAttachmentOptimal + : vk::ImageLayout::eDepthAttachmentOptimal + : has_stencil ? vk::ImageLayout::eDepthStencilReadOnlyOptimal + : vk::ImageLayout::eDepthReadOnlyOptimal; + image.Transit(new_layout, + vk::AccessFlagBits2::eDepthStencilAttachmentWrite | + vk::AccessFlagBits2::eDepthStencilAttachmentRead, + desc.view_info.range); + } + state.depth_attachment.imageLayout = image.last_state.layout; + image.usage.depth_target = true; + image.usage.stencil = has_stencil; + } + scheduler.BeginRendering(state); } @@ -306,10 +775,12 @@ void Rasterizer::Resolve() { // Read from MRT0, average all samples, and write to MRT1, which is one-sample const auto& mrt0_hint = liverpool->last_cb_extent[0]; const auto& mrt1_hint = liverpool->last_cb_extent[1]; - VideoCore::ImageInfo mrt0_info{liverpool->regs.color_buffers[0], mrt0_hint}; - VideoCore::ImageInfo mrt1_info{liverpool->regs.color_buffers[1], mrt1_hint}; - auto& mrt0_image = texture_cache.GetImage(texture_cache.FindImage(mrt0_info)); - auto& mrt1_image = texture_cache.GetImage(texture_cache.FindImage(mrt1_info)); + VideoCore::TextureCache::RenderTargetDesc mrt0_desc{liverpool->regs.color_buffers[0], + mrt0_hint}; + VideoCore::TextureCache::RenderTargetDesc mrt1_desc{liverpool->regs.color_buffers[1], + mrt1_hint}; + auto& mrt0_image = texture_cache.GetImage(texture_cache.FindImage(mrt0_desc)); + auto& mrt1_image = texture_cache.GetImage(texture_cache.FindImage(mrt1_desc)); VideoCore::SubresourceRange mrt0_range; mrt0_range.base.layer = liverpool->regs.color_buffers[0].view.slice_start; @@ -359,9 +830,9 @@ u32 Rasterizer::ReadDataFromGds(u32 gds_offset) { return value; } -void Rasterizer::InvalidateMemory(VAddr addr, u64 size) { - buffer_cache.InvalidateMemory(addr, size); - texture_cache.InvalidateMemory(addr, size); +void Rasterizer::InvalidateMemory(VAddr addr, VAddr addr_aligned, u64 size) { + buffer_cache.InvalidateMemory(addr_aligned, size); + texture_cache.InvalidateMemory(addr, addr_aligned, size); } void Rasterizer::MapMemory(VAddr addr, u64 size) { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 9035ed9dc..fe8aceba7 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -19,6 +19,7 @@ class MemoryManager; namespace Vulkan { class Scheduler; +class RenderState; class GraphicsPipeline; class Rasterizer { @@ -32,7 +33,8 @@ public: } void Draw(bool is_indexed, u32 index_offset = 0); - void DrawIndirect(bool is_indexed, VAddr address, u32 offset, u32 size); + void DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u32 size, u32 max_count, + VAddr count_address); void DispatchDirect(); void DispatchIndirect(VAddr address, u32 offset, u32 size); @@ -44,7 +46,7 @@ public: void InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds); u32 ReadDataFromGds(u32 gsd_offset); - void InvalidateMemory(VAddr addr, u64 size); + void InvalidateMemory(VAddr addr, VAddr addr_aligned, u64 size); void MapMemory(VAddr addr, u64 size); void UnmapMemory(VAddr addr, u64 size); @@ -53,7 +55,8 @@ public: void Finish(); private: - void BeginRendering(const GraphicsPipeline& pipeline); + RenderState PrepareRenderState(u32 mrt_mask); + void BeginRendering(const GraphicsPipeline& pipeline, RenderState& state); void Resolve(); void UpdateDynamicState(const GraphicsPipeline& pipeline); @@ -62,6 +65,21 @@ private: bool FilterDraw(); + void BindBuffers(const Shader::Info& stage, Shader::Backend::Bindings& binding, + Shader::PushData& push_data, Pipeline::DescriptorWrites& set_writes, + Pipeline::BufferBarriers& buffer_barriers); + + void BindTextures(const Shader::Info& stage, Shader::Backend::Bindings& binding, + Pipeline::DescriptorWrites& set_writes); + + bool BindResources(const Pipeline* pipeline); + void ResetBindings() { + for (auto& image_id : bound_images) { + texture_cache.GetImage(image_id).binding.Reset(); + } + bound_images.clear(); + } + private: const Instance& instance; Scheduler& scheduler; @@ -71,6 +89,25 @@ private: AmdGpu::Liverpool* liverpool; Core::MemoryManager* memory; PipelineCache pipeline_cache; + + boost::container::static_vector< + std::pair, 8> + cb_descs; + std::optional> db_desc; + boost::container::static_vector image_infos; + boost::container::static_vector buffer_views; + boost::container::static_vector buffer_infos; + boost::container::static_vector bound_images; + + Pipeline::DescriptorWrites set_writes; + Pipeline::BufferBarriers buffer_barriers; + + using BufferBindingInfo = std::pair; + boost::container::static_vector buffer_bindings; + using TexBufferBindingInfo = std::pair; + boost::container::static_vector texbuffer_bindings; + using ImageBindingInfo = std::pair; + boost::container::static_vector image_bindings; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index 86d7d5063..d0bc7ebdc 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -14,7 +14,7 @@ namespace Vulkan { Swapchain::Swapchain(const Instance& instance_, const Frontend::WindowSDL& window) : instance{instance_}, surface{CreateSurface(instance.GetInstance(), window)} { FindPresentFormat(); - Create(window.getWidth(), window.getHeight(), surface); + Create(window.GetWidth(), window.GetHeight(), surface); } Swapchain::~Swapchain() { diff --git a/src/video_core/texture_cache/image.cpp b/src/video_core/texture_cache/image.cpp index bea2ce4ff..e7e1ce1da 100644 --- a/src/video_core/texture_cache/image.cpp +++ b/src/video_core/texture_cache/image.cpp @@ -61,6 +61,15 @@ bool ImageInfo::IsDepthStencil() const { } } +bool ImageInfo::HasStencil() const { + if (pixel_format == vk::Format::eD32SfloatS8Uint || + pixel_format == vk::Format::eD24UnormS8Uint || + pixel_format == vk::Format::eD16UnormS8Uint) { + return true; + } + return false; +} + static vk::ImageUsageFlags ImageUsageFlags(const ImageInfo& info) { vk::ImageUsageFlags usage = vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst | @@ -135,22 +144,25 @@ void UniqueImage::Create(const vk::ImageCreateInfo& image_ci) { Image::Image(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_, const ImageInfo& info_) : instance{&instance_}, scheduler{&scheduler_}, info{info_}, - image{instance->GetDevice(), instance->GetAllocator()}, cpu_addr{info.guest_address}, - cpu_addr_end{cpu_addr + info.guest_size_bytes} { + image{instance->GetDevice(), instance->GetAllocator()} { mip_hashes.resize(info.resources.levels); ASSERT(info.pixel_format != vk::Format::eUndefined); // Here we force `eExtendedUsage` as don't know all image usage cases beforehand. In normal case // the texture cache should re-create the resource with the usage requested vk::ImageCreateFlags flags{vk::ImageCreateFlagBits::eMutableFormat | vk::ImageCreateFlagBits::eExtendedUsage}; - if (info.props.is_cube || (info.type == vk::ImageType::e2D && info.resources.layers >= 6)) { + const bool can_be_cube = + (info.type == vk::ImageType::e2D) && + ((info.props.is_pow2 ? (info.resources.layers % 8) : (info.resources.layers % 6)) == 0) && + (info.size.width == info.size.height); + if (info.props.is_cube || can_be_cube) { flags |= vk::ImageCreateFlagBits::eCubeCompatible; } else if (info.props.is_volume) { flags |= vk::ImageCreateFlagBits::e2DArrayCompatible; } - usage = ImageUsageFlags(info); - format_features = FormatFeatureFlags(usage); + usage_flags = ImageUsageFlags(info); + format_features = FormatFeatureFlags(usage_flags); switch (info.pixel_format) { case vk::Format::eD16Unorm: @@ -170,7 +182,7 @@ Image::Image(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_, constexpr auto tiling = vk::ImageTiling::eOptimal; const auto supported_format = instance->GetSupportedFormat(info.pixel_format, format_features); const auto properties = instance->GetPhysicalDevice().getImageFormatProperties( - supported_format, info.type, tiling, usage, flags); + supported_format, info.type, tiling, usage_flags, flags); const auto supported_samples = properties.result == vk::Result::eSuccess ? properties.value.sampleCounts : vk::SampleCountFlagBits::e1; @@ -188,7 +200,7 @@ Image::Image(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_, .arrayLayers = static_cast(info.resources.layers), .samples = LiverpoolToVK::NumSamples(info.num_samples, supported_samples), .tiling = tiling, - .usage = usage, + .usage = usage_flags, .initialLayout = vk::ImageLayout::eUndefined, }; diff --git a/src/video_core/texture_cache/image.h b/src/video_core/texture_cache/image.h index 312ff97e8..a1b1b007f 100644 --- a/src/video_core/texture_cache/image.h +++ b/src/video_core/texture_cache/image.h @@ -22,16 +22,15 @@ VK_DEFINE_HANDLE(VmaAllocator) namespace VideoCore { enum ImageFlagBits : u32 { - CpuDirty = 1 << 1, ///< Contents have been modified from the CPU + Empty = 0, + MaybeCpuDirty = 1 << 0, ///< The page this image is in was touched before the image address + CpuDirty = 1 << 1, ///< Contents have been modified from the CPU GpuDirty = 1 << 2, ///< Contents have been modified from the GPU (valid data in buffer cache) - Dirty = CpuDirty | GpuDirty, + Dirty = MaybeCpuDirty | CpuDirty | GpuDirty, GpuModified = 1 << 3, ///< Contents have been modified from the GPU - Tracked = 1 << 4, ///< Writes and reads are being hooked from the CPU Registered = 1 << 6, ///< True when the image is registered Picked = 1 << 7, ///< Temporary flag to mark the image as picked MetaRegistered = 1 << 8, ///< True when metadata for this surface is known and registered - Bound = 1 << 9, ///< True when the image is bound to a descriptor set - NeedsRebind = 1 << 10, ///< True when the image needs to be rebound }; DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits) @@ -80,7 +79,9 @@ struct Image { [[nodiscard]] bool Overlaps(VAddr overlap_cpu_addr, size_t overlap_size) const noexcept { const VAddr overlap_end = overlap_cpu_addr + overlap_size; - return cpu_addr < overlap_end && overlap_cpu_addr < cpu_addr_end; + const auto image_addr = info.guest_address; + const auto image_end = info.guest_address + info.guest_size_bytes; + return image_addr < overlap_end && overlap_cpu_addr < image_end; } ImageViewId FindView(const ImageViewInfo& info) const { @@ -101,19 +102,31 @@ struct Image { void CopyImage(const Image& image); void CopyMip(const Image& image, u32 mip); + bool IsTracked() { + return track_addr != 0 && track_addr_end != 0; + } + const Vulkan::Instance* instance; Vulkan::Scheduler* scheduler; ImageInfo info; UniqueImage image; vk::ImageAspectFlags aspect_mask = vk::ImageAspectFlagBits::eColor; ImageFlagBits flags = ImageFlagBits::Dirty; - VAddr cpu_addr = 0; - VAddr cpu_addr_end = 0; + VAddr track_addr = 0; + VAddr track_addr_end = 0; std::vector image_view_infos; std::vector image_view_ids; // Resource state tracking - vk::ImageUsageFlags usage; + struct { + u32 texture : 1; + u32 storage : 1; + u32 render_target : 1; + u32 depth_target : 1; + u32 stencil : 1; + u32 vo_surface : 1; + } usage{}; + vk::ImageUsageFlags usage_flags; vk::FormatFeatureFlags2 format_features; struct State { vk::Flags pl_stage = vk::PipelineStageFlagBits2::eAllCommands; @@ -124,6 +137,23 @@ struct Image { std::vector subresource_states{}; boost::container::small_vector mip_hashes{}; u64 tick_accessed_last{0}; + u64 hash{0}; + + struct { + union { + struct { + u32 is_bound : 1; // the image is bound to a descriptor set + u32 is_target : 1; // the image is bound as color/depth target + u32 needs_rebind : 1; // the image needs to be rebound + u32 force_general : 1; // the image needs to be used in general layout + }; + u32 raw{}; + }; + + void Reset() { + raw = 0u; + } + } binding{}; }; } // namespace VideoCore diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp index 055753dbc..0ed36ee39 100644 --- a/src/video_core/texture_cache/image_info.cpp +++ b/src/video_core/texture_cache/image_info.cpp @@ -47,7 +47,7 @@ static vk::ImageType ConvertImageType(AmdGpu::ImageType type) noexcept { // clang-format off // The table of macro tiles parameters for given tiling index (row) and bpp (column) -static constexpr std::array macro_tile_extents{ +static constexpr std::array macro_tile_extents_x1{ std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, // 00 std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 01 std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 02 @@ -66,26 +66,123 @@ static constexpr std::array macro_tile_extents{ std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, // 0F std::pair{256u, 256u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, // 10 std::pair{256u, 256u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, // 11 - std::pair{256u, 256u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 12 + std::pair{256u, 256u}, std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, // 12 std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 13 std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 14 std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 15 std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 16 std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 17 - std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{128u, 64u}, // 18 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 18 std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 19 std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 1A }; + +static constexpr std::array macro_tile_extents_x2{ + std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, // 00 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 01 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 02 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 03 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 04 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 05 + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 06 + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 07 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 08 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 09 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0A + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0B + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0C + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 0D + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0E + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0F + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 10 + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 11 + std::pair{256u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 12 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 13 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 14 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 15 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 16 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 17 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 18 + std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 19 + std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 1A +}; + +static constexpr std::array macro_tile_extents_x4{ + std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, // 00 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 01 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 02 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 03 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 04 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 05 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 06 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 07 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 08 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 09 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0A + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0B + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0C + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 0D + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0E + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0F + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 10 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 11 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 12 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 13 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 14 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 15 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 16 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 17 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 18 + std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 19 + std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 1A +}; + +static constexpr std::array macro_tile_extents_x8{ + std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, std::pair{256u, 128u}, // 00 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 01 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 02 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, // 03 + std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 04 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 05 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 128u}, // 06 + std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 07 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 08 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 09 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0A + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0B + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0C + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 0D + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0E + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 0F + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 10 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 11 + std::pair{128u, 128u}, std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 12 + std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, std::pair{0u, 0u}, // 13 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 14 + std::pair{128u, 64u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 15 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 16 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 17 + std::pair{128u, 128u}, std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 18 + std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 19 + std::pair{128u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, std::pair{64u, 64u}, // 1A +}; + +static constexpr std::array macro_tile_extents{ + macro_tile_extents_x1, + macro_tile_extents_x2, + macro_tile_extents_x4, + macro_tile_extents_x8, +}; // clang-format on static constexpr std::pair micro_tile_extent{8u, 8u}; static constexpr auto hw_pipe_interleave = 256u; static constexpr std::pair GetMacroTileExtents(u32 tiling_idx, u32 bpp, u32 num_samples) { - ASSERT(num_samples == 1); + ASSERT(num_samples <= 8); const auto row = tiling_idx * 5; const auto column = std::bit_width(bpp) - 4; // bpps are 8, 16, 32, 64, 128 - return macro_tile_extents[row + column]; + return (macro_tile_extents[std::log2(num_samples)])[row + column]; } static constexpr std::pair ImageSizeLinearAligned(u32 pitch, u32 height, u32 bpp, @@ -116,9 +213,21 @@ static constexpr std::pair ImageSizeMicroTiled(u32 pitch, u32 heigh } static constexpr std::pair ImageSizeMacroTiled(u32 pitch, u32 height, u32 bpp, - u32 num_samples, u32 tiling_idx) { + u32 num_samples, u32 tiling_idx, + u32 mip_n) { const auto& [pitch_align, height_align] = GetMacroTileExtents(tiling_idx, bpp, num_samples); ASSERT(pitch_align != 0 && height_align != 0); + bool downgrade_to_micro = false; + if (mip_n > 0) { + const bool is_less_than_tile = pitch < pitch_align || height < height_align; + // TODO: threshold check + downgrade_to_micro = is_less_than_tile; + } + + if (downgrade_to_micro) { + return ImageSizeMicroTiled(pitch, height, bpp, num_samples); + } + const auto pitch_aligned = (pitch + pitch_align - 1) & ~(pitch_align - 1); const auto height_aligned = (height + height_align - 1) & ~(height_align - 1); const auto log_sz = pitch_aligned * height_aligned * num_samples; @@ -136,7 +245,6 @@ ImageInfo::ImageInfo(const Libraries::VideoOut::BufferAttributeGroup& group, size.width = attrib.width; size.height = attrib.height; pitch = attrib.tiling_mode == TilingMode::Linear ? size.width : (size.width + 127) & (~127); - usage.vo_buffer = true; num_bits = attrib.pixel_format != VideoOutFormat::A16R16G16B16Float ? 32 : 64; ASSERT(num_bits == 32); @@ -168,7 +276,6 @@ ImageInfo::ImageInfo(const AmdGpu::Liverpool::ColorBuffer& buffer, resources.layers = buffer.NumSlices(); meta_info.cmask_addr = buffer.info.fast_clear ? buffer.CmaskAddress() : 0; meta_info.fmask_addr = buffer.info.compression ? buffer.FmaskAddress() : 0; - usage.render_target = true; guest_address = buffer.Address(); const auto color_slice_sz = buffer.GetColorSliceSize(); @@ -190,9 +297,6 @@ ImageInfo::ImageInfo(const AmdGpu::Liverpool::DepthBuffer& buffer, u32 num_slice pitch = buffer.Pitch(); resources.layers = num_slices; meta_info.htile_addr = buffer.z_info.tile_surface_en ? htile_address : 0; - usage.depth_target = true; - usage.stencil = - buffer.stencil_info.format != AmdGpu::Liverpool::DepthBuffer::StencilFormat::Invalid; guest_address = buffer.Address(); const auto depth_slice_sz = buffer.GetDepthSliceSize(); @@ -221,7 +325,6 @@ ImageInfo::ImageInfo(const AmdGpu::Image& image, const Shader::ImageResource& de resources.layers = image.NumLayers(desc.is_array); num_samples = image.NumSamples(); num_bits = NumBits(image.GetDataFmt()); - usage.texture = true; guest_address = image.Address(); @@ -274,9 +377,8 @@ void ImageInfo::UpdateSize() { case AmdGpu::TilingMode::Texture_MacroTiled: case AmdGpu::TilingMode::Depth_MacroTiled: { ASSERT(!props.is_block); - ASSERT(num_samples == 1); std::tie(mip_info.pitch, mip_info.size) = - ImageSizeMacroTiled(mip_w, mip_h, bpp, num_samples, tiling_idx); + ImageSizeMacroTiled(mip_w, mip_h, bpp, num_samples, tiling_idx, mip); break; } default: { @@ -284,7 +386,6 @@ void ImageInfo::UpdateSize() { } } mip_info.size *= mip_d; - mip_info.offset = guest_size_bytes; mips_layout.emplace_back(mip_info); guest_size_bytes += mip_info.size; @@ -292,79 +393,87 @@ void ImageInfo::UpdateSize() { guest_size_bytes *= resources.layers; } -bool ImageInfo::IsMipOf(const ImageInfo& info) const { +int ImageInfo::IsMipOf(const ImageInfo& info) const { if (!IsCompatible(info)) { - return false; + return -1; + } + + if (IsTilingCompatible(info.tiling_idx, tiling_idx)) { + return -1; } // Currently we expect only on level to be copied. if (resources.levels != 1) { - return false; + return -1; } - const int mip = info.resources.levels - resources.levels; - if (mip < 1) { - return false; + if (info.mips_layout.empty()) { + UNREACHABLE(); } + // Find mip + auto mip = -1; + for (auto m = 0; m < info.mips_layout.size(); ++m) { + if (guest_address == (info.guest_address + info.mips_layout[m].offset)) { + mip = m; + break; + } + } + + if (mip < 0) { + return -1; + } + ASSERT(mip != 0); + const auto mip_w = std::max(info.size.width >> mip, 1u); const auto mip_h = std::max(info.size.height >> mip, 1u); if ((size.width != mip_w) || (size.height != mip_h)) { - return false; + return -1; } const auto mip_d = std::max(info.size.depth >> mip, 1u); if (info.type == vk::ImageType::e3D && type == vk::ImageType::e2D) { // In case of 2D array to 3D copy, make sure we have proper number of layers. if (resources.layers != mip_d) { - return false; + return -1; } } else { if (type != info.type) { - return false; + return -1; } } - // Check if the mip has correct size. - if (info.mips_layout.size() <= mip || info.mips_layout[mip].size != guest_size_bytes) { - return false; - } - - // Ensure that address matches too. - if ((info.guest_address + info.mips_layout[mip].offset) != guest_address) { - return false; - } - - return true; + return mip; } -bool ImageInfo::IsSliceOf(const ImageInfo& info) const { +int ImageInfo::IsSliceOf(const ImageInfo& info) const { if (!IsCompatible(info)) { - return false; + return -1; } // Array slices should be of the same type. if (type != info.type) { - return false; + return -1; } // 2D dimensions of both images should be the same. if ((size.width != info.size.width) || (size.height != info.size.height)) { - return false; + return -1; } // Check for size alignment. const bool slice_size = info.guest_size_bytes / info.resources.layers; if (guest_size_bytes % slice_size != 0) { - return false; + return -1; } // Ensure that address is aligned too. - if (((info.guest_address - guest_address) % guest_size_bytes) != 0) { - return false; + const auto addr_diff = guest_address - info.guest_address; + if ((addr_diff % guest_size_bytes) != 0) { + return -1; } - return true; + return addr_diff / guest_size_bytes; } } // namespace VideoCore diff --git a/src/video_core/texture_cache/image_info.h b/src/video_core/texture_cache/image_info.h index 2ae2547f7..e12ae3be1 100644 --- a/src/video_core/texture_cache/image_info.h +++ b/src/video_core/texture_cache/image_info.h @@ -28,14 +28,28 @@ struct ImageInfo { bool IsBlockCoded() const; bool IsPacked() const; bool IsDepthStencil() const; + bool HasStencil() const; - bool IsMipOf(const ImageInfo& info) const; - bool IsSliceOf(const ImageInfo& info) const; + int IsMipOf(const ImageInfo& info) const; + int IsSliceOf(const ImageInfo& info) const; /// Verifies if images are compatible for subresource merging. bool IsCompatible(const ImageInfo& info) const { - return (pixel_format == info.pixel_format && tiling_idx == info.tiling_idx && - num_samples == info.num_samples && num_bits == info.num_bits); + return (pixel_format == info.pixel_format && num_samples == info.num_samples && + num_bits == info.num_bits); + } + + bool IsTilingCompatible(u32 lhs, u32 rhs) const { + if (lhs == rhs) { + return true; + } + if (lhs == 0x0e && rhs == 0x0d) { + return true; + } + if (lhs == 0x0d && rhs == 0x0e) { + return true; + } + return false; } void UpdateSize(); @@ -46,15 +60,6 @@ struct ImageInfo { VAddr htile_addr; } meta_info{}; - struct { - u32 texture : 1; - u32 storage : 1; - u32 render_target : 1; - u32 depth_target : 1; - u32 stencil : 1; - u32 vo_buffer : 1; - } usage{}; // Usage data tracked during image lifetime - struct { u32 is_cube : 1; u32 is_volume : 1; @@ -81,6 +86,9 @@ struct ImageInfo { VAddr guest_address{0}; u32 guest_size_bytes{0}; u32 tiling_idx{0}; // TODO: merge with existing! + + VAddr stencil_addr{0}; + u32 stencil_size{0}; }; } // namespace VideoCore diff --git a/src/video_core/texture_cache/image_view.cpp b/src/video_core/texture_cache/image_view.cpp index 7dbf1230e..61f1aaafe 100644 --- a/src/video_core/texture_cache/image_view.cpp +++ b/src/video_core/texture_cache/image_view.cpp @@ -58,11 +58,22 @@ bool IsIdentityMapping(u32 dst_sel, u32 num_components) { } vk::Format TrySwizzleFormat(vk::Format format, u32 dst_sel) { - if (format == vk::Format::eR8G8B8A8Unorm && dst_sel == 0b111100101110) { - return vk::Format::eB8G8R8A8Unorm; - } - if (format == vk::Format::eR8G8B8A8Srgb && dst_sel == 0b111100101110) { - return vk::Format::eB8G8R8A8Srgb; + // BGRA + if (dst_sel == 0b111100101110) { + switch (format) { + case vk::Format::eR8G8B8A8Unorm: + return vk::Format::eB8G8R8A8Unorm; + case vk::Format::eR8G8B8A8Snorm: + return vk::Format::eB8G8R8A8Snorm; + case vk::Format::eR8G8B8A8Uint: + return vk::Format::eB8G8R8A8Uint; + case vk::Format::eR8G8B8A8Sint: + return vk::Format::eB8G8R8A8Sint; + case vk::Format::eR8G8B8A8Srgb: + return vk::Format::eB8G8R8A8Srgb; + default: + break; + } } return format; } @@ -87,12 +98,9 @@ ImageViewInfo::ImageViewInfo(const AmdGpu::Image& image, const Shader::ImageReso range.extent.levels = image.last_level - image.base_level + 1; } range.extent.layers = image.last_array - image.base_array + 1; - type = ConvertImageViewType(image.GetType()); + type = ConvertImageViewType(image.GetBoundType()); - // Adjust view type for partial cubemaps and arrays - if (image.IsPartialCubemap()) { - type = vk::ImageViewType::e2DArray; - } + // Adjust view type for arrays if (type == vk::ImageViewType::eCube) { if (desc.is_array) { type = vk::ImageViewType::eCubeArray; @@ -125,14 +133,14 @@ ImageViewInfo::ImageViewInfo(const AmdGpu::Image& image, const Shader::ImageReso } } -ImageViewInfo::ImageViewInfo(const AmdGpu::Liverpool::ColorBuffer& col_buffer, - bool is_vo_surface) noexcept { +ImageViewInfo::ImageViewInfo(const AmdGpu::Liverpool::ColorBuffer& col_buffer) noexcept { const auto base_format = Vulkan::LiverpoolToVK::SurfaceFormat(col_buffer.info.format, col_buffer.NumFormat()); range.base.layer = col_buffer.view.slice_start; range.extent.layers = col_buffer.NumSlices() - range.base.layer; - format = Vulkan::LiverpoolToVK::AdjustColorBufferFormat( - base_format, col_buffer.info.comp_swap.Value(), is_vo_surface); + type = range.extent.layers > 1 ? vk::ImageViewType::e2DArray : vk::ImageViewType::e2D; + format = Vulkan::LiverpoolToVK::AdjustColorBufferFormat(base_format, + col_buffer.info.comp_swap.Value()); } ImageViewInfo::ImageViewInfo(const AmdGpu::Liverpool::DepthBuffer& depth_buffer, @@ -143,20 +151,22 @@ ImageViewInfo::ImageViewInfo(const AmdGpu::Liverpool::DepthBuffer& depth_buffer, is_storage = ctl.depth_write_enable; range.base.layer = view.slice_start; range.extent.layers = view.NumSlices() - range.base.layer; + type = range.extent.layers > 1 ? vk::ImageViewType::e2DArray : vk::ImageViewType::e2D; } ImageView::ImageView(const Vulkan::Instance& instance, const ImageViewInfo& info_, Image& image, ImageId image_id_) : image_id{image_id_}, info{info_} { - vk::ImageViewUsageCreateInfo usage_ci{.usage = image.usage}; + vk::ImageViewUsageCreateInfo usage_ci{.usage = image.usage_flags}; if (!info.is_storage) { usage_ci.usage &= ~vk::ImageUsageFlagBits::eStorage; } - // When sampling D32 texture from shader, the T# specifies R32 Float format so adjust it. + // When sampling D32/D16 texture from shader, the T# specifies R32/R16 format so adjust it. vk::Format format = info.format; vk::ImageAspectFlags aspect = image.aspect_mask; if (image.aspect_mask & vk::ImageAspectFlagBits::eDepth && - (format == vk::Format::eR32Sfloat || format == vk::Format::eD32Sfloat)) { + (format == vk::Format::eR32Sfloat || format == vk::Format::eD32Sfloat || + format == vk::Format::eR16Unorm || format == vk::Format::eD16Unorm)) { format = image.info.pixel_format; aspect = vk::ImageAspectFlagBits::eDepth; } diff --git a/src/video_core/texture_cache/image_view.h b/src/video_core/texture_cache/image_view.h index ba8d2c72b..23c703d23 100644 --- a/src/video_core/texture_cache/image_view.h +++ b/src/video_core/texture_cache/image_view.h @@ -19,7 +19,7 @@ namespace VideoCore { struct ImageViewInfo { ImageViewInfo() = default; ImageViewInfo(const AmdGpu::Image& image, const Shader::ImageResource& desc) noexcept; - ImageViewInfo(const AmdGpu::Liverpool::ColorBuffer& col_buffer, bool is_vo_surface) noexcept; + ImageViewInfo(const AmdGpu::Liverpool::ColorBuffer& col_buffer) noexcept; ImageViewInfo(const AmdGpu::Liverpool::DepthBuffer& depth_buffer, AmdGpu::Liverpool::DepthView view, AmdGpu::Liverpool::DepthControl ctl); diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 27c288885..1670648b3 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -28,28 +28,61 @@ TextureCache::TextureCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& info.num_bits = 32; info.UpdateSize(); const ImageId null_id = slot_images.insert(instance, scheduler, info); - ASSERT(null_id.index == 0); - const vk::Image& null_image = slot_images[null_id].image; + 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"); - slot_images[null_id].flags = ImageFlagBits::Tracked; + img.flags = ImageFlagBits::Empty; + img.track_addr = img.info.guest_address; + img.track_addr_end = img.info.guest_address + img.info.guest_size_bytes; 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 == 0); + 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"); } TextureCache::~TextureCache() = default; -void TextureCache::InvalidateMemory(VAddr address, size_t size) { +void TextureCache::MarkAsMaybeDirty(ImageId image_id, Image& image) { + if (image.hash == 0) { + // Initialize hash + const u8* addr = std::bit_cast(image.info.guest_address); + image.hash = XXH3_64bits(addr, image.info.guest_size_bytes); + } + image.flags |= ImageFlagBits::MaybeCpuDirty; + UntrackImage(image_id); +} + +void TextureCache::InvalidateMemory(VAddr addr, VAddr page_addr, size_t size) { std::scoped_lock lock{mutex}; - ForEachImageInRegion(address, size, [&](ImageId image_id, Image& image) { - // Ensure image is reuploaded when accessed again. - image.flags |= ImageFlagBits::CpuDirty; - // Untrack image, so the range is unprotected and the guest can write freely. - UntrackImage(image_id); + ForEachImageInRegion(page_addr, size, [&](ImageId image_id, Image& image) { + const auto image_begin = image.info.guest_address; + const auto image_end = image.info.guest_address + image.info.guest_size_bytes; + const auto page_end = page_addr + size; + if (image_begin <= addr && addr < image_end) { + // This image was definitely accessed by this page fault. + // Untrack image, so the range is unprotected and the guest can write freely + image.flags |= ImageFlagBits::CpuDirty; + UntrackImage(image_id); + } else if (page_end < image_end) { + // This page access may or may not modify the image. + // We should not mark it as dirty now. If it really was modified + // it will receive more invalidations on its other pages. + // Remove tracking from this page only. + UntrackImageHead(image_id); + } else if (image_begin < page_addr) { + // This page access does not modify the image but the page should be untracked. + // We should not mark this image as dirty now. If it really was modified + // it will receive more invalidations on its other pages. + UntrackImageTail(image_id); + } else { + // Image begins and ends on this page so it can not receive any more invalidations. + // We will check it's hash later to see if it really was modified. + MarkAsMaybeDirty(image_id, image); + } }); } @@ -77,84 +110,149 @@ void TextureCache::UnmapMemory(VAddr cpu_addr, size_t size) { } } -ImageId TextureCache::ResolveDepthOverlap(const ImageInfo& requested_info, ImageId cache_image_id) { - const auto& cache_info = slot_images[cache_image_id].info; +ImageId TextureCache::ResolveDepthOverlap(const ImageInfo& requested_info, BindingType binding, + ImageId cache_image_id) { + const auto& cache_image = slot_images[cache_image_id]; - const bool was_bound_as_texture = - !cache_info.usage.depth_target && (cache_info.usage.texture || cache_info.usage.storage); - if (requested_info.usage.depth_target && was_bound_as_texture) { - auto new_image_id = slot_images.insert(instance, scheduler, requested_info); + if (!cache_image.info.IsDepthStencil() && !requested_info.IsDepthStencil()) { + return {}; + } + + const bool stencil_match = requested_info.HasStencil() == cache_image.info.HasStencil(); + const bool bpp_match = requested_info.num_bits == cache_image.info.num_bits; + + // If an image in the cache has less slices we need to expand it + bool recreate = cache_image.info.resources < requested_info.resources; + + switch (binding) { + case BindingType::Texture: + // The guest requires a depth sampled texture, but cache can offer only Rxf. Need to + // recreate the image. + recreate |= requested_info.IsDepthStencil() && !cache_image.info.IsDepthStencil(); + break; + case BindingType::Storage: + // If the guest is going to use previously created depth as storage, the image needs to be + // recreated. (TODO: Probably a case with linear rgba8 aliasing is legit) + recreate |= cache_image.info.IsDepthStencil(); + break; + case BindingType::RenderTarget: + // Render target can have only Rxf format. If the cache contains only Dx[S8] we need to + // re-create the image. + ASSERT(!requested_info.IsDepthStencil()); + recreate |= cache_image.info.IsDepthStencil(); + break; + case BindingType::DepthTarget: + // The guest has requested previously allocated texture to be bound as a depth target. + // In this case we need to convert Rx float to a Dx[S8] as requested + recreate |= !cache_image.info.IsDepthStencil(); + + // The guest is trying to bind a depth target and cache has it. Need to be sure that aspects + // and bpp match + recreate |= cache_image.info.IsDepthStencil() && !(stencil_match && bpp_match); + break; + default: + break; + } + + if (recreate) { + auto new_info{requested_info}; + new_info.resources = std::max(requested_info.resources, cache_image.info.resources); + new_info.UpdateSize(); + const auto new_image_id = slot_images.insert(instance, scheduler, new_info); RegisterImage(new_image_id); + // Inherit image usage + auto& new_image = GetImage(new_image_id); + new_image.usage = cache_image.usage; + // TODO: perform a depth copy here FreeImage(cache_image_id); return new_image_id; } - const bool should_bind_as_texture = - !requested_info.usage.depth_target && - (requested_info.usage.texture || requested_info.usage.storage); - if (cache_info.usage.depth_target && should_bind_as_texture) { - if (cache_info.resources == requested_info.resources) { - return cache_image_id; - } else { - UNREACHABLE(); - } - } - - return {}; + // Will be handled by view + return cache_image_id; } -ImageId TextureCache::ResolveOverlap(const ImageInfo& image_info, ImageId cache_image_id, - ImageId merged_image_id) { +std::tuple TextureCache::ResolveOverlap(const ImageInfo& image_info, + BindingType binding, + ImageId cache_image_id, + ImageId merged_image_id) { auto& tex_cache_image = slot_images[cache_image_id]; + // We can assume it is safe to delete the image if it wasn't accessed in some number of frames. + const bool safe_to_delete = + scheduler.CurrentTick() - tex_cache_image.tick_accessed_last > NumFramesBeforeRemoval; if (image_info.guest_address == tex_cache_image.info.guest_address) { // Equal address if (image_info.size != tex_cache_image.info.size) { - // Very likely this kind of overlap is caused by allocation from a pool. We can assume - // it is safe to delete the image if it wasn't accessed in some amount of frames. - if (scheduler.CurrentTick() - tex_cache_image.tick_accessed_last > - NumFramesBeforeRemoval) { - + // Very likely this kind of overlap is caused by allocation from a pool. + if (safe_to_delete) { FreeImage(cache_image_id); } - return merged_image_id; + return {merged_image_id, -1, -1}; } - if (auto depth_image_id = ResolveDepthOverlap(image_info, cache_image_id)) { - return depth_image_id; + if (const auto depth_image_id = ResolveDepthOverlap(image_info, binding, cache_image_id)) { + return {depth_image_id, -1, -1}; } if (image_info.pixel_format != tex_cache_image.info.pixel_format || image_info.guest_size_bytes <= tex_cache_image.info.guest_size_bytes) { auto result_id = merged_image_id ? merged_image_id : cache_image_id; const auto& result_image = slot_images[result_id]; - return IsVulkanFormatCompatible(image_info.pixel_format, result_image.info.pixel_format) - ? result_id - : ImageId{}; + return { + IsVulkanFormatCompatible(image_info.pixel_format, result_image.info.pixel_format) + ? result_id + : ImageId{}, + -1, -1}; } ImageId new_image_id{}; if (image_info.type == tex_cache_image.info.type) { + ASSERT(image_info.resources > tex_cache_image.info.resources); new_image_id = ExpandImage(image_info, cache_image_id); } else { UNREACHABLE(); } - return new_image_id; + return {new_image_id, -1, -1}; } // Right overlap, the image requested is a possible subresource of the image from cache. if (image_info.guest_address > tex_cache_image.info.guest_address) { - // Should be handled by view. No additional actions needed. + if (auto mip = image_info.IsMipOf(tex_cache_image.info); mip >= 0) { + return {cache_image_id, mip, -1}; + } + + if (auto slice = image_info.IsSliceOf(tex_cache_image.info); slice >= 0) { + return {cache_image_id, -1, slice}; + } + + // TODO: slice and mip + + if (safe_to_delete) { + FreeImage(cache_image_id); + } + + return {{}, -1, -1}; } else { // Left overlap, the image from cache is a possible subresource of the image requested if (!merged_image_id) { // We need to have a larger, already allocated image to copy this one into - return {}; + return {{}, -1, -1}; } - if (tex_cache_image.info.IsMipOf(image_info)) { + if (auto mip = tex_cache_image.info.IsMipOf(image_info); mip >= 0) { + if (tex_cache_image.binding.is_target) { + // We have a larger image created and a separate one, representing a subres of it, + // bound as render target. In this case we need to rebind render target. + tex_cache_image.binding.needs_rebind = 1u; + GetImage(merged_image_id).binding.is_target = 1u; + + FreeImage(cache_image_id); + return {merged_image_id, -1, -1}; + } + tex_cache_image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {}); @@ -162,13 +260,13 @@ ImageId TextureCache::ResolveOverlap(const ImageInfo& image_info, ImageId cache_ ASSERT(num_mips_to_copy == 1); auto& merged_image = slot_images[merged_image_id]; - merged_image.CopyMip(tex_cache_image, image_info.resources.levels - 1); + merged_image.CopyMip(tex_cache_image, mip); FreeImage(cache_image_id); } } - return merged_image_id; + return {merged_image_id, -1, -1}; } ImageId TextureCache::ExpandImage(const ImageInfo& info, ImageId image_id) { @@ -181,8 +279,8 @@ ImageId TextureCache::ExpandImage(const ImageInfo& info, ImageId image_id) { src_image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {}); new_image.CopyImage(src_image); - if (True(src_image.flags & ImageFlagBits::Bound)) { - src_image.flags |= ImageFlagBits::NeedsRebind; + if (src_image.binding.is_bound || src_image.binding.is_target) { + src_image.binding.needs_rebind = 1u; } FreeImage(image_id); @@ -192,9 +290,11 @@ ImageId TextureCache::ExpandImage(const ImageInfo& info, ImageId image_id) { return new_image_id; } -ImageId TextureCache::FindImage(const ImageInfo& info, FindFlags flags) { +ImageId TextureCache::FindImage(BaseDesc& desc, FindFlags flags) { + const auto& info = desc.info; + if (info.guest_address == 0) [[unlikely]] { - return NULL_IMAGE_VIEW_ID; + return NULL_IMAGE_ID; } std::scoped_lock lock{mutex}; @@ -231,10 +331,16 @@ ImageId TextureCache::FindImage(const ImageInfo& info, FindFlags flags) { } // Try to resolve overlaps (if any) + int view_mip{-1}; + int view_slice{-1}; if (!image_id) { for (const auto& cache_id : image_ids) { + view_mip = -1; + view_slice = -1; + const auto& merged_info = image_id ? slot_images[image_id].info : info; - image_id = ResolveOverlap(merged_info, cache_id, image_id); + std::tie(image_id, view_mip, view_slice) = + ResolveOverlap(merged_info, desc.type, cache_id, image_id); } } @@ -254,6 +360,10 @@ ImageId TextureCache::FindImage(const ImageInfo& info, FindFlags flags) { RegisterImage(image_id); } + if (view_mip > 0) { + desc.view_info.range.base.level = view_mip; + } + Image& image = slot_images[image_id]; image.tick_accessed_last = scheduler.CurrentTick(); @@ -275,100 +385,55 @@ ImageView& TextureCache::RegisterImageView(ImageId image_id, const ImageViewInfo ImageView& TextureCache::FindTexture(ImageId image_id, const ImageViewInfo& view_info) { Image& image = slot_images[image_id]; UpdateImage(image_id); - auto& usage = image.info.usage; - - if (view_info.is_storage) { - image.Transit(vk::ImageLayout::eGeneral, - vk::AccessFlagBits2::eShaderRead | vk::AccessFlagBits2::eShaderWrite, - view_info.range); - usage.storage = true; - } else { - const auto new_layout = image.info.IsDepthStencil() - ? vk::ImageLayout::eDepthStencilReadOnlyOptimal - : vk::ImageLayout::eShaderReadOnlyOptimal; - image.Transit(new_layout, vk::AccessFlagBits2::eShaderRead, view_info.range); - usage.texture = true; - } - return RegisterImageView(image_id, view_info); } -ImageView& TextureCache::FindRenderTarget(const ImageInfo& image_info, - const ImageViewInfo& view_info) { - const ImageId image_id = FindImage(image_info); +ImageView& TextureCache::FindRenderTarget(BaseDesc& desc) { + const ImageId image_id = FindImage(desc); Image& image = slot_images[image_id]; image.flags |= ImageFlagBits::GpuModified; + image.usage.render_target = 1u; UpdateImage(image_id); - image.Transit(vk::ImageLayout::eColorAttachmentOptimal, - vk::AccessFlagBits2::eColorAttachmentWrite | - vk::AccessFlagBits2::eColorAttachmentRead, - view_info.range); - // Register meta data for this color buffer if (!(image.flags & ImageFlagBits::MetaRegistered)) { - if (image_info.meta_info.cmask_addr) { - surface_metas.emplace( - image_info.meta_info.cmask_addr, - MetaDataInfo{.type = MetaDataInfo::Type::CMask, .is_cleared = true}); - image.info.meta_info.cmask_addr = image_info.meta_info.cmask_addr; + if (desc.info.meta_info.cmask_addr) { + surface_metas.emplace(desc.info.meta_info.cmask_addr, + MetaDataInfo{.type = MetaDataInfo::Type::CMask}); + image.info.meta_info.cmask_addr = desc.info.meta_info.cmask_addr; image.flags |= ImageFlagBits::MetaRegistered; } - if (image_info.meta_info.fmask_addr) { - surface_metas.emplace( - image_info.meta_info.fmask_addr, - MetaDataInfo{.type = MetaDataInfo::Type::FMask, .is_cleared = true}); - image.info.meta_info.fmask_addr = image_info.meta_info.fmask_addr; + if (desc.info.meta_info.fmask_addr) { + surface_metas.emplace(desc.info.meta_info.fmask_addr, + MetaDataInfo{.type = MetaDataInfo::Type::FMask}); + image.info.meta_info.fmask_addr = desc.info.meta_info.fmask_addr; image.flags |= ImageFlagBits::MetaRegistered; } } - // Update tracked image usage - image.info.usage.render_target = true; - - return RegisterImageView(image_id, view_info); + return RegisterImageView(image_id, desc.view_info); } -ImageView& TextureCache::FindDepthTarget(const ImageInfo& image_info, - const ImageViewInfo& view_info) { - const ImageId image_id = FindImage(image_info); +ImageView& TextureCache::FindDepthTarget(BaseDesc& desc) { + const ImageId image_id = FindImage(desc); Image& image = slot_images[image_id]; image.flags |= ImageFlagBits::GpuModified; image.flags &= ~ImageFlagBits::Dirty; - image.aspect_mask = vk::ImageAspectFlagBits::eDepth; - - const bool has_stencil = image_info.usage.stencil; - if (has_stencil) { - image.aspect_mask |= vk::ImageAspectFlagBits::eStencil; - } - - const auto new_layout = view_info.is_storage - ? has_stencil ? vk::ImageLayout::eDepthStencilAttachmentOptimal - : vk::ImageLayout::eDepthAttachmentOptimal - : has_stencil ? vk::ImageLayout::eDepthStencilReadOnlyOptimal - : vk::ImageLayout::eDepthReadOnlyOptimal; - image.Transit(new_layout, - vk::AccessFlagBits2::eDepthStencilAttachmentWrite | - vk::AccessFlagBits2::eDepthStencilAttachmentRead, - view_info.range); + image.usage.depth_target = 1u; + image.usage.stencil = image.info.HasStencil(); // Register meta data for this depth buffer if (!(image.flags & ImageFlagBits::MetaRegistered)) { - if (image_info.meta_info.htile_addr) { - surface_metas.emplace( - image_info.meta_info.htile_addr, - MetaDataInfo{.type = MetaDataInfo::Type::HTile, .is_cleared = true}); - image.info.meta_info.htile_addr = image_info.meta_info.htile_addr; + if (desc.info.meta_info.htile_addr) { + surface_metas.emplace(desc.info.meta_info.htile_addr, + MetaDataInfo{.type = MetaDataInfo::Type::HTile}); + image.info.meta_info.htile_addr = desc.info.meta_info.htile_addr; image.flags |= ImageFlagBits::MetaRegistered; } } - // Update tracked image usage - image.info.usage.depth_target = true; - image.info.usage.stencil = has_stencil; - - return RegisterImageView(image_id, view_info); + return RegisterImageView(image_id, desc.view_info); } void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_scheduler /*= nullptr*/) { @@ -376,6 +441,27 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule return; } + if (image.info.num_samples > 1) { + return; + } + + if (True(image.flags & ImageFlagBits::MaybeCpuDirty) && + False(image.flags & ImageFlagBits::CpuDirty)) { + // The image size should be less than page size to be considered MaybeCpuDirty + // So this calculation should be very uncommon and reasonably fast + // For now we'll just check up to 64 first pixels + const auto addr = std::bit_cast(image.info.guest_address); + const auto w = std::min(image.info.size.width, u32(8)); + const auto h = std::min(image.info.size.height, u32(8)); + const auto size = w * h * image.info.num_bits / 8; + const u64 hash = XXH3_64bits(addr, size); + if (image.hash == hash) { + image.flags &= ~ImageFlagBits::MaybeCpuDirty; + return; + } + image.hash = hash; + } + const auto& num_layers = image.info.resources.layers; const auto& num_mips = image.info.resources.levels; ASSERT(num_mips == image.info.mips_layout.size()); @@ -386,14 +472,14 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule const u32 height = std::max(image.info.size.height >> m, 1u); const u32 depth = image.info.props.is_volume ? std::max(image.info.size.depth >> m, 1u) : 1u; - const auto& [mip_size, mip_pitch, mip_height, mip_ofs] = image.info.mips_layout[m]; + const auto& mip = image.info.mips_layout[m]; // Protect GPU modified resources from accidental CPU reuploads. const bool is_gpu_modified = True(image.flags & ImageFlagBits::GpuModified); const bool is_gpu_dirty = True(image.flags & ImageFlagBits::GpuDirty); if (is_gpu_modified && !is_gpu_dirty) { const u8* addr = std::bit_cast(image.info.guest_address); - const u64 hash = XXH3_64bits(addr + mip_ofs, mip_size); + const u64 hash = XXH3_64bits(addr + mip.offset, mip.size); if (image.mip_hashes[m] == hash) { continue; } @@ -401,9 +487,9 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule } image_copy.push_back({ - .bufferOffset = mip_ofs * num_layers, - .bufferRowLength = static_cast(mip_pitch), - .bufferImageHeight = static_cast(mip_height), + .bufferOffset = mip.offset * num_layers, + .bufferRowLength = static_cast(mip.pitch), + .bufferImageHeight = static_cast(mip.height), .imageSubresource{ .aspectMask = image.aspect_mask & ~vk::ImageAspectFlagBits::eStencil, .mipLevel = m, @@ -416,6 +502,7 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule } if (image_copy.empty()) { + image.flags &= ~ImageFlagBits::Dirty; return; } @@ -461,16 +548,16 @@ void TextureCache::RegisterImage(ImageId image_id) { ASSERT_MSG(False(image.flags & ImageFlagBits::Registered), "Trying to register an already registered image"); image.flags |= ImageFlagBits::Registered; - ForEachPage(image.cpu_addr, image.info.guest_size_bytes, + ForEachPage(image.info.guest_address, image.info.guest_size_bytes, [this, image_id](u64 page) { page_table[page].push_back(image_id); }); } void TextureCache::UnregisterImage(ImageId image_id) { Image& image = slot_images[image_id]; ASSERT_MSG(True(image.flags & ImageFlagBits::Registered), - "Trying to unregister an already registered image"); + "Trying to unregister an already unregistered image"); image.flags &= ~ImageFlagBits::Registered; - ForEachPage(image.cpu_addr, image.info.guest_size_bytes, [this, image_id](u64 page) { + ForEachPage(image.info.guest_address, image.info.guest_size_bytes, [this, image_id](u64 page) { const auto page_it = page_table.find(page); if (page_it == nullptr) { UNREACHABLE_MSG("Unregistering unregistered page=0x{:x}", page << PageShift); @@ -488,25 +575,106 @@ void TextureCache::UnregisterImage(ImageId image_id) { void TextureCache::TrackImage(ImageId image_id) { auto& image = slot_images[image_id]; - if (True(image.flags & ImageFlagBits::Tracked)) { + const auto image_begin = image.info.guest_address; + const auto image_end = image.info.guest_address + image.info.guest_size_bytes; + if (image_begin == image.track_addr && image_end == image.track_addr_end) { return; } - image.flags |= ImageFlagBits::Tracked; - tracker.UpdatePagesCachedCount(image.cpu_addr, image.info.guest_size_bytes, 1); + + if (!image.IsTracked()) { + // Re-track the whole image + image.track_addr = image_begin; + image.track_addr_end = image_end; + tracker.UpdatePagesCachedCount(image_begin, image.info.guest_size_bytes, 1); + } else { + if (image_begin < image.track_addr) { + TrackImageHead(image_id); + } + if (image.track_addr_end < image_end) { + TrackImageTail(image_id); + } + } +} + +void TextureCache::TrackImageHead(ImageId image_id) { + auto& image = slot_images[image_id]; + const auto image_begin = image.info.guest_address; + if (image_begin == image.track_addr) { + return; + } + ASSERT(image.track_addr != 0 && image_begin < image.track_addr); + const auto size = image.track_addr - image_begin; + image.track_addr = image_begin; + tracker.UpdatePagesCachedCount(image_begin, size, 1); +} + +void TextureCache::TrackImageTail(ImageId image_id) { + auto& image = slot_images[image_id]; + const auto image_end = image.info.guest_address + image.info.guest_size_bytes; + if (image_end == image.track_addr_end) { + return; + } + ASSERT(image.track_addr_end != 0 && image.track_addr_end < image_end); + const auto addr = image.track_addr_end; + const auto size = image_end - image.track_addr_end; + image.track_addr_end = image_end; + tracker.UpdatePagesCachedCount(addr, size, 1); } void TextureCache::UntrackImage(ImageId image_id) { auto& image = slot_images[image_id]; - if (False(image.flags & ImageFlagBits::Tracked)) { + if (!image.IsTracked()) { return; } - image.flags &= ~ImageFlagBits::Tracked; - tracker.UpdatePagesCachedCount(image.cpu_addr, image.info.guest_size_bytes, -1); + const auto addr = image.track_addr; + const auto size = image.track_addr_end - image.track_addr; + image.track_addr = 0; + image.track_addr_end = 0; + if (size != 0) { + tracker.UpdatePagesCachedCount(addr, size, -1); + } +} + +void TextureCache::UntrackImageHead(ImageId image_id) { + auto& image = slot_images[image_id]; + const auto image_begin = image.info.guest_address; + if (!image.IsTracked() || image_begin < image.track_addr) { + return; + } + const auto addr = tracker.GetNextPageAddr(image_begin); + const auto size = addr - image_begin; + image.track_addr = addr; + if (image.track_addr == image.track_addr_end) { + // This image spans only 2 pages and both are modified, + // but the image itself was not directly affected. + // Cehck its hash later. + MarkAsMaybeDirty(image_id, image); + } + tracker.UpdatePagesCachedCount(image_begin, size, -1); +} + +void TextureCache::UntrackImageTail(ImageId image_id) { + auto& image = slot_images[image_id]; + const auto image_end = image.info.guest_address + image.info.guest_size_bytes; + if (!image.IsTracked() || image.track_addr_end < image_end) { + return; + } + ASSERT(image.track_addr_end != 0); + const auto addr = tracker.GetPageAddr(image_end); + const auto size = image_end - addr; + image.track_addr_end = addr; + if (image.track_addr == image.track_addr_end) { + // This image spans only 2 pages and both are modified, + // but the image itself was not directly affected. + // Cehck its hash later. + MarkAsMaybeDirty(image_id, image); + } + tracker.UpdatePagesCachedCount(addr, size, -1); } void TextureCache::DeleteImage(ImageId image_id) { Image& image = slot_images[image_id]; - ASSERT_MSG(False(image.flags & ImageFlagBits::Tracked), "Image was not untracked"); + ASSERT_MSG(!image.IsTracked(), "Image was not untracked"); ASSERT_MSG(False(image.flags & ImageFlagBits::Registered), "Image was not unregistered"); // Remove any registered meta areas. diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 3bbfd952c..676ede777 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -36,19 +36,66 @@ static constexpr u32 MaxInvalidateDist = 12_MB; class TextureCache { struct Traits { using Entry = boost::container::small_vector; - static constexpr size_t AddressSpaceBits = 39; - static constexpr size_t FirstLevelBits = 9; + static constexpr size_t AddressSpaceBits = 40; + static constexpr size_t FirstLevelBits = 10; static constexpr size_t PageBits = 20; }; using PageTable = MultiLevelPageTable; public: - explicit TextureCache(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler, - BufferCache& buffer_cache, PageManager& tracker); + enum class BindingType : u32 { + Texture, + Storage, + RenderTarget, + DepthTarget, + VideoOut, + }; + + struct BaseDesc { + ImageInfo info; + ImageViewInfo view_info; + BindingType type{BindingType::Texture}; + + BaseDesc() = default; + BaseDesc(BindingType type_, ImageInfo info_, ImageViewInfo view_info_) noexcept + : info{std::move(info_)}, view_info{std::move(view_info_)}, type{type_} {} + }; + + struct TextureDesc : public BaseDesc { + TextureDesc() = default; + TextureDesc(const AmdGpu::Image& image, const Shader::ImageResource& desc) + : BaseDesc{desc.is_storage ? BindingType::Storage : BindingType::Texture, + ImageInfo{image, desc}, ImageViewInfo{image, desc}} {} + }; + + struct RenderTargetDesc : public BaseDesc { + RenderTargetDesc(const AmdGpu::Liverpool::ColorBuffer& buffer, + const AmdGpu::Liverpool::CbDbExtent& hint = {}) + : BaseDesc{BindingType::RenderTarget, ImageInfo{buffer, hint}, ImageViewInfo{buffer}} {} + }; + + struct DepthTargetDesc : public BaseDesc { + DepthTargetDesc(const AmdGpu::Liverpool::DepthBuffer& buffer, + const AmdGpu::Liverpool::DepthView& view, + const AmdGpu::Liverpool::DepthControl& ctl, VAddr htile_address, + const AmdGpu::Liverpool::CbDbExtent& hint = {}) + : BaseDesc{BindingType::DepthTarget, + ImageInfo{buffer, view.NumSlices(), htile_address, hint}, + ImageViewInfo{buffer, view, ctl}} {} + }; + + struct VideoOutDesc : public BaseDesc { + VideoOutDesc(const Libraries::VideoOut::BufferAttributeGroup& group, VAddr cpu_address) + : BaseDesc{BindingType::VideoOut, ImageInfo{group, cpu_address}, ImageViewInfo{}} {} + }; + +public: + TextureCache(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler, + BufferCache& buffer_cache, PageManager& tracker); ~TextureCache(); /// Invalidates any image in the logical page range. - void InvalidateMemory(VAddr address, size_t size); + void InvalidateMemory(VAddr addr, VAddr page_addr, size_t size); /// Marks an image as dirty if it exists at the provided address. void InvalidateMemoryFromGPU(VAddr address, size_t max_size); @@ -57,18 +104,16 @@ public: void UnmapMemory(VAddr cpu_addr, size_t size); /// Retrieves the image handle of the image with the provided attributes. - [[nodiscard]] ImageId FindImage(const ImageInfo& info, FindFlags flags = {}); + [[nodiscard]] ImageId FindImage(BaseDesc& desc, FindFlags flags = {}); /// Retrieves an image view with the properties of the specified image id. [[nodiscard]] ImageView& FindTexture(ImageId image_id, const ImageViewInfo& view_info); /// Retrieves the render target with specified properties - [[nodiscard]] ImageView& FindRenderTarget(const ImageInfo& image_info, - const ImageViewInfo& view_info); + [[nodiscard]] ImageView& FindRenderTarget(BaseDesc& desc); /// Retrieves the depth target with specified properties - [[nodiscard]] ImageView& FindDepthTarget(const ImageInfo& image_info, - const ImageViewInfo& view_info); + [[nodiscard]] ImageView& FindDepthTarget(BaseDesc& desc); /// Updates image contents if it was modified by CPU. void UpdateImage(ImageId image_id, Vulkan::Scheduler* custom_scheduler = nullptr) { @@ -77,11 +122,13 @@ public: RefreshImage(image, custom_scheduler); } - [[nodiscard]] ImageId ResolveOverlap(const ImageInfo& info, ImageId cache_img_id, - ImageId merged_image_id); + [[nodiscard]] std::tuple ResolveOverlap(const ImageInfo& info, + BindingType binding, + ImageId cache_img_id, + ImageId merged_image_id); /// Resolves depth overlap and either re-creates the image or returns existing one - [[nodiscard]] ImageId ResolveDepthOverlap(const ImageInfo& requested_info, + [[nodiscard]] ImageId ResolveDepthOverlap(const ImageInfo& requested_info, BindingType binding, ImageId cache_img_id); [[nodiscard]] ImageId ExpandImage(const ImageInfo& info, ImageId image_id); @@ -102,22 +149,38 @@ public: return slot_image_views[id]; } + /// Registers an image view for provided image + ImageView& RegisterImageView(ImageId image_id, const ImageViewInfo& view_info); + bool IsMeta(VAddr address) const { return surface_metas.contains(address); } - bool IsMetaCleared(VAddr address) const { + bool IsMetaCleared(VAddr address, u32 slice) const { const auto& it = surface_metas.find(address); if (it != surface_metas.end()) { - return it.value().is_cleared; + return it.value().clear_mask & (1u << slice); } return false; } - bool TouchMeta(VAddr address, bool is_clear) { + bool ClearMeta(VAddr address) { auto it = surface_metas.find(address); if (it != surface_metas.end()) { - it.value().is_cleared = is_clear; + it.value().clear_mask = u32(-1); + return true; + } + return false; + } + + bool TouchMeta(VAddr address, u32 slice, bool is_clear) { + auto it = surface_metas.find(address); + if (it != surface_metas.end()) { + if (is_clear) { + it.value().clear_mask |= 1u << slice; + } else { + it.value().clear_mask &= ~(1u << slice); + } return true; } return false; @@ -181,9 +244,6 @@ private: } } - /// Registers an image view for provided image - ImageView& RegisterImageView(ImageId image_id, const ImageViewInfo& view_info); - /// Create an image from the given parameters [[nodiscard]] ImageId InsertImage(const ImageInfo& info, VAddr cpu_addr); @@ -195,9 +255,15 @@ private: /// Track CPU reads and writes for image void TrackImage(ImageId image_id); + void TrackImageHead(ImageId image_id); + void TrackImageTail(ImageId image_id); /// Stop tracking CPU reads and writes for image void UntrackImage(ImageId image_id); + void UntrackImageHead(ImageId image_id); + void UntrackImageTail(ImageId image_id); + + void MarkAsMaybeDirty(ImageId image_id, Image& image); /// Removes the image and any views/surface metas that reference it. void DeleteImage(ImageId image_id); @@ -227,7 +293,7 @@ private: HTile, }; Type type; - bool is_cleared; + u32 clear_mask{u32(-1)}; }; tsl::robin_map surface_metas; }; diff --git a/src/video_core/texture_cache/tile_manager.cpp b/src/video_core/texture_cache/tile_manager.cpp index c4f24420d..2bc3bf282 100644 --- a/src/video_core/texture_cache/tile_manager.cpp +++ b/src/video_core/texture_cache/tile_manager.cpp @@ -14,7 +14,7 @@ #include "video_core/host_shaders/detile_m8x2_comp.h" #include -#include +#include #include namespace VideoCore { @@ -182,12 +182,15 @@ vk::Format DemoteImageFormatForDetiling(vk::Format format) { case vk::Format::eB8G8R8A8Srgb: case vk::Format::eB8G8R8A8Unorm: case vk::Format::eR8G8B8A8Unorm: + case vk::Format::eR8G8B8A8Snorm: case vk::Format::eR8G8B8A8Uint: case vk::Format::eR32Sfloat: case vk::Format::eR32Uint: case vk::Format::eR16G16Sfloat: case vk::Format::eR16G16Unorm: + case vk::Format::eR16G16Snorm: case vk::Format::eB10G11R11UfloatPack32: + case vk::Format::eA2B10G10R10UnormPack32: return vk::Format::eR32Uint; case vk::Format::eBc1RgbaSrgbBlock: case vk::Format::eBc1RgbaUnormBlock: @@ -389,7 +392,8 @@ std::pair TileManager::TryDetile(vk::Buffer in_buffer, u32 in_o const auto* detiler = GetDetiler(image); if (!detiler) { if (image.info.tiling_mode != AmdGpu::TilingMode::Texture_MacroTiled && - image.info.tiling_mode != AmdGpu::TilingMode::Display_MacroTiled) { + image.info.tiling_mode != AmdGpu::TilingMode::Display_MacroTiled && + image.info.tiling_mode != AmdGpu::TilingMode::Depth_MacroTiled) { LOG_ERROR(Render_Vulkan, "Unsupported tiled image: {} ({})", vk::to_string(image.info.pixel_format), NameOf(image.info.tiling_mode)); }