diff options
225 files changed, 3899 insertions, 891 deletions
diff --git a/.gitmodules b/.gitmodules index 8a90f4d15..8e98ee9cb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -27,7 +27,7 @@ url = https://github.com/KhronosGroup/Vulkan-Headers.git [submodule "sirit"] path = externals/sirit - url = https://github.com/ReinUsesLisp/sirit + url = https://github.com/yuzu-emu/sirit [submodule "mbedtls"] path = externals/mbedtls url = https://github.com/yuzu-emu/mbedtls diff --git a/CMakeLists.txt b/CMakeLists.txt index b2fbe8806..cd59e7485 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,10 @@ cmake_minimum_required(VERSION 3.22) +# Dynarmic has cmake_minimum_required(3.12) and we may want to override +# some of its variables, which is only possible in 3.13+ +set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/find-modules") @@ -27,8 +31,6 @@ CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" "${MSV option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON) -option(YUZU_USE_BUNDLED_LIBUSB "Compile bundled libusb" OFF) - option(YUZU_USE_BUNDLED_FFMPEG "Download/Build bundled FFmpeg" "${WIN32}") option(YUZU_USE_QT_MULTIMEDIA "Use QtMultimedia for Camera" OFF) @@ -39,10 +41,10 @@ option(ENABLE_CUBEB "Enables the cubeb audio backend" ON) option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF) -option(YUZU_USE_BUNDLED_OPUS "Compile bundled opus" ON) - option(YUZU_TESTS "Compile tests" ON) +option(YUZU_USE_PRECOMPILED_HEADERS "Use precompiled headers" ON) + CMAKE_DEPENDENT_OPTION(YUZU_CRASH_DUMPS "Compile Windows crash dump (Minidump) support" OFF "WIN32" OFF) option(YUZU_USE_BUNDLED_VCPKG "Use vcpkg for yuzu dependencies" "${MSVC}") @@ -64,6 +66,21 @@ elseif(NOT "$ENV{VCPKG_TOOLCHAIN_FILE}" STREQUAL "") include("$ENV{VCPKG_TOOLCHAIN_FILE}") endif() +if (YUZU_USE_PRECOMPILED_HEADERS) + if (MSVC AND CCACHE) + # buildcache does not properly cache PCH files, leading to compilation errors. + # See https://github.com/mbitsnbites/buildcache/discussions/230 + message(WARNING "buildcache does not properly support Precompiled Headers. Disabling PCH") + set(DYNARMIC_USE_PRECOMPILED_HEADERS OFF CACHE BOOL "" FORCE) + set(YUZU_USE_PRECOMPILED_HEADERS OFF CACHE BOOL "" FORCE) + endif() +endif() +if (YUZU_USE_PRECOMPILED_HEADERS) + message(STATUS "Using Precompiled Headers.") + set(CMAKE_PCH_INSTANTIATE_TEMPLATES ON) +endif() + + # Default to a Release build get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) if (NOT IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE) @@ -180,24 +197,40 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) # System imported libraries # ======================================================================= -find_package(fmt 8.0.1 REQUIRED CONFIG) -find_package(nlohmann_json 3.8 REQUIRED CONFIG) +find_package(enet 1.3) +find_package(fmt 9 REQUIRED) +find_package(inih) +find_package(libusb 1.0.24) +find_package(lz4 REQUIRED) +find_package(nlohmann_json 3.8 REQUIRED) +find_package(Opus 1.3) +find_package(Vulkan 1.3.213) find_package(ZLIB 1.2 REQUIRED) +find_package(zstd 1.5 REQUIRED) + +if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) + find_package(xbyak 6) +endif() + +if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) + find_package(dynarmic 6.4.0) +endif() -# Search for config-only package first (for vcpkg), then try non-config -find_package(zstd 1.5 CONFIG) -if (NOT zstd_FOUND) - find_package(zstd 1.5 REQUIRED) +if (ENABLE_CUBEB) + find_package(cubeb) endif() -# lz4 1.8 is required, but vcpkg's lz4-config.cmake does not have version info -find_package(lz4 CONFIG) -if (NOT lz4_FOUND) - find_package(lz4 1.8 REQUIRED) +if (USE_DISCORD_PRESENCE) + find_package(DiscordRPC) +endif() + +if (ENABLE_WEB_SERVICE) + find_package(cpp-jwt 1.4) + find_package(httplib 0.11) endif() if (YUZU_TESTS) - find_package(Catch2 2.13.7 REQUIRED CONFIG) + find_package(Catch2 2.13.7 REQUIRED) endif() find_package(Boost 1.73.0 COMPONENTS context) @@ -399,23 +432,13 @@ if (ENABLE_SDL2) set(SDL2_LIBRARY "${SDL2_PREFIX}/lib/x64/SDL2.lib" CACHE PATH "Path to SDL2 library") set(SDL2_DLL_DIR "${SDL2_PREFIX}/lib/x64/" CACHE PATH "Path to SDL2.dll") - add_library(SDL2 INTERFACE) - target_link_libraries(SDL2 INTERFACE "${SDL2_LIBRARY}") - target_include_directories(SDL2 INTERFACE "${SDL2_INCLUDE_DIR}") + add_library(SDL2::SDL2 INTERFACE IMPORTED) + target_link_libraries(SDL2::SDL2 INTERFACE "${SDL2_LIBRARY}") + target_include_directories(SDL2::SDL2 INTERFACE "${SDL2_INCLUDE_DIR}") elseif (YUZU_USE_EXTERNAL_SDL2) message(STATUS "Using SDL2 from externals.") else() find_package(SDL2 2.0.18 REQUIRED) - - # Some installations don't set SDL2_LIBRARIES - if("${SDL2_LIBRARIES}" STREQUAL "") - message(WARNING "SDL2_LIBRARIES wasn't set, manually setting to SDL2::SDL2") - set(SDL2_LIBRARIES "SDL2::SDL2") - endif() - - include_directories(SYSTEM ${SDL2_INCLUDE_DIRS}) - add_library(SDL2 INTERFACE) - target_link_libraries(SDL2 INTERFACE "${SDL2_LIBRARIES}") endif() endif() @@ -427,25 +450,6 @@ if (TARGET Boost::boost) add_library(boost ALIAS Boost::boost) endif() -# Ensure libusb is properly configured (based on dolphin libusb include) -if(NOT APPLE AND NOT YUZU_USE_BUNDLED_LIBUSB) - find_package(PkgConfig) - if (PKG_CONFIG_FOUND AND NOT CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD") - pkg_check_modules(LIBUSB QUIET libusb-1.0>=1.0.24) - else() - find_package(LibUSB) - endif() - - if (LIBUSB_FOUND) - add_library(usb INTERFACE) - target_include_directories(usb INTERFACE "${LIBUSB_INCLUDE_DIRS}") - target_link_libraries(usb INTERFACE "${LIBUSB_LIBRARIES}") - else() - message(WARNING "libusb not found, falling back to externals") - set(YUZU_USE_BUNDLED_LIBUSB ON) - endif() -endif() - # List of all FFmpeg components required set(FFmpeg_COMPONENTS avcodec diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 9740e017c..4ffafd18c 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -6,18 +6,16 @@ list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/externals/find-modules") include(DownloadExternals) # xbyak -if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) - add_subdirectory(xbyak) +if ((ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) AND NOT TARGET xbyak::xbyak) + add_subdirectory(xbyak EXCLUDE_FROM_ALL) endif() # Dynarmic -if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) - if (ARCHITECTURE_arm64) - set(DYNARMIC_FRONTENDS "A32") - endif() +if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) AND NOT TARGET dynarmic::dynarmic) set(DYNARMIC_NO_BUNDLED_FMT ON) set(DYNARMIC_IGNORE_ASSERTS ON CACHE BOOL "" FORCE) - add_subdirectory(dynarmic) + add_subdirectory(dynarmic EXCLUDE_FROM_ALL) + add_library(dynarmic::dynarmic ALIAS dynarmic) endif() # getopt @@ -29,7 +27,9 @@ endif() add_subdirectory(glad) # inih -add_subdirectory(inih) +if (NOT TARGET inih::INIReader) + add_subdirectory(inih) +endif() # mbedtls add_subdirectory(mbedtls EXCLUDE_FROM_ALL) @@ -45,8 +45,8 @@ if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12" AND CMAKE_CXX_COMPILER endif() # libusb -if (NOT LIBUSB_FOUND OR YUZU_USE_BUNDLED_LIBUSB) - add_subdirectory(libusb) +if (NOT TARGET libusb::usb) + add_subdirectory(libusb EXCLUDE_FROM_ALL) endif() # SDL2 @@ -67,30 +67,38 @@ if (YUZU_USE_EXTERNAL_SDL2) endif() set(SDL_STATIC ON) set(SDL_SHARED OFF) + if (APPLE) + set(SDL_FILE ON) + endif() add_subdirectory(SDL EXCLUDE_FROM_ALL) endif() # ENet -add_subdirectory(enet) -target_include_directories(enet INTERFACE ./enet/include) +if (NOT TARGET enet::enet) + add_subdirectory(enet EXCLUDE_FROM_ALL) + target_include_directories(enet INTERFACE ./enet/include) + add_library(enet::enet ALIAS enet) +endif() # Cubeb -if(ENABLE_CUBEB) +if (ENABLE_CUBEB AND NOT TARGET cubeb::cubeb) set(BUILD_TESTS OFF CACHE BOOL "") add_subdirectory(cubeb EXCLUDE_FROM_ALL) + add_library(cubeb::cubeb ALIAS cubeb) endif() # DiscordRPC -if (USE_DISCORD_PRESENCE) +if (USE_DISCORD_PRESENCE AND NOT TARGET DiscordRPC::discord-rpc) add_subdirectory(discord-rpc EXCLUDE_FROM_ALL) target_include_directories(discord-rpc INTERFACE ./discord-rpc/include) + add_library(DiscordRPC::discord-rpc ALIAS discord-rpc) endif() # Sirit -add_subdirectory(sirit) +add_subdirectory(sirit EXCLUDE_FROM_ALL) -if (ENABLE_WEB_SERVICE) +if (ENABLE_WEB_SERVICE AND NOT TARGET httplib::httplib) if (NOT WIN32) find_package(OpenSSL 1.1) if (OPENSSL_FOUND) @@ -118,18 +126,20 @@ if (ENABLE_WEB_SERVICE) if (WIN32) target_link_libraries(httplib INTERFACE crypt32 cryptui ws2_32) endif() - - # cpp-jwt + add_library(httplib::httplib ALIAS httplib) +endif() + +# cpp-jwt +if (ENABLE_WEB_SERVICE AND NOT TARGET cpp-jwt::cpp-jwt) add_library(cpp-jwt INTERFACE) target_include_directories(cpp-jwt INTERFACE ./cpp-jwt/include) target_compile_definitions(cpp-jwt INTERFACE CPP_JWT_USE_VENDORED_NLOHMANN_JSON) + add_library(cpp-jwt::cpp-jwt ALIAS cpp-jwt) endif() # Opus -if (YUZU_USE_BUNDLED_OPUS) +if (NOT TARGET Opus::opus) add_subdirectory(opus EXCLUDE_FROM_ALL) -else() - find_package(Opus 1.3 REQUIRED) endif() # FFMpeg @@ -140,3 +150,8 @@ if (YUZU_USE_BUNDLED_FFMPEG) set(FFmpeg_LIBRARIES "${FFmpeg_LIBRARIES}" PARENT_SCOPE) set(FFmpeg_INCLUDE_DIR "${FFmpeg_INCLUDE_DIR}" PARENT_SCOPE) endif() + +# Vulkan-Headers +if (NOT TARGET Vulkan::Headers) + add_subdirectory(Vulkan-Headers EXCLUDE_FROM_ALL) +endif() diff --git a/externals/SDL b/externals/SDL -Subproject b424665e0899769b200231ba943353a5fee1b6b +Subproject f17058b562c8a1090c0c996b42982721ace9090 diff --git a/externals/Vulkan-Headers b/externals/Vulkan-Headers -Subproject 33d4dd987fc8fc6475ff9ca2b4f0c3cc6e79333 +Subproject 2826791bed6a793f164bf534cd859968f13df8a diff --git a/externals/dynarmic b/externals/dynarmic -Subproject 07c614f91b0af5335e1f9c0653c2d75e7b5f53b +Subproject bd570e093ca1d1206961296b90df65cda7de8e8 diff --git a/externals/find-modules/FindDiscordRPC.cmake b/externals/find-modules/FindDiscordRPC.cmake new file mode 100644 index 000000000..44ca9904f --- /dev/null +++ b/externals/find-modules/FindDiscordRPC.cmake @@ -0,0 +1,27 @@ +# SPDX-FileCopyrightText: 2022 Alexandre Bouvier <contact@amb.tf> +# +# SPDX-License-Identifier: GPL-3.0-or-later + +find_path(DiscordRPC_INCLUDE_DIR discord_rpc.h) + +find_library(DiscordRPC_LIBRARY discord-rpc) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(DiscordRPC + REQUIRED_VARS + DiscordRPC_LIBRARY + DiscordRPC_INCLUDE_DIR +) + +if (DiscordRPC_FOUND AND NOT TARGET DiscordRPC::discord-rpc) + add_library(DiscordRPC::discord-rpc UNKNOWN IMPORTED) + set_target_properties(DiscordRPC::discord-rpc PROPERTIES + IMPORTED_LOCATION "${DiscordRPC_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${DiscordRPC_INCLUDE_DIR}" + ) +endif() + +mark_as_advanced( + DiscordRPC_INCLUDE_DIR + DiscordRPC_LIBRARY +) diff --git a/externals/find-modules/FindLibUSB.cmake b/externals/find-modules/FindLibUSB.cmake deleted file mode 100644 index 617daf9a5..000000000 --- a/externals/find-modules/FindLibUSB.cmake +++ /dev/null @@ -1,44 +0,0 @@ -# SPDX-FileCopyrightText: 2009 Michal Cihar <michal@cihar.com> -# SPDX-License-Identifier: GPL-2.0-or-later - -# - Find libusb-1.0 library -# This module defines -# LIBUSB_INCLUDE_DIR, where to find bluetooth.h -# LIBUSB_LIBRARIES, the libraries needed to use libusb-1.0. -# LIBUSB_FOUND, If false, do not try to use libusb-1.0. -# -# vim: expandtab sw=4 ts=4 sts=4: - -if(ANDROID) - set(LIBUSB_FOUND FALSE CACHE INTERNAL "libusb-1.0 found") - message(STATUS "libusb-1.0 not found.") -elseif (NOT LIBUSB_FOUND) - pkg_check_modules (LIBUSB_PKG libusb-1.0) - - find_path(LIBUSB_INCLUDE_DIR NAMES libusb.h - PATHS - ${LIBUSB_PKG_INCLUDE_DIRS} - /usr/include/libusb-1.0 - /usr/include - /usr/local/include/libusb-1.0 - /usr/local/include - ) - - find_library(LIBUSB_LIBRARIES NAMES usb-1.0 usb - PATHS - ${LIBUSB_PKG_LIBRARY_DIRS} - /usr/lib - /usr/local/lib - ) - - if(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES) - set(LIBUSB_FOUND TRUE CACHE INTERNAL "libusb-1.0 found") - message(STATUS "Found libusb-1.0: ${LIBUSB_INCLUDE_DIR}, ${LIBUSB_LIBRARIES}") - else(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES) - set(LIBUSB_FOUND FALSE CACHE INTERNAL "libusb-1.0 found") - message(STATUS "libusb-1.0 not found.") - endif(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES) - - mark_as_advanced(LIBUSB_INCLUDE_DIR LIBUSB_LIBRARIES) -endif () - diff --git a/externals/find-modules/FindOpus.cmake b/externals/find-modules/FindOpus.cmake index b68a6046b..2ba515352 100644 --- a/externals/find-modules/FindOpus.cmake +++ b/externals/find-modules/FindOpus.cmake @@ -1,19 +1,17 @@ # SPDX-FileCopyrightText: 2022 yuzu Emulator Project # SPDX-License-Identifier: GPL-2.0-or-later -find_package(PkgConfig) - +find_package(PkgConfig QUIET) if (PKG_CONFIG_FOUND) - pkg_search_module(opus IMPORTED_TARGET GLOBAL opus) - if (opus_FOUND) - add_library(Opus::opus ALIAS PkgConfig::opus) - endif() + pkg_search_module(OPUS QUIET IMPORTED_TARGET opus) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Opus - REQUIRED_VARS - opus_LINK_LIBRARIES - opus_FOUND - VERSION_VAR opus_VERSION + REQUIRED_VARS OPUS_LINK_LIBRARIES + VERSION_VAR OPUS_VERSION ) + +if (Opus_FOUND AND NOT TARGET Opus::opus) + add_library(Opus::opus ALIAS PkgConfig::OPUS) +endif() diff --git a/externals/find-modules/Findenet.cmake b/externals/find-modules/Findenet.cmake new file mode 100644 index 000000000..6dae76f4c --- /dev/null +++ b/externals/find-modules/Findenet.cmake @@ -0,0 +1,18 @@ +# SPDX-FileCopyrightText: 2022 Alexandre Bouvier <contact@amb.tf> +# +# SPDX-License-Identifier: GPL-3.0-or-later + +find_package(PkgConfig QUIET) +if (PKG_CONFIG_FOUND) + pkg_search_module(ENET QUIET IMPORTED_TARGET libenet) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(enet + REQUIRED_VARS ENET_LINK_LIBRARIES + VERSION_VAR ENET_VERSION +) + +if (enet_FOUND AND NOT TARGET enet::enet) + add_library(enet::enet ALIAS PkgConfig::ENET) +endif() diff --git a/externals/find-modules/Findhttplib.cmake b/externals/find-modules/Findhttplib.cmake new file mode 100644 index 000000000..b72bad076 --- /dev/null +++ b/externals/find-modules/Findhttplib.cmake @@ -0,0 +1,23 @@ +# SPDX-FileCopyrightText: 2022 Andrea Pappacoda <andrea@pappacoda.it> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +include(FindPackageHandleStandardArgs) + +find_package(httplib QUIET CONFIG) +if (httplib_FOUND) + find_package_handle_standard_args(httplib CONFIG_MODE) +else() + find_package(PkgConfig QUIET) + if (PKG_CONFIG_FOUND) + pkg_search_module(HTTPLIB QUIET IMPORTED_TARGET cpp-httplib) + endif() + find_package_handle_standard_args(httplib + REQUIRED_VARS HTTPLIB_INCLUDEDIR + VERSION_VAR HTTPLIB_VERSION + ) +endif() + +if (httplib_FOUND AND NOT TARGET httplib::httplib) + add_library(httplib::httplib ALIAS PkgConfig::HTTPLIB) +endif() diff --git a/externals/find-modules/Findinih.cmake b/externals/find-modules/Findinih.cmake new file mode 100644 index 000000000..8d1a07243 --- /dev/null +++ b/externals/find-modules/Findinih.cmake @@ -0,0 +1,18 @@ +# SPDX-FileCopyrightText: 2022 Alexandre Bouvier <contact@amb.tf> +# +# SPDX-License-Identifier: GPL-3.0-or-later + +find_package(PkgConfig QUIET) +if (PKG_CONFIG_FOUND) + pkg_search_module(INIREADER QUIET IMPORTED_TARGET INIReader) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(inih + REQUIRED_VARS INIREADER_LINK_LIBRARIES + VERSION_VAR INIREADER_VERSION +) + +if (inih_FOUND AND NOT TARGET inih::INIReader) + add_library(inih::INIReader ALIAS PkgConfig::INIREADER) +endif() diff --git a/externals/find-modules/Findlibusb.cmake b/externals/find-modules/Findlibusb.cmake new file mode 100644 index 000000000..66f61001c --- /dev/null +++ b/externals/find-modules/Findlibusb.cmake @@ -0,0 +1,18 @@ +# SPDX-FileCopyrightText: 2022 Alexandre Bouvier <contact@amb.tf> +# +# SPDX-License-Identifier: GPL-3.0-or-later + +find_package(PkgConfig QUIET) +if (PKG_CONFIG_FOUND) + pkg_search_module(LIBUSB QUIET IMPORTED_TARGET libusb-1.0) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(libusb + REQUIRED_VARS LIBUSB_LINK_LIBRARIES + VERSION_VAR LIBUSB_VERSION +) + +if (libusb_FOUND AND NOT TARGET libusb::usb) + add_library(libusb::usb ALIAS PkgConfig::LIBUSB) +endif() diff --git a/externals/find-modules/Findlz4.cmake b/externals/find-modules/Findlz4.cmake index 13ca5de66..f4c7005ba 100644 --- a/externals/find-modules/Findlz4.cmake +++ b/externals/find-modules/Findlz4.cmake @@ -1,19 +1,28 @@ # SPDX-FileCopyrightText: 2022 yuzu Emulator Project # SPDX-License-Identifier: GPL-2.0-or-later -find_package(PkgConfig) +include(FindPackageHandleStandardArgs) -if (PKG_CONFIG_FOUND) - pkg_search_module(liblz4 IMPORTED_TARGET GLOBAL liblz4) - if (liblz4_FOUND) - add_library(lz4::lz4 ALIAS PkgConfig::liblz4) +find_package(lz4 QUIET CONFIG) +if (lz4_FOUND) + find_package_handle_standard_args(lz4 CONFIG_MODE) +else() + find_package(PkgConfig QUIET) + if (PKG_CONFIG_FOUND) + pkg_search_module(LZ4 QUIET IMPORTED_TARGET liblz4) endif() + find_package_handle_standard_args(lz4 + REQUIRED_VARS LZ4_LINK_LIBRARIES + VERSION_VAR LZ4_VERSION + ) endif() -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(lz4 - REQUIRED_VARS - liblz4_LINK_LIBRARIES - liblz4_FOUND - VERSION_VAR liblz4_VERSION -) +if (lz4_FOUND AND NOT TARGET lz4::lz4) + if (TARGET LZ4::lz4_shared) + add_library(lz4::lz4 ALIAS LZ4::lz4_shared) + elseif (TARGET LZ4::lz4_static) + add_library(lz4::lz4 ALIAS LZ4::lz4_static) + else() + add_library(lz4::lz4 ALIAS PkgConfig::LZ4) + endif() +endif() diff --git a/externals/find-modules/Findzstd.cmake b/externals/find-modules/Findzstd.cmake index f4031eb70..1aacc41d0 100644 --- a/externals/find-modules/Findzstd.cmake +++ b/externals/find-modules/Findzstd.cmake @@ -1,19 +1,28 @@ # SPDX-FileCopyrightText: 2022 yuzu Emulator Project # SPDX-License-Identifier: GPL-2.0-or-later -find_package(PkgConfig) +include(FindPackageHandleStandardArgs) -if (PKG_CONFIG_FOUND) - pkg_search_module(libzstd IMPORTED_TARGET GLOBAL libzstd) - if (libzstd_FOUND) - add_library(zstd::zstd ALIAS PkgConfig::libzstd) +find_package(zstd QUIET CONFIG) +if (zstd_FOUND) + find_package_handle_standard_args(zstd CONFIG_MODE) +else() + find_package(PkgConfig QUIET) + if (PKG_CONFIG_FOUND) + pkg_search_module(ZSTD QUIET IMPORTED_TARGET libzstd) endif() + find_package_handle_standard_args(zstd + REQUIRED_VARS ZSTD_LINK_LIBRARIES + VERSION_VAR ZSTD_VERSION + ) endif() -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(zstd - REQUIRED_VARS - libzstd_LINK_LIBRARIES - libzstd_FOUND - VERSION_VAR libzstd_VERSION -) +if (zstd_FOUND AND NOT TARGET zstd::zstd) + if (TARGET zstd::libzstd_shared) + add_library(zstd::zstd ALIAS zstd::libzstd_shared) + elseif (TARGET zstd::libzstd_static) + add_library(zstd::zstd ALIAS zstd::libzstd_static) + else() + add_library(zstd::zstd ALIAS PkgConfig::ZSTD) + endif() +endif() diff --git a/externals/inih/CMakeLists.txt b/externals/inih/CMakeLists.txt index b686e3cf5..ebb60a976 100644 --- a/externals/inih/CMakeLists.txt +++ b/externals/inih/CMakeLists.txt @@ -9,4 +9,5 @@ add_library(inih ) create_target_directory_groups(inih) -target_include_directories(inih INTERFACE .) +target_include_directories(inih INTERFACE inih/cpp) +add_library(inih::INIReader ALIAS inih) diff --git a/externals/libusb/CMakeLists.txt b/externals/libusb/CMakeLists.txt index 3cb1b3687..6317ea807 100644 --- a/externals/libusb/CMakeLists.txt +++ b/externals/libusb/CMakeLists.txt @@ -273,3 +273,5 @@ else() # MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux") configure_file(config.h.in config.h) endif() # MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux") + +add_library(libusb::usb ALIAS usb) diff --git a/externals/sirit b/externals/sirit -Subproject aa292d56650bc28f2b2d75973fab2e61d0136f9 +Subproject d7ad93a88864bda94e282e95028f90b5784e4d2 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0ac3d254e..140415474 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -82,8 +82,9 @@ if (MSVC) /wd4324 # 'struct_name': structure was padded due to __declspec(align()) ) - if (USE_CCACHE) + if (USE_CCACHE OR YUZU_USE_PRECOMPILED_HEADERS) # when caching, we need to use /Z7 to downgrade debug info to use an older but more cachable format + # Precompiled headers are deleted if not using /Z7. See https://github.com/nanoant/CMakePCHCompiler/issues/21 add_compile_options(/Z7) else() add_compile_options(/Zi) @@ -112,6 +113,8 @@ else() $<$<CXX_COMPILER_ID:Clang>:-Wno-braced-scalar-init> $<$<CXX_COMPILER_ID:Clang>:-Wno-unused-private-field> + $<$<CXX_COMPILER_ID:AppleClang>:-Wno-braced-scalar-init> + $<$<CXX_COMPILER_ID:AppleClang>:-Wno-unused-private-field> ) if (ARCHITECTURE_x86_64) diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 75416c53a..420ba62e0 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -31,6 +31,7 @@ add_library(audio_core STATIC out/audio_out.h out/audio_out_system.cpp out/audio_out_system.h + precompiled_headers.h renderer/adsp/adsp.cpp renderer/adsp/adsp.h renderer/adsp/audio_renderer.cpp @@ -218,18 +219,18 @@ endif() target_link_libraries(audio_core PUBLIC common core) if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) - target_link_libraries(audio_core PRIVATE dynarmic) + target_link_libraries(audio_core PRIVATE dynarmic::dynarmic) endif() if(ENABLE_CUBEB) - target_link_libraries(audio_core PRIVATE cubeb) + target_link_libraries(audio_core PRIVATE cubeb::cubeb) target_compile_definitions(audio_core PRIVATE -DHAVE_CUBEB=1) endif() if(ENABLE_SDL2) - if (YUZU_USE_EXTERNAL_SDL2) - target_link_libraries(audio_core PRIVATE SDL2-static) - else() - target_link_libraries(audio_core PRIVATE SDL2) - endif() + target_link_libraries(audio_core PRIVATE SDL2::SDL2) target_compile_definitions(audio_core PRIVATE HAVE_SDL2) endif() + +if (YUZU_USE_PRECOMPILED_HEADERS) + target_precompile_headers(audio_core PRIVATE precompiled_headers.h) +endif() diff --git a/src/audio_core/audio_event.cpp b/src/audio_core/audio_event.cpp index 424049c7a..d15568e1f 100644 --- a/src/audio_core/audio_event.cpp +++ b/src/audio_core/audio_event.cpp @@ -3,6 +3,7 @@ #include "audio_core/audio_event.h" #include "common/assert.h" +#include "common/polyfill_ranges.h" namespace AudioCore { diff --git a/src/audio_core/audio_manager.h b/src/audio_core/audio_manager.h index abf077de4..02270242a 100644 --- a/src/audio_core/audio_manager.h +++ b/src/audio_core/audio_manager.h @@ -9,6 +9,8 @@ #include <mutex> #include <thread> +#include "common/polyfill_thread.h" + #include "audio_core/audio_event.h" union Result; diff --git a/src/audio_core/audio_render_manager.h b/src/audio_core/audio_render_manager.h index bf4837190..fffa5944d 100644 --- a/src/audio_core/audio_render_manager.h +++ b/src/audio_core/audio_render_manager.h @@ -7,6 +7,8 @@ #include <memory> #include <mutex> +#include "common/polyfill_thread.h" + #include "audio_core/common/common.h" #include "audio_core/renderer/system_manager.h" #include "core/hle/service/audio/errors.h" diff --git a/src/audio_core/common/feature_support.h b/src/audio_core/common/feature_support.h index 55c9e690d..e71905ae8 100644 --- a/src/audio_core/common/feature_support.h +++ b/src/audio_core/common/feature_support.h @@ -10,6 +10,7 @@ #include "common/assert.h" #include "common/common_funcs.h" #include "common/common_types.h" +#include "common/polyfill_ranges.h" namespace AudioCore { constexpr u32 CurrentRevision = 11; diff --git a/src/audio_core/precompiled_headers.h b/src/audio_core/precompiled_headers.h new file mode 100644 index 000000000..aabae730b --- /dev/null +++ b/src/audio_core/precompiled_headers.h @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_precompiled_headers.h" diff --git a/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp b/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp index c4bf3943a..2187d8a65 100644 --- a/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp +++ b/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp @@ -5,6 +5,7 @@ #include "audio_core/renderer/adsp/command_list_processor.h" #include "audio_core/renderer/command/effect/i3dl2_reverb.h" +#include "common/polyfill_ranges.h" namespace AudioCore::AudioRenderer { diff --git a/src/audio_core/renderer/command/effect/reverb.cpp b/src/audio_core/renderer/command/effect/reverb.cpp index fe2b1eb43..427489214 100644 --- a/src/audio_core/renderer/command/effect/reverb.cpp +++ b/src/audio_core/renderer/command/effect/reverb.cpp @@ -6,6 +6,7 @@ #include "audio_core/renderer/adsp/command_list_processor.h" #include "audio_core/renderer/command/effect/reverb.h" +#include "common/polyfill_ranges.h" namespace AudioCore::AudioRenderer { diff --git a/src/audio_core/renderer/mix/mix_context.cpp b/src/audio_core/renderer/mix/mix_context.cpp index 2427c83ed..35b748ede 100644 --- a/src/audio_core/renderer/mix/mix_context.cpp +++ b/src/audio_core/renderer/mix/mix_context.cpp @@ -5,6 +5,7 @@ #include "audio_core/renderer/mix/mix_context.h" #include "audio_core/renderer/splitter/splitter_context.h" +#include "common/polyfill_ranges.h" namespace AudioCore::AudioRenderer { diff --git a/src/audio_core/renderer/voice/voice_context.cpp b/src/audio_core/renderer/voice/voice_context.cpp index a501a677d..16a3e839d 100644 --- a/src/audio_core/renderer/voice/voice_context.cpp +++ b/src/audio_core/renderer/voice/voice_context.cpp @@ -4,6 +4,7 @@ #include <ranges> #include "audio_core/renderer/voice/voice_context.h" +#include "common/polyfill_ranges.h" namespace AudioCore::AudioRenderer { diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp index 67e194e3c..06c2a876e 100644 --- a/src/audio_core/sink/sink_stream.cpp +++ b/src/audio_core/sink/sink_stream.cpp @@ -170,8 +170,8 @@ void SinkStream::ProcessAudioIn(std::span<const s16> input_buffer, std::size_t n // Get the minimum frames available between the currently playing buffer, and the // amount we have left to fill - size_t frames_available{std::min(playing_buffer.frames - playing_buffer.frames_played, - num_frames - frames_written)}; + size_t frames_available{std::min<u64>(playing_buffer.frames - playing_buffer.frames_played, + num_frames - frames_written)}; samples_buffer.Push(&input_buffer[frames_written * frame_size], frames_available * frame_size); @@ -241,8 +241,8 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz // Get the minimum frames available between the currently playing buffer, and the // amount we have left to fill - size_t frames_available{std::min(playing_buffer.frames - playing_buffer.frames_played, - num_frames - frames_written)}; + size_t frames_available{std::min<u64>(playing_buffer.frames - playing_buffer.frames_played, + num_frames - frames_written)}; samples_buffer.Pop(&output_buffer[frames_written * frame_size], frames_available * frame_size); diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index b7c15c191..6bdffcb7a 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -37,6 +37,7 @@ add_library(common STATIC cache_management.cpp cache_management.h common_funcs.h + common_precompiled_headers.h common_types.h concepts.h div_ceil.h @@ -95,6 +96,7 @@ add_library(common STATIC param_package.h parent_of_member.h point.h + precompiled_headers.h quaternion.h reader_writer_queue.h ring_buffer.h @@ -147,7 +149,7 @@ if(ARCHITECTURE_x86_64) x64/xbyak_abi.h x64/xbyak_util.h ) - target_link_libraries(common PRIVATE xbyak) + target_link_libraries(common PRIVATE xbyak::xbyak) endif() if (MSVC) @@ -172,14 +174,8 @@ endif() create_target_directory_groups(common) target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads) -if (TARGET lz4::lz4) - target_link_libraries(common PRIVATE lz4::lz4) -else() - target_link_libraries(common PRIVATE LZ4::lz4_shared) -endif() -if (TARGET zstd::zstd) - target_link_libraries(common PRIVATE zstd::zstd) -else() - target_link_libraries(common PRIVATE - $<IF:$<TARGET_EXISTS:zstd::libzstd_shared>,zstd::libzstd_shared,zstd::libzstd_static>) +target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd) + +if (YUZU_USE_PRECOMPILED_HEADERS) + target_precompile_headers(common PRIVATE precompiled_headers.h) endif() diff --git a/src/common/common_precompiled_headers.h b/src/common/common_precompiled_headers.h new file mode 100644 index 000000000..be7e5b5f9 --- /dev/null +++ b/src/common/common_precompiled_headers.h @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <algorithm> +#include <array> +#include <chrono> +#include <memory> + +#include <fmt/format.h> + +#include "common/assert.h" +#include "common/common_types.h" diff --git a/src/common/fs/file.cpp b/src/common/fs/file.cpp index fa8422c41..656b03cc5 100644 --- a/src/common/fs/file.cpp +++ b/src/common/fs/file.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include <vector> + #include "common/fs/file.h" #include "common/fs/fs.h" #include "common/logging/log.h" diff --git a/src/common/fs/fs_util.cpp b/src/common/fs/fs_util.cpp index eb4ac1deb..813a713c3 100644 --- a/src/common/fs/fs_util.cpp +++ b/src/common/fs/fs_util.cpp @@ -4,6 +4,7 @@ #include <algorithm> #include "common/fs/fs_util.h" +#include "common/polyfill_ranges.h" namespace Common::FS { diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp index 1074f2421..defa3e918 100644 --- a/src/common/fs/path_util.cpp +++ b/src/common/fs/path_util.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> +#include <sstream> #include <unordered_map> #include "common/fs/fs.h" diff --git a/src/common/input.h b/src/common/input.h index 449e0193f..fc14fd7bf 100644 --- a/src/common/input.h +++ b/src/common/input.h @@ -8,6 +8,7 @@ #include <string> #include <unordered_map> #include <utility> +#include <vector> #include "common/logging/log.h" #include "common/param_package.h" #include "common/uuid.h" diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 15d92505e..2a3bded40 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -4,7 +4,6 @@ #include <atomic> #include <chrono> #include <climits> -#include <stop_token> #include <thread> #include <fmt/format.h> @@ -18,6 +17,7 @@ #include "common/fs/fs_paths.h" #include "common/fs/path_util.h" #include "common/literals.h" +#include "common/polyfill_thread.h" #include "common/thread.h" #include "common/logging/backend.h" diff --git a/src/common/polyfill_ranges.h b/src/common/polyfill_ranges.h new file mode 100644 index 000000000..ca44bfaef --- /dev/null +++ b/src/common/polyfill_ranges.h @@ -0,0 +1,530 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +// +// TODO: remove this file when ranges are supported by all compilation targets +// + +#pragma once + +#include <algorithm> +#include <utility> +#include <version> + +#ifndef __cpp_lib_ranges + +namespace std { +namespace ranges { + +template <typename T> +concept range = requires(T& t) { + begin(t); + end(t); +}; + +template <typename T> +concept input_range = range<T>; + +template <typename T> +concept output_range = range<T>; + +template <range R> +using range_difference_t = ptrdiff_t; + +// +// find, find_if, find_if_not +// + +struct find_fn { + template <typename Iterator, typename T, typename Proj = std::identity> + constexpr Iterator operator()(Iterator first, Iterator last, const T& value, + Proj proj = {}) const { + for (; first != last; ++first) { + if (std::invoke(proj, *first) == value) { + return first; + } + } + return first; + } + + template <ranges::input_range R, typename T, typename Proj = std::identity> + constexpr ranges::iterator_t<R> operator()(R&& r, const T& value, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), value, std::ref(proj)); + } +}; + +struct find_if_fn { + template <typename Iterator, typename Proj = std::identity, typename Pred> + constexpr Iterator operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const { + for (; first != last; ++first) { + if (std::invoke(pred, std::invoke(proj, *first))) { + return first; + } + } + return first; + } + + template <ranges::input_range R, typename Proj = std::identity, typename Pred> + constexpr ranges::iterator_t<R> operator()(R&& r, Pred pred, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj)); + } +}; + +struct find_if_not_fn { + template <typename Iterator, typename Proj = std::identity, typename Pred> + constexpr Iterator operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const { + for (; first != last; ++first) { + if (!std::invoke(pred, std::invoke(proj, *first))) { + return first; + } + } + return first; + } + + template <ranges::input_range R, typename Proj = std::identity, typename Pred> + constexpr ranges::iterator_t<R> operator()(R&& r, Pred pred, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj)); + } +}; + +inline constexpr find_fn find; +inline constexpr find_if_fn find_if; +inline constexpr find_if_not_fn find_if_not; + +// +// any_of, all_of, none_of +// + +struct all_of_fn { + template <typename Iterator, typename Proj = std::identity, typename Pred> + constexpr bool operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const { + return ranges::find_if_not(first, last, std::ref(pred), std::ref(proj)) == last; + } + + template <ranges::input_range R, typename Proj = std::identity, typename Pred> + constexpr bool operator()(R&& r, Pred pred, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj)); + } +}; + +struct any_of_fn { + template <typename Iterator, typename Proj = std::identity, typename Pred> + constexpr bool operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const { + return ranges::find_if(first, last, std::ref(pred), std::ref(proj)) != last; + } + + template <ranges::input_range R, typename Proj = std::identity, typename Pred> + constexpr bool operator()(R&& r, Pred pred, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj)); + } +}; + +struct none_of_fn { + template <typename Iterator, typename Proj = std::identity, typename Pred> + constexpr bool operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const { + return ranges::find_if(first, last, std::ref(pred), std::ref(proj)) == last; + } + + template <ranges::input_range R, typename Proj = std::identity, typename Pred> + constexpr bool operator()(R&& r, Pred pred, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj)); + } +}; + +inline constexpr any_of_fn any_of; +inline constexpr all_of_fn all_of; +inline constexpr none_of_fn none_of; + +// +// count, count_if +// + +struct count_fn { + template <typename Iterator, typename T, typename Proj = std::identity> + constexpr ptrdiff_t operator()(Iterator first, Iterator last, const T& value, + Proj proj = {}) const { + ptrdiff_t counter = 0; + for (; first != last; ++first) + if (std::invoke(proj, *first) == value) + ++counter; + return counter; + } + + template <ranges::input_range R, typename T, typename Proj = std::identity> + constexpr ptrdiff_t operator()(R&& r, const T& value, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), value, std::ref(proj)); + } +}; + +struct count_if_fn { + template <typename Iterator, typename Proj = std::identity, typename Pred> + constexpr ptrdiff_t operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const { + ptrdiff_t counter = 0; + for (; first != last; ++first) + if (std::invoke(pred, std::invoke(proj, *first))) + ++counter; + return counter; + } + + template <ranges::input_range R, typename Proj = std::identity, typename Pred> + constexpr ptrdiff_t operator()(R&& r, Pred pred, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj)); + } +}; + +inline constexpr count_fn count; +inline constexpr count_if_fn count_if; + +// +// transform +// + +struct transform_fn { + template <typename InputIterator, typename OutputIterator, typename F, + typename Proj = std::identity> + constexpr void operator()(InputIterator first1, InputIterator last1, OutputIterator result, + F op, Proj proj = {}) const { + for (; first1 != last1; ++first1, (void)++result) { + *result = std::invoke(op, std::invoke(proj, *first1)); + } + } + + template <ranges::input_range R, typename OutputIterator, typename F, + typename Proj = std::identity> + constexpr void operator()(R&& r, OutputIterator result, F op, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), result, std::ref(op), std::ref(proj)); + } +}; + +inline constexpr transform_fn transform; + +// +// sort +// + +struct sort_fn { + template <typename Iterator, typename Comp = ranges::less, typename Proj = std::identity> + constexpr void operator()(Iterator first, Iterator last, Comp comp = {}, Proj proj = {}) const { + if (first == last) + return; + + Iterator last_iter = ranges::next(first, last); + std::sort(first, last_iter, + [&](auto& lhs, auto& rhs) { return comp(proj(lhs), proj(rhs)); }); + } + + template <ranges::input_range R, typename Comp = ranges::less, typename Proj = std::identity> + constexpr void operator()(R&& r, Comp comp = {}, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), std::move(comp), std::move(proj)); + } +}; + +inline constexpr sort_fn sort; + +// +// fill +// + +struct fill_fn { + template <typename T, typename OutputIterator> + constexpr OutputIterator operator()(OutputIterator first, OutputIterator last, + const T& value) const { + while (first != last) { + *first++ = value; + } + + return first; + } + + template <typename T, ranges::output_range R> + constexpr ranges::iterator_t<R> operator()(R&& r, const T& value) const { + return operator()(ranges::begin(r), ranges::end(r), value); + } +}; + +inline constexpr fill_fn fill; + +// +// for_each +// + +struct for_each_fn { + template <typename Iterator, typename Proj = std::identity, typename Fun> + constexpr void operator()(Iterator first, Iterator last, Fun f, Proj proj = {}) const { + for (; first != last; ++first) { + std::invoke(f, std::invoke(proj, *first)); + } + } + + template <ranges::input_range R, typename Proj = std::identity, typename Fun> + constexpr void operator()(R&& r, Fun f, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), std::move(f), std::ref(proj)); + } +}; + +inline constexpr for_each_fn for_each; + +// +// min_element, max_element +// + +struct min_element_fn { + template <typename Iterator, typename Proj = std::identity, typename Comp = ranges::less> + constexpr Iterator operator()(Iterator first, Iterator last, Comp comp = {}, + Proj proj = {}) const { + if (first == last) { + return last; + } + + auto smallest = first; + ++first; + for (; first != last; ++first) { + if (!std::invoke(comp, std::invoke(proj, *smallest), std::invoke(proj, *first))) { + smallest = first; + } + } + return smallest; + } + + template <ranges::input_range R, typename Proj = std::identity, typename Comp = ranges::less> + constexpr ranges::iterator_t<R> operator()(R&& r, Comp comp = {}, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), std::ref(comp), std::ref(proj)); + } +}; + +struct max_element_fn { + template <typename Iterator, typename Proj = std::identity, typename Comp = ranges::less> + constexpr Iterator operator()(Iterator first, Iterator last, Comp comp = {}, + Proj proj = {}) const { + if (first == last) { + return last; + } + + auto largest = first; + ++first; + for (; first != last; ++first) { + if (std::invoke(comp, std::invoke(proj, *largest), std::invoke(proj, *first))) { + largest = first; + } + } + return largest; + } + + template <ranges::input_range R, typename Proj = std::identity, typename Comp = ranges::less> + constexpr ranges::iterator_t<R> operator()(R&& r, Comp comp = {}, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), std::ref(comp), std::ref(proj)); + } +}; + +inline constexpr min_element_fn min_element; +inline constexpr max_element_fn max_element; + +// +// replace, replace_if +// + +struct replace_fn { + template <typename Iterator, typename T1, typename T2, typename Proj = std::identity> + constexpr Iterator operator()(Iterator first, Iterator last, const T1& old_value, + const T2& new_value, Proj proj = {}) const { + for (; first != last; ++first) { + if (old_value == std::invoke(proj, *first)) { + *first = new_value; + } + } + return first; + } + + template <ranges::input_range R, typename T1, typename T2, typename Proj = std::identity> + constexpr ranges::iterator_t<R> operator()(R&& r, const T1& old_value, const T2& new_value, + Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), old_value, new_value, std::move(proj)); + } +}; + +struct replace_if_fn { + template <typename Iterator, typename T, typename Proj = std::identity, typename Pred> + constexpr Iterator operator()(Iterator first, Iterator last, Pred pred, const T& new_value, + Proj proj = {}) const { + for (; first != last; ++first) { + if (!!std::invoke(pred, std::invoke(proj, *first))) { + *first = new_value; + } + } + return std::move(first); + } + + template <ranges::input_range R, typename T, typename Proj = std::identity, typename Pred> + constexpr ranges::iterator_t<R> operator()(R&& r, Pred pred, const T& new_value, + Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), std::move(pred), new_value, + std::move(proj)); + } +}; + +inline constexpr replace_fn replace; +inline constexpr replace_if_fn replace_if; + +// +// copy, copy_if +// + +struct copy_fn { + template <typename InputIterator, typename OutputIterator> + constexpr void operator()(InputIterator first, InputIterator last, + OutputIterator result) const { + for (; first != last; ++first, (void)++result) { + *result = *first; + } + } + + template <ranges::input_range R, typename OutputIterator> + constexpr void operator()(R&& r, OutputIterator result) const { + return operator()(ranges::begin(r), ranges::end(r), std::move(result)); + } +}; + +struct copy_if_fn { + template <typename InputIterator, typename OutputIterator, typename Proj = std::identity, + typename Pred> + constexpr void operator()(InputIterator first, InputIterator last, OutputIterator result, + Pred pred, Proj proj = {}) const { + for (; first != last; ++first) { + if (std::invoke(pred, std::invoke(proj, *first))) { + *result = *first; + ++result; + } + } + } + + template <ranges::input_range R, typename OutputIterator, typename Proj = std::identity, + typename Pred> + constexpr void operator()(R&& r, OutputIterator result, Pred pred, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), std::move(result), std::ref(pred), + std::ref(proj)); + } +}; + +inline constexpr copy_fn copy; +inline constexpr copy_if_fn copy_if; + +// +// generate +// + +struct generate_fn { + template <typename Iterator, typename F> + constexpr Iterator operator()(Iterator first, Iterator last, F gen) const { + for (; first != last; *first = std::invoke(gen), ++first) + ; + return first; + } + + template <typename R, std::copy_constructible F> + requires std::invocable<F&> && ranges::output_range<R> + constexpr ranges::iterator_t<R> operator()(R&& r, F gen) const { + return operator()(ranges::begin(r), ranges::end(r), std::move(gen)); + } +}; + +inline constexpr generate_fn generate; + +// +// lower_bound, upper_bound +// + +struct lower_bound_fn { + template <typename Iterator, typename T, typename Proj = std::identity, + typename Comp = ranges::less> + constexpr Iterator operator()(Iterator first, Iterator last, const T& value, Comp comp = {}, + Proj proj = {}) const { + Iterator it; + std::ptrdiff_t _count, _step; + _count = std::distance(first, last); + + while (_count > 0) { + it = first; + _step = _count / 2; + ranges::advance(it, _step, last); + if (comp(std::invoke(proj, *it), value)) { + first = ++it; + _count -= _step + 1; + } else { + _count = _step; + } + } + return first; + } + + template <ranges::input_range R, typename T, typename Proj = std::identity, + typename Comp = ranges::less> + constexpr ranges::iterator_t<R> operator()(R&& r, const T& value, Comp comp = {}, + Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), value, std::ref(comp), std::ref(proj)); + } +}; + +struct upper_bound_fn { + template <typename Iterator, typename T, typename Proj = std::identity, + typename Comp = ranges::less> + constexpr Iterator operator()(Iterator first, Iterator last, const T& value, Comp comp = {}, + Proj proj = {}) const { + Iterator it; + std::ptrdiff_t _count, _step; + _count = std::distance(first, last); + + while (_count > 0) { + it = first; + _step = _count / 2; + ranges::advance(it, _step, last); + if (!comp(value, std::invoke(proj, *it))) { + first = ++it; + _count -= _step + 1; + } else { + _count = _step; + } + } + return first; + } + + template <ranges::input_range R, typename T, typename Proj = std::identity, + typename Comp = ranges::less> + constexpr ranges::iterator_t<R> operator()(R&& r, const T& value, Comp comp = {}, + Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), value, std::ref(comp), std::ref(proj)); + } +}; + +inline constexpr lower_bound_fn lower_bound; +inline constexpr upper_bound_fn upper_bound; + +// +// adjacent_find +// + +struct adjacent_find_fn { + template <typename Iterator, typename Proj = std::identity, typename Pred = ranges::equal_to> + constexpr Iterator operator()(Iterator first, Iterator last, Pred pred = {}, + Proj proj = {}) const { + if (first == last) + return first; + auto _next = ranges::next(first); + for (; _next != last; ++_next, ++first) + if (std::invoke(pred, std::invoke(proj, *first), std::invoke(proj, *_next))) + return first; + return _next; + } + + template <ranges::input_range R, typename Proj = std::identity, + typename Pred = ranges::equal_to> + constexpr ranges::iterator_t<R> operator()(R&& r, Pred pred = {}, Proj proj = {}) const { + return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj)); + } +}; + +inline constexpr adjacent_find_fn adjacent_find; + +} // namespace ranges +} // namespace std + +#endif diff --git a/src/common/polyfill_thread.h b/src/common/polyfill_thread.h new file mode 100644 index 000000000..5a8d1ce08 --- /dev/null +++ b/src/common/polyfill_thread.h @@ -0,0 +1,323 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +// +// TODO: remove this file when jthread is supported by all compilation targets +// + +#pragma once + +#include <version> + +#ifdef __cpp_lib_jthread + +#include <stop_token> +#include <thread> + +namespace Common { + +template <typename Condvar, typename Lock, typename Pred> +void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred&& pred) { + cv.wait(lock, token, std::move(pred)); +} + +} // namespace Common + +#else + +#include <atomic> +#include <functional> +#include <list> +#include <memory> +#include <mutex> +#include <optional> +#include <thread> +#include <type_traits> + +namespace std { +namespace polyfill { + +using stop_state_callbacks = list<function<void()>>; + +class stop_state { +public: + stop_state() = default; + ~stop_state() = default; + + bool request_stop() { + stop_state_callbacks callbacks; + + { + scoped_lock lk{m_lock}; + + if (m_stop_requested.load()) { + // Already set, nothing to do + return false; + } + + // Set as requested + m_stop_requested = true; + + // Copy callback list + callbacks = m_callbacks; + } + + for (auto callback : callbacks) { + callback(); + } + + return true; + } + + bool stop_requested() const { + return m_stop_requested.load(); + } + + stop_state_callbacks::const_iterator insert_callback(function<void()> f) { + stop_state_callbacks::const_iterator ret{}; + bool should_run{}; + + { + scoped_lock lk{m_lock}; + should_run = m_stop_requested.load(); + m_callbacks.push_front(f); + ret = m_callbacks.begin(); + } + + if (should_run) { + f(); + } + + return ret; + } + + void remove_callback(stop_state_callbacks::const_iterator it) { + scoped_lock lk{m_lock}; + m_callbacks.erase(it); + } + +private: + mutex m_lock; + atomic<bool> m_stop_requested; + stop_state_callbacks m_callbacks; +}; + +} // namespace polyfill + +class stop_token; +class stop_source; +struct nostopstate_t { + explicit nostopstate_t() = default; +}; +inline constexpr nostopstate_t nostopstate{}; + +template <class Callback> +class stop_callback; + +class stop_token { +public: + stop_token() noexcept = default; + + stop_token(const stop_token&) noexcept = default; + stop_token(stop_token&&) noexcept = default; + stop_token& operator=(const stop_token&) noexcept = default; + stop_token& operator=(stop_token&&) noexcept = default; + ~stop_token() = default; + + void swap(stop_token& other) noexcept { + m_stop_state.swap(other.m_stop_state); + } + + [[nodiscard]] bool stop_requested() const noexcept { + return m_stop_state && m_stop_state->stop_requested(); + } + [[nodiscard]] bool stop_possible() const noexcept { + return m_stop_state != nullptr; + } + +private: + friend class stop_source; + template <typename Callback> + friend class stop_callback; + stop_token(shared_ptr<polyfill::stop_state> stop_state) : m_stop_state(move(stop_state)) {} + +private: + shared_ptr<polyfill::stop_state> m_stop_state; +}; + +class stop_source { +public: + stop_source() : m_stop_state(make_shared<polyfill::stop_state>()) {} + explicit stop_source(nostopstate_t) noexcept {} + + stop_source(const stop_source&) noexcept = default; + stop_source(stop_source&&) noexcept = default; + stop_source& operator=(const stop_source&) noexcept = default; + stop_source& operator=(stop_source&&) noexcept = default; + ~stop_source() = default; + void swap(stop_source& other) noexcept { + m_stop_state.swap(other.m_stop_state); + } + + [[nodiscard]] stop_token get_token() const noexcept { + return stop_token(m_stop_state); + } + [[nodiscard]] bool stop_possible() const noexcept { + return m_stop_state != nullptr; + } + [[nodiscard]] bool stop_requested() const noexcept { + return m_stop_state && m_stop_state->stop_requested(); + } + bool request_stop() noexcept { + return m_stop_state && m_stop_state->request_stop(); + } + +private: + friend class jthread; + explicit stop_source(shared_ptr<polyfill::stop_state> stop_state) + : m_stop_state(move(stop_state)) {} + +private: + shared_ptr<polyfill::stop_state> m_stop_state; +}; + +template <typename Callback> +class stop_callback { + static_assert(is_nothrow_destructible_v<Callback>); + static_assert(is_invocable_v<Callback>); + +public: + using callback_type = Callback; + + template <typename C> + requires constructible_from<Callback, C> + explicit stop_callback(const stop_token& st, + C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>) + : m_stop_state(st.m_stop_state) { + if (m_stop_state) { + m_callback = m_stop_state->insert_callback(move(cb)); + } + } + template <typename C> + requires constructible_from<Callback, C> + explicit stop_callback(stop_token&& st, + C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>) + : m_stop_state(move(st.m_stop_state)) { + if (m_stop_state) { + m_callback = m_stop_state->insert_callback(move(cb)); + } + } + ~stop_callback() { + if (m_stop_state && m_callback) { + m_stop_state->remove_callback(*m_callback); + } + } + + stop_callback(const stop_callback&) = delete; + stop_callback(stop_callback&&) = delete; + stop_callback& operator=(const stop_callback&) = delete; + stop_callback& operator=(stop_callback&&) = delete; + +private: + shared_ptr<polyfill::stop_state> m_stop_state; + optional<polyfill::stop_state_callbacks::const_iterator> m_callback; +}; + +template <typename Callback> +stop_callback(stop_token, Callback) -> stop_callback<Callback>; + +class jthread { +public: + using id = thread::id; + using native_handle_type = thread::native_handle_type; + + jthread() noexcept = default; + + template <typename F, typename... Args, + typename = enable_if_t<!is_same_v<remove_cvref_t<F>, jthread>>> + explicit jthread(F&& f, Args&&... args) + : m_stop_state(make_shared<polyfill::stop_state>()), + m_thread(make_thread(move(f), move(args)...)) {} + + ~jthread() { + if (joinable()) { + request_stop(); + join(); + } + } + + jthread(const jthread&) = delete; + jthread(jthread&&) noexcept = default; + jthread& operator=(const jthread&) = delete; + + jthread& operator=(jthread&& other) noexcept { + m_thread.swap(other.m_thread); + m_stop_state.swap(other.m_stop_state); + return *this; + } + + void swap(jthread& other) noexcept { + m_thread.swap(other.m_thread); + m_stop_state.swap(other.m_stop_state); + } + [[nodiscard]] bool joinable() const noexcept { + return m_thread.joinable(); + } + void join() { + m_thread.join(); + } + void detach() { + m_thread.detach(); + m_stop_state.reset(); + } + + [[nodiscard]] id get_id() const noexcept { + return m_thread.get_id(); + } + [[nodiscard]] native_handle_type native_handle() { + return m_thread.native_handle(); + } + [[nodiscard]] stop_source get_stop_source() noexcept { + return stop_source(m_stop_state); + } + [[nodiscard]] stop_token get_stop_token() const noexcept { + return stop_source(m_stop_state).get_token(); + } + bool request_stop() noexcept { + return get_stop_source().request_stop(); + } + [[nodiscard]] static unsigned int hardware_concurrency() noexcept { + return thread::hardware_concurrency(); + } + +private: + template <typename F, typename... Args> + thread make_thread(F&& f, Args&&... args) { + if constexpr (is_invocable_v<decay_t<F>, stop_token, decay_t<Args>...>) { + return thread(move(f), get_stop_token(), move(args)...); + } else { + return thread(move(f), move(args)...); + } + } + + shared_ptr<polyfill::stop_state> m_stop_state; + thread m_thread; +}; + +} // namespace std + +namespace Common { + +template <typename Condvar, typename Lock, typename Pred> +void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred pred) { + if (token.stop_requested()) { + return; + } + + std::stop_callback callback(token, [&] { cv.notify_all(); }); + cv.wait(lock, [&] { return pred() || token.stop_requested(); }); +} + +} // namespace Common + +#endif diff --git a/src/common/precompiled_headers.h b/src/common/precompiled_headers.h new file mode 100644 index 000000000..aabae730b --- /dev/null +++ b/src/common/precompiled_headers.h @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_precompiled_headers.h" diff --git a/src/common/settings.h b/src/common/settings.h index c29d6f98b..29b730cff 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -19,6 +19,7 @@ namespace Settings { enum class RendererBackend : u32 { OpenGL = 0, Vulkan = 1, + Null = 2, }; enum class ShaderBackend : u32 { @@ -412,7 +413,7 @@ struct Values { // Renderer SwitchableSetting<RendererBackend, true> renderer_backend{ - RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"}; + RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Null, "backend"}; Setting<bool> renderer_debug{false, "debug"}; Setting<bool> renderer_shader_feedback{false, "shader_feedback"}; Setting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; diff --git a/src/common/settings_input.h b/src/common/settings_input.h index 485e4ad22..46f38c703 100644 --- a/src/common/settings_input.h +++ b/src/common/settings_input.h @@ -391,6 +391,7 @@ struct PlayerInput { u32 body_color_right; u32 button_color_left; u32 button_color_right; + std::string profile_name; }; struct TouchscreenInput { diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index 7a495bc79..b26db4796 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp @@ -141,7 +141,7 @@ static std::wstring CPToUTF16(u32 code_page, const std::string& input) { MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), nullptr, 0); if (size == 0) { - return L""; + return {}; } std::wstring output(size, L'\0'); @@ -158,7 +158,7 @@ std::string UTF16ToUTF8(const std::wstring& input) { const auto size = WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()), nullptr, 0, nullptr, nullptr); if (size == 0) { - return ""; + return {}; } std::string output(size, '\0'); diff --git a/src/common/thread_worker.h b/src/common/thread_worker.h index 62c60f724..260ad44e4 100644 --- a/src/common/thread_worker.h +++ b/src/common/thread_worker.h @@ -7,13 +7,13 @@ #include <condition_variable> #include <functional> #include <mutex> -#include <stop_token> #include <string> #include <thread> #include <type_traits> #include <vector> #include <queue> +#include "common/polyfill_thread.h" #include "common/thread.h" #include "common/unique_function.h" @@ -47,7 +47,8 @@ public: if (requests.empty()) { wait_condition.notify_all(); } - condition.wait(lock, stop_token, [this] { return !requests.empty(); }); + Common::CondvarWait(condition, lock, stop_token, + [this] { return !requests.empty(); }); if (stop_token.stop_requested()) { break; } diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h index 053798e79..2ef1da064 100644 --- a/src/common/threadsafe_queue.h +++ b/src/common/threadsafe_queue.h @@ -12,6 +12,8 @@ #include <mutex> #include <utility> +#include "common/polyfill_thread.h" + namespace Common { template <typename T, bool with_stop_token = false> class SPSCQueue { @@ -97,7 +99,7 @@ public: T PopWait(std::stop_token stop_token) { if (Empty()) { std::unique_lock lock{cv_mutex}; - cv.wait(lock, stop_token, [this] { return !Empty(); }); + Common::CondvarWait(cv, lock, stop_token, [this] { return !Empty(); }); } if (stop_token.stop_requested()) { return T{}; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 5629980d9..c6b5ac196 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -528,6 +528,8 @@ add_library(core STATIC hle/service/mnpp/mnpp_app.h hle/service/ncm/ncm.cpp hle/service/ncm/ncm.h + hle/service/nfc/mifare_user.cpp + hle/service/nfc/mifare_user.h hle/service/nfc/nfc.cpp hle/service/nfc/nfc.h hle/service/nfc/nfc_device.cpp @@ -771,6 +773,7 @@ add_library(core STATIC memory.h perf_stats.cpp perf_stats.h + precompiled_headers.h reporter.cpp reporter.h telemetry_session.cpp @@ -823,5 +826,9 @@ if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) hle/service/jit/jit.cpp hle/service/jit/jit.h ) - target_link_libraries(core PRIVATE dynarmic) + target_link_libraries(core PRIVATE dynarmic::dynarmic) +endif() + +if (YUZU_USE_PRECOMPILED_HEADERS) + target_precompile_headers(core PRIVATE precompiled_headers.h) endif() diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h index 95ea3ef39..374367468 100644 --- a/src/core/cpu_manager.h +++ b/src/core/cpu_manager.h @@ -10,6 +10,7 @@ #include <thread> #include "common/fiber.h" +#include "common/polyfill_thread.h" #include "common/thread.h" #include "core/hardware_properties.h" diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp index 1a8e02e6a..a9675df76 100644 --- a/src/core/debugger/debugger.cpp +++ b/src/core/debugger/debugger.cpp @@ -9,6 +9,7 @@ #include <boost/process/async_pipe.hpp> #include "common/logging/log.h" +#include "common/polyfill_thread.h" #include "common/thread.h" #include "core/core.h" #include "core/debugger/debugger.h" diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 78e56bbbd..50303fe42 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -7,6 +7,7 @@ #include <utility> #include "common/logging/log.h" +#include "common/polyfill_ranges.h" #include "core/crypto/aes_util.h" #include "core/crypto/ctr_encryption_layer.h" #include "core/crypto/key_manager.h" diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp index 6c230f619..52919484e 100644 --- a/src/core/frontend/applets/controller.cpp +++ b/src/core/frontend/applets/controller.cpp @@ -16,7 +16,7 @@ DefaultControllerApplet::DefaultControllerApplet(HID::HIDCore& hid_core_) : hid_ DefaultControllerApplet::~DefaultControllerApplet() = default; -void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callback, +void DefaultControllerApplet::ReconfigureControllers(ReconfigureCallback callback, const ControllerParameters& parameters) const { LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!"); diff --git a/src/core/frontend/applets/controller.h b/src/core/frontend/applets/controller.h index 1d2850ad5..adb2feefd 100644 --- a/src/core/frontend/applets/controller.h +++ b/src/core/frontend/applets/controller.h @@ -4,6 +4,7 @@ #pragma once #include <functional> +#include <vector> #include "common/common_types.h" @@ -35,9 +36,11 @@ struct ControllerParameters { class ControllerApplet { public: + using ReconfigureCallback = std::function<void()>; + virtual ~ControllerApplet(); - virtual void ReconfigureControllers(std::function<void()> callback, + virtual void ReconfigureControllers(ReconfigureCallback callback, const ControllerParameters& parameters) const = 0; }; @@ -46,7 +49,7 @@ public: explicit DefaultControllerApplet(HID::HIDCore& hid_core_); ~DefaultControllerApplet() override; - void ReconfigureControllers(std::function<void()> callback, + void ReconfigureControllers(ReconfigureCallback callback, const ControllerParameters& parameters) const override; private: diff --git a/src/core/frontend/applets/error.cpp b/src/core/frontend/applets/error.cpp index f8b961098..69c2b2b4d 100644 --- a/src/core/frontend/applets/error.cpp +++ b/src/core/frontend/applets/error.cpp @@ -8,13 +8,13 @@ namespace Core::Frontend { ErrorApplet::~ErrorApplet() = default; -void DefaultErrorApplet::ShowError(Result error, std::function<void()> finished) const { +void DefaultErrorApplet::ShowError(Result error, FinishedCallback finished) const { LOG_CRITICAL(Service_Fatal, "Application requested error display: {:04}-{:04} (raw={:08X})", error.module.Value(), error.description.Value(), error.raw); } void DefaultErrorApplet::ShowErrorWithTimestamp(Result error, std::chrono::seconds time, - std::function<void()> finished) const { + FinishedCallback finished) const { LOG_CRITICAL( Service_Fatal, "Application requested error display: {:04X}-{:04X} (raw={:08X}) with timestamp={:016X}", @@ -23,7 +23,7 @@ void DefaultErrorApplet::ShowErrorWithTimestamp(Result error, std::chrono::secon void DefaultErrorApplet::ShowCustomErrorText(Result error, std::string main_text, std::string detail_text, - std::function<void()> finished) const { + FinishedCallback finished) const { LOG_CRITICAL(Service_Fatal, "Application requested custom error with error_code={:04X}-{:04X} (raw={:08X})", error.module.Value(), error.description.Value(), error.raw); diff --git a/src/core/frontend/applets/error.h b/src/core/frontend/applets/error.h index f378f8805..884f2f653 100644 --- a/src/core/frontend/applets/error.h +++ b/src/core/frontend/applets/error.h @@ -12,25 +12,27 @@ namespace Core::Frontend { class ErrorApplet { public: + using FinishedCallback = std::function<void()>; + virtual ~ErrorApplet(); - virtual void ShowError(Result error, std::function<void()> finished) const = 0; + virtual void ShowError(Result error, FinishedCallback finished) const = 0; virtual void ShowErrorWithTimestamp(Result error, std::chrono::seconds time, - std::function<void()> finished) const = 0; + FinishedCallback finished) const = 0; virtual void ShowCustomErrorText(Result error, std::string dialog_text, std::string fullscreen_text, - std::function<void()> finished) const = 0; + FinishedCallback finished) const = 0; }; class DefaultErrorApplet final : public ErrorApplet { public: - void ShowError(Result error, std::function<void()> finished) const override; + void ShowError(Result error, FinishedCallback finished) const override; void ShowErrorWithTimestamp(Result error, std::chrono::seconds time, - std::function<void()> finished) const override; + FinishedCallback finished) const override; void ShowCustomErrorText(Result error, std::string main_text, std::string detail_text, - std::function<void()> finished) const override; + FinishedCallback finished) const override; }; } // namespace Core::Frontend diff --git a/src/core/frontend/applets/mii_edit.cpp b/src/core/frontend/applets/mii_edit.cpp index d37b5368a..bc8c57067 100644 --- a/src/core/frontend/applets/mii_edit.cpp +++ b/src/core/frontend/applets/mii_edit.cpp @@ -8,7 +8,7 @@ namespace Core::Frontend { MiiEditApplet::~MiiEditApplet() = default; -void DefaultMiiEditApplet::ShowMiiEdit(const std::function<void()>& callback) const { +void DefaultMiiEditApplet::ShowMiiEdit(const MiiEditCallback& callback) const { LOG_WARNING(Service_AM, "(STUBBED) called"); callback(); diff --git a/src/core/frontend/applets/mii_edit.h b/src/core/frontend/applets/mii_edit.h index 58fa2039b..d828f06ec 100644 --- a/src/core/frontend/applets/mii_edit.h +++ b/src/core/frontend/applets/mii_edit.h @@ -9,14 +9,16 @@ namespace Core::Frontend { class MiiEditApplet { public: + using MiiEditCallback = std::function<void()>; + virtual ~MiiEditApplet(); - virtual void ShowMiiEdit(const std::function<void()>& callback) const = 0; + virtual void ShowMiiEdit(const MiiEditCallback& callback) const = 0; }; class DefaultMiiEditApplet final : public MiiEditApplet { public: - void ShowMiiEdit(const std::function<void()>& callback) const override; + void ShowMiiEdit(const MiiEditCallback& callback) const override; }; } // namespace Core::Frontend diff --git a/src/core/frontend/applets/profile_select.cpp b/src/core/frontend/applets/profile_select.cpp index d11fbce0a..da4cfbf87 100644 --- a/src/core/frontend/applets/profile_select.cpp +++ b/src/core/frontend/applets/profile_select.cpp @@ -9,8 +9,7 @@ namespace Core::Frontend { ProfileSelectApplet::~ProfileSelectApplet() = default; -void DefaultProfileSelectApplet::SelectProfile( - std::function<void(std::optional<Common::UUID>)> callback) const { +void DefaultProfileSelectApplet::SelectProfile(SelectProfileCallback callback) const { Service::Account::ProfileManager manager; callback(manager.GetUser(Settings::values.current_user.GetValue()).value_or(Common::UUID{})); LOG_INFO(Service_ACC, "called, selecting current user instead of prompting..."); diff --git a/src/core/frontend/applets/profile_select.h b/src/core/frontend/applets/profile_select.h index 8d6ee5279..138429533 100644 --- a/src/core/frontend/applets/profile_select.h +++ b/src/core/frontend/applets/profile_select.h @@ -11,14 +11,16 @@ namespace Core::Frontend { class ProfileSelectApplet { public: + using SelectProfileCallback = std::function<void(std::optional<Common::UUID>)>; + virtual ~ProfileSelectApplet(); - virtual void SelectProfile(std::function<void(std::optional<Common::UUID>)> callback) const = 0; + virtual void SelectProfile(SelectProfileCallback callback) const = 0; }; class DefaultProfileSelectApplet final : public ProfileSelectApplet { public: - void SelectProfile(std::function<void(std::optional<Common::UUID>)> callback) const override; + void SelectProfile(SelectProfileCallback callback) const override; }; } // namespace Core::Frontend diff --git a/src/core/frontend/applets/software_keyboard.cpp b/src/core/frontend/applets/software_keyboard.cpp index 020c7fa5e..a3720f4d7 100644 --- a/src/core/frontend/applets/software_keyboard.cpp +++ b/src/core/frontend/applets/software_keyboard.cpp @@ -15,10 +15,7 @@ DefaultSoftwareKeyboardApplet::~DefaultSoftwareKeyboardApplet() = default; void DefaultSoftwareKeyboardApplet::InitializeKeyboard( bool is_inline, KeyboardInitializeParameters initialize_parameters, - std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)> - submit_normal_callback_, - std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)> - submit_inline_callback_) { + SubmitNormalCallback submit_normal_callback_, SubmitInlineCallback submit_inline_callback_) { if (is_inline) { LOG_WARNING( Service_AM, diff --git a/src/core/frontend/applets/software_keyboard.h b/src/core/frontend/applets/software_keyboard.h index 094d1e713..8aef103d3 100644 --- a/src/core/frontend/applets/software_keyboard.h +++ b/src/core/frontend/applets/software_keyboard.h @@ -54,14 +54,17 @@ struct InlineTextParameters { class SoftwareKeyboardApplet { public: + using SubmitInlineCallback = + std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>; + using SubmitNormalCallback = + std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)>; + virtual ~SoftwareKeyboardApplet(); - virtual void InitializeKeyboard( - bool is_inline, KeyboardInitializeParameters initialize_parameters, - std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)> - submit_normal_callback_, - std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)> - submit_inline_callback_) = 0; + virtual void InitializeKeyboard(bool is_inline, + KeyboardInitializeParameters initialize_parameters, + SubmitNormalCallback submit_normal_callback_, + SubmitInlineCallback submit_inline_callback_) = 0; virtual void ShowNormalKeyboard() const = 0; @@ -81,12 +84,9 @@ class DefaultSoftwareKeyboardApplet final : public SoftwareKeyboardApplet { public: ~DefaultSoftwareKeyboardApplet() override; - void InitializeKeyboard( - bool is_inline, KeyboardInitializeParameters initialize_parameters, - std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)> - submit_normal_callback_, - std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)> - submit_inline_callback_) override; + void InitializeKeyboard(bool is_inline, KeyboardInitializeParameters initialize_parameters, + SubmitNormalCallback submit_normal_callback_, + SubmitInlineCallback submit_inline_callback_) override; void ShowNormalKeyboard() const override; @@ -105,12 +105,10 @@ private: void SubmitNormalText(std::u16string text) const; void SubmitInlineText(std::u16string_view text) const; - KeyboardInitializeParameters parameters; + KeyboardInitializeParameters parameters{}; - mutable std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)> - submit_normal_callback; - mutable std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)> - submit_inline_callback; + mutable SubmitNormalCallback submit_normal_callback; + mutable SubmitInlineCallback submit_inline_callback; }; } // namespace Core::Frontend diff --git a/src/core/frontend/applets/web_browser.cpp b/src/core/frontend/applets/web_browser.cpp index 27c7086be..b09cb7102 100644 --- a/src/core/frontend/applets/web_browser.cpp +++ b/src/core/frontend/applets/web_browser.cpp @@ -10,18 +10,17 @@ WebBrowserApplet::~WebBrowserApplet() = default; DefaultWebBrowserApplet::~DefaultWebBrowserApplet() = default; -void DefaultWebBrowserApplet::OpenLocalWebPage( - const std::string& local_url, std::function<void()> extract_romfs_callback, - std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback) const { +void DefaultWebBrowserApplet::OpenLocalWebPage(const std::string& local_url, + ExtractROMFSCallback extract_romfs_callback, + OpenWebPageCallback callback) const { LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to open local web page at {}", local_url); callback(Service::AM::Applets::WebExitReason::WindowClosed, "http://localhost/"); } -void DefaultWebBrowserApplet::OpenExternalWebPage( - const std::string& external_url, - std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback) const { +void DefaultWebBrowserApplet::OpenExternalWebPage(const std::string& external_url, + OpenWebPageCallback callback) const { LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to open external web page at {}", external_url); diff --git a/src/core/frontend/applets/web_browser.h b/src/core/frontend/applets/web_browser.h index 1411274f8..4f72284ad 100644 --- a/src/core/frontend/applets/web_browser.h +++ b/src/core/frontend/applets/web_browser.h @@ -11,29 +11,29 @@ namespace Core::Frontend { class WebBrowserApplet { public: + using ExtractROMFSCallback = std::function<void()>; + using OpenWebPageCallback = + std::function<void(Service::AM::Applets::WebExitReason, std::string)>; + virtual ~WebBrowserApplet(); - virtual void OpenLocalWebPage( - const std::string& local_url, std::function<void()> extract_romfs_callback, - std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback) const = 0; + virtual void OpenLocalWebPage(const std::string& local_url, + ExtractROMFSCallback extract_romfs_callback, + OpenWebPageCallback callback) const = 0; - virtual void OpenExternalWebPage( - const std::string& external_url, - std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback) const = 0; + virtual void OpenExternalWebPage(const std::string& external_url, + OpenWebPageCallback callback) const = 0; }; class DefaultWebBrowserApplet final : public WebBrowserApplet { public: ~DefaultWebBrowserApplet() override; - void OpenLocalWebPage(const std::string& local_url, - std::function<void()> extract_romfs_callback, - std::function<void(Service::AM::Applets::WebExitReason, std::string)> - callback) const override; + void OpenLocalWebPage(const std::string& local_url, ExtractROMFSCallback extract_romfs_callback, + OpenWebPageCallback callback) const override; void OpenExternalWebPage(const std::string& external_url, - std::function<void(Service::AM::Applets::WebExitReason, std::string)> - callback) const override; + OpenWebPageCallback callback) const override; }; } // namespace Core::Frontend diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index ac1906d5e..95363b645 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -17,6 +17,8 @@ enum class WindowSystemType { Windows, X11, Wayland, + Cocoa, + Android, }; /** diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp index b6c8cc58d..30c2e9d17 100644 --- a/src/core/hid/emulated_console.cpp +++ b/src/core/hid/emulated_console.cpp @@ -37,7 +37,7 @@ void EmulatedConsole::SetTouchParams() { touchscreen_param.Set("axis_x", i * 2); touchscreen_param.Set("axis_y", (i * 2) + 1); touchscreen_param.Set("button", i); - touch_params[index++] = touchscreen_param; + touch_params[index++] = std::move(touchscreen_param); } const auto button_index = @@ -59,7 +59,7 @@ void EmulatedConsole::SetTouchParams() { touch_button_params.Set("button", params.Serialize()); touch_button_params.Set("x", x); touch_button_params.Set("y", y); - touch_params[index] = touch_button_params; + touch_params[index] = std::move(touch_button_params); index++; } } @@ -131,7 +131,7 @@ Common::ParamPackage EmulatedConsole::GetMotionParam() const { } void EmulatedConsole::SetMotionParam(Common::ParamPackage param) { - motion_params = param; + motion_params = std::move(param); ReloadInput(); } @@ -199,7 +199,7 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st if (is_new_input) { touch_value.pressed.value = true; - touch_value.id = static_cast<u32>(index); + touch_value.id = static_cast<int>(index); } touch_value.x = touch_input.x; @@ -284,7 +284,7 @@ void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) { int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) { std::scoped_lock lock{callback_mutex}; - callback_list.insert_or_assign(last_callback_key, update_callback); + callback_list.insert_or_assign(last_callback_key, std::move(update_callback)); return last_callback_key++; } diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index c96d9eef3..67969e938 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -3,6 +3,7 @@ #include <algorithm> +#include "common/polyfill_ranges.h" #include "common/thread.h" #include "core/hid/emulated_controller.h" #include "core/hid/input_converter.h" @@ -109,10 +110,9 @@ void EmulatedController::ReloadFromSettings() { original_npad_type = npad_type; } + Disconnect(); if (player.connected) { Connect(); - } else { - Disconnect(); } ReloadInput(); @@ -424,15 +424,14 @@ void EmulatedController::RestoreConfig() { ReloadFromSettings(); } -std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices( - EmulatedDeviceIndex device_index) const { +std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices() const { std::vector<Common::ParamPackage> devices; for (const auto& param : button_params) { if (!param.Has("engine")) { continue; } const auto devices_it = std::find_if( - devices.begin(), devices.end(), [param](const Common::ParamPackage param_) { + devices.begin(), devices.end(), [¶m](const Common::ParamPackage& param_) { return param.Get("engine", "") == param_.Get("engine", "") && param.Get("guid", "") == param_.Get("guid", "") && param.Get("port", 0) == param_.Get("port", 0) && @@ -441,12 +440,12 @@ std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices( if (devices_it != devices.end()) { continue; } - Common::ParamPackage device{}; + + auto& device = devices.emplace_back(); device.Set("engine", param.Get("engine", "")); device.Set("guid", param.Get("guid", "")); device.Set("port", param.Get("port", 0)); device.Set("pad", param.Get("pad", 0)); - devices.push_back(device); } for (const auto& param : stick_params) { @@ -457,7 +456,7 @@ std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices( continue; } const auto devices_it = std::find_if( - devices.begin(), devices.end(), [param](const Common::ParamPackage param_) { + devices.begin(), devices.end(), [¶m](const Common::ParamPackage& param_) { return param.Get("engine", "") == param_.Get("engine", "") && param.Get("guid", "") == param_.Get("guid", "") && param.Get("port", 0) == param_.Get("port", 0) && @@ -466,12 +465,12 @@ std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices( if (devices_it != devices.end()) { continue; } - Common::ParamPackage device{}; + + auto& device = devices.emplace_back(); device.Set("engine", param.Get("engine", "")); device.Set("guid", param.Get("guid", "")); device.Set("port", param.Get("port", 0)); device.Set("pad", param.Get("pad", 0)); - devices.push_back(device); } return devices; } diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index d004ca56a..fa7a34278 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h @@ -8,6 +8,7 @@ #include <memory> #include <mutex> #include <unordered_map> +#include <vector> #include "common/common_types.h" #include "common/input.h" @@ -243,7 +244,7 @@ public: void RestoreConfig(); /// Returns a vector of mapped devices from the mapped button and stick parameters - std::vector<Common::ParamPackage> GetMappedDevices(EmulatedDeviceIndex device_index) const; + std::vector<Common::ParamPackage> GetMappedDevices() const; // Returns the current mapped button device Common::ParamPackage GetButtonParam(std::size_t index) const; diff --git a/src/core/hid/emulated_devices.h b/src/core/hid/emulated_devices.h index 4149eeced..4cdbf9dc6 100644 --- a/src/core/hid/emulated_devices.h +++ b/src/core/hid/emulated_devices.h @@ -8,6 +8,7 @@ #include <memory> #include <mutex> #include <unordered_map> +#include <vector> #include "common/common_types.h" #include "common/input.h" diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h index 3b6e7baff..87ca65592 100644 --- a/src/core/hle/kernel/k_memory_block.h +++ b/src/core/hle/kernel/k_memory_block.h @@ -280,18 +280,19 @@ struct KMemoryInfo { class KMemoryBlock : public Common::IntrusiveRedBlackTreeBaseNode<KMemoryBlock> { private: - u16 m_device_disable_merge_left_count; - u16 m_device_disable_merge_right_count; - VAddr m_address; - size_t m_num_pages; - KMemoryState m_memory_state; - u16 m_ipc_lock_count; - u16 m_device_use_count; - u16 m_ipc_disable_merge_count; - KMemoryPermission m_permission; - KMemoryPermission m_original_permission; - KMemoryAttribute m_attribute; - KMemoryBlockDisableMergeAttribute m_disable_merge_attribute; + u16 m_device_disable_merge_left_count{}; + u16 m_device_disable_merge_right_count{}; + VAddr m_address{}; + size_t m_num_pages{}; + KMemoryState m_memory_state{KMemoryState::None}; + u16 m_ipc_lock_count{}; + u16 m_device_use_count{}; + u16 m_ipc_disable_merge_count{}; + KMemoryPermission m_permission{KMemoryPermission::None}; + KMemoryPermission m_original_permission{KMemoryPermission::None}; + KMemoryAttribute m_attribute{KMemoryAttribute::None}; + KMemoryBlockDisableMergeAttribute m_disable_merge_attribute{ + KMemoryBlockDisableMergeAttribute::None}; public: static constexpr int Compare(const KMemoryBlock& lhs, const KMemoryBlock& rhs) { @@ -367,12 +368,8 @@ public: constexpr KMemoryBlock(VAddr addr, size_t np, KMemoryState ms, KMemoryPermission p, KMemoryAttribute attr) - : Common::IntrusiveRedBlackTreeBaseNode<KMemoryBlock>(), - m_device_disable_merge_left_count(), m_device_disable_merge_right_count(), - m_address(addr), m_num_pages(np), m_memory_state(ms), m_ipc_lock_count(0), - m_device_use_count(0), m_ipc_disable_merge_count(), m_permission(p), - m_original_permission(KMemoryPermission::None), m_attribute(attr), - m_disable_merge_attribute() {} + : Common::IntrusiveRedBlackTreeBaseNode<KMemoryBlock>(), m_address(addr), m_num_pages(np), + m_memory_state(ms), m_permission(p), m_attribute(attr) {} constexpr void Initialize(VAddr addr, size_t np, KMemoryState ms, KMemoryPermission p, KMemoryAttribute attr) { diff --git a/src/core/hle/kernel/k_memory_block_manager.h b/src/core/hle/kernel/k_memory_block_manager.h index 9b5873883..d382722a6 100644 --- a/src/core/hle/kernel/k_memory_block_manager.h +++ b/src/core/hle/kernel/k_memory_block_manager.h @@ -3,6 +3,7 @@ #pragma once +#include <array> #include <functional> #include "common/common_funcs.h" @@ -17,9 +18,9 @@ public: static constexpr size_t MaxBlocks = 2; private: - KMemoryBlock* m_blocks[MaxBlocks]; - size_t m_index; - KMemoryBlockSlabManager* m_slab_manager; + std::array<KMemoryBlock*, MaxBlocks> m_blocks{}; + size_t m_index{MaxBlocks}; + KMemoryBlockSlabManager* m_slab_manager{}; private: Result Initialize(size_t num_blocks) { @@ -41,7 +42,7 @@ private: public: KMemoryBlockManagerUpdateAllocator(Result* out_result, KMemoryBlockSlabManager* sm, size_t num_blocks = MaxBlocks) - : m_blocks(), m_index(MaxBlocks), m_slab_manager(sm) { + : m_slab_manager(sm) { *out_result = this->Initialize(num_blocks); } diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp index c4bf306e8..bd33571da 100644 --- a/src/core/hle/kernel/k_memory_manager.cpp +++ b/src/core/hle/kernel/k_memory_manager.cpp @@ -225,8 +225,8 @@ Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, ON_RESULT_FAILURE { for (const auto& it : out->Nodes()) { auto& manager = this->GetManager(it.GetAddress()); - const size_t node_num_pages = - std::min(it.GetNumPages(), (manager.GetEndAddress() - it.GetAddress()) / PageSize); + const size_t node_num_pages = std::min<u64>( + it.GetNumPages(), (manager.GetEndAddress() - it.GetAddress()) / PageSize); manager.Free(it.GetAddress(), node_num_pages); } out->Finalize(); diff --git a/src/core/hle/kernel/k_shared_memory.h b/src/core/hle/kernel/k_shared_memory.h index 5620c3660..a96c55a3e 100644 --- a/src/core/hle/kernel/k_shared_memory.h +++ b/src/core/hle/kernel/k_shared_memory.h @@ -74,7 +74,7 @@ public: static void PostDestroy([[maybe_unused]] uintptr_t arg) {} private: - Core::DeviceMemory* device_memory; + Core::DeviceMemory* device_memory{}; KProcess* owner_process{}; KPageGroup page_list; Svc::MemoryPermission owner_permission{}; diff --git a/src/core/hle/kernel/k_slab_heap.h b/src/core/hle/kernel/k_slab_heap.h index a8c77a7d4..68469b041 100644 --- a/src/core/hle/kernel/k_slab_heap.h +++ b/src/core/hle/kernel/k_slab_heap.h @@ -6,6 +6,7 @@ #include <atomic> #include "common/assert.h" +#include "common/atomic_ops.h" #include "common/common_funcs.h" #include "common/common_types.h" #include "common/spin_lock.h" @@ -82,16 +83,13 @@ private: private: void UpdatePeakImpl(uintptr_t obj) { - static_assert(std::atomic_ref<uintptr_t>::is_always_lock_free); - std::atomic_ref<uintptr_t> peak_ref(m_peak); - const uintptr_t alloc_peak = obj + this->GetObjectSize(); uintptr_t cur_peak = m_peak; do { if (alloc_peak <= cur_peak) { break; } - } while (!peak_ref.compare_exchange_strong(cur_peak, alloc_peak)); + } while (!Common::AtomicCompareAndSwap(&m_peak, alloc_peak, cur_peak, cur_peak)); } public: diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index f38c92bff..dc52b4ed3 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -784,8 +784,8 @@ private: std::vector<KSynchronizationObject*> wait_objects_for_debugging; VAddr mutex_wait_address_for_debugging{}; ThreadWaitReasonForDebugging wait_reason_for_debugging{}; - uintptr_t argument; - VAddr stack_top; + uintptr_t argument{}; + VAddr stack_top{}; public: using ConditionVariableThreadTreeType = ConditionVariableThreadTree; diff --git a/src/core/hle/kernel/k_thread_local_page.h b/src/core/hle/kernel/k_thread_local_page.h index 5d466ace7..fe0cff084 100644 --- a/src/core/hle/kernel/k_thread_local_page.h +++ b/src/core/hle/kernel/k_thread_local_page.h @@ -10,6 +10,7 @@ #include "common/assert.h" #include "common/common_types.h" #include "common/intrusive_red_black_tree.h" +#include "common/polyfill_ranges.h" #include "core/hle/kernel/memory_types.h" #include "core/hle/kernel/slab_helpers.h" #include "core/hle/result.h" diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index b77723503..288f97df5 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -891,7 +891,7 @@ struct KernelCore::Impl { Common::ThreadWorker service_threads_manager; Common::Barrier service_thread_barrier; - std::array<KThread*, Core::Hardware::NUM_CPU_CORES> shutdown_threads; + std::array<KThread*, Core::Hardware::NUM_CPU_CORES> shutdown_threads{}; std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{}; bool is_multicore{}; diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h index 2fc8d4be2..fb2ba4c6b 100644 --- a/src/core/hle/kernel/physical_core.h +++ b/src/core/hle/kernel/physical_core.h @@ -85,7 +85,7 @@ private: std::mutex guard; std::condition_variable on_interrupt; std::unique_ptr<Core::ARM_Interface> arm_interface; - bool is_interrupted; + bool is_interrupted{}; }; } // namespace Kernel diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp index e6e41ac34..e72c3d35d 100644 --- a/src/core/hle/kernel/service_thread.cpp +++ b/src/core/hle/kernel/service_thread.cpp @@ -7,6 +7,7 @@ #include <thread> #include <vector> +#include "common/polyfill_thread.h" #include "common/scope_exit.h" #include "common/thread.h" #include "core/hle/ipc_helpers.h" @@ -35,14 +36,14 @@ public: private: KernelCore& kernel; - - std::jthread m_host_thread; - std::mutex m_session_mutex; - std::map<KServerSession*, std::shared_ptr<SessionRequestManager>> m_sessions; - KEvent* m_wakeup_event; - KThread* m_thread; - std::atomic<bool> m_shutdown_requested; const std::string m_service_name; + + std::jthread m_host_thread{}; + std::mutex m_session_mutex{}; + std::map<KServerSession*, std::shared_ptr<SessionRequestManager>> m_sessions{}; + KEvent* m_wakeup_event{}; + KThread* m_thread{}; + std::atomic<bool> m_shutdown_requested{}; }; void ServiceThread::Impl::WaitAndProcessImpl() { diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 3730937fe..1ea8c7fbc 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -82,7 +82,7 @@ void SvcWrap64(Core::System& system) { } // Used by ControlCodeMemory -template <Result func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)> +template <Result func(Core::System&, Handle, u32, VAddr, size_t, Svc::MemoryPermission)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)), static_cast<u32>(Param(system, 1)), Param(system, 2), Param(system, 3), @@ -327,7 +327,7 @@ void SvcWrap64(Core::System& system) { } // Used by CreateCodeMemory -template <Result func(Core::System&, Handle*, u64, u64)> +template <Result func(Core::System&, Handle*, VAddr, size_t)> void SvcWrap64(Core::System& system) { u32 param_1 = 0; const u32 retval = func(system, ¶m_1, Param(system, 1), Param(system, 2)).raw; diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 85a3f0802..6d1084fd1 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -7,6 +7,7 @@ #include "common/fs/file.h" #include "common/fs/path_util.h" #include "common/logging/log.h" +#include "common/polyfill_ranges.h" #include "common/string_util.h" #include "common/swap.h" #include "core/constants.h" diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index 481e0d141..97f7c6688 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp @@ -9,6 +9,7 @@ #include "common/fs/file.h" #include "common/fs/fs.h" #include "common/fs/path_util.h" +#include "common/polyfill_ranges.h" #include "common/settings.h" #include "core/hle/service/acc/profile_manager.h" diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 8ea7fd760..22999c942 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -1125,7 +1125,7 @@ void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) { const u64 offset{rp.Pop<u64>()}; const std::vector<u8> data{ctx.ReadBuffer()}; - const std::size_t size{std::min(data.size(), backing.GetSize() - offset)}; + const std::size_t size{std::min<u64>(data.size(), backing.GetSize() - offset)}; LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size); @@ -1149,7 +1149,7 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u64 offset{rp.Pop<u64>()}; - const std::size_t size{std::min(ctx.GetWriteBufferSize(), backing.GetSize() - offset)}; + const std::size_t size{std::min<u64>(ctx.GetWriteBufferSize(), backing.GetSize() - offset)}; LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size); diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp index 053e8f9dd..26dec7147 100644 --- a/src/core/hle/service/audio/audin_u.cpp +++ b/src/core/hle/service/audio/audin_u.cpp @@ -203,9 +203,8 @@ private: }; AudInU::AudInU(Core::System& system_) - : ServiceFramework{system_, "audin:u", ServiceThreadType::CreateNew}, - service_context{system_, "AudInU"}, impl{std::make_unique<AudioCore::AudioIn::Manager>( - system_)} { + : ServiceFramework{system_, "audin:u"}, service_context{system_, "AudInU"}, + impl{std::make_unique<AudioCore::AudioIn::Manager>(system_)} { // clang-format off static const FunctionInfo functions[] = { {0, &AudInU::ListAudioIns, "ListAudioIns"}, diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 29751f075..991e30ba1 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -26,9 +26,8 @@ public: explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager, size_t session_id, const std::string& device_name, const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id) - : ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew}, - service_context{system_, "IAudioOut"}, event{service_context.CreateEvent( - "AudioOutEvent")}, + : ServiceFramework{system_, "IAudioOut"}, service_context{system_, "IAudioOut"}, + event{service_context.CreateEvent("AudioOutEvent")}, impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} { // clang-format off @@ -221,9 +220,8 @@ private: }; AudOutU::AudOutU(Core::System& system_) - : ServiceFramework{system_, "audout:u", ServiceThreadType::CreateNew}, - service_context{system_, "AudOutU"}, impl{std::make_unique<AudioCore::AudioOut::Manager>( - system_)} { + : ServiceFramework{system_, "audout:u"}, service_context{system_, "AudOutU"}, + impl{std::make_unique<AudioCore::AudioOut::Manager>(system_)} { // clang-format off static const FunctionInfo functions[] = { {0, &AudOutU::ListAudioOuts, "ListAudioOuts"}, diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 034ee273f..ead16c321 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -14,6 +14,7 @@ #include "common/bit_util.h" #include "common/common_funcs.h" #include "common/logging/log.h" +#include "common/polyfill_ranges.h" #include "common/string_util.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" @@ -34,10 +35,9 @@ public: AudioCore::AudioRendererParameterInternal& params, Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size, u32 process_handle, u64 applet_resource_user_id, s32 session_id) - : ServiceFramework{system_, "IAudioRenderer", ServiceThreadType::CreateNew}, - service_context{system_, "IAudioRenderer"}, rendered_event{service_context.CreateEvent( - "IAudioRendererEvent")}, - manager{manager_}, impl{std::make_unique<Renderer>(system_, manager, rendered_event)} { + : ServiceFramework{system_, "IAudioRenderer"}, service_context{system_, "IAudioRenderer"}, + rendered_event{service_context.CreateEvent("IAudioRendererEvent")}, manager{manager_}, + impl{std::make_unique<Renderer>(system_, manager, rendered_event)} { // clang-format off static const FunctionInfo functions[] = { {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"}, @@ -242,10 +242,8 @@ class IAudioDevice final : public ServiceFramework<IAudioDevice> { public: explicit IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u32 revision, u32 device_num) - : ServiceFramework{system_, "IAudioDevice", ServiceThreadType::CreateNew}, - service_context{system_, "IAudioDevice"}, impl{std::make_unique<AudioDevice>( - system_, applet_resource_user_id, - revision)}, + : ServiceFramework{system_, "IAudioDevice"}, service_context{system_, "IAudioDevice"}, + impl{std::make_unique<AudioDevice>(system_, applet_resource_user_id, revision)}, event{service_context.CreateEvent(fmt::format("IAudioDeviceEvent-{}", device_num))} { static const FunctionInfo functions[] = { {0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"}, @@ -420,7 +418,7 @@ private: }; AudRenU::AudRenU(Core::System& system_) - : ServiceFramework{system_, "audren:u", ServiceThreadType::CreateNew}, + : ServiceFramework{system_, "audren:u"}, service_context{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} { // clang-format off static const FunctionInfo functions[] = { diff --git a/src/core/hle/service/nfc/mifare_user.cpp b/src/core/hle/service/nfc/mifare_user.cpp new file mode 100644 index 000000000..51523a3ae --- /dev/null +++ b/src/core/hle/service/nfc/mifare_user.cpp @@ -0,0 +1,400 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/core.h" +#include "core/hid/hid_types.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/nfc/mifare_user.h" +#include "core/hle/service/nfc/nfc_device.h" +#include "core/hle/service/nfc/nfc_result.h" + +namespace Service::NFC { + +MFIUser::MFIUser(Core::System& system_) + : ServiceFramework{system_, "NFC::MFIUser"}, service_context{system_, service_name} { + static const FunctionInfo functions[] = { + {0, &MFIUser::Initialize, "Initialize"}, + {1, &MFIUser::Finalize, "Finalize"}, + {2, &MFIUser::ListDevices, "ListDevices"}, + {3, &MFIUser::StartDetection, "StartDetection"}, + {4, &MFIUser::StopDetection, "StopDetection"}, + {5, &MFIUser::Read, "Read"}, + {6, &MFIUser::Write, "Write"}, + {7, &MFIUser::GetTagInfo, "GetTagInfo"}, + {8, &MFIUser::GetActivateEventHandle, "GetActivateEventHandle"}, + {9, &MFIUser::GetDeactivateEventHandle, "GetDeactivateEventHandle"}, + {10, &MFIUser::GetState, "GetState"}, + {11, &MFIUser::GetDeviceState, "GetDeviceState"}, + {12, &MFIUser::GetNpadId, "GetNpadId"}, + {13, &MFIUser::GetAvailabilityChangeEventHandle, "GetAvailabilityChangeEventHandle"}, + }; + RegisterHandlers(functions); + + availability_change_event = service_context.CreateEvent("MFIUser:AvailabilityChangeEvent"); + + for (u32 device_index = 0; device_index < 10; device_index++) { + devices[device_index] = + std::make_shared<NfcDevice>(Core::HID::IndexToNpadIdType(device_index), system, + service_context, availability_change_event); + } +} + +MFIUser ::~MFIUser() { + availability_change_event->Close(); +} + +void MFIUser::Initialize(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_NFC, "called"); + + state = State::Initialized; + + for (auto& device : devices) { + device->Initialize(); + } + + IPC::ResponseBuilder rb{ctx, 2, 0}; + rb.Push(ResultSuccess); +} + +void MFIUser::Finalize(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_NFC, "called"); + + state = State::NonInitialized; + + for (auto& device : devices) { + device->Finalize(); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void MFIUser::ListDevices(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFC, "called"); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + if (!ctx.CanWriteBuffer()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareInvalidArgument); + return; + } + + if (ctx.GetWriteBufferSize() == 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareInvalidArgument); + return; + } + + std::vector<u64> nfp_devices; + const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements<u64>(); + + for (const auto& device : devices) { + if (nfp_devices.size() >= max_allowed_devices) { + continue; + } + if (device->GetCurrentState() != NFP::DeviceState::Unavailable) { + nfp_devices.push_back(device->GetHandle()); + } + } + + if (nfp_devices.empty()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + ctx.WriteBuffer(nfp_devices); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast<s32>(nfp_devices.size())); +} + +void MFIUser::StartDetection(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_INFO(Service_NFC, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + const auto result = device.value()->StartDetection(NFP::TagProtocol::All); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void MFIUser::StopDetection(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_INFO(Service_NFC, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + const auto result = device.value()->StopDetection(); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void MFIUser::Read(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + const auto buffer{ctx.ReadBuffer()}; + const auto number_of_commands{ctx.GetReadBufferNumElements<NFP::MifareReadBlockParameter>()}; + std::vector<NFP::MifareReadBlockParameter> read_commands(number_of_commands); + + memcpy(read_commands.data(), buffer.data(), + number_of_commands * sizeof(NFP::MifareReadBlockParameter)); + + LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, read_commands_size={}", + device_handle, number_of_commands); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + Result result = ResultSuccess; + std::vector<NFP::MifareReadBlockData> out_data(number_of_commands); + for (std::size_t i = 0; i < number_of_commands; i++) { + result = device.value()->MifareRead(read_commands[i], out_data[i]); + if (result.IsError()) { + break; + } + } + + ctx.WriteBuffer(out_data); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void MFIUser::Write(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + const auto buffer{ctx.ReadBuffer()}; + const auto number_of_commands{ctx.GetReadBufferNumElements<NFP::MifareWriteBlockParameter>()}; + std::vector<NFP::MifareWriteBlockParameter> write_commands(number_of_commands); + + memcpy(write_commands.data(), buffer.data(), + number_of_commands * sizeof(NFP::MifareWriteBlockParameter)); + + LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, write_commands_size={}", + device_handle, number_of_commands); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + Result result = ResultSuccess; + std::vector<NFP::MifareReadBlockData> out_data(number_of_commands); + for (std::size_t i = 0; i < number_of_commands; i++) { + result = device.value()->MifareWrite(write_commands[i]); + if (result.IsError()) { + break; + } + } + + if (result.IsSuccess()) { + result = device.value()->Flush(); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void MFIUser::GetTagInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_INFO(Service_NFC, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + NFP::TagInfo tag_info{}; + const auto result = device.value()->GetTagInfo(tag_info, true); + ctx.WriteBuffer(tag_info); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void MFIUser::GetActivateEventHandle(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(device.value()->GetActivateEvent()); +} + +void MFIUser::GetDeactivateEventHandle(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(device.value()->GetDeactivateEvent()); +} + +void MFIUser::GetState(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFC, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(state); +} + +void MFIUser::GetDeviceState(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(device.value()->GetCurrentState()); +} + +void MFIUser::GetNpadId(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop<u64>()}; + LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + auto device = GetNfcDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareDeviceNotFound); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(device.value()->GetNpadId()); +} + +void MFIUser::GetAvailabilityChangeEventHandle(Kernel::HLERequestContext& ctx) { + LOG_INFO(Service_NFC, "called"); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(MifareNfcDisabled); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(availability_change_event->GetReadableEvent()); +} + +std::optional<std::shared_ptr<NfcDevice>> MFIUser::GetNfcDevice(u64 handle) { + for (auto& device : devices) { + if (device->GetHandle() == handle) { + return device; + } + } + return std::nullopt; +} + +} // namespace Service::NFC diff --git a/src/core/hle/service/nfc/mifare_user.h b/src/core/hle/service/nfc/mifare_user.h new file mode 100644 index 000000000..0e0638cb6 --- /dev/null +++ b/src/core/hle/service/nfc/mifare_user.h @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include <memory> +#include <optional> + +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/service.h" + +namespace Service::NFC { +class NfcDevice; + +class MFIUser final : public ServiceFramework<MFIUser> { +public: + explicit MFIUser(Core::System& system_); + ~MFIUser(); + +private: + enum class State : u32 { + NonInitialized, + Initialized, + }; + + void Initialize(Kernel::HLERequestContext& ctx); + void Finalize(Kernel::HLERequestContext& ctx); + void ListDevices(Kernel::HLERequestContext& ctx); + void StartDetection(Kernel::HLERequestContext& ctx); + void StopDetection(Kernel::HLERequestContext& ctx); + void Read(Kernel::HLERequestContext& ctx); + void Write(Kernel::HLERequestContext& ctx); + void GetTagInfo(Kernel::HLERequestContext& ctx); + void GetActivateEventHandle(Kernel::HLERequestContext& ctx); + void GetDeactivateEventHandle(Kernel::HLERequestContext& ctx); + void GetState(Kernel::HLERequestContext& ctx); + void GetDeviceState(Kernel::HLERequestContext& ctx); + void GetNpadId(Kernel::HLERequestContext& ctx); + void GetAvailabilityChangeEventHandle(Kernel::HLERequestContext& ctx); + + std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle); + + KernelHelpers::ServiceContext service_context; + + std::array<std::shared_ptr<NfcDevice>, 10> devices{}; + + State state{State::NonInitialized}; + Kernel::KEvent* availability_change_event; +}; + +} // namespace Service::NFC diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp index 2f4bacb3b..b17b18ab9 100644 --- a/src/core/hle/service/nfc/nfc.cpp +++ b/src/core/hle/service/nfc/nfc.cpp @@ -6,6 +6,7 @@ #include "common/logging/log.h" #include "common/settings.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/service/nfc/mifare_user.h" #include "core/hle/service/nfc/nfc.h" #include "core/hle/service/nfc/nfc_user.h" #include "core/hle/service/service.h" @@ -50,32 +51,6 @@ private: } }; -class MFIUser final : public ServiceFramework<MFIUser> { -public: - explicit MFIUser(Core::System& system_) : ServiceFramework{system_, "NFC::MFIUser"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "Initialize"}, - {1, nullptr, "Finalize"}, - {2, nullptr, "ListDevices"}, - {3, nullptr, "StartDetection"}, - {4, nullptr, "StopDetection"}, - {5, nullptr, "Read"}, - {6, nullptr, "Write"}, - {7, nullptr, "GetTagInfo"}, - {8, nullptr, "GetActivateEventHandle"}, - {9, nullptr, "GetDeactivateEventHandle"}, - {10, nullptr, "GetState"}, - {11, nullptr, "GetDeviceState"}, - {12, nullptr, "GetNpadId"}, - {13, nullptr, "GetAvailabilityChangeEventHandle"}, - }; - // clang-format on - - RegisterHandlers(functions); - } -}; - class NFC_MF_U final : public ServiceFramework<NFC_MF_U> { public: explicit NFC_MF_U(Core::System& system_) : ServiceFramework{system_, "nfc:mf:u"} { diff --git a/src/core/hle/service/nfc/nfc_device.cpp b/src/core/hle/service/nfc/nfc_device.cpp index 4d514cf5f..78578f723 100644 --- a/src/core/hle/service/nfc/nfc_device.cpp +++ b/src/core/hle/service/nfc/nfc_device.cpp @@ -77,11 +77,13 @@ bool NfcDevice::LoadNfcTag(std::span<const u8> data) { return false; } - if (data.size() != sizeof(NFP::EncryptedNTAG215File)) { + if (data.size() < sizeof(NFP::EncryptedNTAG215File)) { LOG_ERROR(Service_NFC, "Not an amiibo, size={}", data.size()); return false; } + tag_data.resize(data.size()); + memcpy(tag_data.data(), data.data(), data.size()); memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File)); device_state = NFP::DeviceState::TagFound; @@ -121,7 +123,7 @@ void NfcDevice::Finalize() { device_state = NFP::DeviceState::Unavailable; } -Result NfcDevice::StartDetection(s32 protocol_) { +Result NfcDevice::StartDetection(NFP::TagProtocol allowed_protocol) { if (device_state != NFP::DeviceState::Initialized && device_state != NFP::DeviceState::TagRemoved) { LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); @@ -134,7 +136,7 @@ Result NfcDevice::StartDetection(s32 protocol_) { } device_state = NFP::DeviceState::SearchingForTag; - protocol = protocol_; + allowed_protocols = allowed_protocol; return ResultSuccess; } @@ -160,7 +162,7 @@ Result NfcDevice::StopDetection() { return WrongDeviceState; } -Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info) const { +Result NfcDevice::Flush() { if (device_state != NFP::DeviceState::TagFound && device_state != NFP::DeviceState::TagMounted) { LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); @@ -170,6 +172,34 @@ Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info) const { return WrongDeviceState; } + if (!npad_device->WriteNfc(tag_data)) { + LOG_ERROR(Service_NFP, "Error writing to file"); + return MifareReadError; + } + + return ResultSuccess; +} + +Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const { + if (device_state != NFP::DeviceState::TagFound && + device_state != NFP::DeviceState::TagMounted) { + LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); + if (device_state == NFP::DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (is_mifare) { + tag_info = { + .uuid = encrypted_tag_data.uuid.uid, + .uuid_length = static_cast<u8>(encrypted_tag_data.uuid.uid.size()), + .protocol = NFP::TagProtocol::TypeA, + .tag_type = NFP::TagType::Type4, + }; + return ResultSuccess; + } + // Protocol and tag type may change here tag_info = { .uuid = encrypted_tag_data.uuid.uid, @@ -181,6 +211,52 @@ Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info) const { return ResultSuccess; } +Result NfcDevice::MifareRead(const NFP::MifareReadBlockParameter& parameter, + NFP::MifareReadBlockData& read_block_data) { + const std::size_t sector_index = parameter.sector_number * sizeof(NFP::DataBlock); + read_block_data.sector_number = parameter.sector_number; + + if (device_state != NFP::DeviceState::TagFound && + device_state != NFP::DeviceState::TagMounted) { + LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); + if (device_state == NFP::DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (tag_data.size() < sector_index + sizeof(NFP::DataBlock)) { + return MifareReadError; + } + + // TODO: Use parameter.sector_key to read encrypted data + memcpy(read_block_data.data.data(), tag_data.data() + sector_index, sizeof(NFP::DataBlock)); + + return ResultSuccess; +} + +Result NfcDevice::MifareWrite(const NFP::MifareWriteBlockParameter& parameter) { + const std::size_t sector_index = parameter.sector_number * sizeof(NFP::DataBlock); + + if (device_state != NFP::DeviceState::TagFound && + device_state != NFP::DeviceState::TagMounted) { + LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); + if (device_state == NFP::DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (tag_data.size() < sector_index + sizeof(NFP::DataBlock)) { + return MifareReadError; + } + + // TODO: Use parameter.sector_key to encrypt the data + memcpy(tag_data.data() + sector_index, parameter.data.data(), sizeof(NFP::DataBlock)); + + return ResultSuccess; +} + u64 NfcDevice::GetHandle() const { // Generate a handle based of the npad id return static_cast<u64>(npad_id); diff --git a/src/core/hle/service/nfc/nfc_device.h b/src/core/hle/service/nfc/nfc_device.h index fa1348f1a..a6e114d36 100644 --- a/src/core/hle/service/nfc/nfc_device.h +++ b/src/core/hle/service/nfc/nfc_device.h @@ -34,10 +34,16 @@ public: void Initialize(); void Finalize(); - Result StartDetection(s32 protocol_); + Result StartDetection(NFP::TagProtocol allowed_protocol); Result StopDetection(); + Result Flush(); - Result GetTagInfo(NFP::TagInfo& tag_info) const; + Result GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const; + + Result MifareRead(const NFP::MifareReadBlockParameter& parameter, + NFP::MifareReadBlockData& read_block_data); + + Result MifareWrite(const NFP::MifareWriteBlockParameter& parameter); u64 GetHandle() const; NFP::DeviceState GetCurrentState() const; @@ -61,10 +67,11 @@ private: Kernel::KEvent* deactivate_event = nullptr; Kernel::KEvent* availability_change_event = nullptr; - s32 protocol{}; + NFP::TagProtocol allowed_protocols{}; NFP::DeviceState device_state{NFP::DeviceState::Unavailable}; NFP::EncryptedNTAG215File encrypted_tag_data{}; + std::vector<u8> tag_data{}; }; } // namespace Service::NFC diff --git a/src/core/hle/service/nfc/nfc_result.h b/src/core/hle/service/nfc/nfc_result.h index 537dc15f4..146b8ba61 100644 --- a/src/core/hle/service/nfc/nfc_result.h +++ b/src/core/hle/service/nfc/nfc_result.h @@ -12,6 +12,12 @@ constexpr Result InvalidArgument(ErrorModule::NFC, 65); constexpr Result WrongDeviceState(ErrorModule::NFC, 73); constexpr Result NfcDisabled(ErrorModule::NFC, 80); constexpr Result TagRemoved(ErrorModule::NFC, 97); -constexpr Result CorruptedData(ErrorModule::NFC, 144); + +constexpr Result MifareDeviceNotFound(ErrorModule::NFCMifare, 64); +constexpr Result MifareInvalidArgument(ErrorModule::NFCMifare, 65); +constexpr Result MifareWrongDeviceState(ErrorModule::NFCMifare, 73); +constexpr Result MifareNfcDisabled(ErrorModule::NFCMifare, 80); +constexpr Result MifareTagRemoved(ErrorModule::NFCMifare, 97); +constexpr Result MifareReadError(ErrorModule::NFCMifare, 288); } // namespace Service::NFC diff --git a/src/core/hle/service/nfc/nfc_user.cpp b/src/core/hle/service/nfc/nfc_user.cpp index ced2d560b..4615697e2 100644 --- a/src/core/hle/service/nfc/nfc_user.cpp +++ b/src/core/hle/service/nfc/nfc_user.cpp @@ -201,7 +201,7 @@ void IUser::AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { void IUser::StartDetection(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop<u64>()}; - const auto nfp_protocol{rp.Pop<s32>()}; + const auto nfp_protocol{rp.PopEnum<NFP::TagProtocol>()}; LOG_INFO(Service_NFC, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol); if (state == State::NonInitialized) { @@ -267,7 +267,7 @@ void IUser::GetTagInfo(Kernel::HLERequestContext& ctx) { } NFP::TagInfo tag_info{}; - const auto result = device.value()->GetTagInfo(tag_info); + const auto result = device.value()->GetTagInfo(tag_info, false); ctx.WriteBuffer(tag_info); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h index 69858096a..fc228c2b2 100644 --- a/src/core/hle/service/nfp/nfp_types.h +++ b/src/core/hle/service/nfp/nfp_types.h @@ -106,11 +106,24 @@ enum class CabinetMode : u8 { StartFormatter, }; +enum class MifareCmd : u8 { + AuthA = 0x60, + AuthB = 0x61, + Read = 0x30, + Write = 0xA0, + Transfer = 0xB0, + Decrement = 0xC0, + Increment = 0xC1, + Store = 0xC2 +}; + using UniqueSerialNumber = std::array<u8, 7>; using LockBytes = std::array<u8, 2>; using HashData = std::array<u8, 0x20>; using ApplicationArea = std::array<u8, 0xD8>; using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>; +using DataBlock = std::array<u8, 0x10>; +using KeyData = std::array<u8, 0x6>; struct TagUuid { UniqueSerialNumber uid; @@ -323,4 +336,37 @@ struct RegisterInfo { }; static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size"); +struct SectorKey { + MifareCmd command; + u8 unknown; // Usually 1 + INSERT_PADDING_BYTES(0x6); + KeyData sector_key; + INSERT_PADDING_BYTES(0x2); +}; +static_assert(sizeof(SectorKey) == 0x10, "SectorKey is an invalid size"); + +struct MifareReadBlockParameter { + u8 sector_number; + INSERT_PADDING_BYTES(0x7); + SectorKey sector_key; +}; +static_assert(sizeof(MifareReadBlockParameter) == 0x18, + "MifareReadBlockParameter is an invalid size"); + +struct MifareReadBlockData { + DataBlock data; + u8 sector_number; + INSERT_PADDING_BYTES(0x7); +}; +static_assert(sizeof(MifareReadBlockData) == 0x18, "MifareReadBlockData is an invalid size"); + +struct MifareWriteBlockParameter { + DataBlock data; + u8 sector_number; + INSERT_PADDING_BYTES(0x7); + SectorKey sector_key; +}; +static_assert(sizeof(MifareWriteBlockParameter) == 0x28, + "MifareWriteBlockParameter is an invalid size"); + } // namespace Service::NFP diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index e3ef06481..4fa9f51a6 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -129,6 +129,9 @@ static_assert(sizeof(NifmNetworkProfileData) == 0x18E, "NifmNetworkProfileData has incorrect size."); #pragma pack(pop) +constexpr Result ResultPendingConnection{ErrorModule::NIFM, 111}; +constexpr Result ResultNetworkCommunicationDisabled{ErrorModule::NIFM, 1111}; + class IScanRequest final : public ServiceFramework<IScanRequest> { public: explicit IScanRequest(Core::System& system_) : ServiceFramework{system_, "IScanRequest"} { @@ -192,6 +195,10 @@ private: void Submit(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); + if (state == RequestState::NotSubmitted) { + UpdateState(RequestState::Pending); + } + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } @@ -201,19 +208,32 @@ private: IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - - if (Network::GetHostIPv4Address().has_value()) { - rb.PushEnum(RequestState::Connected); - } else { - rb.PushEnum(RequestState::NotSubmitted); - } + rb.PushEnum(state); } void GetResult(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); + const auto result = [this] { + const auto has_connection = Network::GetHostIPv4Address().has_value(); + switch (state) { + case RequestState::NotSubmitted: + return has_connection ? ResultSuccess : ResultNetworkCommunicationDisabled; + case RequestState::Pending: + if (has_connection) { + UpdateState(RequestState::Connected); + } else { + UpdateState(RequestState::Error); + } + return ResultPendingConnection; + case RequestState::Connected: + default: + return ResultSuccess; + } + }(); + IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void GetSystemEventReadableHandles(Kernel::HLERequestContext& ctx) { @@ -252,8 +272,15 @@ private: rb.Push<u32>(0); } + void UpdateState(RequestState new_state) { + state = new_state; + event1->Signal(); + } + KernelHelpers::ServiceContext service_context; + RequestState state; + Kernel::KEvent* event1; Kernel::KEvent* event2; }; diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 460bef976..9b22397db 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -11,6 +11,7 @@ #include <vector> #include "common/common_types.h" +#include "common/polyfill_thread.h" #include "core/hle/result.h" #include "core/hle/service/kernel_helpers.h" diff --git a/src/core/internal_network/network_interface.cpp b/src/core/internal_network/network_interface.cpp index 057fd3661..7b8e510a2 100644 --- a/src/core/internal_network/network_interface.cpp +++ b/src/core/internal_network/network_interface.cpp @@ -9,6 +9,7 @@ #include "common/bit_cast.h" #include "common/common_types.h" #include "common/logging/log.h" +#include "common/polyfill_ranges.h" #include "common/settings.h" #include "common/string_util.h" #include "core/internal_network/network_interface.h" diff --git a/src/core/precompiled_headers.h b/src/core/precompiled_headers.h new file mode 100644 index 000000000..30a31001d --- /dev/null +++ b/src/core/precompiled_headers.h @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <boost/container/flat_map.hpp> // used by service.h which is heavily included +#include <boost/intrusive/rbtree.hpp> // used by k_auto_object.h which is heavily included + +#include "common/common_precompiled_headers.h" + +#include "core/hle/kernel/k_process.h" diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index 6e21296f6..77821e047 100644 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp @@ -38,7 +38,7 @@ std::string GetTimestamp() { using namespace nlohmann; -void SaveToFile(json json, const std::filesystem::path& filename) { +void SaveToFile(const json& json, const std::filesystem::path& filename) { if (!Common::FS::CreateParentDirs(filename)) { LOG_ERROR(Core, "Failed to create path for '{}' to save report!", Common::FS::PathToUTF8String(filename)); @@ -81,8 +81,8 @@ json GetReportCommonData(u64 title_id, Result result, const std::string& timesta } json GetProcessorStateData(const std::string& architecture, u64 entry_point, u64 sp, u64 pc, - u64 pstate, std::array<u64, 31> registers, - std::optional<std::array<u64, 32>> backtrace = {}) { + u64 pstate, const std::array<u64, 31>& registers, + const std::optional<std::array<u64, 32>>& backtrace = {}) { auto out = json{ {"entry_point", fmt::format("{:016X}", entry_point)}, {"sp", fmt::format("{:016X}", sp)}, @@ -224,11 +224,11 @@ void Reporter::SaveCrashReport(u64 title_id, Result result, u64 set_flags, u64 e out["processor_state"] = std::move(proc_out); - SaveToFile(std::move(out), GetPath("crash_report", title_id, timestamp)); + SaveToFile(out, GetPath("crash_report", title_id, timestamp)); } void Reporter::SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 info2, - std::optional<std::vector<u8>> resolved_buffer) const { + const std::optional<std::vector<u8>>& resolved_buffer) const { if (!IsReportingEnabled()) { return; } @@ -250,7 +250,7 @@ void Reporter::SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 out["svc_break"] = std::move(break_out); - SaveToFile(std::move(out), GetPath("svc_break_report", title_id, timestamp)); + SaveToFile(out, GetPath("svc_break_report", title_id, timestamp)); } void Reporter::SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u32 command_id, @@ -271,13 +271,13 @@ void Reporter::SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u out["function"] = std::move(function_out); - SaveToFile(std::move(out), GetPath("unimpl_func_report", title_id, timestamp)); + SaveToFile(out, GetPath("unimpl_func_report", title_id, timestamp)); } void Reporter::SaveUnimplementedAppletReport( u32 applet_id, u32 common_args_version, u32 library_version, u32 theme_color, - bool startup_sound, u64 system_tick, std::vector<std::vector<u8>> normal_channel, - std::vector<std::vector<u8>> interactive_channel) const { + bool startup_sound, u64 system_tick, const std::vector<std::vector<u8>>& normal_channel, + const std::vector<std::vector<u8>>& interactive_channel) const { if (!IsReportingEnabled()) { return; } @@ -308,10 +308,11 @@ void Reporter::SaveUnimplementedAppletReport( out["applet_normal_data"] = std::move(normal_out); out["applet_interactive_data"] = std::move(interactive_out); - SaveToFile(std::move(out), GetPath("unimpl_applet_report", title_id, timestamp)); + SaveToFile(out, GetPath("unimpl_applet_report", title_id, timestamp)); } -void Reporter::SavePlayReport(PlayReportType type, u64 title_id, std::vector<std::vector<u8>> data, +void Reporter::SavePlayReport(PlayReportType type, u64 title_id, + const std::vector<std::vector<u8>>& data, std::optional<u64> process_id, std::optional<u128> user_id) const { if (!IsReportingEnabled()) { return; @@ -335,12 +336,12 @@ void Reporter::SavePlayReport(PlayReportType type, u64 title_id, std::vector<std out["play_report_type"] = fmt::format("{:02}", static_cast<u8>(type)); out["play_report_data"] = std::move(data_out); - SaveToFile(std::move(out), GetPath("play_report", title_id, timestamp)); + SaveToFile(out, GetPath("play_report", title_id, timestamp)); } void Reporter::SaveErrorReport(u64 title_id, Result result, - std::optional<std::string> custom_text_main, - std::optional<std::string> custom_text_detail) const { + const std::optional<std::string>& custom_text_main, + const std::optional<std::string>& custom_text_detail) const { if (!IsReportingEnabled()) { return; } @@ -354,11 +355,11 @@ void Reporter::SaveErrorReport(u64 title_id, Result result, out["backtrace"] = GetBacktraceData(system); out["error_custom_text"] = { - {"main", *custom_text_main}, - {"detail", *custom_text_detail}, + {"main", custom_text_main.value_or("")}, + {"detail", custom_text_detail.value_or("")}, }; - SaveToFile(std::move(out), GetPath("error_report", title_id, timestamp)); + SaveToFile(out, GetPath("error_report", title_id, timestamp)); } void Reporter::SaveFSAccessLog(std::string_view log_message) const { diff --git a/src/core/reporter.h b/src/core/reporter.h index 68755cbde..9fdb9d6c1 100644 --- a/src/core/reporter.h +++ b/src/core/reporter.h @@ -36,7 +36,7 @@ public: // Used by syscall svcBreak void SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 info2, - std::optional<std::vector<u8>> resolved_buffer = {}) const; + const std::optional<std::vector<u8>>& resolved_buffer = {}) const; // Used by HLE service handler void SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u32 command_id, @@ -44,10 +44,10 @@ public: const std::string& service_name) const; // Used by stub applet implementation - void SaveUnimplementedAppletReport(u32 applet_id, u32 common_args_version, u32 library_version, - u32 theme_color, bool startup_sound, u64 system_tick, - std::vector<std::vector<u8>> normal_channel, - std::vector<std::vector<u8>> interactive_channel) const; + void SaveUnimplementedAppletReport( + u32 applet_id, u32 common_args_version, u32 library_version, u32 theme_color, + bool startup_sound, u64 system_tick, const std::vector<std::vector<u8>>& normal_channel, + const std::vector<std::vector<u8>>& interactive_channel) const; enum class PlayReportType { Old, @@ -56,13 +56,13 @@ public: System, }; - void SavePlayReport(PlayReportType type, u64 title_id, std::vector<std::vector<u8>> data, + void SavePlayReport(PlayReportType type, u64 title_id, const std::vector<std::vector<u8>>& data, std::optional<u64> process_id = {}, std::optional<u128> user_id = {}) const; // Used by error applet void SaveErrorReport(u64 title_id, Result result, - std::optional<std::string> custom_text_main = {}, - std::optional<std::string> custom_text_detail = {}) const; + const std::optional<std::string>& custom_text_main = {}, + const std::optional<std::string>& custom_text_detail = {}) const; void SaveFSAccessLog(std::string_view log_message) const; diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index abcf6eb11..8d5f2be2f 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -55,6 +55,8 @@ static const char* TranslateRenderer(Settings::RendererBackend backend) { return "OpenGL"; case Settings::RendererBackend::Vulkan: return "Vulkan"; + case Settings::RendererBackend::Null: + return "Null"; } return "Unknown"; } diff --git a/src/dedicated_room/CMakeLists.txt b/src/dedicated_room/CMakeLists.txt index 2d9731f19..5bbe1d4b5 100644 --- a/src/dedicated_room/CMakeLists.txt +++ b/src/dedicated_room/CMakeLists.txt @@ -4,6 +4,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules) add_executable(yuzu-room + precompiled_headers.h yuzu_room.cpp yuzu_room.rc ) @@ -25,3 +26,7 @@ target_link_libraries(yuzu-room PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) if(UNIX AND NOT APPLE) install(TARGETS yuzu-room) endif() + +if (YUZU_USE_PRECOMPILED_HEADERS) + target_precompile_headers(yuzu-room PRIVATE precompiled_headers.h) +endif() diff --git a/src/dedicated_room/precompiled_headers.h b/src/dedicated_room/precompiled_headers.h new file mode 100644 index 000000000..aabae730b --- /dev/null +++ b/src/dedicated_room/precompiled_headers.h @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_precompiled_headers.h" diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index 193127d0a..7932aaab0 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -34,6 +34,7 @@ add_library(input_common STATIC input_poller.h main.cpp main.h + precompiled_headers.h ) if (MSVC) @@ -55,15 +56,13 @@ if (ENABLE_SDL2) drivers/sdl_driver.cpp drivers/sdl_driver.h ) - if (YUZU_USE_EXTERNAL_SDL2) - target_link_libraries(input_common PRIVATE SDL2-static) - else() - target_link_libraries(input_common PRIVATE SDL2) - endif() + target_link_libraries(input_common PRIVATE SDL2::SDL2) target_compile_definitions(input_common PRIVATE HAVE_SDL2) endif() -target_link_libraries(input_common PRIVATE usb) - create_target_directory_groups(input_common) -target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost) +target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost libusb::usb) + +if (YUZU_USE_PRECOMPILED_HEADERS) + target_precompile_headers(input_common PRIVATE precompiled_headers.h) +endif() diff --git a/src/input_common/drivers/gc_adapter.h b/src/input_common/drivers/gc_adapter.h index 7f81767f7..b5270fd0b 100644 --- a/src/input_common/drivers/gc_adapter.h +++ b/src/input_common/drivers/gc_adapter.h @@ -5,10 +5,10 @@ #include <array> #include <memory> -#include <stop_token> #include <string> #include <thread> +#include "common/polyfill_thread.h" #include "input_common/input_engine.h" struct libusb_context; diff --git a/src/input_common/drivers/mouse.cpp b/src/input_common/drivers/mouse.cpp index 98c3157a8..faf9cbdc3 100644 --- a/src/input_common/drivers/mouse.cpp +++ b/src/input_common/drivers/mouse.cpp @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include <stop_token> #include <thread> #include <fmt/format.h> diff --git a/src/input_common/drivers/mouse.h b/src/input_common/drivers/mouse.h index 286ce1cf6..72073cc23 100644 --- a/src/input_common/drivers/mouse.h +++ b/src/input_common/drivers/mouse.h @@ -3,9 +3,9 @@ #pragma once -#include <stop_token> #include <thread> +#include "common/polyfill_thread.h" #include "common/vector_math.h" #include "input_common/input_engine.h" diff --git a/src/input_common/drivers/tas_input.cpp b/src/input_common/drivers/tas_input.cpp index 21c6ed405..f3ade90da 100644 --- a/src/input_common/drivers/tas_input.cpp +++ b/src/input_common/drivers/tas_input.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> +#include <sstream> #include <fmt/format.h> #include "common/fs/file.h" diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp index 564a188e5..63ffaca67 100644 --- a/src/input_common/drivers/virtual_amiibo.cpp +++ b/src/input_common/drivers/virtual_amiibo.cpp @@ -47,20 +47,20 @@ Common::Input::NfcState VirtualAmiibo::SupportsNfc( Common::Input::NfcState VirtualAmiibo::WriteNfcData( [[maybe_unused]] const PadIdentifier& identifier_, const std::vector<u8>& data) { - const Common::FS::IOFile amiibo_file{file_path, Common::FS::FileAccessMode::ReadWrite, - Common::FS::FileType::BinaryFile}; + const Common::FS::IOFile nfc_file{file_path, Common::FS::FileAccessMode::ReadWrite, + Common::FS::FileType::BinaryFile}; - if (!amiibo_file.IsOpen()) { + if (!nfc_file.IsOpen()) { LOG_ERROR(Core, "Amiibo is already on use"); return Common::Input::NfcState::WriteFailed; } - if (!amiibo_file.Write(data)) { + if (!nfc_file.Write(data)) { LOG_ERROR(Service_NFP, "Error writting to file"); return Common::Input::NfcState::WriteFailed; } - amiibo_data = data; + nfc_data = data; return Common::Input::NfcState::Success; } @@ -70,32 +70,44 @@ VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const { } VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) { - const Common::FS::IOFile amiibo_file{filename, Common::FS::FileAccessMode::Read, - Common::FS::FileType::BinaryFile}; + const Common::FS::IOFile nfc_file{filename, Common::FS::FileAccessMode::Read, + Common::FS::FileType::BinaryFile}; if (state != State::WaitingForAmiibo) { return Info::WrongDeviceState; } - if (!amiibo_file.IsOpen()) { + if (!nfc_file.IsOpen()) { return Info::UnableToLoad; } - amiibo_data.resize(amiibo_size); - - if (amiibo_file.Read(amiibo_data) < amiibo_size_without_password) { + switch (nfc_file.GetSize()) { + case AmiiboSize: + case AmiiboSizeWithoutPassword: + nfc_data.resize(AmiiboSize); + if (nfc_file.Read(nfc_data) < AmiiboSizeWithoutPassword) { + return Info::NotAnAmiibo; + } + break; + case MifareSize: + nfc_data.resize(MifareSize); + if (nfc_file.Read(nfc_data) < MifareSize) { + return Info::NotAnAmiibo; + } + break; + default: return Info::NotAnAmiibo; } file_path = filename; state = State::AmiiboIsOpen; - SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, amiibo_data}); + SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data}); return Info::Success; } VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() { if (state == State::AmiiboIsOpen) { - SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, amiibo_data}); + SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data}); return Info::Success; } diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h index 9baeb3997..0f9dad333 100644 --- a/src/input_common/drivers/virtual_amiibo.h +++ b/src/input_common/drivers/virtual_amiibo.h @@ -53,12 +53,13 @@ public: std::string GetLastFilePath() const; private: - static constexpr std::size_t amiibo_size = 0x21C; - static constexpr std::size_t amiibo_size_without_password = amiibo_size - 0x8; + static constexpr std::size_t AmiiboSize = 0x21C; + static constexpr std::size_t AmiiboSizeWithoutPassword = AmiiboSize - 0x8; + static constexpr std::size_t MifareSize = 0x400; std::string file_path{}; State state{State::Initialized}; - std::vector<u8> amiibo_data; + std::vector<u8> nfc_data; Common::Input::PollingMode polling_mode{Common::Input::PollingMode::Pasive}; }; } // namespace InputCommon diff --git a/src/input_common/precompiled_headers.h b/src/input_common/precompiled_headers.h new file mode 100644 index 000000000..aabae730b --- /dev/null +++ b/src/input_common/precompiled_headers.h @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_precompiled_headers.h" diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt index 6f8ca4b90..1ab52da59 100644 --- a/src/network/CMakeLists.txt +++ b/src/network/CMakeLists.txt @@ -8,6 +8,7 @@ add_library(network STATIC network.h packet.cpp packet.h + precompiled_headers.h room.cpp room.h room_member.cpp @@ -18,8 +19,12 @@ add_library(network STATIC create_target_directory_groups(network) -target_link_libraries(network PRIVATE common enet Boost::boost) +target_link_libraries(network PRIVATE common enet::enet Boost::boost) if (ENABLE_WEB_SERVICE) target_compile_definitions(network PRIVATE -DENABLE_WEB_SERVICE) target_link_libraries(network PRIVATE web_service) endif() + +if (YUZU_USE_PRECOMPILED_HEADERS) + target_precompile_headers(network PRIVATE precompiled_headers.h) +endif() diff --git a/src/network/precompiled_headers.h b/src/network/precompiled_headers.h new file mode 100644 index 000000000..aabae730b --- /dev/null +++ b/src/network/precompiled_headers.h @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_precompiled_headers.h" diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt index 545d69c7e..525b2363c 100644 --- a/src/shader_recompiler/CMakeLists.txt +++ b/src/shader_recompiler/CMakeLists.txt @@ -221,6 +221,7 @@ add_library(shader_recompiler STATIC ir_opt/dual_vertex_pass.cpp ir_opt/global_memory_to_storage_buffer_pass.cpp ir_opt/identity_removal_pass.cpp + ir_opt/layer_pass.cpp ir_opt/lower_fp16_to_fp32.cpp ir_opt/lower_int64_to_int32.cpp ir_opt/passes.h @@ -230,6 +231,7 @@ add_library(shader_recompiler STATIC ir_opt/texture_pass.cpp ir_opt/verification_pass.cpp object_pool.h + precompiled_headers.h profile.h program_header.h runtime_info.h @@ -254,7 +256,12 @@ else() # Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6. # And this in turns limits the size of a std::array. $<$<CXX_COMPILER_ID:Clang>:-fbracket-depth=1024> + $<$<CXX_COMPILER_ID:AppleClang>:-fbracket-depth=1024> ) endif() create_target_directory_groups(shader_recompiler) + +if (YUZU_USE_PRECOMPILED_HEADERS) + target_precompile_headers(shader_recompiler PRIVATE precompiled_headers.h) +endif() diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index 265ac9c85..0f86a8004 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -402,8 +402,10 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct ctx.AddCapability(spv::Capability::SparseResidency); } if (info.uses_demote_to_helper_invocation && profile.support_demote_to_helper_invocation) { - ctx.AddExtension("SPV_EXT_demote_to_helper_invocation"); - ctx.AddCapability(spv::Capability::DemoteToHelperInvocationEXT); + if (profile.supported_spirv < 0x00010600) { + ctx.AddExtension("SPV_EXT_demote_to_helper_invocation"); + } + ctx.AddCapability(spv::Capability::DemoteToHelperInvocation); } if (info.stores[IR::Attribute::ViewportIndex]) { ctx.AddCapability(spv::Capability::MultiViewport); @@ -426,12 +428,11 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct if ((info.uses_subgroup_vote || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles) && profile.support_vote) { - ctx.AddExtension("SPV_KHR_shader_ballot"); - ctx.AddCapability(spv::Capability::SubgroupBallotKHR); + ctx.AddCapability(spv::Capability::GroupNonUniformBallot); + ctx.AddCapability(spv::Capability::GroupNonUniformShuffle); if (!profile.warp_size_potentially_larger_than_guest) { // vote ops are only used when not taking the long path - ctx.AddExtension("SPV_KHR_subgroup_vote"); - ctx.AddCapability(spv::Capability::SubgroupVoteKHR); + ctx.AddCapability(spv::Capability::GroupNonUniformVote); } } if (info.uses_int64_bit_atomics && profile.support_int64_atomics) { diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp index 7ad0b08ac..fb2c792c1 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp @@ -12,7 +12,7 @@ void EmitJoin(EmitContext&) { void EmitDemoteToHelperInvocation(EmitContext& ctx) { if (ctx.profile.support_demote_to_helper_invocation) { - ctx.OpDemoteToHelperInvocationEXT(); + ctx.OpDemoteToHelperInvocation(); } else { const Id kill_label{ctx.OpLabel()}; const Id impossible_label{ctx.OpLabel()}; diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp index 7cbbbfaa6..2c90f2368 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp @@ -6,6 +6,10 @@ namespace Shader::Backend::SPIRV { namespace { +Id SubgroupScope(EmitContext& ctx) { + return ctx.Const(static_cast<u32>(spv::Scope::Subgroup)); +} + Id GetThreadId(EmitContext& ctx) { return ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id); } @@ -49,8 +53,9 @@ Id GetMaxThreadId(EmitContext& ctx, Id thread_id, Id clamp, Id segmentation_mask } Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) { - return ctx.OpSelect(ctx.U32[1], in_range, - ctx.OpSubgroupReadInvocationKHR(ctx.U32[1], value, src_thread_id), value); + return ctx.OpSelect( + ctx.U32[1], in_range, + ctx.OpGroupNonUniformShuffle(ctx.U32[1], SubgroupScope(ctx), value, src_thread_id), value); } Id GetUpperClamp(EmitContext& ctx, Id invocation_id, Id clamp) { @@ -71,40 +76,46 @@ Id EmitLaneId(EmitContext& ctx) { Id EmitVoteAll(EmitContext& ctx, Id pred) { if (!ctx.profile.warp_size_potentially_larger_than_guest) { - return ctx.OpSubgroupAllKHR(ctx.U1, pred); + return ctx.OpGroupNonUniformAll(ctx.U1, SubgroupScope(ctx), pred); } - const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)}; + const Id mask_ballot{ + ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), ctx.true_value)}; const Id active_mask{WarpExtract(ctx, mask_ballot)}; - const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))}; + const Id ballot{ + WarpExtract(ctx, ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred))}; const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)}; return ctx.OpIEqual(ctx.U1, lhs, active_mask); } Id EmitVoteAny(EmitContext& ctx, Id pred) { if (!ctx.profile.warp_size_potentially_larger_than_guest) { - return ctx.OpSubgroupAnyKHR(ctx.U1, pred); + return ctx.OpGroupNonUniformAny(ctx.U1, SubgroupScope(ctx), pred); } - const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)}; + const Id mask_ballot{ + ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), ctx.true_value)}; const Id active_mask{WarpExtract(ctx, mask_ballot)}; - const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))}; + const Id ballot{ + WarpExtract(ctx, ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred))}; const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)}; return ctx.OpINotEqual(ctx.U1, lhs, ctx.u32_zero_value); } Id EmitVoteEqual(EmitContext& ctx, Id pred) { if (!ctx.profile.warp_size_potentially_larger_than_guest) { - return ctx.OpSubgroupAllEqualKHR(ctx.U1, pred); + return ctx.OpGroupNonUniformAllEqual(ctx.U1, SubgroupScope(ctx), pred); } - const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)}; + const Id mask_ballot{ + ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), ctx.true_value)}; const Id active_mask{WarpExtract(ctx, mask_ballot)}; - const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))}; + const Id ballot{ + WarpExtract(ctx, ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred))}; const Id lhs{ctx.OpBitwiseXor(ctx.U32[1], ballot, active_mask)}; return ctx.OpLogicalOr(ctx.U1, ctx.OpIEqual(ctx.U1, lhs, ctx.u32_zero_value), ctx.OpIEqual(ctx.U1, lhs, active_mask)); } Id EmitSubgroupBallot(EmitContext& ctx, Id pred) { - const Id ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], pred)}; + const Id ballot{ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred)}; if (!ctx.profile.warp_size_potentially_larger_than_guest) { return ctx.OpCompositeExtract(ctx.U32[1], ballot, 0U); } diff --git a/src/shader_recompiler/frontend/ir/opcodes.h b/src/shader_recompiler/frontend/ir/opcodes.h index e70d7745c..d155afd0f 100644 --- a/src/shader_recompiler/frontend/ir/opcodes.h +++ b/src/shader_recompiler/frontend/ir/opcodes.h @@ -8,6 +8,7 @@ #include <fmt/format.h> +#include "common/polyfill_ranges.h" #include "shader_recompiler/frontend/ir/type.h" namespace Shader::IR { diff --git a/src/shader_recompiler/frontend/ir/value.h b/src/shader_recompiler/frontend/ir/value.h index e8bbb93a5..8b34356fd 100644 --- a/src/shader_recompiler/frontend/ir/value.h +++ b/src/shader_recompiler/frontend/ir/value.h @@ -23,7 +23,6 @@ #include "shader_recompiler/frontend/ir/pred.h" #include "shader_recompiler/frontend/ir/reg.h" #include "shader_recompiler/frontend/ir/type.h" -#include "shader_recompiler/frontend/ir/value.h" namespace Shader::IR { diff --git a/src/shader_recompiler/frontend/maxwell/control_flow.cpp b/src/shader_recompiler/frontend/maxwell/control_flow.cpp index 6939692cd..dce414cb4 100644 --- a/src/shader_recompiler/frontend/maxwell/control_flow.cpp +++ b/src/shader_recompiler/frontend/maxwell/control_flow.cpp @@ -9,6 +9,7 @@ #include <fmt/format.h> +#include "common/polyfill_ranges.h" #include "shader_recompiler/exception.h" #include "shader_recompiler/frontend/maxwell/control_flow.h" #include "shader_recompiler/frontend/maxwell/decode.h" diff --git a/src/shader_recompiler/frontend/maxwell/decode.cpp b/src/shader_recompiler/frontend/maxwell/decode.cpp index 455c91470..774f65bc5 100644 --- a/src/shader_recompiler/frontend/maxwell/decode.cpp +++ b/src/shader_recompiler/frontend/maxwell/decode.cpp @@ -7,6 +7,7 @@ #include <memory> #include "common/common_types.h" +#include "common/polyfill_ranges.h" #include "shader_recompiler/exception.h" #include "shader_recompiler/frontend/maxwell/decode.h" #include "shader_recompiler/frontend/maxwell/opcodes.h" diff --git a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp index ce42475d4..80c90fe6a 100644 --- a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp +++ b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp @@ -12,6 +12,7 @@ #include <boost/intrusive/list.hpp> +#include "common/polyfill_ranges.h" #include "shader_recompiler/environment.h" #include "shader_recompiler/frontend/ir/basic_block.h" #include "shader_recompiler/frontend/ir/ir_emitter.h" diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp index 4942878b9..85c18d942 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp @@ -176,12 +176,13 @@ void TranslateF2I(TranslatorVisitor& v, u64 insn, const IR::F16F32F64& src_a) { (f2i.src_format == SrcFormat::F64) != (f2i.dest_format == DestFormat::I64); if (special_nan_cases) { if (f2i.dest_format == DestFormat::I32) { + constexpr u32 nan_value = 0x8000'0000U; handled_special_case = true; - result = IR::U32{v.ir.Select(v.ir.FPIsNan(op_a), v.ir.Imm32(0x8000'0000U), result)}; + result = IR::U32{v.ir.Select(v.ir.FPIsNan(op_a), v.ir.Imm32(nan_value), result)}; } else if (f2i.dest_format == DestFormat::I64) { + constexpr u64 nan_value = 0x8000'0000'0000'0000ULL; handled_special_case = true; - result = IR::U64{ - v.ir.Select(v.ir.FPIsNan(op_a), v.ir.Imm64(0x8000'0000'0000'0000UL), result)}; + result = IR::U64{v.ir.Select(v.ir.FPIsNan(op_a), v.ir.Imm64(nan_value), result)}; } } if (!handled_special_case && is_signed) { diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index 376aae0ea..3adbd2b16 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp @@ -9,6 +9,7 @@ #include "common/settings.h" #include "shader_recompiler/exception.h" #include "shader_recompiler/frontend/ir/basic_block.h" +#include "shader_recompiler/frontend/ir/ir_emitter.h" #include "shader_recompiler/frontend/ir/post_order.h" #include "shader_recompiler/frontend/maxwell/structured_control_flow.h" #include "shader_recompiler/frontend/maxwell/translate/translate.h" @@ -233,6 +234,8 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo Optimization::VerificationPass(program); } Optimization::CollectShaderInfoPass(env, program); + Optimization::LayerPass(program, host_info); + CollectInterpolationInfo(env, program); AddNVNStorageBuffers(program); return program; @@ -331,4 +334,82 @@ void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& run } } +IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool, + ObjectPool<IR::Block>& block_pool, + const HostTranslateInfo& host_info, + IR::Program& source_program, + Shader::OutputTopology output_topology) { + IR::Program program; + program.stage = Stage::Geometry; + program.output_topology = output_topology; + switch (output_topology) { + case OutputTopology::PointList: + program.output_vertices = 1; + break; + case OutputTopology::LineStrip: + program.output_vertices = 2; + break; + default: + program.output_vertices = 3; + break; + } + + program.is_geometry_passthrough = false; + program.info.loads.mask = source_program.info.stores.mask; + program.info.stores.mask = source_program.info.stores.mask; + program.info.stores.Set(IR::Attribute::Layer, true); + program.info.stores.Set(source_program.info.emulated_layer, false); + + IR::Block* current_block = block_pool.Create(inst_pool); + auto& node{program.syntax_list.emplace_back()}; + node.type = IR::AbstractSyntaxNode::Type::Block; + node.data.block = current_block; + + IR::IREmitter ir{*current_block}; + for (u32 i = 0; i < program.output_vertices; i++) { + // Assign generics from input + for (u32 j = 0; j < 32; j++) { + if (!program.info.stores.Generic(j)) { + continue; + } + + const IR::Attribute attr = IR::Attribute::Generic0X + (j * 4); + ir.SetAttribute(attr + 0, ir.GetAttribute(attr + 0, ir.Imm32(i)), ir.Imm32(0)); + ir.SetAttribute(attr + 1, ir.GetAttribute(attr + 1, ir.Imm32(i)), ir.Imm32(0)); + ir.SetAttribute(attr + 2, ir.GetAttribute(attr + 2, ir.Imm32(i)), ir.Imm32(0)); + ir.SetAttribute(attr + 3, ir.GetAttribute(attr + 3, ir.Imm32(i)), ir.Imm32(0)); + } + + // Assign position from input + const IR::Attribute attr = IR::Attribute::PositionX; + ir.SetAttribute(attr + 0, ir.GetAttribute(attr + 0, ir.Imm32(i)), ir.Imm32(0)); + ir.SetAttribute(attr + 1, ir.GetAttribute(attr + 1, ir.Imm32(i)), ir.Imm32(0)); + ir.SetAttribute(attr + 2, ir.GetAttribute(attr + 2, ir.Imm32(i)), ir.Imm32(0)); + ir.SetAttribute(attr + 3, ir.GetAttribute(attr + 3, ir.Imm32(i)), ir.Imm32(0)); + + // Assign layer + ir.SetAttribute(IR::Attribute::Layer, ir.GetAttribute(source_program.info.emulated_layer), + ir.Imm32(0)); + + // Emit vertex + ir.EmitVertex(ir.Imm32(0)); + } + ir.EndPrimitive(ir.Imm32(0)); + + IR::Block* return_block{block_pool.Create(inst_pool)}; + IR::IREmitter{*return_block}.Epilogue(); + current_block->AddBranch(return_block); + + auto& merge{program.syntax_list.emplace_back()}; + merge.type = IR::AbstractSyntaxNode::Type::Block; + merge.data.block = return_block; + program.syntax_list.emplace_back().type = IR::AbstractSyntaxNode::Type::Return; + + program.blocks = GenerateBlocks(program.syntax_list); + program.post_order_blocks = PostOrder(program.syntax_list.front()); + Optimization::SsaRewritePass(program); + + return program; +} + } // namespace Shader::Maxwell diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.h b/src/shader_recompiler/frontend/maxwell/translate_program.h index 02ede8c9c..497afe7cb 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.h +++ b/src/shader_recompiler/frontend/maxwell/translate_program.h @@ -25,4 +25,13 @@ namespace Shader::Maxwell { void ConvertLegacyToGeneric(IR::Program& program, const RuntimeInfo& runtime_info); +// Maxwell v1 and older Nvidia cards don't support setting gl_Layer from non-geometry stages. +// This creates a workaround by setting the layer as a generic output and creating a +// passthrough geometry shader that reads the generic and sets the layer. +[[nodiscard]] IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool, + ObjectPool<IR::Block>& block_pool, + const HostTranslateInfo& host_info, + IR::Program& source_program, + Shader::OutputTopology output_topology); + } // namespace Shader::Maxwell diff --git a/src/shader_recompiler/host_translate_info.h b/src/shader_recompiler/host_translate_info.h index cc1500690..d5d279554 100644 --- a/src/shader_recompiler/host_translate_info.h +++ b/src/shader_recompiler/host_translate_info.h @@ -13,7 +13,8 @@ struct HostTranslateInfo { bool support_float16{}; ///< True when the device supports 16-bit floats bool support_int64{}; ///< True when the device supports 64-bit integers bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered - bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers + bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers + bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS }; } // namespace Shader diff --git a/src/shader_recompiler/ir_opt/layer_pass.cpp b/src/shader_recompiler/ir_opt/layer_pass.cpp new file mode 100644 index 000000000..4574f7cf2 --- /dev/null +++ b/src/shader_recompiler/ir_opt/layer_pass.cpp @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <algorithm> +#include <bit> +#include <optional> + +#include <boost/container/small_vector.hpp> + +#include "shader_recompiler/environment.h" +#include "shader_recompiler/frontend/ir/basic_block.h" +#include "shader_recompiler/frontend/ir/breadth_first_search.h" +#include "shader_recompiler/frontend/ir/ir_emitter.h" +#include "shader_recompiler/host_translate_info.h" +#include "shader_recompiler/ir_opt/passes.h" +#include "shader_recompiler/shader_info.h" + +namespace Shader::Optimization { + +static IR::Attribute EmulatedLayerAttribute(VaryingState& stores) { + for (u32 i = 0; i < 32; i++) { + if (!stores.Generic(i)) { + return IR::Attribute::Generic0X + (i * 4); + } + } + return IR::Attribute::Layer; +} + +static bool PermittedProgramStage(Stage stage) { + switch (stage) { + case Stage::VertexA: + case Stage::VertexB: + case Stage::TessellationControl: + case Stage::TessellationEval: + return true; + default: + return false; + } +} + +void LayerPass(IR::Program& program, const HostTranslateInfo& host_info) { + if (host_info.support_viewport_index_layer || !PermittedProgramStage(program.stage)) { + return; + } + + const auto end{program.post_order_blocks.end()}; + const auto layer_attribute = EmulatedLayerAttribute(program.info.stores); + bool requires_layer_emulation = false; + + for (auto block = program.post_order_blocks.begin(); block != end; ++block) { + for (IR::Inst& inst : (*block)->Instructions()) { + if (inst.GetOpcode() == IR::Opcode::SetAttribute && + inst.Arg(0).Attribute() == IR::Attribute::Layer) { + requires_layer_emulation = true; + inst.SetArg(0, IR::Value{layer_attribute}); + } + } + } + + if (requires_layer_emulation) { + program.info.requires_layer_emulation = true; + program.info.emulated_layer = layer_attribute; + program.info.stores.Set(IR::Attribute::Layer, false); + program.info.stores.Set(layer_attribute, true); + } +} + +} // namespace Shader::Optimization diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h index 586a0668f..11bfe801a 100644 --- a/src/shader_recompiler/ir_opt/passes.h +++ b/src/shader_recompiler/ir_opt/passes.h @@ -23,6 +23,7 @@ void RescalingPass(IR::Program& program); void SsaRewritePass(IR::Program& program); void PositionPass(Environment& env, IR::Program& program); void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info); +void LayerPass(IR::Program& program, const HostTranslateInfo& host_info); void VerificationPass(const IR::Program& program); // Dual Vertex diff --git a/src/shader_recompiler/precompiled_headers.h b/src/shader_recompiler/precompiled_headers.h new file mode 100644 index 000000000..5dd6b7eca --- /dev/null +++ b/src/shader_recompiler/precompiled_headers.h @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_precompiled_headers.h" +#include "frontend/maxwell/translate/impl/impl.h" diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h index ee6252bb5..d9c6e92db 100644 --- a/src/shader_recompiler/shader_info.h +++ b/src/shader_recompiler/shader_info.h @@ -204,6 +204,9 @@ struct Info { u32 nvn_buffer_base{}; std::bitset<16> nvn_buffer_used{}; + bool requires_layer_emulation{}; + IR::Attribute emulated_layer{}; + boost::container::static_vector<ConstantBufferDescriptor, MAX_CBUFS> constant_buffer_descriptors; boost::container::static_vector<StorageBufferDescriptor, MAX_SSBOS> storage_buffers_descriptors; diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 43ad2c7ff..348d1edf4 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -11,6 +11,7 @@ add_executable(tests common/unique_function.cpp core/core_timing.cpp core/internal_network/network.cpp + precompiled_headers.h tests.cpp video_core/buffer_base.cpp input_common/calibration_configuration_job.cpp @@ -22,3 +23,7 @@ target_link_libraries(tests PRIVATE common core input_common) target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} Catch2::Catch2 Threads::Threads) add_test(NAME tests COMMAND tests) + +if (YUZU_USE_PRECOMPILED_HEADERS) + target_precompile_headers(tests PRIVATE precompiled_headers.h) +endif() diff --git a/src/tests/precompiled_headers.h b/src/tests/precompiled_headers.h new file mode 100644 index 000000000..aabae730b --- /dev/null +++ b/src/tests/precompiled_headers.h @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_precompiled_headers.h" diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index b03a30992..5096d935e 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -84,6 +84,7 @@ add_library(video_core STATIC gpu_thread.h memory_manager.cpp memory_manager.h + precompiled_headers.h pte_kind.h query_cache.h rasterizer_accelerated.cpp @@ -91,6 +92,10 @@ add_library(video_core STATIC rasterizer_interface.h renderer_base.cpp renderer_base.h + renderer_null/null_rasterizer.cpp + renderer_null/null_rasterizer.h + renderer_null/renderer_null.cpp + renderer_null/renderer_null.h renderer_opengl/gl_buffer_cache.cpp renderer_opengl/gl_buffer_cache.h renderer_opengl/gl_compute_pipeline.cpp @@ -259,8 +264,7 @@ target_link_options(video_core PRIVATE ${FFmpeg_LDFLAGS}) add_dependencies(video_core host_shaders) target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE}) -target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include) -target_link_libraries(video_core PRIVATE sirit) +target_link_libraries(video_core PRIVATE sirit Vulkan::Headers) if (ENABLE_NSIGHT_AFTERMATH) if (NOT DEFINED ENV{NSIGHT_AFTERMATH_SDK}) @@ -279,9 +283,15 @@ if (MSVC) /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data ) else() - target_compile_options(video_core PRIVATE - -Werror=conversion + if (APPLE) + # error: declaration shadows a typedef in 'interval_base_set<SubType, DomainT, Compare, Interval, Alloc>' + # error: implicit conversion loses integer precision: 'int' to 'boost::icl::bound_type' (aka 'unsigned char') + target_compile_options(video_core PRIVATE -Wno-shadow -Wno-unused-local-typedef) + else() + target_compile_options(video_core PRIVATE -Werror=conversion) + endif() + target_compile_options(video_core PRIVATE -Wno-sign-conversion ) @@ -294,9 +304,13 @@ if (ARCHITECTURE_x86_64) macro/macro_jit_x64.cpp macro/macro_jit_x64.h ) - target_link_libraries(video_core PUBLIC xbyak) + target_link_libraries(video_core PUBLIC xbyak::xbyak) endif() if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) - target_link_libraries(video_core PRIVATE dynarmic) + target_link_libraries(video_core PRIVATE dynarmic::dynarmic) +endif() + +if (YUZU_USE_PRECOMPILED_HEADERS) + target_precompile_headers(video_core PRIVATE precompiled_headers.h) endif() diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h index f9a6472cf..92d77eef2 100644 --- a/src/video_core/buffer_cache/buffer_base.h +++ b/src/video_core/buffer_cache/buffer_base.h @@ -535,7 +535,7 @@ private: const u64* const state_words = Array<type>(); const u64 num_query_words = size / BYTES_PER_WORD + 1; const u64 word_begin = offset / BYTES_PER_WORD; - const u64 word_end = std::min(word_begin + num_query_words, NumWords()); + const u64 word_end = std::min<u64>(word_begin + num_query_words, NumWords()); const u64 page_limit = Common::DivCeil(offset + size, BYTES_PER_PAGE); u64 page_index = (offset / BYTES_PER_PAGE) % PAGES_PER_WORD; for (u64 word_index = word_begin; word_index < word_end; ++word_index, page_index = 0) { diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 5d3a8293b..6881b34c4 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -19,6 +19,7 @@ #include "common/literals.h" #include "common/lru_cache.h" #include "common/microprofile.h" +#include "common/polyfill_ranges.h" #include "common/settings.h" #include "core/memory.h" #include "video_core/buffer_cache/buffer_base.h" diff --git a/src/video_core/control/channel_state_cache.h b/src/video_core/control/channel_state_cache.h index 584a0c26c..cdaf4f8d5 100644 --- a/src/video_core/control/channel_state_cache.h +++ b/src/video_core/control/channel_state_cache.h @@ -35,8 +35,6 @@ public: explicit ChannelInfo(Tegra::Control::ChannelState& state); ChannelInfo(const ChannelInfo& state) = delete; ChannelInfo& operator=(const ChannelInfo&) = delete; - ChannelInfo(ChannelInfo&& other) = default; - ChannelInfo& operator=(ChannelInfo&& other) = default; Tegra::Engines::Maxwell3D& maxwell3d; Tegra::Engines::KeplerCompute& kepler_compute; diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 55462752c..fb9b9b94e 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -126,7 +126,6 @@ void Maxwell3D::InitializeRegisterDefaults() { draw_command[MAXWELL3D_REG_INDEX(draw_inline_index)] = true; draw_command[MAXWELL3D_REG_INDEX(inline_index_2x16.even)] = true; draw_command[MAXWELL3D_REG_INDEX(inline_index_4x8.index0)] = true; - draw_command[MAXWELL3D_REG_INDEX(draw.instance_id)] = true; } void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) { @@ -218,16 +217,19 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume regs.index_buffer.count = regs.index_buffer32_first.count; regs.index_buffer.first = regs.index_buffer32_first.first; dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; + draw_indexed = true; return ProcessDraw(); case MAXWELL3D_REG_INDEX(index_buffer16_first): regs.index_buffer.count = regs.index_buffer16_first.count; regs.index_buffer.first = regs.index_buffer16_first.first; dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; + draw_indexed = true; return ProcessDraw(); case MAXWELL3D_REG_INDEX(index_buffer8_first): regs.index_buffer.count = regs.index_buffer8_first.count; regs.index_buffer.first = regs.index_buffer8_first.first; dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; + draw_indexed = true; return ProcessDraw(); case MAXWELL3D_REG_INDEX(topology_override): use_topology_override = true; @@ -300,21 +302,33 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { draw_mode = DrawMode::InlineIndex; }; switch (method) { + case MAXWELL3D_REG_INDEX(draw.begin): { + draw_mode = + (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) || + (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Unchanged) + ? DrawMode::Instance + : DrawMode::General; + break; + } case MAXWELL3D_REG_INDEX(draw.end): switch (draw_mode) { case DrawMode::General: - ProcessDraw(1); + ProcessDraw(); break; case DrawMode::InlineIndex: regs.index_buffer.count = static_cast<u32>(inline_index_draw_indexes.size() / 4); regs.index_buffer.format = Regs::IndexFormat::UnsignedInt; - ProcessDraw(1); + draw_indexed = true; + ProcessDraw(); inline_index_draw_indexes.clear(); break; case DrawMode::Instance: break; } break; + case MAXWELL3D_REG_INDEX(index_buffer.count): + draw_indexed = true; + break; case MAXWELL3D_REG_INDEX(draw_inline_index): update_inline_index(method_argument); break; @@ -328,13 +342,6 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { update_inline_index(regs.inline_index_4x8.index2); update_inline_index(regs.inline_index_4x8.index3); break; - case MAXWELL3D_REG_INDEX(draw.instance_id): - draw_mode = - (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) || - (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Unchanged) - ? DrawMode::Instance - : DrawMode::General; - break; } } else { ProcessDeferredDraw(); @@ -486,41 +493,51 @@ void Maxwell3D::ProcessQueryGet() { void Maxwell3D::ProcessQueryCondition() { const GPUVAddr condition_address{regs.render_enable.Address()}; - switch (regs.render_enable.mode) { - case Regs::RenderEnable::Mode::True: { + switch (regs.render_enable_override) { + case Regs::RenderEnable::Override::AlwaysRender: execute_on = true; break; - } - case Regs::RenderEnable::Mode::False: { + case Regs::RenderEnable::Override::NeverRender: execute_on = false; break; - } - case Regs::RenderEnable::Mode::Conditional: { - Regs::ReportSemaphore::Compare cmp; - memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp)); - execute_on = cmp.initial_sequence != 0U && cmp.initial_mode != 0U; - break; - } - case Regs::RenderEnable::Mode::IfEqual: { - Regs::ReportSemaphore::Compare cmp; - memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp)); - execute_on = - cmp.initial_sequence == cmp.current_sequence && cmp.initial_mode == cmp.current_mode; - break; - } - case Regs::RenderEnable::Mode::IfNotEqual: { - Regs::ReportSemaphore::Compare cmp; - memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp)); - execute_on = - cmp.initial_sequence != cmp.current_sequence || cmp.initial_mode != cmp.current_mode; - break; - } - default: { - UNIMPLEMENTED_MSG("Uninplemented Condition Mode!"); - execute_on = true; + case Regs::RenderEnable::Override::UseRenderEnable: + switch (regs.render_enable.mode) { + case Regs::RenderEnable::Mode::True: { + execute_on = true; + break; + } + case Regs::RenderEnable::Mode::False: { + execute_on = false; + break; + } + case Regs::RenderEnable::Mode::Conditional: { + Regs::ReportSemaphore::Compare cmp; + memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp)); + execute_on = cmp.initial_sequence != 0U && cmp.initial_mode != 0U; + break; + } + case Regs::RenderEnable::Mode::IfEqual: { + Regs::ReportSemaphore::Compare cmp; + memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp)); + execute_on = cmp.initial_sequence == cmp.current_sequence && + cmp.initial_mode == cmp.current_mode; + break; + } + case Regs::RenderEnable::Mode::IfNotEqual: { + Regs::ReportSemaphore::Compare cmp; + memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp)); + execute_on = cmp.initial_sequence != cmp.current_sequence || + cmp.initial_mode != cmp.current_mode; + break; + } + default: { + UNIMPLEMENTED_MSG("Uninplemented Condition Mode!"); + execute_on = true; + break; + } + } break; } - } } void Maxwell3D::ProcessCounterReset() { @@ -624,27 +641,16 @@ void Maxwell3D::ProcessClearBuffers(u32 layer_count) { void Maxwell3D::ProcessDraw(u32 instance_count) { LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(), - regs.vertex_buffer.count); - - ASSERT_MSG(!(regs.index_buffer.count && regs.vertex_buffer.count), "Both indexed and direct?"); - - // Both instance configuration registers can not be set at the same time. - ASSERT_MSG(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::First || - regs.draw.instance_id != Maxwell3D::Regs::Draw::InstanceId::Unchanged, - "Illegal combination of instancing parameters"); + draw_indexed ? regs.index_buffer.count : regs.vertex_buffer.count); ProcessTopologyOverride(); - const bool is_indexed = regs.index_buffer.count && !regs.vertex_buffer.count; if (ShouldExecute()) { - rasterizer->Draw(is_indexed, instance_count); + rasterizer->Draw(draw_indexed, instance_count); } - if (is_indexed) { - regs.index_buffer.count = 0; - } else { - regs.vertex_buffer.count = 0; - } + draw_indexed = false; + deferred_draw_method.clear(); } void Maxwell3D::ProcessDeferredDraw() { @@ -667,8 +673,6 @@ void Maxwell3D::ProcessDeferredDraw() { ASSERT_MSG(!(vertex_buffer_count && index_buffer_count), "Instance both indexed and direct?"); ProcessDraw(instance_count); - - deferred_draw_method.clear(); } } // namespace Tegra::Engines diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index deba292a5..a541cd95f 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -3159,6 +3159,7 @@ private: std::vector<u32> deferred_draw_method; enum class DrawMode : u32 { General = 0, Instance, InlineIndex }; DrawMode draw_mode{DrawMode::General}; + bool draw_indexed{}; }; #define ASSERT_REG_POSITION(field_name, position) \ diff --git a/src/video_core/engines/sw_blitter/converter.cpp b/src/video_core/engines/sw_blitter/converter.cpp index cd46dfd4f..2419b5632 100644 --- a/src/video_core/engines/sw_blitter/converter.cpp +++ b/src/video_core/engines/sw_blitter/converter.cpp @@ -2,12 +2,12 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include <array> -#include <bit> #include <cmath> #include <span> #include <unordered_map> #include "common/assert.h" +#include "common/bit_cast.h" #include "video_core/engines/sw_blitter/converter.h" #include "video_core/surface.h" #include "video_core/textures/decoders.h" @@ -693,21 +693,21 @@ private: return shifted_value >> shift_amount; }; const auto force_to_fp16 = [](f32 base_value) { - u32 tmp = std::bit_cast<u32>(base_value); + u32 tmp = Common::BitCast<u32>(base_value); constexpr size_t fp32_mantissa_bits = 23; constexpr size_t fp16_mantissa_bits = 10; constexpr size_t mantissa_mask = ~((1ULL << (fp32_mantissa_bits - fp16_mantissa_bits)) - 1ULL); tmp = tmp & static_cast<u32>(mantissa_mask); // TODO: force the exponent within the range of half float. Not needed in UNORM / SNORM - return std::bit_cast<f32>(tmp); + return Common::BitCast<f32>(tmp); }; const auto from_fp_n = [&sign_extend](u32 base_value, size_t bits, size_t mantissa) { constexpr size_t fp32_mantissa_bits = 23; size_t shift_towards = fp32_mantissa_bits - mantissa; const u32 new_value = static_cast<u32>(sign_extend(base_value, bits) << shift_towards) & (~(1U << 31)); - return std::bit_cast<f32>(new_value); + return Common::BitCast<f32>(new_value); }; const auto calculate_snorm = [&]() { return static_cast<f32>( @@ -737,13 +737,13 @@ private: out_component = force_to_fp16(out_component); } else if constexpr (component_types[which_component] == ComponentType::FLOAT) { if constexpr (component_sizes[which_component] == 32) { - out_component = std::bit_cast<f32>(value); + out_component = Common::BitCast<f32>(value); } else if constexpr (component_sizes[which_component] == 16) { static constexpr u32 sign_mask = 0x8000; static constexpr u32 mantissa_mask = 0x8000; - out_component = std::bit_cast<f32>(((value & sign_mask) << 16) | - (((value & 0x7c00) + 0x1C000) << 13) | - ((value & mantissa_mask) << 13)); + out_component = Common::BitCast<f32>(((value & sign_mask) << 16) | + (((value & 0x7c00) + 0x1C000) << 13) | + ((value & mantissa_mask) << 13)); } else { out_component = from_fp_n(value, component_sizes[which_component], component_sizes[which_component] - 5); @@ -771,7 +771,7 @@ private: }; const auto to_fp_n = [](f32 base_value, size_t bits, size_t mantissa) { constexpr size_t fp32_mantissa_bits = 23; - u32 tmp_value = std::bit_cast<u32>(std::max(base_value, 0.0f)); + u32 tmp_value = Common::BitCast<u32>(std::max(base_value, 0.0f)); size_t shift_towards = fp32_mantissa_bits - mantissa; return tmp_value >> shift_towards; }; @@ -799,13 +799,13 @@ private: insert_to_word(tmp_word); } else if constexpr (component_types[which_component] == ComponentType::FLOAT) { if constexpr (component_sizes[which_component] == 32) { - u32 tmp_word = std::bit_cast<u32>(in_component); + u32 tmp_word = Common::BitCast<u32>(in_component); insert_to_word(tmp_word); } else if constexpr (component_sizes[which_component] == 16) { static constexpr u32 sign_mask = 0x8000; static constexpr u32 mantissa_mask = 0x03ff; static constexpr u32 exponent_mask = 0x7c00; - const u32 tmp_word = std::bit_cast<u32>(in_component); + const u32 tmp_word = Common::BitCast<u32>(in_component); const u32 half = ((tmp_word >> 16) & sign_mask) | ((((tmp_word & 0x7f800000) - 0x38000000) >> 13) & exponent_mask) | ((tmp_word >> 13) & mantissa_mask); diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index 1bd477011..164a5252a 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp @@ -125,7 +125,7 @@ u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) { state.queue.Push(CommandDataContainer(std::move(command_data), fence, block)); if (block) { - state.cv.wait(lk, thread.get_stop_token(), [this, fence] { + Common::CondvarWait(state.cv, lk, thread.get_stop_token(), [this, fence] { return fence <= state.signaled_fence.load(std::memory_order_relaxed); }); } diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h index 64628d3e3..c71a419c7 100644 --- a/src/video_core/gpu_thread.h +++ b/src/video_core/gpu_thread.h @@ -10,6 +10,7 @@ #include <thread> #include <variant> +#include "common/polyfill_thread.h" #include "common/threadsafe_queue.h" #include "video_core/framebuffer_config.h" diff --git a/src/video_core/precompiled_headers.h b/src/video_core/precompiled_headers.h new file mode 100644 index 000000000..aabae730b --- /dev/null +++ b/src/video_core/precompiled_headers.h @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_precompiled_headers.h" diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index cfd872a40..b6907463c 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h @@ -6,8 +6,8 @@ #include <functional> #include <optional> #include <span> -#include <stop_token> #include "common/common_types.h" +#include "common/polyfill_thread.h" #include "video_core/engines/fermi_2d.h" #include "video_core/gpu.h" diff --git a/src/video_core/renderer_null/null_rasterizer.cpp b/src/video_core/renderer_null/null_rasterizer.cpp new file mode 100644 index 000000000..9734d84bc --- /dev/null +++ b/src/video_core/renderer_null/null_rasterizer.cpp @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "video_core/host1x/host1x.h" +#include "video_core/memory_manager.h" +#include "video_core/renderer_null/null_rasterizer.h" + +namespace Null { + +AccelerateDMA::AccelerateDMA() = default; + +bool AccelerateDMA::BufferCopy(GPUVAddr start_address, GPUVAddr end_address, u64 amount) { + return true; +} +bool AccelerateDMA::BufferClear(GPUVAddr src_address, u64 amount, u32 value) { + return true; +} + +RasterizerNull::RasterizerNull(Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu) + : RasterizerAccelerated(cpu_memory_), m_gpu{gpu} {} +RasterizerNull::~RasterizerNull() = default; + +void RasterizerNull::Draw(bool is_indexed, u32 instance_count) {} +void RasterizerNull::Clear(u32 layer_count) {} +void RasterizerNull::DispatchCompute() {} +void RasterizerNull::ResetCounter(VideoCore::QueryType type) {} +void RasterizerNull::Query(GPUVAddr gpu_addr, VideoCore::QueryType type, + std::optional<u64> timestamp) { + if (!gpu_memory) { + return; + } + + gpu_memory->Write(gpu_addr, u64{0}); + if (timestamp) { + gpu_memory->Write(gpu_addr + 8, *timestamp); + } +} +void RasterizerNull::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, + u32 size) {} +void RasterizerNull::DisableGraphicsUniformBuffer(size_t stage, u32 index) {} +void RasterizerNull::FlushAll() {} +void RasterizerNull::FlushRegion(VAddr addr, u64 size) {} +bool RasterizerNull::MustFlushRegion(VAddr addr, u64 size) { + return false; +} +void RasterizerNull::InvalidateRegion(VAddr addr, u64 size) {} +void RasterizerNull::OnCPUWrite(VAddr addr, u64 size) {} +void RasterizerNull::InvalidateGPUCache() {} +void RasterizerNull::UnmapMemory(VAddr addr, u64 size) {} +void RasterizerNull::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) {} +void RasterizerNull::SignalFence(std::function<void()>&& func) { + func(); +} +void RasterizerNull::SyncOperation(std::function<void()>&& func) { + func(); +} +void RasterizerNull::SignalSyncPoint(u32 value) { + auto& syncpoint_manager = m_gpu.Host1x().GetSyncpointManager(); + syncpoint_manager.IncrementGuest(value); + syncpoint_manager.IncrementHost(value); +} +void RasterizerNull::SignalReference() {} +void RasterizerNull::ReleaseFences() {} +void RasterizerNull::FlushAndInvalidateRegion(VAddr addr, u64 size) {} +void RasterizerNull::WaitForIdle() {} +void RasterizerNull::FragmentBarrier() {} +void RasterizerNull::TiledCacheBarrier() {} +void RasterizerNull::FlushCommands() {} +void RasterizerNull::TickFrame() {} +Tegra::Engines::AccelerateDMAInterface& RasterizerNull::AccessAccelerateDMA() { + return m_accelerate_dma; +} +bool RasterizerNull::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, + const Tegra::Engines::Fermi2D::Surface& dst, + const Tegra::Engines::Fermi2D::Config& copy_config) { + return true; +} +void RasterizerNull::AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, + std::span<const u8> memory) {} +bool RasterizerNull::AccelerateDisplay(const Tegra::FramebufferConfig& config, + VAddr framebuffer_addr, u32 pixel_stride) { + return true; +} +void RasterizerNull::LoadDiskResources(u64 title_id, std::stop_token stop_loading, + const VideoCore::DiskResourceLoadCallback& callback) {} +void RasterizerNull::InitializeChannel(Tegra::Control::ChannelState& channel) {} +void RasterizerNull::BindChannel(Tegra::Control::ChannelState& channel) {} +void RasterizerNull::ReleaseChannel(s32 channel_id) {} + +} // namespace Null diff --git a/src/video_core/renderer_null/null_rasterizer.h b/src/video_core/renderer_null/null_rasterizer.h new file mode 100644 index 000000000..ecf77ba42 --- /dev/null +++ b/src/video_core/renderer_null/null_rasterizer.h @@ -0,0 +1,78 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "video_core/control/channel_state_cache.h" +#include "video_core/engines/maxwell_dma.h" +#include "video_core/rasterizer_accelerated.h" +#include "video_core/rasterizer_interface.h" + +namespace Core { +class System; +} + +namespace Null { + +class RasterizerNull; + +class AccelerateDMA : public Tegra::Engines::AccelerateDMAInterface { +public: + explicit AccelerateDMA(); + bool BufferCopy(GPUVAddr start_address, GPUVAddr end_address, u64 amount) override; + bool BufferClear(GPUVAddr src_address, u64 amount, u32 value) override; +}; + +class RasterizerNull final : public VideoCore::RasterizerAccelerated, + protected VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo> { +public: + explicit RasterizerNull(Core::Memory::Memory& cpu_memory, Tegra::GPU& gpu); + ~RasterizerNull() override; + + void Draw(bool is_indexed, u32 instance_count) override; + void Clear(u32 layer_count) override; + void DispatchCompute() override; + void ResetCounter(VideoCore::QueryType type) override; + void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override; + void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override; + void DisableGraphicsUniformBuffer(size_t stage, u32 index) override; + void FlushAll() override; + void FlushRegion(VAddr addr, u64 size) override; + bool MustFlushRegion(VAddr addr, u64 size) override; + void InvalidateRegion(VAddr addr, u64 size) override; + void OnCPUWrite(VAddr addr, u64 size) override; + void InvalidateGPUCache() override; + void UnmapMemory(VAddr addr, u64 size) override; + void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override; + void SignalFence(std::function<void()>&& func) override; + void SyncOperation(std::function<void()>&& func) override; + void SignalSyncPoint(u32 value) override; + void SignalReference() override; + void ReleaseFences() override; + void FlushAndInvalidateRegion(VAddr addr, u64 size) override; + void WaitForIdle() override; + void FragmentBarrier() override; + void TiledCacheBarrier() override; + void FlushCommands() override; + void TickFrame() override; + bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, + const Tegra::Engines::Fermi2D::Surface& dst, + const Tegra::Engines::Fermi2D::Config& copy_config) override; + Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; + void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, + std::span<const u8> memory) override; + bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, + u32 pixel_stride) override; + void LoadDiskResources(u64 title_id, std::stop_token stop_loading, + const VideoCore::DiskResourceLoadCallback& callback) override; + void InitializeChannel(Tegra::Control::ChannelState& channel) override; + void BindChannel(Tegra::Control::ChannelState& channel) override; + void ReleaseChannel(s32 channel_id) override; + +private: + Tegra::GPU& m_gpu; + AccelerateDMA m_accelerate_dma; +}; + +} // namespace Null diff --git a/src/video_core/renderer_null/renderer_null.cpp b/src/video_core/renderer_null/renderer_null.cpp new file mode 100644 index 000000000..e2a189b63 --- /dev/null +++ b/src/video_core/renderer_null/renderer_null.cpp @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "video_core/renderer_null/renderer_null.h" + +namespace Null { + +RendererNull::RendererNull(Core::Frontend::EmuWindow& emu_window, Core::Memory::Memory& cpu_memory, + Tegra::GPU& gpu, + std::unique_ptr<Core::Frontend::GraphicsContext> context_) + : RendererBase(emu_window, std::move(context_)), m_gpu(gpu), m_rasterizer(cpu_memory, gpu) {} + +RendererNull::~RendererNull() = default; + +void RendererNull::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { + if (!framebuffer) { + return; + } + + m_gpu.RendererFrameEndNotify(); + render_window.OnFrameDisplayed(); +} + +} // namespace Null diff --git a/src/video_core/renderer_null/renderer_null.h b/src/video_core/renderer_null/renderer_null.h new file mode 100644 index 000000000..967ff5645 --- /dev/null +++ b/src/video_core/renderer_null/renderer_null.h @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <memory> +#include <string> + +#include "video_core/renderer_base.h" +#include "video_core/renderer_null/null_rasterizer.h" + +namespace Null { + +class RendererNull final : public VideoCore::RendererBase { +public: + explicit RendererNull(Core::Frontend::EmuWindow& emu_window, Core::Memory::Memory& cpu_memory, + Tegra::GPU& gpu, + std::unique_ptr<Core::Frontend::GraphicsContext> context); + ~RendererNull() override; + + void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; + + VideoCore::RasterizerInterface* ReadRasterizer() override { + return &m_rasterizer; + } + + [[nodiscard]] std::string GetDeviceVendor() const override { + return "NULL"; + } + +private: + Tegra::GPU& m_gpu; + RasterizerNull m_rasterizer; +}; + +} // namespace Null diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index 1663e277d..e2e3dac34 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp @@ -14,6 +14,7 @@ #include "common/literals.h" #include "common/logging/log.h" +#include "common/polyfill_ranges.h" #include "common/settings.h" #include "shader_recompiler/stage.h" #include "video_core/renderer_opengl/gl_device.h" diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 3fe04a115..a38060100 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -39,6 +39,7 @@ using Shader::Backend::GLASM::EmitGLASM; using Shader::Backend::GLSL::EmitGLSL; using Shader::Backend::SPIRV::EmitSPIRV; using Shader::Maxwell::ConvertLegacyToGeneric; +using Shader::Maxwell::GenerateGeometryPassthrough; using Shader::Maxwell::MergeDualVertexPrograms; using Shader::Maxwell::TranslateProgram; using VideoCommon::ComputeEnvironment; @@ -56,6 +57,17 @@ auto MakeSpan(Container& container) { return std::span(container.data(), container.size()); } +Shader::OutputTopology MaxwellToOutputTopology(Maxwell::PrimitiveTopology topology) { + switch (topology) { + case Maxwell::PrimitiveTopology::Points: + return Shader::OutputTopology::PointList; + case Maxwell::PrimitiveTopology::LineStrip: + return Shader::OutputTopology::LineStrip; + default: + return Shader::OutputTopology::TriangleStrip; + } +} + Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key, const Shader::IR::Program& program, const Shader::IR::Program* previous_program, @@ -220,6 +232,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo .support_int64 = device.HasShaderInt64(), .needs_demote_reorder = device.IsAmd(), .support_snorm_render_buffer = false, + .support_viewport_index_layer = device.HasVertexViewportLayer(), } { if (use_asynchronous_shaders) { workers = CreateWorkers(); @@ -314,9 +327,7 @@ GraphicsPipeline* ShaderCache::CurrentGraphicsPipeline() { const auto& regs{maxwell3d->regs}; graphics_key.raw = 0; graphics_key.early_z.Assign(regs.mandated_early_z != 0 ? 1 : 0); - graphics_key.gs_input_topology.Assign(graphics_key.unique_hashes[4] != 0 - ? regs.draw.topology.Value() - : Maxwell::PrimitiveTopology{}); + graphics_key.gs_input_topology.Assign(regs.draw.topology.Value()); graphics_key.tessellation_primitive.Assign(regs.tessellation.params.domain_type.Value()); graphics_key.tessellation_spacing.Assign(regs.tessellation.params.spacing.Value()); graphics_key.tessellation_clockwise.Assign( @@ -415,7 +426,19 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline( std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs; const bool uses_vertex_a{key.unique_hashes[0] != 0}; const bool uses_vertex_b{key.unique_hashes[1] != 0}; + + // Layer passthrough generation for devices without GL_ARB_shader_viewport_layer_array + Shader::IR::Program* layer_source_program{}; + for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { + const bool is_emulated_stage = layer_source_program != nullptr && + index == static_cast<u32>(Maxwell::ShaderType::Geometry); + if (key.unique_hashes[index] == 0 && is_emulated_stage) { + auto topology = MaxwellToOutputTopology(key.gs_input_topology); + programs[index] = GenerateGeometryPassthrough(pools.inst, pools.block, host_info, + *layer_source_program, topology); + continue; + } if (key.unique_hashes[index] == 0) { continue; } @@ -443,6 +466,10 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline( Shader::NumDescriptors(program_vb.info.storage_buffers_descriptors); programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); } + + if (programs[index].info.requires_layer_emulation) { + layer_source_program = &programs[index]; + } } const u32 glasm_storage_buffer_limit{device.GetMaxGLASMStorageBufferBlocks()}; const bool glasm_use_storage_buffers{total_storage_buffers <= glasm_storage_buffer_limit}; @@ -456,7 +483,9 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline( const bool use_glasm{device.UseAssemblyShaders()}; const size_t first_index = uses_vertex_a && uses_vertex_b ? 1 : 0; for (size_t index = first_index; index < Maxwell::MaxShaderProgram; ++index) { - if (key.unique_hashes[index] == 0) { + const bool is_emulated_stage = layer_source_program != nullptr && + index == static_cast<u32>(Maxwell::ShaderType::Geometry); + if (key.unique_hashes[index] == 0 && !is_emulated_stage) { continue; } UNIMPLEMENTED_IF(index == 0); diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index 89f181fe3..53ffea904 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h @@ -4,7 +4,6 @@ #pragma once #include <filesystem> -#include <stop_token> #include <unordered_map> #include "common/common_types.h" diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index 98cc26679..5864e772b 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -7,6 +7,7 @@ #include "common/bit_cast.h" #include "common/cityhash.h" #include "common/common_types.h" +#include "common/polyfill_ranges.h" #include "video_core/renderer_vulkan/fixed_pipeline_state.h" #include "video_core/renderer_vulkan/vk_state_tracker.h" @@ -92,6 +93,8 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, provoking_vertex_last.Assign(regs.provoking_vertex == Maxwell::ProvokingVertex::Last ? 1 : 0); conservative_raster_enable.Assign(regs.conservative_raster_enable != 0 ? 1 : 0); smooth_lines.Assign(regs.line_anti_alias_enable != 0 ? 1 : 0); + alpha_to_coverage_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_coverage != 0 ? 1 : 0); + alpha_to_one_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_one != 0 ? 1 : 0); for (size_t i = 0; i < regs.rt.size(); ++i) { color_formats[i] = static_cast<u8>(regs.rt[i].format); diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index 1afdef329..ab79fb8f3 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h @@ -195,6 +195,8 @@ struct FixedPipelineState { BitField<12, 1, u32> provoking_vertex_last; BitField<13, 1, u32> conservative_raster_enable; BitField<14, 1, u32> smooth_lines; + BitField<15, 1, u32> alpha_to_coverage_enabled; + BitField<16, 1, u32> alpha_to_one_enabled; }; std::array<u8, Maxwell::NumRenderTargets> color_formats; diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index 430a84272..3e03c5cd6 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp @@ -58,7 +58,7 @@ VkSamplerAddressMode WrapMode(const Device& device, Tegra::Texture::WrapMode wra case Tegra::Texture::WrapMode::Border: return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; case Tegra::Texture::WrapMode::Clamp: - if (device.GetDriverID() == VK_DRIVER_ID_NVIDIA_PROPRIETARY_KHR) { + if (device.GetDriverID() == VK_DRIVER_ID_NVIDIA_PROPRIETARY) { // Nvidia's Vulkan driver defaults to GL_CLAMP on invalid enumerations, we can hack this // by sending an invalid enumeration. return static_cast<VkSamplerAddressMode>(0xcafe); diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h index b7843e995..28b893e25 100644 --- a/src/video_core/renderer_vulkan/pipeline_helper.h +++ b/src/video_core/renderer_vulkan/pipeline_helper.h @@ -44,17 +44,17 @@ public: }); } - vk::DescriptorUpdateTemplateKHR CreateTemplate(VkDescriptorSetLayout descriptor_set_layout, - VkPipelineLayout pipeline_layout, - bool use_push_descriptor) const { + vk::DescriptorUpdateTemplate CreateTemplate(VkDescriptorSetLayout descriptor_set_layout, + VkPipelineLayout pipeline_layout, + bool use_push_descriptor) const { if (entries.empty()) { return nullptr; } const VkDescriptorUpdateTemplateType type = use_push_descriptor ? VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR - : VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR; - return device->GetLogical().CreateDescriptorUpdateTemplateKHR({ - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR, + : VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET; + return device->GetLogical().CreateDescriptorUpdateTemplate({ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .descriptorUpdateEntryCount = static_cast<u32>(entries.size()), @@ -129,7 +129,7 @@ private: const Device* device{}; bool is_compute{}; boost::container::small_vector<VkDescriptorSetLayoutBinding, 32> bindings; - boost::container::small_vector<VkDescriptorUpdateTemplateEntryKHR, 32> entries; + boost::container::small_vector<VkDescriptorUpdateTemplateEntry, 32> entries; u32 binding{}; u32 num_descriptors{}; size_t offset{}; diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index c2a95200b..18be54729 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -45,14 +45,14 @@ std::string GetDriverVersion(const Device& device) { // https://github.com/SaschaWillems/vulkan.gpuinfo.org/blob/5dddea46ea1120b0df14eef8f15ff8e318e35462/functions.php#L308-L314 const u32 version = device.GetDriverVersion(); - if (device.GetDriverID() == VK_DRIVER_ID_NVIDIA_PROPRIETARY_KHR) { + if (device.GetDriverID() == VK_DRIVER_ID_NVIDIA_PROPRIETARY) { const u32 major = (version >> 22) & 0x3ff; const u32 minor = (version >> 14) & 0x0ff; const u32 secondary = (version >> 6) & 0x0ff; const u32 tertiary = version & 0x003f; return fmt::format("{}.{}.{}.{}", major, minor, secondary, tertiary); } - if (device.GetDriverID() == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS_KHR) { + if (device.GetDriverID() == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) { const u32 major = version >> 14; const u32 minor = version & 0x3fff; return fmt::format("{}.{}", major, minor); diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index 89426121f..6e5abade4 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -10,6 +10,7 @@ #include "common/assert.h" #include "common/common_types.h" #include "common/math_util.h" +#include "common/polyfill_ranges.h" #include "common/settings.h" #include "core/core.h" #include "core/frontend/emu_window.h" diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index 241d7573e..2c00979d7 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -93,7 +93,7 @@ constexpr DescriptorBankInfo ASTC_BANK_INFO{ .score = 2, }; -constexpr VkDescriptorUpdateTemplateEntryKHR INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMPLATE{ +constexpr VkDescriptorUpdateTemplateEntry INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMPLATE{ .dstBinding = 0, .dstArrayElement = 0, .descriptorCount = 2, @@ -102,7 +102,7 @@ constexpr VkDescriptorUpdateTemplateEntryKHR INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMP .stride = sizeof(DescriptorUpdateEntry), }; -constexpr std::array<VkDescriptorUpdateTemplateEntryKHR, ASTC_NUM_BINDINGS> +constexpr std::array<VkDescriptorUpdateTemplateEntry, ASTC_NUM_BINDINGS> ASTC_PASS_DESCRIPTOR_UPDATE_TEMPLATE_ENTRY{{ { .dstBinding = ASTC_BINDING_INPUT_BUFFER, @@ -134,7 +134,7 @@ struct AstcPushConstants { ComputePass::ComputePass(const Device& device_, DescriptorPool& descriptor_pool, vk::Span<VkDescriptorSetLayoutBinding> bindings, - vk::Span<VkDescriptorUpdateTemplateEntryKHR> templates, + vk::Span<VkDescriptorUpdateTemplateEntry> templates, const DescriptorBankInfo& bank_info, vk::Span<VkPushConstantRange> push_constants, std::span<const u32> code) : device{device_} { @@ -155,13 +155,13 @@ ComputePass::ComputePass(const Device& device_, DescriptorPool& descriptor_pool, .pPushConstantRanges = push_constants.data(), }); if (!templates.empty()) { - descriptor_template = device.GetLogical().CreateDescriptorUpdateTemplateKHR({ - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR, + descriptor_template = device.GetLogical().CreateDescriptorUpdateTemplate({ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .descriptorUpdateEntryCount = templates.size(), .pDescriptorUpdateEntries = templates.data(), - .templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR, + .templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET, .descriptorSetLayout = *descriptor_set_layout, .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, .pipelineLayout = *layout, diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.h b/src/video_core/renderer_vulkan/vk_compute_pass.h index dcc691a8e..5d32e3caf 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.h +++ b/src/video_core/renderer_vulkan/vk_compute_pass.h @@ -29,14 +29,14 @@ class ComputePass { public: explicit ComputePass(const Device& device, DescriptorPool& descriptor_pool, vk::Span<VkDescriptorSetLayoutBinding> bindings, - vk::Span<VkDescriptorUpdateTemplateEntryKHR> templates, + vk::Span<VkDescriptorUpdateTemplateEntry> templates, const DescriptorBankInfo& bank_info, vk::Span<VkPushConstantRange> push_constants, std::span<const u32> code); ~ComputePass(); protected: const Device& device; - vk::DescriptorUpdateTemplateKHR descriptor_template; + vk::DescriptorUpdateTemplate descriptor_template; vk::PipelineLayout layout; vk::Pipeline pipeline; vk::DescriptorSetLayout descriptor_set_layout; diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index 7906e11a8..04a3a861e 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -53,7 +53,7 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript .requiredSubgroupSize = GuestWarpSize, }; VkPipelineCreateFlags flags{}; - if (device.IsKhrPipelineEexecutablePropertiesEnabled()) { + if (device.IsKhrPipelineExecutablePropertiesEnabled()) { flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; } pipeline = device.GetLogical().CreateComputePipeline({ diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.h b/src/video_core/renderer_vulkan/vk_compute_pipeline.h index 9879735fe..d70837fc5 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.h @@ -55,7 +55,7 @@ private: vk::DescriptorSetLayout descriptor_set_layout; DescriptorAllocator descriptor_allocator; vk::PipelineLayout pipeline_layout; - vk::DescriptorUpdateTemplateKHR descriptor_update_template; + vk::DescriptorUpdateTemplate descriptor_update_template; vk::Pipeline pipeline; std::condition_variable build_condvar; diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp index c7196b64e..b5ae6443c 100644 --- a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp @@ -7,6 +7,7 @@ #include <vector> #include "common/common_types.h" +#include "common/polyfill_ranges.h" #include "video_core/renderer_vulkan/vk_descriptor_pool.h" #include "video_core/renderer_vulkan/vk_resource_pool.h" #include "video_core/renderer_vulkan/vk_scheduler.h" diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index ef75c126c..006128638 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -714,8 +714,8 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { .sampleShadingEnable = VK_FALSE, .minSampleShading = 0.0f, .pSampleMask = nullptr, - .alphaToCoverageEnable = VK_FALSE, - .alphaToOneEnable = VK_FALSE, + .alphaToCoverageEnable = key.state.alpha_to_coverage_enabled != 0 ? VK_TRUE : VK_FALSE, + .alphaToOneEnable = key.state.alpha_to_one_enabled != 0 ? VK_TRUE : VK_FALSE, }; const VkPipelineDepthStencilStateCreateInfo depth_stencil_ci{ .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, @@ -830,7 +830,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { */ } VkPipelineCreateFlags flags{}; - if (device.IsKhrPipelineEexecutablePropertiesEnabled()) { + if (device.IsKhrPipelineExecutablePropertiesEnabled()) { flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; } pipeline = device.GetLogical().CreateGraphicsPipeline({ diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index 6bf577d25..1ed2967be 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -151,7 +151,7 @@ private: vk::DescriptorSetLayout descriptor_set_layout; DescriptorAllocator descriptor_allocator; vk::PipelineLayout pipeline_layout; - vk::DescriptorUpdateTemplateKHR descriptor_update_template; + vk::DescriptorUpdateTemplate descriptor_update_template; vk::Pipeline pipeline; std::condition_variable build_condvar; diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp index 4e81d3d28..8aa07ef9d 100644 --- a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp +++ b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp @@ -11,10 +11,10 @@ namespace Vulkan { MasterSemaphore::MasterSemaphore(const Device& device) { - static constexpr VkSemaphoreTypeCreateInfoKHR semaphore_type_ci{ - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO_KHR, + static constexpr VkSemaphoreTypeCreateInfo semaphore_type_ci{ + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO, .pNext = nullptr, - .semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE_KHR, + .semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE, .initialValue = 0, }; static constexpr VkSemaphoreCreateInfo semaphore_ci{ @@ -28,7 +28,7 @@ MasterSemaphore::MasterSemaphore(const Device& device) { return; } // Validation layers have a bug where they fail to track resource usage when using timeline - // semaphores and synchronizing with GetSemaphoreCounterValueKHR. To workaround this issue, have + // semaphores and synchronizing with GetSemaphoreCounterValue. To workaround this issue, have // a separate thread waiting for each timeline semaphore value. debug_thread = std::jthread([this](std::stop_token stop_token) { u64 counter = 0; diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.h b/src/video_core/renderer_vulkan/vk_master_semaphore.h index 362ed579a..689f02ea5 100644 --- a/src/video_core/renderer_vulkan/vk_master_semaphore.h +++ b/src/video_core/renderer_vulkan/vk_master_semaphore.h @@ -7,6 +7,7 @@ #include <thread> #include "common/common_types.h" +#include "common/polyfill_thread.h" #include "video_core/vulkan_common/vulkan_wrapper.h" namespace Vulkan { diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index d4b0a542a..38a6b7488 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -46,6 +46,7 @@ MICROPROFILE_DECLARE(Vulkan_PipelineCache); namespace { using Shader::Backend::SPIRV::EmitSPIRV; using Shader::Maxwell::ConvertLegacyToGeneric; +using Shader::Maxwell::GenerateGeometryPassthrough; using Shader::Maxwell::MergeDualVertexPrograms; using Shader::Maxwell::TranslateProgram; using VideoCommon::ComputeEnvironment; @@ -53,13 +54,24 @@ using VideoCommon::FileEnvironment; using VideoCommon::GenericEnvironment; using VideoCommon::GraphicsEnvironment; -constexpr u32 CACHE_VERSION = 7; +constexpr u32 CACHE_VERSION = 8; template <typename Container> auto MakeSpan(Container& container) { return std::span(container.data(), container.size()); } +Shader::OutputTopology MaxwellToOutputTopology(Maxwell::PrimitiveTopology topology) { + switch (topology) { + case Maxwell::PrimitiveTopology::Points: + return Shader::OutputTopology::PointList; + case Maxwell::PrimitiveTopology::LineStrip: + return Shader::OutputTopology::LineStrip; + default: + return Shader::OutputTopology::TriangleStrip; + } +} + Shader::CompareFunction MaxwellToCompareFunction(Maxwell::ComparisonOp comparison) { switch (comparison) { case Maxwell::ComparisonOp::Never_D3D: @@ -275,9 +287,9 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device workers(std::max(std::thread::hardware_concurrency(), 2U) - 1, "VkPipelineBuilder"), serialization_thread(1, "VkPipelineSerialization") { const auto& float_control{device.FloatControlProperties()}; - const VkDriverIdKHR driver_id{device.GetDriverID()}; + const VkDriverId driver_id{device.GetDriverID()}; profile = Shader::Profile{ - .supported_spirv = device.IsKhrSpirv1_4Supported() ? 0x00010400U : 0x00010000U, + .supported_spirv = device.SupportedSpirvVersion(), .unified_descriptor_binding = true, .support_descriptor_aliasing = true, .support_int8 = device.IsInt8Supported(), @@ -285,10 +297,10 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device .support_int64 = device.IsShaderInt64Supported(), .support_vertex_instance_id = false, .support_float_controls = true, - .support_separate_denorm_behavior = float_control.denormBehaviorIndependence == - VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL_KHR, + .support_separate_denorm_behavior = + float_control.denormBehaviorIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL, .support_separate_rounding_mode = - float_control.roundingModeIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL_KHR, + float_control.roundingModeIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL, .support_fp16_denorm_preserve = float_control.shaderDenormPreserveFloat16 != VK_FALSE, .support_fp32_denorm_preserve = float_control.shaderDenormPreserveFloat32 != VK_FALSE, .support_fp16_denorm_flush = float_control.shaderDenormFlushToZeroFloat16 != VK_FALSE, @@ -315,18 +327,19 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device .lower_left_origin_mode = false, .need_declared_frag_colors = false, - .has_broken_spirv_clamp = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS_KHR, + .has_broken_spirv_clamp = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS, .has_broken_unsigned_image_offsets = false, .has_broken_signed_operations = false, - .has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY_KHR, + .has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY, .ignore_nan_fp_comparisons = false, }; host_info = Shader::HostTranslateInfo{ .support_float16 = device.IsFloat16Supported(), .support_int64 = device.IsShaderInt64Supported(), - .needs_demote_reorder = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY_KHR || - driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR, + .needs_demote_reorder = + driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE, .support_snorm_render_buffer = true, + .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(), }; } @@ -395,7 +408,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading std::unique_ptr<PipelineStatistics> statistics; } state; - if (device.IsKhrPipelineEexecutablePropertiesEnabled()) { + if (device.IsKhrPipelineExecutablePropertiesEnabled()) { state.statistics = std::make_unique<PipelineStatistics>(device); } const auto load_compute{[&](std::ifstream& file, FileEnvironment env) { @@ -509,7 +522,19 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline( std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs; const bool uses_vertex_a{key.unique_hashes[0] != 0}; const bool uses_vertex_b{key.unique_hashes[1] != 0}; + + // Layer passthrough generation for devices without VK_EXT_shader_viewport_index_layer + Shader::IR::Program* layer_source_program{}; + for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { + const bool is_emulated_stage = layer_source_program != nullptr && + index == static_cast<u32>(Maxwell::ShaderType::Geometry); + if (key.unique_hashes[index] == 0 && is_emulated_stage) { + auto topology = MaxwellToOutputTopology(key.state.topology); + programs[index] = GenerateGeometryPassthrough(pools.inst, pools.block, host_info, + *layer_source_program, topology); + continue; + } if (key.unique_hashes[index] == 0) { continue; } @@ -530,6 +555,10 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline( auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)}; programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); } + + if (programs[index].info.requires_layer_emulation) { + layer_source_program = &programs[index]; + } } std::array<const Shader::Info*, Maxwell::MaxShaderStage> infos{}; std::array<vk::ShaderModule, Maxwell::MaxShaderStage> modules; @@ -538,7 +567,9 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline( Shader::Backend::Bindings binding; for (size_t index = uses_vertex_a && uses_vertex_b ? 1 : 0; index < Maxwell::MaxShaderProgram; ++index) { - if (key.unique_hashes[index] == 0) { + const bool is_emulated_stage = layer_source_program != nullptr && + index == static_cast<u32>(Maxwell::ShaderType::Geometry); + if (key.unique_hashes[index] == 0 && !is_emulated_stage) { continue; } UNIMPLEMENTED_IF(index == 0); diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index 4b15c0f85..929c8ece6 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp @@ -98,7 +98,7 @@ HostCounter::HostCounter(QueryCache& cache_, std::shared_ptr<HostCounter> depend query{cache_.AllocateQuery(type_)}, tick{cache_.GetScheduler().CurrentTick()} { const vk::Device* logical = &cache.GetDevice().GetLogical(); cache.GetScheduler().Record([logical, query = query](vk::CommandBuffer cmdbuf) { - logical->ResetQueryPoolEXT(query.first, query.second, 1); + logical->ResetQueryPool(query.first, query.second, 1); cmdbuf.BeginQuery(query.first, query.second, VK_QUERY_CONTROL_PRECISE_BIT); }); } diff --git a/src/video_core/renderer_vulkan/vk_render_pass_cache.h b/src/video_core/renderer_vulkan/vk_render_pass_cache.h index dc21b7e69..91ad4bf57 100644 --- a/src/video_core/renderer_vulkan/vk_render_pass_cache.h +++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.h @@ -12,7 +12,7 @@ namespace Vulkan { struct RenderPassKey { - auto operator<=>(const RenderPassKey&) const noexcept = default; + bool operator==(const RenderPassKey&) const noexcept = default; std::array<VideoCore::Surface::PixelFormat, 8> color_formats; VideoCore::Surface::PixelFormat depth_format; diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 4a7b633b7..c2e53a5d5 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -145,7 +145,7 @@ void Scheduler::WorkerThread(std::stop_token stop_token) { if (work_queue.empty()) { wait_cv.notify_all(); } - work_cv.wait(lock, stop_token, [this] { return !work_queue.empty(); }); + Common::CondvarWait(work_cv, lock, stop_token, [&] { return !work_queue.empty(); }); if (stop_token.stop_requested()) { continue; } @@ -194,8 +194,8 @@ void Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_s VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, }; - const VkTimelineSemaphoreSubmitInfoKHR timeline_si{ - .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR, + const VkTimelineSemaphoreSubmitInfo timeline_si{ + .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO, .pNext = nullptr, .waitSemaphoreValueCount = num_wait_semaphores, .pWaitSemaphoreValues = wait_values.data(), diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index 929216749..3858c506c 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -12,6 +12,7 @@ #include "common/alignment.h" #include "common/common_types.h" +#include "common/polyfill_thread.h" #include "video_core/renderer_vulkan/vk_master_semaphore.h" #include "video_core/vulkan_common/vulkan_wrapper.h" diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index 706d9ba74..d7be417f5 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -7,6 +7,7 @@ #include <vector> #include "common/logging/log.h" +#include "common/polyfill_ranges.h" #include "common/settings.h" #include "core/core.h" #include "video_core/renderer_vulkan/vk_scheduler.h" diff --git a/src/video_core/shader_cache.h b/src/video_core/shader_cache.h index a4391202d..f3cc4c70b 100644 --- a/src/video_core/shader_cache.h +++ b/src/video_core/shader_cache.h @@ -12,6 +12,7 @@ #include <vector> #include "common/common_types.h" +#include "common/polyfill_ranges.h" #include "video_core/control/channel_state_cache.h" #include "video_core/rasterizer_interface.h" #include "video_core/shader_environment.h" diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp index f24f320b6..958810747 100644 --- a/src/video_core/shader_environment.cpp +++ b/src/video_core/shader_environment.cpp @@ -15,6 +15,7 @@ #include "common/fs/fs.h" #include "common/fs/path_util.h" #include "common/logging/log.h" +#include "common/polyfill_ranges.h" #include "shader_recompiler/environment.h" #include "video_core/engines/kepler_compute.h" #include "video_core/memory_manager.h" diff --git a/src/video_core/shader_environment.h b/src/video_core/shader_environment.h index bb55b029f..1342fab1e 100644 --- a/src/video_core/shader_environment.h +++ b/src/video_core/shader_environment.h @@ -10,12 +10,12 @@ #include <memory> #include <optional> #include <span> -#include <stop_token> #include <type_traits> #include <unordered_map> #include <vector> #include "common/common_types.h" +#include "common/polyfill_thread.h" #include "common/unique_function.h" #include "shader_recompiler/environment.h" #include "video_core/engines/maxwell_3d.h" diff --git a/src/video_core/texture_cache/formatter.cpp b/src/video_core/texture_cache/formatter.cpp index ee4f2d406..418890126 100644 --- a/src/video_core/texture_cache/formatter.cpp +++ b/src/video_core/texture_cache/formatter.cpp @@ -4,6 +4,7 @@ #include <algorithm> #include <string> +#include "common/polyfill_ranges.h" #include "video_core/texture_cache/formatter.h" #include "video_core/texture_cache/image_base.h" #include "video_core/texture_cache/image_info.h" diff --git a/src/video_core/texture_cache/render_targets.h b/src/video_core/texture_cache/render_targets.h index 1efbd6507..0829d773a 100644 --- a/src/video_core/texture_cache/render_targets.h +++ b/src/video_core/texture_cache/render_targets.h @@ -13,7 +13,7 @@ namespace VideoCommon { /// Framebuffer properties used to lookup a framebuffer struct RenderTargets { - constexpr auto operator<=>(const RenderTargets&) const noexcept = default; + constexpr bool operator==(const RenderTargets&) const noexcept = default; constexpr bool Contains(std::span<const ImageViewId> elements) const noexcept { const auto contains = [elements](ImageViewId item) { diff --git a/src/video_core/texture_cache/slot_vector.h b/src/video_core/texture_cache/slot_vector.h index 46e8a86e6..1e2aad76a 100644 --- a/src/video_core/texture_cache/slot_vector.h +++ b/src/video_core/texture_cache/slot_vector.h @@ -12,6 +12,7 @@ #include "common/assert.h" #include "common/common_types.h" +#include "common/polyfill_ranges.h" namespace VideoCommon { diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index 9db7195bf..587339a31 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -16,6 +16,7 @@ #include "common/hash.h" #include "common/literals.h" #include "common/lru_cache.h" +#include "common/polyfill_ranges.h" #include "video_core/compatible_formats.h" #include "video_core/control/channel_state_cache.h" #include "video_core/delayed_destruction_ring.h" @@ -60,8 +61,6 @@ public: TextureCacheChannelInfo(Tegra::Control::ChannelState& state) noexcept; TextureCacheChannelInfo(const TextureCacheChannelInfo& state) = delete; TextureCacheChannelInfo& operator=(const TextureCacheChannelInfo&) = delete; - TextureCacheChannelInfo(TextureCacheChannelInfo&& other) noexcept = default; - TextureCacheChannelInfo& operator=(TextureCacheChannelInfo&& other) noexcept = default; DescriptorTable<TICEntry> graphics_image_table{gpu_memory}; DescriptorTable<TSCEntry> graphics_sampler_table{gpu_memory}; diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp index 69a32819a..e8d7c7863 100644 --- a/src/video_core/textures/astc.cpp +++ b/src/video_core/textures/astc.cpp @@ -15,6 +15,7 @@ #include "common/alignment.h" #include "common/common_types.h" +#include "common/polyfill_ranges.h" #include "common/thread_worker.h" #include "video_core/textures/astc.h" diff --git a/src/video_core/transform_feedback.cpp b/src/video_core/transform_feedback.cpp index 45071185a..155599316 100644 --- a/src/video_core/transform_feedback.cpp +++ b/src/video_core/transform_feedback.cpp @@ -7,6 +7,7 @@ #include "common/alignment.h" #include "common/assert.h" +#include "common/polyfill_ranges.h" #include "shader_recompiler/shader_info.h" #include "video_core/transform_feedback.h" diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index 04ac4af11..fedb4a7bb 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp @@ -7,6 +7,7 @@ #include "common/settings.h" #include "core/core.h" #include "video_core/renderer_base.h" +#include "video_core/renderer_null/renderer_null.h" #include "video_core/renderer_opengl/renderer_opengl.h" #include "video_core/renderer_vulkan/renderer_vulkan.h" #include "video_core/video_core.h" @@ -26,6 +27,9 @@ std::unique_ptr<VideoCore::RendererBase> CreateRenderer( case Settings::RendererBackend::Vulkan: return std::make_unique<Vulkan::RendererVulkan>(telemetry_session, emu_window, cpu_memory, gpu, std::move(context)); + case Settings::RendererBackend::Null: + return std::make_unique<Null::RendererNull>(emu_window, cpu_memory, gpu, + std::move(context)); default: return nullptr; } diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index ddecfca13..467878431 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -12,6 +12,7 @@ #include "common/assert.h" #include "common/literals.h" +#include "common/polyfill_ranges.h" #include "common/settings.h" #include "video_core/vulkan_common/nsight_aftermath_tracker.h" #include "video_core/vulkan_common/vulkan_device.h" @@ -74,23 +75,8 @@ enum class NvidiaArchitecture { }; constexpr std::array REQUIRED_EXTENSIONS{ - VK_KHR_MAINTENANCE1_EXTENSION_NAME, - VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_EXTENSION_NAME, - VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME, - VK_KHR_16BIT_STORAGE_EXTENSION_NAME, - VK_KHR_8BIT_STORAGE_EXTENSION_NAME, - VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME, - VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME, - VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME, - VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME, - VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME, - VK_KHR_VARIABLE_POINTERS_EXTENSION_NAME, VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME, - VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME, - VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME, VK_EXT_ROBUSTNESS_2_EXTENSION_NAME, - VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME, - VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME, #ifdef _WIN32 VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME, #endif @@ -99,6 +85,19 @@ constexpr std::array REQUIRED_EXTENSIONS{ #endif }; +constexpr std::array REQUIRED_EXTENSIONS_BEFORE_1_2{ + VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME, + VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME, + VK_KHR_8BIT_STORAGE_EXTENSION_NAME, + VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME, + VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME, + VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME, +}; + +constexpr std::array REQUIRED_EXTENSIONS_BEFORE_1_3{ + VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME, +}; + template <typename T> void SetNext(void**& next, T& data) { *next = &data; @@ -308,10 +307,10 @@ NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical, VkPhysicalDeviceFragmentShadingRatePropertiesKHR shading_rate_props{}; shading_rate_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR; - VkPhysicalDeviceProperties2KHR physical_properties{}; - physical_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR; + VkPhysicalDeviceProperties2 physical_properties{}; + physical_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; physical_properties.pNext = &shading_rate_props; - physical.GetProperties2KHR(physical_properties); + physical.GetProperties2(physical_properties); if (shading_rate_props.primitiveFragmentShadingRateWithMultipleViewports) { // Only Ampere and newer support this feature return NvidiaArchitecture::AmpereOrNewer; @@ -327,7 +326,8 @@ NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical, Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR surface, const vk::InstanceDispatch& dld_) : instance{instance_}, dld{dld_}, physical{physical_}, properties{physical.GetProperties()}, - supported_extensions{GetSupportedExtensions(physical)}, + instance_version{properties.apiVersion}, supported_extensions{GetSupportedExtensions( + physical)}, format_properties(GetFormatProperties(physical)) { CheckSuitability(surface != nullptr); SetupFamilies(surface); @@ -401,15 +401,15 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR const void* first_next = &features2; void** next = &features2.pNext; - VkPhysicalDeviceTimelineSemaphoreFeaturesKHR timeline_semaphore{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES_KHR, + VkPhysicalDeviceTimelineSemaphoreFeatures timeline_semaphore{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES, .pNext = nullptr, .timelineSemaphore = true, }; SetNext(next, timeline_semaphore); - VkPhysicalDevice16BitStorageFeaturesKHR bit16_storage{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR, + VkPhysicalDevice16BitStorageFeatures bit16_storage{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES, .pNext = nullptr, .storageBuffer16BitAccess = true, .uniformAndStorageBuffer16BitAccess = true, @@ -418,8 +418,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR }; SetNext(next, bit16_storage); - VkPhysicalDevice8BitStorageFeaturesKHR bit8_storage{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR, + VkPhysicalDevice8BitStorageFeatures bit8_storage{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES, .pNext = nullptr, .storageBuffer8BitAccess = false, .uniformAndStorageBuffer8BitAccess = true, @@ -436,32 +436,39 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR }; SetNext(next, robustness2); - VkPhysicalDeviceHostQueryResetFeaturesEXT host_query_reset{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT, + VkPhysicalDeviceHostQueryResetFeatures host_query_reset{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES, .pNext = nullptr, .hostQueryReset = true, }; SetNext(next, host_query_reset); - VkPhysicalDeviceVariablePointerFeaturesKHR variable_pointers{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES_KHR, + VkPhysicalDeviceVariablePointerFeatures variable_pointers{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES, .pNext = nullptr, .variablePointersStorageBuffer = VK_TRUE, .variablePointers = VK_TRUE, }; SetNext(next, variable_pointers); - VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT demote{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES_EXT, + VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures demote{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES, .pNext = nullptr, .shaderDemoteToHelperInvocation = true, }; SetNext(next, demote); - VkPhysicalDeviceFloat16Int8FeaturesKHR float16_int8; + VkPhysicalDeviceShaderDrawParametersFeatures draw_parameters{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES, + .pNext = nullptr, + .shaderDrawParameters = true, + }; + SetNext(next, draw_parameters); + + VkPhysicalDeviceShaderFloat16Int8Features float16_int8; if (is_int8_supported || is_float16_supported) { float16_int8 = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR, + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES, .pNext = nullptr, .shaderFloat16 = is_float16_supported, .shaderInt8 = is_int8_supported, @@ -487,10 +494,10 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR LOG_INFO(Render_Vulkan, "Device doesn't support passthrough geometry shaders"); } - VkPhysicalDeviceUniformBufferStandardLayoutFeaturesKHR std430_layout; + VkPhysicalDeviceUniformBufferStandardLayoutFeatures std430_layout; if (khr_uniform_buffer_standard_layout) { std430_layout = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES_KHR, + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES, .pNext = nullptr, .uniformBufferStandardLayout = true, }; @@ -608,10 +615,10 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR LOG_INFO(Render_Vulkan, "Device doesn't support vertex input dynamic state"); } - VkPhysicalDeviceShaderAtomicInt64FeaturesKHR atomic_int64; + VkPhysicalDeviceShaderAtomicInt64Features atomic_int64; if (ext_shader_atomic_int64) { atomic_int64 = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES_KHR, + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES, .pNext = nullptr, .shaderBufferInt64Atomics = VK_TRUE, .shaderSharedInt64Atomics = VK_TRUE, @@ -896,28 +903,51 @@ std::string Device::GetDriverName() const { } } +static std::vector<const char*> ExtensionsRequiredForInstanceVersion(u32 available_version) { + std::vector<const char*> extensions{REQUIRED_EXTENSIONS.begin(), REQUIRED_EXTENSIONS.end()}; + + if (available_version < VK_API_VERSION_1_2) { + extensions.insert(extensions.end(), REQUIRED_EXTENSIONS_BEFORE_1_2.begin(), + REQUIRED_EXTENSIONS_BEFORE_1_2.end()); + } + + if (available_version < VK_API_VERSION_1_3) { + extensions.insert(extensions.end(), REQUIRED_EXTENSIONS_BEFORE_1_3.begin(), + REQUIRED_EXTENSIONS_BEFORE_1_3.end()); + } + + return extensions; +} + void Device::CheckSuitability(bool requires_swapchain) const { - std::bitset<REQUIRED_EXTENSIONS.size()> available_extensions; - bool has_swapchain = false; - for (const VkExtensionProperties& property : physical.EnumerateDeviceExtensionProperties()) { - const std::string_view name{property.extensionName}; - for (size_t i = 0; i < REQUIRED_EXTENSIONS.size(); ++i) { - if (available_extensions[i]) { - continue; - } - available_extensions[i] = name == REQUIRED_EXTENSIONS[i]; - } - has_swapchain = has_swapchain || name == VK_KHR_SWAPCHAIN_EXTENSION_NAME; + std::vector<const char*> required_extensions = + ExtensionsRequiredForInstanceVersion(instance_version); + std::vector<const char*> available_extensions; + + if (requires_swapchain) { + required_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); } - for (size_t i = 0; i < REQUIRED_EXTENSIONS.size(); ++i) { - if (available_extensions[i]) { - continue; + + auto extension_properties = physical.EnumerateDeviceExtensionProperties(); + + for (const VkExtensionProperties& property : extension_properties) { + available_extensions.push_back(property.extensionName); + } + + bool has_all_required_extensions = true; + for (const char* requirement_name : required_extensions) { + const bool found = + std::ranges::any_of(available_extensions, [&](const char* extension_name) { + return std::strcmp(requirement_name, extension_name) == 0; + }); + + if (!found) { + LOG_ERROR(Render_Vulkan, "Missing required extension: {}", requirement_name); + has_all_required_extensions = false; } - LOG_ERROR(Render_Vulkan, "Missing required extension: {}", REQUIRED_EXTENSIONS[i]); - throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT); } - if (requires_swapchain && !has_swapchain) { - LOG_ERROR(Render_Vulkan, "Missing required extension: VK_KHR_swapchain"); + + if (!has_all_required_extensions) { throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT); } @@ -940,27 +970,46 @@ void Device::CheckSuitability(bool requires_swapchain) const { throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT); } } - VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT demote{}; - demote.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES_EXT; + VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures demote{}; + demote.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES; demote.pNext = nullptr; - VkPhysicalDeviceVariablePointerFeaturesKHR variable_pointers{}; - variable_pointers.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES_KHR; + VkPhysicalDeviceVariablePointerFeatures variable_pointers{}; + variable_pointers.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES; variable_pointers.pNext = &demote; VkPhysicalDeviceRobustness2FeaturesEXT robustness2{}; robustness2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT; robustness2.pNext = &variable_pointers; - VkPhysicalDeviceFeatures2KHR features2{}; + VkPhysicalDeviceTimelineSemaphoreFeatures timeline_semaphore{}; + timeline_semaphore.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES; + timeline_semaphore.pNext = &robustness2; + + VkPhysicalDevice16BitStorageFeatures bit16_storage{}; + bit16_storage.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES; + bit16_storage.pNext = &timeline_semaphore; + + VkPhysicalDevice8BitStorageFeatures bit8_storage{}; + bit8_storage.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES; + bit8_storage.pNext = &bit16_storage; + + VkPhysicalDeviceHostQueryResetFeatures host_query_reset{}; + host_query_reset.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES; + host_query_reset.pNext = &bit8_storage; + + VkPhysicalDeviceShaderDrawParametersFeatures draw_parameters{}; + draw_parameters.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES; + draw_parameters.pNext = &host_query_reset; + + VkPhysicalDeviceFeatures2 features2{}; features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; - features2.pNext = &robustness2; + features2.pNext = &draw_parameters; - physical.GetFeatures2KHR(features2); + physical.GetFeatures2(features2); const VkPhysicalDeviceFeatures& features{features2.features}; - const std::array feature_report{ + std::array feature_report{ std::make_pair(features.robustBufferAccess, "robustBufferAccess"), std::make_pair(features.vertexPipelineStoresAndAtomics, "vertexPipelineStoresAndAtomics"), std::make_pair(features.imageCubeArray, "imageCubeArray"), @@ -976,6 +1025,7 @@ void Device::CheckSuitability(bool requires_swapchain) const { std::make_pair(features.tessellationShader, "tessellationShader"), std::make_pair(features.sampleRateShading, "sampleRateShading"), std::make_pair(features.dualSrcBlend, "dualSrcBlend"), + std::make_pair(features.logicOp, "logicOp"), std::make_pair(features.occlusionQueryPrecise, "occlusionQueryPrecise"), std::make_pair(features.fragmentStoresAndAtomics, "fragmentStoresAndAtomics"), std::make_pair(features.shaderImageGatherExtended, "shaderImageGatherExtended"), @@ -983,27 +1033,38 @@ void Device::CheckSuitability(bool requires_swapchain) const { "shaderStorageImageWriteWithoutFormat"), std::make_pair(features.shaderClipDistance, "shaderClipDistance"), std::make_pair(features.shaderCullDistance, "shaderCullDistance"), - std::make_pair(demote.shaderDemoteToHelperInvocation, "shaderDemoteToHelperInvocation"), std::make_pair(variable_pointers.variablePointers, "variablePointers"), std::make_pair(variable_pointers.variablePointersStorageBuffer, "variablePointersStorageBuffer"), std::make_pair(robustness2.robustBufferAccess2, "robustBufferAccess2"), std::make_pair(robustness2.robustImageAccess2, "robustImageAccess2"), std::make_pair(robustness2.nullDescriptor, "nullDescriptor"), + std::make_pair(demote.shaderDemoteToHelperInvocation, "shaderDemoteToHelperInvocation"), + std::make_pair(timeline_semaphore.timelineSemaphore, "timelineSemaphore"), + std::make_pair(bit16_storage.storageBuffer16BitAccess, "storageBuffer16BitAccess"), + std::make_pair(bit16_storage.uniformAndStorageBuffer16BitAccess, + "uniformAndStorageBuffer16BitAccess"), + std::make_pair(bit8_storage.uniformAndStorageBuffer8BitAccess, + "uniformAndStorageBuffer8BitAccess"), + std::make_pair(host_query_reset.hostQueryReset, "hostQueryReset"), + std::make_pair(draw_parameters.shaderDrawParameters, "shaderDrawParameters"), }; + + bool has_all_required_features = true; for (const auto& [is_supported, name] : feature_report) { - if (is_supported) { - continue; + if (!is_supported) { + LOG_ERROR(Render_Vulkan, "Missing required feature: {}", name); + has_all_required_features = false; } - LOG_ERROR(Render_Vulkan, "Missing required feature: {}", name); + } + + if (!has_all_required_features) { throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT); } } std::vector<const char*> Device::LoadExtensions(bool requires_surface) { - std::vector<const char*> extensions; - extensions.reserve(8 + REQUIRED_EXTENSIONS.size()); - extensions.insert(extensions.begin(), REQUIRED_EXTENSIONS.begin(), REQUIRED_EXTENSIONS.end()); + std::vector<const char*> extensions = ExtensionsRequiredForInstanceVersion(instance_version); if (requires_surface) { extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); } @@ -1079,37 +1140,37 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) { VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME, false); } } - VkPhysicalDeviceFeatures2KHR features{}; - features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR; + VkPhysicalDeviceFeatures2 features{}; + features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; - VkPhysicalDeviceProperties2KHR physical_properties{}; - physical_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR; + VkPhysicalDeviceProperties2 physical_properties{}; + physical_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; if (has_khr_shader_float16_int8) { - VkPhysicalDeviceFloat16Int8FeaturesKHR float16_int8_features; - float16_int8_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR; + VkPhysicalDeviceShaderFloat16Int8Features float16_int8_features; + float16_int8_features.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES; float16_int8_features.pNext = nullptr; features.pNext = &float16_int8_features; - physical.GetFeatures2KHR(features); + physical.GetFeatures2(features); is_float16_supported = float16_int8_features.shaderFloat16; is_int8_supported = float16_int8_features.shaderInt8; extensions.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME); } if (has_ext_subgroup_size_control) { - VkPhysicalDeviceSubgroupSizeControlFeaturesEXT subgroup_features; - subgroup_features.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES_EXT; + VkPhysicalDeviceSubgroupSizeControlFeatures subgroup_features; + subgroup_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES; subgroup_features.pNext = nullptr; features.pNext = &subgroup_features; - physical.GetFeatures2KHR(features); + physical.GetFeatures2(features); - VkPhysicalDeviceSubgroupSizeControlPropertiesEXT subgroup_properties; + VkPhysicalDeviceSubgroupSizeControlProperties subgroup_properties; subgroup_properties.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES_EXT; + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES; subgroup_properties.pNext = nullptr; physical_properties.pNext = &subgroup_properties; - physical.GetProperties2KHR(physical_properties); + physical.GetProperties2(physical_properties); is_warp_potentially_bigger = subgroup_properties.maxSubgroupSize > GuestWarpSize; @@ -1128,7 +1189,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) { provoking_vertex.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT; provoking_vertex.pNext = nullptr; features.pNext = &provoking_vertex; - physical.GetFeatures2KHR(features); + physical.GetFeatures2(features); if (provoking_vertex.provokingVertexLast && provoking_vertex.transformFeedbackPreservesProvokingVertex) { @@ -1142,7 +1203,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_INPUT_DYNAMIC_STATE_FEATURES_EXT; vertex_input.pNext = nullptr; features.pNext = &vertex_input; - physical.GetFeatures2KHR(features); + physical.GetFeatures2(features); if (vertex_input.vertexInputDynamicState) { extensions.push_back(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); @@ -1154,7 +1215,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) { atomic_int64.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES; atomic_int64.pNext = nullptr; features.pNext = &atomic_int64; - physical.GetFeatures2KHR(features); + physical.GetFeatures2(features); if (atomic_int64.shaderBufferInt64Atomics && atomic_int64.shaderSharedInt64Atomics) { extensions.push_back(VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME); @@ -1166,13 +1227,13 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) { tfb_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT; tfb_features.pNext = nullptr; features.pNext = &tfb_features; - physical.GetFeatures2KHR(features); + physical.GetFeatures2(features); VkPhysicalDeviceTransformFeedbackPropertiesEXT tfb_properties; tfb_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT; tfb_properties.pNext = nullptr; physical_properties.pNext = &tfb_properties; - physical.GetProperties2KHR(physical_properties); + physical.GetProperties2(physical_properties); if (tfb_features.transformFeedback && tfb_features.geometryStreams && tfb_properties.maxTransformFeedbackStreams >= 4 && @@ -1187,7 +1248,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) { border_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT; border_features.pNext = nullptr; features.pNext = &border_features; - physical.GetFeatures2KHR(features); + physical.GetFeatures2(features); if (border_features.customBorderColors && border_features.customBorderColorWithoutFormat) { extensions.push_back(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); @@ -1200,7 +1261,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT; extended_dynamic_state.pNext = nullptr; features.pNext = &extended_dynamic_state; - physical.GetFeatures2KHR(features); + physical.GetFeatures2(features); if (extended_dynamic_state.extendedDynamicState) { extensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); @@ -1212,7 +1273,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) { line_raster.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT; line_raster.pNext = nullptr; features.pNext = &line_raster; - physical.GetFeatures2KHR(features); + physical.GetFeatures2(features); if (line_raster.rectangularLines && line_raster.smoothLines) { extensions.push_back(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME); ext_line_rasterization = true; @@ -1224,7 +1285,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_FEATURES_KHR; layout.pNext = nullptr; features.pNext = &layout; - physical.GetFeatures2KHR(features); + physical.GetFeatures2(features); if (layout.workgroupMemoryExplicitLayout && layout.workgroupMemoryExplicitLayout8BitAccess && @@ -1240,7 +1301,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR; executable_properties.pNext = nullptr; features.pNext = &executable_properties; - physical.GetFeatures2KHR(features); + physical.GetFeatures2(features); if (executable_properties.pipelineExecutableInfo) { extensions.push_back(VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME); @@ -1253,7 +1314,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT; primitive_topology_list_restart.pNext = nullptr; features.pNext = &primitive_topology_list_restart; - physical.GetFeatures2KHR(features); + physical.GetFeatures2(features); is_topology_list_restart_supported = primitive_topology_list_restart.primitiveTopologyListRestart; @@ -1271,7 +1332,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) { push_descriptor.pNext = nullptr; physical_properties.pNext = &push_descriptor; - physical.GetProperties2KHR(physical_properties); + physical.GetProperties2(physical_properties); max_push_descriptors = push_descriptor.maxPushDescriptors; } @@ -1322,18 +1383,18 @@ void Device::SetupFeatures() { } void Device::SetupProperties() { - float_controls.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES_KHR; + float_controls.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES; VkPhysicalDeviceProperties2KHR properties2{}; - properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR; + properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; properties2.pNext = &float_controls; - physical.GetProperties2KHR(properties2); + physical.GetProperties2(properties2); } void Device::CollectTelemetryParameters() { - VkPhysicalDeviceDriverPropertiesKHR driver{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR, + VkPhysicalDeviceDriverProperties driver{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES, .pNext = nullptr, .driverID = {}, .driverName = {}, @@ -1341,12 +1402,12 @@ void Device::CollectTelemetryParameters() { .conformanceVersion = {}, }; - VkPhysicalDeviceProperties2KHR device_properties{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR, + VkPhysicalDeviceProperties2 device_properties{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, .pNext = &driver, .properties = {}, }; - physical.GetProperties2KHR(device_properties); + physical.GetProperties2(device_properties); driver_id = driver.driverID; vendor_name = driver.driverName; @@ -1402,23 +1463,10 @@ void Device::CollectToolingInfo() { if (!ext_tooling_info) { return; } - const auto vkGetPhysicalDeviceToolPropertiesEXT = - reinterpret_cast<PFN_vkGetPhysicalDeviceToolPropertiesEXT>( - dld.vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceToolPropertiesEXT")); - if (!vkGetPhysicalDeviceToolPropertiesEXT) { - return; - } - u32 tool_count = 0; - if (vkGetPhysicalDeviceToolPropertiesEXT(physical, &tool_count, nullptr) != VK_SUCCESS) { - return; - } - std::vector<VkPhysicalDeviceToolPropertiesEXT> tools(tool_count); - if (vkGetPhysicalDeviceToolPropertiesEXT(physical, &tool_count, tools.data()) != VK_SUCCESS) { - return; - } - for (const VkPhysicalDeviceToolPropertiesEXT& tool : tools) { + auto tools{physical.GetPhysicalDeviceToolProperties()}; + for (const VkPhysicalDeviceToolProperties& tool : tools) { const std::string_view name = tool.name; - LOG_INFO(Render_Vulkan, "{}", name); + LOG_INFO(Render_Vulkan, "Attached debugging tool: {}", name); has_renderdoc = has_renderdoc || name == "RenderDoc"; has_nsight_graphics = has_nsight_graphics || name == "NVIDIA Nsight Graphics"; } diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index d7cc6c593..db802437c 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -211,18 +211,13 @@ public: return khr_uniform_buffer_standard_layout; } - /// Returns true if the device supports VK_KHR_spirv_1_4. - bool IsKhrSpirv1_4Supported() const { - return khr_spirv_1_4; - } - /// Returns true if the device supports VK_KHR_push_descriptor. bool IsKhrPushDescriptorSupported() const { return khr_push_descriptor; } /// Returns true if VK_KHR_pipeline_executable_properties is enabled. - bool IsKhrPipelineEexecutablePropertiesEnabled() const { + bool IsKhrPipelineExecutablePropertiesEnabled() const { return khr_pipeline_executable_properties; } @@ -316,6 +311,17 @@ public: return ext_shader_atomic_int64; } + /// Returns the minimum supported version of SPIR-V. + u32 SupportedSpirvVersion() const { + if (instance_version >= VK_API_VERSION_1_3) { + return 0x00010600U; + } + if (khr_spirv_1_4) { + return 0x00010400U; + } + return 0x00010000U; + } + /// Returns true when a known debugging tool is attached. bool HasDebuggingToolAttached() const { return has_renderdoc || has_nsight_graphics; diff --git a/src/video_core/vulkan_common/vulkan_instance.cpp b/src/video_core/vulkan_common/vulkan_instance.cpp index a082e3059..562039b56 100644 --- a/src/video_core/vulkan_common/vulkan_instance.cpp +++ b/src/video_core/vulkan_common/vulkan_instance.cpp @@ -9,18 +9,21 @@ #include "common/common_types.h" #include "common/dynamic_library.h" #include "common/logging/log.h" +#include "common/polyfill_ranges.h" #include "core/frontend/emu_window.h" #include "video_core/vulkan_common/vulkan_instance.h" #include "video_core/vulkan_common/vulkan_wrapper.h" // Include these late to avoid polluting previous headers -#ifdef _WIN32 +#if defined(_WIN32) #include <windows.h> // ensure include order #include <vulkan/vulkan_win32.h> -#endif - -#if !defined(_WIN32) && !defined(__APPLE__) +#elif defined(__APPLE__) +#include <vulkan/vulkan_macos.h> +#elif defined(__ANDROID__) +#include <vulkan/vulkan_android.h> +#else #include <X11/Xlib.h> #include <vulkan/vulkan_wayland.h> #include <vulkan/vulkan_xlib.h> @@ -39,8 +42,15 @@ namespace { case Core::Frontend::WindowSystemType::Windows: extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); break; -#endif -#if !defined(_WIN32) && !defined(__APPLE__) +#elif defined(__APPLE__) + case Core::Frontend::WindowSystemType::Cocoa: + extensions.push_back(VK_MVK_MACOS_SURFACE_EXTENSION_NAME); + break; +#elif defined(__ANDROID__) + case Core::Frontend::WindowSystemType::Android: + extensions.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME); + break; +#else case Core::Frontend::WindowSystemType::X11: extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); break; @@ -59,6 +69,10 @@ namespace { extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); } extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + +#ifdef __APPLE__ + extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); +#endif return extensions; } @@ -140,7 +154,7 @@ vk::Instance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceD } vk::Instance instance = std::async([&] { - return vk::Instance::Create(required_version, layers, extensions, dld); + return vk::Instance::Create(available_version, layers, extensions, dld); }).get(); if (!vk::Load(*instance, dld)) { LOG_ERROR(Render_Vulkan, "Failed to load Vulkan instance function pointers"); diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp index 6442898bd..1732866e0 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp @@ -12,6 +12,7 @@ #include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" +#include "common/polyfill_ranges.h" #include "video_core/vulkan_common/vulkan_device.h" #include "video_core/vulkan_common/vulkan_memory_allocator.h" #include "video_core/vulkan_common/vulkan_wrapper.h" diff --git a/src/video_core/vulkan_common/vulkan_surface.cpp b/src/video_core/vulkan_common/vulkan_surface.cpp index 69f9c494b..fa9bafa20 100644 --- a/src/video_core/vulkan_common/vulkan_surface.cpp +++ b/src/video_core/vulkan_common/vulkan_surface.cpp @@ -11,9 +11,11 @@ #include <windows.h> // ensure include order #include <vulkan/vulkan_win32.h> -#endif - -#if !defined(_WIN32) && !defined(__APPLE__) +#elif defined(__APPLE__) +#include <vulkan/vulkan_macos.h> +#elif defined(__ANDROID__) +#include <vulkan/vulkan_android.h> +#else #include <X11/Xlib.h> #include <vulkan/vulkan_wayland.h> #include <vulkan/vulkan_xlib.h> @@ -40,8 +42,33 @@ vk::SurfaceKHR CreateSurface(const vk::Instance& instance, throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); } } -#endif -#if !defined(_WIN32) && !defined(__APPLE__) +#elif defined(__APPLE__) + if (window_info.type == Core::Frontend::WindowSystemType::Cocoa) { + const VkMacOSSurfaceCreateInfoMVK mvk_ci{VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK, + nullptr, 0, window_info.render_surface}; + const auto vkCreateMacOSSurfaceMVK = reinterpret_cast<PFN_vkCreateMacOSSurfaceMVK>( + dld.vkGetInstanceProcAddr(*instance, "vkCreateMacOSSurfaceMVK")); + if (!vkCreateMacOSSurfaceMVK || + vkCreateMacOSSurfaceMVK(*instance, &mvk_ci, nullptr, &unsafe_surface) != VK_SUCCESS) { + LOG_ERROR(Render_Vulkan, "Failed to initialize Metal surface"); + throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); + } + } +#elif defined(__ANDROID__) + if (window_info.type == Core::Frontend::WindowSystemType::Android) { + const VkAndroidSurfaceCreateInfoKHR android_ci{ + VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR, nullptr, 0, + reinterpret_cast<ANativeWindow*>(window_info.render_surface)}; + const auto vkCreateAndroidSurfaceKHR = reinterpret_cast<PFN_vkCreateAndroidSurfaceKHR>( + dld.vkGetInstanceProcAddr(*instance, "vkCreateAndroidSurfaceKHR")); + if (!vkCreateAndroidSurfaceKHR || + vkCreateAndroidSurfaceKHR(*instance, &android_ci, nullptr, &unsafe_surface) != + VK_SUCCESS) { + LOG_ERROR(Render_Vulkan, "Failed to initialize Android surface"); + throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); + } + } +#else if (window_info.type == Core::Frontend::WindowSystemType::X11) { const VkXlibSurfaceCreateInfoKHR xlib_ci{ VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, nullptr, 0, @@ -70,6 +97,7 @@ vk::SurfaceKHR CreateSurface(const vk::Instance& instance, } } #endif + if (!unsafe_surface) { LOG_ERROR(Render_Vulkan, "Presentation not supported on this platform"); throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index 2ad98dcfe..bda10ee2f 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -130,7 +130,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkCreateComputePipelines); X(vkCreateDescriptorPool); X(vkCreateDescriptorSetLayout); - X(vkCreateDescriptorUpdateTemplateKHR); + X(vkCreateDescriptorUpdateTemplate); X(vkCreateEvent); X(vkCreateFence); X(vkCreateFramebuffer); @@ -149,7 +149,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkDestroyCommandPool); X(vkDestroyDescriptorPool); X(vkDestroyDescriptorSetLayout); - X(vkDestroyDescriptorUpdateTemplateKHR); + X(vkDestroyDescriptorUpdateTemplate); X(vkDestroyEvent); X(vkDestroyFence); X(vkDestroyFramebuffer); @@ -180,18 +180,29 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkGetQueryPoolResults); X(vkGetPipelineExecutablePropertiesKHR); X(vkGetPipelineExecutableStatisticsKHR); - X(vkGetSemaphoreCounterValueKHR); + X(vkGetSemaphoreCounterValue); X(vkMapMemory); X(vkQueueSubmit); X(vkResetFences); - X(vkResetQueryPoolEXT); + X(vkResetQueryPool); X(vkSetDebugUtilsObjectNameEXT); X(vkSetDebugUtilsObjectTagEXT); X(vkUnmapMemory); - X(vkUpdateDescriptorSetWithTemplateKHR); + X(vkUpdateDescriptorSetWithTemplate); X(vkUpdateDescriptorSets); X(vkWaitForFences); - X(vkWaitSemaphoresKHR); + X(vkWaitSemaphores); + + // Support for timeline semaphores is mandatory in Vulkan 1.2 + if (!dld.vkGetSemaphoreCounterValue) { + Proc(dld.vkGetSemaphoreCounterValue, dld, "vkGetSemaphoreCounterValueKHR", device); + Proc(dld.vkWaitSemaphores, dld, "vkWaitSemaphoresKHR", device); + } + + // Support for host query reset is mandatory in Vulkan 1.2 + if (!dld.vkResetQueryPool) { + Proc(dld.vkResetQueryPool, dld, "vkResetQueryPoolEXT", device); + } #undef X } @@ -224,12 +235,13 @@ bool Load(VkInstance instance, InstanceDispatch& dld) noexcept { X(vkCreateDebugUtilsMessengerEXT); X(vkDestroyDebugUtilsMessengerEXT); X(vkDestroySurfaceKHR); - X(vkGetPhysicalDeviceFeatures2KHR); - X(vkGetPhysicalDeviceProperties2KHR); + X(vkGetPhysicalDeviceFeatures2); + X(vkGetPhysicalDeviceProperties2); X(vkGetPhysicalDeviceSurfaceCapabilitiesKHR); X(vkGetPhysicalDeviceSurfaceFormatsKHR); X(vkGetPhysicalDeviceSurfacePresentModesKHR); X(vkGetPhysicalDeviceSurfaceSupportKHR); + X(vkGetPhysicalDeviceToolProperties); X(vkGetSwapchainImagesKHR); X(vkQueuePresentKHR); @@ -359,9 +371,9 @@ void Destroy(VkDevice device, VkDescriptorSetLayout handle, const DeviceDispatch dld.vkDestroyDescriptorSetLayout(device, handle, nullptr); } -void Destroy(VkDevice device, VkDescriptorUpdateTemplateKHR handle, +void Destroy(VkDevice device, VkDescriptorUpdateTemplate handle, const DeviceDispatch& dld) noexcept { - dld.vkDestroyDescriptorUpdateTemplateKHR(device, handle, nullptr); + dld.vkDestroyDescriptorUpdateTemplate(device, handle, nullptr); } void Destroy(VkDevice device, VkDeviceMemory handle, const DeviceDispatch& dld) noexcept { @@ -442,6 +454,12 @@ VkResult Free(VkDevice device, VkCommandPool handle, Span<VkCommandBuffer> buffe Instance Instance::Create(u32 version, Span<const char*> layers, Span<const char*> extensions, InstanceDispatch& dispatch) { +#ifdef __APPLE__ + constexpr VkFlags ci_flags{VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR}; +#else + constexpr VkFlags ci_flags{}; +#endif + const VkApplicationInfo application_info{ .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .pNext = nullptr, @@ -454,7 +472,7 @@ Instance Instance::Create(u32 version, Span<const char*> layers, Span<const char const VkInstanceCreateInfo ci{ .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, .pNext = nullptr, - .flags = 0, + .flags = ci_flags, .pApplicationInfo = &application_info, .enabledLayerCount = layers.size(), .ppEnabledLayerNames = layers.data(), @@ -737,11 +755,11 @@ CommandPool Device::CreateCommandPool(const VkCommandPoolCreateInfo& ci) const { return CommandPool(object, handle, *dld); } -DescriptorUpdateTemplateKHR Device::CreateDescriptorUpdateTemplateKHR( - const VkDescriptorUpdateTemplateCreateInfoKHR& ci) const { - VkDescriptorUpdateTemplateKHR object; - Check(dld->vkCreateDescriptorUpdateTemplateKHR(handle, &ci, nullptr, &object)); - return DescriptorUpdateTemplateKHR(object, handle, *dld); +DescriptorUpdateTemplate Device::CreateDescriptorUpdateTemplate( + const VkDescriptorUpdateTemplateCreateInfo& ci) const { + VkDescriptorUpdateTemplate object; + Check(dld->vkCreateDescriptorUpdateTemplate(handle, &ci, nullptr, &object)); + return DescriptorUpdateTemplate(object, handle, *dld); } QueryPool Device::CreateQueryPool(const VkQueryPoolCreateInfo& ci) const { @@ -857,20 +875,20 @@ VkPhysicalDeviceProperties PhysicalDevice::GetProperties() const noexcept { return properties; } -void PhysicalDevice::GetProperties2KHR(VkPhysicalDeviceProperties2KHR& properties) const noexcept { - dld->vkGetPhysicalDeviceProperties2KHR(physical_device, &properties); +void PhysicalDevice::GetProperties2(VkPhysicalDeviceProperties2& properties) const noexcept { + dld->vkGetPhysicalDeviceProperties2(physical_device, &properties); } VkPhysicalDeviceFeatures PhysicalDevice::GetFeatures() const noexcept { - VkPhysicalDeviceFeatures2KHR features2; - features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR; + VkPhysicalDeviceFeatures2 features2; + features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; features2.pNext = nullptr; - dld->vkGetPhysicalDeviceFeatures2KHR(physical_device, &features2); + dld->vkGetPhysicalDeviceFeatures2(physical_device, &features2); return features2.features; } -void PhysicalDevice::GetFeatures2KHR(VkPhysicalDeviceFeatures2KHR& features) const noexcept { - dld->vkGetPhysicalDeviceFeatures2KHR(physical_device, &features); +void PhysicalDevice::GetFeatures2(VkPhysicalDeviceFeatures2& features) const noexcept { + dld->vkGetPhysicalDeviceFeatures2(physical_device, &features); } VkFormatProperties PhysicalDevice::GetFormatProperties(VkFormat format) const noexcept { @@ -895,6 +913,18 @@ std::vector<VkQueueFamilyProperties> PhysicalDevice::GetQueueFamilyProperties() return properties; } +std::vector<VkPhysicalDeviceToolProperties> PhysicalDevice::GetPhysicalDeviceToolProperties() + const { + u32 num = 0; + if (!dld->vkGetPhysicalDeviceToolProperties) { + return {}; + } + dld->vkGetPhysicalDeviceToolProperties(physical_device, &num, nullptr); + std::vector<VkPhysicalDeviceToolProperties> properties(num); + dld->vkGetPhysicalDeviceToolProperties(physical_device, &num, properties.data()); + return properties; +} + bool PhysicalDevice::GetSurfaceSupportKHR(u32 queue_family_index, VkSurfaceKHR surface) const { VkBool32 supported; Check(dld->vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, queue_family_index, surface, diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index 1b3f493bd..8395ff2cb 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -168,12 +168,13 @@ struct InstanceDispatch { PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties{}; PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices{}; PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr{}; - PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR{}; + PFN_vkGetPhysicalDeviceFeatures2 vkGetPhysicalDeviceFeatures2{}; PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties{}; PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties{}; PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2{}; PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties{}; - PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR{}; + PFN_vkGetPhysicalDeviceProperties2 vkGetPhysicalDeviceProperties2{}; + PFN_vkGetPhysicalDeviceToolProperties vkGetPhysicalDeviceToolProperties{}; PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties{}; PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR{}; PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR{}; @@ -247,7 +248,7 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCreateComputePipelines vkCreateComputePipelines{}; PFN_vkCreateDescriptorPool vkCreateDescriptorPool{}; PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout{}; - PFN_vkCreateDescriptorUpdateTemplateKHR vkCreateDescriptorUpdateTemplateKHR{}; + PFN_vkCreateDescriptorUpdateTemplate vkCreateDescriptorUpdateTemplate{}; PFN_vkCreateEvent vkCreateEvent{}; PFN_vkCreateFence vkCreateFence{}; PFN_vkCreateFramebuffer vkCreateFramebuffer{}; @@ -266,7 +267,7 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkDestroyCommandPool vkDestroyCommandPool{}; PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool{}; PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout{}; - PFN_vkDestroyDescriptorUpdateTemplateKHR vkDestroyDescriptorUpdateTemplateKHR{}; + PFN_vkDestroyDescriptorUpdateTemplate vkDestroyDescriptorUpdateTemplate{}; PFN_vkDestroyEvent vkDestroyEvent{}; PFN_vkDestroyFence vkDestroyFence{}; PFN_vkDestroyFramebuffer vkDestroyFramebuffer{}; @@ -297,18 +298,18 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkGetPipelineExecutablePropertiesKHR vkGetPipelineExecutablePropertiesKHR{}; PFN_vkGetPipelineExecutableStatisticsKHR vkGetPipelineExecutableStatisticsKHR{}; PFN_vkGetQueryPoolResults vkGetQueryPoolResults{}; - PFN_vkGetSemaphoreCounterValueKHR vkGetSemaphoreCounterValueKHR{}; + PFN_vkGetSemaphoreCounterValue vkGetSemaphoreCounterValue{}; PFN_vkMapMemory vkMapMemory{}; PFN_vkQueueSubmit vkQueueSubmit{}; PFN_vkResetFences vkResetFences{}; - PFN_vkResetQueryPoolEXT vkResetQueryPoolEXT{}; + PFN_vkResetQueryPool vkResetQueryPool{}; PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT{}; PFN_vkSetDebugUtilsObjectTagEXT vkSetDebugUtilsObjectTagEXT{}; PFN_vkUnmapMemory vkUnmapMemory{}; - PFN_vkUpdateDescriptorSetWithTemplateKHR vkUpdateDescriptorSetWithTemplateKHR{}; + PFN_vkUpdateDescriptorSetWithTemplate vkUpdateDescriptorSetWithTemplate{}; PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets{}; PFN_vkWaitForFences vkWaitForFences{}; - PFN_vkWaitSemaphoresKHR vkWaitSemaphoresKHR{}; + PFN_vkWaitSemaphores vkWaitSemaphores{}; }; /// Loads instance agnostic function pointers. @@ -327,7 +328,7 @@ void Destroy(VkDevice, VkBufferView, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkCommandPool, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkDescriptorPool, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkDescriptorSetLayout, const DeviceDispatch&) noexcept; -void Destroy(VkDevice, VkDescriptorUpdateTemplateKHR, const DeviceDispatch&) noexcept; +void Destroy(VkDevice, VkDescriptorUpdateTemplate, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkDeviceMemory, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkEvent, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkFence, const DeviceDispatch&) noexcept; @@ -559,7 +560,7 @@ private: using DebugUtilsMessenger = Handle<VkDebugUtilsMessengerEXT, VkInstance, InstanceDispatch>; using DescriptorSetLayout = Handle<VkDescriptorSetLayout, VkDevice, DeviceDispatch>; -using DescriptorUpdateTemplateKHR = Handle<VkDescriptorUpdateTemplateKHR, VkDevice, DeviceDispatch>; +using DescriptorUpdateTemplate = Handle<VkDescriptorUpdateTemplate, VkDevice, DeviceDispatch>; using Pipeline = Handle<VkPipeline, VkDevice, DeviceDispatch>; using PipelineLayout = Handle<VkPipelineLayout, VkDevice, DeviceDispatch>; using QueryPool = Handle<VkQueryPool, VkDevice, DeviceDispatch>; @@ -766,7 +767,7 @@ public: [[nodiscard]] u64 GetCounter() const { u64 value; - Check(dld->vkGetSemaphoreCounterValueKHR(owner, handle, &value)); + Check(dld->vkGetSemaphoreCounterValue(owner, handle, &value)); return value; } @@ -778,15 +779,15 @@ public: * @return True on successful wait, false on timeout */ bool Wait(u64 value, u64 timeout = std::numeric_limits<u64>::max()) const { - const VkSemaphoreWaitInfoKHR wait_info{ - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO_KHR, + const VkSemaphoreWaitInfo wait_info{ + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO, .pNext = nullptr, .flags = 0, .semaphoreCount = 1, .pSemaphores = &handle, .pValues = &value, }; - const VkResult result = dld->vkWaitSemaphoresKHR(owner, &wait_info, timeout); + const VkResult result = dld->vkWaitSemaphores(owner, &wait_info, timeout); switch (result) { case VK_SUCCESS: return true; @@ -840,8 +841,8 @@ public: CommandPool CreateCommandPool(const VkCommandPoolCreateInfo& ci) const; - DescriptorUpdateTemplateKHR CreateDescriptorUpdateTemplateKHR( - const VkDescriptorUpdateTemplateCreateInfoKHR& ci) const; + DescriptorUpdateTemplate CreateDescriptorUpdateTemplate( + const VkDescriptorUpdateTemplateCreateInfo& ci) const; QueryPool CreateQueryPool(const VkQueryPoolCreateInfo& ci) const; @@ -869,9 +870,9 @@ public: void UpdateDescriptorSets(Span<VkWriteDescriptorSet> writes, Span<VkCopyDescriptorSet> copies) const noexcept; - void UpdateDescriptorSet(VkDescriptorSet set, VkDescriptorUpdateTemplateKHR update_template, + void UpdateDescriptorSet(VkDescriptorSet set, VkDescriptorUpdateTemplate update_template, const void* data) const noexcept { - dld->vkUpdateDescriptorSetWithTemplateKHR(handle, set, update_template, data); + dld->vkUpdateDescriptorSetWithTemplate(handle, set, update_template, data); } VkResult AcquireNextImageKHR(VkSwapchainKHR swapchain, u64 timeout, VkSemaphore semaphore, @@ -884,8 +885,8 @@ public: return dld->vkDeviceWaitIdle(handle); } - void ResetQueryPoolEXT(VkQueryPool query_pool, u32 first, u32 count) const noexcept { - dld->vkResetQueryPoolEXT(handle, query_pool, first, count); + void ResetQueryPool(VkQueryPool query_pool, u32 first, u32 count) const noexcept { + dld->vkResetQueryPool(handle, query_pool, first, count); } VkResult GetQueryResults(VkQueryPool query_pool, u32 first, u32 count, std::size_t data_size, @@ -910,11 +911,11 @@ public: VkPhysicalDeviceProperties GetProperties() const noexcept; - void GetProperties2KHR(VkPhysicalDeviceProperties2KHR&) const noexcept; + void GetProperties2(VkPhysicalDeviceProperties2&) const noexcept; VkPhysicalDeviceFeatures GetFeatures() const noexcept; - void GetFeatures2KHR(VkPhysicalDeviceFeatures2KHR&) const noexcept; + void GetFeatures2(VkPhysicalDeviceFeatures2&) const noexcept; VkFormatProperties GetFormatProperties(VkFormat) const noexcept; @@ -922,6 +923,8 @@ public: std::vector<VkQueueFamilyProperties> GetQueueFamilyProperties() const; + std::vector<VkPhysicalDeviceToolProperties> GetPhysicalDeviceToolProperties() const; + bool GetSurfaceSupportKHR(u32 queue_family_index, VkSurfaceKHR) const; VkSurfaceCapabilitiesKHR GetSurfaceCapabilitiesKHR(VkSurfaceKHR) const; @@ -980,7 +983,7 @@ public: dynamic_offsets.size(), dynamic_offsets.data()); } - void PushDescriptorSetWithTemplateKHR(VkDescriptorUpdateTemplateKHR update_template, + void PushDescriptorSetWithTemplateKHR(VkDescriptorUpdateTemplate update_template, VkPipelineLayout layout, u32 set, const void* data) const noexcept { dld->vkCmdPushDescriptorSetWithTemplateKHR(handle, update_template, layout, set, data); diff --git a/src/web_service/CMakeLists.txt b/src/web_service/CMakeLists.txt index 3f75d97d1..02582aa04 100644 --- a/src/web_service/CMakeLists.txt +++ b/src/web_service/CMakeLists.txt @@ -4,6 +4,7 @@ add_library(web_service STATIC announce_room_json.cpp announce_room_json.h + precompiled_headers.h telemetry_json.cpp telemetry_json.h verify_login.cpp @@ -16,4 +17,8 @@ add_library(web_service STATIC ) create_target_directory_groups(web_service) -target_link_libraries(web_service PRIVATE common network nlohmann_json::nlohmann_json httplib cpp-jwt) +target_link_libraries(web_service PRIVATE common network nlohmann_json::nlohmann_json httplib::httplib cpp-jwt::cpp-jwt) + +if (YUZU_USE_PRECOMPILED_HEADERS) + target_precompile_headers(web_service PRIVATE precompiled_headers.h) +endif() diff --git a/src/web_service/precompiled_headers.h b/src/web_service/precompiled_headers.h new file mode 100644 index 000000000..aabae730b --- /dev/null +++ b/src/web_service/precompiled_headers.h @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_precompiled_headers.h" diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 060de0259..d23eb2907 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -88,6 +88,9 @@ add_executable(yuzu configuration/configure_input_advanced.cpp configuration/configure_input_advanced.h configuration/configure_input_advanced.ui + configuration/configure_input_per_game.cpp + configuration/configure_input_per_game.h + configuration/configure_input_per_game.ui configuration/configure_input_player.cpp configuration/configure_input_player.h configuration/configure_input_player.ui @@ -186,6 +189,7 @@ add_executable(yuzu multiplayer/state.cpp multiplayer/state.h multiplayer/validation.h + precompiled_headers.h startup_checks.cpp startup_checks.h uisettings.cpp @@ -314,7 +318,7 @@ target_link_libraries(yuzu PRIVATE common core input_common network video_core) target_link_libraries(yuzu PRIVATE Boost::boost glad Qt${QT_MAJOR_VERSION}::Widgets) target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) -target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include) +target_link_libraries(yuzu PRIVATE Vulkan::Headers) if (NOT WIN32) target_include_directories(yuzu PRIVATE ${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS}) endif() @@ -350,7 +354,7 @@ if (USE_DISCORD_PRESENCE) discord_impl.cpp discord_impl.h ) - target_link_libraries(yuzu PRIVATE discord-rpc) + target_link_libraries(yuzu PRIVATE DiscordRPC::discord-rpc) target_compile_definitions(yuzu PRIVATE -DUSE_DISCORD_PRESENCE) endif() @@ -387,11 +391,7 @@ if (YUZU_USE_BUNDLED_QT AND QT_VERSION VERSION_LESS 6) endif() if (ENABLE_SDL2) - if (YUZU_USE_EXTERNAL_SDL2) - target_link_libraries(yuzu PRIVATE SDL2-static) - else() - target_link_libraries(yuzu PRIVATE SDL2) - endif() + target_link_libraries(yuzu PRIVATE SDL2::SDL2) target_compile_definitions(yuzu PRIVATE HAVE_SDL2) endif() @@ -407,5 +407,9 @@ if (NOT APPLE) endif() if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) - target_link_libraries(yuzu PRIVATE dynarmic) + target_link_libraries(yuzu PRIVATE dynarmic::dynarmic) +endif() + +if (YUZU_USE_PRECOMPILED_HEADERS) + target_precompile_headers(yuzu PRIVATE precompiled_headers.h) endif() diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp index 12efdc216..c30b54499 100644 --- a/src/yuzu/applets/qt_controller.cpp +++ b/src/yuzu/applets/qt_controller.cpp @@ -685,7 +685,7 @@ QtControllerSelector::QtControllerSelector(GMainWindow& parent) { QtControllerSelector::~QtControllerSelector() = default; void QtControllerSelector::ReconfigureControllers( - std::function<void()> callback_, const Core::Frontend::ControllerParameters& parameters) const { + ReconfigureCallback callback_, const Core::Frontend::ControllerParameters& parameters) const { callback = std::move(callback_); emit MainWindowReconfigureControllers(parameters); } diff --git a/src/yuzu/applets/qt_controller.h b/src/yuzu/applets/qt_controller.h index cf948d2b5..16e99f507 100644 --- a/src/yuzu/applets/qt_controller.h +++ b/src/yuzu/applets/qt_controller.h @@ -157,7 +157,7 @@ public: ~QtControllerSelector() override; void ReconfigureControllers( - std::function<void()> callback_, + ReconfigureCallback callback_, const Core::Frontend::ControllerParameters& parameters) const override; signals: @@ -167,5 +167,5 @@ signals: private: void MainWindowReconfigureFinished(); - mutable std::function<void()> callback; + mutable ReconfigureCallback callback; }; diff --git a/src/yuzu/applets/qt_error.cpp b/src/yuzu/applets/qt_error.cpp index 367d5352d..e0190a979 100644 --- a/src/yuzu/applets/qt_error.cpp +++ b/src/yuzu/applets/qt_error.cpp @@ -14,7 +14,7 @@ QtErrorDisplay::QtErrorDisplay(GMainWindow& parent) { QtErrorDisplay::~QtErrorDisplay() = default; -void QtErrorDisplay::ShowError(Result error, std::function<void()> finished) const { +void QtErrorDisplay::ShowError(Result error, FinishedCallback finished) const { callback = std::move(finished); emit MainWindowDisplayError( tr("Error Code: %1-%2 (0x%3)") @@ -25,7 +25,7 @@ void QtErrorDisplay::ShowError(Result error, std::function<void()> finished) con } void QtErrorDisplay::ShowErrorWithTimestamp(Result error, std::chrono::seconds time, - std::function<void()> finished) const { + FinishedCallback finished) const { callback = std::move(finished); const QDateTime date_time = QDateTime::fromSecsSinceEpoch(time.count()); @@ -42,7 +42,7 @@ void QtErrorDisplay::ShowErrorWithTimestamp(Result error, std::chrono::seconds t void QtErrorDisplay::ShowCustomErrorText(Result error, std::string dialog_text, std::string fullscreen_text, - std::function<void()> finished) const { + FinishedCallback finished) const { callback = std::move(finished); emit MainWindowDisplayError( tr("Error Code: %1-%2 (0x%3)") diff --git a/src/yuzu/applets/qt_error.h b/src/yuzu/applets/qt_error.h index eb4107c7e..e4e174721 100644 --- a/src/yuzu/applets/qt_error.h +++ b/src/yuzu/applets/qt_error.h @@ -16,11 +16,11 @@ public: explicit QtErrorDisplay(GMainWindow& parent); ~QtErrorDisplay() override; - void ShowError(Result error, std::function<void()> finished) const override; + void ShowError(Result error, FinishedCallback finished) const override; void ShowErrorWithTimestamp(Result error, std::chrono::seconds time, - std::function<void()> finished) const override; + FinishedCallback finished) const override; void ShowCustomErrorText(Result error, std::string dialog_text, std::string fullscreen_text, - std::function<void()> finished) const override; + FinishedCallback finished) const override; signals: void MainWindowDisplayError(QString error_code, QString error_text) const; @@ -28,5 +28,5 @@ signals: private: void MainWindowFinishedError(); - mutable std::function<void()> callback; + mutable FinishedCallback callback; }; diff --git a/src/yuzu/applets/qt_profile_select.cpp b/src/yuzu/applets/qt_profile_select.cpp index c8bcfb223..4145c5299 100644 --- a/src/yuzu/applets/qt_profile_select.cpp +++ b/src/yuzu/applets/qt_profile_select.cpp @@ -163,8 +163,7 @@ QtProfileSelector::QtProfileSelector(GMainWindow& parent) { QtProfileSelector::~QtProfileSelector() = default; -void QtProfileSelector::SelectProfile( - std::function<void(std::optional<Common::UUID>)> callback_) const { +void QtProfileSelector::SelectProfile(SelectProfileCallback callback_) const { callback = std::move(callback_); emit MainWindowSelectProfile(); } diff --git a/src/yuzu/applets/qt_profile_select.h b/src/yuzu/applets/qt_profile_select.h index 124f2cdbd..637a3bda2 100644 --- a/src/yuzu/applets/qt_profile_select.h +++ b/src/yuzu/applets/qt_profile_select.h @@ -65,7 +65,7 @@ public: explicit QtProfileSelector(GMainWindow& parent); ~QtProfileSelector() override; - void SelectProfile(std::function<void(std::optional<Common::UUID>)> callback_) const override; + void SelectProfile(SelectProfileCallback callback_) const override; signals: void MainWindowSelectProfile() const; @@ -73,5 +73,5 @@ signals: private: void MainWindowFinishedSelection(std::optional<Common::UUID> uuid); - mutable std::function<void(std::optional<Common::UUID>)> callback; + mutable SelectProfileCallback callback; }; diff --git a/src/yuzu/applets/qt_software_keyboard.cpp b/src/yuzu/applets/qt_software_keyboard.cpp index e60506197..734b0ea40 100644 --- a/src/yuzu/applets/qt_software_keyboard.cpp +++ b/src/yuzu/applets/qt_software_keyboard.cpp @@ -1566,10 +1566,7 @@ QtSoftwareKeyboard::~QtSoftwareKeyboard() = default; void QtSoftwareKeyboard::InitializeKeyboard( bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters, - std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)> - submit_normal_callback_, - std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)> - submit_inline_callback_) { + SubmitNormalCallback submit_normal_callback_, SubmitInlineCallback submit_inline_callback_) { if (is_inline) { submit_inline_callback = std::move(submit_inline_callback_); } else { diff --git a/src/yuzu/applets/qt_software_keyboard.h b/src/yuzu/applets/qt_software_keyboard.h index 35d4ee2ef..30ac8ecf6 100644 --- a/src/yuzu/applets/qt_software_keyboard.h +++ b/src/yuzu/applets/qt_software_keyboard.h @@ -233,12 +233,10 @@ public: explicit QtSoftwareKeyboard(GMainWindow& parent); ~QtSoftwareKeyboard() override; - void InitializeKeyboard( - bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters, - std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)> - submit_normal_callback_, - std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)> - submit_inline_callback_) override; + void InitializeKeyboard(bool is_inline, + Core::Frontend::KeyboardInitializeParameters initialize_parameters, + SubmitNormalCallback submit_normal_callback_, + SubmitInlineCallback submit_inline_callback_) override; void ShowNormalKeyboard() const override; @@ -279,8 +277,6 @@ private: void SubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) const; - mutable std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)> - submit_normal_callback; - mutable std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)> - submit_inline_callback; + mutable SubmitNormalCallback submit_normal_callback; + mutable SubmitInlineCallback submit_inline_callback; }; diff --git a/src/yuzu/applets/qt_web_browser.cpp b/src/yuzu/applets/qt_web_browser.cpp index 89bd482e0..0a5912326 100644 --- a/src/yuzu/applets/qt_web_browser.cpp +++ b/src/yuzu/applets/qt_web_browser.cpp @@ -401,9 +401,9 @@ QtWebBrowser::QtWebBrowser(GMainWindow& main_window) { QtWebBrowser::~QtWebBrowser() = default; -void QtWebBrowser::OpenLocalWebPage( - const std::string& local_url, std::function<void()> extract_romfs_callback_, - std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback_) const { +void QtWebBrowser::OpenLocalWebPage(const std::string& local_url, + ExtractROMFSCallback extract_romfs_callback_, + OpenWebPageCallback callback_) const { extract_romfs_callback = std::move(extract_romfs_callback_); callback = std::move(callback_); @@ -416,9 +416,8 @@ void QtWebBrowser::OpenLocalWebPage( } } -void QtWebBrowser::OpenExternalWebPage( - const std::string& external_url, - std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback_) const { +void QtWebBrowser::OpenExternalWebPage(const std::string& external_url, + OpenWebPageCallback callback_) const { callback = std::move(callback_); const auto index = external_url.find('?'); diff --git a/src/yuzu/applets/qt_web_browser.h b/src/yuzu/applets/qt_web_browser.h index 043800853..e8fe511ed 100644 --- a/src/yuzu/applets/qt_web_browser.h +++ b/src/yuzu/applets/qt_web_browser.h @@ -197,13 +197,11 @@ public: ~QtWebBrowser() override; void OpenLocalWebPage(const std::string& local_url, - std::function<void()> extract_romfs_callback_, - std::function<void(Service::AM::Applets::WebExitReason, std::string)> - callback_) const override; + ExtractROMFSCallback extract_romfs_callback_, + OpenWebPageCallback callback_) const override; void OpenExternalWebPage(const std::string& external_url, - std::function<void(Service::AM::Applets::WebExitReason, std::string)> - callback_) const override; + OpenWebPageCallback callback_) const override; signals: void MainWindowOpenWebPage(const std::string& main_url, const std::string& additional_args, @@ -215,7 +213,6 @@ private: void MainWindowWebBrowserClosed(Service::AM::Applets::WebExitReason exit_reason, std::string last_url); - mutable std::function<void()> extract_romfs_callback; - - mutable std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback; + mutable ExtractROMFSCallback extract_romfs_callback; + mutable OpenWebPageCallback callback; }; diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index c934069dd..5b5b6fed8 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -118,7 +118,7 @@ void EmuThread::run() { } } else { std::unique_lock lock{running_mutex}; - running_cv.wait(lock, stop_token, [this] { return IsRunning(); }); + Common::CondvarWait(running_cv, lock, stop_token, [&] { return IsRunning(); }); } } @@ -237,8 +237,7 @@ private: GRenderWindow* render_window; }; -class OpenGLRenderWidget : public RenderWidget { -public: +struct OpenGLRenderWidget : public RenderWidget { explicit OpenGLRenderWidget(GRenderWindow* parent) : RenderWidget(parent) { windowHandle()->setSurfaceType(QWindow::OpenGLSurface); } @@ -251,13 +250,16 @@ private: std::unique_ptr<Core::Frontend::GraphicsContext> context; }; -class VulkanRenderWidget : public RenderWidget { -public: +struct VulkanRenderWidget : public RenderWidget { explicit VulkanRenderWidget(GRenderWindow* parent) : RenderWidget(parent) { windowHandle()->setSurfaceType(QWindow::VulkanSurface); } }; +struct NullRenderWidget : public RenderWidget { + explicit NullRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {} +}; + static Core::Frontend::WindowSystemType GetWindowSystemType() { // Determine WSI type based on Qt platform. QString platform_name = QGuiApplication::platformName(); @@ -267,6 +269,10 @@ static Core::Frontend::WindowSystemType GetWindowSystemType() { return Core::Frontend::WindowSystemType::X11; else if (platform_name == QStringLiteral("wayland")) return Core::Frontend::WindowSystemType::Wayland; + else if (platform_name == QStringLiteral("cocoa")) + return Core::Frontend::WindowSystemType::Cocoa; + else if (platform_name == QStringLiteral("android")) + return Core::Frontend::WindowSystemType::Android; LOG_CRITICAL(Frontend, "Unknown Qt platform!"); return Core::Frontend::WindowSystemType::Windows; @@ -874,6 +880,9 @@ bool GRenderWindow::InitRenderTarget() { return false; } break; + case Settings::RendererBackend::Null: + InitializeNull(); + break; } // Update the Window System information with the new render target @@ -970,6 +979,11 @@ bool GRenderWindow::InitializeVulkan() { return true; } +void GRenderWindow::InitializeNull() { + child_widget = new NullRenderWidget(this); + main_context = std::make_unique<DummyContext>(); +} + bool GRenderWindow::LoadOpenGL() { auto context = CreateSharedContext(); auto scope = context->Acquire(); diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 4a01481cd..f4deae4ee 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -14,6 +14,7 @@ #include <QTouchEvent> #include <QWidget> +#include "common/polyfill_thread.h" #include "common/thread.h" #include "core/frontend/emu_window.h" @@ -218,6 +219,7 @@ private: bool InitializeOpenGL(); bool InitializeVulkan(); + void InitializeNull(); bool LoadOpenGL(); QStringList GetUnsupportedGLExtensions() const; diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 750285478..722fc708e 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -124,6 +124,10 @@ void Config::Initialize(const std::string& config_name) { } } +bool Config::IsCustomConfig() { + return type == ConfigType::PerGameConfig; +} + /* {Read,Write}BasicSetting and WriteGlobalSetting templates must be defined here before their * usages later in this file. This allows explicit definition of some types that don't work * nicely with the general version. @@ -194,8 +198,20 @@ void Config::ReadPlayerValue(std::size_t player_index) { }(); auto& player = Settings::values.players.GetValue()[player_index]; + if (IsCustomConfig()) { + const auto profile_name = + qt_config->value(QStringLiteral("%1profile_name").arg(player_prefix), QString{}) + .toString() + .toStdString(); + if (profile_name.empty()) { + // Use the global input config + player = Settings::values.players.GetValue(true)[player_index]; + return; + } + player.profile_name = profile_name; + } - if (player_prefix.isEmpty()) { + if (player_prefix.isEmpty() && Settings::IsConfiguringGlobal()) { const auto controller = static_cast<Settings::ControllerType>( qt_config ->value(QStringLiteral("%1type").arg(player_prefix), @@ -388,9 +404,26 @@ void Config::ReadAudioValues() { void Config::ReadControlValues() { qt_config->beginGroup(QStringLiteral("Controls")); + Settings::values.players.SetGlobal(!IsCustomConfig()); for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { ReadPlayerValue(p); } + ReadGlobalSetting(Settings::values.use_docked_mode); + + // Disable docked mode if handheld is selected + const auto controller_type = Settings::values.players.GetValue()[0].controller_type; + if (controller_type == Settings::ControllerType::Handheld) { + Settings::values.use_docked_mode.SetGlobal(!IsCustomConfig()); + Settings::values.use_docked_mode.SetValue(false); + } + + ReadGlobalSetting(Settings::values.vibration_enabled); + ReadGlobalSetting(Settings::values.enable_accurate_vibrations); + ReadGlobalSetting(Settings::values.motion_enabled); + if (IsCustomConfig()) { + qt_config->endGroup(); + return; + } ReadDebugValues(); ReadKeyboardValues(); ReadMouseValues(); @@ -412,18 +445,6 @@ void Config::ReadControlValues() { ReadBasicSetting(Settings::values.tas_loop); ReadBasicSetting(Settings::values.pause_tas_on_load); - ReadGlobalSetting(Settings::values.use_docked_mode); - - // Disable docked mode if handheld is selected - const auto controller_type = Settings::values.players.GetValue()[0].controller_type; - if (controller_type == Settings::ControllerType::Handheld) { - Settings::values.use_docked_mode.SetValue(false); - } - - ReadGlobalSetting(Settings::values.vibration_enabled); - ReadGlobalSetting(Settings::values.enable_accurate_vibrations); - ReadGlobalSetting(Settings::values.motion_enabled); - ReadBasicSetting(Settings::values.controller_navigation); qt_config->endGroup(); @@ -906,7 +927,6 @@ void Config::ReadMultiplayerValues() { void Config::ReadValues() { if (global) { - ReadControlValues(); ReadDataStorageValues(); ReadDebuggingValues(); ReadDisabledAddOnValues(); @@ -915,6 +935,7 @@ void Config::ReadValues() { ReadWebServiceValues(); ReadMiscellaneousValues(); } + ReadControlValues(); ReadCoreValues(); ReadCpuValues(); ReadRendererValues(); @@ -933,12 +954,20 @@ void Config::SavePlayerValue(std::size_t player_index) { }(); const auto& player = Settings::values.players.GetValue()[player_index]; + if (IsCustomConfig()) { + if (player.profile_name.empty()) { + // No custom profile selected + return; + } + WriteSetting(QStringLiteral("%1profile_name").arg(player_prefix), + QString::fromStdString(player.profile_name), QString{}); + } WriteSetting(QStringLiteral("%1type").arg(player_prefix), static_cast<u8>(player.controller_type), static_cast<u8>(Settings::ControllerType::ProController)); - if (!player_prefix.isEmpty()) { + if (!player_prefix.isEmpty() || !Settings::IsConfiguringGlobal()) { WriteSetting(QStringLiteral("%1connected").arg(player_prefix), player.connected, player_index == 0); WriteSetting(QStringLiteral("%1vibration_enabled").arg(player_prefix), @@ -1056,7 +1085,6 @@ void Config::SaveIrCameraValues() { void Config::SaveValues() { if (global) { - SaveControlValues(); SaveDataStorageValues(); SaveDebuggingValues(); SaveDisabledAddOnValues(); @@ -1065,6 +1093,7 @@ void Config::SaveValues() { SaveWebServiceValues(); SaveMiscellaneousValues(); } + SaveControlValues(); SaveCoreValues(); SaveCpuValues(); SaveRendererValues(); @@ -1089,9 +1118,14 @@ void Config::SaveAudioValues() { void Config::SaveControlValues() { qt_config->beginGroup(QStringLiteral("Controls")); + Settings::values.players.SetGlobal(!IsCustomConfig()); for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { SavePlayerValue(p); } + if (IsCustomConfig()) { + qt_config->endGroup(); + return; + } SaveDebugValues(); SaveMouseValues(); SaveTouchscreenValues(); @@ -1581,6 +1615,13 @@ void Config::SaveControlPlayerValue(std::size_t player_index) { qt_config->endGroup(); } +void Config::ClearControlPlayerValues() { + qt_config->beginGroup(QStringLiteral("Controls")); + // If key is an empty string, all keys in the current group() are removed. + qt_config->remove(QString{}); + qt_config->endGroup(); +} + const std::string& Config::GetConfigFilePath() const { return qt_config_loc; } diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index 06fa7d2d0..7d26e9ab6 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -34,6 +34,7 @@ public: void ReadControlPlayerValue(std::size_t player_index); void SaveControlPlayerValue(std::size_t player_index); + void ClearControlPlayerValues(); const std::string& GetConfigFilePath() const; @@ -58,6 +59,7 @@ public: private: void Initialize(const std::string& config_name); + bool IsCustomConfig(); void ReadValues(); void ReadPlayerValue(std::size_t player_index); diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index f1385e972..e9388daad 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp @@ -31,7 +31,7 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* paren ui->backend->addItem(QStringLiteral("GLSL")); ui->backend->addItem(tr("GLASM (Assembly Shaders, NVIDIA Only)")); - ui->backend->addItem(QStringLiteral("SPIR-V (Experimental, Mesa Only)")); + ui->backend->addItem(tr("SPIR-V (Experimental, Mesa Only)")); SetupPerGameUI(); @@ -260,6 +260,7 @@ void ConfigureGraphics::ApplyConfiguration() { Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend()); switch (GetCurrentGraphicsBackend()) { case Settings::RendererBackend::OpenGL: + case Settings::RendererBackend::Null: Settings::values.shader_backend.SetGlobal(false); Settings::values.vulkan_device.SetGlobal(true); Settings::values.shader_backend.SetValue(shader_backend); @@ -348,6 +349,10 @@ void ConfigureGraphics::UpdateAPILayout() { ui->device_widget->setVisible(true); ui->backend_widget->setVisible(false); break; + case Settings::RendererBackend::Null: + ui->device_widget->setVisible(false); + ui->backend_widget->setVisible(false); + break; } } @@ -360,7 +365,7 @@ void ConfigureGraphics::RetrieveVulkanDevices() try { vk::InstanceDispatch dld; const Common::DynamicLibrary library = OpenLibrary(); - const vk::Instance instance = CreateInstance(library, dld, VK_API_VERSION_1_0); + const vk::Instance instance = CreateInstance(library, dld, VK_API_VERSION_1_1); const std::vector<VkPhysicalDevice> physical_devices = instance.EnumeratePhysicalDevices(); vulkan_devices.clear(); diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index 37271f956..f78396690 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui @@ -139,6 +139,11 @@ <string notr="true">Vulkan</string> </property> </item> + <item> + <property name="text"> + <string>None</string> + </property> + </item> </widget> </item> </layout> diff --git a/src/yuzu/configuration/configure_input_per_game.cpp b/src/yuzu/configuration/configure_input_per_game.cpp new file mode 100644 index 000000000..78e65d468 --- /dev/null +++ b/src/yuzu/configuration/configure_input_per_game.cpp @@ -0,0 +1,115 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/settings.h" +#include "core/core.h" +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" +#include "ui_configure_input_per_game.h" +#include "yuzu/configuration/config.h" +#include "yuzu/configuration/configure_input_per_game.h" +#include "yuzu/configuration/input_profiles.h" + +ConfigureInputPerGame::ConfigureInputPerGame(Core::System& system_, Config* config_, + QWidget* parent) + : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPerGame>()), + profiles(std::make_unique<InputProfiles>()), system{system_}, config{config_} { + ui->setupUi(this); + const std::array labels = { + ui->label_player_1, ui->label_player_2, ui->label_player_3, ui->label_player_4, + ui->label_player_5, ui->label_player_6, ui->label_player_7, ui->label_player_8, + }; + profile_comboboxes = { + ui->profile_player_1, ui->profile_player_2, ui->profile_player_3, ui->profile_player_4, + ui->profile_player_5, ui->profile_player_6, ui->profile_player_7, ui->profile_player_8, + }; + + Settings::values.players.SetGlobal(false); + + const auto& profile_names = profiles->GetInputProfileNames(); + const auto populate_profiles = [this, &profile_names](size_t player_index) { + const auto previous_profile = + Settings::values.players.GetValue()[player_index].profile_name; + + auto* const player_combobox = profile_comboboxes[player_index]; + player_combobox->addItem(tr("Use global input configuration")); + + for (size_t index = 0; index < profile_names.size(); ++index) { + const auto& profile_name = profile_names[index]; + player_combobox->addItem(QString::fromStdString(profile_name)); + if (profile_name == previous_profile) { + // offset by 1 since the first element is the global config + player_combobox->setCurrentIndex(static_cast<int>(index + 1)); + } + } + }; + for (size_t index = 0; index < profile_comboboxes.size(); ++index) { + labels[index]->setText(tr("Player %1 profile").arg(index + 1)); + populate_profiles(index); + } + + LoadConfiguration(); +} + +void ConfigureInputPerGame::ApplyConfiguration() { + LoadConfiguration(); + SaveConfiguration(); +} + +void ConfigureInputPerGame::LoadConfiguration() { + static constexpr size_t HANDHELD_INDEX = 8; + + auto& hid_core = system.HIDCore(); + for (size_t player_index = 0; player_index < profile_comboboxes.size(); ++player_index) { + Settings::values.players.SetGlobal(false); + + auto* emulated_controller = hid_core.GetEmulatedControllerByIndex(player_index); + auto* const player_combobox = profile_comboboxes[player_index]; + + const auto selection_index = player_combobox->currentIndex(); + if (selection_index == 0) { + Settings::values.players.GetValue()[player_index].profile_name = ""; + if (player_index == 0) { + Settings::values.players.GetValue()[HANDHELD_INDEX] = {}; + } + Settings::values.players.SetGlobal(true); + emulated_controller->ReloadFromSettings(); + continue; + } + const auto profile_name = player_combobox->itemText(selection_index).toStdString(); + if (profile_name.empty()) { + continue; + } + auto& player = Settings::values.players.GetValue()[player_index]; + player.profile_name = profile_name; + // Read from the profile into the custom player settings + profiles->LoadProfile(profile_name, player_index); + // Make sure the controller is connected + player.connected = true; + + emulated_controller->ReloadFromSettings(); + + if (player_index > 0) { + continue; + } + // Handle Handheld cases + auto& handheld_player = Settings::values.players.GetValue()[HANDHELD_INDEX]; + auto* handheld_controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld); + if (player.controller_type == Settings::ControllerType::Handheld) { + handheld_player = player; + } else { + handheld_player = {}; + } + handheld_controller->ReloadFromSettings(); + } +} + +void ConfigureInputPerGame::SaveConfiguration() { + Settings::values.players.SetGlobal(false); + + // Clear all controls from the config in case the user reverted back to globals + config->ClearControlPlayerValues(); + for (size_t index = 0; index < Settings::values.players.GetValue().size(); ++index) { + config->SaveControlPlayerValue(index); + } +} diff --git a/src/yuzu/configuration/configure_input_per_game.h b/src/yuzu/configuration/configure_input_per_game.h new file mode 100644 index 000000000..660faf574 --- /dev/null +++ b/src/yuzu/configuration/configure_input_per_game.h @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <memory> + +#include <QWidget> + +#include "ui_configure_input_per_game.h" +#include "yuzu/configuration/input_profiles.h" + +class QComboBox; + +namespace Core { +class System; +} // namespace Core + +class Config; + +class ConfigureInputPerGame : public QWidget { + Q_OBJECT + +public: + explicit ConfigureInputPerGame(Core::System& system_, Config* config_, + QWidget* parent = nullptr); + + /// Load and Save configurations to settings file. + void ApplyConfiguration(); + +private: + /// Load configuration from settings file. + void LoadConfiguration(); + + /// Save configuration to settings file. + void SaveConfiguration(); + + std::unique_ptr<Ui::ConfigureInputPerGame> ui; + std::unique_ptr<InputProfiles> profiles; + + std::array<QComboBox*, 8> profile_comboboxes; + + Core::System& system; + Config* config; +}; diff --git a/src/yuzu/configuration/configure_input_per_game.ui b/src/yuzu/configuration/configure_input_per_game.ui new file mode 100644 index 000000000..fbd8eab1c --- /dev/null +++ b/src/yuzu/configuration/configure_input_per_game.ui @@ -0,0 +1,333 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ConfigureInputPerGame</class> + <widget class="QWidget" name="PerGameInput"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>541</width> + <height>759</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <property name="accessibleName"> + <string>Graphics</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_1"> + <item> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <property name="spacing"> + <number>0</number> + </property> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Input Profiles</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <item> + <widget class="QWidget" name="player_1" native="true"> + <layout class="QHBoxLayout" name="input_profile_layout_1"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="label_player_1"> + <property name="text"> + <string>Player 1 Profile</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="profile_player_1"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="player_2" native="true"> + <layout class="QHBoxLayout" name="input_profile_layout_2"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="label_player_2"> + <property name="text"> + <string>Player 2 Profile</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="profile_player_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="player_3" native="true"> + <layout class="QHBoxLayout" name="input_profile_layout_3"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="label_player_3"> + <property name="text"> + <string>Player 3 Profile</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="profile_player_3"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="player_4" native="true"> + <layout class="QHBoxLayout" name="input_profile_layout_4"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="label_player_4"> + <property name="text"> + <string>Player 4 Profile</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="profile_player_4"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="player_5" native="true"> + <layout class="QHBoxLayout" name="input_profile_layout_5"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="label_player_5"> + <property name="text"> + <string>Player 5 Profile</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="profile_player_5"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="player_6" native="true"> + <layout class="QHBoxLayout" name="input_profile_layout_6"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="label_player_6"> + <property name="text"> + <string>Player 6 Profile</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="profile_player_6"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="player_7" native="true"> + <layout class="QHBoxLayout" name="input_profile_layout_7"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="label_player_7"> + <property name="text"> + <string>Player 7 Profile</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="profile_player_7"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="player_8" native="true"> + <layout class="QHBoxLayout" name="input_profile_layout_8"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="label_player_8"> + <property name="text"> + <string>Player 8 Profile</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="profile_player_8"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 9e5a40fe7..b1575b0d3 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -855,8 +855,7 @@ void ConfigureInputPlayer::UpdateInputDeviceCombobox() { return; } - const auto devices = - emulated_controller->GetMappedDevices(Core::HID::EmulatedDeviceIndex::AllDevices); + const auto devices = emulated_controller->GetMappedDevices(); UpdateInputDevices(); if (devices.empty()) { @@ -1553,6 +1552,7 @@ void ConfigureInputPlayer::LoadProfile() { } void ConfigureInputPlayer::SaveProfile() { + static constexpr size_t HANDHELD_INDEX = 8; const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex()); if (profile_name.isEmpty()) { @@ -1561,7 +1561,12 @@ void ConfigureInputPlayer::SaveProfile() { ApplyConfiguration(); - if (!profiles->SaveProfile(profile_name.toStdString(), player_index)) { + // When we're in handheld mode, only the handheld emulated controller bindings are updated + const bool is_handheld = player_index == 0 && emulated_controller->GetNpadIdType() == + Core::HID::NpadIdType::Handheld; + const auto profile_player_index = is_handheld ? HANDHELD_INDEX : player_index; + + if (!profiles->SaveProfile(profile_name.toStdString(), profile_player_index)) { QMessageBox::critical(this, tr("Save Input Profile"), tr("Failed to save the input profile \"%1\"").arg(profile_name)); UpdateInputProfiles(); diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index 79434fdd8..26f60d121 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h @@ -38,7 +38,7 @@ enum class InputType; namespace Ui { class ConfigureInputPlayer; -} +} // namespace Ui namespace Core::HID { class HIDCore; diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp index c3cb8f61d..93db47cfd 100644 --- a/src/yuzu/configuration/configure_per_game.cpp +++ b/src/yuzu/configuration/configure_per_game.cpp @@ -28,7 +28,7 @@ #include "yuzu/configuration/configure_general.h" #include "yuzu/configuration/configure_graphics.h" #include "yuzu/configuration/configure_graphics_advanced.h" -#include "yuzu/configuration/configure_input.h" +#include "yuzu/configuration/configure_input_per_game.h" #include "yuzu/configuration/configure_per_game.h" #include "yuzu/configuration/configure_per_game_addons.h" #include "yuzu/configuration/configure_system.h" @@ -50,6 +50,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st general_tab = std::make_unique<ConfigureGeneral>(system_, this); graphics_tab = std::make_unique<ConfigureGraphics>(system_, this); graphics_advanced_tab = std::make_unique<ConfigureGraphicsAdvanced>(system_, this); + input_tab = std::make_unique<ConfigureInputPerGame>(system_, game_config.get(), this); system_tab = std::make_unique<ConfigureSystem>(system_, this); ui->setupUi(this); @@ -61,6 +62,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st ui->tabWidget->addTab(graphics_tab.get(), tr("Graphics")); ui->tabWidget->addTab(graphics_advanced_tab.get(), tr("Adv. Graphics")); ui->tabWidget->addTab(audio_tab.get(), tr("Audio")); + ui->tabWidget->addTab(input_tab.get(), tr("Input Profiles")); setFocusPolicy(Qt::ClickFocus); setWindowTitle(tr("Properties")); @@ -91,6 +93,7 @@ void ConfigurePerGame::ApplyConfiguration() { graphics_tab->ApplyConfiguration(); graphics_advanced_tab->ApplyConfiguration(); audio_tab->ApplyConfiguration(); + input_tab->ApplyConfiguration(); system.ApplySettings(); Settings::LogSettings(); diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h index 17a98a0f3..4ecc43541 100644 --- a/src/yuzu/configuration/configure_per_game.h +++ b/src/yuzu/configuration/configure_per_game.h @@ -16,12 +16,17 @@ namespace Core { class System; } +namespace InputCommon { +class InputSubsystem; +} + class ConfigurePerGameAddons; class ConfigureAudio; class ConfigureCpu; class ConfigureGeneral; class ConfigureGraphics; class ConfigureGraphicsAdvanced; +class ConfigureInputPerGame; class ConfigureSystem; class QGraphicsScene; @@ -72,5 +77,6 @@ private: std::unique_ptr<ConfigureGeneral> general_tab; std::unique_ptr<ConfigureGraphics> graphics_tab; std::unique_ptr<ConfigureGraphicsAdvanced> graphics_advanced_tab; + std::unique_ptr<ConfigureInputPerGame> input_tab; std::unique_ptr<ConfigureSystem> system_tab; }; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index c21153560..c0afb2e5f 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -126,6 +126,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "yuzu/compatibility_list.h" #include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_dialog.h" +#include "yuzu/configuration/configure_input_per_game.h" #include "yuzu/debugger/console.h" #include "yuzu/debugger/controller.h" #include "yuzu/debugger/profiler.h" @@ -1012,29 +1013,11 @@ void GMainWindow::InitializeWidgets() { renderer_status_button->setObjectName(QStringLiteral("RendererStatusBarButton")); renderer_status_button->setCheckable(true); renderer_status_button->setFocusPolicy(Qt::NoFocus); - connect(renderer_status_button, &QPushButton::toggled, [this](bool checked) { - renderer_status_button->setText(checked ? tr("VULKAN") : tr("OPENGL")); - }); - renderer_status_button->toggle(); - + connect(renderer_status_button, &QPushButton::clicked, this, &GMainWindow::OnToggleGraphicsAPI); + UpdateAPIText(); + renderer_status_button->setCheckable(true); renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::Vulkan); - connect(renderer_status_button, &QPushButton::clicked, [this] { - if (emulation_running) { - return; - } - if (renderer_status_button->isChecked()) { - Settings::values.renderer_backend.SetValue(Settings::RendererBackend::Vulkan); - } else { - Settings::values.renderer_backend.SetValue(Settings::RendererBackend::OpenGL); - if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { - Settings::values.scaling_filter.SetValue(Settings::ScalingFilter::NearestNeighbor); - UpdateFilterText(); - } - } - - system->ApplySettings(); - }); statusBar()->insertPermanentWidget(0, renderer_status_button); statusBar()->setVisible(true); @@ -1676,6 +1659,11 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t LOG_INFO(Frontend, "yuzu starting..."); StoreRecentFile(filename); // Put the filename on top of the list + // Save configurations + UpdateUISettings(); + game_list->SaveInterfaceLayout(); + config->Save(); + u64 title_id{0}; last_filename_booted = filename; @@ -1692,14 +1680,10 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t ? Common::FS::PathToUTF8String(file_path.filename()) : fmt::format("{:016X}", title_id); Config per_game_config(config_file_name, Config::ConfigType::PerGameConfig); + system->HIDCore().ReloadInputDevices(); system->ApplySettings(); } - // Save configurations - UpdateUISettings(); - game_list->SaveInterfaceLayout(); - config->Save(); - Settings::LogSettings(); if (UISettings::values.select_user_on_boot) { @@ -2820,6 +2804,7 @@ void GMainWindow::OnStopGame() { ShutdownGame(); Settings::RestoreGlobalState(system->IsPoweredOn()); + system->HIDCore().ReloadInputDevices(); UpdateStatusButtons(); } @@ -2850,6 +2835,7 @@ void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_tex } void GMainWindow::OnMenuReportCompatibility() { +#if defined(ARCHITECTURE_x86_64) && !defined(__APPLE__) const auto& caps = Common::GetCPUCaps(); const bool has_fma = caps.fma || caps.fma4; const auto processor_count = std::thread::hardware_concurrency(); @@ -2876,6 +2862,11 @@ void GMainWindow::OnMenuReportCompatibility() { "> " "Web.")); } +#else + QMessageBox::critical(this, tr("Hardware requirements not met"), + tr("Your system does not meet the recommended hardware requirements. " + "Compatibility reporting has been disabled.")); +#endif } void GMainWindow::OpenURL(const QUrl& url) { @@ -3253,6 +3244,18 @@ void GMainWindow::OnToggleAdaptingFilter() { UpdateFilterText(); } +void GMainWindow::OnToggleGraphicsAPI() { + auto api = Settings::values.renderer_backend.GetValue(); + if (api == Settings::RendererBackend::OpenGL) { + api = Settings::RendererBackend::Vulkan; + } else { + api = Settings::RendererBackend::OpenGL; + } + Settings::values.renderer_backend.SetValue(api); + renderer_status_button->setChecked(api == Settings::RendererBackend::Vulkan); + UpdateAPIText(); +} + void GMainWindow::OnConfigurePerGame() { const u64 title_id = system->GetCurrentProcessProgramID(); OpenPerGameConfiguration(title_id, current_game_path.toStdString()); @@ -3281,6 +3284,7 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file // 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); + system->HIDCore().ReloadInputDevices(); UISettings::values.configuration_applied = false; @@ -3573,6 +3577,21 @@ void GMainWindow::UpdateDockedButton() { dock_status_button->setText(is_docked ? tr("DOCKED") : tr("HANDHELD")); } +void GMainWindow::UpdateAPIText() { + const auto api = Settings::values.renderer_backend.GetValue(); + switch (api) { + case Settings::RendererBackend::OpenGL: + renderer_status_button->setText(tr("OPENGL")); + break; + case Settings::RendererBackend::Vulkan: + renderer_status_button->setText(tr("VULKAN")); + break; + case Settings::RendererBackend::Null: + renderer_status_button->setText(tr("NULL")); + break; + } +} + void GMainWindow::UpdateFilterText() { const auto filter = Settings::values.scaling_filter.GetValue(); switch (filter) { @@ -3618,6 +3637,7 @@ void GMainWindow::UpdateAAText() { void GMainWindow::UpdateStatusButtons() { renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::Vulkan); + UpdateAPIText(); UpdateGPUAccuracyButton(); UpdateDockedButton(); UpdateFilterText(); @@ -3748,6 +3768,7 @@ void GMainWindow::OnCoreError(Core::SystemResultStatus result, std::string detai ShutdownGame(); Settings::RestoreGlobalState(system->IsPoweredOn()); + system->HIDCore().ReloadInputDevices(); UpdateStatusButtons(); } } else { @@ -3899,18 +3920,19 @@ void GMainWindow::closeEvent(QCloseEvent* event) { // Unload controllers early controller_dialog->UnloadController(); game_list->UnloadController(); - system->HIDCore().UnloadInputDevices(); // Shutdown session if the emu thread is active... if (emu_thread != nullptr) { ShutdownGame(); Settings::RestoreGlobalState(system->IsPoweredOn()); + system->HIDCore().ReloadInputDevices(); UpdateStatusButtons(); } render_window->close(); multiplayer_state->Close(); + system->HIDCore().UnloadInputDevices(); system->GetRoomNetwork().Shutdown(); QWidget::closeEvent(event); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 4f9c3b450..62d629973 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -307,6 +307,7 @@ private slots: void OnTasStartStop(); void OnTasRecord(); void OnTasReset(); + void OnToggleGraphicsAPI(); void OnToggleDockedMode(); void OnToggleGpuAccuracy(); void OnToggleAdaptingFilter(); @@ -347,6 +348,7 @@ private: void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {}, std::string_view gpu_vendor = {}); void UpdateDockedButton(); + void UpdateAPIText(); void UpdateFilterText(); void UpdateAAText(); void UpdateStatusBar(); diff --git a/src/yuzu/multiplayer/chat_room.h b/src/yuzu/multiplayer/chat_room.h index 01c70fad0..dd71ea4cd 100644 --- a/src/yuzu/multiplayer/chat_room.h +++ b/src/yuzu/multiplayer/chat_room.h @@ -4,6 +4,7 @@ #pragma once #include <memory> +#include <unordered_map> #include <unordered_set> #include <QDialog> #include <QSortFilterProxyModel> diff --git a/src/yuzu/precompiled_headers.h b/src/yuzu/precompiled_headers.h new file mode 100644 index 000000000..aabae730b --- /dev/null +++ b/src/yuzu/precompiled_headers.h @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_precompiled_headers.h" diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp index ccdcf10fa..563818362 100644 --- a/src/yuzu/startup_checks.cpp +++ b/src/yuzu/startup_checks.cpp @@ -27,7 +27,7 @@ void CheckVulkan() { Vulkan::vk::InstanceDispatch dld; const Common::DynamicLibrary library = Vulkan::OpenLibrary(); const Vulkan::vk::Instance instance = - Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_0); + Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_1); } catch (const Vulkan::vk::Exception& exception) { fmt::print(stderr, "Failed to initialize Vulkan: {}\n", exception.what()); diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt index 7d8ca3d8a..f6eeb9d8d 100644 --- a/src/yuzu_cmd/CMakeLists.txt +++ b/src/yuzu_cmd/CMakeLists.txt @@ -22,8 +22,11 @@ add_executable(yuzu-cmd emu_window/emu_window_sdl2.h emu_window/emu_window_sdl2_gl.cpp emu_window/emu_window_sdl2_gl.h + emu_window/emu_window_sdl2_null.cpp + emu_window/emu_window_sdl2_null.h emu_window/emu_window_sdl2_vk.cpp emu_window/emu_window_sdl2_vk.h + precompiled_headers.h yuzu.cpp yuzu.rc ) @@ -31,21 +34,16 @@ add_executable(yuzu-cmd create_target_directory_groups(yuzu-cmd) target_link_libraries(yuzu-cmd PRIVATE common core input_common) -target_link_libraries(yuzu-cmd PRIVATE inih glad) +target_link_libraries(yuzu-cmd PRIVATE inih::INIReader glad) if (MSVC) target_link_libraries(yuzu-cmd PRIVATE getopt) endif() -target_link_libraries(yuzu-cmd PRIVATE ${PLATFORM_LIBRARIES} SDL2 Threads::Threads) +target_link_libraries(yuzu-cmd PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) create_resource("../../dist/yuzu.bmp" "yuzu_cmd/yuzu_icon.h" "yuzu_icon") target_include_directories(yuzu-cmd PRIVATE ${RESOURCES_DIR}) -target_include_directories(yuzu-cmd PRIVATE ../../externals/Vulkan-Headers/include) - -if (YUZU_USE_EXTERNAL_SDL2) - target_compile_definitions(yuzu-cmd PRIVATE -DYUZU_USE_EXTERNAL_SDL2) - target_include_directories(yuzu-cmd PRIVATE ${PROJECT_BINARY_DIR}/externals/SDL/include) -endif() +target_link_libraries(yuzu-cmd PRIVATE SDL2::SDL2 Vulkan::Headers) if(UNIX AND NOT APPLE) install(TARGETS yuzu-cmd) @@ -55,3 +53,7 @@ if (MSVC) include(CopyYuzuSDLDeps) copy_yuzu_SDL_deps(yuzu-cmd) endif() + +if (YUZU_USE_PRECOMPILED_HEADERS) + target_precompile_headers(yuzu-cmd PRIVATE precompiled_headers.h) +endif() diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 767b0d0e3..de9b220da 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -15,7 +15,7 @@ #pragma clang diagnostic pop #endif -#include <inih/cpp/INIReader.h> +#include <INIReader.h> #include "common/fs/file.h" #include "common/fs/fs.h" #include "common/fs/path_util.h" diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h index 90bb0b415..25c23e2a5 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h @@ -89,3 +89,5 @@ protected: /// yuzu core instance Core::System& system; }; + +class DummyContext : public Core::Frontend::GraphicsContext {}; diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_null.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_null.cpp new file mode 100644 index 000000000..259192f3c --- /dev/null +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_null.cpp @@ -0,0 +1,51 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <cstdlib> +#include <memory> +#include <string> + +#include <fmt/format.h> + +#include "common/logging/log.h" +#include "common/scm_rev.h" +#include "video_core/renderer_null/renderer_null.h" +#include "yuzu_cmd/emu_window/emu_window_sdl2_null.h" + +#ifdef YUZU_USE_EXTERNAL_SDL2 +// Include this before SDL.h to prevent the external from including a dummy +#define USING_GENERATED_CONFIG_H +#include <SDL_config.h> +#endif + +#include <SDL.h> + +EmuWindow_SDL2_Null::EmuWindow_SDL2_Null(InputCommon::InputSubsystem* input_subsystem_, + Core::System& system_, bool fullscreen) + : EmuWindow_SDL2{input_subsystem_, system_} { + const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name, + Common::g_scm_branch, Common::g_scm_desc); + render_window = + SDL_CreateWindow(window_title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, + SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); + + SetWindowIcon(); + + if (fullscreen) { + Fullscreen(); + ShowCursor(false); + } + + OnResize(); + OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); + SDL_PumpEvents(); + LOG_INFO(Frontend, "yuzu Version: {} | {}-{} (Null)", Common::g_build_name, + Common::g_scm_branch, Common::g_scm_desc); +} + +EmuWindow_SDL2_Null::~EmuWindow_SDL2_Null() = default; + +std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_Null::CreateSharedContext() const { + return std::make_unique<DummyContext>(); +} diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_null.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_null.h new file mode 100644 index 000000000..35aee286d --- /dev/null +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_null.h @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <memory> + +#include "core/frontend/emu_window.h" +#include "yuzu_cmd/emu_window/emu_window_sdl2.h" + +namespace Core { +class System; +} + +namespace InputCommon { +class InputSubsystem; +} + +class EmuWindow_SDL2_Null final : public EmuWindow_SDL2 { +public: + explicit EmuWindow_SDL2_Null(InputCommon::InputSubsystem* input_subsystem_, + Core::System& system, bool fullscreen); + ~EmuWindow_SDL2_Null() override; + + std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; +}; diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp index 25948328c..9ed47d453 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp @@ -12,12 +12,6 @@ #include "video_core/renderer_vulkan/renderer_vulkan.h" #include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" -#ifdef YUZU_USE_EXTERNAL_SDL2 -// Include this before SDL.h to prevent the external from including a dummy -#define USING_GENERATED_CONFIG_H -#include <SDL_config.h> -#endif - #include <SDL.h> #include <SDL_syswm.h> @@ -51,11 +45,6 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste window_info.type = Core::Frontend::WindowSystemType::Windows; window_info.render_surface = reinterpret_cast<void*>(wm.info.win.window); break; -#else - case SDL_SYSWM_TYPE::SDL_SYSWM_WINDOWS: - LOG_CRITICAL(Frontend, "Window manager subsystem Windows not compiled"); - std::exit(EXIT_FAILURE); - break; #endif #ifdef SDL_VIDEO_DRIVER_X11 case SDL_SYSWM_TYPE::SDL_SYSWM_X11: @@ -63,11 +52,6 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste window_info.display_connection = wm.info.x11.display; window_info.render_surface = reinterpret_cast<void*>(wm.info.x11.window); break; -#else - case SDL_SYSWM_TYPE::SDL_SYSWM_X11: - LOG_CRITICAL(Frontend, "Window manager subsystem X11 not compiled"); - std::exit(EXIT_FAILURE); - break; #endif #ifdef SDL_VIDEO_DRIVER_WAYLAND case SDL_SYSWM_TYPE::SDL_SYSWM_WAYLAND: @@ -75,14 +59,21 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste window_info.display_connection = wm.info.wl.display; window_info.render_surface = wm.info.wl.surface; break; -#else - case SDL_SYSWM_TYPE::SDL_SYSWM_WAYLAND: - LOG_CRITICAL(Frontend, "Window manager subsystem Wayland not compiled"); - std::exit(EXIT_FAILURE); +#endif +#ifdef SDL_VIDEO_DRIVER_COCOA + case SDL_SYSWM_TYPE::SDL_SYSWM_COCOA: + window_info.type = Core::Frontend::WindowSystemType::Cocoa; + window_info.render_surface = SDL_Metal_CreateView(render_window); + break; +#endif +#ifdef SDL_VIDEO_DRIVER_ANDROID + case SDL_SYSWM_TYPE::SDL_SYSWM_ANDROID: + window_info.type = Core::Frontend::WindowSystemType::Android; + window_info.render_surface = reinterpret_cast<void*>(wm.info.android.window); break; #endif default: - LOG_CRITICAL(Frontend, "Window manager subsystem not implemented"); + LOG_CRITICAL(Frontend, "Window manager subsystem {} not implemented", wm.subsystem); std::exit(EXIT_FAILURE); break; } diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h index e39ad754d..9467d164a 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h @@ -24,5 +24,3 @@ public: std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; }; - -class DummyContext : public Core::Frontend::GraphicsContext {}; diff --git a/src/yuzu_cmd/precompiled_headers.h b/src/yuzu_cmd/precompiled_headers.h new file mode 100644 index 000000000..aabae730b --- /dev/null +++ b/src/yuzu_cmd/precompiled_headers.h @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_precompiled_headers.h" diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index dfe5a30ea..a80649703 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -34,6 +34,7 @@ #include "yuzu_cmd/config.h" #include "yuzu_cmd/emu_window/emu_window_sdl2.h" #include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" +#include "yuzu_cmd/emu_window/emu_window_sdl2_null.h" #include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" #ifdef _WIN32 @@ -317,6 +318,9 @@ int main(int argc, char** argv) { case Settings::RendererBackend::Vulkan: emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem, system, fullscreen); break; + case Settings::RendererBackend::Null: + emu_window = std::make_unique<EmuWindow_SDL2_Null>(&input_subsystem, system, fullscreen); + break; } system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); |