diff options
65 files changed, 1482 insertions, 629 deletions
diff --git a/.ci/scripts/windows/docker.sh b/.ci/scripts/windows/docker.sh index 192a01fd8..feba3fd6e 100755 --- a/.ci/scripts/windows/docker.sh +++ b/.ci/scripts/windows/docker.sh @@ -47,3 +47,6 @@ python3 .ci/scripts/windows/scan_dll.py package/imageformats/*.dll "package/" EXTERNALS_PATH="$(pwd)/build/externals" FFMPEG_DLL_PATH="$(find ${EXTERNALS_PATH} -maxdepth 1 -type d | grep ffmpeg)/bin" find ${FFMPEG_DLL_PATH} -type f -regex ".*\.dll" -exec cp -v {} package/ ';' + +# copy libraries from yuzu.exe path +find "$(pwd)/build/bin/" -type f -regex ".*\.dll" -exec cp -v {} package/ ';' diff --git a/.ci/templates/build-msvc.yml b/.ci/templates/build-msvc.yml index 74e688c12..f6e7557b2 100644 --- a/.ci/templates/build-msvc.yml +++ b/.ci/templates/build-msvc.yml @@ -8,7 +8,7 @@ steps: displayName: 'Install vulkan-sdk' - script: python -m pip install --upgrade pip conan displayName: 'Install conan' -- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 16 2019" -A x64 -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} .. && cmake --install . --config Release && cd .. +- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 16 2019" -A x64 -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} -DCMAKE_BUILD_TYPE=Release .. && cd .. displayName: 'Configure CMake' - task: MSBuild@1 displayName: 'Build' diff --git a/CMakeLists.txt b/CMakeLists.txt index e70f29636..ba207dfd1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,11 +13,11 @@ project(yuzu) option(ENABLE_SDL2 "Enable the SDL2 frontend" ON) CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 binaries" ON "ENABLE_SDL2;MSVC" OFF) # On Linux system SDL2 is likely to be lacking HIDAPI support which have drawbacks but is needed for SDL motion -CMAKE_DEPENDENT_OPTION(YUZU_ALLOW_SYSTEM_SDL2 "Try using system SDL2 before fallling back to one from externals" NOT UNIX "ENABLE_SDL2" OFF) +option(YUZU_ALLOW_SYSTEM_SDL2 "Try using system SDL2 before fallling back to one from externals" OFF) option(ENABLE_QT "Enable the Qt frontend" ON) option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF) -CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" ON "ENABLE_QT;MSVC" OFF) +CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" "${MSVC}" "ENABLE_QT" OFF) option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON) @@ -240,6 +240,7 @@ yuzu_find_packages() # Qt5 requires that we find components, so it doesn't fit our pretty little find package function if(ENABLE_QT) + set(QT_VERSION 5.12) # We want to load the generated conan qt config so that we get the QT_ROOT var so that we can use the official # Qt5Config inside the root folder instead of the conan generated one. if(EXISTS ${CMAKE_BINARY_DIR}/qtConfig.cmake) @@ -247,22 +248,40 @@ if(ENABLE_QT) list(APPEND CMAKE_MODULE_PATH "${CONAN_QT_ROOT_RELEASE}") list(APPEND CMAKE_PREFIX_PATH "${CONAN_QT_ROOT_RELEASE}") endif() + + # Check for system Qt on Linux, fallback to bundled Qt + if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + if (NOT YUZU_USE_BUNDLED_QT) + find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets QUIET) + if (NOT Qt5_FOUND) + set(YUZU_USE_BUNDLED_QT ON CACHE BOOL "Download bundled Qt" FORCE) + endif() + endif() + if (YUZU_USE_BUNDLED_QT) + # Binary package currently does not support Qt webengine, so make sure it's disabled + set(YUZU_USE_QT_WEB_ENGINE OFF CACHE BOOL "Use Qt Webengine" FORCE) + endif() + endif() + # Workaround for an issue where conan tries to build Qt from scratch instead of download prebuilt binaries set(QT_PREFIX_HINT) + if(YUZU_USE_BUNDLED_QT) if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1930) AND ARCHITECTURE_x86_64) - set(QT_VER qt-5.12.8-msvc2017_64) + set(QT_BUILD qt-5.12.8-msvc2017_64) + elseif ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux") AND NOT MINGW AND ARCHITECTURE_x86_64) + set(QT_BUILD qt5_5_15_2) else() message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable YUZU_USE_BUNDLED_QT and provide your own.") endif() - if (DEFINED QT_VER) - download_bundled_external("qt/" ${QT_VER} QT_PREFIX) + if (DEFINED QT_BUILD) + download_bundled_external("qt/" ${QT_BUILD} QT_PREFIX) endif() set(QT_PREFIX_HINT HINTS "${QT_PREFIX}") endif() - find_package(Qt5 5.9 COMPONENTS Widgets ${QT_PREFIX_HINT}) + find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets ${QT_PREFIX_HINT} NO_CMAKE_SYSTEM_PATH) if (YUZU_USE_QT_WEB_ENGINE) find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets) endif() @@ -271,6 +290,7 @@ if(ENABLE_QT) find_package(Qt5 REQUIRED COMPONENTS LinguistTools ${QT_PREFIX_HINT}) endif() endif() + # find SDL2 exports a bunch of variables that are needed, so its easier to do this outside of the yuzu_find_package if (ENABLE_SDL2) if (YUZU_USE_BUNDLED_SDL2) @@ -379,7 +399,7 @@ if (CONAN_REQUIRED_LIBS) if(ENABLE_QT) list(APPEND CMAKE_MODULE_PATH "${CONAN_QT_ROOT_RELEASE}") list(APPEND CMAKE_PREFIX_PATH "${CONAN_QT_ROOT_RELEASE}") - find_package(Qt5 5.9 REQUIRED COMPONENTS Widgets) + find_package(Qt5 5.12 REQUIRED COMPONENTS Widgets) if (YUZU_USE_QT_WEB_ENGINE) find_package(Qt5 REQUIRED COMPONENTS WebEngineCore WebEngineWidgets) endif() @@ -406,7 +426,7 @@ if(NOT APPLE) endif() if (NOT LIBUSB_FOUND) add_subdirectory(externals/libusb) - set(LIBUSB_INCLUDE_DIR "") + set(LIBUSB_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/externals/libusb/libusb/libusb") set(LIBUSB_LIBRARIES usb) endif() diff --git a/CMakeModules/CopyYuzuQt5Deps.cmake b/CMakeModules/CopyYuzuQt5Deps.cmake index 59343b1ca..4a6aeebbb 100644 --- a/CMakeModules/CopyYuzuQt5Deps.cmake +++ b/CMakeModules/CopyYuzuQt5Deps.cmake @@ -1,52 +1,111 @@ function(copy_yuzu_Qt5_deps target_dir) include(WindowsCopyFiles) - set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/") - set(Qt5_DLL_DIR "${Qt5_DIR}/../../../bin") + if (MSVC) + set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/") + set(Qt5_DLL_DIR "${Qt5_DIR}/../../../bin") + else() + set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/") + set(Qt5_DLL_DIR "${Qt5_DIR}/../../../lib/") + endif() set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/") + set(Qt5_PLATFORMTHEMES_DIR "${Qt5_DIR}/../../../plugins/platformthemes/") + set(Qt5_PLATFORMINPUTCONTEXTS_DIR "${Qt5_DIR}/../../../plugins/platforminputcontexts/") + set(Qt5_XCBGLINTEGRATIONS_DIR "${Qt5_DIR}/../../../plugins/xcbglintegrations/") set(Qt5_STYLES_DIR "${Qt5_DIR}/../../../plugins/styles/") set(Qt5_IMAGEFORMATS_DIR "${Qt5_DIR}/../../../plugins/imageformats/") set(Qt5_RESOURCES_DIR "${Qt5_DIR}/../../../resources/") set(PLATFORMS ${DLL_DEST}plugins/platforms/) set(STYLES ${DLL_DEST}plugins/styles/) set(IMAGEFORMATS ${DLL_DEST}plugins/imageformats/) - windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST} - icudt*.dll - icuin*.dll - icuuc*.dll - Qt5Core$<$<CONFIG:Debug>:d>.* - Qt5Gui$<$<CONFIG:Debug>:d>.* - Qt5Widgets$<$<CONFIG:Debug>:d>.* - ) - - if (YUZU_USE_QT_WEB_ENGINE) - windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST} - Qt5Network$<$<CONFIG:Debug>:d>.* - Qt5Positioning$<$<CONFIG:Debug>:d>.* - Qt5PrintSupport$<$<CONFIG:Debug>:d>.* - Qt5Qml$<$<CONFIG:Debug>:d>.* - Qt5Quick$<$<CONFIG:Debug>:d>.* - Qt5QuickWidgets$<$<CONFIG:Debug>:d>.* - Qt5WebChannel$<$<CONFIG:Debug>:d>.* - Qt5WebEngine$<$<CONFIG:Debug>:d>.* - Qt5WebEngineCore$<$<CONFIG:Debug>:d>.* - Qt5WebEngineWidgets$<$<CONFIG:Debug>:d>.* - QtWebEngineProcess$<$<CONFIG:Debug>:d>.* + if (MSVC) + windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST} + icudt*.dll + icuin*.dll + icuuc*.dll + Qt5Core$<$<CONFIG:Debug>:d>.* + Qt5Gui$<$<CONFIG:Debug>:d>.* + Qt5Widgets$<$<CONFIG:Debug>:d>.* ) - windows_copy_files(${target_dir} ${Qt5_RESOURCES_DIR} ${DLL_DEST} - qtwebengine_resources.pak - qtwebengine_devtools_resources.pak - qtwebengine_resources_100p.pak - qtwebengine_resources_200p.pak - icudtl.dat - ) - endif () - windows_copy_files(yuzu ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*) - windows_copy_files(yuzu ${Qt5_STYLES_DIR} ${STYLES} qwindowsvistastyle$<$<CONFIG:Debug>:d>.*) - windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS} - qjpeg$<$<CONFIG:Debug>:d>.* - qgif$<$<CONFIG:Debug>:d>.* - ) + if (YUZU_USE_QT_WEB_ENGINE) + windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST} + Qt5Network$<$<CONFIG:Debug>:d>.* + Qt5Positioning$<$<CONFIG:Debug>:d>.* + Qt5PrintSupport$<$<CONFIG:Debug>:d>.* + Qt5Qml$<$<CONFIG:Debug>:d>.* + Qt5Quick$<$<CONFIG:Debug>:d>.* + Qt5QuickWidgets$<$<CONFIG:Debug>:d>.* + Qt5WebChannel$<$<CONFIG:Debug>:d>.* + Qt5WebEngine$<$<CONFIG:Debug>:d>.* + Qt5WebEngineCore$<$<CONFIG:Debug>:d>.* + Qt5WebEngineWidgets$<$<CONFIG:Debug>:d>.* + QtWebEngineProcess$<$<CONFIG:Debug>:d>.* + ) + + windows_copy_files(${target_dir} ${Qt5_RESOURCES_DIR} ${DLL_DEST} + qtwebengine_resources.pak + qtwebengine_devtools_resources.pak + qtwebengine_resources_100p.pak + qtwebengine_resources_200p.pak + icudtl.dat + ) + endif () + windows_copy_files(yuzu ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*) + windows_copy_files(yuzu ${Qt5_STYLES_DIR} ${STYLES} qwindowsvistastyle$<$<CONFIG:Debug>:d>.*) + windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS} + qjpeg$<$<CONFIG:Debug>:d>.* + qgif$<$<CONFIG:Debug>:d>.* + ) + else() + set(Qt5_DLLS + "${Qt5_DLL_DIR}libQt5Core.so.5" + "${Qt5_DLL_DIR}libQt5DBus.so.5" + "${Qt5_DLL_DIR}libQt5Gui.so.5" + "${Qt5_DLL_DIR}libQt5Widgets.so.5" + "${Qt5_DLL_DIR}libQt5XcbQpa.so.5" + "${Qt5_DLL_DIR}libicudata.so.60" + "${Qt5_DLL_DIR}libicui18n.so.60" + "${Qt5_DLL_DIR}libicuuc.so.60" + ) + set(Qt5_IMAGEFORMAT_DLLS + "${Qt5_IMAGEFORMATS_DIR}libqjpeg.so" + "${Qt5_IMAGEFORMATS_DIR}libqgif.so" + "${Qt5_IMAGEFORMATS_DIR}libqico.so" + ) + set(Qt5_PLATFORMTHEME_DLLS + "${Qt5_PLATFORMTHEMES_DIR}libqgtk3.so" + "${Qt5_PLATFORMTHEMES_DIR}libqxdgdesktopportal.so" + ) + set(Qt5_PLATFORM_DLLS + "${Qt5_PLATFORMS_DIR}libqxcb.so" + ) + set(Qt5_PLATFORMINPUTCONTEXT_DLLS + "${Qt5_PLATFORMINPUTCONTEXTS_DIR}libcomposeplatforminputcontextplugin.so" + "${Qt5_PLATFORMINPUTCONTEXTS_DIR}libibusplatforminputcontextplugin.so" + ) + set(Qt5_XCBGLINTEGRATION_DLLS + "${Qt5_XCBGLINTEGRATIONS_DIR}libqxcb-glx-integration.so" + ) + foreach(LIB ${Qt5_DLLS}) + file(COPY ${LIB} DESTINATION "${DLL_DEST}/lib" FOLLOW_SYMLINK_CHAIN) + endforeach() + foreach(LIB ${Qt5_IMAGEFORMAT_DLLS}) + file(COPY ${LIB} DESTINATION "${DLL_DEST}plugins/imageformats/" FOLLOW_SYMLINK_CHAIN) + endforeach() + foreach(LIB ${Qt5_PLATFORMTHEME_DLLS}) + file(COPY ${LIB} DESTINATION "${DLL_DEST}plugins/platformthemes/" FOLLOW_SYMLINK_CHAIN) + endforeach() + foreach(LIB ${Qt5_PLATFORM_DLLS}) + file(COPY ${LIB} DESTINATION "${DLL_DEST}plugins/platforms/" FOLLOW_SYMLINK_CHAIN) + endforeach() + foreach(LIB ${Qt5_PLATFORMINPUTCONTEXT_DLLS}) + file(COPY ${LIB} DESTINATION "${DLL_DEST}plugins/platforminputcontexts/" FOLLOW_SYMLINK_CHAIN) + endforeach() + foreach(LIB ${Qt5_XCBGLINTEGRATION_DLLS}) + file(COPY ${LIB} DESTINATION "${DLL_DEST}plugins/xcbglintegrations/" FOLLOW_SYMLINK_CHAIN) + endforeach() + + endif() # Create an empty qt.conf file. Qt will detect that this file exists, and use the folder that its in as the root folder. # This way it'll look for plugins in the root/plugins/ folder add_custom_command(TARGET yuzu POST_BUILD diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index fe1c088ca..aae0baa0b 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -47,19 +47,21 @@ target_include_directories(unicorn-headers INTERFACE ./unicorn/include) # SDL2 if (NOT SDL2_FOUND AND ENABLE_SDL2) - # Yuzu itself needs: Events Joystick Haptic Sensor Timers - # Yuzu-cmd also needs: Video (depends on Loadso/Dlopen) - set(SDL_UNUSED_SUBSYSTEMS - Atomic Audio Render Power Threads - File CPUinfo Filesystem Locale) - foreach(_SUB ${SDL_UNUSED_SUBSYSTEMS}) - string(TOUPPER ${_SUB} _OPT) - option(SDL_${_OPT} "" OFF) - endforeach() - + if (NOT WIN32) + # Yuzu itself needs: Events Joystick Haptic Sensor Timers + # Yuzu-cmd also needs: Video (depends on Loadso/Dlopen) + set(SDL_UNUSED_SUBSYSTEMS + Atomic Audio Render Power Threads + File CPUinfo Filesystem Locale) + foreach(_SUB ${SDL_UNUSED_SUBSYSTEMS}) + string(TOUPPER ${_SUB} _OPT) + option(SDL_${_OPT} "" OFF) + endforeach() + + option(HIDAPI "" ON) + endif() set(SDL_STATIC ON) set(SDL_SHARED OFF) - option(HIDAPI "" ON) add_subdirectory(SDL EXCLUDE_FROM_ALL) add_library(SDL2 ALIAS SDL2-static) diff --git a/externals/dynarmic b/externals/dynarmic -Subproject b2a4da5e65985e6b0a20ac8ac37d14425a2a39d +Subproject 36c3b289a090aaf59a24346f57ebe1b13efb36c diff --git a/externals/libusb/CMakeLists.txt b/externals/libusb/CMakeLists.txt index 70d6735e3..3ef007b40 100644 --- a/externals/libusb/CMakeLists.txt +++ b/externals/libusb/CMakeLists.txt @@ -1,155 +1,239 @@ -# Ensure libusb compiles with UTF-8 encoding on MSVC -if(MSVC) - add_compile_options(/utf-8) -endif() - -add_library(usb STATIC EXCLUDE_FROM_ALL - libusb/libusb/core.c - libusb/libusb/core.c - libusb/libusb/descriptor.c - libusb/libusb/hotplug.c - libusb/libusb/io.c - libusb/libusb/strerror.c - libusb/libusb/sync.c -) -set_target_properties(usb PROPERTIES VERSION 1.0.23) -if(WIN32) - target_include_directories(usb - BEFORE - PUBLIC - libusb/libusb - - PRIVATE - "${CMAKE_CURRENT_BINARY_DIR}" - ) +if (MINGW) + # The MinGW toolchain for some reason doesn't work with this CMakeLists file after updating to + # 1.0.24, so we do it the old-fashioned way for now. We may want to move native Linux toolchains + # to here, too (TODO lat9nq?). + + set(LIBUSB_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/libusb") + set(LIBUSB_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libusb") + # Workarounds for MSYS/MinGW + if (MSYS) + # CMake on Windows passes `C:/`, but we need `/C/` or `/c/` to use `configure` + string(REPLACE ":/" "/" LIBUSB_SRC_DIR "${LIBUSB_SRC_DIR}") + set(LIBUSB_SRC_DIR "/${LIBUSB_SRC_DIR}") - if (NOT MINGW) - target_include_directories(usb BEFORE PRIVATE libusb/msvc) + # And now that we are using /C/ for srcdir but everything else is using C:/, we need to + # compile everything in the source directory, else `configure` won't think the build + # environment is sane. + set(LIBUSB_PREFIX "${LIBUSB_SRC_DIR}") endif() - # Works around other libraries providing their own definition of USB GUIDs (e.g. SDL2) - target_compile_definitions(usb PRIVATE "-DGUID_DEVINTERFACE_USB_DEVICE=(GUID){ 0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED}}") -else() -target_include_directories(usb - # turns out other projects also have "config.h", so make sure the - # LibUSB one comes first - BEFORE - - PUBLIC - libusb/libusb - - PRIVATE - "${CMAKE_CURRENT_BINARY_DIR}" -) -endif() - -if(WIN32 OR CYGWIN) - target_sources(usb PRIVATE - libusb/libusb/os/threads_windows.c - libusb/libusb/os/windows_winusb.c - libusb/libusb/os/windows_usbdk.c - libusb/libusb/os/windows_nt_common.c + set(LIBUSB_CONFIGURE "${LIBUSB_SRC_DIR}/configure") + set(LIBUSB_MAKEFILE "${LIBUSB_PREFIX}/Makefile") + set(LIBUSB_LIBRARY "${LIBUSB_PREFIX}/libusb/.libs/libusb-1.0.dll.a") + set(LIBUSB_SHARED_LIBRARY "${LIBUSB_PREFIX}/libusb/.libs/libusb-1.0.dll") + set(LIBUSB_SHARED_LIBRARY_DEST "${CMAKE_BINARY_DIR}/bin/libusb-1.0.dll") + + # Causes "externals/libusb/libusb/libusb/os/windows_winusb.c:1427:2: error: conversion to non-scalar type requested", so cannot statically link it for now. + # set(LIBUSB_CFLAGS "-DGUID_DEVINTERFACE_USB_DEVICE=\\(GUID\\){0xA5DCBF10,0x6530,0x11D2,{0x90,0x1F,0x00,0xC0,0x4F,0xB9,0x51,0xED}}") + + make_directory("${LIBUSB_PREFIX}") + + add_custom_command( + OUTPUT + "${LIBUSB_LIBRARY}" + COMMAND + make + WORKING_DIRECTORY + "${LIBUSB_PREFIX}" ) - set(OS_WINDOWS TRUE) -elseif(APPLE) - target_sources(usb PRIVATE - libusb/libusb/os/darwin_usb.c + + # We may use this path for other GNU toolchains, so put all of the MinGW-specific stuff here + if (MINGW) + set(LIBUSB_CONFIGURE_ARGS --host=x86_64-w64-mingw32 --build=x86_64-windows) + endif() + + add_custom_command( + OUTPUT + "${LIBUSB_MAKEFILE}" + COMMAND + # /bin/env + # CFLAGS="${LIBUSB_CFLAGS}" + /bin/sh "${LIBUSB_CONFIGURE}" + ${LIBUSB_CONFIGURE_ARGS} + --srcdir="${LIBUSB_SRC_DIR}" + WORKING_DIRECTORY + "${LIBUSB_PREFIX}" ) - find_library(COREFOUNDATION_LIBRARY CoreFoundation) - find_library(IOKIT_LIBRARY IOKit) - find_library(OBJC_LIBRARY objc) - target_link_libraries(usb PRIVATE - ${COREFOUNDATION_LIBRARY} - ${IOKIT_LIBRARY} - ${OBJC_LIBRARY} + + add_custom_command( + OUTPUT + "${LIBUSB_CONFIGURE}" + COMMAND + /bin/sh "${LIBUSB_SRC_DIR}/bootstrap.sh" + WORKING_DIRECTORY + "${LIBUSB_SRC_DIR}" ) - set(OS_DARWIN TRUE) -elseif(ANDROID) - target_sources(usb PRIVATE - libusb/libusb/os/linux_usbfs.c - libusb/libusb/os/linux_netlink.c + + add_custom_command( + OUTPUT + "${LIBUSB_SHARED_LIBRARY_DEST}" + COMMAND + /bin/cp "${LIBUSB_SHARED_LIBRARY}" "${LIBUSB_SHARED_LIBRARY_DEST}" ) - find_library(LOG_LIBRARY log) - target_link_libraries(usb PRIVATE ${LOG_LIBRARY}) - set(OS_LINUX TRUE) -elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux") - target_sources(usb PRIVATE - libusb/libusb/os/linux_usbfs.c + + add_custom_target(usb-bootstrap ALL DEPENDS "${LIBUSB_CONFIGURE}") + add_custom_target(usb-configure ALL DEPENDS "${LIBUSB_MAKEFILE}" usb-bootstrap) + add_custom_target(usb-build ALL DEPENDS "${LIBUSB_LIBRARY}" usb-configure) + # Workaround since static linking didn't work out -- We need to copy the DLL to the bin directory + add_custom_target(usb-copy ALL DEPENDS "${LIBUSB_SHARED_LIBRARY_DEST}" usb-build) + + # Make `usb` alias to LIBUSB_LIBRARY + add_library(usb INTERFACE) + target_link_libraries(usb INTERFACE "${LIBUSB_LIBRARY}") +else() # MINGW + # Ensure libusb compiles with UTF-8 encoding on MSVC + if(MSVC) + add_compile_options(/utf-8) + endif() + + add_library(usb STATIC EXCLUDE_FROM_ALL + libusb/libusb/core.c + libusb/libusb/core.c + libusb/libusb/descriptor.c + libusb/libusb/hotplug.c + libusb/libusb/io.c + libusb/libusb/strerror.c + libusb/libusb/sync.c ) - find_package(Libudev) - if(LIBUDEV_FOUND) - target_sources(usb PRIVATE - libusb/libusb/os/linux_udev.c + set_target_properties(usb PROPERTIES VERSION 1.0.24) + if(WIN32) + target_include_directories(usb + BEFORE + PUBLIC + libusb/libusb + + PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}" ) - target_link_libraries(usb PRIVATE "${LIBUDEV_LIBRARIES}") - target_include_directories(usb PRIVATE "${LIBUDEV_INCLUDE_DIR}") - set(HAVE_LIBUDEV TRUE) - set(USE_UDEV TRUE) + + if (NOT MINGW) + target_include_directories(usb BEFORE PRIVATE libusb/msvc) + endif() + + # Works around other libraries providing their own definition of USB GUIDs (e.g. SDL2) + target_compile_definitions(usb PRIVATE "-DGUID_DEVINTERFACE_USB_DEVICE=(GUID){ 0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED}}") else() + target_include_directories(usb + # turns out other projects also have "config.h", so make sure the + # LibUSB one comes first + BEFORE + + PUBLIC + libusb/libusb + + PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}" + ) + endif() + + if(WIN32 OR CYGWIN) + target_sources(usb PRIVATE + libusb/libusb/os/threads_windows.c + libusb/libusb/os/windows_winusb.c + libusb/libusb/os/windows_usbdk.c + libusb/libusb/os/windows_common.c + ) + set(OS_WINDOWS TRUE) + elseif(APPLE) + target_sources(usb PRIVATE + libusb/libusb/os/darwin_usb.c + ) + find_library(COREFOUNDATION_LIBRARY CoreFoundation) + find_library(IOKIT_LIBRARY IOKit) + find_library(OBJC_LIBRARY objc) + target_link_libraries(usb PRIVATE + ${COREFOUNDATION_LIBRARY} + ${IOKIT_LIBRARY} + ${OBJC_LIBRARY} + ) + set(OS_DARWIN TRUE) + elseif(ANDROID) target_sources(usb PRIVATE + libusb/libusb/os/linux_usbfs.c libusb/libusb/os/linux_netlink.c ) + find_library(LOG_LIBRARY log) + target_link_libraries(usb PRIVATE ${LOG_LIBRARY}) + set(OS_LINUX TRUE) + elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + target_sources(usb PRIVATE + libusb/libusb/os/linux_usbfs.c + ) + find_package(Libudev) + if(LIBUDEV_FOUND) + target_sources(usb PRIVATE + libusb/libusb/os/linux_udev.c + ) + target_link_libraries(usb PRIVATE "${LIBUDEV_LIBRARIES}") + target_include_directories(usb PRIVATE "${LIBUDEV_INCLUDE_DIR}") + set(HAVE_LIBUDEV TRUE) + set(USE_UDEV TRUE) + else() + target_sources(usb PRIVATE + libusb/libusb/os/linux_netlink.c + ) + endif() + set(OS_LINUX TRUE) + elseif(${CMAKE_SYSTEM_NAME} MATCHES "NetBSD") + target_sources(usb PRIVATE + libusb/libusb/os/netbsd_usb.c + ) + set(OS_NETBSD TRUE) + elseif(${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD") + target_sources(usb PRIVATE + libusb/libusb/os/openbsd_usb.c + ) + set(OS_OPENBSD TRUE) endif() - set(OS_LINUX TRUE) -elseif(${CMAKE_SYSTEM_NAME} MATCHES "NetBSD") - target_sources(usb PRIVATE - libusb/libusb/os/netbsd_usb.c - ) - set(OS_NETBSD TRUE) -elseif(${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD") - target_sources(usb PRIVATE - libusb/libusb/os/openbsd_usb.c - ) - set(OS_OPENBSD TRUE) -endif() -if(UNIX) - target_sources(usb PRIVATE - libusb/libusb/os/poll_posix.c - libusb/libusb/os/threads_posix.c - ) - find_package(Threads REQUIRED) - if(THREADS_HAVE_PTHREAD_ARG) - target_compile_options(usb PUBLIC "-pthread") + if(UNIX) + target_sources(usb PRIVATE + libusb/libusb/os/events_posix.c + libusb/libusb/os/threads_posix.c + ) + find_package(Threads REQUIRED) + if(THREADS_HAVE_PTHREAD_ARG) + target_compile_options(usb PUBLIC "-pthread") + endif() + if(CMAKE_THREAD_LIBS_INIT) + target_link_libraries(usb PRIVATE "${CMAKE_THREAD_LIBS_INIT}") + endif() + set(THREADS_POSIX TRUE) + elseif(WIN32) + target_sources(usb PRIVATE + libusb/libusb/os/events_windows.c + libusb/libusb/os/threads_windows.c + ) endif() - if(CMAKE_THREAD_LIBS_INIT) - target_link_libraries(usb PRIVATE "${CMAKE_THREAD_LIBS_INIT}") + + include(CheckFunctionExists) + include(CheckIncludeFiles) + include(CheckTypeSize) + check_include_files(asm/types.h HAVE_ASM_TYPES_H) + check_function_exists(gettimeofday HAVE_GETTIMEOFDAY) + check_include_files(linux/filter.h HAVE_LINUX_FILTER_H) + check_include_files(linux/netlink.h HAVE_LINUX_NETLINK_H) + check_include_files(poll.h HAVE_POLL_H) + check_include_files(signal.h HAVE_SIGNAL_H) + check_include_files(strings.h HAVE_STRINGS_H) + check_type_size("struct timespec" STRUCT_TIMESPEC) + check_function_exists(syslog HAVE_SYSLOG_FUNC) + check_include_files(syslog.h HAVE_SYSLOG_H) + check_include_files(sys/socket.h HAVE_SYS_SOCKET_H) + check_include_files(sys/time.h HAVE_SYS_TIME_H) + check_include_files(sys/types.h HAVE_SYS_TYPES_H) + + set(CMAKE_EXTRA_INCLUDE_FILES poll.h) + check_type_size("nfds_t" nfds_t) + unset(CMAKE_EXTRA_INCLUDE_FILES) + if(HAVE_NFDS_T) + set(POLL_NFDS_TYPE "nfds_t") + else() + set(POLL_NFDS_TYPE "unsigned int") endif() - set(THREADS_POSIX TRUE) -elseif(WIN32) - target_sources(usb PRIVATE - libusb/libusb/os/poll_windows.c - libusb/libusb/os/threads_windows.c - ) -endif() - -include(CheckFunctionExists) -include(CheckIncludeFiles) -include(CheckTypeSize) -check_include_files(asm/types.h HAVE_ASM_TYPES_H) -check_function_exists(gettimeofday HAVE_GETTIMEOFDAY) -check_include_files(linux/filter.h HAVE_LINUX_FILTER_H) -check_include_files(linux/netlink.h HAVE_LINUX_NETLINK_H) -check_include_files(poll.h HAVE_POLL_H) -check_include_files(signal.h HAVE_SIGNAL_H) -check_include_files(strings.h HAVE_STRINGS_H) -check_type_size("struct timespec" STRUCT_TIMESPEC) -check_function_exists(syslog HAVE_SYSLOG_FUNC) -check_include_files(syslog.h HAVE_SYSLOG_H) -check_include_files(sys/socket.h HAVE_SYS_SOCKET_H) -check_include_files(sys/time.h HAVE_SYS_TIME_H) -check_include_files(sys/types.h HAVE_SYS_TYPES_H) - -set(CMAKE_EXTRA_INCLUDE_FILES poll.h) -check_type_size("nfds_t" nfds_t) -unset(CMAKE_EXTRA_INCLUDE_FILES) -if(HAVE_NFDS_T) - set(POLL_NFDS_TYPE "nfds_t") -else() - set(POLL_NFDS_TYPE "unsigned int") -endif() - -check_include_files(sys/timerfd.h USBI_TIMERFD_AVAILABLE) - - -configure_file(config.h.in config.h) + + check_include_files(sys/timerfd.h USBI_TIMERFD_AVAILABLE) + + + configure_file(config.h.in config.h) +endif() # MINGW diff --git a/externals/libusb/libusb b/externals/libusb/libusb -Subproject e782eeb2514266f6738e242cdcb18e3ae1ed06f +Subproject c6a35c56016ea2ab2f19115d2ea1e85e0edae15 diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index eafb96b0b..7a4d9e354 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -154,6 +154,7 @@ add_library(common STATIC param_package.cpp param_package.h parent_of_member.h + point.h quaternion.h ring_buffer.h scm_rev.cpp diff --git a/src/common/fs/file.h b/src/common/fs/file.h index 209f9664b..50e270c5b 100644 --- a/src/common/fs/file.h +++ b/src/common/fs/file.h @@ -117,7 +117,7 @@ template <typename Path> } #endif -class IOFile final : NonCopyable { +class IOFile final { public: IOFile(); @@ -142,7 +142,10 @@ public: FileType type = FileType::BinaryFile, FileShareFlag flag = FileShareFlag::ShareReadOnly); - virtual ~IOFile(); + ~IOFile(); + + IOFile(const IOFile&) = delete; + IOFile& operator=(const IOFile&) = delete; IOFile(IOFile&& other) noexcept; IOFile& operator=(IOFile&& other) noexcept; @@ -441,8 +444,8 @@ public: private: std::filesystem::path file_path; - FileAccessMode file_access_mode; - FileType file_type; + FileAccessMode file_access_mode{}; + FileType file_type{}; std::FILE* file = nullptr; }; diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp index 8fd8620fd..9fffd816f 100644 --- a/src/common/page_table.cpp +++ b/src/common/page_table.cpp @@ -14,6 +14,7 @@ void PageTable::Resize(size_t address_space_width_in_bits, size_t page_size_in_b const size_t num_page_table_entries{1ULL << (address_space_width_in_bits - page_size_in_bits)}; pointers.resize(num_page_table_entries); backing_addr.resize(num_page_table_entries); + current_address_space_width_in_bits = address_space_width_in_bits; } } // namespace Common diff --git a/src/common/page_table.h b/src/common/page_table.h index 61c5552e0..e92b66b2b 100644 --- a/src/common/page_table.h +++ b/src/common/page_table.h @@ -98,6 +98,10 @@ struct PageTable { */ void Resize(size_t address_space_width_in_bits, size_t page_size_in_bits); + size_t GetAddressSpaceBits() const { + return current_address_space_width_in_bits; + } + /** * Vector of memory pointers backing each page. An entry can only be non-null if the * corresponding attribute element is of type `Memory`. @@ -105,6 +109,8 @@ struct PageTable { VirtualBuffer<PageInfo> pointers; VirtualBuffer<u64> backing_addr; + + size_t current_address_space_width_in_bits; }; } // namespace Common diff --git a/src/common/point.h b/src/common/point.h new file mode 100644 index 000000000..c0a52ad8d --- /dev/null +++ b/src/common/point.h @@ -0,0 +1,57 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <type_traits> + +namespace Common { + +// Represents a point within a 2D space. +template <typename T> +struct Point { + static_assert(std::is_arithmetic_v<T>, "T must be an arithmetic type!"); + + T x{}; + T y{}; + +#define ARITHMETIC_OP(op, compound_op) \ + friend constexpr Point operator op(const Point& lhs, const Point& rhs) noexcept { \ + return { \ + .x = static_cast<T>(lhs.x op rhs.x), \ + .y = static_cast<T>(lhs.y op rhs.y), \ + }; \ + } \ + friend constexpr Point operator op(const Point& lhs, T value) noexcept { \ + return { \ + .x = static_cast<T>(lhs.x op value), \ + .y = static_cast<T>(lhs.y op value), \ + }; \ + } \ + friend constexpr Point operator op(T value, const Point& rhs) noexcept { \ + return { \ + .x = static_cast<T>(value op rhs.x), \ + .y = static_cast<T>(value op rhs.y), \ + }; \ + } \ + friend constexpr Point& operator compound_op(Point& lhs, const Point& rhs) noexcept { \ + lhs.x = static_cast<T>(lhs.x op rhs.x); \ + lhs.y = static_cast<T>(lhs.y op rhs.y); \ + return lhs; \ + } \ + friend constexpr Point& operator compound_op(Point& lhs, T value) noexcept { \ + lhs.x = static_cast<T>(lhs.x op value); \ + lhs.y = static_cast<T>(lhs.y op value); \ + return lhs; \ + } + ARITHMETIC_OP(+, +=) + ARITHMETIC_OP(-, -=) + ARITHMETIC_OP(*, *=) + ARITHMETIC_OP(/, /=) +#undef ARITHMETIC_OP + + friend constexpr bool operator==(const Point&, const Point&) = default; +}; + +} // namespace Common diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 9a0151736..689e3ceb5 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -65,9 +65,6 @@ public: /// Step CPU by one instruction virtual void Step() = 0; - /// Exits execution from a callback, the callback must rewind the stack - virtual void ExceptionalExit() = 0; - /// Clear all instruction cache virtual void ClearInstructionCache() = 0; @@ -159,8 +156,6 @@ public: */ virtual void SetTPIDR_EL0(u64 value) = 0; - virtual void ChangeProcessorID(std::size_t new_core_id) = 0; - virtual void SaveContext(ThreadContext32& ctx) = 0; virtual void SaveContext(ThreadContext64& ctx) = 0; virtual void LoadContext(const ThreadContext32& ctx) = 0; diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 93d43e22e..cea7f0fb1 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -4,9 +4,9 @@ #include <cinttypes> #include <memory> -#include <dynarmic/A32/a32.h> -#include <dynarmic/A32/config.h> -#include <dynarmic/A32/context.h> +#include <dynarmic/interface/A32/a32.h> +#include <dynarmic/interface/A32/config.h> +#include <dynarmic/interface/A32/context.h> #include "common/assert.h" #include "common/logging/log.h" #include "common/page_table.h" @@ -24,45 +24,46 @@ namespace Core { class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { public: - explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_) : parent{parent_} {} + explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_) + : parent{parent_}, memory(parent.system.Memory()) {} u8 MemoryRead8(u32 vaddr) override { - return parent.system.Memory().Read8(vaddr); + return memory.Read8(vaddr); } u16 MemoryRead16(u32 vaddr) override { - return parent.system.Memory().Read16(vaddr); + return memory.Read16(vaddr); } u32 MemoryRead32(u32 vaddr) override { - return parent.system.Memory().Read32(vaddr); + return memory.Read32(vaddr); } u64 MemoryRead64(u32 vaddr) override { - return parent.system.Memory().Read64(vaddr); + return memory.Read64(vaddr); } void MemoryWrite8(u32 vaddr, u8 value) override { - parent.system.Memory().Write8(vaddr, value); + memory.Write8(vaddr, value); } void MemoryWrite16(u32 vaddr, u16 value) override { - parent.system.Memory().Write16(vaddr, value); + memory.Write16(vaddr, value); } void MemoryWrite32(u32 vaddr, u32 value) override { - parent.system.Memory().Write32(vaddr, value); + memory.Write32(vaddr, value); } void MemoryWrite64(u32 vaddr, u64 value) override { - parent.system.Memory().Write64(vaddr, value); + memory.Write64(vaddr, value); } bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override { - return parent.system.Memory().WriteExclusive8(vaddr, value, expected); + return memory.WriteExclusive8(vaddr, value, expected); } bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override { - return parent.system.Memory().WriteExclusive16(vaddr, value, expected); + return memory.WriteExclusive16(vaddr, value, expected); } bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override { - return parent.system.Memory().WriteExclusive32(vaddr, value, expected); + return memory.WriteExclusive32(vaddr, value, expected); } bool MemoryWriteExclusive64(u32 vaddr, u64 value, u64 expected) override { - return parent.system.Memory().WriteExclusive64(vaddr, value, expected); + return memory.WriteExclusive64(vaddr, value, expected); } void InterpreterFallback(u32 pc, std::size_t num_instructions) override { @@ -78,7 +79,9 @@ public: } void CallSVC(u32 swi) override { - Kernel::Svc::Call(parent.system, swi); + parent.svc_called = true; + parent.svc_swi = swi; + parent.jit->HaltExecution(); } void AddTicks(u64 ticks) override { @@ -110,6 +113,7 @@ public: } ARM_Dynarmic_32& parent; + Core::Memory::Memory& memory; std::size_t num_interpreted_instructions{}; static constexpr u64 minimum_run_cycles = 1000U; }; @@ -187,11 +191,17 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* } void ARM_Dynarmic_32::Run() { - jit->Run(); -} - -void ARM_Dynarmic_32::ExceptionalExit() { - jit->ExceptionalExit(); + while (true) { + jit->Run(); + if (!svc_called) { + break; + } + svc_called = false; + Kernel::Svc::Call(system, svc_swi); + if (shutdown) { + break; + } + } } void ARM_Dynarmic_32::Step() { @@ -255,10 +265,6 @@ void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) { cp15->uprw = static_cast<u32>(value); } -void ARM_Dynarmic_32::ChangeProcessorID(std::size_t new_core_id) { - jit->ChangeProcessorID(new_core_id); -} - void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) { Dynarmic::A32::Context context; jit->SaveContext(context); @@ -279,6 +285,7 @@ void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) { void ARM_Dynarmic_32::PrepareReschedule() { jit->HaltExecution(); + shutdown = true; } void ARM_Dynarmic_32::ClearInstructionCache() { diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index 42778c02c..063605b46 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h @@ -7,9 +7,9 @@ #include <memory> #include <unordered_map> -#include <dynarmic/A32/a32.h> -#include <dynarmic/A64/a64.h> -#include <dynarmic/exclusive_monitor.h> +#include <dynarmic/interface/A32/a32.h> +#include <dynarmic/interface/A64/a64.h> +#include <dynarmic/interface/exclusive_monitor.h> #include "common/common_types.h" #include "common/hash.h" #include "core/arm/arm_interface.h" @@ -42,13 +42,11 @@ public: u32 GetPSTATE() const override; void SetPSTATE(u32 pstate) override; void Run() override; - void ExceptionalExit() override; void Step() override; VAddr GetTlsAddress() const override; void SetTlsAddress(VAddr address) override; void SetTPIDR_EL0(u64 value) override; u64 GetTPIDR_EL0() const override; - void ChangeProcessorID(std::size_t new_core_id) override; bool IsInThumbMode() const { return (GetPSTATE() & 0x20) != 0; @@ -83,6 +81,12 @@ private: std::size_t core_index; DynarmicExclusiveMonitor& exclusive_monitor; std::shared_ptr<Dynarmic::A32::Jit> jit; + + // SVC callback + u32 svc_swi{}; + bool svc_called{}; + + bool shutdown{}; }; } // namespace Core diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 08fa85904..63193dcb1 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -4,8 +4,8 @@ #include <cinttypes> #include <memory> -#include <dynarmic/A64/a64.h> -#include <dynarmic/A64/config.h> +#include <dynarmic/interface/A64/a64.h> +#include <dynarmic/interface/A64/config.h> #include "common/assert.h" #include "common/logging/log.h" #include "common/page_table.h" @@ -27,57 +27,56 @@ using Vector = Dynarmic::A64::Vector; class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { public: - explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_) : parent{parent_} {} + explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_) + : parent{parent_}, memory(parent.system.Memory()) {} u8 MemoryRead8(u64 vaddr) override { - return parent.system.Memory().Read8(vaddr); + return memory.Read8(vaddr); } u16 MemoryRead16(u64 vaddr) override { - return parent.system.Memory().Read16(vaddr); + return memory.Read16(vaddr); } u32 MemoryRead32(u64 vaddr) override { - return parent.system.Memory().Read32(vaddr); + return memory.Read32(vaddr); } u64 MemoryRead64(u64 vaddr) override { - return parent.system.Memory().Read64(vaddr); + return memory.Read64(vaddr); } Vector MemoryRead128(u64 vaddr) override { - auto& memory = parent.system.Memory(); return {memory.Read64(vaddr), memory.Read64(vaddr + 8)}; } void MemoryWrite8(u64 vaddr, u8 value) override { - parent.system.Memory().Write8(vaddr, value); + memory.Write8(vaddr, value); } void MemoryWrite16(u64 vaddr, u16 value) override { - parent.system.Memory().Write16(vaddr, value); + memory.Write16(vaddr, value); } void MemoryWrite32(u64 vaddr, u32 value) override { - parent.system.Memory().Write32(vaddr, value); + memory.Write32(vaddr, value); } void MemoryWrite64(u64 vaddr, u64 value) override { - parent.system.Memory().Write64(vaddr, value); + memory.Write64(vaddr, value); } void MemoryWrite128(u64 vaddr, Vector value) override { - auto& memory = parent.system.Memory(); memory.Write64(vaddr, value[0]); memory.Write64(vaddr + 8, value[1]); } bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override { - return parent.system.Memory().WriteExclusive8(vaddr, value, expected); + return memory.WriteExclusive8(vaddr, value, expected); } bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override { - return parent.system.Memory().WriteExclusive16(vaddr, value, expected); + return memory.WriteExclusive16(vaddr, value, expected); } bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, std::uint32_t expected) override { - return parent.system.Memory().WriteExclusive32(vaddr, value, expected); + return memory.WriteExclusive32(vaddr, value, expected); } bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, std::uint64_t expected) override { - return parent.system.Memory().WriteExclusive64(vaddr, value, expected); + return memory.WriteExclusive64(vaddr, value, expected); } bool MemoryWriteExclusive128(u64 vaddr, Vector value, Vector expected) override { - return parent.system.Memory().WriteExclusive128(vaddr, value, expected); + return memory.WriteExclusive128(vaddr, value, expected); } void InterpreterFallback(u64 pc, std::size_t num_instructions) override { @@ -102,7 +101,9 @@ public: } void CallSVC(u32 swi) override { - Kernel::Svc::Call(parent.system, swi); + parent.svc_called = true; + parent.svc_swi = swi; + parent.jit->HaltExecution(); } void AddTicks(u64 ticks) override { @@ -137,6 +138,7 @@ public: } ARM_Dynarmic_64& parent; + Core::Memory::Memory& memory; u64 tpidrro_el0 = 0; u64 tpidr_el0 = 0; static constexpr u64 minimum_run_cycles = 1000U; @@ -227,11 +229,17 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* } void ARM_Dynarmic_64::Run() { - jit->Run(); -} - -void ARM_Dynarmic_64::ExceptionalExit() { - jit->ExceptionalExit(); + while (true) { + jit->Run(); + if (!svc_called) { + break; + } + svc_called = false; + Kernel::Svc::Call(system, svc_swi); + if (shutdown) { + break; + } + } } void ARM_Dynarmic_64::Step() { @@ -296,10 +304,6 @@ void ARM_Dynarmic_64::SetTPIDR_EL0(u64 value) { cb->tpidr_el0 = value; } -void ARM_Dynarmic_64::ChangeProcessorID(std::size_t new_core_id) { - jit->ChangeProcessorID(new_core_id); -} - void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) { ctx.cpu_registers = jit->GetRegisters(); ctx.sp = jit->GetSP(); @@ -324,6 +328,7 @@ void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) { void ARM_Dynarmic_64::PrepareReschedule() { jit->HaltExecution(); + shutdown = true; } void ARM_Dynarmic_64::ClearInstructionCache() { diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index b81fbcc66..0c4e46c64 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h @@ -7,7 +7,7 @@ #include <memory> #include <unordered_map> -#include <dynarmic/A64/a64.h> +#include <dynarmic/interface/A64/a64.h> #include "common/common_types.h" #include "common/hash.h" #include "core/arm/arm_interface.h" @@ -40,12 +40,10 @@ public: void SetPSTATE(u32 pstate) override; void Run() override; void Step() override; - void ExceptionalExit() override; VAddr GetTlsAddress() const override; void SetTlsAddress(VAddr address) override; void SetTPIDR_EL0(u64 value) override; u64 GetTPIDR_EL0() const override; - void ChangeProcessorID(std::size_t new_core_id) override; void SaveContext(ThreadContext32& ctx) override {} void SaveContext(ThreadContext64& ctx) override; @@ -76,6 +74,12 @@ private: DynarmicExclusiveMonitor& exclusive_monitor; std::shared_ptr<Dynarmic::A64::Jit> jit; + + // SVC callback + u32 svc_swi{}; + bool svc_called{}; + + bool shutdown{}; }; } // namespace Core diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.h b/src/core/arm/dynarmic/arm_dynarmic_cp15.h index 8597beddf..7c7ede79e 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_cp15.h +++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.h @@ -7,7 +7,7 @@ #include <memory> #include <optional> -#include <dynarmic/A32/coprocessor.h> +#include <dynarmic/interface/A32/coprocessor.h> #include "common/common_types.h" namespace Core { diff --git a/src/core/arm/dynarmic/arm_exclusive_monitor.h b/src/core/arm/dynarmic/arm_exclusive_monitor.h index f9f056a59..73d41f223 100644 --- a/src/core/arm/dynarmic/arm_exclusive_monitor.h +++ b/src/core/arm/dynarmic/arm_exclusive_monitor.h @@ -7,7 +7,7 @@ #include <memory> #include <unordered_map> -#include <dynarmic/exclusive_monitor.h> +#include <dynarmic/interface/exclusive_monitor.h> #include "common/common_types.h" #include "core/arm/dynarmic/arm_dynarmic_32.h" diff --git a/src/core/hle/kernel/k_class_token.cpp b/src/core/hle/kernel/k_class_token.cpp index beb8a2a05..0be0027be 100644 --- a/src/core/hle/kernel/k_class_token.cpp +++ b/src/core/hle/kernel/k_class_token.cpp @@ -84,50 +84,43 @@ static_assert(ClassToken<KTransferMemory> == ((0b10010001 << 8) | ClassToken<KAu // Ensure that the token hierarchy reflects the class hierarchy. // Base classes. -static_assert(!std::is_final<KSynchronizationObject>::value && - std::is_base_of<KAutoObject, KSynchronizationObject>::value); -static_assert(!std::is_final<KReadableEvent>::value && - std::is_base_of<KSynchronizationObject, KReadableEvent>::value); +static_assert(!std::is_final_v<KSynchronizationObject> && + std::is_base_of_v<KAutoObject, KSynchronizationObject>); +static_assert(!std::is_final_v<KReadableEvent> && + std::is_base_of_v<KSynchronizationObject, KReadableEvent>); // Final classes -// static_assert(std::is_final<KInterruptEvent>::value && -// std::is_base_of<KReadableEvent, KInterruptEvent>::value); -// static_assert(std::is_final<KDebug>::value && -// std::is_base_of<KSynchronizationObject, KDebug>::value); -static_assert(std::is_final<KThread>::value && - std::is_base_of<KSynchronizationObject, KThread>::value); -static_assert(std::is_final<KServerPort>::value && - std::is_base_of<KSynchronizationObject, KServerPort>::value); -static_assert(std::is_final<KServerSession>::value && - std::is_base_of<KSynchronizationObject, KServerSession>::value); -static_assert(std::is_final<KClientPort>::value && - std::is_base_of<KSynchronizationObject, KClientPort>::value); -static_assert(std::is_final<KClientSession>::value && - std::is_base_of<KAutoObject, KClientSession>::value); -static_assert(std::is_final<KProcess>::value && - std::is_base_of<KSynchronizationObject, KProcess>::value); -static_assert(std::is_final<KResourceLimit>::value && - std::is_base_of<KAutoObject, KResourceLimit>::value); -// static_assert(std::is_final<KLightSession>::value && -// std::is_base_of<KAutoObject, KLightSession>::value); -static_assert(std::is_final<KPort>::value && std::is_base_of<KAutoObject, KPort>::value); -static_assert(std::is_final<KSession>::value && std::is_base_of<KAutoObject, KSession>::value); -static_assert(std::is_final<KSharedMemory>::value && - std::is_base_of<KAutoObject, KSharedMemory>::value); -static_assert(std::is_final<KEvent>::value && std::is_base_of<KAutoObject, KEvent>::value); -static_assert(std::is_final<KWritableEvent>::value && - std::is_base_of<KAutoObject, KWritableEvent>::value); -// static_assert(std::is_final<KLightClientSession>::value && -// std::is_base_of<KAutoObject, KLightClientSession>::value); -// static_assert(std::is_final<KLightServerSession>::value && -// std::is_base_of<KAutoObject, KLightServerSession>::value); -static_assert(std::is_final<KTransferMemory>::value && - std::is_base_of<KAutoObject, KTransferMemory>::value); -// static_assert(std::is_final<KDeviceAddressSpace>::value && -// std::is_base_of<KAutoObject, KDeviceAddressSpace>::value); -// static_assert(std::is_final<KSessionRequest>::value && -// std::is_base_of<KAutoObject, KSessionRequest>::value); -// static_assert(std::is_final<KCodeMemory>::value && -// std::is_base_of<KAutoObject, KCodeMemory>::value); +// static_assert(std::is_final_v<KInterruptEvent> && +// std::is_base_of_v<KReadableEvent, KInterruptEvent>); +// static_assert(std::is_final_v<KDebug> && +// std::is_base_of_v<KSynchronizationObject, KDebug>); +static_assert(std::is_final_v<KThread> && std::is_base_of_v<KSynchronizationObject, KThread>); +static_assert(std::is_final_v<KServerPort> && + std::is_base_of_v<KSynchronizationObject, KServerPort>); +static_assert(std::is_final_v<KServerSession> && + std::is_base_of_v<KSynchronizationObject, KServerSession>); +static_assert(std::is_final_v<KClientPort> && + std::is_base_of_v<KSynchronizationObject, KClientPort>); +static_assert(std::is_final_v<KClientSession> && std::is_base_of_v<KAutoObject, KClientSession>); +static_assert(std::is_final_v<KProcess> && std::is_base_of_v<KSynchronizationObject, KProcess>); +static_assert(std::is_final_v<KResourceLimit> && std::is_base_of_v<KAutoObject, KResourceLimit>); +// static_assert(std::is_final_v<KLightSession> && +// std::is_base_of_v<KAutoObject, KLightSession>); +static_assert(std::is_final_v<KPort> && std::is_base_of_v<KAutoObject, KPort>); +static_assert(std::is_final_v<KSession> && std::is_base_of_v<KAutoObject, KSession>); +static_assert(std::is_final_v<KSharedMemory> && std::is_base_of_v<KAutoObject, KSharedMemory>); +static_assert(std::is_final_v<KEvent> && std::is_base_of_v<KAutoObject, KEvent>); +static_assert(std::is_final_v<KWritableEvent> && std::is_base_of_v<KAutoObject, KWritableEvent>); +// static_assert(std::is_final_v<KLightClientSession> && +// std::is_base_of_v<KAutoObject, KLightClientSession>); +// static_assert(std::is_final_v<KLightServerSession> && +// std::is_base_of_v<KAutoObject, KLightServerSession>); +static_assert(std::is_final_v<KTransferMemory> && std::is_base_of_v<KAutoObject, KTransferMemory>); +// static_assert(std::is_final_v<KDeviceAddressSpace> && +// std::is_base_of_v<KAutoObject, KDeviceAddressSpace>); +// static_assert(std::is_final_v<KSessionRequest> && +// std::is_base_of_v<KAutoObject, KSessionRequest>); +// static_assert(std::is_final_v<KCodeMemory> && +// std::is_base_of_v<KAutoObject, KCodeMemory>); } // namespace Kernel diff --git a/src/core/hle/kernel/k_client_port.h b/src/core/hle/kernel/k_client_port.h index 8501156e8..f2fff3b01 100644 --- a/src/core/hle/kernel/k_client_port.h +++ b/src/core/hle/kernel/k_client_port.h @@ -22,7 +22,7 @@ class KClientPort final : public KSynchronizationObject { public: explicit KClientPort(KernelCore& kernel_); - virtual ~KClientPort() override; + ~KClientPort() override; void Initialize(KPort* parent_, s32 max_sessions_, std::string&& name_); void OnSessionFinalized(); @@ -49,8 +49,8 @@ public: bool IsServerClosed() const; // Overridden virtual functions. - virtual void Destroy() override; - virtual bool IsSignaled() const override; + void Destroy() override; + bool IsSignaled() const override; ResultCode CreateSession(KClientSession** out); diff --git a/src/core/hle/kernel/k_client_session.h b/src/core/hle/kernel/k_client_session.h index 720a8c243..b11d5b4e3 100644 --- a/src/core/hle/kernel/k_client_session.h +++ b/src/core/hle/kernel/k_client_session.h @@ -34,7 +34,7 @@ class KClientSession final public: explicit KClientSession(KernelCore& kernel_); - virtual ~KClientSession(); + ~KClientSession() override; void Initialize(KSession* parent_, std::string&& name_) { // Set member variables. @@ -42,7 +42,7 @@ public: name = std::move(name_); } - virtual void Destroy() override; + void Destroy() override; static void PostDestroy([[maybe_unused]] uintptr_t arg) {} KSession* GetParent() const { diff --git a/src/core/hle/kernel/k_event.h b/src/core/hle/kernel/k_event.h index 9a59ffb70..3d3ec99e2 100644 --- a/src/core/hle/kernel/k_event.h +++ b/src/core/hle/kernel/k_event.h @@ -20,23 +20,21 @@ class KEvent final : public KAutoObjectWithSlabHeapAndContainer<KEvent, KAutoObj public: explicit KEvent(KernelCore& kernel_); - virtual ~KEvent(); + ~KEvent() override; void Initialize(std::string&& name); - virtual void Finalize() override; + void Finalize() override; - virtual bool IsInitialized() const override { + bool IsInitialized() const override { return initialized; } - virtual uintptr_t GetPostDestroyArgument() const override { + uintptr_t GetPostDestroyArgument() const override { return reinterpret_cast<uintptr_t>(owner); } - static void PostDestroy(uintptr_t arg); - - virtual KProcess* GetOwner() const override { + KProcess* GetOwner() const override { return owner; } @@ -48,6 +46,8 @@ public: return writable_event; } + static void PostDestroy(uintptr_t arg); + private: KReadableEvent readable_event; KWritableEvent writable_event; diff --git a/src/core/hle/kernel/k_port.h b/src/core/hle/kernel/k_port.h index 960f1f3a3..4018ea2df 100644 --- a/src/core/hle/kernel/k_port.h +++ b/src/core/hle/kernel/k_port.h @@ -22,7 +22,7 @@ class KPort final : public KAutoObjectWithSlabHeapAndContainer<KPort, KAutoObjec public: explicit KPort(KernelCore& kernel_); - virtual ~KPort(); + ~KPort() override; static void PostDestroy([[maybe_unused]] uintptr_t arg) {} @@ -59,7 +59,6 @@ private: ServerClosed = 3, }; -private: KServerPort server; KClientPort client; State state{State::Invalid}; diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index 123d71cd3..c0656b9af 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h @@ -331,19 +331,19 @@ public: void LoadModule(CodeSet code_set, VAddr base_addr); - virtual bool IsInitialized() const override { + bool IsInitialized() const override { return is_initialized; } static void PostDestroy([[maybe_unused]] uintptr_t arg) {} - virtual void Finalize(); + void Finalize() override; - virtual u64 GetId() const override final { + u64 GetId() const override { return GetProcessID(); } - virtual bool IsSignaled() const override; + bool IsSignaled() const override; void PinCurrentThread(); void UnpinCurrentThread(); diff --git a/src/core/hle/kernel/k_readable_event.h b/src/core/hle/kernel/k_readable_event.h index 33cd1dd3e..b2850ac7b 100644 --- a/src/core/hle/kernel/k_readable_event.h +++ b/src/core/hle/kernel/k_readable_event.h @@ -31,8 +31,8 @@ public: return parent; } - virtual bool IsSignaled() const override; - virtual void Destroy() override; + bool IsSignaled() const override; + void Destroy() override; ResultCode Signal(); ResultCode Clear(); diff --git a/src/core/hle/kernel/k_resource_limit.h b/src/core/hle/kernel/k_resource_limit.h index 0debbbb51..fab6005ff 100644 --- a/src/core/hle/kernel/k_resource_limit.h +++ b/src/core/hle/kernel/k_resource_limit.h @@ -37,10 +37,10 @@ class KResourceLimit final public: explicit KResourceLimit(KernelCore& kernel_); - virtual ~KResourceLimit(); + ~KResourceLimit() override; void Initialize(const Core::Timing::CoreTiming* core_timing_); - virtual void Finalize() override; + void Finalize() override; s64 GetLimitValue(LimitableResource which) const; s64 GetCurrentValue(LimitableResource which) const; diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index 2f82fbcd6..6a7d80d03 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp @@ -659,7 +659,6 @@ void KScheduler::Unload(KThread* thread) { if (thread) { if (thread->IsCallingSvc()) { - system.ArmInterface(core_id).ExceptionalExit(); thread->ClearIsCallingSvc(); } if (!thread->IsTerminationRequested()) { diff --git a/src/core/hle/kernel/k_server_port.h b/src/core/hle/kernel/k_server_port.h index d1a757ec3..55481d63f 100644 --- a/src/core/hle/kernel/k_server_port.h +++ b/src/core/hle/kernel/k_server_port.h @@ -25,12 +25,9 @@ class SessionRequestHandler; class KServerPort final : public KSynchronizationObject { KERNEL_AUTOOBJECT_TRAITS(KServerPort, KSynchronizationObject); -private: - using SessionList = boost::intrusive::list<KServerSession>; - public: explicit KServerPort(KernelCore& kernel_); - virtual ~KServerPort() override; + ~KServerPort() override; void Initialize(KPort* parent_, std::string&& name_); @@ -63,13 +60,14 @@ public: bool IsLight() const; // Overridden virtual functions. - virtual void Destroy() override; - virtual bool IsSignaled() const override; + void Destroy() override; + bool IsSignaled() const override; private: + using SessionList = boost::intrusive::list<KServerSession>; + void CleanupSessions(); -private: SessionList session_list; SessionRequestHandlerPtr session_handler; KPort* parent{}; diff --git a/src/core/hle/kernel/k_server_session.h b/src/core/hle/kernel/k_server_session.h index dd4de2904..27b757ad2 100644 --- a/src/core/hle/kernel/k_server_session.h +++ b/src/core/hle/kernel/k_server_session.h @@ -42,9 +42,9 @@ class KServerSession final : public KSynchronizationObject, public: explicit KServerSession(KernelCore& kernel_); - virtual ~KServerSession() override; + ~KServerSession() override; - virtual void Destroy() override; + void Destroy() override; void Initialize(KSession* parent_, std::string&& name_); @@ -56,7 +56,7 @@ public: return parent; } - virtual bool IsSignaled() const override; + bool IsSignaled() const override; void OnClientClosed(); diff --git a/src/core/hle/kernel/k_session.h b/src/core/hle/kernel/k_session.h index a981fd1f6..4ddd080d2 100644 --- a/src/core/hle/kernel/k_session.h +++ b/src/core/hle/kernel/k_session.h @@ -18,17 +18,17 @@ class KSession final : public KAutoObjectWithSlabHeapAndContainer<KSession, KAut public: explicit KSession(KernelCore& kernel_); - virtual ~KSession() override; + ~KSession() override; void Initialize(KClientPort* port_, const std::string& name_); - virtual void Finalize() override; + void Finalize() override; - virtual bool IsInitialized() const override { + bool IsInitialized() const override { return initialized; } - virtual uintptr_t GetPostDestroyArgument() const override { + uintptr_t GetPostDestroyArgument() const override { return reinterpret_cast<uintptr_t>(process); } @@ -78,7 +78,6 @@ private: ServerClosed = 3, }; -private: void SetState(State state) { atomic_state = static_cast<u8>(state); } @@ -87,7 +86,6 @@ private: return static_cast<State>(atomic_state.load(std::memory_order_relaxed)); } -private: KServerSession server; KClientSession client; std::atomic<std::underlying_type_t<State>> atomic_state{ diff --git a/src/core/hle/kernel/k_shared_memory.h b/src/core/hle/kernel/k_shared_memory.h index 553a56327..e9815f90b 100644 --- a/src/core/hle/kernel/k_shared_memory.h +++ b/src/core/hle/kernel/k_shared_memory.h @@ -68,9 +68,9 @@ public: return device_memory->GetPointer(physical_address + offset); } - virtual void Finalize() override; + void Finalize() override; - virtual bool IsInitialized() const override { + bool IsInitialized() const override { return is_initialized; } static void PostDestroy([[maybe_unused]] uintptr_t arg) {} diff --git a/src/core/hle/kernel/k_slab_heap.h b/src/core/hle/kernel/k_slab_heap.h index 81d472a3e..0ad74b0a0 100644 --- a/src/core/hle/kernel/k_slab_heap.h +++ b/src/core/hle/kernel/k_slab_heap.h @@ -4,34 +4,213 @@ #pragma once +#include <atomic> + +#include "common/assert.h" +#include "common/common_types.h" + namespace Kernel { class KernelCore; -/// This is a placeholder class to manage slab heaps for kernel objects. For now, we just allocate -/// these with new/delete, but this can be re-implemented later to allocate these in emulated -/// memory. +namespace impl { + +class KSlabHeapImpl final : NonCopyable { +public: + struct Node { + Node* next{}; + }; + + constexpr KSlabHeapImpl() = default; + + void Initialize(std::size_t size) { + ASSERT(head == nullptr); + obj_size = size; + } + + constexpr std::size_t GetObjectSize() const { + return obj_size; + } + + Node* GetHead() const { + return head; + } + + void* Allocate() { + Node* ret = head.load(); + + do { + if (ret == nullptr) { + break; + } + } while (!head.compare_exchange_weak(ret, ret->next)); + + return ret; + } + + void Free(void* obj) { + Node* node = static_cast<Node*>(obj); + + Node* cur_head = head.load(); + do { + node->next = cur_head; + } while (!head.compare_exchange_weak(cur_head, node)); + } + +private: + std::atomic<Node*> head{}; + std::size_t obj_size{}; +}; + +} // namespace impl + +class KSlabHeapBase : NonCopyable { +public: + constexpr KSlabHeapBase() = default; + + constexpr bool Contains(uintptr_t addr) const { + return start <= addr && addr < end; + } + + constexpr std::size_t GetSlabHeapSize() const { + return (end - start) / GetObjectSize(); + } + + constexpr std::size_t GetObjectSize() const { + return impl.GetObjectSize(); + } + + constexpr uintptr_t GetSlabHeapAddress() const { + return start; + } + + std::size_t GetObjectIndexImpl(const void* obj) const { + return (reinterpret_cast<uintptr_t>(obj) - start) / GetObjectSize(); + } + + std::size_t GetPeakIndex() const { + return GetObjectIndexImpl(reinterpret_cast<const void*>(peak)); + } + + void* AllocateImpl() { + return impl.Allocate(); + } + + void FreeImpl(void* obj) { + // Don't allow freeing an object that wasn't allocated from this heap + ASSERT(Contains(reinterpret_cast<uintptr_t>(obj))); + + impl.Free(obj); + } + + void InitializeImpl(std::size_t obj_size, void* memory, std::size_t memory_size) { + // Ensure we don't initialize a slab using null memory + ASSERT(memory != nullptr); + + // Initialize the base allocator + impl.Initialize(obj_size); + + // Set our tracking variables + const std::size_t num_obj = (memory_size / obj_size); + start = reinterpret_cast<uintptr_t>(memory); + end = start + num_obj * obj_size; + peak = start; + + // Free the objects + u8* cur = reinterpret_cast<u8*>(end); + + for (std::size_t i{}; i < num_obj; i++) { + cur -= obj_size; + impl.Free(cur); + } + } + +private: + using Impl = impl::KSlabHeapImpl; + + Impl impl; + uintptr_t peak{}; + uintptr_t start{}; + uintptr_t end{}; +}; template <typename T> -class KSlabHeap final : NonCopyable { +class KSlabHeap final : public KSlabHeapBase { public: - KSlabHeap() = default; + enum class AllocationType { + Host, + Guest, + }; - void Initialize([[maybe_unused]] void* memory, [[maybe_unused]] std::size_t memory_size) { - // Placeholder that should initialize the backing slab heap implementation. + explicit constexpr KSlabHeap(AllocationType allocation_type_ = AllocationType::Host) + : KSlabHeapBase(), allocation_type{allocation_type_} {} + + void Initialize(void* memory, std::size_t memory_size) { + if (allocation_type == AllocationType::Guest) { + InitializeImpl(sizeof(T), memory, memory_size); + } } T* Allocate() { - return new T(); + switch (allocation_type) { + case AllocationType::Host: + // Fallback for cases where we do not yet support allocating guest memory from the slab + // heap, such as for kernel memory regions. + return new T; + + case AllocationType::Guest: + T* obj = static_cast<T*>(AllocateImpl()); + if (obj != nullptr) { + new (obj) T(); + } + return obj; + } + + UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type); + return nullptr; } T* AllocateWithKernel(KernelCore& kernel) { - return new T(kernel); + switch (allocation_type) { + case AllocationType::Host: + // Fallback for cases where we do not yet support allocating guest memory from the slab + // heap, such as for kernel memory regions. + return new T(kernel); + + case AllocationType::Guest: + T* obj = static_cast<T*>(AllocateImpl()); + if (obj != nullptr) { + new (obj) T(kernel); + } + return obj; + } + + UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type); + return nullptr; } void Free(T* obj) { - delete obj; + switch (allocation_type) { + case AllocationType::Host: + // Fallback for cases where we do not yet support allocating guest memory from the slab + // heap, such as for kernel memory regions. + delete obj; + return; + + case AllocationType::Guest: + FreeImpl(obj); + return; + } + + UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type); } + + constexpr std::size_t GetObjectIndex(const T* obj) const { + return GetObjectIndexImpl(obj); + } + +private: + const AllocationType allocation_type; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_synchronization_object.h b/src/core/hle/kernel/k_synchronization_object.h index a41dd1220..3d4ce1fbc 100644 --- a/src/core/hle/kernel/k_synchronization_object.h +++ b/src/core/hle/kernel/k_synchronization_object.h @@ -29,7 +29,7 @@ public: KSynchronizationObject** objects, const s32 num_objects, s64 timeout); - virtual void Finalize() override; + void Finalize() override; [[nodiscard]] virtual bool IsSignaled() const = 0; @@ -37,7 +37,7 @@ public: protected: explicit KSynchronizationObject(KernelCore& kernel); - virtual ~KSynchronizationObject(); + ~KSynchronizationObject() override; virtual void OnFinalizeSynchronizationObject() {} diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index e3f08f256..3cf43d290 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -168,13 +168,13 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s std::memset(static_cast<void*>(std::addressof(GetStackParameters())), 0, sizeof(StackParameters)); - // Setup the TLS, if needed. - if (type == ThreadType::User) { - tls_address = owner->CreateTLSRegion(); - } - // Set parent, if relevant. if (owner != nullptr) { + // Setup the TLS, if needed. + if (type == ThreadType::User) { + tls_address = owner->CreateTLSRegion(); + } + parent = owner; parent->Open(); parent->IncrementThreadCount(); diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index 4abfc2b49..01eebb165 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -358,21 +358,21 @@ public: return termination_requested || GetRawState() == ThreadState::Terminated; } - [[nodiscard]] virtual u64 GetId() const override final { + [[nodiscard]] u64 GetId() const override { return this->GetThreadID(); } - [[nodiscard]] virtual bool IsInitialized() const override { + [[nodiscard]] bool IsInitialized() const override { return initialized; } - [[nodiscard]] virtual uintptr_t GetPostDestroyArgument() const override { + [[nodiscard]] uintptr_t GetPostDestroyArgument() const override { return reinterpret_cast<uintptr_t>(parent) | (resource_limit_release_hint ? 1 : 0); } - virtual void Finalize() override; + void Finalize() override; - [[nodiscard]] virtual bool IsSignaled() const override; + [[nodiscard]] bool IsSignaled() const override; static void PostDestroy(uintptr_t arg); diff --git a/src/core/hle/kernel/k_transfer_memory.h b/src/core/hle/kernel/k_transfer_memory.h index c2d0f1eaf..31029a5c2 100644 --- a/src/core/hle/kernel/k_transfer_memory.h +++ b/src/core/hle/kernel/k_transfer_memory.h @@ -27,23 +27,23 @@ class KTransferMemory final public: explicit KTransferMemory(KernelCore& kernel_); - virtual ~KTransferMemory() override; + ~KTransferMemory() override; ResultCode Initialize(VAddr address_, std::size_t size_, Svc::MemoryPermission owner_perm_); - virtual void Finalize() override; + void Finalize() override; - virtual bool IsInitialized() const override { + bool IsInitialized() const override { return is_initialized; } - virtual uintptr_t GetPostDestroyArgument() const override { + uintptr_t GetPostDestroyArgument() const override { return reinterpret_cast<uintptr_t>(owner); } static void PostDestroy(uintptr_t arg); - KProcess* GetOwner() const { + KProcess* GetOwner() const override { return owner; } diff --git a/src/core/hle/kernel/k_writable_event.h b/src/core/hle/kernel/k_writable_event.h index 607b0eadb..858d982c4 100644 --- a/src/core/hle/kernel/k_writable_event.h +++ b/src/core/hle/kernel/k_writable_event.h @@ -21,7 +21,7 @@ public: explicit KWritableEvent(KernelCore& kernel_); ~KWritableEvent() override; - virtual void Destroy() override; + void Destroy() override; static void PostDestroy([[maybe_unused]] uintptr_t arg) {} diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 8b55df82e..0ffb78d51 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -258,7 +258,7 @@ struct KernelCore::Impl { KAutoObject::Create(thread.get()); ASSERT(KThread::InitializeDummyThread(thread.get()).IsSuccess()); thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId())); - return std::move(thread); + return thread; }; thread_local auto thread = make_thread(); @@ -620,7 +620,8 @@ struct KernelCore::Impl { void InitializePageSlab() { // Allocate slab heaps - user_slab_heap_pages = std::make_unique<KSlabHeap<Page>>(); + user_slab_heap_pages = + std::make_unique<KSlabHeap<Page>>(KSlabHeap<Page>::AllocationType::Guest); // TODO(ameerj): This should be derived, not hardcoded within the kernel constexpr u64 user_slab_heap_size{0x3de000}; diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index b05a5da04..b1a81810b 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -273,8 +273,13 @@ void SoftwareKeyboard::ProcessTextCheck() { std::memcpy(&swkbd_text_check, text_check_data.data(), sizeof(SwkbdTextCheck)); - std::u16string text_check_message = Common::UTF16StringFromFixedZeroTerminatedBuffer( - swkbd_text_check.text_check_message.data(), swkbd_text_check.text_check_message.size()); + std::u16string text_check_message = + swkbd_text_check.text_check_result == SwkbdTextCheckResult::Failure || + swkbd_text_check.text_check_result == SwkbdTextCheckResult::Confirm + ? Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_text_check.text_check_message.data(), + swkbd_text_check.text_check_message.size()) + : u""; LOG_INFO(Service_AM, "\nTextCheckResult: {}\nTextCheckMessage: {}", GetTextCheckResultName(swkbd_text_check.text_check_result), @@ -285,10 +290,10 @@ void SoftwareKeyboard::ProcessTextCheck() { SubmitNormalOutputAndExit(SwkbdResult::Ok, current_text); break; case SwkbdTextCheckResult::Failure: - ShowTextCheckDialog(SwkbdTextCheckResult::Failure, text_check_message); + ShowTextCheckDialog(SwkbdTextCheckResult::Failure, std::move(text_check_message)); break; case SwkbdTextCheckResult::Confirm: - ShowTextCheckDialog(SwkbdTextCheckResult::Confirm, text_check_message); + ShowTextCheckDialog(SwkbdTextCheckResult::Confirm, std::move(text_check_message)); break; case SwkbdTextCheckResult::Silent: default: @@ -482,7 +487,7 @@ void SoftwareKeyboard::InitializeFrontendKeyboard() { max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; Core::Frontend::KeyboardInitializeParameters initialize_parameters{ - .ok_text{ok_text}, + .ok_text{std::move(ok_text)}, .header_text{}, .sub_text{}, .guide_text{}, @@ -558,10 +563,10 @@ void SoftwareKeyboard::InitializeFrontendKeyboard() { : false; Core::Frontend::KeyboardInitializeParameters initialize_parameters{ - .ok_text{ok_text}, - .header_text{header_text}, - .sub_text{sub_text}, - .guide_text{guide_text}, + .ok_text{std::move(ok_text)}, + .header_text{std::move(header_text)}, + .sub_text{std::move(sub_text)}, + .guide_text{std::move(guide_text)}, .initial_text{initial_text}, .max_text_length{max_text_length}, .min_text_length{min_text_length}, @@ -590,7 +595,7 @@ void SoftwareKeyboard::ShowNormalKeyboard() { void SoftwareKeyboard::ShowTextCheckDialog(SwkbdTextCheckResult text_check_result, std::u16string text_check_message) { - frontend.ShowTextCheckDialog(text_check_result, text_check_message); + frontend.ShowTextCheckDialog(text_check_result, std::move(text_check_message)); } void SoftwareKeyboard::ShowInlineKeyboard() { diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp index d311f754b..764abb5b6 100644 --- a/src/core/hle/service/hid/controllers/gesture.cpp +++ b/src/core/hle/service/hid/controllers/gesture.cpp @@ -91,8 +91,7 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture, // Update if coordinates change for (size_t id = 0; id < MAX_POINTS; id++) { - if (gesture.points[id].x != last_gesture.points[id].x || - gesture.points[id].y != last_gesture.points[id].y) { + if (gesture.points[id] != last_gesture.points[id]) { return true; } } @@ -124,8 +123,7 @@ void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size, cur_entry.sampling_number2 = cur_entry.sampling_number; // Reset values to default - cur_entry.delta_x = 0; - cur_entry.delta_y = 0; + cur_entry.delta = {}; cur_entry.vel_x = 0; cur_entry.vel_y = 0; cur_entry.direction = Direction::None; @@ -146,13 +144,9 @@ void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size, cur_entry.detection_count = gesture.detection_count; cur_entry.type = type; cur_entry.attributes = attributes; - cur_entry.x = gesture.mid_point.x; - cur_entry.y = gesture.mid_point.y; + cur_entry.pos = gesture.mid_point; cur_entry.point_count = static_cast<s32>(gesture.active_points); - for (size_t id = 0; id < MAX_POINTS; id++) { - cur_entry.points[id].x = gesture.points[id].x; - cur_entry.points[id].y = gesture.points[id].y; - } + cur_entry.points = gesture.points; last_gesture = gesture; std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); @@ -160,8 +154,8 @@ void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size, void Controller_Gesture::NewGesture(GestureProperties& gesture, TouchType& type, Attribute& attributes) { - const auto& last_entry = - shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17]; + const auto& last_entry = GetLastGestureEntry(); + gesture.detection_count++; type = TouchType::Touch; @@ -174,13 +168,11 @@ void Controller_Gesture::NewGesture(GestureProperties& gesture, TouchType& type, void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, TouchType& type, f32 time_difference) { - const auto& last_entry = - shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17]; + const auto& last_entry = GetLastGestureEntry(); // Promote to pan type if touch moved for (size_t id = 0; id < MAX_POINTS; id++) { - if (gesture.points[id].x != last_gesture.points[id].x || - gesture.points[id].y != last_gesture.points[id].y) { + if (gesture.points[id] != last_gesture.points[id]) { type = TouchType::Pan; break; } @@ -192,10 +184,7 @@ void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, Touch enable_press_and_tap = false; gesture.active_points = 0; gesture.mid_point = {}; - for (size_t id = 0; id < MAX_POINTS; id++) { - gesture.points[id].x = 0; - gesture.points[id].y = 0; - } + gesture.points.fill({}); return; } @@ -214,8 +203,8 @@ void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, Touch void Controller_Gesture::EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props, TouchType& type, Attribute& attributes, f32 time_difference) { - const auto& last_entry = - shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17]; + const auto& last_entry = GetLastGestureEntry(); + if (last_gesture_props.active_points != 0) { switch (last_entry.type) { case TouchType::Touch: @@ -265,13 +254,11 @@ void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, TouchType& type, f32 time_difference) { auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index]; - const auto& last_entry = - shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17]; - cur_entry.delta_x = gesture.mid_point.x - last_entry.x; - cur_entry.delta_y = gesture.mid_point.y - last_entry.y; + const auto& last_entry = GetLastGestureEntry(); - cur_entry.vel_x = static_cast<f32>(cur_entry.delta_x) / time_difference; - cur_entry.vel_y = static_cast<f32>(cur_entry.delta_y) / time_difference; + cur_entry.delta = gesture.mid_point - last_entry.pos; + cur_entry.vel_x = static_cast<f32>(cur_entry.delta.x) / time_difference; + cur_entry.vel_y = static_cast<f32>(cur_entry.delta.y) / time_difference; last_pan_time_difference = time_difference; // Promote to pinch type @@ -295,12 +282,11 @@ void Controller_Gesture::EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, TouchType& type, f32 time_difference) { auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index]; - const auto& last_entry = - shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17]; + const auto& last_entry = GetLastGestureEntry(); cur_entry.vel_x = - static_cast<f32>(last_entry.delta_x) / (last_pan_time_difference + time_difference); + static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference); cur_entry.vel_y = - static_cast<f32>(last_entry.delta_y) / (last_pan_time_difference + time_difference); + static_cast<f32>(last_entry.delta.y) / (last_pan_time_difference + time_difference); const f32 curr_vel = std::sqrt((cur_entry.vel_x * cur_entry.vel_x) + (cur_entry.vel_y * cur_entry.vel_y)); @@ -320,22 +306,22 @@ void Controller_Gesture::EndPanEvent(GestureProperties& gesture, void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, TouchType& type) { auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index]; - const auto& last_entry = - shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17]; + const auto& last_entry = GetLastGestureEntry(); + type = TouchType::Swipe; gesture = last_gesture_props; force_update = true; - cur_entry.delta_x = last_entry.delta_x; - cur_entry.delta_y = last_entry.delta_y; - if (std::abs(cur_entry.delta_x) > std::abs(cur_entry.delta_y)) { - if (cur_entry.delta_x > 0) { + cur_entry.delta = last_entry.delta; + + if (std::abs(cur_entry.delta.x) > std::abs(cur_entry.delta.y)) { + if (cur_entry.delta.x > 0) { cur_entry.direction = Direction::Right; return; } cur_entry.direction = Direction::Left; return; } - if (cur_entry.delta_y > 0) { + if (cur_entry.delta.y > 0) { cur_entry.direction = Direction::Down; return; } @@ -364,6 +350,14 @@ std::optional<std::size_t> Controller_Gesture::GetUnusedFingerID() const { return std::nullopt; } +Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() { + return shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17]; +} + +const Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() const { + return shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17]; +} + std::size_t Controller_Gesture::UpdateTouchInputEvent( const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) { const auto& [x, y, pressed] = touch_input; @@ -381,8 +375,7 @@ std::size_t Controller_Gesture::UpdateTouchInputEvent( finger_id = first_free_id.value(); fingers[finger_id].pressed = true; } - fingers[finger_id].x = x; - fingers[finger_id].y = y; + fingers[finger_id].pos = {x, y}; return finger_id; } @@ -402,17 +395,18 @@ Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); for (size_t id = 0; id < gesture.active_points; ++id) { - gesture.points[id].x = - static_cast<s32>(active_fingers[id].x * Layout::ScreenUndocked::Width); - gesture.points[id].y = - static_cast<s32>(active_fingers[id].y * Layout::ScreenUndocked::Height); + const auto& [active_x, active_y] = active_fingers[id].pos; + gesture.points[id] = { + .x = static_cast<s32>(active_x * Layout::ScreenUndocked::Width), + .y = static_cast<s32>(active_y * Layout::ScreenUndocked::Height), + }; // Hack: There is no touch in docked but games still allow it if (Settings::values.use_docked_mode.GetValue()) { - gesture.points[id].x = - static_cast<s32>(active_fingers[id].x * Layout::ScreenDocked::Width); - gesture.points[id].y = - static_cast<s32>(active_fingers[id].y * Layout::ScreenDocked::Height); + gesture.points[id] = { + .x = static_cast<s32>(active_x * Layout::ScreenDocked::Width), + .y = static_cast<s32>(active_y * Layout::ScreenDocked::Height), + }; } gesture.mid_point.x += static_cast<s32>(gesture.points[id].x / gesture.active_points); diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h index f46e29411..7e7ae6625 100644 --- a/src/core/hle/service/hid/controllers/gesture.h +++ b/src/core/hle/service/hid/controllers/gesture.h @@ -7,6 +7,7 @@ #include <array> #include "common/bit_field.h" #include "common/common_types.h" +#include "common/point.h" #include "core/frontend/input.h" #include "core/hle/service/hid/controllers/controller_base.h" @@ -63,29 +64,21 @@ private: }; static_assert(sizeof(Attribute) == 4, "Attribute is an invalid size"); - struct Points { - s32_le x; - s32_le y; - }; - static_assert(sizeof(Points) == 8, "Points is an invalid size"); - struct GestureState { s64_le sampling_number; s64_le sampling_number2; s64_le detection_count; TouchType type; Direction direction; - s32_le x; - s32_le y; - s32_le delta_x; - s32_le delta_y; + Common::Point<s32_le> pos; + Common::Point<s32_le> delta; f32 vel_x; f32 vel_y; Attribute attributes; f32 scale; f32 rotation_angle; s32_le point_count; - std::array<Points, 4> points; + std::array<Common::Point<s32_le>, 4> points; }; static_assert(sizeof(GestureState) == 0x68, "GestureState is an invalid size"); @@ -96,15 +89,14 @@ private: static_assert(sizeof(SharedMemory) == 0x708, "SharedMemory is an invalid size"); struct Finger { - f32 x{}; - f32 y{}; + Common::Point<f32> pos{}; bool pressed{}; }; struct GestureProperties { - std::array<Points, MAX_POINTS> points{}; + std::array<Common::Point<s32_le>, MAX_POINTS> points{}; std::size_t active_points{}; - Points mid_point{}; + Common::Point<s32_le> mid_point{}; s64_le detection_count{}; u64_le delta_time{}; f32 average_distance{}; @@ -148,7 +140,11 @@ private: TouchType& type); // Returns an unused finger id, if there is no fingers available std::nullopt is returned. - std::optional<size_t> GetUnusedFingerID() const; + [[nodiscard]] std::optional<size_t> GetUnusedFingerID() const; + + // Retrieves the last gesture entry, as indicated by shared memory indices. + [[nodiscard]] GestureState& GetLastGestureEntry(); + [[nodiscard]] const GestureState& GetLastGestureEntry() const; /** * If the touch is new it tries to assign a new finger id, if there is no fingers available no @@ -166,10 +162,10 @@ private: std::unique_ptr<Input::TouchDevice> touch_mouse_device; std::unique_ptr<Input::TouchDevice> touch_udp_device; std::unique_ptr<Input::TouchDevice> touch_btn_device; - std::array<size_t, MAX_FINGERS> mouse_finger_id; - std::array<size_t, MAX_FINGERS> keyboard_finger_id; - std::array<size_t, MAX_FINGERS> udp_finger_id; - std::array<Finger, MAX_POINTS> fingers; + std::array<size_t, MAX_FINGERS> mouse_finger_id{}; + std::array<size_t, MAX_FINGERS> keyboard_finger_id{}; + std::array<size_t, MAX_FINGERS> udp_finger_id{}; + std::array<Finger, MAX_POINTS> fingers{}; GestureProperties last_gesture{}; s64_le last_update_timestamp{}; s64_le last_tap_timestamp{}; diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp index ac9112c40..6ef17acc5 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.cpp +++ b/src/core/hle/service/hid/controllers/touchscreen.cpp @@ -74,8 +74,11 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin for (std::size_t id = 0; id < MAX_FINGERS; ++id) { auto& touch_entry = cur_entry.states[id]; if (id < active_fingers_count) { - touch_entry.x = static_cast<u16>(active_fingers[id].x * Layout::ScreenUndocked::Width); - touch_entry.y = static_cast<u16>(active_fingers[id].y * Layout::ScreenUndocked::Height); + const auto& [active_x, active_y] = active_fingers[id].position; + touch_entry.position = { + .x = static_cast<u16>(active_x * Layout::ScreenUndocked::Width), + .y = static_cast<u16>(active_y * Layout::ScreenUndocked::Height), + }; touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle; @@ -86,8 +89,7 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin } else { // Clear touch entry touch_entry.attribute.raw = 0; - touch_entry.x = 0; - touch_entry.y = 0; + touch_entry.position = {}; touch_entry.diameter_x = 0; touch_entry.diameter_y = 0; touch_entry.rotation_angle = 0; @@ -140,8 +142,7 @@ std::size_t Controller_Touchscreen::UpdateTouchInputEvent( fingers[finger_id].id = static_cast<u32_le>(finger_id); attribute.start_touch.Assign(1); } - fingers[finger_id].x = x; - fingers[finger_id].y = y; + fingers[finger_id].position = {x, y}; fingers[finger_id].attribute = attribute; return finger_id; } diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h index 2869d0cfd..ef2becefd 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.h +++ b/src/core/hle/service/hid/controllers/touchscreen.h @@ -7,6 +7,7 @@ #include "common/bit_field.h" #include "common/common_funcs.h" #include "common/common_types.h" +#include "common/point.h" #include "common/swap.h" #include "core/frontend/input.h" #include "core/hle/service/hid/controllers/controller_base.h" @@ -55,8 +56,7 @@ private: u64_le delta_time; Attributes attribute; u32_le finger; - u32_le x; - u32_le y; + Common::Point<u32_le> position; u32_le diameter_x; u32_le diameter_y; u32_le rotation_angle; @@ -81,8 +81,7 @@ private: struct Finger { u64_le last_touch{}; - float x{}; - float y{}; + Common::Point<float> position; u32_le id{}; bool pressed{}; Attributes attribute; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 49c17fd14..df0fe1c8e 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -1770,7 +1770,7 @@ public: {232, nullptr, "GetIrSensorState"}, {233, nullptr, "GetXcdHandleForNpadWithIrSensor"}, {301, nullptr, "ActivateNpadSystem"}, - {303, nullptr, "ApplyNpadSystemCommonPolicy"}, + {303, &HidSys::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"}, {304, nullptr, "EnableAssigningSingleOnSlSrPress"}, {305, nullptr, "DisableAssigningSingleOnSlSrPress"}, {306, nullptr, "GetLastActiveNpad"}, @@ -1949,6 +1949,15 @@ public: RegisterHandlers(functions); } + +private: + void ApplyNpadSystemCommonPolicy(Kernel::HLERequestContext& ctx) { + // We already do this for homebrew so we can just stub it out + LOG_WARNING(Service_HID, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } }; class HidTmp final : public ServiceFramework<HidTmp> { diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp index d160ffe87..c709a8028 100644 --- a/src/core/hle/service/ldn/ldn.cpp +++ b/src/core/hle/service/ldn/ldn.cpp @@ -215,10 +215,151 @@ public: } }; +class INetworkService final : public ServiceFramework<INetworkService> { +public: + explicit INetworkService(Core::System& system_) : ServiceFramework{system_, "INetworkService"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Initialize"}, + {256, nullptr, "AttachNetworkInterfaceStateChangeEvent"}, + {264, nullptr, "GetNetworkInterfaceLastError"}, + {272, nullptr, "GetRole"}, + {280, nullptr, "GetAdvertiseData"}, + {288, nullptr, "GetGroupInfo"}, + {296, nullptr, "GetGroupInfo2"}, + {304, nullptr, "GetGroupOwner"}, + {312, nullptr, "GetIpConfig"}, + {320, nullptr, "GetLinkLevel"}, + {512, nullptr, "Scan"}, + {768, nullptr, "CreateGroup"}, + {776, nullptr, "DestroyGroup"}, + {784, nullptr, "SetAdvertiseData"}, + {1536, nullptr, "SendToOtherGroup"}, + {1544, nullptr, "RecvFromOtherGroup"}, + {1552, nullptr, "AddAcceptableGroupId"}, + {1560, nullptr, "ClearAcceptableGroupId"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class INetworkServiceMonitor final : public ServiceFramework<INetworkServiceMonitor> { +public: + explicit INetworkServiceMonitor(Core::System& system_) + : ServiceFramework{system_, "INetworkServiceMonitor"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &INetworkServiceMonitor::Initialize, "Initialize"}, + {256, nullptr, "AttachNetworkInterfaceStateChangeEvent"}, + {264, nullptr, "GetNetworkInterfaceLastError"}, + {272, nullptr, "GetRole"}, + {280, nullptr, "GetAdvertiseData"}, + {281, nullptr, "GetAdvertiseData2"}, + {288, nullptr, "GetGroupInfo"}, + {296, nullptr, "GetGroupInfo2"}, + {304, nullptr, "GetGroupOwner"}, + {312, nullptr, "GetIpConfig"}, + {320, nullptr, "GetLinkLevel"}, + {328, nullptr, "AttachJoinEvent"}, + {336, nullptr, "GetMembers"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + + void Initialize(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_LDN, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_DISABLED); + } +}; + +class LP2PAPP final : public ServiceFramework<LP2PAPP> { +public: + explicit LP2PAPP(Core::System& system_) : ServiceFramework{system_, "lp2p:app"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &LP2PAPP::CreateMonitorService, "CreateNetworkService"}, + {8, &LP2PAPP::CreateMonitorService, "CreateNetworkServiceMonitor"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + + void CreateNetworkervice(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u64 reserved_input = rp.Pop<u64>(); + const u32 input = rp.Pop<u32>(); + + LOG_WARNING(Service_LDN, "(STUBBED) called reserved_input={} input={}", reserved_input, + input); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<INetworkService>(system); + } + + void CreateMonitorService(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u64 reserved_input = rp.Pop<u64>(); + + LOG_WARNING(Service_LDN, "(STUBBED) called reserved_input={}", reserved_input); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<INetworkServiceMonitor>(system); + } +}; + +class LP2PSYS final : public ServiceFramework<LP2PSYS> { +public: + explicit LP2PSYS(Core::System& system_) : ServiceFramework{system_, "lp2p:sys"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &LP2PSYS::CreateMonitorService, "CreateNetworkService"}, + {8, &LP2PSYS::CreateMonitorService, "CreateNetworkServiceMonitor"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + + void CreateNetworkervice(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u64 reserved_input = rp.Pop<u64>(); + const u32 input = rp.Pop<u32>(); + + LOG_WARNING(Service_LDN, "(STUBBED) called reserved_input={} input={}", reserved_input, + input); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<INetworkService>(system); + } + + void CreateMonitorService(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u64 reserved_input = rp.Pop<u64>(); + + LOG_WARNING(Service_LDN, "(STUBBED) called reserved_input={}", reserved_input); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<INetworkServiceMonitor>(system); + } +}; + void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { std::make_shared<LDNM>(system)->InstallAsService(sm); std::make_shared<LDNS>(system)->InstallAsService(sm); std::make_shared<LDNU>(system)->InstallAsService(sm); + std::make_shared<LP2PAPP>(system)->InstallAsService(sm); + std::make_shared<LP2PSYS>(system)->InstallAsService(sm); } } // namespace Service::LDN diff --git a/src/core/memory.cpp b/src/core/memory.cpp index bf2ef7816..9857278f6 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -591,7 +591,15 @@ struct Memory::Impl { * @returns The instance of T read from the specified virtual address. */ template <typename T> - T Read(const VAddr vaddr) { + T Read(VAddr vaddr) { + // AARCH64 masks the upper 16 bit of all memory accesses + vaddr &= 0xffffffffffffLL; + + if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) { + LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr); + return 0; + } + // Avoid adding any extra logic to this fast-path block const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw(); if (const u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { @@ -629,7 +637,16 @@ struct Memory::Impl { * is undefined. */ template <typename T> - void Write(const VAddr vaddr, const T data) { + void Write(VAddr vaddr, const T data) { + // AARCH64 masks the upper 16 bit of all memory accesses + vaddr &= 0xffffffffffffLL; + + if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) { + LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, + static_cast<u32>(data), vaddr); + return; + } + // Avoid adding any extra logic to this fast-path block const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw(); if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { @@ -656,7 +673,16 @@ struct Memory::Impl { } template <typename T> - bool WriteExclusive(const VAddr vaddr, const T data, const T expected) { + bool WriteExclusive(VAddr vaddr, const T data, const T expected) { + // AARCH64 masks the upper 16 bit of all memory accesses + vaddr &= 0xffffffffffffLL; + + if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) { + LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, + static_cast<u32>(data), vaddr); + return true; + } + const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw(); if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { // NOTE: Avoid adding any extra logic to this fast-path block @@ -683,7 +709,16 @@ struct Memory::Impl { return true; } - bool WriteExclusive128(const VAddr vaddr, const u128 data, const u128 expected) { + bool WriteExclusive128(VAddr vaddr, const u128 data, const u128 expected) { + // AARCH64 masks the upper 16 bit of all memory accesses + vaddr &= 0xffffffffffffLL; + + if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) { + LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, + static_cast<u32>(data[0]), vaddr); + return true; + } + const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw(); if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { // NOTE: Avoid adding any extra logic to this fast-path block diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index b9b584b2a..68672a92b 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -18,16 +18,6 @@ #include <utility> #include <vector> -// Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307 -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wimplicit-fallthrough" -#endif -#include <SDL.h> -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - #include "common/logging/log.h" #include "common/math_util.h" #include "common/param_package.h" @@ -214,6 +204,40 @@ public: sdl_controller.reset(controller); } + bool IsJoyconLeft() const { + return std::strstr(GetControllerName().c_str(), "Joy-Con Left") != nullptr; + } + + bool IsJoyconRight() const { + return std::strstr(GetControllerName().c_str(), "Joy-Con Right") != nullptr; + } + + std::string GetControllerName() const { + if (sdl_controller) { + switch (SDL_GameControllerGetType(sdl_controller.get())) { + case SDL_CONTROLLER_TYPE_XBOX360: + return "XBox 360 Controller"; + case SDL_CONTROLLER_TYPE_XBOXONE: + return "XBox One Controller"; + default: + break; + } + const auto name = SDL_GameControllerName(sdl_controller.get()); + if (name) { + return name; + } + } + + if (sdl_joystick) { + const auto name = SDL_JoystickName(sdl_joystick.get()); + if (name) { + return name; + } + } + + return "Unknown"; + } + private: struct State { std::unordered_map<int, bool> buttons; @@ -858,23 +882,42 @@ SDLState::~SDLState() { std::vector<Common::ParamPackage> SDLState::GetInputDevices() { std::scoped_lock lock(joystick_map_mutex); std::vector<Common::ParamPackage> devices; + std::unordered_map<int, std::shared_ptr<SDLJoystick>> joycon_pairs; + for (const auto& [key, value] : joystick_map) { + for (const auto& joystick : value) { + if (!joystick->GetSDLJoystick()) { + continue; + } + std::string name = + fmt::format("{} {}", joystick->GetControllerName(), joystick->GetPort()); + devices.emplace_back(Common::ParamPackage{ + {"class", "sdl"}, + {"display", std::move(name)}, + {"guid", joystick->GetGUID()}, + {"port", std::to_string(joystick->GetPort())}, + }); + if (joystick->IsJoyconLeft()) { + joycon_pairs.insert_or_assign(joystick->GetPort(), joystick); + } + } + } + + // Add dual controllers for (const auto& [key, value] : joystick_map) { for (const auto& joystick : value) { - if (auto* const controller = joystick->GetSDLGameController()) { + if (joystick->IsJoyconRight()) { + if (!joycon_pairs.contains(joystick->GetPort())) { + continue; + } + const auto joystick2 = joycon_pairs.at(joystick->GetPort()); + std::string name = - fmt::format("{} {}", GetControllerName(controller), joystick->GetPort()); - devices.emplace_back(Common::ParamPackage{ - {"class", "sdl"}, - {"display", std::move(name)}, - {"guid", joystick->GetGUID()}, - {"port", std::to_string(joystick->GetPort())}, - }); - } else if (auto* const joy = joystick->GetSDLJoystick()) { - std::string name = fmt::format("{} {}", SDL_JoystickName(joy), joystick->GetPort()); + fmt::format("{} {}", "Nintendo Dual Joy-Con", joystick->GetPort()); devices.emplace_back(Common::ParamPackage{ {"class", "sdl"}, {"display", std::move(name)}, {"guid", joystick->GetGUID()}, + {"guid2", joystick2->GetGUID()}, {"port", std::to_string(joystick->GetPort())}, }); } @@ -883,17 +926,6 @@ std::vector<Common::ParamPackage> SDLState::GetInputDevices() { return devices; } -std::string SDLState::GetControllerName(SDL_GameController* controller) const { - switch (SDL_GameControllerGetType(controller)) { - case SDL_CONTROLLER_TYPE_XBOX360: - return "XBox 360 Controller"; - case SDL_CONTROLLER_TYPE_XBOXONE: - return "XBox One Controller"; - default: - return SDL_GameControllerName(controller); - } -} - namespace { Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, s32 axis, float value = 0.1f) { @@ -1073,24 +1105,48 @@ ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& pa return {}; } const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); + auto* controller = joystick->GetSDLGameController(); if (controller == nullptr) { return {}; } - const bool invert = - SDL_GameControllerGetType(controller) != SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO; - // This list is missing ZL/ZR since those are not considered buttons in SDL GameController. // We will add those afterwards // This list also excludes Screenshot since theres not really a mapping for that - using ButtonBindings = - std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 17>; - const ButtonBindings switch_to_sdl_button{{ - {Settings::NativeButton::A, invert ? SDL_CONTROLLER_BUTTON_B : SDL_CONTROLLER_BUTTON_A}, - {Settings::NativeButton::B, invert ? SDL_CONTROLLER_BUTTON_A : SDL_CONTROLLER_BUTTON_B}, - {Settings::NativeButton::X, invert ? SDL_CONTROLLER_BUTTON_Y : SDL_CONTROLLER_BUTTON_X}, - {Settings::NativeButton::Y, invert ? SDL_CONTROLLER_BUTTON_X : SDL_CONTROLLER_BUTTON_Y}, + ButtonBindings switch_to_sdl_button; + + if (SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) { + switch_to_sdl_button = GetNintendoButtonBinding(joystick); + } else { + switch_to_sdl_button = GetDefaultButtonBinding(); + } + + // Add the missing bindings for ZL/ZR + static constexpr ZButtonBindings switch_to_sdl_axis{{ + {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT}, + {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT}, + }}; + + // Parameters contain two joysticks return dual + if (params.Has("guid2")) { + const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0)); + + if (joystick2->GetSDLGameController() != nullptr) { + return GetDualControllerMapping(joystick, joystick2, switch_to_sdl_button, + switch_to_sdl_axis); + } + } + + return GetSingleControllerMapping(joystick, switch_to_sdl_button, switch_to_sdl_axis); +} + +ButtonBindings SDLState::GetDefaultButtonBinding() const { + return { + std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B}, + {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A}, + {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y}, + {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X}, {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK}, {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK}, {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, @@ -1104,18 +1160,51 @@ ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& pa {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER}, {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE}, - }}; + }; +} - // Add the missing bindings for ZL/ZR - using ZBindings = - std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>; - static constexpr ZBindings switch_to_sdl_axis{{ - {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT}, - {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT}, - }}; +ButtonBindings SDLState::GetNintendoButtonBinding( + const std::shared_ptr<SDLJoystick>& joystick) const { + // Default SL/SR mapping for pro controllers + auto sl_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER; + auto sr_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; + + if (joystick->IsJoyconLeft()) { + sl_button = SDL_CONTROLLER_BUTTON_PADDLE2; + sr_button = SDL_CONTROLLER_BUTTON_PADDLE4; + } + if (joystick->IsJoyconRight()) { + sl_button = SDL_CONTROLLER_BUTTON_PADDLE3; + sr_button = SDL_CONTROLLER_BUTTON_PADDLE1; + } + return { + std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_A}, + {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_B}, + {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_X}, + {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_Y}, + {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK}, + {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK}, + {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, + {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER}, + {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START}, + {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK}, + {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT}, + {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP}, + {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT}, + {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN}, + {Settings::NativeButton::SL, sl_button}, + {Settings::NativeButton::SR, sr_button}, + {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE}, + }; +} + +ButtonMapping SDLState::GetSingleControllerMapping( + const std::shared_ptr<SDLJoystick>& joystick, const ButtonBindings& switch_to_sdl_button, + const ZButtonBindings& switch_to_sdl_axis) const { ButtonMapping mapping; mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size()); + auto* controller = joystick->GetSDLGameController(); for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) { const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button); @@ -1133,11 +1222,68 @@ ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& pa return mapping; } +ButtonMapping SDLState::GetDualControllerMapping(const std::shared_ptr<SDLJoystick>& joystick, + const std::shared_ptr<SDLJoystick>& joystick2, + const ButtonBindings& switch_to_sdl_button, + const ZButtonBindings& switch_to_sdl_axis) const { + ButtonMapping mapping; + mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size()); + auto* controller = joystick->GetSDLGameController(); + auto* controller2 = joystick2->GetSDLGameController(); + + for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) { + if (IsButtonOnLeftSide(switch_button)) { + const auto& binding = SDL_GameControllerGetBindForButton(controller2, sdl_button); + mapping.insert_or_assign( + switch_button, + BuildParamPackageForBinding(joystick2->GetPort(), joystick2->GetGUID(), binding)); + continue; + } + const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button); + mapping.insert_or_assign( + switch_button, + BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding)); + } + for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) { + if (IsButtonOnLeftSide(switch_button)) { + const auto& binding = SDL_GameControllerGetBindForAxis(controller2, sdl_axis); + mapping.insert_or_assign( + switch_button, + BuildParamPackageForBinding(joystick2->GetPort(), joystick2->GetGUID(), binding)); + continue; + } + const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis); + mapping.insert_or_assign( + switch_button, + BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding)); + } + + return mapping; +} + +bool SDLState::IsButtonOnLeftSide(Settings::NativeButton::Values button) const { + switch (button) { + case Settings::NativeButton::DDown: + case Settings::NativeButton::DLeft: + case Settings::NativeButton::DRight: + case Settings::NativeButton::DUp: + case Settings::NativeButton::L: + case Settings::NativeButton::LStick: + case Settings::NativeButton::Minus: + case Settings::NativeButton::Screenshot: + case Settings::NativeButton::ZL: + return true; + default: + return false; + } +} + AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& params) { if (!params.Has("guid") || !params.Has("port")) { return {}; } const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); + const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0)); auto* controller = joystick->GetSDLGameController(); if (controller == nullptr) { return {}; @@ -1148,10 +1294,17 @@ AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& pa SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX); const auto& binding_left_y = SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY); - mapping.insert_or_assign(Settings::NativeAnalog::LStick, - BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), - binding_left_x.value.axis, - binding_left_y.value.axis)); + if (params.Has("guid2")) { + mapping.insert_or_assign( + Settings::NativeAnalog::LStick, + BuildParamPackageForAnalog(joystick2->GetPort(), joystick2->GetGUID(), + binding_left_x.value.axis, binding_left_y.value.axis)); + } else { + mapping.insert_or_assign( + Settings::NativeAnalog::LStick, + BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), + binding_left_x.value.axis, binding_left_y.value.axis)); + } const auto& binding_right_x = SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX); const auto& binding_right_y = @@ -1168,20 +1321,32 @@ MotionMapping SDLState::GetMotionMappingForDevice(const Common::ParamPackage& pa return {}; } const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); + const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0)); auto* controller = joystick->GetSDLGameController(); if (controller == nullptr) { return {}; } + MotionMapping mapping = {}; joystick->EnableMotion(); - if (!joystick->HasGyro() && !joystick->HasAccel()) { - return {}; + if (joystick->HasGyro() || joystick->HasAccel()) { + mapping.insert_or_assign(Settings::NativeMotion::MotionRight, + BuildMotionParam(joystick->GetPort(), joystick->GetGUID())); + } + if (params.Has("guid2")) { + joystick2->EnableMotion(); + if (joystick2->HasGyro() || joystick2->HasAccel()) { + mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, + BuildMotionParam(joystick2->GetPort(), joystick2->GetGUID())); + } + } else { + if (joystick->HasGyro() || joystick->HasAccel()) { + mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, + BuildMotionParam(joystick->GetPort(), joystick->GetGUID())); + } } - MotionMapping mapping = {}; - mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, - BuildMotionParam(joystick->GetPort(), joystick->GetGUID())); return mapping; } namespace Polling { diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h index 121e01913..b77afcbd8 100644 --- a/src/input_common/sdl/sdl_impl.h +++ b/src/input_common/sdl/sdl_impl.h @@ -9,6 +9,17 @@ #include <mutex> #include <thread> #include <unordered_map> + +// Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307 +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +#endif +#include <SDL.h> +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + #include "common/common_types.h" #include "common/threadsafe_queue.h" #include "input_common/sdl/sdl.h" @@ -18,6 +29,11 @@ using SDL_GameController = struct _SDL_GameController; using SDL_Joystick = struct _SDL_Joystick; using SDL_JoystickID = s32; +using ButtonBindings = + std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 17>; +using ZButtonBindings = + std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>; + namespace InputCommon::SDL { class SDLAnalogFactory; @@ -66,8 +82,25 @@ private: /// Needs to be called before SDL_QuitSubSystem. void CloseJoysticks(); - /// Returns a custom name for specific controllers because the default name is not correct - std::string GetControllerName(SDL_GameController* controller) const; + /// Returns the default button bindings list for generic controllers + ButtonBindings GetDefaultButtonBinding() const; + + /// Returns the default button bindings list for nintendo controllers + ButtonBindings GetNintendoButtonBinding(const std::shared_ptr<SDLJoystick>& joystick) const; + + /// Returns the button mappings from a single controller + ButtonMapping GetSingleControllerMapping(const std::shared_ptr<SDLJoystick>& joystick, + const ButtonBindings& switch_to_sdl_button, + const ZButtonBindings& switch_to_sdl_axis) const; + + /// Returns the button mappings from two different controllers + ButtonMapping GetDualControllerMapping(const std::shared_ptr<SDLJoystick>& joystick, + const std::shared_ptr<SDLJoystick>& joystick2, + const ButtonBindings& switch_to_sdl_button, + const ZButtonBindings& switch_to_sdl_axis) const; + + /// Returns true if the button is on the left joycon + bool IsButtonOnLeftSide(Settings::NativeButton::Values button) const; // Set to true if SDL supports game controller subsystem bool has_gamecontroller = false; diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index de971041f..9e6b87960 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -596,7 +596,7 @@ void BufferCache<P>::PopAsyncFlushes() { runtime.CopyBuffer(download_staging.buffer, slot_buffers[buffer_id], copies); } runtime.Finish(); - for (const auto [copy, buffer_id] : downloads) { + for (const auto& [copy, buffer_id] : downloads) { const Buffer& buffer = slot_buffers[buffer_id]; const VAddr cpu_addr = buffer.CpuAddr() + copy.src_offset; // Undo the modified offset @@ -606,7 +606,7 @@ void BufferCache<P>::PopAsyncFlushes() { } } else { const std::span<u8> immediate_buffer = ImmediateBuffer(largest_copy); - for (const auto [copy, buffer_id] : downloads) { + for (const auto& [copy, buffer_id] : downloads) { Buffer& buffer = slot_buffers[buffer_id]; buffer.ImmediateDownload(copy.src_offset, immediate_buffer.subspan(0, copy.size)); const VAddr cpu_addr = buffer.CpuAddr() + copy.src_offset; diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 37f7b24e1..35cc561be 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -104,7 +104,13 @@ void GPU::WaitFence(u32 syncpoint_id, u32 value) { } MICROPROFILE_SCOPE(GPU_wait); std::unique_lock lock{sync_mutex}; - sync_cv.wait(lock, [=, this] { return syncpoints.at(syncpoint_id).load() >= value; }); + sync_cv.wait(lock, [=, this] { + if (shutting_down.load(std::memory_order_relaxed)) { + // We're shutting down, ensure no threads continue to wait for the next syncpoint + return true; + } + return syncpoints.at(syncpoint_id).load() >= value; + }); } void GPU::IncrementSyncPoint(const u32 syncpoint_id) { @@ -523,6 +529,10 @@ void GPU::TriggerCpuInterrupt(const u32 syncpoint_id, const u32 value) const { } void GPU::ShutDown() { + // Signal that threads should no longer block on syncpoint fences + shutting_down.store(true, std::memory_order_relaxed); + sync_cv.notify_all(); + gpu_thread.ShutDown(); } diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 29a867863..a8e98e51b 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -389,6 +389,8 @@ private: std::unique_ptr<Engines::KeplerMemory> kepler_memory; /// Shader build notifier std::unique_ptr<VideoCore::ShaderNotify> shader_notify; + /// When true, we are about to shut down emulation session, so terminate outstanding tasks + std::atomic_bool shutting_down{}; std::array<std::atomic<u32>, Service::Nvidia::MaxSyncPoints> syncpoints{}; diff --git a/src/video_core/rasterizer_accelerated.cpp b/src/video_core/rasterizer_accelerated.cpp index 62d84c0f8..6decd2546 100644 --- a/src/video_core/rasterizer_accelerated.cpp +++ b/src/video_core/rasterizer_accelerated.cpp @@ -18,10 +18,10 @@ RasterizerAccelerated::~RasterizerAccelerated() = default; void RasterizerAccelerated::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) { const auto page_end = Common::DivCeil(addr + size, Core::Memory::PAGE_SIZE); for (auto page = addr >> Core::Memory::PAGE_BITS; page != page_end; ++page) { - auto& count = cached_pages.at(page >> 3).Count(page); + auto& count = cached_pages.at(page >> 2).Count(page); if (delta > 0) { - ASSERT_MSG(count < UINT8_MAX, "Count may overflow!"); + ASSERT_MSG(count < UINT16_MAX, "Count may overflow!"); } else if (delta < 0) { ASSERT_MSG(count > 0, "Count may underflow!"); } else { @@ -29,7 +29,7 @@ void RasterizerAccelerated::UpdatePagesCachedCount(VAddr addr, u64 size, int del } // Adds or subtracts 1, as count is a unsigned 8-bit value - count += static_cast<u8>(delta); + count += static_cast<u16>(delta); // Assume delta is either -1 or 1 if (count == 0) { diff --git a/src/video_core/rasterizer_accelerated.h b/src/video_core/rasterizer_accelerated.h index 9227a4adc..ea879bfdd 100644 --- a/src/video_core/rasterizer_accelerated.h +++ b/src/video_core/rasterizer_accelerated.h @@ -29,20 +29,20 @@ private: public: CacheEntry() = default; - std::atomic_uint8_t& Count(std::size_t page) { - return values[page & 7]; + std::atomic_uint16_t& Count(std::size_t page) { + return values[page & 3]; } - const std::atomic_uint8_t& Count(std::size_t page) const { - return values[page & 7]; + const std::atomic_uint16_t& Count(std::size_t page) const { + return values[page & 3]; } private: - std::array<std::atomic_uint8_t, 8> values{}; + std::array<std::atomic_uint16_t, 4> values{}; }; static_assert(sizeof(CacheEntry) == 8, "CacheEntry should be 8 bytes!"); - std::array<CacheEntry, 0x800000> cached_pages; + std::array<CacheEntry, 0x1000000> cached_pages; Core::Memory::Memory& cpu_memory; }; diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp index fa37aa79a..5edd06ebc 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp @@ -53,6 +53,18 @@ struct Range { UNREACHABLE_MSG("Invalid memory usage={}", usage); return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; } + +constexpr VkExportMemoryAllocateInfo EXPORT_ALLOCATE_INFO{ + .sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO, + .pNext = nullptr, +#ifdef _WIN32 + .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT, +#elif __unix__ + .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT, +#else + .handleTypes = 0, +#endif +}; } // Anonymous namespace class MemoryAllocation { @@ -131,7 +143,7 @@ public: /// Returns whether this allocation is compatible with the arguments. [[nodiscard]] bool IsCompatible(VkMemoryPropertyFlags flags, u32 type_mask) const { - return (flags & property_flags) && (type_mask & shifted_memory_type) != 0; + return (flags & property_flags) == property_flags && (type_mask & shifted_memory_type) != 0; } private: @@ -217,14 +229,18 @@ MemoryAllocator::~MemoryAllocator() = default; MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements& requirements, MemoryUsage usage) { // Find the fastest memory flags we can afford with the current requirements - const VkMemoryPropertyFlags flags = MemoryPropertyFlags(requirements.memoryTypeBits, usage); + const u32 type_mask = requirements.memoryTypeBits; + const VkMemoryPropertyFlags usage_flags = MemoryUsagePropertyFlags(usage); + const VkMemoryPropertyFlags flags = MemoryPropertyFlags(type_mask, usage_flags); if (std::optional<MemoryCommit> commit = TryCommit(requirements, flags)) { return std::move(*commit); } // Commit has failed, allocate more memory. - // TODO(Rodrigo): Handle out of memory situations in some way like flushing to guest memory. - AllocMemory(flags, requirements.memoryTypeBits, AllocationChunkSize(requirements.size)); - + const u64 chunk_size = AllocationChunkSize(requirements.size); + if (!TryAllocMemory(flags, type_mask, chunk_size)) { + // TODO(Rodrigo): Handle out of memory situations in some way like flushing to guest memory. + throw vk::Exception(VK_ERROR_OUT_OF_DEVICE_MEMORY); + } // Commit again, this time it won't fail since there's a fresh allocation above. // If it does, there's a bug. return TryCommit(requirements, flags).value(); @@ -242,26 +258,25 @@ MemoryCommit MemoryAllocator::Commit(const vk::Image& image, MemoryUsage usage) return commit; } -void MemoryAllocator::AllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size) { +bool MemoryAllocator::TryAllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size) { const u32 type = FindType(flags, type_mask).value(); - const VkExportMemoryAllocateInfo export_allocate_info{ - .sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO, - .pNext = nullptr, -#ifdef _WIN32 - .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT, -#elif __unix__ - .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT, -#else - .handleTypes = 0, -#endif - }; - vk::DeviceMemory memory = device.GetLogical().AllocateMemory({ + vk::DeviceMemory memory = device.GetLogical().TryAllocateMemory({ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, - .pNext = export_allocations ? &export_allocate_info : nullptr, + .pNext = export_allocations ? &EXPORT_ALLOCATE_INFO : nullptr, .allocationSize = size, .memoryTypeIndex = type, }); + if (!memory) { + if ((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0) { + // Try to allocate non device local memory + return TryAllocMemory(flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, type_mask, size); + } else { + // RIP + return false; + } + } allocations.push_back(std::make_unique<MemoryAllocation>(std::move(memory), flags, size, type)); + return true; } std::optional<MemoryCommit> MemoryAllocator::TryCommit(const VkMemoryRequirements& requirements, @@ -274,24 +289,24 @@ std::optional<MemoryCommit> MemoryAllocator::TryCommit(const VkMemoryRequirement return commit; } } + if ((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0) { + // Look for non device local commits on failure + return TryCommit(requirements, flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + } return std::nullopt; } -VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask, MemoryUsage usage) const { - return MemoryPropertyFlags(type_mask, MemoryUsagePropertyFlags(usage)); -} - VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask, VkMemoryPropertyFlags flags) const { if (FindType(flags, type_mask)) { // Found a memory type with those requirements return flags; } - if (flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) { + if ((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0) { // Remove host cached bit in case it's not supported return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_HOST_CACHED_BIT); } - if (flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) { + if ((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0) { // Remove device local, if it's not supported by the requested resource return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); } @@ -302,7 +317,7 @@ VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask, std::optional<u32> MemoryAllocator::FindType(VkMemoryPropertyFlags flags, u32 type_mask) const { for (u32 type_index = 0; type_index < properties.memoryTypeCount; ++type_index) { const VkMemoryPropertyFlags type_flags = properties.memoryTypes[type_index].propertyFlags; - if ((type_mask & (1U << type_index)) && (type_flags & flags)) { + if ((type_mask & (1U << type_index)) != 0 && (type_flags & flags) == flags) { // The type matches in type and in the wanted properties. return type_index; } diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.h b/src/video_core/vulkan_common/vulkan_memory_allocator.h index d1ce29450..db12d02f4 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.h +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.h @@ -101,16 +101,13 @@ public: MemoryCommit Commit(const vk::Image& image, MemoryUsage usage); private: - /// Allocates a chunk of memory. - void AllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size); + /// Tries to allocate a chunk of memory. + bool TryAllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size); /// Tries to allocate a memory commit. std::optional<MemoryCommit> TryCommit(const VkMemoryRequirements& requirements, VkMemoryPropertyFlags flags); - /// Returns the fastest compatible memory property flags from a wanted usage. - VkMemoryPropertyFlags MemoryPropertyFlags(u32 type_mask, MemoryUsage usage) const; - /// Returns the fastest compatible memory property flags from the wanted flags. VkMemoryPropertyFlags MemoryPropertyFlags(u32 type_mask, VkMemoryPropertyFlags flags) const; diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index cc0790e07..634fe66a5 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -4,6 +4,12 @@ set(CMAKE_AUTOUIC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules) +# Set the RPATH for Qt Libraries +# This must be done before the `yuzu` target is created +if (YUZU_USE_BUNDLED_QT AND (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")) + set(CMAKE_BUILD_RPATH "${CMAKE_BINARY_DIR}/bin/lib/") +endif() + add_executable(yuzu Info.plist about_dialog.cpp @@ -278,11 +284,14 @@ if(UNIX AND NOT APPLE) install(TARGETS yuzu RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") endif() -if (MSVC) +if (YUZU_USE_BUNDLED_QT) include(CopyYuzuQt5Deps) + copy_yuzu_Qt5_deps(yuzu) +endif() + +if (MSVC) include(CopyYuzuSDLDeps) include(CopyYuzuFFmpegDeps) - copy_yuzu_Qt5_deps(yuzu) copy_yuzu_SDL_deps(yuzu) copy_yuzu_FFmpeg_deps(yuzu) endif() diff --git a/src/yuzu/applets/software_keyboard.cpp b/src/yuzu/applets/software_keyboard.cpp index b0f764994..aa453a79f 100644 --- a/src/yuzu/applets/software_keyboard.cpp +++ b/src/yuzu/applets/software_keyboard.cpp @@ -720,21 +720,9 @@ void QtSoftwareKeyboardDialog::SetTextDrawType() { ui->line_edit_osk->setFocus(); }); - connect(ui->line_edit_osk, &QLineEdit::returnPressed, [this] { - switch (bottom_osk_index) { - case BottomOSKIndex::LowerCase: - ui->button_ok->click(); - break; - case BottomOSKIndex::UpperCase: - ui->button_ok_shift->click(); - break; - case BottomOSKIndex::NumberPad: - ui->button_ok_num->click(); - break; - default: - break; - } - }); + connect( + ui->line_edit_osk, &QLineEdit::returnPressed, this, + [this] { TranslateButtonPress(HIDButton::Plus); }, Qt::QueuedConnection); ui->line_edit_osk->setPlaceholderText( QString::fromStdU16String(initialize_parameters.guide_text)); @@ -1113,12 +1101,11 @@ void QtSoftwareKeyboardDialog::NormalKeyboardButtonClicked(QPushButton* button) } if (button == ui->button_ok || button == ui->button_ok_shift || button == ui->button_ok_num) { - if (ui->topOSK->currentIndex() == 1) { - emit SubmitNormalText(SwkbdResult::Ok, - ui->text_edit_osk->toPlainText().toStdU16String()); - } else { - emit SubmitNormalText(SwkbdResult::Ok, ui->line_edit_osk->text().toStdU16String()); - } + auto text = ui->topOSK->currentIndex() == 1 + ? ui->text_edit_osk->toPlainText().toStdU16String() + : ui->line_edit_osk->text().toStdU16String(); + + emit SubmitNormalText(SwkbdResult::Ok, std::move(text)); return; } @@ -1277,13 +1264,11 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(HIDButton button) { if (is_inline) { emit SubmitInlineText(SwkbdReplyType::DecidedCancel, current_text, cursor_position); } else { - if (ui->topOSK->currentIndex() == 1) { - emit SubmitNormalText(SwkbdResult::Cancel, - ui->text_edit_osk->toPlainText().toStdU16String()); - } else { - emit SubmitNormalText(SwkbdResult::Cancel, - ui->line_edit_osk->text().toStdU16String()); - } + auto text = ui->topOSK->currentIndex() == 1 + ? ui->text_edit_osk->toPlainText().toStdU16String() + : ui->line_edit_osk->text().toStdU16String(); + + emit SubmitNormalText(SwkbdResult::Cancel, std::move(text)); } break; case HIDButton::Y: @@ -1575,7 +1560,7 @@ void QtSoftwareKeyboard::ShowNormalKeyboard() const { void QtSoftwareKeyboard::ShowTextCheckDialog( Service::AM::Applets::SwkbdTextCheckResult text_check_result, std::u16string text_check_message) const { - emit MainWindowShowTextCheckDialog(text_check_result, text_check_message); + emit MainWindowShowTextCheckDialog(text_check_result, std::move(text_check_message)); } void QtSoftwareKeyboard::ShowInlineKeyboard( diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index 3ad40d2b3..6028135c5 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <QAbstractButton> +#include <QDialogButtonBox> #include <QHash> #include <QListWidgetItem> +#include <QPushButton> #include <QSignalBlocker> #include "common/settings.h" #include "core/core.h" @@ -31,6 +34,12 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, connect(ui->selectorList, &QListWidget::itemSelectionChanged, this, &ConfigureDialog::UpdateVisibleTabs); + if (Core::System::GetInstance().IsPoweredOn()) { + QPushButton* apply_button = ui->buttonBox->addButton(QDialogButtonBox::Apply); + connect(apply_button, &QAbstractButton::clicked, this, + &ConfigureDialog::HandleApplyButtonClicked); + } + adjustSize(); ui->selectorList->setCurrentRow(0); } @@ -80,6 +89,11 @@ void ConfigureDialog::RetranslateUI() { ui->tabWidget->setCurrentIndex(old_index); } +void ConfigureDialog::HandleApplyButtonClicked() { + UISettings::values.configuration_applied = true; + ApplyConfiguration(); +} + Q_DECLARE_METATYPE(QList<QWidget*>); void ConfigureDialog::PopulateSelectionList() { diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h index 570c3b941..abe019635 100644 --- a/src/yuzu/configuration/configure_dialog.h +++ b/src/yuzu/configuration/configure_dialog.h @@ -35,9 +35,10 @@ signals: private: void changeEvent(QEvent* event) override; - void RetranslateUI(); + void HandleApplyButtonClicked(); + void SetConfiguration(); void UpdateVisibleTabs(); void PopulateSelectionList(); diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp index 007d93c77..7dfcf150c 100644 --- a/src/yuzu/configuration/configure_per_game.cpp +++ b/src/yuzu/configuration/configure_per_game.cpp @@ -7,9 +7,12 @@ #include <string> #include <utility> +#include <QAbstractButton> #include <QCheckBox> +#include <QDialogButtonBox> #include <QHeaderView> #include <QMenu> +#include <QPushButton> #include <QStandardItemModel> #include <QString> #include <QTimer> @@ -45,6 +48,12 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id, std::string_vi scene = new QGraphicsScene; ui->icon_view->setScene(scene); + if (Core::System::GetInstance().IsPoweredOn()) { + QPushButton* apply_button = ui->buttonBox->addButton(QDialogButtonBox::Apply); + connect(apply_button, &QAbstractButton::clicked, this, + &ConfigurePerGame::HandleApplyButtonClicked); + } + LoadConfiguration(); } @@ -77,6 +86,11 @@ void ConfigurePerGame::RetranslateUI() { ui->retranslateUi(this); } +void ConfigurePerGame::HandleApplyButtonClicked() { + UISettings::values.configuration_applied = true; + ApplyConfiguration(); +} + void ConfigurePerGame::LoadFromFile(FileSys::VirtualFile file) { this->file = std::move(file); LoadConfiguration(); diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h index d86749a0e..dc6b68763 100644 --- a/src/yuzu/configuration/configure_per_game.h +++ b/src/yuzu/configuration/configure_per_game.h @@ -40,6 +40,8 @@ private: void changeEvent(QEvent* event) override; void RetranslateUI(); + void HandleApplyButtonClicked(); + void LoadConfiguration(); std::unique_ptr<Ui::ConfigurePerGame> ui; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 1d63ababb..dd8dd3233 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2594,12 +2594,12 @@ void GMainWindow::OnConfigure() { &GMainWindow::OnLanguageChanged); const auto result = configure_dialog.exec(); - if (result != QDialog::Accepted) { + if (result != QDialog::Accepted && !UISettings::values.configuration_applied) { return; + } else if (result == QDialog::Accepted) { + configure_dialog.ApplyConfiguration(); + controller_dialog->refreshConfiguration(); } - - configure_dialog.ApplyConfiguration(); - controller_dialog->refreshConfiguration(); InitializeHotkeys(); if (UISettings::values.theme != old_theme) { UpdateUITheme(); @@ -2614,6 +2614,8 @@ void GMainWindow::OnConfigure() { game_list->PopulateAsync(UISettings::values.game_dirs); } + UISettings::values.configuration_applied = false; + config->Save(); if ((UISettings::values.hide_mouse || Settings::values.mouse_panning) && emulation_running) { @@ -2643,23 +2645,27 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file ConfigurePerGame dialog(this, title_id, file_name); dialog.LoadFromFile(v_file); const auto result = dialog.exec(); - if (result == QDialog::Accepted) { + + if (result != QDialog::Accepted && !UISettings::values.configuration_applied) { + Settings::RestoreGlobalState(system.IsPoweredOn()); + return; + } else if (result == QDialog::Accepted) { dialog.ApplyConfiguration(); + } - const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); - if (reload) { - game_list->PopulateAsync(UISettings::values.game_dirs); - } + const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); + if (reload) { + game_list->PopulateAsync(UISettings::values.game_dirs); + } - // Do not cause the global config to write local settings into the config file - const bool is_powered_on = system.IsPoweredOn(); - Settings::RestoreGlobalState(is_powered_on); + // Do not cause the global config to write local settings into the config file + const bool is_powered_on = system.IsPoweredOn(); + Settings::RestoreGlobalState(is_powered_on); - if (!is_powered_on) { - config->Save(); - } - } else { - Settings::RestoreGlobalState(system.IsPoweredOn()); + UISettings::values.configuration_applied = false; + + if (!is_powered_on) { + config->Save(); } } diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 5ba00b8c8..49122ec32 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -95,6 +95,8 @@ struct Values { uint8_t row_2_text_id; std::atomic_bool is_game_list_reload_pending{false}; bool cache_game_list; + + bool configuration_applied; }; extern Values values; |