diff options
240 files changed, 5791 insertions, 5646 deletions
diff --git a/.ci/scripts/common/post-upload.sh b/.ci/scripts/common/post-upload.sh index e46ee0abb..99e79fcb6 100644 --- a/.ci/scripts/common/post-upload.sh +++ b/.ci/scripts/common/post-upload.sh | |||
| @@ -15,5 +15,5 @@ mv "${REV_NAME}-source.tar.xz" $RELEASE_NAME | |||
| 15 | 7z a "$REV_NAME.7z" $RELEASE_NAME | 15 | 7z a "$REV_NAME.7z" $RELEASE_NAME |
| 16 | 16 | ||
| 17 | # move the compiled archive into the artifacts directory to be uploaded by travis releases | 17 | # move the compiled archive into the artifacts directory to be uploaded by travis releases |
| 18 | mv "$ARCHIVE_NAME" artifacts/ | 18 | mv "$ARCHIVE_NAME" "${ARTIFACTS_DIR}/" |
| 19 | mv "$REV_NAME.7z" artifacts/ | 19 | mv "$REV_NAME.7z" "${ARTIFACTS_DIR}/" |
diff --git a/.ci/scripts/common/pre-upload.sh b/.ci/scripts/common/pre-upload.sh index 3c2fc79a2..a49e3fff3 100644 --- a/.ci/scripts/common/pre-upload.sh +++ b/.ci/scripts/common/pre-upload.sh | |||
| @@ -2,5 +2,6 @@ | |||
| 2 | 2 | ||
| 3 | GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`" | 3 | GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`" |
| 4 | GITREV="`git show -s --format='%h'`" | 4 | GITREV="`git show -s --format='%h'`" |
| 5 | ARTIFACTS_DIR="artifacts" | ||
| 5 | 6 | ||
| 6 | mkdir -p artifacts | 7 | mkdir -p "${ARTIFACTS_DIR}/" |
diff --git a/.ci/scripts/linux/docker.sh b/.ci/scripts/linux/docker.sh index e0c018cfd..39b1f77d7 100755 --- a/.ci/scripts/linux/docker.sh +++ b/.ci/scripts/linux/docker.sh | |||
| @@ -1,14 +1,49 @@ | |||
| 1 | #!/bin/bash -ex | 1 | #!/bin/bash -ex |
| 2 | 2 | ||
| 3 | # Exit on error, rather than continuing with the rest of the script. | ||
| 4 | set -e | ||
| 5 | |||
| 3 | cd /yuzu | 6 | cd /yuzu |
| 4 | 7 | ||
| 5 | ccache -s | 8 | ccache -s |
| 6 | 9 | ||
| 7 | mkdir build || true && cd build | 10 | mkdir build || true && cd build |
| 8 | cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON | 11 | cmake .. -DDISPLAY_VERSION=$1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DCMAKE_INSTALL_PREFIX="/usr" |
| 9 | 12 | ||
| 10 | ninja | 13 | make -j$(nproc) |
| 11 | 14 | ||
| 12 | ccache -s | 15 | ccache -s |
| 13 | 16 | ||
| 14 | ctest -VV -C Release | 17 | ctest -VV -C Release |
| 18 | |||
| 19 | make install DESTDIR=AppDir | ||
| 20 | rm -vf AppDir/usr/bin/yuzu-cmd AppDir/usr/bin/yuzu-tester | ||
| 21 | |||
| 22 | # Download tools needed to build an AppImage | ||
| 23 | wget -nc https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage | ||
| 24 | wget -nc https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage | ||
| 25 | wget -nc https://github.com/darealshinji/AppImageKit-checkrt/releases/download/continuous/AppRun-patched-x86_64 | ||
| 26 | wget -nc https://github.com/darealshinji/AppImageKit-checkrt/releases/download/continuous/exec-x86_64.so | ||
| 27 | # Set executable bit | ||
| 28 | chmod 755 \ | ||
| 29 | AppRun-patched-x86_64 \ | ||
| 30 | exec-x86_64.so \ | ||
| 31 | linuxdeploy-x86_64.AppImage \ | ||
| 32 | linuxdeploy-plugin-qt-x86_64.AppImage | ||
| 33 | |||
| 34 | # Workaround for https://github.com/AppImage/AppImageKit/issues/828 | ||
| 35 | export APPIMAGE_EXTRACT_AND_RUN=1 | ||
| 36 | |||
| 37 | mkdir -p AppDir/usr/optional | ||
| 38 | mkdir -p AppDir/usr/optional/libstdc++ | ||
| 39 | mkdir -p AppDir/usr/optional/libgcc_s | ||
| 40 | |||
| 41 | # Deploy yuzu's needed dependencies | ||
| 42 | ./linuxdeploy-x86_64.AppImage --appdir AppDir --plugin qt | ||
| 43 | |||
| 44 | # Workaround for building yuzu with GCC 10 but also trying to distribute it to Ubuntu 18.04 et al. | ||
| 45 | # See https://github.com/darealshinji/AppImageKit-checkrt | ||
| 46 | cp exec-x86_64.so AppDir/usr/optional/exec.so | ||
| 47 | cp AppRun-patched-x86_64 AppDir/AppRun | ||
| 48 | cp --dereference /usr/lib/x86_64-linux-gnu/libstdc++.so.6 AppDir/usr/optional/libstdc++/libstdc++.so.6 | ||
| 49 | cp --dereference /lib/x86_64-linux-gnu/libgcc_s.so.1 AppDir/usr/optional/libgcc_s/libgcc_s.so.1 | ||
diff --git a/.ci/scripts/linux/upload.sh b/.ci/scripts/linux/upload.sh index fe4e6b2ac..b2ea07388 100644 --- a/.ci/scripts/linux/upload.sh +++ b/.ci/scripts/linux/upload.sh | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | 2 | ||
| 3 | . .ci/scripts/common/pre-upload.sh | 3 | . .ci/scripts/common/pre-upload.sh |
| 4 | 4 | ||
| 5 | APPIMAGE_NAME="yuzu-${GITDATE}-${GITREV}.AppImage" | ||
| 5 | REV_NAME="yuzu-linux-${GITDATE}-${GITREV}" | 6 | REV_NAME="yuzu-linux-${GITDATE}-${GITREV}" |
| 6 | ARCHIVE_NAME="${REV_NAME}.tar.xz" | 7 | ARCHIVE_NAME="${REV_NAME}.tar.xz" |
| 7 | COMPRESSION_FLAGS="-cJvf" | 8 | COMPRESSION_FLAGS="-cJvf" |
| @@ -17,4 +18,24 @@ mkdir "$DIR_NAME" | |||
| 17 | cp build/bin/yuzu-cmd "$DIR_NAME" | 18 | cp build/bin/yuzu-cmd "$DIR_NAME" |
| 18 | cp build/bin/yuzu "$DIR_NAME" | 19 | cp build/bin/yuzu "$DIR_NAME" |
| 19 | 20 | ||
| 21 | # Build an AppImage | ||
| 22 | cd build | ||
| 23 | |||
| 24 | wget -nc https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage | ||
| 25 | chmod 755 appimagetool-x86_64.AppImage | ||
| 26 | |||
| 27 | if [ "${RELEASE_NAME}" = "mainline" ]; then | ||
| 28 | # Generate update information if releasing to mainline | ||
| 29 | ./appimagetool-x86_64.AppImage -u "gh-releases-zsync|yuzu-emu|yuzu-${RELEASE_NAME}|latest|yuzu-*.AppImage.zsync" AppDir "${APPIMAGE_NAME}" | ||
| 30 | else | ||
| 31 | ./appimagetool-x86_64.AppImage AppDir "${APPIMAGE_NAME}" | ||
| 32 | fi | ||
| 33 | cd .. | ||
| 34 | |||
| 35 | # Copy the AppImage and update info to the artifacts directory and avoid compressing it | ||
| 36 | cp "build/${APPIMAGE_NAME}" "${ARTIFACTS_DIR}/" | ||
| 37 | if [ -f "build/${APPIMAGE_NAME}.zsync" ]; then | ||
| 38 | cp "build/${APPIMAGE_NAME}.zsync" "${ARTIFACTS_DIR}/" | ||
| 39 | fi | ||
| 40 | |||
| 20 | . .ci/scripts/common/post-upload.sh | 41 | . .ci/scripts/common/post-upload.sh |
diff --git a/CMakeLists.txt b/CMakeLists.txt index eda555494..aaf3a90cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -26,6 +26,10 @@ option(ENABLE_CUBEB "Enables the cubeb audio backend" ON) | |||
| 26 | 26 | ||
| 27 | option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF) | 27 | option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF) |
| 28 | 28 | ||
| 29 | if (NOT ENABLE_WEB_SERVICE) | ||
| 30 | set(YUZU_ENABLE_BOXCAT OFF) | ||
| 31 | endif() | ||
| 32 | |||
| 29 | # Default to a Release build | 33 | # Default to a Release build |
| 30 | get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) | 34 | get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) |
| 31 | if (NOT IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE) | 35 | if (NOT IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE) |
| @@ -165,7 +169,7 @@ macro(yuzu_find_packages) | |||
| 165 | "lz4 1.8 lz4/1.9.2" | 169 | "lz4 1.8 lz4/1.9.2" |
| 166 | "nlohmann_json 3.8 nlohmann_json/3.8.0" | 170 | "nlohmann_json 3.8 nlohmann_json/3.8.0" |
| 167 | "ZLIB 1.2 zlib/1.2.11" | 171 | "ZLIB 1.2 zlib/1.2.11" |
| 168 | "zstd 1.4 zstd/1.4.5" | 172 | "zstd 1.4 zstd/1.4.8" |
| 169 | ) | 173 | ) |
| 170 | 174 | ||
| 171 | foreach(PACKAGE ${REQUIRED_LIBS}) | 175 | foreach(PACKAGE ${REQUIRED_LIBS}) |
| @@ -239,7 +243,7 @@ if(ENABLE_QT) | |||
| 239 | if (YUZU_USE_QT_WEB_ENGINE) | 243 | if (YUZU_USE_QT_WEB_ENGINE) |
| 240 | find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets) | 244 | find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets) |
| 241 | endif() | 245 | endif() |
| 242 | 246 | ||
| 243 | if (ENABLE_QT_TRANSLATION) | 247 | if (ENABLE_QT_TRANSLATION) |
| 244 | find_package(Qt5 REQUIRED COMPONENTS LinguistTools ${QT_PREFIX_HINT}) | 248 | find_package(Qt5 REQUIRED COMPONENTS LinguistTools ${QT_PREFIX_HINT}) |
| 245 | endif() | 249 | endif() |
| @@ -322,7 +326,7 @@ if (CONAN_REQUIRED_LIBS) | |||
| 322 | list(APPEND Boost_LIBRARIES Boost::context) | 326 | list(APPEND Boost_LIBRARIES Boost::context) |
| 323 | endif() | 327 | endif() |
| 324 | endif() | 328 | endif() |
| 325 | 329 | ||
| 326 | # Due to issues with variable scopes in functions, we need to also find_package(qt5) outside of the function | 330 | # Due to issues with variable scopes in functions, we need to also find_package(qt5) outside of the function |
| 327 | if(ENABLE_QT) | 331 | if(ENABLE_QT) |
| 328 | list(APPEND CMAKE_MODULE_PATH "${CONAN_QT_ROOT_RELEASE}") | 332 | list(APPEND CMAKE_MODULE_PATH "${CONAN_QT_ROOT_RELEASE}") |
| @@ -30,7 +30,6 @@ If you want to contribute to the user interface translation, please check out th | |||
| 30 | 30 | ||
| 31 | * __Windows__: [Windows Build](https://github.com/yuzu-emu/yuzu/wiki/Building-For-Windows) | 31 | * __Windows__: [Windows Build](https://github.com/yuzu-emu/yuzu/wiki/Building-For-Windows) |
| 32 | * __Linux__: [Linux Build](https://github.com/yuzu-emu/yuzu/wiki/Building-For-Linux) | 32 | * __Linux__: [Linux Build](https://github.com/yuzu-emu/yuzu/wiki/Building-For-Linux) |
| 33 | * __macOS__: [macOS Build](https://github.com/yuzu-emu/yuzu/wiki/Building-for-macOS) | ||
| 34 | 33 | ||
| 35 | 34 | ||
| 36 | ### Support | 35 | ### Support |
diff --git a/externals/dynarmic b/externals/dynarmic | |||
| Subproject 4a9a0d07f7376b439430e686721e8176c7b56ce | Subproject 3806284cbefc4115436dcdc687776a45ec31309 | ||
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8777df751..478246b6f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt | |||
| @@ -45,10 +45,15 @@ if (MSVC) | |||
| 45 | 45 | ||
| 46 | # Warnings | 46 | # Warnings |
| 47 | /W3 | 47 | /W3 |
| 48 | /we4062 # enumerator 'identifier' in a switch of enum 'enumeration' is not handled | ||
| 49 | /we4101 # 'identifier': unreferenced local variable | ||
| 50 | /we4265 # 'class': class has virtual functions, but destructor is not virtual | ||
| 51 | /we4388 # signed/unsigned mismatch | ||
| 48 | /we4547 # 'operator' : operator before comma has no effect; expected operator with side-effect | 52 | /we4547 # 'operator' : operator before comma has no effect; expected operator with side-effect |
| 49 | /we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'? | 53 | /we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'? |
| 50 | /we4555 # Expression has no effect; expected expression with side-effect | 54 | /we4555 # Expression has no effect; expected expression with side-effect |
| 51 | /we4834 # Discarding return value of function with 'nodiscard' attribute | 55 | /we4834 # Discarding return value of function with 'nodiscard' attribute |
| 56 | /we5038 # data member 'member1' will be initialized after data member 'member2' | ||
| 52 | ) | 57 | ) |
| 53 | 58 | ||
| 54 | # /GS- - No stack buffer overflow checks | 59 | # /GS- - No stack buffer overflow checks |
| @@ -62,8 +67,11 @@ else() | |||
| 62 | -Werror=implicit-fallthrough | 67 | -Werror=implicit-fallthrough |
| 63 | -Werror=missing-declarations | 68 | -Werror=missing-declarations |
| 64 | -Werror=reorder | 69 | -Werror=reorder |
| 70 | -Werror=switch | ||
| 65 | -Werror=uninitialized | 71 | -Werror=uninitialized |
| 72 | -Werror=unused-function | ||
| 66 | -Werror=unused-result | 73 | -Werror=unused-result |
| 74 | -Werror=unused-variable | ||
| 67 | -Wextra | 75 | -Wextra |
| 68 | -Wmissing-declarations | 76 | -Wmissing-declarations |
| 69 | -Wno-attributes | 77 | -Wno-attributes |
| @@ -122,7 +130,6 @@ add_subdirectory(tests) | |||
| 122 | 130 | ||
| 123 | if (ENABLE_SDL2) | 131 | if (ENABLE_SDL2) |
| 124 | add_subdirectory(yuzu_cmd) | 132 | add_subdirectory(yuzu_cmd) |
| 125 | add_subdirectory(yuzu_tester) | ||
| 126 | endif() | 133 | endif() |
| 127 | 134 | ||
| 128 | if (ENABLE_QT) | 135 | if (ENABLE_QT) |
diff --git a/src/audio_core/sink_context.h b/src/audio_core/sink_context.h index 05541becb..66ee4e8a0 100644 --- a/src/audio_core/sink_context.h +++ b/src/audio_core/sink_context.h | |||
| @@ -40,17 +40,17 @@ public: | |||
| 40 | SinkSampleFormat sample_format; | 40 | SinkSampleFormat sample_format; |
| 41 | std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input; | 41 | std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input; |
| 42 | bool in_use; | 42 | bool in_use; |
| 43 | INSERT_UNION_PADDING_BYTES(5); | 43 | INSERT_PADDING_BYTES_NOINIT(5); |
| 44 | }; | 44 | }; |
| 45 | static_assert(sizeof(CircularBufferIn) == 0x28, | 45 | static_assert(sizeof(CircularBufferIn) == 0x28, |
| 46 | "SinkInfo::CircularBufferIn is in invalid size"); | 46 | "SinkInfo::CircularBufferIn is in invalid size"); |
| 47 | 47 | ||
| 48 | struct DeviceIn { | 48 | struct DeviceIn { |
| 49 | std::array<u8, 255> device_name; | 49 | std::array<u8, 255> device_name; |
| 50 | INSERT_UNION_PADDING_BYTES(1); | 50 | INSERT_PADDING_BYTES_NOINIT(1); |
| 51 | s32_le input_count; | 51 | s32_le input_count; |
| 52 | std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input; | 52 | std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input; |
| 53 | INSERT_UNION_PADDING_BYTES(1); | 53 | INSERT_PADDING_BYTES_NOINIT(1); |
| 54 | bool down_matrix_enabled; | 54 | bool down_matrix_enabled; |
| 55 | DownmixCoefficients down_matrix_coef; | 55 | DownmixCoefficients down_matrix_coef; |
| 56 | }; | 56 | }; |
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index abe62543e..f77575a00 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -107,7 +107,6 @@ add_library(common STATIC | |||
| 107 | bit_util.h | 107 | bit_util.h |
| 108 | cityhash.cpp | 108 | cityhash.cpp |
| 109 | cityhash.h | 109 | cityhash.h |
| 110 | color.h | ||
| 111 | common_funcs.h | 110 | common_funcs.h |
| 112 | common_paths.h | 111 | common_paths.h |
| 113 | common_types.h | 112 | common_types.h |
| @@ -122,6 +121,7 @@ add_library(common STATIC | |||
| 122 | hash.h | 121 | hash.h |
| 123 | hex_util.cpp | 122 | hex_util.cpp |
| 124 | hex_util.h | 123 | hex_util.h |
| 124 | intrusive_red_black_tree.h | ||
| 125 | logging/backend.cpp | 125 | logging/backend.cpp |
| 126 | logging/backend.h | 126 | logging/backend.h |
| 127 | logging/filter.cpp | 127 | logging/filter.cpp |
| @@ -142,6 +142,7 @@ add_library(common STATIC | |||
| 142 | page_table.h | 142 | page_table.h |
| 143 | param_package.cpp | 143 | param_package.cpp |
| 144 | param_package.h | 144 | param_package.h |
| 145 | parent_of_member.h | ||
| 145 | quaternion.h | 146 | quaternion.h |
| 146 | ring_buffer.h | 147 | ring_buffer.h |
| 147 | scm_rev.cpp | 148 | scm_rev.cpp |
| @@ -164,8 +165,7 @@ add_library(common STATIC | |||
| 164 | threadsafe_queue.h | 165 | threadsafe_queue.h |
| 165 | time_zone.cpp | 166 | time_zone.cpp |
| 166 | time_zone.h | 167 | time_zone.h |
| 167 | timer.cpp | 168 | tree.h |
| 168 | timer.h | ||
| 169 | uint128.cpp | 169 | uint128.cpp |
| 170 | uint128.h | 170 | uint128.h |
| 171 | uuid.cpp | 171 | uuid.cpp |
diff --git a/src/common/alignment.h b/src/common/alignment.h index 5040043de..fb81f10d8 100644 --- a/src/common/alignment.h +++ b/src/common/alignment.h | |||
| @@ -9,50 +9,45 @@ | |||
| 9 | namespace Common { | 9 | namespace Common { |
| 10 | 10 | ||
| 11 | template <typename T> | 11 | template <typename T> |
| 12 | [[nodiscard]] constexpr T AlignUp(T value, std::size_t size) { | 12 | requires std::is_unsigned_v<T>[[nodiscard]] constexpr T AlignUp(T value, size_t size) { |
| 13 | static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); | ||
| 14 | auto mod{static_cast<T>(value % size)}; | 13 | auto mod{static_cast<T>(value % size)}; |
| 15 | value -= mod; | 14 | value -= mod; |
| 16 | return static_cast<T>(mod == T{0} ? value : value + size); | 15 | return static_cast<T>(mod == T{0} ? value : value + size); |
| 17 | } | 16 | } |
| 18 | 17 | ||
| 19 | template <typename T> | 18 | template <typename T> |
| 20 | [[nodiscard]] constexpr T AlignDown(T value, std::size_t size) { | 19 | requires std::is_unsigned_v<T>[[nodiscard]] constexpr T AlignUpLog2(T value, size_t align_log2) { |
| 21 | static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); | 20 | return static_cast<T>((value + ((1ULL << align_log2) - 1)) >> align_log2 << align_log2); |
| 22 | return static_cast<T>(value - value % size); | ||
| 23 | } | 21 | } |
| 24 | 22 | ||
| 25 | template <typename T> | 23 | template <typename T> |
| 26 | [[nodiscard]] constexpr T AlignBits(T value, std::size_t align) { | 24 | requires std::is_unsigned_v<T>[[nodiscard]] constexpr T AlignDown(T value, size_t size) { |
| 27 | static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); | 25 | return static_cast<T>(value - value % size); |
| 28 | return static_cast<T>((value + ((1ULL << align) - 1)) >> align << align); | ||
| 29 | } | 26 | } |
| 30 | 27 | ||
| 31 | template <typename T> | 28 | template <typename T> |
| 32 | [[nodiscard]] constexpr bool Is4KBAligned(T value) { | 29 | requires std::is_unsigned_v<T>[[nodiscard]] constexpr bool Is4KBAligned(T value) { |
| 33 | static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); | ||
| 34 | return (value & 0xFFF) == 0; | 30 | return (value & 0xFFF) == 0; |
| 35 | } | 31 | } |
| 36 | 32 | ||
| 37 | template <typename T> | 33 | template <typename T> |
| 38 | [[nodiscard]] constexpr bool IsWordAligned(T value) { | 34 | requires std::is_unsigned_v<T>[[nodiscard]] constexpr bool IsWordAligned(T value) { |
| 39 | static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); | ||
| 40 | return (value & 0b11) == 0; | 35 | return (value & 0b11) == 0; |
| 41 | } | 36 | } |
| 42 | 37 | ||
| 43 | template <typename T> | 38 | template <typename T> |
| 44 | [[nodiscard]] constexpr bool IsAligned(T value, std::size_t alignment) { | 39 | requires std::is_integral_v<T>[[nodiscard]] constexpr bool IsAligned(T value, size_t alignment) { |
| 45 | using U = typename std::make_unsigned<T>::type; | 40 | using U = typename std::make_unsigned_t<T>; |
| 46 | const U mask = static_cast<U>(alignment - 1); | 41 | const U mask = static_cast<U>(alignment - 1); |
| 47 | return (value & mask) == 0; | 42 | return (value & mask) == 0; |
| 48 | } | 43 | } |
| 49 | 44 | ||
| 50 | template <typename T, std::size_t Align = 16> | 45 | template <typename T, size_t Align = 16> |
| 51 | class AlignmentAllocator { | 46 | class AlignmentAllocator { |
| 52 | public: | 47 | public: |
| 53 | using value_type = T; | 48 | using value_type = T; |
| 54 | using size_type = std::size_t; | 49 | using size_type = size_t; |
| 55 | using difference_type = std::ptrdiff_t; | 50 | using difference_type = ptrdiff_t; |
| 56 | 51 | ||
| 57 | using propagate_on_container_copy_assignment = std::true_type; | 52 | using propagate_on_container_copy_assignment = std::true_type; |
| 58 | using propagate_on_container_move_assignment = std::true_type; | 53 | using propagate_on_container_move_assignment = std::true_type; |
diff --git a/src/common/bit_util.h b/src/common/bit_util.h index 29f59a9a3..685e7fc9b 100644 --- a/src/common/bit_util.h +++ b/src/common/bit_util.h | |||
| @@ -22,82 +22,6 @@ template <typename T> | |||
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | #ifdef _MSC_VER | 24 | #ifdef _MSC_VER |
| 25 | [[nodiscard]] inline u32 CountLeadingZeroes32(u32 value) { | ||
| 26 | unsigned long leading_zero = 0; | ||
| 27 | |||
| 28 | if (_BitScanReverse(&leading_zero, value) != 0) { | ||
| 29 | return 31 - leading_zero; | ||
| 30 | } | ||
| 31 | |||
| 32 | return 32; | ||
| 33 | } | ||
| 34 | |||
| 35 | [[nodiscard]] inline u32 CountLeadingZeroes64(u64 value) { | ||
| 36 | unsigned long leading_zero = 0; | ||
| 37 | |||
| 38 | if (_BitScanReverse64(&leading_zero, value) != 0) { | ||
| 39 | return 63 - leading_zero; | ||
| 40 | } | ||
| 41 | |||
| 42 | return 64; | ||
| 43 | } | ||
| 44 | #else | ||
| 45 | [[nodiscard]] inline u32 CountLeadingZeroes32(u32 value) { | ||
| 46 | if (value == 0) { | ||
| 47 | return 32; | ||
| 48 | } | ||
| 49 | |||
| 50 | return static_cast<u32>(__builtin_clz(value)); | ||
| 51 | } | ||
| 52 | |||
| 53 | [[nodiscard]] inline u32 CountLeadingZeroes64(u64 value) { | ||
| 54 | if (value == 0) { | ||
| 55 | return 64; | ||
| 56 | } | ||
| 57 | |||
| 58 | return static_cast<u32>(__builtin_clzll(value)); | ||
| 59 | } | ||
| 60 | #endif | ||
| 61 | |||
| 62 | #ifdef _MSC_VER | ||
| 63 | [[nodiscard]] inline u32 CountTrailingZeroes32(u32 value) { | ||
| 64 | unsigned long trailing_zero = 0; | ||
| 65 | |||
| 66 | if (_BitScanForward(&trailing_zero, value) != 0) { | ||
| 67 | return trailing_zero; | ||
| 68 | } | ||
| 69 | |||
| 70 | return 32; | ||
| 71 | } | ||
| 72 | |||
| 73 | [[nodiscard]] inline u32 CountTrailingZeroes64(u64 value) { | ||
| 74 | unsigned long trailing_zero = 0; | ||
| 75 | |||
| 76 | if (_BitScanForward64(&trailing_zero, value) != 0) { | ||
| 77 | return trailing_zero; | ||
| 78 | } | ||
| 79 | |||
| 80 | return 64; | ||
| 81 | } | ||
| 82 | #else | ||
| 83 | [[nodiscard]] inline u32 CountTrailingZeroes32(u32 value) { | ||
| 84 | if (value == 0) { | ||
| 85 | return 32; | ||
| 86 | } | ||
| 87 | |||
| 88 | return static_cast<u32>(__builtin_ctz(value)); | ||
| 89 | } | ||
| 90 | |||
| 91 | [[nodiscard]] inline u32 CountTrailingZeroes64(u64 value) { | ||
| 92 | if (value == 0) { | ||
| 93 | return 64; | ||
| 94 | } | ||
| 95 | |||
| 96 | return static_cast<u32>(__builtin_ctzll(value)); | ||
| 97 | } | ||
| 98 | #endif | ||
| 99 | |||
| 100 | #ifdef _MSC_VER | ||
| 101 | 25 | ||
| 102 | [[nodiscard]] inline u32 MostSignificantBit32(const u32 value) { | 26 | [[nodiscard]] inline u32 MostSignificantBit32(const u32 value) { |
| 103 | unsigned long result; | 27 | unsigned long result; |
diff --git a/src/common/color.h b/src/common/color.h deleted file mode 100644 index bbcac858e..000000000 --- a/src/common/color.h +++ /dev/null | |||
| @@ -1,271 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <cstring> | ||
| 8 | |||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "common/swap.h" | ||
| 11 | #include "common/vector_math.h" | ||
| 12 | |||
| 13 | namespace Common::Color { | ||
| 14 | |||
| 15 | /// Convert a 1-bit color component to 8 bit | ||
| 16 | [[nodiscard]] constexpr u8 Convert1To8(u8 value) { | ||
| 17 | return value * 255; | ||
| 18 | } | ||
| 19 | |||
| 20 | /// Convert a 4-bit color component to 8 bit | ||
| 21 | [[nodiscard]] constexpr u8 Convert4To8(u8 value) { | ||
| 22 | return (value << 4) | value; | ||
| 23 | } | ||
| 24 | |||
| 25 | /// Convert a 5-bit color component to 8 bit | ||
| 26 | [[nodiscard]] constexpr u8 Convert5To8(u8 value) { | ||
| 27 | return (value << 3) | (value >> 2); | ||
| 28 | } | ||
| 29 | |||
| 30 | /// Convert a 6-bit color component to 8 bit | ||
| 31 | [[nodiscard]] constexpr u8 Convert6To8(u8 value) { | ||
| 32 | return (value << 2) | (value >> 4); | ||
| 33 | } | ||
| 34 | |||
| 35 | /// Convert a 8-bit color component to 1 bit | ||
| 36 | [[nodiscard]] constexpr u8 Convert8To1(u8 value) { | ||
| 37 | return value >> 7; | ||
| 38 | } | ||
| 39 | |||
| 40 | /// Convert a 8-bit color component to 4 bit | ||
| 41 | [[nodiscard]] constexpr u8 Convert8To4(u8 value) { | ||
| 42 | return value >> 4; | ||
| 43 | } | ||
| 44 | |||
| 45 | /// Convert a 8-bit color component to 5 bit | ||
| 46 | [[nodiscard]] constexpr u8 Convert8To5(u8 value) { | ||
| 47 | return value >> 3; | ||
| 48 | } | ||
| 49 | |||
| 50 | /// Convert a 8-bit color component to 6 bit | ||
| 51 | [[nodiscard]] constexpr u8 Convert8To6(u8 value) { | ||
| 52 | return value >> 2; | ||
| 53 | } | ||
| 54 | |||
| 55 | /** | ||
| 56 | * Decode a color stored in RGBA8 format | ||
| 57 | * @param bytes Pointer to encoded source color | ||
| 58 | * @return Result color decoded as Common::Vec4<u8> | ||
| 59 | */ | ||
| 60 | [[nodiscard]] inline Common::Vec4<u8> DecodeRGBA8(const u8* bytes) { | ||
| 61 | return {bytes[3], bytes[2], bytes[1], bytes[0]}; | ||
| 62 | } | ||
| 63 | |||
| 64 | /** | ||
| 65 | * Decode a color stored in RGB8 format | ||
| 66 | * @param bytes Pointer to encoded source color | ||
| 67 | * @return Result color decoded as Common::Vec4<u8> | ||
| 68 | */ | ||
| 69 | [[nodiscard]] inline Common::Vec4<u8> DecodeRGB8(const u8* bytes) { | ||
| 70 | return {bytes[2], bytes[1], bytes[0], 255}; | ||
| 71 | } | ||
| 72 | |||
| 73 | /** | ||
| 74 | * Decode a color stored in RG8 (aka HILO8) format | ||
| 75 | * @param bytes Pointer to encoded source color | ||
| 76 | * @return Result color decoded as Common::Vec4<u8> | ||
| 77 | */ | ||
| 78 | [[nodiscard]] inline Common::Vec4<u8> DecodeRG8(const u8* bytes) { | ||
| 79 | return {bytes[1], bytes[0], 0, 255}; | ||
| 80 | } | ||
| 81 | |||
| 82 | /** | ||
| 83 | * Decode a color stored in RGB565 format | ||
| 84 | * @param bytes Pointer to encoded source color | ||
| 85 | * @return Result color decoded as Common::Vec4<u8> | ||
| 86 | */ | ||
| 87 | [[nodiscard]] inline Common::Vec4<u8> DecodeRGB565(const u8* bytes) { | ||
| 88 | u16_le pixel; | ||
| 89 | std::memcpy(&pixel, bytes, sizeof(pixel)); | ||
| 90 | return {Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F), | ||
| 91 | Convert5To8(pixel & 0x1F), 255}; | ||
| 92 | } | ||
| 93 | |||
| 94 | /** | ||
| 95 | * Decode a color stored in RGB5A1 format | ||
| 96 | * @param bytes Pointer to encoded source color | ||
| 97 | * @return Result color decoded as Common::Vec4<u8> | ||
| 98 | */ | ||
| 99 | [[nodiscard]] inline Common::Vec4<u8> DecodeRGB5A1(const u8* bytes) { | ||
| 100 | u16_le pixel; | ||
| 101 | std::memcpy(&pixel, bytes, sizeof(pixel)); | ||
| 102 | return {Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F), | ||
| 103 | Convert5To8((pixel >> 1) & 0x1F), Convert1To8(pixel & 0x1)}; | ||
| 104 | } | ||
| 105 | |||
| 106 | /** | ||
| 107 | * Decode a color stored in RGBA4 format | ||
| 108 | * @param bytes Pointer to encoded source color | ||
| 109 | * @return Result color decoded as Common::Vec4<u8> | ||
| 110 | */ | ||
| 111 | [[nodiscard]] inline Common::Vec4<u8> DecodeRGBA4(const u8* bytes) { | ||
| 112 | u16_le pixel; | ||
| 113 | std::memcpy(&pixel, bytes, sizeof(pixel)); | ||
| 114 | return {Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF), | ||
| 115 | Convert4To8((pixel >> 4) & 0xF), Convert4To8(pixel & 0xF)}; | ||
| 116 | } | ||
| 117 | |||
| 118 | /** | ||
| 119 | * Decode a depth value stored in D16 format | ||
| 120 | * @param bytes Pointer to encoded source value | ||
| 121 | * @return Depth value as an u32 | ||
| 122 | */ | ||
| 123 | [[nodiscard]] inline u32 DecodeD16(const u8* bytes) { | ||
| 124 | u16_le data; | ||
| 125 | std::memcpy(&data, bytes, sizeof(data)); | ||
| 126 | return data; | ||
| 127 | } | ||
| 128 | |||
| 129 | /** | ||
| 130 | * Decode a depth value stored in D24 format | ||
| 131 | * @param bytes Pointer to encoded source value | ||
| 132 | * @return Depth value as an u32 | ||
| 133 | */ | ||
| 134 | [[nodiscard]] inline u32 DecodeD24(const u8* bytes) { | ||
| 135 | return (bytes[2] << 16) | (bytes[1] << 8) | bytes[0]; | ||
| 136 | } | ||
| 137 | |||
| 138 | /** | ||
| 139 | * Decode a depth value and a stencil value stored in D24S8 format | ||
| 140 | * @param bytes Pointer to encoded source values | ||
| 141 | * @return Resulting values stored as a Common::Vec2 | ||
| 142 | */ | ||
| 143 | [[nodiscard]] inline Common::Vec2<u32> DecodeD24S8(const u8* bytes) { | ||
| 144 | return {static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3]}; | ||
| 145 | } | ||
| 146 | |||
| 147 | /** | ||
| 148 | * Encode a color as RGBA8 format | ||
| 149 | * @param color Source color to encode | ||
| 150 | * @param bytes Destination pointer to store encoded color | ||
| 151 | */ | ||
| 152 | inline void EncodeRGBA8(const Common::Vec4<u8>& color, u8* bytes) { | ||
| 153 | bytes[3] = color.r(); | ||
| 154 | bytes[2] = color.g(); | ||
| 155 | bytes[1] = color.b(); | ||
| 156 | bytes[0] = color.a(); | ||
| 157 | } | ||
| 158 | |||
| 159 | /** | ||
| 160 | * Encode a color as RGB8 format | ||
| 161 | * @param color Source color to encode | ||
| 162 | * @param bytes Destination pointer to store encoded color | ||
| 163 | */ | ||
| 164 | inline void EncodeRGB8(const Common::Vec4<u8>& color, u8* bytes) { | ||
| 165 | bytes[2] = color.r(); | ||
| 166 | bytes[1] = color.g(); | ||
| 167 | bytes[0] = color.b(); | ||
| 168 | } | ||
| 169 | |||
| 170 | /** | ||
| 171 | * Encode a color as RG8 (aka HILO8) format | ||
| 172 | * @param color Source color to encode | ||
| 173 | * @param bytes Destination pointer to store encoded color | ||
| 174 | */ | ||
| 175 | inline void EncodeRG8(const Common::Vec4<u8>& color, u8* bytes) { | ||
| 176 | bytes[1] = color.r(); | ||
| 177 | bytes[0] = color.g(); | ||
| 178 | } | ||
| 179 | /** | ||
| 180 | * Encode a color as RGB565 format | ||
| 181 | * @param color Source color to encode | ||
| 182 | * @param bytes Destination pointer to store encoded color | ||
| 183 | */ | ||
| 184 | inline void EncodeRGB565(const Common::Vec4<u8>& color, u8* bytes) { | ||
| 185 | const u16_le data = | ||
| 186 | (Convert8To5(color.r()) << 11) | (Convert8To6(color.g()) << 5) | Convert8To5(color.b()); | ||
| 187 | |||
| 188 | std::memcpy(bytes, &data, sizeof(data)); | ||
| 189 | } | ||
| 190 | |||
| 191 | /** | ||
| 192 | * Encode a color as RGB5A1 format | ||
| 193 | * @param color Source color to encode | ||
| 194 | * @param bytes Destination pointer to store encoded color | ||
| 195 | */ | ||
| 196 | inline void EncodeRGB5A1(const Common::Vec4<u8>& color, u8* bytes) { | ||
| 197 | const u16_le data = (Convert8To5(color.r()) << 11) | (Convert8To5(color.g()) << 6) | | ||
| 198 | (Convert8To5(color.b()) << 1) | Convert8To1(color.a()); | ||
| 199 | |||
| 200 | std::memcpy(bytes, &data, sizeof(data)); | ||
| 201 | } | ||
| 202 | |||
| 203 | /** | ||
| 204 | * Encode a color as RGBA4 format | ||
| 205 | * @param color Source color to encode | ||
| 206 | * @param bytes Destination pointer to store encoded color | ||
| 207 | */ | ||
| 208 | inline void EncodeRGBA4(const Common::Vec4<u8>& color, u8* bytes) { | ||
| 209 | const u16 data = (Convert8To4(color.r()) << 12) | (Convert8To4(color.g()) << 8) | | ||
| 210 | (Convert8To4(color.b()) << 4) | Convert8To4(color.a()); | ||
| 211 | |||
| 212 | std::memcpy(bytes, &data, sizeof(data)); | ||
| 213 | } | ||
| 214 | |||
| 215 | /** | ||
| 216 | * Encode a 16 bit depth value as D16 format | ||
| 217 | * @param value 16 bit source depth value to encode | ||
| 218 | * @param bytes Pointer where to store the encoded value | ||
| 219 | */ | ||
| 220 | inline void EncodeD16(u32 value, u8* bytes) { | ||
| 221 | const u16_le data = static_cast<u16>(value); | ||
| 222 | std::memcpy(bytes, &data, sizeof(data)); | ||
| 223 | } | ||
| 224 | |||
| 225 | /** | ||
| 226 | * Encode a 24 bit depth value as D24 format | ||
| 227 | * @param value 24 bit source depth value to encode | ||
| 228 | * @param bytes Pointer where to store the encoded value | ||
| 229 | */ | ||
| 230 | inline void EncodeD24(u32 value, u8* bytes) { | ||
| 231 | bytes[0] = value & 0xFF; | ||
| 232 | bytes[1] = (value >> 8) & 0xFF; | ||
| 233 | bytes[2] = (value >> 16) & 0xFF; | ||
| 234 | } | ||
| 235 | |||
| 236 | /** | ||
| 237 | * Encode a 24 bit depth and 8 bit stencil values as D24S8 format | ||
| 238 | * @param depth 24 bit source depth value to encode | ||
| 239 | * @param stencil 8 bit source stencil value to encode | ||
| 240 | * @param bytes Pointer where to store the encoded value | ||
| 241 | */ | ||
| 242 | inline void EncodeD24S8(u32 depth, u8 stencil, u8* bytes) { | ||
| 243 | bytes[0] = depth & 0xFF; | ||
| 244 | bytes[1] = (depth >> 8) & 0xFF; | ||
| 245 | bytes[2] = (depth >> 16) & 0xFF; | ||
| 246 | bytes[3] = stencil; | ||
| 247 | } | ||
| 248 | |||
| 249 | /** | ||
| 250 | * Encode a 24 bit depth value as D24X8 format (32 bits per pixel with 8 bits unused) | ||
| 251 | * @param depth 24 bit source depth value to encode | ||
| 252 | * @param bytes Pointer where to store the encoded value | ||
| 253 | * @note unused bits will not be modified | ||
| 254 | */ | ||
| 255 | inline void EncodeD24X8(u32 depth, u8* bytes) { | ||
| 256 | bytes[0] = depth & 0xFF; | ||
| 257 | bytes[1] = (depth >> 8) & 0xFF; | ||
| 258 | bytes[2] = (depth >> 16) & 0xFF; | ||
| 259 | } | ||
| 260 | |||
| 261 | /** | ||
| 262 | * Encode an 8 bit stencil value as X24S8 format (32 bits per pixel with 24 bits unused) | ||
| 263 | * @param stencil 8 bit source stencil value to encode | ||
| 264 | * @param bytes Pointer where to store the encoded value | ||
| 265 | * @note unused bits will not be modified | ||
| 266 | */ | ||
| 267 | inline void EncodeX24S8(u8 stencil, u8* bytes) { | ||
| 268 | bytes[3] = stencil; | ||
| 269 | } | ||
| 270 | |||
| 271 | } // namespace Common::Color | ||
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index 367b6bf6e..75f3027fb 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h | |||
| @@ -24,10 +24,10 @@ | |||
| 24 | #define INSERT_PADDING_WORDS(num_words) \ | 24 | #define INSERT_PADDING_WORDS(num_words) \ |
| 25 | std::array<u32, num_words> CONCAT2(pad, __LINE__) {} | 25 | std::array<u32, num_words> CONCAT2(pad, __LINE__) {} |
| 26 | 26 | ||
| 27 | /// These are similar to the INSERT_PADDING_* macros, but are needed for padding unions. This is | 27 | /// These are similar to the INSERT_PADDING_* macros but do not zero-initialize the contents. |
| 28 | /// because unions can only be initialized by one member. | 28 | /// This keeps the structure trivial to construct. |
| 29 | #define INSERT_UNION_PADDING_BYTES(num_bytes) std::array<u8, num_bytes> CONCAT2(pad, __LINE__) | 29 | #define INSERT_PADDING_BYTES_NOINIT(num_bytes) std::array<u8, num_bytes> CONCAT2(pad, __LINE__) |
| 30 | #define INSERT_UNION_PADDING_WORDS(num_words) std::array<u32, num_words> CONCAT2(pad, __LINE__) | 30 | #define INSERT_PADDING_WORDS_NOINIT(num_words) std::array<u32, num_words> CONCAT2(pad, __LINE__) |
| 31 | 31 | ||
| 32 | #ifndef _MSC_VER | 32 | #ifndef _MSC_VER |
| 33 | 33 | ||
| @@ -93,6 +93,14 @@ __declspec(dllimport) void __stdcall DebugBreak(void); | |||
| 93 | return static_cast<T>(key) == 0; \ | 93 | return static_cast<T>(key) == 0; \ |
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | /// Evaluates a boolean expression, and returns a result unless that expression is true. | ||
| 97 | #define R_UNLESS(expr, res) \ | ||
| 98 | { \ | ||
| 99 | if (!(expr)) { \ | ||
| 100 | return res; \ | ||
| 101 | } \ | ||
| 102 | } | ||
| 103 | |||
| 96 | namespace Common { | 104 | namespace Common { |
| 97 | 105 | ||
| 98 | [[nodiscard]] constexpr u32 MakeMagic(char a, char b, char c, char d) { | 106 | [[nodiscard]] constexpr u32 MakeMagic(char a, char b, char c, char d) { |
diff --git a/src/common/div_ceil.h b/src/common/div_ceil.h index 6b2c48f91..95e1489a9 100644 --- a/src/common/div_ceil.h +++ b/src/common/div_ceil.h | |||
| @@ -11,16 +11,16 @@ namespace Common { | |||
| 11 | 11 | ||
| 12 | /// Ceiled integer division. | 12 | /// Ceiled integer division. |
| 13 | template <typename N, typename D> | 13 | template <typename N, typename D> |
| 14 | requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr auto DivCeil( | 14 | requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr N DivCeil(N number, |
| 15 | N number, D divisor) { | 15 | D divisor) { |
| 16 | return (static_cast<D>(number) + divisor - 1) / divisor; | 16 | return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor); |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | /// Ceiled integer division with logarithmic divisor in base 2 | 19 | /// Ceiled integer division with logarithmic divisor in base 2 |
| 20 | template <typename N, typename D> | 20 | template <typename N, typename D> |
| 21 | requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr auto DivCeilLog2( | 21 | requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr N DivCeilLog2( |
| 22 | N value, D alignment_log2) { | 22 | N value, D alignment_log2) { |
| 23 | return (static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2; | 23 | return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2); |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | } // namespace Common | 26 | } // namespace Common |
diff --git a/src/common/intrusive_red_black_tree.h b/src/common/intrusive_red_black_tree.h new file mode 100644 index 000000000..c0bbcd457 --- /dev/null +++ b/src/common/intrusive_red_black_tree.h | |||
| @@ -0,0 +1,602 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/parent_of_member.h" | ||
| 8 | #include "common/tree.h" | ||
| 9 | |||
| 10 | namespace Common { | ||
| 11 | |||
| 12 | namespace impl { | ||
| 13 | |||
| 14 | class IntrusiveRedBlackTreeImpl; | ||
| 15 | |||
| 16 | } | ||
| 17 | |||
| 18 | struct IntrusiveRedBlackTreeNode { | ||
| 19 | public: | ||
| 20 | using EntryType = RBEntry<IntrusiveRedBlackTreeNode>; | ||
| 21 | |||
| 22 | constexpr IntrusiveRedBlackTreeNode() = default; | ||
| 23 | |||
| 24 | void SetEntry(const EntryType& new_entry) { | ||
| 25 | entry = new_entry; | ||
| 26 | } | ||
| 27 | |||
| 28 | [[nodiscard]] EntryType& GetEntry() { | ||
| 29 | return entry; | ||
| 30 | } | ||
| 31 | |||
| 32 | [[nodiscard]] const EntryType& GetEntry() const { | ||
| 33 | return entry; | ||
| 34 | } | ||
| 35 | |||
| 36 | private: | ||
| 37 | EntryType entry{}; | ||
| 38 | |||
| 39 | friend class impl::IntrusiveRedBlackTreeImpl; | ||
| 40 | |||
| 41 | template <class, class, class> | ||
| 42 | friend class IntrusiveRedBlackTree; | ||
| 43 | }; | ||
| 44 | |||
| 45 | template <class T, class Traits, class Comparator> | ||
| 46 | class IntrusiveRedBlackTree; | ||
| 47 | |||
| 48 | namespace impl { | ||
| 49 | |||
| 50 | class IntrusiveRedBlackTreeImpl { | ||
| 51 | private: | ||
| 52 | template <class, class, class> | ||
| 53 | friend class ::Common::IntrusiveRedBlackTree; | ||
| 54 | |||
| 55 | using RootType = RBHead<IntrusiveRedBlackTreeNode>; | ||
| 56 | RootType root; | ||
| 57 | |||
| 58 | public: | ||
| 59 | template <bool Const> | ||
| 60 | class Iterator; | ||
| 61 | |||
| 62 | using value_type = IntrusiveRedBlackTreeNode; | ||
| 63 | using size_type = size_t; | ||
| 64 | using difference_type = ptrdiff_t; | ||
| 65 | using pointer = value_type*; | ||
| 66 | using const_pointer = const value_type*; | ||
| 67 | using reference = value_type&; | ||
| 68 | using const_reference = const value_type&; | ||
| 69 | using iterator = Iterator<false>; | ||
| 70 | using const_iterator = Iterator<true>; | ||
| 71 | |||
| 72 | template <bool Const> | ||
| 73 | class Iterator { | ||
| 74 | public: | ||
| 75 | using iterator_category = std::bidirectional_iterator_tag; | ||
| 76 | using value_type = typename IntrusiveRedBlackTreeImpl::value_type; | ||
| 77 | using difference_type = typename IntrusiveRedBlackTreeImpl::difference_type; | ||
| 78 | using pointer = std::conditional_t<Const, IntrusiveRedBlackTreeImpl::const_pointer, | ||
| 79 | IntrusiveRedBlackTreeImpl::pointer>; | ||
| 80 | using reference = std::conditional_t<Const, IntrusiveRedBlackTreeImpl::const_reference, | ||
| 81 | IntrusiveRedBlackTreeImpl::reference>; | ||
| 82 | |||
| 83 | private: | ||
| 84 | pointer node; | ||
| 85 | |||
| 86 | public: | ||
| 87 | explicit Iterator(pointer n) : node(n) {} | ||
| 88 | |||
| 89 | bool operator==(const Iterator& rhs) const { | ||
| 90 | return this->node == rhs.node; | ||
| 91 | } | ||
| 92 | |||
| 93 | bool operator!=(const Iterator& rhs) const { | ||
| 94 | return !(*this == rhs); | ||
| 95 | } | ||
| 96 | |||
| 97 | pointer operator->() const { | ||
| 98 | return this->node; | ||
| 99 | } | ||
| 100 | |||
| 101 | reference operator*() const { | ||
| 102 | return *this->node; | ||
| 103 | } | ||
| 104 | |||
| 105 | Iterator& operator++() { | ||
| 106 | this->node = GetNext(this->node); | ||
| 107 | return *this; | ||
| 108 | } | ||
| 109 | |||
| 110 | Iterator& operator--() { | ||
| 111 | this->node = GetPrev(this->node); | ||
| 112 | return *this; | ||
| 113 | } | ||
| 114 | |||
| 115 | Iterator operator++(int) { | ||
| 116 | const Iterator it{*this}; | ||
| 117 | ++(*this); | ||
| 118 | return it; | ||
| 119 | } | ||
| 120 | |||
| 121 | Iterator operator--(int) { | ||
| 122 | const Iterator it{*this}; | ||
| 123 | --(*this); | ||
| 124 | return it; | ||
| 125 | } | ||
| 126 | |||
| 127 | operator Iterator<true>() const { | ||
| 128 | return Iterator<true>(this->node); | ||
| 129 | } | ||
| 130 | }; | ||
| 131 | |||
| 132 | private: | ||
| 133 | // Define accessors using RB_* functions. | ||
| 134 | bool EmptyImpl() const { | ||
| 135 | return root.IsEmpty(); | ||
| 136 | } | ||
| 137 | |||
| 138 | IntrusiveRedBlackTreeNode* GetMinImpl() const { | ||
| 139 | return RB_MIN(const_cast<RootType*>(&root)); | ||
| 140 | } | ||
| 141 | |||
| 142 | IntrusiveRedBlackTreeNode* GetMaxImpl() const { | ||
| 143 | return RB_MAX(const_cast<RootType*>(&root)); | ||
| 144 | } | ||
| 145 | |||
| 146 | IntrusiveRedBlackTreeNode* RemoveImpl(IntrusiveRedBlackTreeNode* node) { | ||
| 147 | return RB_REMOVE(&root, node); | ||
| 148 | } | ||
| 149 | |||
| 150 | public: | ||
| 151 | static IntrusiveRedBlackTreeNode* GetNext(IntrusiveRedBlackTreeNode* node) { | ||
| 152 | return RB_NEXT(node); | ||
| 153 | } | ||
| 154 | |||
| 155 | static IntrusiveRedBlackTreeNode* GetPrev(IntrusiveRedBlackTreeNode* node) { | ||
| 156 | return RB_PREV(node); | ||
| 157 | } | ||
| 158 | |||
| 159 | static const IntrusiveRedBlackTreeNode* GetNext(const IntrusiveRedBlackTreeNode* node) { | ||
| 160 | return static_cast<const IntrusiveRedBlackTreeNode*>( | ||
| 161 | GetNext(const_cast<IntrusiveRedBlackTreeNode*>(node))); | ||
| 162 | } | ||
| 163 | |||
| 164 | static const IntrusiveRedBlackTreeNode* GetPrev(const IntrusiveRedBlackTreeNode* node) { | ||
| 165 | return static_cast<const IntrusiveRedBlackTreeNode*>( | ||
| 166 | GetPrev(const_cast<IntrusiveRedBlackTreeNode*>(node))); | ||
| 167 | } | ||
| 168 | |||
| 169 | public: | ||
| 170 | constexpr IntrusiveRedBlackTreeImpl() {} | ||
| 171 | |||
| 172 | // Iterator accessors. | ||
| 173 | iterator begin() { | ||
| 174 | return iterator(this->GetMinImpl()); | ||
| 175 | } | ||
| 176 | |||
| 177 | const_iterator begin() const { | ||
| 178 | return const_iterator(this->GetMinImpl()); | ||
| 179 | } | ||
| 180 | |||
| 181 | iterator end() { | ||
| 182 | return iterator(static_cast<IntrusiveRedBlackTreeNode*>(nullptr)); | ||
| 183 | } | ||
| 184 | |||
| 185 | const_iterator end() const { | ||
| 186 | return const_iterator(static_cast<const IntrusiveRedBlackTreeNode*>(nullptr)); | ||
| 187 | } | ||
| 188 | |||
| 189 | const_iterator cbegin() const { | ||
| 190 | return this->begin(); | ||
| 191 | } | ||
| 192 | |||
| 193 | const_iterator cend() const { | ||
| 194 | return this->end(); | ||
| 195 | } | ||
| 196 | |||
| 197 | iterator iterator_to(reference ref) { | ||
| 198 | return iterator(&ref); | ||
| 199 | } | ||
| 200 | |||
| 201 | const_iterator iterator_to(const_reference ref) const { | ||
| 202 | return const_iterator(&ref); | ||
| 203 | } | ||
| 204 | |||
| 205 | // Content management. | ||
| 206 | bool empty() const { | ||
| 207 | return this->EmptyImpl(); | ||
| 208 | } | ||
| 209 | |||
| 210 | reference back() { | ||
| 211 | return *this->GetMaxImpl(); | ||
| 212 | } | ||
| 213 | |||
| 214 | const_reference back() const { | ||
| 215 | return *this->GetMaxImpl(); | ||
| 216 | } | ||
| 217 | |||
| 218 | reference front() { | ||
| 219 | return *this->GetMinImpl(); | ||
| 220 | } | ||
| 221 | |||
| 222 | const_reference front() const { | ||
| 223 | return *this->GetMinImpl(); | ||
| 224 | } | ||
| 225 | |||
| 226 | iterator erase(iterator it) { | ||
| 227 | auto cur = std::addressof(*it); | ||
| 228 | auto next = GetNext(cur); | ||
| 229 | this->RemoveImpl(cur); | ||
| 230 | return iterator(next); | ||
| 231 | } | ||
| 232 | }; | ||
| 233 | |||
| 234 | } // namespace impl | ||
| 235 | |||
| 236 | template <typename T> | ||
| 237 | concept HasLightCompareType = requires { | ||
| 238 | { std::is_same<typename T::LightCompareType, void>::value } | ||
| 239 | ->std::convertible_to<bool>; | ||
| 240 | }; | ||
| 241 | |||
| 242 | namespace impl { | ||
| 243 | |||
| 244 | template <typename T, typename Default> | ||
| 245 | consteval auto* GetLightCompareType() { | ||
| 246 | if constexpr (HasLightCompareType<T>) { | ||
| 247 | return static_cast<typename T::LightCompareType*>(nullptr); | ||
| 248 | } else { | ||
| 249 | return static_cast<Default*>(nullptr); | ||
| 250 | } | ||
| 251 | } | ||
| 252 | |||
| 253 | } // namespace impl | ||
| 254 | |||
| 255 | template <typename T, typename Default> | ||
| 256 | using LightCompareType = std::remove_pointer_t<decltype(impl::GetLightCompareType<T, Default>())>; | ||
| 257 | |||
| 258 | template <class T, class Traits, class Comparator> | ||
| 259 | class IntrusiveRedBlackTree { | ||
| 260 | |||
| 261 | public: | ||
| 262 | using ImplType = impl::IntrusiveRedBlackTreeImpl; | ||
| 263 | |||
| 264 | private: | ||
| 265 | ImplType impl{}; | ||
| 266 | |||
| 267 | public: | ||
| 268 | template <bool Const> | ||
| 269 | class Iterator; | ||
| 270 | |||
| 271 | using value_type = T; | ||
| 272 | using size_type = size_t; | ||
| 273 | using difference_type = ptrdiff_t; | ||
| 274 | using pointer = T*; | ||
| 275 | using const_pointer = const T*; | ||
| 276 | using reference = T&; | ||
| 277 | using const_reference = const T&; | ||
| 278 | using iterator = Iterator<false>; | ||
| 279 | using const_iterator = Iterator<true>; | ||
| 280 | |||
| 281 | using light_value_type = LightCompareType<Comparator, value_type>; | ||
| 282 | using const_light_pointer = const light_value_type*; | ||
| 283 | using const_light_reference = const light_value_type&; | ||
| 284 | |||
| 285 | template <bool Const> | ||
| 286 | class Iterator { | ||
| 287 | public: | ||
| 288 | friend class IntrusiveRedBlackTree<T, Traits, Comparator>; | ||
| 289 | |||
| 290 | using ImplIterator = | ||
| 291 | std::conditional_t<Const, ImplType::const_iterator, ImplType::iterator>; | ||
| 292 | |||
| 293 | using iterator_category = std::bidirectional_iterator_tag; | ||
| 294 | using value_type = typename IntrusiveRedBlackTree::value_type; | ||
| 295 | using difference_type = typename IntrusiveRedBlackTree::difference_type; | ||
| 296 | using pointer = std::conditional_t<Const, IntrusiveRedBlackTree::const_pointer, | ||
| 297 | IntrusiveRedBlackTree::pointer>; | ||
| 298 | using reference = std::conditional_t<Const, IntrusiveRedBlackTree::const_reference, | ||
| 299 | IntrusiveRedBlackTree::reference>; | ||
| 300 | |||
| 301 | private: | ||
| 302 | ImplIterator iterator; | ||
| 303 | |||
| 304 | private: | ||
| 305 | explicit Iterator(ImplIterator it) : iterator(it) {} | ||
| 306 | |||
| 307 | explicit Iterator(typename std::conditional<Const, ImplType::const_iterator, | ||
| 308 | ImplType::iterator>::type::pointer ptr) | ||
| 309 | : iterator(ptr) {} | ||
| 310 | |||
| 311 | ImplIterator GetImplIterator() const { | ||
| 312 | return this->iterator; | ||
| 313 | } | ||
| 314 | |||
| 315 | public: | ||
| 316 | bool operator==(const Iterator& rhs) const { | ||
| 317 | return this->iterator == rhs.iterator; | ||
| 318 | } | ||
| 319 | |||
| 320 | bool operator!=(const Iterator& rhs) const { | ||
| 321 | return !(*this == rhs); | ||
| 322 | } | ||
| 323 | |||
| 324 | pointer operator->() const { | ||
| 325 | return Traits::GetParent(std::addressof(*this->iterator)); | ||
| 326 | } | ||
| 327 | |||
| 328 | reference operator*() const { | ||
| 329 | return *Traits::GetParent(std::addressof(*this->iterator)); | ||
| 330 | } | ||
| 331 | |||
| 332 | Iterator& operator++() { | ||
| 333 | ++this->iterator; | ||
| 334 | return *this; | ||
| 335 | } | ||
| 336 | |||
| 337 | Iterator& operator--() { | ||
| 338 | --this->iterator; | ||
| 339 | return *this; | ||
| 340 | } | ||
| 341 | |||
| 342 | Iterator operator++(int) { | ||
| 343 | const Iterator it{*this}; | ||
| 344 | ++this->iterator; | ||
| 345 | return it; | ||
| 346 | } | ||
| 347 | |||
| 348 | Iterator operator--(int) { | ||
| 349 | const Iterator it{*this}; | ||
| 350 | --this->iterator; | ||
| 351 | return it; | ||
| 352 | } | ||
| 353 | |||
| 354 | operator Iterator<true>() const { | ||
| 355 | return Iterator<true>(this->iterator); | ||
| 356 | } | ||
| 357 | }; | ||
| 358 | |||
| 359 | private: | ||
| 360 | static int CompareImpl(const IntrusiveRedBlackTreeNode* lhs, | ||
| 361 | const IntrusiveRedBlackTreeNode* rhs) { | ||
| 362 | return Comparator::Compare(*Traits::GetParent(lhs), *Traits::GetParent(rhs)); | ||
| 363 | } | ||
| 364 | |||
| 365 | static int LightCompareImpl(const void* elm, const IntrusiveRedBlackTreeNode* rhs) { | ||
| 366 | return Comparator::Compare(*static_cast<const_light_pointer>(elm), *Traits::GetParent(rhs)); | ||
| 367 | } | ||
| 368 | |||
| 369 | // Define accessors using RB_* functions. | ||
| 370 | IntrusiveRedBlackTreeNode* InsertImpl(IntrusiveRedBlackTreeNode* node) { | ||
| 371 | return RB_INSERT(&impl.root, node, CompareImpl); | ||
| 372 | } | ||
| 373 | |||
| 374 | IntrusiveRedBlackTreeNode* FindImpl(const IntrusiveRedBlackTreeNode* node) const { | ||
| 375 | return RB_FIND(const_cast<ImplType::RootType*>(&impl.root), | ||
| 376 | const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl); | ||
| 377 | } | ||
| 378 | |||
| 379 | IntrusiveRedBlackTreeNode* NFindImpl(const IntrusiveRedBlackTreeNode* node) const { | ||
| 380 | return RB_NFIND(const_cast<ImplType::RootType*>(&impl.root), | ||
| 381 | const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl); | ||
| 382 | } | ||
| 383 | |||
| 384 | IntrusiveRedBlackTreeNode* FindLightImpl(const_light_pointer lelm) const { | ||
| 385 | return RB_FIND_LIGHT(const_cast<ImplType::RootType*>(&impl.root), | ||
| 386 | static_cast<const void*>(lelm), LightCompareImpl); | ||
| 387 | } | ||
| 388 | |||
| 389 | IntrusiveRedBlackTreeNode* NFindLightImpl(const_light_pointer lelm) const { | ||
| 390 | return RB_NFIND_LIGHT(const_cast<ImplType::RootType*>(&impl.root), | ||
| 391 | static_cast<const void*>(lelm), LightCompareImpl); | ||
| 392 | } | ||
| 393 | |||
| 394 | public: | ||
| 395 | constexpr IntrusiveRedBlackTree() = default; | ||
| 396 | |||
| 397 | // Iterator accessors. | ||
| 398 | iterator begin() { | ||
| 399 | return iterator(this->impl.begin()); | ||
| 400 | } | ||
| 401 | |||
| 402 | const_iterator begin() const { | ||
| 403 | return const_iterator(this->impl.begin()); | ||
| 404 | } | ||
| 405 | |||
| 406 | iterator end() { | ||
| 407 | return iterator(this->impl.end()); | ||
| 408 | } | ||
| 409 | |||
| 410 | const_iterator end() const { | ||
| 411 | return const_iterator(this->impl.end()); | ||
| 412 | } | ||
| 413 | |||
| 414 | const_iterator cbegin() const { | ||
| 415 | return this->begin(); | ||
| 416 | } | ||
| 417 | |||
| 418 | const_iterator cend() const { | ||
| 419 | return this->end(); | ||
| 420 | } | ||
| 421 | |||
| 422 | iterator iterator_to(reference ref) { | ||
| 423 | return iterator(this->impl.iterator_to(*Traits::GetNode(std::addressof(ref)))); | ||
| 424 | } | ||
| 425 | |||
| 426 | const_iterator iterator_to(const_reference ref) const { | ||
| 427 | return const_iterator(this->impl.iterator_to(*Traits::GetNode(std::addressof(ref)))); | ||
| 428 | } | ||
| 429 | |||
| 430 | // Content management. | ||
| 431 | bool empty() const { | ||
| 432 | return this->impl.empty(); | ||
| 433 | } | ||
| 434 | |||
| 435 | reference back() { | ||
| 436 | return *Traits::GetParent(std::addressof(this->impl.back())); | ||
| 437 | } | ||
| 438 | |||
| 439 | const_reference back() const { | ||
| 440 | return *Traits::GetParent(std::addressof(this->impl.back())); | ||
| 441 | } | ||
| 442 | |||
| 443 | reference front() { | ||
| 444 | return *Traits::GetParent(std::addressof(this->impl.front())); | ||
| 445 | } | ||
| 446 | |||
| 447 | const_reference front() const { | ||
| 448 | return *Traits::GetParent(std::addressof(this->impl.front())); | ||
| 449 | } | ||
| 450 | |||
| 451 | iterator erase(iterator it) { | ||
| 452 | return iterator(this->impl.erase(it.GetImplIterator())); | ||
| 453 | } | ||
| 454 | |||
| 455 | iterator insert(reference ref) { | ||
| 456 | ImplType::pointer node = Traits::GetNode(std::addressof(ref)); | ||
| 457 | this->InsertImpl(node); | ||
| 458 | return iterator(node); | ||
| 459 | } | ||
| 460 | |||
| 461 | iterator find(const_reference ref) const { | ||
| 462 | return iterator(this->FindImpl(Traits::GetNode(std::addressof(ref)))); | ||
| 463 | } | ||
| 464 | |||
| 465 | iterator nfind(const_reference ref) const { | ||
| 466 | return iterator(this->NFindImpl(Traits::GetNode(std::addressof(ref)))); | ||
| 467 | } | ||
| 468 | |||
| 469 | iterator find_light(const_light_reference ref) const { | ||
| 470 | return iterator(this->FindLightImpl(std::addressof(ref))); | ||
| 471 | } | ||
| 472 | |||
| 473 | iterator nfind_light(const_light_reference ref) const { | ||
| 474 | return iterator(this->NFindLightImpl(std::addressof(ref))); | ||
| 475 | } | ||
| 476 | }; | ||
| 477 | |||
| 478 | template <auto T, class Derived = impl::GetParentType<T>> | ||
| 479 | class IntrusiveRedBlackTreeMemberTraits; | ||
| 480 | |||
| 481 | template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived> | ||
| 482 | class IntrusiveRedBlackTreeMemberTraits<Member, Derived> { | ||
| 483 | public: | ||
| 484 | template <class Comparator> | ||
| 485 | using TreeType = IntrusiveRedBlackTree<Derived, IntrusiveRedBlackTreeMemberTraits, Comparator>; | ||
| 486 | using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl; | ||
| 487 | |||
| 488 | private: | ||
| 489 | template <class, class, class> | ||
| 490 | friend class IntrusiveRedBlackTree; | ||
| 491 | |||
| 492 | friend class impl::IntrusiveRedBlackTreeImpl; | ||
| 493 | |||
| 494 | static constexpr IntrusiveRedBlackTreeNode* GetNode(Derived* parent) { | ||
| 495 | return std::addressof(parent->*Member); | ||
| 496 | } | ||
| 497 | |||
| 498 | static constexpr IntrusiveRedBlackTreeNode const* GetNode(Derived const* parent) { | ||
| 499 | return std::addressof(parent->*Member); | ||
| 500 | } | ||
| 501 | |||
| 502 | static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) { | ||
| 503 | return GetParentPointer<Member, Derived>(node); | ||
| 504 | } | ||
| 505 | |||
| 506 | static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) { | ||
| 507 | return GetParentPointer<Member, Derived>(node); | ||
| 508 | } | ||
| 509 | |||
| 510 | private: | ||
| 511 | static constexpr TypedStorage<Derived> DerivedStorage = {}; | ||
| 512 | static_assert(GetParent(GetNode(GetPointer(DerivedStorage))) == GetPointer(DerivedStorage)); | ||
| 513 | }; | ||
| 514 | |||
| 515 | template <auto T, class Derived = impl::GetParentType<T>> | ||
| 516 | class IntrusiveRedBlackTreeMemberTraitsDeferredAssert; | ||
| 517 | |||
| 518 | template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived> | ||
| 519 | class IntrusiveRedBlackTreeMemberTraitsDeferredAssert<Member, Derived> { | ||
| 520 | public: | ||
| 521 | template <class Comparator> | ||
| 522 | using TreeType = | ||
| 523 | IntrusiveRedBlackTree<Derived, IntrusiveRedBlackTreeMemberTraitsDeferredAssert, Comparator>; | ||
| 524 | using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl; | ||
| 525 | |||
| 526 | static constexpr bool IsValid() { | ||
| 527 | TypedStorage<Derived> DerivedStorage = {}; | ||
| 528 | return GetParent(GetNode(GetPointer(DerivedStorage))) == GetPointer(DerivedStorage); | ||
| 529 | } | ||
| 530 | |||
| 531 | private: | ||
| 532 | template <class, class, class> | ||
| 533 | friend class IntrusiveRedBlackTree; | ||
| 534 | |||
| 535 | friend class impl::IntrusiveRedBlackTreeImpl; | ||
| 536 | |||
| 537 | static constexpr IntrusiveRedBlackTreeNode* GetNode(Derived* parent) { | ||
| 538 | return std::addressof(parent->*Member); | ||
| 539 | } | ||
| 540 | |||
| 541 | static constexpr IntrusiveRedBlackTreeNode const* GetNode(Derived const* parent) { | ||
| 542 | return std::addressof(parent->*Member); | ||
| 543 | } | ||
| 544 | |||
| 545 | static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) { | ||
| 546 | return GetParentPointer<Member, Derived>(node); | ||
| 547 | } | ||
| 548 | |||
| 549 | static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) { | ||
| 550 | return GetParentPointer<Member, Derived>(node); | ||
| 551 | } | ||
| 552 | }; | ||
| 553 | |||
| 554 | template <class Derived> | ||
| 555 | class IntrusiveRedBlackTreeBaseNode : public IntrusiveRedBlackTreeNode { | ||
| 556 | public: | ||
| 557 | constexpr Derived* GetPrev() { | ||
| 558 | return static_cast<Derived*>(impl::IntrusiveRedBlackTreeImpl::GetPrev(this)); | ||
| 559 | } | ||
| 560 | constexpr const Derived* GetPrev() const { | ||
| 561 | return static_cast<const Derived*>(impl::IntrusiveRedBlackTreeImpl::GetPrev(this)); | ||
| 562 | } | ||
| 563 | |||
| 564 | constexpr Derived* GetNext() { | ||
| 565 | return static_cast<Derived*>(impl::IntrusiveRedBlackTreeImpl::GetNext(this)); | ||
| 566 | } | ||
| 567 | constexpr const Derived* GetNext() const { | ||
| 568 | return static_cast<const Derived*>(impl::IntrusiveRedBlackTreeImpl::GetNext(this)); | ||
| 569 | } | ||
| 570 | }; | ||
| 571 | |||
| 572 | template <class Derived> | ||
| 573 | class IntrusiveRedBlackTreeBaseTraits { | ||
| 574 | public: | ||
| 575 | template <class Comparator> | ||
| 576 | using TreeType = IntrusiveRedBlackTree<Derived, IntrusiveRedBlackTreeBaseTraits, Comparator>; | ||
| 577 | using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl; | ||
| 578 | |||
| 579 | private: | ||
| 580 | template <class, class, class> | ||
| 581 | friend class IntrusiveRedBlackTree; | ||
| 582 | |||
| 583 | friend class impl::IntrusiveRedBlackTreeImpl; | ||
| 584 | |||
| 585 | static constexpr IntrusiveRedBlackTreeNode* GetNode(Derived* parent) { | ||
| 586 | return static_cast<IntrusiveRedBlackTreeNode*>(parent); | ||
| 587 | } | ||
| 588 | |||
| 589 | static constexpr IntrusiveRedBlackTreeNode const* GetNode(Derived const* parent) { | ||
| 590 | return static_cast<const IntrusiveRedBlackTreeNode*>(parent); | ||
| 591 | } | ||
| 592 | |||
| 593 | static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) { | ||
| 594 | return static_cast<Derived*>(node); | ||
| 595 | } | ||
| 596 | |||
| 597 | static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) { | ||
| 598 | return static_cast<const Derived*>(node); | ||
| 599 | } | ||
| 600 | }; | ||
| 601 | |||
| 602 | } // namespace Common | ||
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 631f64d05..2d4d2e9e7 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp | |||
| @@ -145,10 +145,18 @@ void ColorConsoleBackend::Write(const Entry& entry) { | |||
| 145 | PrintColoredMessage(entry); | 145 | PrintColoredMessage(entry); |
| 146 | } | 146 | } |
| 147 | 147 | ||
| 148 | // _SH_DENYWR allows read only access to the file for other programs. | 148 | FileBackend::FileBackend(const std::string& filename) : bytes_written(0) { |
| 149 | // It is #defined to 0 on other platforms | 149 | if (Common::FS::Exists(filename + ".old.txt")) { |
| 150 | FileBackend::FileBackend(const std::string& filename) | 150 | Common::FS::Delete(filename + ".old.txt"); |
| 151 | : file(filename, "w", _SH_DENYWR), bytes_written(0) {} | 151 | } |
| 152 | if (Common::FS::Exists(filename)) { | ||
| 153 | Common::FS::Rename(filename, filename + ".old.txt"); | ||
| 154 | } | ||
| 155 | |||
| 156 | // _SH_DENYWR allows read only access to the file for other programs. | ||
| 157 | // It is #defined to 0 on other platforms | ||
| 158 | file = Common::FS::IOFile(filename, "w", _SH_DENYWR); | ||
| 159 | } | ||
| 152 | 160 | ||
| 153 | void FileBackend::Write(const Entry& entry) { | 161 | void FileBackend::Write(const Entry& entry) { |
| 154 | // prevent logs from going over the maximum size (in case its spamming and the user doesn't | 162 | // prevent logs from going over the maximum size (in case its spamming and the user doesn't |
diff --git a/src/common/page_table.h b/src/common/page_table.h index 0c14e6433..61c5552e0 100644 --- a/src/common/page_table.h +++ b/src/common/page_table.h | |||
| @@ -90,7 +90,7 @@ struct PageTable { | |||
| 90 | PageTable& operator=(PageTable&&) noexcept = default; | 90 | PageTable& operator=(PageTable&&) noexcept = default; |
| 91 | 91 | ||
| 92 | /** | 92 | /** |
| 93 | * Resizes the page table to be able to accomodate enough pages within | 93 | * Resizes the page table to be able to accommodate enough pages within |
| 94 | * a given address space. | 94 | * a given address space. |
| 95 | * | 95 | * |
| 96 | * @param address_space_width_in_bits The address size width in bits. | 96 | * @param address_space_width_in_bits The address size width in bits. |
diff --git a/src/common/parent_of_member.h b/src/common/parent_of_member.h new file mode 100644 index 000000000..d9a14529d --- /dev/null +++ b/src/common/parent_of_member.h | |||
| @@ -0,0 +1,191 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <type_traits> | ||
| 8 | |||
| 9 | #include "common/assert.h" | ||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 12 | namespace Common { | ||
| 13 | namespace detail { | ||
| 14 | template <typename T, size_t Size, size_t Align> | ||
| 15 | struct TypedStorageImpl { | ||
| 16 | std::aligned_storage_t<Size, Align> storage_; | ||
| 17 | }; | ||
| 18 | } // namespace detail | ||
| 19 | |||
| 20 | template <typename T> | ||
| 21 | using TypedStorage = detail::TypedStorageImpl<T, sizeof(T), alignof(T)>; | ||
| 22 | |||
| 23 | template <typename T> | ||
| 24 | static constexpr T* GetPointer(TypedStorage<T>& ts) { | ||
| 25 | return static_cast<T*>(static_cast<void*>(std::addressof(ts.storage_))); | ||
| 26 | } | ||
| 27 | |||
| 28 | template <typename T> | ||
| 29 | static constexpr const T* GetPointer(const TypedStorage<T>& ts) { | ||
| 30 | return static_cast<const T*>(static_cast<const void*>(std::addressof(ts.storage_))); | ||
| 31 | } | ||
| 32 | |||
| 33 | namespace impl { | ||
| 34 | |||
| 35 | template <size_t MaxDepth> | ||
| 36 | struct OffsetOfUnionHolder { | ||
| 37 | template <typename ParentType, typename MemberType, size_t Offset> | ||
| 38 | union UnionImpl { | ||
| 39 | using PaddingMember = char; | ||
| 40 | static constexpr size_t GetOffset() { | ||
| 41 | return Offset; | ||
| 42 | } | ||
| 43 | |||
| 44 | #pragma pack(push, 1) | ||
| 45 | struct { | ||
| 46 | PaddingMember padding[Offset]; | ||
| 47 | MemberType members[(sizeof(ParentType) / sizeof(MemberType)) + 1]; | ||
| 48 | } data; | ||
| 49 | #pragma pack(pop) | ||
| 50 | UnionImpl<ParentType, MemberType, Offset + 1> next_union; | ||
| 51 | }; | ||
| 52 | |||
| 53 | template <typename ParentType, typename MemberType> | ||
| 54 | union UnionImpl<ParentType, MemberType, 0> { | ||
| 55 | static constexpr size_t GetOffset() { | ||
| 56 | return 0; | ||
| 57 | } | ||
| 58 | |||
| 59 | struct { | ||
| 60 | MemberType members[(sizeof(ParentType) / sizeof(MemberType)) + 1]; | ||
| 61 | } data; | ||
| 62 | UnionImpl<ParentType, MemberType, 1> next_union; | ||
| 63 | }; | ||
| 64 | |||
| 65 | template <typename ParentType, typename MemberType> | ||
| 66 | union UnionImpl<ParentType, MemberType, MaxDepth> {}; | ||
| 67 | }; | ||
| 68 | |||
| 69 | template <typename ParentType, typename MemberType> | ||
| 70 | struct OffsetOfCalculator { | ||
| 71 | using UnionHolder = | ||
| 72 | typename OffsetOfUnionHolder<sizeof(MemberType)>::template UnionImpl<ParentType, MemberType, | ||
| 73 | 0>; | ||
| 74 | union Union { | ||
| 75 | char c{}; | ||
| 76 | UnionHolder first_union; | ||
| 77 | TypedStorage<ParentType> parent; | ||
| 78 | |||
| 79 | constexpr Union() : c() {} | ||
| 80 | }; | ||
| 81 | static constexpr Union U = {}; | ||
| 82 | |||
| 83 | static constexpr const MemberType* GetNextAddress(const MemberType* start, | ||
| 84 | const MemberType* target) { | ||
| 85 | while (start < target) { | ||
| 86 | start++; | ||
| 87 | } | ||
| 88 | return start; | ||
| 89 | } | ||
| 90 | |||
| 91 | static constexpr std::ptrdiff_t GetDifference(const MemberType* start, | ||
| 92 | const MemberType* target) { | ||
| 93 | return (target - start) * sizeof(MemberType); | ||
| 94 | } | ||
| 95 | |||
| 96 | template <typename CurUnion> | ||
| 97 | static constexpr std::ptrdiff_t OffsetOfImpl(MemberType ParentType::*member, | ||
| 98 | CurUnion& cur_union) { | ||
| 99 | constexpr size_t Offset = CurUnion::GetOffset(); | ||
| 100 | const auto target = std::addressof(GetPointer(U.parent)->*member); | ||
| 101 | const auto start = std::addressof(cur_union.data.members[0]); | ||
| 102 | const auto next = GetNextAddress(start, target); | ||
| 103 | |||
| 104 | if (next != target) { | ||
| 105 | if constexpr (Offset < sizeof(MemberType) - 1) { | ||
| 106 | return OffsetOfImpl(member, cur_union.next_union); | ||
| 107 | } else { | ||
| 108 | UNREACHABLE(); | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | return (next - start) * sizeof(MemberType) + Offset; | ||
| 113 | } | ||
| 114 | |||
| 115 | static constexpr std::ptrdiff_t OffsetOf(MemberType ParentType::*member) { | ||
| 116 | return OffsetOfImpl(member, U.first_union); | ||
| 117 | } | ||
| 118 | }; | ||
| 119 | |||
| 120 | template <typename T> | ||
| 121 | struct GetMemberPointerTraits; | ||
| 122 | |||
| 123 | template <typename P, typename M> | ||
| 124 | struct GetMemberPointerTraits<M P::*> { | ||
| 125 | using Parent = P; | ||
| 126 | using Member = M; | ||
| 127 | }; | ||
| 128 | |||
| 129 | template <auto MemberPtr> | ||
| 130 | using GetParentType = typename GetMemberPointerTraits<decltype(MemberPtr)>::Parent; | ||
| 131 | |||
| 132 | template <auto MemberPtr> | ||
| 133 | using GetMemberType = typename GetMemberPointerTraits<decltype(MemberPtr)>::Member; | ||
| 134 | |||
| 135 | template <auto MemberPtr, typename RealParentType = GetParentType<MemberPtr>> | ||
| 136 | static inline std::ptrdiff_t OffsetOf = [] { | ||
| 137 | using DeducedParentType = GetParentType<MemberPtr>; | ||
| 138 | using MemberType = GetMemberType<MemberPtr>; | ||
| 139 | static_assert(std::is_base_of<DeducedParentType, RealParentType>::value || | ||
| 140 | std::is_same<RealParentType, DeducedParentType>::value); | ||
| 141 | |||
| 142 | return OffsetOfCalculator<RealParentType, MemberType>::OffsetOf(MemberPtr); | ||
| 143 | }(); | ||
| 144 | |||
| 145 | } // namespace impl | ||
| 146 | |||
| 147 | template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>> | ||
| 148 | constexpr RealParentType& GetParentReference(impl::GetMemberType<MemberPtr>* member) { | ||
| 149 | std::ptrdiff_t Offset = impl::OffsetOf<MemberPtr, RealParentType>; | ||
| 150 | return *static_cast<RealParentType*>( | ||
| 151 | static_cast<void*>(static_cast<uint8_t*>(static_cast<void*>(member)) - Offset)); | ||
| 152 | } | ||
| 153 | |||
| 154 | template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>> | ||
| 155 | constexpr RealParentType const& GetParentReference(impl::GetMemberType<MemberPtr> const* member) { | ||
| 156 | std::ptrdiff_t Offset = impl::OffsetOf<MemberPtr, RealParentType>; | ||
| 157 | return *static_cast<const RealParentType*>(static_cast<const void*>( | ||
| 158 | static_cast<const uint8_t*>(static_cast<const void*>(member)) - Offset)); | ||
| 159 | } | ||
| 160 | |||
| 161 | template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>> | ||
| 162 | constexpr RealParentType* GetParentPointer(impl::GetMemberType<MemberPtr>* member) { | ||
| 163 | return std::addressof(GetParentReference<MemberPtr, RealParentType>(member)); | ||
| 164 | } | ||
| 165 | |||
| 166 | template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>> | ||
| 167 | constexpr RealParentType const* GetParentPointer(impl::GetMemberType<MemberPtr> const* member) { | ||
| 168 | return std::addressof(GetParentReference<MemberPtr, RealParentType>(member)); | ||
| 169 | } | ||
| 170 | |||
| 171 | template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>> | ||
| 172 | constexpr RealParentType& GetParentReference(impl::GetMemberType<MemberPtr>& member) { | ||
| 173 | return GetParentReference<MemberPtr, RealParentType>(std::addressof(member)); | ||
| 174 | } | ||
| 175 | |||
| 176 | template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>> | ||
| 177 | constexpr RealParentType const& GetParentReference(impl::GetMemberType<MemberPtr> const& member) { | ||
| 178 | return GetParentReference<MemberPtr, RealParentType>(std::addressof(member)); | ||
| 179 | } | ||
| 180 | |||
| 181 | template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>> | ||
| 182 | constexpr RealParentType* GetParentPointer(impl::GetMemberType<MemberPtr>& member) { | ||
| 183 | return std::addressof(GetParentReference<MemberPtr, RealParentType>(member)); | ||
| 184 | } | ||
| 185 | |||
| 186 | template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>> | ||
| 187 | constexpr RealParentType const* GetParentPointer(impl::GetMemberType<MemberPtr> const& member) { | ||
| 188 | return std::addressof(GetParentReference<MemberPtr, RealParentType>(member)); | ||
| 189 | } | ||
| 190 | |||
| 191 | } // namespace Common | ||
diff --git a/src/common/swap.h b/src/common/swap.h index 7665942a2..a80e191dc 100644 --- a/src/common/swap.h +++ b/src/common/swap.h | |||
| @@ -394,7 +394,7 @@ public: | |||
| 394 | template <typename S, typename T2, typename F2> | 394 | template <typename S, typename T2, typename F2> |
| 395 | friend S operator%(const S& p, const swapped_t v); | 395 | friend S operator%(const S& p, const swapped_t v); |
| 396 | 396 | ||
| 397 | // Arithmetics + assignements | 397 | // Arithmetics + assignments |
| 398 | template <typename S, typename T2, typename F2> | 398 | template <typename S, typename T2, typename F2> |
| 399 | friend S operator+=(const S& p, const swapped_t v); | 399 | friend S operator+=(const S& p, const swapped_t v); |
| 400 | 400 | ||
| @@ -451,7 +451,7 @@ S operator%(const S& i, const swap_struct_t<T, F> v) { | |||
| 451 | return i % v.swap(); | 451 | return i % v.swap(); |
| 452 | } | 452 | } |
| 453 | 453 | ||
| 454 | // Arithmetics + assignements | 454 | // Arithmetics + assignments |
| 455 | template <typename S, typename T, typename F> | 455 | template <typename S, typename T, typename F> |
| 456 | S& operator+=(S& i, const swap_struct_t<T, F> v) { | 456 | S& operator+=(S& i, const swap_struct_t<T, F> v) { |
| 457 | i += v.swap(); | 457 | i += v.swap(); |
diff --git a/src/common/timer.cpp b/src/common/timer.cpp deleted file mode 100644 index d17dc2a50..000000000 --- a/src/common/timer.cpp +++ /dev/null | |||
| @@ -1,159 +0,0 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <ctime> | ||
| 6 | #include <fmt/format.h> | ||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/string_util.h" | ||
| 9 | #include "common/timer.h" | ||
| 10 | |||
| 11 | namespace Common { | ||
| 12 | |||
| 13 | std::chrono::milliseconds Timer::GetTimeMs() { | ||
| 14 | return std::chrono::duration_cast<std::chrono::milliseconds>( | ||
| 15 | std::chrono::system_clock::now().time_since_epoch()); | ||
| 16 | } | ||
| 17 | |||
| 18 | // -------------------------------------------- | ||
| 19 | // Initiate, Start, Stop, and Update the time | ||
| 20 | // -------------------------------------------- | ||
| 21 | |||
| 22 | // Set initial values for the class | ||
| 23 | Timer::Timer() : m_LastTime(0), m_StartTime(0), m_Running(false) { | ||
| 24 | Update(); | ||
| 25 | } | ||
| 26 | |||
| 27 | // Write the starting time | ||
| 28 | void Timer::Start() { | ||
| 29 | m_StartTime = GetTimeMs(); | ||
| 30 | m_Running = true; | ||
| 31 | } | ||
| 32 | |||
| 33 | // Stop the timer | ||
| 34 | void Timer::Stop() { | ||
| 35 | // Write the final time | ||
| 36 | m_LastTime = GetTimeMs(); | ||
| 37 | m_Running = false; | ||
| 38 | } | ||
| 39 | |||
| 40 | // Update the last time variable | ||
| 41 | void Timer::Update() { | ||
| 42 | m_LastTime = GetTimeMs(); | ||
| 43 | // TODO(ector) - QPF | ||
| 44 | } | ||
| 45 | |||
| 46 | // ------------------------------------- | ||
| 47 | // Get time difference and elapsed time | ||
| 48 | // ------------------------------------- | ||
| 49 | |||
| 50 | // Get the number of milliseconds since the last Update() | ||
| 51 | std::chrono::milliseconds Timer::GetTimeDifference() { | ||
| 52 | return GetTimeMs() - m_LastTime; | ||
| 53 | } | ||
| 54 | |||
| 55 | // Add the time difference since the last Update() to the starting time. | ||
| 56 | // This is used to compensate for a paused game. | ||
| 57 | void Timer::AddTimeDifference() { | ||
| 58 | m_StartTime += GetTimeDifference(); | ||
| 59 | } | ||
| 60 | |||
| 61 | // Get the time elapsed since the Start() | ||
| 62 | std::chrono::milliseconds Timer::GetTimeElapsed() { | ||
| 63 | // If we have not started yet, return 1 (because then I don't | ||
| 64 | // have to change the FPS calculation in CoreRerecording.cpp . | ||
| 65 | if (m_StartTime.count() == 0) | ||
| 66 | return std::chrono::milliseconds(1); | ||
| 67 | |||
| 68 | // Return the final timer time if the timer is stopped | ||
| 69 | if (!m_Running) | ||
| 70 | return (m_LastTime - m_StartTime); | ||
| 71 | |||
| 72 | return (GetTimeMs() - m_StartTime); | ||
| 73 | } | ||
| 74 | |||
| 75 | // Get the formatted time elapsed since the Start() | ||
| 76 | std::string Timer::GetTimeElapsedFormatted() const { | ||
| 77 | // If we have not started yet, return zero | ||
| 78 | if (m_StartTime.count() == 0) | ||
| 79 | return "00:00:00:000"; | ||
| 80 | |||
| 81 | // The number of milliseconds since the start. | ||
| 82 | // Use a different value if the timer is stopped. | ||
| 83 | std::chrono::milliseconds Milliseconds; | ||
| 84 | if (m_Running) | ||
| 85 | Milliseconds = GetTimeMs() - m_StartTime; | ||
| 86 | else | ||
| 87 | Milliseconds = m_LastTime - m_StartTime; | ||
| 88 | // Seconds | ||
| 89 | std::chrono::seconds Seconds = std::chrono::duration_cast<std::chrono::seconds>(Milliseconds); | ||
| 90 | // Minutes | ||
| 91 | std::chrono::minutes Minutes = std::chrono::duration_cast<std::chrono::minutes>(Milliseconds); | ||
| 92 | // Hours | ||
| 93 | std::chrono::hours Hours = std::chrono::duration_cast<std::chrono::hours>(Milliseconds); | ||
| 94 | |||
| 95 | std::string TmpStr = fmt::format("{:02}:{:02}:{:02}:{:03}", Hours.count(), Minutes.count() % 60, | ||
| 96 | Seconds.count() % 60, Milliseconds.count() % 1000); | ||
| 97 | return TmpStr; | ||
| 98 | } | ||
| 99 | |||
| 100 | // Get the number of seconds since January 1 1970 | ||
| 101 | std::chrono::seconds Timer::GetTimeSinceJan1970() { | ||
| 102 | return std::chrono::duration_cast<std::chrono::seconds>(GetTimeMs()); | ||
| 103 | } | ||
| 104 | |||
| 105 | std::chrono::seconds Timer::GetLocalTimeSinceJan1970() { | ||
| 106 | time_t sysTime, tzDiff, tzDST; | ||
| 107 | struct tm* gmTime; | ||
| 108 | |||
| 109 | time(&sysTime); | ||
| 110 | |||
| 111 | // Account for DST where needed | ||
| 112 | gmTime = localtime(&sysTime); | ||
| 113 | if (gmTime->tm_isdst == 1) | ||
| 114 | tzDST = 3600; | ||
| 115 | else | ||
| 116 | tzDST = 0; | ||
| 117 | |||
| 118 | // Lazy way to get local time in sec | ||
| 119 | gmTime = gmtime(&sysTime); | ||
| 120 | tzDiff = sysTime - mktime(gmTime); | ||
| 121 | |||
| 122 | return std::chrono::seconds(sysTime + tzDiff + tzDST); | ||
| 123 | } | ||
| 124 | |||
| 125 | // Return the current time formatted as Minutes:Seconds:Milliseconds | ||
| 126 | // in the form 00:00:000. | ||
| 127 | std::string Timer::GetTimeFormatted() { | ||
| 128 | time_t sysTime; | ||
| 129 | struct tm* gmTime; | ||
| 130 | char tmp[13]; | ||
| 131 | |||
| 132 | time(&sysTime); | ||
| 133 | gmTime = localtime(&sysTime); | ||
| 134 | |||
| 135 | strftime(tmp, 6, "%M:%S", gmTime); | ||
| 136 | |||
| 137 | u64 milliseconds = static_cast<u64>(GetTimeMs().count()) % 1000; | ||
| 138 | return fmt::format("{}:{:03}", tmp, milliseconds); | ||
| 139 | } | ||
| 140 | |||
| 141 | // Returns a timestamp with decimals for precise time comparisons | ||
| 142 | // ---------------- | ||
| 143 | double Timer::GetDoubleTime() { | ||
| 144 | // Get continuous timestamp | ||
| 145 | auto tmp_seconds = static_cast<u64>(GetTimeSinceJan1970().count()); | ||
| 146 | const auto ms = static_cast<double>(static_cast<u64>(GetTimeMs().count()) % 1000); | ||
| 147 | |||
| 148 | // Remove a few years. We only really want enough seconds to make | ||
| 149 | // sure that we are detecting actual actions, perhaps 60 seconds is | ||
| 150 | // enough really, but I leave a year of seconds anyway, in case the | ||
| 151 | // user's clock is incorrect or something like that. | ||
| 152 | tmp_seconds = tmp_seconds - (38 * 365 * 24 * 60 * 60); | ||
| 153 | |||
| 154 | // Make a smaller integer that fits in the double | ||
| 155 | const auto seconds = static_cast<u32>(tmp_seconds); | ||
| 156 | return seconds + ms; | ||
| 157 | } | ||
| 158 | |||
| 159 | } // Namespace Common | ||
diff --git a/src/common/timer.h b/src/common/timer.h deleted file mode 100644 index 8894a143d..000000000 --- a/src/common/timer.h +++ /dev/null | |||
| @@ -1,41 +0,0 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <chrono> | ||
| 8 | #include <string> | ||
| 9 | #include "common/common_types.h" | ||
| 10 | |||
| 11 | namespace Common { | ||
| 12 | class Timer { | ||
| 13 | public: | ||
| 14 | Timer(); | ||
| 15 | |||
| 16 | void Start(); | ||
| 17 | void Stop(); | ||
| 18 | void Update(); | ||
| 19 | |||
| 20 | // The time difference is always returned in milliseconds, regardless of alternative internal | ||
| 21 | // representation | ||
| 22 | [[nodiscard]] std::chrono::milliseconds GetTimeDifference(); | ||
| 23 | void AddTimeDifference(); | ||
| 24 | |||
| 25 | [[nodiscard]] static std::chrono::seconds GetTimeSinceJan1970(); | ||
| 26 | [[nodiscard]] static std::chrono::seconds GetLocalTimeSinceJan1970(); | ||
| 27 | [[nodiscard]] static double GetDoubleTime(); | ||
| 28 | |||
| 29 | [[nodiscard]] static std::string GetTimeFormatted(); | ||
| 30 | [[nodiscard]] std::string GetTimeElapsedFormatted() const; | ||
| 31 | [[nodiscard]] std::chrono::milliseconds GetTimeElapsed(); | ||
| 32 | |||
| 33 | [[nodiscard]] static std::chrono::milliseconds GetTimeMs(); | ||
| 34 | |||
| 35 | private: | ||
| 36 | std::chrono::milliseconds m_LastTime; | ||
| 37 | std::chrono::milliseconds m_StartTime; | ||
| 38 | bool m_Running; | ||
| 39 | }; | ||
| 40 | |||
| 41 | } // Namespace Common | ||
diff --git a/src/common/tree.h b/src/common/tree.h new file mode 100644 index 000000000..3da49e422 --- /dev/null +++ b/src/common/tree.h | |||
| @@ -0,0 +1,674 @@ | |||
| 1 | /* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */ | ||
| 2 | /* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */ | ||
| 3 | /* $FreeBSD$ */ | ||
| 4 | |||
| 5 | /*- | ||
| 6 | * Copyright 2002 Niels Provos <provos@citi.umich.edu> | ||
| 7 | * All rights reserved. | ||
| 8 | * | ||
| 9 | * Redistribution and use in source and binary forms, with or without | ||
| 10 | * modification, are permitted provided that the following conditions | ||
| 11 | * are met: | ||
| 12 | * 1. Redistributions of source code must retain the above copyright | ||
| 13 | * notice, this list of conditions and the following disclaimer. | ||
| 14 | * 2. Redistributions in binary form must reproduce the above copyright | ||
| 15 | * notice, this list of conditions and the following disclaimer in the | ||
| 16 | * documentation and/or other materials provided with the distribution. | ||
| 17 | * | ||
| 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||
| 19 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
| 20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||
| 21 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
| 22 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
| 23 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
| 27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 28 | */ | ||
| 29 | |||
| 30 | #pragma once | ||
| 31 | |||
| 32 | /* | ||
| 33 | * This file defines data structures for red-black trees. | ||
| 34 | * | ||
| 35 | * A red-black tree is a binary search tree with the node color as an | ||
| 36 | * extra attribute. It fulfills a set of conditions: | ||
| 37 | * - every search path from the root to a leaf consists of the | ||
| 38 | * same number of black nodes, | ||
| 39 | * - each red node (except for the root) has a black parent, | ||
| 40 | * - each leaf node is black. | ||
| 41 | * | ||
| 42 | * Every operation on a red-black tree is bounded as O(lg n). | ||
| 43 | * The maximum height of a red-black tree is 2lg (n+1). | ||
| 44 | */ | ||
| 45 | |||
| 46 | namespace Common { | ||
| 47 | template <typename T> | ||
| 48 | class RBHead { | ||
| 49 | public: | ||
| 50 | [[nodiscard]] T* Root() { | ||
| 51 | return rbh_root; | ||
| 52 | } | ||
| 53 | |||
| 54 | [[nodiscard]] const T* Root() const { | ||
| 55 | return rbh_root; | ||
| 56 | } | ||
| 57 | |||
| 58 | void SetRoot(T* root) { | ||
| 59 | rbh_root = root; | ||
| 60 | } | ||
| 61 | |||
| 62 | [[nodiscard]] bool IsEmpty() const { | ||
| 63 | return Root() == nullptr; | ||
| 64 | } | ||
| 65 | |||
| 66 | private: | ||
| 67 | T* rbh_root = nullptr; | ||
| 68 | }; | ||
| 69 | |||
| 70 | enum class EntryColor { | ||
| 71 | Black, | ||
| 72 | Red, | ||
| 73 | }; | ||
| 74 | |||
| 75 | template <typename T> | ||
| 76 | class RBEntry { | ||
| 77 | public: | ||
| 78 | [[nodiscard]] T* Left() { | ||
| 79 | return rbe_left; | ||
| 80 | } | ||
| 81 | |||
| 82 | [[nodiscard]] const T* Left() const { | ||
| 83 | return rbe_left; | ||
| 84 | } | ||
| 85 | |||
| 86 | void SetLeft(T* left) { | ||
| 87 | rbe_left = left; | ||
| 88 | } | ||
| 89 | |||
| 90 | [[nodiscard]] T* Right() { | ||
| 91 | return rbe_right; | ||
| 92 | } | ||
| 93 | |||
| 94 | [[nodiscard]] const T* Right() const { | ||
| 95 | return rbe_right; | ||
| 96 | } | ||
| 97 | |||
| 98 | void SetRight(T* right) { | ||
| 99 | rbe_right = right; | ||
| 100 | } | ||
| 101 | |||
| 102 | [[nodiscard]] T* Parent() { | ||
| 103 | return rbe_parent; | ||
| 104 | } | ||
| 105 | |||
| 106 | [[nodiscard]] const T* Parent() const { | ||
| 107 | return rbe_parent; | ||
| 108 | } | ||
| 109 | |||
| 110 | void SetParent(T* parent) { | ||
| 111 | rbe_parent = parent; | ||
| 112 | } | ||
| 113 | |||
| 114 | [[nodiscard]] bool IsBlack() const { | ||
| 115 | return rbe_color == EntryColor::Black; | ||
| 116 | } | ||
| 117 | |||
| 118 | [[nodiscard]] bool IsRed() const { | ||
| 119 | return rbe_color == EntryColor::Red; | ||
| 120 | } | ||
| 121 | |||
| 122 | [[nodiscard]] EntryColor Color() const { | ||
| 123 | return rbe_color; | ||
| 124 | } | ||
| 125 | |||
| 126 | void SetColor(EntryColor color) { | ||
| 127 | rbe_color = color; | ||
| 128 | } | ||
| 129 | |||
| 130 | private: | ||
| 131 | T* rbe_left = nullptr; | ||
| 132 | T* rbe_right = nullptr; | ||
| 133 | T* rbe_parent = nullptr; | ||
| 134 | EntryColor rbe_color{}; | ||
| 135 | }; | ||
| 136 | |||
| 137 | template <typename Node> | ||
| 138 | [[nodiscard]] RBEntry<Node>& RB_ENTRY(Node* node) { | ||
| 139 | return node->GetEntry(); | ||
| 140 | } | ||
| 141 | |||
| 142 | template <typename Node> | ||
| 143 | [[nodiscard]] const RBEntry<Node>& RB_ENTRY(const Node* node) { | ||
| 144 | return node->GetEntry(); | ||
| 145 | } | ||
| 146 | |||
| 147 | template <typename Node> | ||
| 148 | [[nodiscard]] Node* RB_PARENT(Node* node) { | ||
| 149 | return RB_ENTRY(node).Parent(); | ||
| 150 | } | ||
| 151 | |||
| 152 | template <typename Node> | ||
| 153 | [[nodiscard]] const Node* RB_PARENT(const Node* node) { | ||
| 154 | return RB_ENTRY(node).Parent(); | ||
| 155 | } | ||
| 156 | |||
| 157 | template <typename Node> | ||
| 158 | void RB_SET_PARENT(Node* node, Node* parent) { | ||
| 159 | return RB_ENTRY(node).SetParent(parent); | ||
| 160 | } | ||
| 161 | |||
| 162 | template <typename Node> | ||
| 163 | [[nodiscard]] Node* RB_LEFT(Node* node) { | ||
| 164 | return RB_ENTRY(node).Left(); | ||
| 165 | } | ||
| 166 | |||
| 167 | template <typename Node> | ||
| 168 | [[nodiscard]] const Node* RB_LEFT(const Node* node) { | ||
| 169 | return RB_ENTRY(node).Left(); | ||
| 170 | } | ||
| 171 | |||
| 172 | template <typename Node> | ||
| 173 | void RB_SET_LEFT(Node* node, Node* left) { | ||
| 174 | return RB_ENTRY(node).SetLeft(left); | ||
| 175 | } | ||
| 176 | |||
| 177 | template <typename Node> | ||
| 178 | [[nodiscard]] Node* RB_RIGHT(Node* node) { | ||
| 179 | return RB_ENTRY(node).Right(); | ||
| 180 | } | ||
| 181 | |||
| 182 | template <typename Node> | ||
| 183 | [[nodiscard]] const Node* RB_RIGHT(const Node* node) { | ||
| 184 | return RB_ENTRY(node).Right(); | ||
| 185 | } | ||
| 186 | |||
| 187 | template <typename Node> | ||
| 188 | void RB_SET_RIGHT(Node* node, Node* right) { | ||
| 189 | return RB_ENTRY(node).SetRight(right); | ||
| 190 | } | ||
| 191 | |||
| 192 | template <typename Node> | ||
| 193 | [[nodiscard]] bool RB_IS_BLACK(const Node* node) { | ||
| 194 | return RB_ENTRY(node).IsBlack(); | ||
| 195 | } | ||
| 196 | |||
| 197 | template <typename Node> | ||
| 198 | [[nodiscard]] bool RB_IS_RED(const Node* node) { | ||
| 199 | return RB_ENTRY(node).IsRed(); | ||
| 200 | } | ||
| 201 | |||
| 202 | template <typename Node> | ||
| 203 | [[nodiscard]] EntryColor RB_COLOR(const Node* node) { | ||
| 204 | return RB_ENTRY(node).Color(); | ||
| 205 | } | ||
| 206 | |||
| 207 | template <typename Node> | ||
| 208 | void RB_SET_COLOR(Node* node, EntryColor color) { | ||
| 209 | return RB_ENTRY(node).SetColor(color); | ||
| 210 | } | ||
| 211 | |||
| 212 | template <typename Node> | ||
| 213 | void RB_SET(Node* node, Node* parent) { | ||
| 214 | auto& entry = RB_ENTRY(node); | ||
| 215 | entry.SetParent(parent); | ||
| 216 | entry.SetLeft(nullptr); | ||
| 217 | entry.SetRight(nullptr); | ||
| 218 | entry.SetColor(EntryColor::Red); | ||
| 219 | } | ||
| 220 | |||
| 221 | template <typename Node> | ||
| 222 | void RB_SET_BLACKRED(Node* black, Node* red) { | ||
| 223 | RB_SET_COLOR(black, EntryColor::Black); | ||
| 224 | RB_SET_COLOR(red, EntryColor::Red); | ||
| 225 | } | ||
| 226 | |||
| 227 | template <typename Node> | ||
| 228 | void RB_ROTATE_LEFT(RBHead<Node>* head, Node* elm, Node*& tmp) { | ||
| 229 | tmp = RB_RIGHT(elm); | ||
| 230 | RB_SET_RIGHT(elm, RB_LEFT(tmp)); | ||
| 231 | if (RB_RIGHT(elm) != nullptr) { | ||
| 232 | RB_SET_PARENT(RB_LEFT(tmp), elm); | ||
| 233 | } | ||
| 234 | |||
| 235 | RB_SET_PARENT(tmp, RB_PARENT(elm)); | ||
| 236 | if (RB_PARENT(tmp) != nullptr) { | ||
| 237 | if (elm == RB_LEFT(RB_PARENT(elm))) { | ||
| 238 | RB_SET_LEFT(RB_PARENT(elm), tmp); | ||
| 239 | } else { | ||
| 240 | RB_SET_RIGHT(RB_PARENT(elm), tmp); | ||
| 241 | } | ||
| 242 | } else { | ||
| 243 | head->SetRoot(tmp); | ||
| 244 | } | ||
| 245 | |||
| 246 | RB_SET_LEFT(tmp, elm); | ||
| 247 | RB_SET_PARENT(elm, tmp); | ||
| 248 | } | ||
| 249 | |||
| 250 | template <typename Node> | ||
| 251 | void RB_ROTATE_RIGHT(RBHead<Node>* head, Node* elm, Node*& tmp) { | ||
| 252 | tmp = RB_LEFT(elm); | ||
| 253 | RB_SET_LEFT(elm, RB_RIGHT(tmp)); | ||
| 254 | if (RB_LEFT(elm) != nullptr) { | ||
| 255 | RB_SET_PARENT(RB_RIGHT(tmp), elm); | ||
| 256 | } | ||
| 257 | |||
| 258 | RB_SET_PARENT(tmp, RB_PARENT(elm)); | ||
| 259 | if (RB_PARENT(tmp) != nullptr) { | ||
| 260 | if (elm == RB_LEFT(RB_PARENT(elm))) { | ||
| 261 | RB_SET_LEFT(RB_PARENT(elm), tmp); | ||
| 262 | } else { | ||
| 263 | RB_SET_RIGHT(RB_PARENT(elm), tmp); | ||
| 264 | } | ||
| 265 | } else { | ||
| 266 | head->SetRoot(tmp); | ||
| 267 | } | ||
| 268 | |||
| 269 | RB_SET_RIGHT(tmp, elm); | ||
| 270 | RB_SET_PARENT(elm, tmp); | ||
| 271 | } | ||
| 272 | |||
| 273 | template <typename Node> | ||
| 274 | void RB_INSERT_COLOR(RBHead<Node>* head, Node* elm) { | ||
| 275 | Node* parent = nullptr; | ||
| 276 | Node* tmp = nullptr; | ||
| 277 | |||
| 278 | while ((parent = RB_PARENT(elm)) != nullptr && RB_IS_RED(parent)) { | ||
| 279 | Node* gparent = RB_PARENT(parent); | ||
| 280 | if (parent == RB_LEFT(gparent)) { | ||
| 281 | tmp = RB_RIGHT(gparent); | ||
| 282 | if (tmp && RB_IS_RED(tmp)) { | ||
| 283 | RB_SET_COLOR(tmp, EntryColor::Black); | ||
| 284 | RB_SET_BLACKRED(parent, gparent); | ||
| 285 | elm = gparent; | ||
| 286 | continue; | ||
| 287 | } | ||
| 288 | |||
| 289 | if (RB_RIGHT(parent) == elm) { | ||
| 290 | RB_ROTATE_LEFT(head, parent, tmp); | ||
| 291 | tmp = parent; | ||
| 292 | parent = elm; | ||
| 293 | elm = tmp; | ||
| 294 | } | ||
| 295 | |||
| 296 | RB_SET_BLACKRED(parent, gparent); | ||
| 297 | RB_ROTATE_RIGHT(head, gparent, tmp); | ||
| 298 | } else { | ||
| 299 | tmp = RB_LEFT(gparent); | ||
| 300 | if (tmp && RB_IS_RED(tmp)) { | ||
| 301 | RB_SET_COLOR(tmp, EntryColor::Black); | ||
| 302 | RB_SET_BLACKRED(parent, gparent); | ||
| 303 | elm = gparent; | ||
| 304 | continue; | ||
| 305 | } | ||
| 306 | |||
| 307 | if (RB_LEFT(parent) == elm) { | ||
| 308 | RB_ROTATE_RIGHT(head, parent, tmp); | ||
| 309 | tmp = parent; | ||
| 310 | parent = elm; | ||
| 311 | elm = tmp; | ||
| 312 | } | ||
| 313 | |||
| 314 | RB_SET_BLACKRED(parent, gparent); | ||
| 315 | RB_ROTATE_LEFT(head, gparent, tmp); | ||
| 316 | } | ||
| 317 | } | ||
| 318 | |||
| 319 | RB_SET_COLOR(head->Root(), EntryColor::Black); | ||
| 320 | } | ||
| 321 | |||
| 322 | template <typename Node> | ||
| 323 | void RB_REMOVE_COLOR(RBHead<Node>* head, Node* parent, Node* elm) { | ||
| 324 | Node* tmp; | ||
| 325 | while ((elm == nullptr || RB_IS_BLACK(elm)) && elm != head->Root()) { | ||
| 326 | if (RB_LEFT(parent) == elm) { | ||
| 327 | tmp = RB_RIGHT(parent); | ||
| 328 | if (RB_IS_RED(tmp)) { | ||
| 329 | RB_SET_BLACKRED(tmp, parent); | ||
| 330 | RB_ROTATE_LEFT(head, parent, tmp); | ||
| 331 | tmp = RB_RIGHT(parent); | ||
| 332 | } | ||
| 333 | |||
| 334 | if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) && | ||
| 335 | (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) { | ||
| 336 | RB_SET_COLOR(tmp, EntryColor::Red); | ||
| 337 | elm = parent; | ||
| 338 | parent = RB_PARENT(elm); | ||
| 339 | } else { | ||
| 340 | if (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp))) { | ||
| 341 | Node* oleft; | ||
| 342 | if ((oleft = RB_LEFT(tmp)) != nullptr) { | ||
| 343 | RB_SET_COLOR(oleft, EntryColor::Black); | ||
| 344 | } | ||
| 345 | |||
| 346 | RB_SET_COLOR(tmp, EntryColor::Red); | ||
| 347 | RB_ROTATE_RIGHT(head, tmp, oleft); | ||
| 348 | tmp = RB_RIGHT(parent); | ||
| 349 | } | ||
| 350 | |||
| 351 | RB_SET_COLOR(tmp, RB_COLOR(parent)); | ||
| 352 | RB_SET_COLOR(parent, EntryColor::Black); | ||
| 353 | if (RB_RIGHT(tmp)) { | ||
| 354 | RB_SET_COLOR(RB_RIGHT(tmp), EntryColor::Black); | ||
| 355 | } | ||
| 356 | |||
| 357 | RB_ROTATE_LEFT(head, parent, tmp); | ||
| 358 | elm = head->Root(); | ||
| 359 | break; | ||
| 360 | } | ||
| 361 | } else { | ||
| 362 | tmp = RB_LEFT(parent); | ||
| 363 | if (RB_IS_RED(tmp)) { | ||
| 364 | RB_SET_BLACKRED(tmp, parent); | ||
| 365 | RB_ROTATE_RIGHT(head, parent, tmp); | ||
| 366 | tmp = RB_LEFT(parent); | ||
| 367 | } | ||
| 368 | |||
| 369 | if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) && | ||
| 370 | (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) { | ||
| 371 | RB_SET_COLOR(tmp, EntryColor::Red); | ||
| 372 | elm = parent; | ||
| 373 | parent = RB_PARENT(elm); | ||
| 374 | } else { | ||
| 375 | if (RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) { | ||
| 376 | Node* oright; | ||
| 377 | if ((oright = RB_RIGHT(tmp)) != nullptr) { | ||
| 378 | RB_SET_COLOR(oright, EntryColor::Black); | ||
| 379 | } | ||
| 380 | |||
| 381 | RB_SET_COLOR(tmp, EntryColor::Red); | ||
| 382 | RB_ROTATE_LEFT(head, tmp, oright); | ||
| 383 | tmp = RB_LEFT(parent); | ||
| 384 | } | ||
| 385 | |||
| 386 | RB_SET_COLOR(tmp, RB_COLOR(parent)); | ||
| 387 | RB_SET_COLOR(parent, EntryColor::Black); | ||
| 388 | |||
| 389 | if (RB_LEFT(tmp)) { | ||
| 390 | RB_SET_COLOR(RB_LEFT(tmp), EntryColor::Black); | ||
| 391 | } | ||
| 392 | |||
| 393 | RB_ROTATE_RIGHT(head, parent, tmp); | ||
| 394 | elm = head->Root(); | ||
| 395 | break; | ||
| 396 | } | ||
| 397 | } | ||
| 398 | } | ||
| 399 | |||
| 400 | if (elm) { | ||
| 401 | RB_SET_COLOR(elm, EntryColor::Black); | ||
| 402 | } | ||
| 403 | } | ||
| 404 | |||
| 405 | template <typename Node> | ||
| 406 | Node* RB_REMOVE(RBHead<Node>* head, Node* elm) { | ||
| 407 | Node* child = nullptr; | ||
| 408 | Node* parent = nullptr; | ||
| 409 | Node* old = elm; | ||
| 410 | EntryColor color{}; | ||
| 411 | |||
| 412 | const auto finalize = [&] { | ||
| 413 | if (color == EntryColor::Black) { | ||
| 414 | RB_REMOVE_COLOR(head, parent, child); | ||
| 415 | } | ||
| 416 | |||
| 417 | return old; | ||
| 418 | }; | ||
| 419 | |||
| 420 | if (RB_LEFT(elm) == nullptr) { | ||
| 421 | child = RB_RIGHT(elm); | ||
| 422 | } else if (RB_RIGHT(elm) == nullptr) { | ||
| 423 | child = RB_LEFT(elm); | ||
| 424 | } else { | ||
| 425 | Node* left; | ||
| 426 | elm = RB_RIGHT(elm); | ||
| 427 | while ((left = RB_LEFT(elm)) != nullptr) { | ||
| 428 | elm = left; | ||
| 429 | } | ||
| 430 | |||
| 431 | child = RB_RIGHT(elm); | ||
| 432 | parent = RB_PARENT(elm); | ||
| 433 | color = RB_COLOR(elm); | ||
| 434 | |||
| 435 | if (child) { | ||
| 436 | RB_SET_PARENT(child, parent); | ||
| 437 | } | ||
| 438 | if (parent) { | ||
| 439 | if (RB_LEFT(parent) == elm) { | ||
| 440 | RB_SET_LEFT(parent, child); | ||
| 441 | } else { | ||
| 442 | RB_SET_RIGHT(parent, child); | ||
| 443 | } | ||
| 444 | } else { | ||
| 445 | head->SetRoot(child); | ||
| 446 | } | ||
| 447 | |||
| 448 | if (RB_PARENT(elm) == old) { | ||
| 449 | parent = elm; | ||
| 450 | } | ||
| 451 | |||
| 452 | elm->SetEntry(old->GetEntry()); | ||
| 453 | |||
| 454 | if (RB_PARENT(old)) { | ||
| 455 | if (RB_LEFT(RB_PARENT(old)) == old) { | ||
| 456 | RB_SET_LEFT(RB_PARENT(old), elm); | ||
| 457 | } else { | ||
| 458 | RB_SET_RIGHT(RB_PARENT(old), elm); | ||
| 459 | } | ||
| 460 | } else { | ||
| 461 | head->SetRoot(elm); | ||
| 462 | } | ||
| 463 | RB_SET_PARENT(RB_LEFT(old), elm); | ||
| 464 | if (RB_RIGHT(old)) { | ||
| 465 | RB_SET_PARENT(RB_RIGHT(old), elm); | ||
| 466 | } | ||
| 467 | if (parent) { | ||
| 468 | left = parent; | ||
| 469 | } | ||
| 470 | |||
| 471 | return finalize(); | ||
| 472 | } | ||
| 473 | |||
| 474 | parent = RB_PARENT(elm); | ||
| 475 | color = RB_COLOR(elm); | ||
| 476 | |||
| 477 | if (child) { | ||
| 478 | RB_SET_PARENT(child, parent); | ||
| 479 | } | ||
| 480 | if (parent) { | ||
| 481 | if (RB_LEFT(parent) == elm) { | ||
| 482 | RB_SET_LEFT(parent, child); | ||
| 483 | } else { | ||
| 484 | RB_SET_RIGHT(parent, child); | ||
| 485 | } | ||
| 486 | } else { | ||
| 487 | head->SetRoot(child); | ||
| 488 | } | ||
| 489 | |||
| 490 | return finalize(); | ||
| 491 | } | ||
| 492 | |||
| 493 | // Inserts a node into the RB tree | ||
| 494 | template <typename Node, typename CompareFunction> | ||
| 495 | Node* RB_INSERT(RBHead<Node>* head, Node* elm, CompareFunction cmp) { | ||
| 496 | Node* parent = nullptr; | ||
| 497 | Node* tmp = head->Root(); | ||
| 498 | int comp = 0; | ||
| 499 | |||
| 500 | while (tmp) { | ||
| 501 | parent = tmp; | ||
| 502 | comp = cmp(elm, parent); | ||
| 503 | if (comp < 0) { | ||
| 504 | tmp = RB_LEFT(tmp); | ||
| 505 | } else if (comp > 0) { | ||
| 506 | tmp = RB_RIGHT(tmp); | ||
| 507 | } else { | ||
| 508 | return tmp; | ||
| 509 | } | ||
| 510 | } | ||
| 511 | |||
| 512 | RB_SET(elm, parent); | ||
| 513 | |||
| 514 | if (parent != nullptr) { | ||
| 515 | if (comp < 0) { | ||
| 516 | RB_SET_LEFT(parent, elm); | ||
| 517 | } else { | ||
| 518 | RB_SET_RIGHT(parent, elm); | ||
| 519 | } | ||
| 520 | } else { | ||
| 521 | head->SetRoot(elm); | ||
| 522 | } | ||
| 523 | |||
| 524 | RB_INSERT_COLOR(head, elm); | ||
| 525 | return nullptr; | ||
| 526 | } | ||
| 527 | |||
| 528 | // Finds the node with the same key as elm | ||
| 529 | template <typename Node, typename CompareFunction> | ||
| 530 | Node* RB_FIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) { | ||
| 531 | Node* tmp = head->Root(); | ||
| 532 | |||
| 533 | while (tmp) { | ||
| 534 | const int comp = cmp(elm, tmp); | ||
| 535 | if (comp < 0) { | ||
| 536 | tmp = RB_LEFT(tmp); | ||
| 537 | } else if (comp > 0) { | ||
| 538 | tmp = RB_RIGHT(tmp); | ||
| 539 | } else { | ||
| 540 | return tmp; | ||
| 541 | } | ||
| 542 | } | ||
| 543 | |||
| 544 | return nullptr; | ||
| 545 | } | ||
| 546 | |||
| 547 | // Finds the first node greater than or equal to the search key | ||
| 548 | template <typename Node, typename CompareFunction> | ||
| 549 | Node* RB_NFIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) { | ||
| 550 | Node* tmp = head->Root(); | ||
| 551 | Node* res = nullptr; | ||
| 552 | |||
| 553 | while (tmp) { | ||
| 554 | const int comp = cmp(elm, tmp); | ||
| 555 | if (comp < 0) { | ||
| 556 | res = tmp; | ||
| 557 | tmp = RB_LEFT(tmp); | ||
| 558 | } else if (comp > 0) { | ||
| 559 | tmp = RB_RIGHT(tmp); | ||
| 560 | } else { | ||
| 561 | return tmp; | ||
| 562 | } | ||
| 563 | } | ||
| 564 | |||
| 565 | return res; | ||
| 566 | } | ||
| 567 | |||
| 568 | // Finds the node with the same key as lelm | ||
| 569 | template <typename Node, typename CompareFunction> | ||
| 570 | Node* RB_FIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) { | ||
| 571 | Node* tmp = head->Root(); | ||
| 572 | |||
| 573 | while (tmp) { | ||
| 574 | const int comp = lcmp(lelm, tmp); | ||
| 575 | if (comp < 0) { | ||
| 576 | tmp = RB_LEFT(tmp); | ||
| 577 | } else if (comp > 0) { | ||
| 578 | tmp = RB_RIGHT(tmp); | ||
| 579 | } else { | ||
| 580 | return tmp; | ||
| 581 | } | ||
| 582 | } | ||
| 583 | |||
| 584 | return nullptr; | ||
| 585 | } | ||
| 586 | |||
| 587 | // Finds the first node greater than or equal to the search key | ||
| 588 | template <typename Node, typename CompareFunction> | ||
| 589 | Node* RB_NFIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) { | ||
| 590 | Node* tmp = head->Root(); | ||
| 591 | Node* res = nullptr; | ||
| 592 | |||
| 593 | while (tmp) { | ||
| 594 | const int comp = lcmp(lelm, tmp); | ||
| 595 | if (comp < 0) { | ||
| 596 | res = tmp; | ||
| 597 | tmp = RB_LEFT(tmp); | ||
| 598 | } else if (comp > 0) { | ||
| 599 | tmp = RB_RIGHT(tmp); | ||
| 600 | } else { | ||
| 601 | return tmp; | ||
| 602 | } | ||
| 603 | } | ||
| 604 | |||
| 605 | return res; | ||
| 606 | } | ||
| 607 | |||
| 608 | template <typename Node> | ||
| 609 | Node* RB_NEXT(Node* elm) { | ||
| 610 | if (RB_RIGHT(elm)) { | ||
| 611 | elm = RB_RIGHT(elm); | ||
| 612 | while (RB_LEFT(elm)) { | ||
| 613 | elm = RB_LEFT(elm); | ||
| 614 | } | ||
| 615 | } else { | ||
| 616 | if (RB_PARENT(elm) && (elm == RB_LEFT(RB_PARENT(elm)))) { | ||
| 617 | elm = RB_PARENT(elm); | ||
| 618 | } else { | ||
| 619 | while (RB_PARENT(elm) && (elm == RB_RIGHT(RB_PARENT(elm)))) { | ||
| 620 | elm = RB_PARENT(elm); | ||
| 621 | } | ||
| 622 | elm = RB_PARENT(elm); | ||
| 623 | } | ||
| 624 | } | ||
| 625 | return elm; | ||
| 626 | } | ||
| 627 | |||
| 628 | template <typename Node> | ||
| 629 | Node* RB_PREV(Node* elm) { | ||
| 630 | if (RB_LEFT(elm)) { | ||
| 631 | elm = RB_LEFT(elm); | ||
| 632 | while (RB_RIGHT(elm)) { | ||
| 633 | elm = RB_RIGHT(elm); | ||
| 634 | } | ||
| 635 | } else { | ||
| 636 | if (RB_PARENT(elm) && (elm == RB_RIGHT(RB_PARENT(elm)))) { | ||
| 637 | elm = RB_PARENT(elm); | ||
| 638 | } else { | ||
| 639 | while (RB_PARENT(elm) && (elm == RB_LEFT(RB_PARENT(elm)))) { | ||
| 640 | elm = RB_PARENT(elm); | ||
| 641 | } | ||
| 642 | elm = RB_PARENT(elm); | ||
| 643 | } | ||
| 644 | } | ||
| 645 | return elm; | ||
| 646 | } | ||
| 647 | |||
| 648 | template <typename Node> | ||
| 649 | Node* RB_MINMAX(RBHead<Node>* head, bool is_min) { | ||
| 650 | Node* tmp = head->Root(); | ||
| 651 | Node* parent = nullptr; | ||
| 652 | |||
| 653 | while (tmp) { | ||
| 654 | parent = tmp; | ||
| 655 | if (is_min) { | ||
| 656 | tmp = RB_LEFT(tmp); | ||
| 657 | } else { | ||
| 658 | tmp = RB_RIGHT(tmp); | ||
| 659 | } | ||
| 660 | } | ||
| 661 | |||
| 662 | return parent; | ||
| 663 | } | ||
| 664 | |||
| 665 | template <typename Node> | ||
| 666 | Node* RB_MIN(RBHead<Node>* head) { | ||
| 667 | return RB_MINMAX(head, true); | ||
| 668 | } | ||
| 669 | |||
| 670 | template <typename Node> | ||
| 671 | Node* RB_MAX(RBHead<Node>* head) { | ||
| 672 | return RB_MINMAX(head, false); | ||
| 673 | } | ||
| 674 | } // namespace Common | ||
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 01f3e9419..99310dc50 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -142,8 +142,6 @@ add_library(core STATIC | |||
| 142 | hardware_interrupt_manager.h | 142 | hardware_interrupt_manager.h |
| 143 | hle/ipc.h | 143 | hle/ipc.h |
| 144 | hle/ipc_helpers.h | 144 | hle/ipc_helpers.h |
| 145 | hle/kernel/address_arbiter.cpp | ||
| 146 | hle/kernel/address_arbiter.h | ||
| 147 | hle/kernel/client_port.cpp | 145 | hle/kernel/client_port.cpp |
| 148 | hle/kernel/client_port.h | 146 | hle/kernel/client_port.h |
| 149 | hle/kernel/client_session.cpp | 147 | hle/kernel/client_session.cpp |
| @@ -157,13 +155,19 @@ add_library(core STATIC | |||
| 157 | hle/kernel/handle_table.h | 155 | hle/kernel/handle_table.h |
| 158 | hle/kernel/hle_ipc.cpp | 156 | hle/kernel/hle_ipc.cpp |
| 159 | hle/kernel/hle_ipc.h | 157 | hle/kernel/hle_ipc.h |
| 158 | hle/kernel/k_address_arbiter.cpp | ||
| 159 | hle/kernel/k_address_arbiter.h | ||
| 160 | hle/kernel/k_affinity_mask.h | 160 | hle/kernel/k_affinity_mask.h |
| 161 | hle/kernel/k_condition_variable.cpp | ||
| 162 | hle/kernel/k_condition_variable.h | ||
| 161 | hle/kernel/k_priority_queue.h | 163 | hle/kernel/k_priority_queue.h |
| 162 | hle/kernel/k_scheduler.cpp | 164 | hle/kernel/k_scheduler.cpp |
| 163 | hle/kernel/k_scheduler.h | 165 | hle/kernel/k_scheduler.h |
| 164 | hle/kernel/k_scheduler_lock.h | 166 | hle/kernel/k_scheduler_lock.h |
| 165 | hle/kernel/k_scoped_lock.h | 167 | hle/kernel/k_scoped_lock.h |
| 166 | hle/kernel/k_scoped_scheduler_lock_and_sleep.h | 168 | hle/kernel/k_scoped_scheduler_lock_and_sleep.h |
| 169 | hle/kernel/k_synchronization_object.cpp | ||
| 170 | hle/kernel/k_synchronization_object.h | ||
| 167 | hle/kernel/kernel.cpp | 171 | hle/kernel/kernel.cpp |
| 168 | hle/kernel/kernel.h | 172 | hle/kernel/kernel.h |
| 169 | hle/kernel/memory/address_space_info.cpp | 173 | hle/kernel/memory/address_space_info.cpp |
| @@ -183,8 +187,6 @@ add_library(core STATIC | |||
| 183 | hle/kernel/memory/slab_heap.h | 187 | hle/kernel/memory/slab_heap.h |
| 184 | hle/kernel/memory/system_control.cpp | 188 | hle/kernel/memory/system_control.cpp |
| 185 | hle/kernel/memory/system_control.h | 189 | hle/kernel/memory/system_control.h |
| 186 | hle/kernel/mutex.cpp | ||
| 187 | hle/kernel/mutex.h | ||
| 188 | hle/kernel/object.cpp | 190 | hle/kernel/object.cpp |
| 189 | hle/kernel/object.h | 191 | hle/kernel/object.h |
| 190 | hle/kernel/physical_core.cpp | 192 | hle/kernel/physical_core.cpp |
| @@ -210,12 +212,10 @@ add_library(core STATIC | |||
| 210 | hle/kernel/shared_memory.h | 212 | hle/kernel/shared_memory.h |
| 211 | hle/kernel/svc.cpp | 213 | hle/kernel/svc.cpp |
| 212 | hle/kernel/svc.h | 214 | hle/kernel/svc.h |
| 215 | hle/kernel/svc_common.h | ||
| 216 | hle/kernel/svc_results.h | ||
| 213 | hle/kernel/svc_types.h | 217 | hle/kernel/svc_types.h |
| 214 | hle/kernel/svc_wrap.h | 218 | hle/kernel/svc_wrap.h |
| 215 | hle/kernel/synchronization_object.cpp | ||
| 216 | hle/kernel/synchronization_object.h | ||
| 217 | hle/kernel/synchronization.cpp | ||
| 218 | hle/kernel/synchronization.h | ||
| 219 | hle/kernel/thread.cpp | 219 | hle/kernel/thread.cpp |
| 220 | hle/kernel/thread.h | 220 | hle/kernel/thread.h |
| 221 | hle/kernel/time_manager.cpp | 221 | hle/kernel/time_manager.cpp |
| @@ -635,15 +635,15 @@ if (MSVC) | |||
| 635 | /we4267 | 635 | /we4267 |
| 636 | # 'context' : truncation from 'type1' to 'type2' | 636 | # 'context' : truncation from 'type1' to 'type2' |
| 637 | /we4305 | 637 | /we4305 |
| 638 | # 'function' : not all control paths return a value | ||
| 639 | /we4715 | ||
| 638 | ) | 640 | ) |
| 639 | else() | 641 | else() |
| 640 | target_compile_options(core PRIVATE | 642 | target_compile_options(core PRIVATE |
| 641 | -Werror=conversion | 643 | -Werror=conversion |
| 642 | -Werror=ignored-qualifiers | 644 | -Werror=ignored-qualifiers |
| 643 | -Werror=implicit-fallthrough | 645 | -Werror=implicit-fallthrough |
| 644 | -Werror=reorder | ||
| 645 | -Werror=sign-compare | 646 | -Werror=sign-compare |
| 646 | -Werror=unused-variable | ||
| 647 | 647 | ||
| 648 | $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter> | 648 | $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter> |
| 649 | $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable> | 649 | $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable> |
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 70098c526..9a0151736 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h | |||
| @@ -26,9 +26,10 @@ using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CO | |||
| 26 | /// Generic ARMv8 CPU interface | 26 | /// Generic ARMv8 CPU interface |
| 27 | class ARM_Interface : NonCopyable { | 27 | class ARM_Interface : NonCopyable { |
| 28 | public: | 28 | public: |
| 29 | explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers, bool uses_wall_clock) | 29 | explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers_, |
| 30 | : system{system_}, interrupt_handlers{interrupt_handlers}, uses_wall_clock{ | 30 | bool uses_wall_clock_) |
| 31 | uses_wall_clock} {} | 31 | : system{system_}, interrupt_handlers{interrupt_handlers_}, uses_wall_clock{ |
| 32 | uses_wall_clock_} {} | ||
| 32 | virtual ~ARM_Interface() = default; | 33 | virtual ~ARM_Interface() = default; |
| 33 | 34 | ||
| 34 | struct ThreadContext32 { | 35 | struct ThreadContext32 { |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 8aaf11eee..6c4c8e9e4 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp | |||
| @@ -71,15 +71,8 @@ public: | |||
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { | 73 | void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { |
| 74 | switch (exception) { | ||
| 75 | case Dynarmic::A32::Exception::UndefinedInstruction: | ||
| 76 | case Dynarmic::A32::Exception::UnpredictableInstruction: | ||
| 77 | break; | ||
| 78 | case Dynarmic::A32::Exception::Breakpoint: | ||
| 79 | break; | ||
| 80 | } | ||
| 81 | LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", | 74 | LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", |
| 82 | static_cast<std::size_t>(exception), pc, MemoryReadCode(pc)); | 75 | exception, pc, MemoryReadCode(pc)); |
| 83 | UNIMPLEMENTED(); | 76 | UNIMPLEMENTED(); |
| 84 | } | 77 | } |
| 85 | 78 | ||
| @@ -181,6 +174,9 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable& | |||
| 181 | if (Settings::values.cpuopt_unsafe_reduce_fp_error) { | 174 | if (Settings::values.cpuopt_unsafe_reduce_fp_error) { |
| 182 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; | 175 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; |
| 183 | } | 176 | } |
| 177 | if (Settings::values.cpuopt_unsafe_inaccurate_nan) { | ||
| 178 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; | ||
| 179 | } | ||
| 184 | } | 180 | } |
| 185 | 181 | ||
| 186 | return std::make_unique<Dynarmic::A32::Jit>(config); | 182 | return std::make_unique<Dynarmic::A32::Jit>(config); |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index d2e1dc724..4c5ebca22 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp | |||
| @@ -212,6 +212,9 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable& | |||
| 212 | if (Settings::values.cpuopt_unsafe_reduce_fp_error) { | 212 | if (Settings::values.cpuopt_unsafe_reduce_fp_error) { |
| 213 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; | 213 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; |
| 214 | } | 214 | } |
| 215 | if (Settings::values.cpuopt_unsafe_inaccurate_nan) { | ||
| 216 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; | ||
| 217 | } | ||
| 215 | } | 218 | } |
| 216 | 219 | ||
| 217 | return std::make_shared<Dynarmic::A64::Jit>(config); | 220 | return std::make_shared<Dynarmic::A64::Jit>(config); |
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index e6c8461a5..874b5673a 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp | |||
| @@ -49,6 +49,7 @@ void CoreTiming::ThreadEntry(CoreTiming& instance) { | |||
| 49 | Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh); | 49 | Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh); |
| 50 | instance.on_thread_init(); | 50 | instance.on_thread_init(); |
| 51 | instance.ThreadLoop(); | 51 | instance.ThreadLoop(); |
| 52 | MicroProfileOnThreadExit(); | ||
| 52 | } | 53 | } |
| 53 | 54 | ||
| 54 | void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) { | 55 | void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) { |
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index da15f764a..cebe2ce37 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp | |||
| @@ -143,6 +143,7 @@ u64 GetSignatureTypeDataSize(SignatureType type) { | |||
| 143 | return 0x3C; | 143 | return 0x3C; |
| 144 | } | 144 | } |
| 145 | UNREACHABLE(); | 145 | UNREACHABLE(); |
| 146 | return 0; | ||
| 146 | } | 147 | } |
| 147 | 148 | ||
| 148 | u64 GetSignatureTypePaddingSize(SignatureType type) { | 149 | u64 GetSignatureTypePaddingSize(SignatureType type) { |
| @@ -157,6 +158,7 @@ u64 GetSignatureTypePaddingSize(SignatureType type) { | |||
| 157 | return 0x40; | 158 | return 0x40; |
| 158 | } | 159 | } |
| 159 | UNREACHABLE(); | 160 | UNREACHABLE(); |
| 161 | return 0; | ||
| 160 | } | 162 | } |
| 161 | 163 | ||
| 162 | SignatureType Ticket::GetSignatureType() const { | 164 | SignatureType Ticket::GetSignatureType() const { |
| @@ -169,8 +171,7 @@ SignatureType Ticket::GetSignatureType() const { | |||
| 169 | if (const auto* ticket = std::get_if<ECDSATicket>(&data)) { | 171 | if (const auto* ticket = std::get_if<ECDSATicket>(&data)) { |
| 170 | return ticket->sig_type; | 172 | return ticket->sig_type; |
| 171 | } | 173 | } |
| 172 | 174 | throw std::bad_variant_access{}; | |
| 173 | UNREACHABLE(); | ||
| 174 | } | 175 | } |
| 175 | 176 | ||
| 176 | TicketData& Ticket::GetData() { | 177 | TicketData& Ticket::GetData() { |
| @@ -183,8 +184,7 @@ TicketData& Ticket::GetData() { | |||
| 183 | if (auto* ticket = std::get_if<ECDSATicket>(&data)) { | 184 | if (auto* ticket = std::get_if<ECDSATicket>(&data)) { |
| 184 | return ticket->data; | 185 | return ticket->data; |
| 185 | } | 186 | } |
| 186 | 187 | throw std::bad_variant_access{}; | |
| 187 | UNREACHABLE(); | ||
| 188 | } | 188 | } |
| 189 | 189 | ||
| 190 | const TicketData& Ticket::GetData() const { | 190 | const TicketData& Ticket::GetData() const { |
| @@ -197,8 +197,7 @@ const TicketData& Ticket::GetData() const { | |||
| 197 | if (const auto* ticket = std::get_if<ECDSATicket>(&data)) { | 197 | if (const auto* ticket = std::get_if<ECDSATicket>(&data)) { |
| 198 | return ticket->data; | 198 | return ticket->data; |
| 199 | } | 199 | } |
| 200 | 200 | throw std::bad_variant_access{}; | |
| 201 | UNREACHABLE(); | ||
| 202 | } | 201 | } |
| 203 | 202 | ||
| 204 | u64 Ticket::GetSize() const { | 203 | u64 Ticket::GetSize() const { |
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index a6c0337fa..d12218fc2 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp | |||
| @@ -43,17 +43,17 @@ static_assert(sizeof(IVFCLevel) == 0x18, "IVFCLevel has incorrect size."); | |||
| 43 | struct IVFCHeader { | 43 | struct IVFCHeader { |
| 44 | u32_le magic; | 44 | u32_le magic; |
| 45 | u32_le magic_number; | 45 | u32_le magic_number; |
| 46 | INSERT_UNION_PADDING_BYTES(8); | 46 | INSERT_PADDING_BYTES_NOINIT(8); |
| 47 | std::array<IVFCLevel, 6> levels; | 47 | std::array<IVFCLevel, 6> levels; |
| 48 | INSERT_UNION_PADDING_BYTES(64); | 48 | INSERT_PADDING_BYTES_NOINIT(64); |
| 49 | }; | 49 | }; |
| 50 | static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size."); | 50 | static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size."); |
| 51 | 51 | ||
| 52 | struct NCASectionHeaderBlock { | 52 | struct NCASectionHeaderBlock { |
| 53 | INSERT_UNION_PADDING_BYTES(3); | 53 | INSERT_PADDING_BYTES_NOINIT(3); |
| 54 | NCASectionFilesystemType filesystem_type; | 54 | NCASectionFilesystemType filesystem_type; |
| 55 | NCASectionCryptoType crypto_type; | 55 | NCASectionCryptoType crypto_type; |
| 56 | INSERT_UNION_PADDING_BYTES(3); | 56 | INSERT_PADDING_BYTES_NOINIT(3); |
| 57 | }; | 57 | }; |
| 58 | static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size."); | 58 | static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size."); |
| 59 | 59 | ||
| @@ -61,7 +61,7 @@ struct NCASectionRaw { | |||
| 61 | NCASectionHeaderBlock header; | 61 | NCASectionHeaderBlock header; |
| 62 | std::array<u8, 0x138> block_data; | 62 | std::array<u8, 0x138> block_data; |
| 63 | std::array<u8, 0x8> section_ctr; | 63 | std::array<u8, 0x8> section_ctr; |
| 64 | INSERT_UNION_PADDING_BYTES(0xB8); | 64 | INSERT_PADDING_BYTES_NOINIT(0xB8); |
| 65 | }; | 65 | }; |
| 66 | static_assert(sizeof(NCASectionRaw) == 0x200, "NCASectionRaw has incorrect size."); | 66 | static_assert(sizeof(NCASectionRaw) == 0x200, "NCASectionRaw has incorrect size."); |
| 67 | 67 | ||
| @@ -69,19 +69,19 @@ struct PFS0Superblock { | |||
| 69 | NCASectionHeaderBlock header_block; | 69 | NCASectionHeaderBlock header_block; |
| 70 | std::array<u8, 0x20> hash; | 70 | std::array<u8, 0x20> hash; |
| 71 | u32_le size; | 71 | u32_le size; |
| 72 | INSERT_UNION_PADDING_BYTES(4); | 72 | INSERT_PADDING_BYTES_NOINIT(4); |
| 73 | u64_le hash_table_offset; | 73 | u64_le hash_table_offset; |
| 74 | u64_le hash_table_size; | 74 | u64_le hash_table_size; |
| 75 | u64_le pfs0_header_offset; | 75 | u64_le pfs0_header_offset; |
| 76 | u64_le pfs0_size; | 76 | u64_le pfs0_size; |
| 77 | INSERT_UNION_PADDING_BYTES(0x1B0); | 77 | INSERT_PADDING_BYTES_NOINIT(0x1B0); |
| 78 | }; | 78 | }; |
| 79 | static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size."); | 79 | static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size."); |
| 80 | 80 | ||
| 81 | struct RomFSSuperblock { | 81 | struct RomFSSuperblock { |
| 82 | NCASectionHeaderBlock header_block; | 82 | NCASectionHeaderBlock header_block; |
| 83 | IVFCHeader ivfc; | 83 | IVFCHeader ivfc; |
| 84 | INSERT_UNION_PADDING_BYTES(0x118); | 84 | INSERT_PADDING_BYTES_NOINIT(0x118); |
| 85 | }; | 85 | }; |
| 86 | static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size."); | 86 | static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size."); |
| 87 | 87 | ||
| @@ -89,19 +89,19 @@ struct BKTRHeader { | |||
| 89 | u64_le offset; | 89 | u64_le offset; |
| 90 | u64_le size; | 90 | u64_le size; |
| 91 | u32_le magic; | 91 | u32_le magic; |
| 92 | INSERT_UNION_PADDING_BYTES(0x4); | 92 | INSERT_PADDING_BYTES_NOINIT(0x4); |
| 93 | u32_le number_entries; | 93 | u32_le number_entries; |
| 94 | INSERT_UNION_PADDING_BYTES(0x4); | 94 | INSERT_PADDING_BYTES_NOINIT(0x4); |
| 95 | }; | 95 | }; |
| 96 | static_assert(sizeof(BKTRHeader) == 0x20, "BKTRHeader has incorrect size."); | 96 | static_assert(sizeof(BKTRHeader) == 0x20, "BKTRHeader has incorrect size."); |
| 97 | 97 | ||
| 98 | struct BKTRSuperblock { | 98 | struct BKTRSuperblock { |
| 99 | NCASectionHeaderBlock header_block; | 99 | NCASectionHeaderBlock header_block; |
| 100 | IVFCHeader ivfc; | 100 | IVFCHeader ivfc; |
| 101 | INSERT_UNION_PADDING_BYTES(0x18); | 101 | INSERT_PADDING_BYTES_NOINIT(0x18); |
| 102 | BKTRHeader relocation; | 102 | BKTRHeader relocation; |
| 103 | BKTRHeader subsection; | 103 | BKTRHeader subsection; |
| 104 | INSERT_UNION_PADDING_BYTES(0xC0); | 104 | INSERT_PADDING_BYTES_NOINIT(0xC0); |
| 105 | }; | 105 | }; |
| 106 | static_assert(sizeof(BKTRSuperblock) == 0x200, "BKTRSuperblock has incorrect size."); | 106 | static_assert(sizeof(BKTRSuperblock) == 0x200, "BKTRSuperblock has incorrect size."); |
| 107 | 107 | ||
diff --git a/src/core/file_sys/nca_patch.cpp b/src/core/file_sys/nca_patch.cpp index adcf0732f..a65ec6798 100644 --- a/src/core/file_sys/nca_patch.cpp +++ b/src/core/file_sys/nca_patch.cpp | |||
| @@ -51,8 +51,8 @@ std::pair<std::size_t, std::size_t> SearchBucketEntry(u64 offset, const BlockTyp | |||
| 51 | low = mid + 1; | 51 | low = mid + 1; |
| 52 | } | 52 | } |
| 53 | } | 53 | } |
| 54 | |||
| 55 | UNREACHABLE_MSG("Offset could not be found in BKTR block."); | 54 | UNREACHABLE_MSG("Offset could not be found in BKTR block."); |
| 55 | return {0, 0}; | ||
| 56 | } | 56 | } |
| 57 | } // Anonymous namespace | 57 | } // Anonymous namespace |
| 58 | 58 | ||
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index da01002d5..431302f55 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp | |||
| @@ -105,7 +105,8 @@ ContentRecordType GetCRTypeFromNCAType(NCAContentType type) { | |||
| 105 | // TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal. | 105 | // TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal. |
| 106 | return ContentRecordType::HtmlDocument; | 106 | return ContentRecordType::HtmlDocument; |
| 107 | default: | 107 | default: |
| 108 | UNREACHABLE_MSG("Invalid NCAContentType={:02X}", static_cast<u8>(type)); | 108 | UNREACHABLE_MSG("Invalid NCAContentType={:02X}", type); |
| 109 | return ContentRecordType{}; | ||
| 109 | } | 110 | } |
| 110 | } | 111 | } |
| 111 | 112 | ||
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index 5b414b0f0..b08a1687a 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h | |||
| @@ -67,18 +67,18 @@ public: | |||
| 67 | virtual void Refresh() = 0; | 67 | virtual void Refresh() = 0; |
| 68 | 68 | ||
| 69 | virtual bool HasEntry(u64 title_id, ContentRecordType type) const = 0; | 69 | virtual bool HasEntry(u64 title_id, ContentRecordType type) const = 0; |
| 70 | virtual bool HasEntry(ContentProviderEntry entry) const; | 70 | bool HasEntry(ContentProviderEntry entry) const; |
| 71 | 71 | ||
| 72 | virtual std::optional<u32> GetEntryVersion(u64 title_id) const = 0; | 72 | virtual std::optional<u32> GetEntryVersion(u64 title_id) const = 0; |
| 73 | 73 | ||
| 74 | virtual VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const = 0; | 74 | virtual VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const = 0; |
| 75 | virtual VirtualFile GetEntryUnparsed(ContentProviderEntry entry) const; | 75 | VirtualFile GetEntryUnparsed(ContentProviderEntry entry) const; |
| 76 | 76 | ||
| 77 | virtual VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const = 0; | 77 | virtual VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const = 0; |
| 78 | virtual VirtualFile GetEntryRaw(ContentProviderEntry entry) const; | 78 | VirtualFile GetEntryRaw(ContentProviderEntry entry) const; |
| 79 | 79 | ||
| 80 | virtual std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const = 0; | 80 | virtual std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const = 0; |
| 81 | virtual std::unique_ptr<NCA> GetEntry(ContentProviderEntry entry) const; | 81 | std::unique_ptr<NCA> GetEntry(ContentProviderEntry entry) const; |
| 82 | 82 | ||
| 83 | virtual std::vector<ContentProviderEntry> ListEntries() const; | 83 | virtual std::vector<ContentProviderEntry> ListEntries() const; |
| 84 | 84 | ||
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h index 7ce313190..79bcf5762 100644 --- a/src/core/hle/ipc.h +++ b/src/core/hle/ipc.h | |||
| @@ -160,7 +160,7 @@ struct DomainMessageHeader { | |||
| 160 | // Used when responding to an IPC request, Server -> Client. | 160 | // Used when responding to an IPC request, Server -> Client. |
| 161 | struct { | 161 | struct { |
| 162 | u32_le num_objects; | 162 | u32_le num_objects; |
| 163 | INSERT_UNION_PADDING_WORDS(3); | 163 | INSERT_PADDING_WORDS_NOINIT(3); |
| 164 | }; | 164 | }; |
| 165 | 165 | ||
| 166 | // Used when performing an IPC request, Client -> Server. | 166 | // Used when performing an IPC request, Client -> Server. |
| @@ -171,7 +171,7 @@ struct DomainMessageHeader { | |||
| 171 | BitField<16, 16, u32> size; | 171 | BitField<16, 16, u32> size; |
| 172 | }; | 172 | }; |
| 173 | u32_le object_id; | 173 | u32_le object_id; |
| 174 | INSERT_UNION_PADDING_WORDS(2); | 174 | INSERT_PADDING_WORDS_NOINIT(2); |
| 175 | }; | 175 | }; |
| 176 | 176 | ||
| 177 | std::array<u32, 4> raw{}; | 177 | std::array<u32, 4> raw{}; |
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp deleted file mode 100644 index 20ffa7d47..000000000 --- a/src/core/hle/kernel/address_arbiter.cpp +++ /dev/null | |||
| @@ -1,317 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <vector> | ||
| 7 | |||
| 8 | #include "common/assert.h" | ||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "core/arm/exclusive_monitor.h" | ||
| 11 | #include "core/core.h" | ||
| 12 | #include "core/hle/kernel/address_arbiter.h" | ||
| 13 | #include "core/hle/kernel/errors.h" | ||
| 14 | #include "core/hle/kernel/handle_table.h" | ||
| 15 | #include "core/hle/kernel/k_scheduler.h" | ||
| 16 | #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" | ||
| 17 | #include "core/hle/kernel/kernel.h" | ||
| 18 | #include "core/hle/kernel/thread.h" | ||
| 19 | #include "core/hle/kernel/time_manager.h" | ||
| 20 | #include "core/hle/result.h" | ||
| 21 | #include "core/memory.h" | ||
| 22 | |||
| 23 | namespace Kernel { | ||
| 24 | |||
| 25 | // Wake up num_to_wake (or all) threads in a vector. | ||
| 26 | void AddressArbiter::WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads, | ||
| 27 | s32 num_to_wake) { | ||
| 28 | // Only process up to 'target' threads, unless 'target' is <= 0, in which case process | ||
| 29 | // them all. | ||
| 30 | std::size_t last = waiting_threads.size(); | ||
| 31 | if (num_to_wake > 0) { | ||
| 32 | last = std::min(last, static_cast<std::size_t>(num_to_wake)); | ||
| 33 | } | ||
| 34 | |||
| 35 | // Signal the waiting threads. | ||
| 36 | for (std::size_t i = 0; i < last; i++) { | ||
| 37 | waiting_threads[i]->SetSynchronizationResults(nullptr, RESULT_SUCCESS); | ||
| 38 | RemoveThread(waiting_threads[i]); | ||
| 39 | waiting_threads[i]->WaitForArbitration(false); | ||
| 40 | waiting_threads[i]->ResumeFromWait(); | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | AddressArbiter::AddressArbiter(Core::System& system) : system{system} {} | ||
| 45 | AddressArbiter::~AddressArbiter() = default; | ||
| 46 | |||
| 47 | ResultCode AddressArbiter::SignalToAddress(VAddr address, SignalType type, s32 value, | ||
| 48 | s32 num_to_wake) { | ||
| 49 | switch (type) { | ||
| 50 | case SignalType::Signal: | ||
| 51 | return SignalToAddressOnly(address, num_to_wake); | ||
| 52 | case SignalType::IncrementAndSignalIfEqual: | ||
| 53 | return IncrementAndSignalToAddressIfEqual(address, value, num_to_wake); | ||
| 54 | case SignalType::ModifyByWaitingCountAndSignalIfEqual: | ||
| 55 | return ModifyByWaitingCountAndSignalToAddressIfEqual(address, value, num_to_wake); | ||
| 56 | default: | ||
| 57 | return ERR_INVALID_ENUM_VALUE; | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) { | ||
| 62 | KScopedSchedulerLock lock(system.Kernel()); | ||
| 63 | const std::vector<std::shared_ptr<Thread>> waiting_threads = | ||
| 64 | GetThreadsWaitingOnAddress(address); | ||
| 65 | WakeThreads(waiting_threads, num_to_wake); | ||
| 66 | return RESULT_SUCCESS; | ||
| 67 | } | ||
| 68 | |||
| 69 | ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, | ||
| 70 | s32 num_to_wake) { | ||
| 71 | KScopedSchedulerLock lock(system.Kernel()); | ||
| 72 | auto& memory = system.Memory(); | ||
| 73 | |||
| 74 | // Ensure that we can write to the address. | ||
| 75 | if (!memory.IsValidVirtualAddress(address)) { | ||
| 76 | return ERR_INVALID_ADDRESS_STATE; | ||
| 77 | } | ||
| 78 | |||
| 79 | const std::size_t current_core = system.CurrentCoreIndex(); | ||
| 80 | auto& monitor = system.Monitor(); | ||
| 81 | u32 current_value; | ||
| 82 | do { | ||
| 83 | current_value = monitor.ExclusiveRead32(current_core, address); | ||
| 84 | |||
| 85 | if (current_value != static_cast<u32>(value)) { | ||
| 86 | return ERR_INVALID_STATE; | ||
| 87 | } | ||
| 88 | current_value++; | ||
| 89 | } while (!monitor.ExclusiveWrite32(current_core, address, current_value)); | ||
| 90 | |||
| 91 | return SignalToAddressOnly(address, num_to_wake); | ||
| 92 | } | ||
| 93 | |||
| 94 | ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, | ||
| 95 | s32 num_to_wake) { | ||
| 96 | KScopedSchedulerLock lock(system.Kernel()); | ||
| 97 | auto& memory = system.Memory(); | ||
| 98 | |||
| 99 | // Ensure that we can write to the address. | ||
| 100 | if (!memory.IsValidVirtualAddress(address)) { | ||
| 101 | return ERR_INVALID_ADDRESS_STATE; | ||
| 102 | } | ||
| 103 | |||
| 104 | // Get threads waiting on the address. | ||
| 105 | const std::vector<std::shared_ptr<Thread>> waiting_threads = | ||
| 106 | GetThreadsWaitingOnAddress(address); | ||
| 107 | |||
| 108 | const std::size_t current_core = system.CurrentCoreIndex(); | ||
| 109 | auto& monitor = system.Monitor(); | ||
| 110 | s32 updated_value; | ||
| 111 | do { | ||
| 112 | updated_value = monitor.ExclusiveRead32(current_core, address); | ||
| 113 | |||
| 114 | if (updated_value != value) { | ||
| 115 | return ERR_INVALID_STATE; | ||
| 116 | } | ||
| 117 | // Determine the modified value depending on the waiting count. | ||
| 118 | if (num_to_wake <= 0) { | ||
| 119 | if (waiting_threads.empty()) { | ||
| 120 | updated_value = value + 1; | ||
| 121 | } else { | ||
| 122 | updated_value = value - 1; | ||
| 123 | } | ||
| 124 | } else { | ||
| 125 | if (waiting_threads.empty()) { | ||
| 126 | updated_value = value + 1; | ||
| 127 | } else if (waiting_threads.size() <= static_cast<u32>(num_to_wake)) { | ||
| 128 | updated_value = value - 1; | ||
| 129 | } else { | ||
| 130 | updated_value = value; | ||
| 131 | } | ||
| 132 | } | ||
| 133 | } while (!monitor.ExclusiveWrite32(current_core, address, updated_value)); | ||
| 134 | |||
| 135 | WakeThreads(waiting_threads, num_to_wake); | ||
| 136 | return RESULT_SUCCESS; | ||
| 137 | } | ||
| 138 | |||
| 139 | ResultCode AddressArbiter::WaitForAddress(VAddr address, ArbitrationType type, s32 value, | ||
| 140 | s64 timeout_ns) { | ||
| 141 | switch (type) { | ||
| 142 | case ArbitrationType::WaitIfLessThan: | ||
| 143 | return WaitForAddressIfLessThan(address, value, timeout_ns, false); | ||
| 144 | case ArbitrationType::DecrementAndWaitIfLessThan: | ||
| 145 | return WaitForAddressIfLessThan(address, value, timeout_ns, true); | ||
| 146 | case ArbitrationType::WaitIfEqual: | ||
| 147 | return WaitForAddressIfEqual(address, value, timeout_ns); | ||
| 148 | default: | ||
| 149 | return ERR_INVALID_ENUM_VALUE; | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, | ||
| 154 | bool should_decrement) { | ||
| 155 | auto& memory = system.Memory(); | ||
| 156 | auto& kernel = system.Kernel(); | ||
| 157 | Thread* current_thread = kernel.CurrentScheduler()->GetCurrentThread(); | ||
| 158 | |||
| 159 | Handle event_handle = InvalidHandle; | ||
| 160 | { | ||
| 161 | KScopedSchedulerLockAndSleep lock(kernel, event_handle, current_thread, timeout); | ||
| 162 | |||
| 163 | if (current_thread->IsPendingTermination()) { | ||
| 164 | lock.CancelSleep(); | ||
| 165 | return ERR_THREAD_TERMINATING; | ||
| 166 | } | ||
| 167 | |||
| 168 | // Ensure that we can read the address. | ||
| 169 | if (!memory.IsValidVirtualAddress(address)) { | ||
| 170 | lock.CancelSleep(); | ||
| 171 | return ERR_INVALID_ADDRESS_STATE; | ||
| 172 | } | ||
| 173 | |||
| 174 | s32 current_value = static_cast<s32>(memory.Read32(address)); | ||
| 175 | if (current_value >= value) { | ||
| 176 | lock.CancelSleep(); | ||
| 177 | return ERR_INVALID_STATE; | ||
| 178 | } | ||
| 179 | |||
| 180 | current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); | ||
| 181 | |||
| 182 | s32 decrement_value; | ||
| 183 | |||
| 184 | const std::size_t current_core = system.CurrentCoreIndex(); | ||
| 185 | auto& monitor = system.Monitor(); | ||
| 186 | do { | ||
| 187 | current_value = static_cast<s32>(monitor.ExclusiveRead32(current_core, address)); | ||
| 188 | if (should_decrement) { | ||
| 189 | decrement_value = current_value - 1; | ||
| 190 | } else { | ||
| 191 | decrement_value = current_value; | ||
| 192 | } | ||
| 193 | } while ( | ||
| 194 | !monitor.ExclusiveWrite32(current_core, address, static_cast<u32>(decrement_value))); | ||
| 195 | |||
| 196 | // Short-circuit without rescheduling, if timeout is zero. | ||
| 197 | if (timeout == 0) { | ||
| 198 | lock.CancelSleep(); | ||
| 199 | return RESULT_TIMEOUT; | ||
| 200 | } | ||
| 201 | |||
| 202 | current_thread->SetArbiterWaitAddress(address); | ||
| 203 | InsertThread(SharedFrom(current_thread)); | ||
| 204 | current_thread->SetStatus(ThreadStatus::WaitArb); | ||
| 205 | current_thread->WaitForArbitration(true); | ||
| 206 | } | ||
| 207 | |||
| 208 | if (event_handle != InvalidHandle) { | ||
| 209 | auto& time_manager = kernel.TimeManager(); | ||
| 210 | time_manager.UnscheduleTimeEvent(event_handle); | ||
| 211 | } | ||
| 212 | |||
| 213 | { | ||
| 214 | KScopedSchedulerLock lock(kernel); | ||
| 215 | if (current_thread->IsWaitingForArbitration()) { | ||
| 216 | RemoveThread(SharedFrom(current_thread)); | ||
| 217 | current_thread->WaitForArbitration(false); | ||
| 218 | } | ||
| 219 | } | ||
| 220 | |||
| 221 | return current_thread->GetSignalingResult(); | ||
| 222 | } | ||
| 223 | |||
| 224 | ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) { | ||
| 225 | auto& memory = system.Memory(); | ||
| 226 | auto& kernel = system.Kernel(); | ||
| 227 | Thread* current_thread = kernel.CurrentScheduler()->GetCurrentThread(); | ||
| 228 | |||
| 229 | Handle event_handle = InvalidHandle; | ||
| 230 | { | ||
| 231 | KScopedSchedulerLockAndSleep lock(kernel, event_handle, current_thread, timeout); | ||
| 232 | |||
| 233 | if (current_thread->IsPendingTermination()) { | ||
| 234 | lock.CancelSleep(); | ||
| 235 | return ERR_THREAD_TERMINATING; | ||
| 236 | } | ||
| 237 | |||
| 238 | // Ensure that we can read the address. | ||
| 239 | if (!memory.IsValidVirtualAddress(address)) { | ||
| 240 | lock.CancelSleep(); | ||
| 241 | return ERR_INVALID_ADDRESS_STATE; | ||
| 242 | } | ||
| 243 | |||
| 244 | s32 current_value = static_cast<s32>(memory.Read32(address)); | ||
| 245 | if (current_value != value) { | ||
| 246 | lock.CancelSleep(); | ||
| 247 | return ERR_INVALID_STATE; | ||
| 248 | } | ||
| 249 | |||
| 250 | // Short-circuit without rescheduling, if timeout is zero. | ||
| 251 | if (timeout == 0) { | ||
| 252 | lock.CancelSleep(); | ||
| 253 | return RESULT_TIMEOUT; | ||
| 254 | } | ||
| 255 | |||
| 256 | current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); | ||
| 257 | current_thread->SetArbiterWaitAddress(address); | ||
| 258 | InsertThread(SharedFrom(current_thread)); | ||
| 259 | current_thread->SetStatus(ThreadStatus::WaitArb); | ||
| 260 | current_thread->WaitForArbitration(true); | ||
| 261 | } | ||
| 262 | |||
| 263 | if (event_handle != InvalidHandle) { | ||
| 264 | auto& time_manager = kernel.TimeManager(); | ||
| 265 | time_manager.UnscheduleTimeEvent(event_handle); | ||
| 266 | } | ||
| 267 | |||
| 268 | { | ||
| 269 | KScopedSchedulerLock lock(kernel); | ||
| 270 | if (current_thread->IsWaitingForArbitration()) { | ||
| 271 | RemoveThread(SharedFrom(current_thread)); | ||
| 272 | current_thread->WaitForArbitration(false); | ||
| 273 | } | ||
| 274 | } | ||
| 275 | |||
| 276 | return current_thread->GetSignalingResult(); | ||
| 277 | } | ||
| 278 | |||
| 279 | void AddressArbiter::InsertThread(std::shared_ptr<Thread> thread) { | ||
| 280 | const VAddr arb_addr = thread->GetArbiterWaitAddress(); | ||
| 281 | std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[arb_addr]; | ||
| 282 | |||
| 283 | const auto iter = | ||
| 284 | std::find_if(thread_list.cbegin(), thread_list.cend(), [&thread](const auto& entry) { | ||
| 285 | return entry->GetPriority() >= thread->GetPriority(); | ||
| 286 | }); | ||
| 287 | |||
| 288 | if (iter == thread_list.cend()) { | ||
| 289 | thread_list.push_back(std::move(thread)); | ||
| 290 | } else { | ||
| 291 | thread_list.insert(iter, std::move(thread)); | ||
| 292 | } | ||
| 293 | } | ||
| 294 | |||
| 295 | void AddressArbiter::RemoveThread(std::shared_ptr<Thread> thread) { | ||
| 296 | const VAddr arb_addr = thread->GetArbiterWaitAddress(); | ||
| 297 | std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[arb_addr]; | ||
| 298 | |||
| 299 | const auto iter = std::find_if(thread_list.cbegin(), thread_list.cend(), | ||
| 300 | [&thread](const auto& entry) { return thread == entry; }); | ||
| 301 | |||
| 302 | if (iter != thread_list.cend()) { | ||
| 303 | thread_list.erase(iter); | ||
| 304 | } | ||
| 305 | } | ||
| 306 | |||
| 307 | std::vector<std::shared_ptr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress( | ||
| 308 | VAddr address) const { | ||
| 309 | const auto iter = arb_threads.find(address); | ||
| 310 | if (iter == arb_threads.cend()) { | ||
| 311 | return {}; | ||
| 312 | } | ||
| 313 | |||
| 314 | const std::list<std::shared_ptr<Thread>>& thread_list = iter->second; | ||
| 315 | return {thread_list.cbegin(), thread_list.cend()}; | ||
| 316 | } | ||
| 317 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h deleted file mode 100644 index b91edc67d..000000000 --- a/src/core/hle/kernel/address_arbiter.h +++ /dev/null | |||
| @@ -1,91 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <list> | ||
| 8 | #include <memory> | ||
| 9 | #include <unordered_map> | ||
| 10 | #include <vector> | ||
| 11 | |||
| 12 | #include "common/common_types.h" | ||
| 13 | |||
| 14 | union ResultCode; | ||
| 15 | |||
| 16 | namespace Core { | ||
| 17 | class System; | ||
| 18 | } | ||
| 19 | |||
| 20 | namespace Kernel { | ||
| 21 | |||
| 22 | class Thread; | ||
| 23 | |||
| 24 | class AddressArbiter { | ||
| 25 | public: | ||
| 26 | enum class ArbitrationType { | ||
| 27 | WaitIfLessThan = 0, | ||
| 28 | DecrementAndWaitIfLessThan = 1, | ||
| 29 | WaitIfEqual = 2, | ||
| 30 | }; | ||
| 31 | |||
| 32 | enum class SignalType { | ||
| 33 | Signal = 0, | ||
| 34 | IncrementAndSignalIfEqual = 1, | ||
| 35 | ModifyByWaitingCountAndSignalIfEqual = 2, | ||
| 36 | }; | ||
| 37 | |||
| 38 | explicit AddressArbiter(Core::System& system); | ||
| 39 | ~AddressArbiter(); | ||
| 40 | |||
| 41 | AddressArbiter(const AddressArbiter&) = delete; | ||
| 42 | AddressArbiter& operator=(const AddressArbiter&) = delete; | ||
| 43 | |||
| 44 | AddressArbiter(AddressArbiter&&) = default; | ||
| 45 | AddressArbiter& operator=(AddressArbiter&&) = delete; | ||
| 46 | |||
| 47 | /// Signals an address being waited on with a particular signaling type. | ||
| 48 | ResultCode SignalToAddress(VAddr address, SignalType type, s32 value, s32 num_to_wake); | ||
| 49 | |||
| 50 | /// Waits on an address with a particular arbitration type. | ||
| 51 | ResultCode WaitForAddress(VAddr address, ArbitrationType type, s32 value, s64 timeout_ns); | ||
| 52 | |||
| 53 | private: | ||
| 54 | /// Signals an address being waited on. | ||
| 55 | ResultCode SignalToAddressOnly(VAddr address, s32 num_to_wake); | ||
| 56 | |||
| 57 | /// Signals an address being waited on and increments its value if equal to the value argument. | ||
| 58 | ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake); | ||
| 59 | |||
| 60 | /// Signals an address being waited on and modifies its value based on waiting thread count if | ||
| 61 | /// equal to the value argument. | ||
| 62 | ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, | ||
| 63 | s32 num_to_wake); | ||
| 64 | |||
| 65 | /// Waits on an address if the value passed is less than the argument value, | ||
| 66 | /// optionally decrementing. | ||
| 67 | ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, | ||
| 68 | bool should_decrement); | ||
| 69 | |||
| 70 | /// Waits on an address if the value passed is equal to the argument value. | ||
| 71 | ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout); | ||
| 72 | |||
| 73 | /// Wake up num_to_wake (or all) threads in a vector. | ||
| 74 | void WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads, s32 num_to_wake); | ||
| 75 | |||
| 76 | /// Insert a thread into the address arbiter container | ||
| 77 | void InsertThread(std::shared_ptr<Thread> thread); | ||
| 78 | |||
| 79 | /// Removes a thread from the address arbiter container | ||
| 80 | void RemoveThread(std::shared_ptr<Thread> thread); | ||
| 81 | |||
| 82 | // Gets the threads waiting on an address. | ||
| 83 | std::vector<std::shared_ptr<Thread>> GetThreadsWaitingOnAddress(VAddr address) const; | ||
| 84 | |||
| 85 | /// List of threads waiting for a address arbiter | ||
| 86 | std::unordered_map<VAddr, std::list<std::shared_ptr<Thread>>> arb_threads; | ||
| 87 | |||
| 88 | Core::System& system; | ||
| 89 | }; | ||
| 90 | |||
| 91 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp index 8aff2227a..f8f005f15 100644 --- a/src/core/hle/kernel/client_port.cpp +++ b/src/core/hle/kernel/client_port.cpp | |||
| @@ -33,9 +33,6 @@ ResultVal<std::shared_ptr<ClientSession>> ClientPort::Connect() { | |||
| 33 | server_port->AppendPendingSession(std::move(server)); | 33 | server_port->AppendPendingSession(std::move(server)); |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | // Wake the threads waiting on the ServerPort | ||
| 37 | server_port->Signal(); | ||
| 38 | |||
| 39 | return MakeResult(std::move(client)); | 36 | return MakeResult(std::move(client)); |
| 40 | } | 37 | } |
| 41 | 38 | ||
diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp index be9eba519..e8e52900d 100644 --- a/src/core/hle/kernel/client_session.cpp +++ b/src/core/hle/kernel/client_session.cpp | |||
| @@ -12,7 +12,7 @@ | |||
| 12 | 12 | ||
| 13 | namespace Kernel { | 13 | namespace Kernel { |
| 14 | 14 | ||
| 15 | ClientSession::ClientSession(KernelCore& kernel) : SynchronizationObject{kernel} {} | 15 | ClientSession::ClientSession(KernelCore& kernel) : KSynchronizationObject{kernel} {} |
| 16 | 16 | ||
| 17 | ClientSession::~ClientSession() { | 17 | ClientSession::~ClientSession() { |
| 18 | // This destructor will be called automatically when the last ClientSession handle is closed by | 18 | // This destructor will be called automatically when the last ClientSession handle is closed by |
| @@ -22,15 +22,6 @@ ClientSession::~ClientSession() { | |||
| 22 | } | 22 | } |
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | bool ClientSession::ShouldWait(const Thread* thread) const { | ||
| 26 | UNIMPLEMENTED(); | ||
| 27 | return {}; | ||
| 28 | } | ||
| 29 | |||
| 30 | void ClientSession::Acquire(Thread* thread) { | ||
| 31 | UNIMPLEMENTED(); | ||
| 32 | } | ||
| 33 | |||
| 34 | bool ClientSession::IsSignaled() const { | 25 | bool ClientSession::IsSignaled() const { |
| 35 | UNIMPLEMENTED(); | 26 | UNIMPLEMENTED(); |
| 36 | return true; | 27 | return true; |
diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h index e5e0690c2..d5c9ebee8 100644 --- a/src/core/hle/kernel/client_session.h +++ b/src/core/hle/kernel/client_session.h | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <string> | 8 | #include <string> |
| 9 | 9 | ||
| 10 | #include "core/hle/kernel/synchronization_object.h" | 10 | #include "core/hle/kernel/k_synchronization_object.h" |
| 11 | #include "core/hle/result.h" | 11 | #include "core/hle/result.h" |
| 12 | 12 | ||
| 13 | union ResultCode; | 13 | union ResultCode; |
| @@ -26,7 +26,7 @@ class KernelCore; | |||
| 26 | class Session; | 26 | class Session; |
| 27 | class Thread; | 27 | class Thread; |
| 28 | 28 | ||
| 29 | class ClientSession final : public SynchronizationObject { | 29 | class ClientSession final : public KSynchronizationObject { |
| 30 | public: | 30 | public: |
| 31 | explicit ClientSession(KernelCore& kernel); | 31 | explicit ClientSession(KernelCore& kernel); |
| 32 | ~ClientSession() override; | 32 | ~ClientSession() override; |
| @@ -49,10 +49,6 @@ public: | |||
| 49 | ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory, | 49 | ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory, |
| 50 | Core::Timing::CoreTiming& core_timing); | 50 | Core::Timing::CoreTiming& core_timing); |
| 51 | 51 | ||
| 52 | bool ShouldWait(const Thread* thread) const override; | ||
| 53 | |||
| 54 | void Acquire(Thread* thread) override; | ||
| 55 | |||
| 56 | bool IsSignaled() const override; | 52 | bool IsSignaled() const override; |
| 57 | 53 | ||
| 58 | private: | 54 | private: |
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index d4e5d88cf..7d32a39f0 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h | |||
| @@ -13,12 +13,14 @@ namespace Kernel { | |||
| 13 | constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7}; | 13 | constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7}; |
| 14 | constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14}; | 14 | constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14}; |
| 15 | constexpr ResultCode ERR_THREAD_TERMINATING{ErrorModule::Kernel, 59}; | 15 | constexpr ResultCode ERR_THREAD_TERMINATING{ErrorModule::Kernel, 59}; |
| 16 | constexpr ResultCode ERR_TERMINATION_REQUESTED{ErrorModule::Kernel, 59}; | ||
| 16 | constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101}; | 17 | constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101}; |
| 17 | constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102}; | 18 | constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102}; |
| 18 | constexpr ResultCode ERR_OUT_OF_RESOURCES{ErrorModule::Kernel, 103}; | 19 | constexpr ResultCode ERR_OUT_OF_RESOURCES{ErrorModule::Kernel, 103}; |
| 19 | constexpr ResultCode ERR_OUT_OF_MEMORY{ErrorModule::Kernel, 104}; | 20 | constexpr ResultCode ERR_OUT_OF_MEMORY{ErrorModule::Kernel, 104}; |
| 20 | constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105}; | 21 | constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105}; |
| 21 | constexpr ResultCode ERR_INVALID_ADDRESS_STATE{ErrorModule::Kernel, 106}; | 22 | constexpr ResultCode ERR_INVALID_ADDRESS_STATE{ErrorModule::Kernel, 106}; |
| 23 | constexpr ResultCode ERR_INVALID_CURRENT_MEMORY{ErrorModule::Kernel, 106}; | ||
| 22 | constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS{ErrorModule::Kernel, 108}; | 24 | constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS{ErrorModule::Kernel, 108}; |
| 23 | constexpr ResultCode ERR_INVALID_MEMORY_RANGE{ErrorModule::Kernel, 110}; | 25 | constexpr ResultCode ERR_INVALID_MEMORY_RANGE{ErrorModule::Kernel, 110}; |
| 24 | constexpr ResultCode ERR_INVALID_PROCESSOR_ID{ErrorModule::Kernel, 113}; | 26 | constexpr ResultCode ERR_INVALID_PROCESSOR_ID{ErrorModule::Kernel, 113}; |
| @@ -28,6 +30,7 @@ constexpr ResultCode ERR_INVALID_POINTER{ErrorModule::Kernel, 115}; | |||
| 28 | constexpr ResultCode ERR_INVALID_COMBINATION{ErrorModule::Kernel, 116}; | 30 | constexpr ResultCode ERR_INVALID_COMBINATION{ErrorModule::Kernel, 116}; |
| 29 | constexpr ResultCode RESULT_TIMEOUT{ErrorModule::Kernel, 117}; | 31 | constexpr ResultCode RESULT_TIMEOUT{ErrorModule::Kernel, 117}; |
| 30 | constexpr ResultCode ERR_SYNCHRONIZATION_CANCELED{ErrorModule::Kernel, 118}; | 32 | constexpr ResultCode ERR_SYNCHRONIZATION_CANCELED{ErrorModule::Kernel, 118}; |
| 33 | constexpr ResultCode ERR_CANCELLED{ErrorModule::Kernel, 118}; | ||
| 31 | constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119}; | 34 | constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119}; |
| 32 | constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120}; | 35 | constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120}; |
| 33 | constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121}; | 36 | constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121}; |
diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp new file mode 100644 index 000000000..d9e702f13 --- /dev/null +++ b/src/core/hle/kernel/k_address_arbiter.cpp | |||
| @@ -0,0 +1,367 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/arm/exclusive_monitor.h" | ||
| 6 | #include "core/core.h" | ||
| 7 | #include "core/hle/kernel/k_address_arbiter.h" | ||
| 8 | #include "core/hle/kernel/k_scheduler.h" | ||
| 9 | #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" | ||
| 10 | #include "core/hle/kernel/kernel.h" | ||
| 11 | #include "core/hle/kernel/svc_results.h" | ||
| 12 | #include "core/hle/kernel/thread.h" | ||
| 13 | #include "core/hle/kernel/time_manager.h" | ||
| 14 | #include "core/memory.h" | ||
| 15 | |||
| 16 | namespace Kernel { | ||
| 17 | |||
| 18 | KAddressArbiter::KAddressArbiter(Core::System& system_) | ||
| 19 | : system{system_}, kernel{system.Kernel()} {} | ||
| 20 | KAddressArbiter::~KAddressArbiter() = default; | ||
| 21 | |||
| 22 | namespace { | ||
| 23 | |||
| 24 | bool ReadFromUser(Core::System& system, s32* out, VAddr address) { | ||
| 25 | *out = system.Memory().Read32(address); | ||
| 26 | return true; | ||
| 27 | } | ||
| 28 | |||
| 29 | bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 value) { | ||
| 30 | auto& monitor = system.Monitor(); | ||
| 31 | const auto current_core = system.CurrentCoreIndex(); | ||
| 32 | |||
| 33 | // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. | ||
| 34 | // TODO(bunnei): We should call CanAccessAtomic(..) here. | ||
| 35 | |||
| 36 | // Load the value from the address. | ||
| 37 | const s32 current_value = static_cast<s32>(monitor.ExclusiveRead32(current_core, address)); | ||
| 38 | |||
| 39 | // Compare it to the desired one. | ||
| 40 | if (current_value < value) { | ||
| 41 | // If less than, we want to try to decrement. | ||
| 42 | const s32 decrement_value = current_value - 1; | ||
| 43 | |||
| 44 | // Decrement and try to store. | ||
| 45 | if (!monitor.ExclusiveWrite32(current_core, address, static_cast<u32>(decrement_value))) { | ||
| 46 | // If we failed to store, try again. | ||
| 47 | DecrementIfLessThan(system, out, address, value); | ||
| 48 | } | ||
| 49 | } else { | ||
| 50 | // Otherwise, clear our exclusive hold and finish | ||
| 51 | monitor.ClearExclusive(); | ||
| 52 | } | ||
| 53 | |||
| 54 | // We're done. | ||
| 55 | *out = current_value; | ||
| 56 | return true; | ||
| 57 | } | ||
| 58 | |||
| 59 | bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 new_value) { | ||
| 60 | auto& monitor = system.Monitor(); | ||
| 61 | const auto current_core = system.CurrentCoreIndex(); | ||
| 62 | |||
| 63 | // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. | ||
| 64 | // TODO(bunnei): We should call CanAccessAtomic(..) here. | ||
| 65 | |||
| 66 | // Load the value from the address. | ||
| 67 | const s32 current_value = static_cast<s32>(monitor.ExclusiveRead32(current_core, address)); | ||
| 68 | |||
| 69 | // Compare it to the desired one. | ||
| 70 | if (current_value == value) { | ||
| 71 | // If equal, we want to try to write the new value. | ||
| 72 | |||
| 73 | // Try to store. | ||
| 74 | if (!monitor.ExclusiveWrite32(current_core, address, static_cast<u32>(new_value))) { | ||
| 75 | // If we failed to store, try again. | ||
| 76 | UpdateIfEqual(system, out, address, value, new_value); | ||
| 77 | } | ||
| 78 | } else { | ||
| 79 | // Otherwise, clear our exclusive hold and finish. | ||
| 80 | monitor.ClearExclusive(); | ||
| 81 | } | ||
| 82 | |||
| 83 | // We're done. | ||
| 84 | *out = current_value; | ||
| 85 | return true; | ||
| 86 | } | ||
| 87 | |||
| 88 | } // namespace | ||
| 89 | |||
| 90 | ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) { | ||
| 91 | // Perform signaling. | ||
| 92 | s32 num_waiters{}; | ||
| 93 | { | ||
| 94 | KScopedSchedulerLock sl(kernel); | ||
| 95 | |||
| 96 | auto it = thread_tree.nfind_light({addr, -1}); | ||
| 97 | while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && | ||
| 98 | (it->GetAddressArbiterKey() == addr)) { | ||
| 99 | Thread* target_thread = std::addressof(*it); | ||
| 100 | target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS); | ||
| 101 | |||
| 102 | ASSERT(target_thread->IsWaitingForAddressArbiter()); | ||
| 103 | target_thread->Wakeup(); | ||
| 104 | |||
| 105 | it = thread_tree.erase(it); | ||
| 106 | target_thread->ClearAddressArbiter(); | ||
| 107 | ++num_waiters; | ||
| 108 | } | ||
| 109 | } | ||
| 110 | return RESULT_SUCCESS; | ||
| 111 | } | ||
| 112 | |||
| 113 | ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count) { | ||
| 114 | // Perform signaling. | ||
| 115 | s32 num_waiters{}; | ||
| 116 | { | ||
| 117 | KScopedSchedulerLock sl(kernel); | ||
| 118 | |||
| 119 | // Check the userspace value. | ||
| 120 | s32 user_value{}; | ||
| 121 | R_UNLESS(UpdateIfEqual(system, std::addressof(user_value), addr, value, value + 1), | ||
| 122 | Svc::ResultInvalidCurrentMemory); | ||
| 123 | R_UNLESS(user_value == value, Svc::ResultInvalidState); | ||
| 124 | |||
| 125 | auto it = thread_tree.nfind_light({addr, -1}); | ||
| 126 | while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && | ||
| 127 | (it->GetAddressArbiterKey() == addr)) { | ||
| 128 | Thread* target_thread = std::addressof(*it); | ||
| 129 | target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS); | ||
| 130 | |||
| 131 | ASSERT(target_thread->IsWaitingForAddressArbiter()); | ||
| 132 | target_thread->Wakeup(); | ||
| 133 | |||
| 134 | it = thread_tree.erase(it); | ||
| 135 | target_thread->ClearAddressArbiter(); | ||
| 136 | ++num_waiters; | ||
| 137 | } | ||
| 138 | } | ||
| 139 | return RESULT_SUCCESS; | ||
| 140 | } | ||
| 141 | |||
| 142 | ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count) { | ||
| 143 | // Perform signaling. | ||
| 144 | s32 num_waiters{}; | ||
| 145 | { | ||
| 146 | KScopedSchedulerLock sl(kernel); | ||
| 147 | |||
| 148 | auto it = thread_tree.nfind_light({addr, -1}); | ||
| 149 | // Determine the updated value. | ||
| 150 | s32 new_value{}; | ||
| 151 | if (/*GetTargetFirmware() >= TargetFirmware_7_0_0*/ true) { | ||
| 152 | if (count <= 0) { | ||
| 153 | if ((it != thread_tree.end()) && (it->GetAddressArbiterKey() == addr)) { | ||
| 154 | new_value = value - 2; | ||
| 155 | } else { | ||
| 156 | new_value = value + 1; | ||
| 157 | } | ||
| 158 | } else { | ||
| 159 | if ((it != thread_tree.end()) && (it->GetAddressArbiterKey() == addr)) { | ||
| 160 | auto tmp_it = it; | ||
| 161 | s32 tmp_num_waiters{}; | ||
| 162 | while ((++tmp_it != thread_tree.end()) && | ||
| 163 | (tmp_it->GetAddressArbiterKey() == addr)) { | ||
| 164 | if ((tmp_num_waiters++) >= count) { | ||
| 165 | break; | ||
| 166 | } | ||
| 167 | } | ||
| 168 | |||
| 169 | if (tmp_num_waiters < count) { | ||
| 170 | new_value = value - 1; | ||
| 171 | } else { | ||
| 172 | new_value = value; | ||
| 173 | } | ||
| 174 | } else { | ||
| 175 | new_value = value + 1; | ||
| 176 | } | ||
| 177 | } | ||
| 178 | } else { | ||
| 179 | if (count <= 0) { | ||
| 180 | if ((it != thread_tree.end()) && (it->GetAddressArbiterKey() == addr)) { | ||
| 181 | new_value = value - 1; | ||
| 182 | } else { | ||
| 183 | new_value = value + 1; | ||
| 184 | } | ||
| 185 | } else { | ||
| 186 | auto tmp_it = it; | ||
| 187 | s32 tmp_num_waiters{}; | ||
| 188 | while ((tmp_it != thread_tree.end()) && (tmp_it->GetAddressArbiterKey() == addr) && | ||
| 189 | (tmp_num_waiters < count + 1)) { | ||
| 190 | ++tmp_num_waiters; | ||
| 191 | ++tmp_it; | ||
| 192 | } | ||
| 193 | |||
| 194 | if (tmp_num_waiters == 0) { | ||
| 195 | new_value = value + 1; | ||
| 196 | } else if (tmp_num_waiters <= count) { | ||
| 197 | new_value = value - 1; | ||
| 198 | } else { | ||
| 199 | new_value = value; | ||
| 200 | } | ||
| 201 | } | ||
| 202 | } | ||
| 203 | |||
| 204 | // Check the userspace value. | ||
| 205 | s32 user_value{}; | ||
| 206 | bool succeeded{}; | ||
| 207 | if (value != new_value) { | ||
| 208 | succeeded = UpdateIfEqual(system, std::addressof(user_value), addr, value, new_value); | ||
| 209 | } else { | ||
| 210 | succeeded = ReadFromUser(system, std::addressof(user_value), addr); | ||
| 211 | } | ||
| 212 | |||
| 213 | R_UNLESS(succeeded, Svc::ResultInvalidCurrentMemory); | ||
| 214 | R_UNLESS(user_value == value, Svc::ResultInvalidState); | ||
| 215 | |||
| 216 | while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && | ||
| 217 | (it->GetAddressArbiterKey() == addr)) { | ||
| 218 | Thread* target_thread = std::addressof(*it); | ||
| 219 | target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS); | ||
| 220 | |||
| 221 | ASSERT(target_thread->IsWaitingForAddressArbiter()); | ||
| 222 | target_thread->Wakeup(); | ||
| 223 | |||
| 224 | it = thread_tree.erase(it); | ||
| 225 | target_thread->ClearAddressArbiter(); | ||
| 226 | ++num_waiters; | ||
| 227 | } | ||
| 228 | } | ||
| 229 | return RESULT_SUCCESS; | ||
| 230 | } | ||
| 231 | |||
| 232 | ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) { | ||
| 233 | // Prepare to wait. | ||
| 234 | Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); | ||
| 235 | Handle timer = InvalidHandle; | ||
| 236 | |||
| 237 | { | ||
| 238 | KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout); | ||
| 239 | |||
| 240 | // Check that the thread isn't terminating. | ||
| 241 | if (cur_thread->IsTerminationRequested()) { | ||
| 242 | slp.CancelSleep(); | ||
| 243 | return Svc::ResultTerminationRequested; | ||
| 244 | } | ||
| 245 | |||
| 246 | // Set the synced object. | ||
| 247 | cur_thread->SetSyncedObject(nullptr, Svc::ResultTimedOut); | ||
| 248 | |||
| 249 | // Read the value from userspace. | ||
| 250 | s32 user_value{}; | ||
| 251 | bool succeeded{}; | ||
| 252 | if (decrement) { | ||
| 253 | succeeded = DecrementIfLessThan(system, std::addressof(user_value), addr, value); | ||
| 254 | } else { | ||
| 255 | succeeded = ReadFromUser(system, std::addressof(user_value), addr); | ||
| 256 | } | ||
| 257 | |||
| 258 | if (!succeeded) { | ||
| 259 | slp.CancelSleep(); | ||
| 260 | return Svc::ResultInvalidCurrentMemory; | ||
| 261 | } | ||
| 262 | |||
| 263 | // Check that the value is less than the specified one. | ||
| 264 | if (user_value >= value) { | ||
| 265 | slp.CancelSleep(); | ||
| 266 | return Svc::ResultInvalidState; | ||
| 267 | } | ||
| 268 | |||
| 269 | // Check that the timeout is non-zero. | ||
| 270 | if (timeout == 0) { | ||
| 271 | slp.CancelSleep(); | ||
| 272 | return Svc::ResultTimedOut; | ||
| 273 | } | ||
| 274 | |||
| 275 | // Set the arbiter. | ||
| 276 | cur_thread->SetAddressArbiter(std::addressof(thread_tree), addr); | ||
| 277 | thread_tree.insert(*cur_thread); | ||
| 278 | cur_thread->SetState(ThreadState::Waiting); | ||
| 279 | cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration); | ||
| 280 | } | ||
| 281 | |||
| 282 | // Cancel the timer wait. | ||
| 283 | if (timer != InvalidHandle) { | ||
| 284 | auto& time_manager = kernel.TimeManager(); | ||
| 285 | time_manager.UnscheduleTimeEvent(timer); | ||
| 286 | } | ||
| 287 | |||
| 288 | // Remove from the address arbiter. | ||
| 289 | { | ||
| 290 | KScopedSchedulerLock sl(kernel); | ||
| 291 | |||
| 292 | if (cur_thread->IsWaitingForAddressArbiter()) { | ||
| 293 | thread_tree.erase(thread_tree.iterator_to(*cur_thread)); | ||
| 294 | cur_thread->ClearAddressArbiter(); | ||
| 295 | } | ||
| 296 | } | ||
| 297 | |||
| 298 | // Get the result. | ||
| 299 | KSynchronizationObject* dummy{}; | ||
| 300 | return cur_thread->GetWaitResult(std::addressof(dummy)); | ||
| 301 | } | ||
| 302 | |||
| 303 | ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) { | ||
| 304 | // Prepare to wait. | ||
| 305 | Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); | ||
| 306 | Handle timer = InvalidHandle; | ||
| 307 | |||
| 308 | { | ||
| 309 | KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout); | ||
| 310 | |||
| 311 | // Check that the thread isn't terminating. | ||
| 312 | if (cur_thread->IsTerminationRequested()) { | ||
| 313 | slp.CancelSleep(); | ||
| 314 | return Svc::ResultTerminationRequested; | ||
| 315 | } | ||
| 316 | |||
| 317 | // Set the synced object. | ||
| 318 | cur_thread->SetSyncedObject(nullptr, Svc::ResultTimedOut); | ||
| 319 | |||
| 320 | // Read the value from userspace. | ||
| 321 | s32 user_value{}; | ||
| 322 | if (!ReadFromUser(system, std::addressof(user_value), addr)) { | ||
| 323 | slp.CancelSleep(); | ||
| 324 | return Svc::ResultInvalidCurrentMemory; | ||
| 325 | } | ||
| 326 | |||
| 327 | // Check that the value is equal. | ||
| 328 | if (value != user_value) { | ||
| 329 | slp.CancelSleep(); | ||
| 330 | return Svc::ResultInvalidState; | ||
| 331 | } | ||
| 332 | |||
| 333 | // Check that the timeout is non-zero. | ||
| 334 | if (timeout == 0) { | ||
| 335 | slp.CancelSleep(); | ||
| 336 | return Svc::ResultTimedOut; | ||
| 337 | } | ||
| 338 | |||
| 339 | // Set the arbiter. | ||
| 340 | cur_thread->SetAddressArbiter(std::addressof(thread_tree), addr); | ||
| 341 | thread_tree.insert(*cur_thread); | ||
| 342 | cur_thread->SetState(ThreadState::Waiting); | ||
| 343 | cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration); | ||
| 344 | } | ||
| 345 | |||
| 346 | // Cancel the timer wait. | ||
| 347 | if (timer != InvalidHandle) { | ||
| 348 | auto& time_manager = kernel.TimeManager(); | ||
| 349 | time_manager.UnscheduleTimeEvent(timer); | ||
| 350 | } | ||
| 351 | |||
| 352 | // Remove from the address arbiter. | ||
| 353 | { | ||
| 354 | KScopedSchedulerLock sl(kernel); | ||
| 355 | |||
| 356 | if (cur_thread->IsWaitingForAddressArbiter()) { | ||
| 357 | thread_tree.erase(thread_tree.iterator_to(*cur_thread)); | ||
| 358 | cur_thread->ClearAddressArbiter(); | ||
| 359 | } | ||
| 360 | } | ||
| 361 | |||
| 362 | // Get the result. | ||
| 363 | KSynchronizationObject* dummy{}; | ||
| 364 | return cur_thread->GetWaitResult(std::addressof(dummy)); | ||
| 365 | } | ||
| 366 | |||
| 367 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_address_arbiter.h b/src/core/hle/kernel/k_address_arbiter.h new file mode 100644 index 000000000..8d379b524 --- /dev/null +++ b/src/core/hle/kernel/k_address_arbiter.h | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/assert.h" | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "core/hle/kernel/k_condition_variable.h" | ||
| 10 | #include "core/hle/kernel/svc_types.h" | ||
| 11 | |||
| 12 | union ResultCode; | ||
| 13 | |||
| 14 | namespace Core { | ||
| 15 | class System; | ||
| 16 | } | ||
| 17 | |||
| 18 | namespace Kernel { | ||
| 19 | |||
| 20 | class KernelCore; | ||
| 21 | |||
| 22 | class KAddressArbiter { | ||
| 23 | public: | ||
| 24 | using ThreadTree = KConditionVariable::ThreadTree; | ||
| 25 | |||
| 26 | explicit KAddressArbiter(Core::System& system_); | ||
| 27 | ~KAddressArbiter(); | ||
| 28 | |||
| 29 | [[nodiscard]] ResultCode SignalToAddress(VAddr addr, Svc::SignalType type, s32 value, | ||
| 30 | s32 count) { | ||
| 31 | switch (type) { | ||
| 32 | case Svc::SignalType::Signal: | ||
| 33 | return Signal(addr, count); | ||
| 34 | case Svc::SignalType::SignalAndIncrementIfEqual: | ||
| 35 | return SignalAndIncrementIfEqual(addr, value, count); | ||
| 36 | case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual: | ||
| 37 | return SignalAndModifyByWaitingCountIfEqual(addr, value, count); | ||
| 38 | } | ||
| 39 | UNREACHABLE(); | ||
| 40 | return RESULT_UNKNOWN; | ||
| 41 | } | ||
| 42 | |||
| 43 | [[nodiscard]] ResultCode WaitForAddress(VAddr addr, Svc::ArbitrationType type, s32 value, | ||
| 44 | s64 timeout) { | ||
| 45 | switch (type) { | ||
| 46 | case Svc::ArbitrationType::WaitIfLessThan: | ||
| 47 | return WaitIfLessThan(addr, value, false, timeout); | ||
| 48 | case Svc::ArbitrationType::DecrementAndWaitIfLessThan: | ||
| 49 | return WaitIfLessThan(addr, value, true, timeout); | ||
| 50 | case Svc::ArbitrationType::WaitIfEqual: | ||
| 51 | return WaitIfEqual(addr, value, timeout); | ||
| 52 | } | ||
| 53 | UNREACHABLE(); | ||
| 54 | return RESULT_UNKNOWN; | ||
| 55 | } | ||
| 56 | |||
| 57 | private: | ||
| 58 | [[nodiscard]] ResultCode Signal(VAddr addr, s32 count); | ||
| 59 | [[nodiscard]] ResultCode SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count); | ||
| 60 | [[nodiscard]] ResultCode SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count); | ||
| 61 | [[nodiscard]] ResultCode WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout); | ||
| 62 | [[nodiscard]] ResultCode WaitIfEqual(VAddr addr, s32 value, s64 timeout); | ||
| 63 | |||
| 64 | ThreadTree thread_tree; | ||
| 65 | |||
| 66 | Core::System& system; | ||
| 67 | KernelCore& kernel; | ||
| 68 | }; | ||
| 69 | |||
| 70 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp new file mode 100644 index 000000000..49a068310 --- /dev/null +++ b/src/core/hle/kernel/k_condition_variable.cpp | |||
| @@ -0,0 +1,349 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <vector> | ||
| 6 | |||
| 7 | #include "core/arm/exclusive_monitor.h" | ||
| 8 | #include "core/core.h" | ||
| 9 | #include "core/hle/kernel/k_condition_variable.h" | ||
| 10 | #include "core/hle/kernel/k_scheduler.h" | ||
| 11 | #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" | ||
| 12 | #include "core/hle/kernel/k_synchronization_object.h" | ||
| 13 | #include "core/hle/kernel/kernel.h" | ||
| 14 | #include "core/hle/kernel/process.h" | ||
| 15 | #include "core/hle/kernel/svc_common.h" | ||
| 16 | #include "core/hle/kernel/svc_results.h" | ||
| 17 | #include "core/hle/kernel/thread.h" | ||
| 18 | #include "core/memory.h" | ||
| 19 | |||
| 20 | namespace Kernel { | ||
| 21 | |||
| 22 | namespace { | ||
| 23 | |||
| 24 | bool ReadFromUser(Core::System& system, u32* out, VAddr address) { | ||
| 25 | *out = system.Memory().Read32(address); | ||
| 26 | return true; | ||
| 27 | } | ||
| 28 | |||
| 29 | bool WriteToUser(Core::System& system, VAddr address, const u32* p) { | ||
| 30 | system.Memory().Write32(address, *p); | ||
| 31 | return true; | ||
| 32 | } | ||
| 33 | |||
| 34 | bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero, | ||
| 35 | u32 new_orr_mask) { | ||
| 36 | auto& monitor = system.Monitor(); | ||
| 37 | const auto current_core = system.CurrentCoreIndex(); | ||
| 38 | |||
| 39 | // Load the value from the address. | ||
| 40 | const auto expected = monitor.ExclusiveRead32(current_core, address); | ||
| 41 | |||
| 42 | // Orr in the new mask. | ||
| 43 | u32 value = expected | new_orr_mask; | ||
| 44 | |||
| 45 | // If the value is zero, use the if_zero value, otherwise use the newly orr'd value. | ||
| 46 | if (!expected) { | ||
| 47 | value = if_zero; | ||
| 48 | } | ||
| 49 | |||
| 50 | // Try to store. | ||
| 51 | if (!monitor.ExclusiveWrite32(current_core, address, value)) { | ||
| 52 | // If we failed to store, try again. | ||
| 53 | return UpdateLockAtomic(system, out, address, if_zero, new_orr_mask); | ||
| 54 | } | ||
| 55 | |||
| 56 | // We're done. | ||
| 57 | *out = expected; | ||
| 58 | return true; | ||
| 59 | } | ||
| 60 | |||
| 61 | } // namespace | ||
| 62 | |||
| 63 | KConditionVariable::KConditionVariable(Core::System& system_) | ||
| 64 | : system{system_}, kernel{system.Kernel()} {} | ||
| 65 | |||
| 66 | KConditionVariable::~KConditionVariable() = default; | ||
| 67 | |||
| 68 | ResultCode KConditionVariable::SignalToAddress(VAddr addr) { | ||
| 69 | Thread* owner_thread = kernel.CurrentScheduler()->GetCurrentThread(); | ||
| 70 | |||
| 71 | // Signal the address. | ||
| 72 | { | ||
| 73 | KScopedSchedulerLock sl(kernel); | ||
| 74 | |||
| 75 | // Remove waiter thread. | ||
| 76 | s32 num_waiters{}; | ||
| 77 | Thread* next_owner_thread = | ||
| 78 | owner_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr); | ||
| 79 | |||
| 80 | // Determine the next tag. | ||
| 81 | u32 next_value{}; | ||
| 82 | if (next_owner_thread) { | ||
| 83 | next_value = next_owner_thread->GetAddressKeyValue(); | ||
| 84 | if (num_waiters > 1) { | ||
| 85 | next_value |= Svc::HandleWaitMask; | ||
| 86 | } | ||
| 87 | |||
| 88 | next_owner_thread->SetSyncedObject(nullptr, RESULT_SUCCESS); | ||
| 89 | next_owner_thread->Wakeup(); | ||
| 90 | } | ||
| 91 | |||
| 92 | // Write the value to userspace. | ||
| 93 | if (!WriteToUser(system, addr, std::addressof(next_value))) { | ||
| 94 | if (next_owner_thread) { | ||
| 95 | next_owner_thread->SetSyncedObject(nullptr, Svc::ResultInvalidCurrentMemory); | ||
| 96 | } | ||
| 97 | |||
| 98 | return Svc::ResultInvalidCurrentMemory; | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | return RESULT_SUCCESS; | ||
| 103 | } | ||
| 104 | |||
| 105 | ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) { | ||
| 106 | Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); | ||
| 107 | |||
| 108 | // Wait for the address. | ||
| 109 | { | ||
| 110 | std::shared_ptr<Thread> owner_thread; | ||
| 111 | ASSERT(!owner_thread); | ||
| 112 | { | ||
| 113 | KScopedSchedulerLock sl(kernel); | ||
| 114 | cur_thread->SetSyncedObject(nullptr, RESULT_SUCCESS); | ||
| 115 | |||
| 116 | // Check if the thread should terminate. | ||
| 117 | R_UNLESS(!cur_thread->IsTerminationRequested(), Svc::ResultTerminationRequested); | ||
| 118 | |||
| 119 | { | ||
| 120 | // Read the tag from userspace. | ||
| 121 | u32 test_tag{}; | ||
| 122 | R_UNLESS(ReadFromUser(system, std::addressof(test_tag), addr), | ||
| 123 | Svc::ResultInvalidCurrentMemory); | ||
| 124 | |||
| 125 | // If the tag isn't the handle (with wait mask), we're done. | ||
| 126 | R_UNLESS(test_tag == (handle | Svc::HandleWaitMask), RESULT_SUCCESS); | ||
| 127 | |||
| 128 | // Get the lock owner thread. | ||
| 129 | owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<Thread>(handle); | ||
| 130 | R_UNLESS(owner_thread, Svc::ResultInvalidHandle); | ||
| 131 | |||
| 132 | // Update the lock. | ||
| 133 | cur_thread->SetAddressKey(addr, value); | ||
| 134 | owner_thread->AddWaiter(cur_thread); | ||
| 135 | cur_thread->SetState(ThreadState::Waiting); | ||
| 136 | cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar); | ||
| 137 | cur_thread->SetMutexWaitAddressForDebugging(addr); | ||
| 138 | } | ||
| 139 | } | ||
| 140 | ASSERT(owner_thread); | ||
| 141 | } | ||
| 142 | |||
| 143 | // Remove the thread as a waiter from the lock owner. | ||
| 144 | { | ||
| 145 | KScopedSchedulerLock sl(kernel); | ||
| 146 | Thread* owner_thread = cur_thread->GetLockOwner(); | ||
| 147 | if (owner_thread != nullptr) { | ||
| 148 | owner_thread->RemoveWaiter(cur_thread); | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | // Get the wait result. | ||
| 153 | KSynchronizationObject* dummy{}; | ||
| 154 | return cur_thread->GetWaitResult(std::addressof(dummy)); | ||
| 155 | } | ||
| 156 | |||
| 157 | Thread* KConditionVariable::SignalImpl(Thread* thread) { | ||
| 158 | // Check pre-conditions. | ||
| 159 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); | ||
| 160 | |||
| 161 | // Update the tag. | ||
| 162 | VAddr address = thread->GetAddressKey(); | ||
| 163 | u32 own_tag = thread->GetAddressKeyValue(); | ||
| 164 | |||
| 165 | u32 prev_tag{}; | ||
| 166 | bool can_access{}; | ||
| 167 | { | ||
| 168 | // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. | ||
| 169 | // TODO(bunnei): We should call CanAccessAtomic(..) here. | ||
| 170 | can_access = true; | ||
| 171 | if (can_access) { | ||
| 172 | UpdateLockAtomic(system, std::addressof(prev_tag), address, own_tag, | ||
| 173 | Svc::HandleWaitMask); | ||
| 174 | } | ||
| 175 | } | ||
| 176 | |||
| 177 | Thread* thread_to_close = nullptr; | ||
| 178 | if (can_access) { | ||
| 179 | if (prev_tag == InvalidHandle) { | ||
| 180 | // If nobody held the lock previously, we're all good. | ||
| 181 | thread->SetSyncedObject(nullptr, RESULT_SUCCESS); | ||
| 182 | thread->Wakeup(); | ||
| 183 | } else { | ||
| 184 | // Get the previous owner. | ||
| 185 | auto owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<Thread>( | ||
| 186 | prev_tag & ~Svc::HandleWaitMask); | ||
| 187 | |||
| 188 | if (owner_thread) { | ||
| 189 | // Add the thread as a waiter on the owner. | ||
| 190 | owner_thread->AddWaiter(thread); | ||
| 191 | thread_to_close = owner_thread.get(); | ||
| 192 | } else { | ||
| 193 | // The lock was tagged with a thread that doesn't exist. | ||
| 194 | thread->SetSyncedObject(nullptr, Svc::ResultInvalidState); | ||
| 195 | thread->Wakeup(); | ||
| 196 | } | ||
| 197 | } | ||
| 198 | } else { | ||
| 199 | // If the address wasn't accessible, note so. | ||
| 200 | thread->SetSyncedObject(nullptr, Svc::ResultInvalidCurrentMemory); | ||
| 201 | thread->Wakeup(); | ||
| 202 | } | ||
| 203 | |||
| 204 | return thread_to_close; | ||
| 205 | } | ||
| 206 | |||
| 207 | void KConditionVariable::Signal(u64 cv_key, s32 count) { | ||
| 208 | // Prepare for signaling. | ||
| 209 | constexpr int MaxThreads = 16; | ||
| 210 | |||
| 211 | // TODO(bunnei): This should just be Thread once we implement KAutoObject instead of using | ||
| 212 | // std::shared_ptr. | ||
| 213 | std::vector<std::shared_ptr<Thread>> thread_list; | ||
| 214 | std::array<Thread*, MaxThreads> thread_array; | ||
| 215 | s32 num_to_close{}; | ||
| 216 | |||
| 217 | // Perform signaling. | ||
| 218 | s32 num_waiters{}; | ||
| 219 | { | ||
| 220 | KScopedSchedulerLock sl(kernel); | ||
| 221 | |||
| 222 | auto it = thread_tree.nfind_light({cv_key, -1}); | ||
| 223 | while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && | ||
| 224 | (it->GetConditionVariableKey() == cv_key)) { | ||
| 225 | Thread* target_thread = std::addressof(*it); | ||
| 226 | |||
| 227 | if (Thread* thread = SignalImpl(target_thread); thread != nullptr) { | ||
| 228 | if (num_to_close < MaxThreads) { | ||
| 229 | thread_array[num_to_close++] = thread; | ||
| 230 | } else { | ||
| 231 | thread_list.push_back(SharedFrom(thread)); | ||
| 232 | } | ||
| 233 | } | ||
| 234 | |||
| 235 | it = thread_tree.erase(it); | ||
| 236 | target_thread->ClearConditionVariable(); | ||
| 237 | ++num_waiters; | ||
| 238 | } | ||
| 239 | |||
| 240 | // If we have no waiters, clear the has waiter flag. | ||
| 241 | if (it == thread_tree.end() || it->GetConditionVariableKey() != cv_key) { | ||
| 242 | const u32 has_waiter_flag{}; | ||
| 243 | WriteToUser(system, cv_key, std::addressof(has_waiter_flag)); | ||
| 244 | } | ||
| 245 | } | ||
| 246 | |||
| 247 | // Close threads in the array. | ||
| 248 | for (auto i = 0; i < num_to_close; ++i) { | ||
| 249 | thread_array[i]->Close(); | ||
| 250 | } | ||
| 251 | |||
| 252 | // Close threads in the list. | ||
| 253 | for (auto it = thread_list.begin(); it != thread_list.end(); it = thread_list.erase(it)) { | ||
| 254 | (*it)->Close(); | ||
| 255 | } | ||
| 256 | } | ||
| 257 | |||
| 258 | ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) { | ||
| 259 | // Prepare to wait. | ||
| 260 | Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); | ||
| 261 | Handle timer = InvalidHandle; | ||
| 262 | |||
| 263 | { | ||
| 264 | KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout); | ||
| 265 | |||
| 266 | // Set the synced object. | ||
| 267 | cur_thread->SetSyncedObject(nullptr, Svc::ResultTimedOut); | ||
| 268 | |||
| 269 | // Check that the thread isn't terminating. | ||
| 270 | if (cur_thread->IsTerminationRequested()) { | ||
| 271 | slp.CancelSleep(); | ||
| 272 | return Svc::ResultTerminationRequested; | ||
| 273 | } | ||
| 274 | |||
| 275 | // Update the value and process for the next owner. | ||
| 276 | { | ||
| 277 | // Remove waiter thread. | ||
| 278 | s32 num_waiters{}; | ||
| 279 | Thread* next_owner_thread = | ||
| 280 | cur_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr); | ||
| 281 | |||
| 282 | // Update for the next owner thread. | ||
| 283 | u32 next_value{}; | ||
| 284 | if (next_owner_thread != nullptr) { | ||
| 285 | // Get the next tag value. | ||
| 286 | next_value = next_owner_thread->GetAddressKeyValue(); | ||
| 287 | if (num_waiters > 1) { | ||
| 288 | next_value |= Svc::HandleWaitMask; | ||
| 289 | } | ||
| 290 | |||
| 291 | // Wake up the next owner. | ||
| 292 | next_owner_thread->SetSyncedObject(nullptr, RESULT_SUCCESS); | ||
| 293 | next_owner_thread->Wakeup(); | ||
| 294 | } | ||
| 295 | |||
| 296 | // Write to the cv key. | ||
| 297 | { | ||
| 298 | const u32 has_waiter_flag = 1; | ||
| 299 | WriteToUser(system, key, std::addressof(has_waiter_flag)); | ||
| 300 | // TODO(bunnei): We should call DataMemoryBarrier(..) here. | ||
| 301 | } | ||
| 302 | |||
| 303 | // Write the value to userspace. | ||
| 304 | if (!WriteToUser(system, addr, std::addressof(next_value))) { | ||
| 305 | slp.CancelSleep(); | ||
| 306 | return Svc::ResultInvalidCurrentMemory; | ||
| 307 | } | ||
| 308 | } | ||
| 309 | |||
| 310 | // Update condition variable tracking. | ||
| 311 | { | ||
| 312 | cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value); | ||
| 313 | thread_tree.insert(*cur_thread); | ||
| 314 | } | ||
| 315 | |||
| 316 | // If the timeout is non-zero, set the thread as waiting. | ||
| 317 | if (timeout != 0) { | ||
| 318 | cur_thread->SetState(ThreadState::Waiting); | ||
| 319 | cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar); | ||
| 320 | cur_thread->SetMutexWaitAddressForDebugging(addr); | ||
| 321 | } | ||
| 322 | } | ||
| 323 | |||
| 324 | // Cancel the timer wait. | ||
| 325 | if (timer != InvalidHandle) { | ||
| 326 | auto& time_manager = kernel.TimeManager(); | ||
| 327 | time_manager.UnscheduleTimeEvent(timer); | ||
| 328 | } | ||
| 329 | |||
| 330 | // Remove from the condition variable. | ||
| 331 | { | ||
| 332 | KScopedSchedulerLock sl(kernel); | ||
| 333 | |||
| 334 | if (Thread* owner = cur_thread->GetLockOwner(); owner != nullptr) { | ||
| 335 | owner->RemoveWaiter(cur_thread); | ||
| 336 | } | ||
| 337 | |||
| 338 | if (cur_thread->IsWaitingForConditionVariable()) { | ||
| 339 | thread_tree.erase(thread_tree.iterator_to(*cur_thread)); | ||
| 340 | cur_thread->ClearConditionVariable(); | ||
| 341 | } | ||
| 342 | } | ||
| 343 | |||
| 344 | // Get the result. | ||
| 345 | KSynchronizationObject* dummy{}; | ||
| 346 | return cur_thread->GetWaitResult(std::addressof(dummy)); | ||
| 347 | } | ||
| 348 | |||
| 349 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_condition_variable.h b/src/core/hle/kernel/k_condition_variable.h new file mode 100644 index 000000000..98ed5b323 --- /dev/null +++ b/src/core/hle/kernel/k_condition_variable.h | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/assert.h" | ||
| 8 | #include "common/common_types.h" | ||
| 9 | |||
| 10 | #include "core/hle/kernel/k_scheduler.h" | ||
| 11 | #include "core/hle/kernel/kernel.h" | ||
| 12 | #include "core/hle/kernel/thread.h" | ||
| 13 | #include "core/hle/result.h" | ||
| 14 | |||
| 15 | namespace Core { | ||
| 16 | class System; | ||
| 17 | } | ||
| 18 | |||
| 19 | namespace Kernel { | ||
| 20 | |||
| 21 | class KConditionVariable { | ||
| 22 | public: | ||
| 23 | using ThreadTree = typename Thread::ConditionVariableThreadTreeType; | ||
| 24 | |||
| 25 | explicit KConditionVariable(Core::System& system_); | ||
| 26 | ~KConditionVariable(); | ||
| 27 | |||
| 28 | // Arbitration | ||
| 29 | [[nodiscard]] ResultCode SignalToAddress(VAddr addr); | ||
| 30 | [[nodiscard]] ResultCode WaitForAddress(Handle handle, VAddr addr, u32 value); | ||
| 31 | |||
| 32 | // Condition variable | ||
| 33 | void Signal(u64 cv_key, s32 count); | ||
| 34 | [[nodiscard]] ResultCode Wait(VAddr addr, u64 key, u32 value, s64 timeout); | ||
| 35 | |||
| 36 | private: | ||
| 37 | [[nodiscard]] Thread* SignalImpl(Thread* thread); | ||
| 38 | |||
| 39 | ThreadTree thread_tree; | ||
| 40 | |||
| 41 | Core::System& system; | ||
| 42 | KernelCore& kernel; | ||
| 43 | }; | ||
| 44 | |||
| 45 | inline void BeforeUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree, | ||
| 46 | Thread* thread) { | ||
| 47 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); | ||
| 48 | |||
| 49 | tree->erase(tree->iterator_to(*thread)); | ||
| 50 | } | ||
| 51 | |||
| 52 | inline void AfterUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree, | ||
| 53 | Thread* thread) { | ||
| 54 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); | ||
| 55 | |||
| 56 | tree->insert(*thread); | ||
| 57 | } | ||
| 58 | |||
| 59 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_priority_queue.h b/src/core/hle/kernel/k_priority_queue.h index 99fb8fe93..0dc929040 100644 --- a/src/core/hle/kernel/k_priority_queue.h +++ b/src/core/hle/kernel/k_priority_queue.h | |||
| @@ -8,11 +8,11 @@ | |||
| 8 | #pragma once | 8 | #pragma once |
| 9 | 9 | ||
| 10 | #include <array> | 10 | #include <array> |
| 11 | #include <bit> | ||
| 11 | #include <concepts> | 12 | #include <concepts> |
| 12 | 13 | ||
| 13 | #include "common/assert.h" | 14 | #include "common/assert.h" |
| 14 | #include "common/bit_set.h" | 15 | #include "common/bit_set.h" |
| 15 | #include "common/bit_util.h" | ||
| 16 | #include "common/common_types.h" | 16 | #include "common/common_types.h" |
| 17 | #include "common/concepts.h" | 17 | #include "common/concepts.h" |
| 18 | 18 | ||
| @@ -268,7 +268,7 @@ private: | |||
| 268 | } | 268 | } |
| 269 | 269 | ||
| 270 | constexpr s32 GetNextCore(u64& affinity) { | 270 | constexpr s32 GetNextCore(u64& affinity) { |
| 271 | const s32 core = Common::CountTrailingZeroes64(affinity); | 271 | const s32 core = std::countr_zero(affinity); |
| 272 | ClearAffinityBit(affinity, core); | 272 | ClearAffinityBit(affinity, core); |
| 273 | return core; | 273 | return core; |
| 274 | } | 274 | } |
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index c5fd82a6b..12b5619fb 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp | |||
| @@ -5,6 +5,8 @@ | |||
| 5 | // This file references various implementation details from Atmosphere, an open-source firmware for | 5 | // This file references various implementation details from Atmosphere, an open-source firmware for |
| 6 | // the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. | 6 | // the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. |
| 7 | 7 | ||
| 8 | #include <bit> | ||
| 9 | |||
| 8 | #include "common/assert.h" | 10 | #include "common/assert.h" |
| 9 | #include "common/bit_util.h" | 11 | #include "common/bit_util.h" |
| 10 | #include "common/fiber.h" | 12 | #include "common/fiber.h" |
| @@ -31,12 +33,12 @@ static void IncrementScheduledCount(Kernel::Thread* thread) { | |||
| 31 | 33 | ||
| 32 | void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule, | 34 | void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule, |
| 33 | Core::EmuThreadHandle global_thread) { | 35 | Core::EmuThreadHandle global_thread) { |
| 34 | u32 current_core = global_thread.host_handle; | 36 | const u32 current_core = global_thread.host_handle; |
| 35 | bool must_context_switch = global_thread.guest_handle != InvalidHandle && | 37 | bool must_context_switch = global_thread.guest_handle != InvalidHandle && |
| 36 | (current_core < Core::Hardware::NUM_CPU_CORES); | 38 | (current_core < Core::Hardware::NUM_CPU_CORES); |
| 37 | 39 | ||
| 38 | while (cores_pending_reschedule != 0) { | 40 | while (cores_pending_reschedule != 0) { |
| 39 | u32 core = Common::CountTrailingZeroes64(cores_pending_reschedule); | 41 | const auto core = static_cast<u32>(std::countr_zero(cores_pending_reschedule)); |
| 40 | ASSERT(core < Core::Hardware::NUM_CPU_CORES); | 42 | ASSERT(core < Core::Hardware::NUM_CPU_CORES); |
| 41 | if (!must_context_switch || core != current_core) { | 43 | if (!must_context_switch || core != current_core) { |
| 42 | auto& phys_core = kernel.PhysicalCore(core); | 44 | auto& phys_core = kernel.PhysicalCore(core); |
| @@ -109,7 +111,7 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { | |||
| 109 | 111 | ||
| 110 | // Idle cores are bad. We're going to try to migrate threads to each idle core in turn. | 112 | // Idle cores are bad. We're going to try to migrate threads to each idle core in turn. |
| 111 | while (idle_cores != 0) { | 113 | while (idle_cores != 0) { |
| 112 | u32 core_id = Common::CountTrailingZeroes64(idle_cores); | 114 | const auto core_id = static_cast<u32>(std::countr_zero(idle_cores)); |
| 113 | if (Thread* suggested = priority_queue.GetSuggestedFront(core_id); suggested != nullptr) { | 115 | if (Thread* suggested = priority_queue.GetSuggestedFront(core_id); suggested != nullptr) { |
| 114 | s32 migration_candidates[Core::Hardware::NUM_CPU_CORES]; | 116 | s32 migration_candidates[Core::Hardware::NUM_CPU_CORES]; |
| 115 | size_t num_candidates = 0; | 117 | size_t num_candidates = 0; |
| @@ -180,22 +182,22 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { | |||
| 180 | return cores_needing_scheduling; | 182 | return cores_needing_scheduling; |
| 181 | } | 183 | } |
| 182 | 184 | ||
| 183 | void KScheduler::OnThreadStateChanged(KernelCore& kernel, Thread* thread, u32 old_state) { | 185 | void KScheduler::OnThreadStateChanged(KernelCore& kernel, Thread* thread, ThreadState old_state) { |
| 184 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); | 186 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); |
| 185 | 187 | ||
| 186 | // Check if the state has changed, because if it hasn't there's nothing to do. | 188 | // Check if the state has changed, because if it hasn't there's nothing to do. |
| 187 | const auto cur_state = thread->scheduling_state; | 189 | const auto cur_state = thread->GetRawState(); |
| 188 | if (cur_state == old_state) { | 190 | if (cur_state == old_state) { |
| 189 | return; | 191 | return; |
| 190 | } | 192 | } |
| 191 | 193 | ||
| 192 | // Update the priority queues. | 194 | // Update the priority queues. |
| 193 | if (old_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { | 195 | if (old_state == ThreadState::Runnable) { |
| 194 | // If we were previously runnable, then we're not runnable now, and we should remove. | 196 | // If we were previously runnable, then we're not runnable now, and we should remove. |
| 195 | GetPriorityQueue(kernel).Remove(thread); | 197 | GetPriorityQueue(kernel).Remove(thread); |
| 196 | IncrementScheduledCount(thread); | 198 | IncrementScheduledCount(thread); |
| 197 | SetSchedulerUpdateNeeded(kernel); | 199 | SetSchedulerUpdateNeeded(kernel); |
| 198 | } else if (cur_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { | 200 | } else if (cur_state == ThreadState::Runnable) { |
| 199 | // If we're now runnable, then we weren't previously, and we should add. | 201 | // If we're now runnable, then we weren't previously, and we should add. |
| 200 | GetPriorityQueue(kernel).PushBack(thread); | 202 | GetPriorityQueue(kernel).PushBack(thread); |
| 201 | IncrementScheduledCount(thread); | 203 | IncrementScheduledCount(thread); |
| @@ -203,13 +205,11 @@ void KScheduler::OnThreadStateChanged(KernelCore& kernel, Thread* thread, u32 ol | |||
| 203 | } | 205 | } |
| 204 | } | 206 | } |
| 205 | 207 | ||
| 206 | void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, Thread* current_thread, | 208 | void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, s32 old_priority) { |
| 207 | u32 old_priority) { | ||
| 208 | |||
| 209 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); | 209 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); |
| 210 | 210 | ||
| 211 | // If the thread is runnable, we want to change its priority in the queue. | 211 | // If the thread is runnable, we want to change its priority in the queue. |
| 212 | if (thread->scheduling_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { | 212 | if (thread->GetRawState() == ThreadState::Runnable) { |
| 213 | GetPriorityQueue(kernel).ChangePriority( | 213 | GetPriorityQueue(kernel).ChangePriority( |
| 214 | old_priority, thread == kernel.CurrentScheduler()->GetCurrentThread(), thread); | 214 | old_priority, thread == kernel.CurrentScheduler()->GetCurrentThread(), thread); |
| 215 | IncrementScheduledCount(thread); | 215 | IncrementScheduledCount(thread); |
| @@ -222,7 +222,7 @@ void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, Thread* thread, | |||
| 222 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); | 222 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); |
| 223 | 223 | ||
| 224 | // If the thread is runnable, we want to change its affinity in the queue. | 224 | // If the thread is runnable, we want to change its affinity in the queue. |
| 225 | if (thread->scheduling_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { | 225 | if (thread->GetRawState() == ThreadState::Runnable) { |
| 226 | GetPriorityQueue(kernel).ChangeAffinityMask(old_core, old_affinity, thread); | 226 | GetPriorityQueue(kernel).ChangeAffinityMask(old_core, old_affinity, thread); |
| 227 | IncrementScheduledCount(thread); | 227 | IncrementScheduledCount(thread); |
| 228 | SetSchedulerUpdateNeeded(kernel); | 228 | SetSchedulerUpdateNeeded(kernel); |
| @@ -292,7 +292,7 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) { | |||
| 292 | 292 | ||
| 293 | // If the best thread we can choose has a priority the same or worse than ours, try to | 293 | // If the best thread we can choose has a priority the same or worse than ours, try to |
| 294 | // migrate a higher priority thread. | 294 | // migrate a higher priority thread. |
| 295 | if (best_thread != nullptr && best_thread->GetPriority() >= static_cast<u32>(priority)) { | 295 | if (best_thread != nullptr && best_thread->GetPriority() >= priority) { |
| 296 | Thread* suggested = priority_queue.GetSuggestedFront(core_id); | 296 | Thread* suggested = priority_queue.GetSuggestedFront(core_id); |
| 297 | while (suggested != nullptr) { | 297 | while (suggested != nullptr) { |
| 298 | // If the suggestion's priority is the same as ours, don't bother. | 298 | // If the suggestion's priority is the same as ours, don't bother. |
| @@ -395,8 +395,8 @@ void KScheduler::YieldWithoutCoreMigration() { | |||
| 395 | { | 395 | { |
| 396 | KScopedSchedulerLock lock(kernel); | 396 | KScopedSchedulerLock lock(kernel); |
| 397 | 397 | ||
| 398 | const auto cur_state = cur_thread.scheduling_state; | 398 | const auto cur_state = cur_thread.GetRawState(); |
| 399 | if (cur_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { | 399 | if (cur_state == ThreadState::Runnable) { |
| 400 | // Put the current thread at the back of the queue. | 400 | // Put the current thread at the back of the queue. |
| 401 | Thread* next_thread = priority_queue.MoveToScheduledBack(std::addressof(cur_thread)); | 401 | Thread* next_thread = priority_queue.MoveToScheduledBack(std::addressof(cur_thread)); |
| 402 | IncrementScheduledCount(std::addressof(cur_thread)); | 402 | IncrementScheduledCount(std::addressof(cur_thread)); |
| @@ -436,8 +436,8 @@ void KScheduler::YieldWithCoreMigration() { | |||
| 436 | { | 436 | { |
| 437 | KScopedSchedulerLock lock(kernel); | 437 | KScopedSchedulerLock lock(kernel); |
| 438 | 438 | ||
| 439 | const auto cur_state = cur_thread.scheduling_state; | 439 | const auto cur_state = cur_thread.GetRawState(); |
| 440 | if (cur_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { | 440 | if (cur_state == ThreadState::Runnable) { |
| 441 | // Get the current active core. | 441 | // Get the current active core. |
| 442 | const s32 core_id = cur_thread.GetActiveCore(); | 442 | const s32 core_id = cur_thread.GetActiveCore(); |
| 443 | 443 | ||
| @@ -526,8 +526,8 @@ void KScheduler::YieldToAnyThread() { | |||
| 526 | { | 526 | { |
| 527 | KScopedSchedulerLock lock(kernel); | 527 | KScopedSchedulerLock lock(kernel); |
| 528 | 528 | ||
| 529 | const auto cur_state = cur_thread.scheduling_state; | 529 | const auto cur_state = cur_thread.GetRawState(); |
| 530 | if (cur_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { | 530 | if (cur_state == ThreadState::Runnable) { |
| 531 | // Get the current active core. | 531 | // Get the current active core. |
| 532 | const s32 core_id = cur_thread.GetActiveCore(); | 532 | const s32 core_id = cur_thread.GetActiveCore(); |
| 533 | 533 | ||
| @@ -645,8 +645,7 @@ void KScheduler::Unload(Thread* thread) { | |||
| 645 | 645 | ||
| 646 | void KScheduler::Reload(Thread* thread) { | 646 | void KScheduler::Reload(Thread* thread) { |
| 647 | if (thread) { | 647 | if (thread) { |
| 648 | ASSERT_MSG(thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable, | 648 | ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable."); |
| 649 | "Thread must be runnable."); | ||
| 650 | 649 | ||
| 651 | // Cancel any outstanding wakeup events for this thread | 650 | // Cancel any outstanding wakeup events for this thread |
| 652 | thread->SetIsRunning(true); | 651 | thread->SetIsRunning(true); |
| @@ -725,7 +724,7 @@ void KScheduler::SwitchToCurrent() { | |||
| 725 | do { | 724 | do { |
| 726 | if (current_thread != nullptr && !current_thread->IsHLEThread()) { | 725 | if (current_thread != nullptr && !current_thread->IsHLEThread()) { |
| 727 | current_thread->context_guard.lock(); | 726 | current_thread->context_guard.lock(); |
| 728 | if (!current_thread->IsRunnable()) { | 727 | if (current_thread->GetRawState() != ThreadState::Runnable) { |
| 729 | current_thread->context_guard.unlock(); | 728 | current_thread->context_guard.unlock(); |
| 730 | break; | 729 | break; |
| 731 | } | 730 | } |
| @@ -772,7 +771,7 @@ void KScheduler::Initialize() { | |||
| 772 | 771 | ||
| 773 | { | 772 | { |
| 774 | KScopedSchedulerLock lock{system.Kernel()}; | 773 | KScopedSchedulerLock lock{system.Kernel()}; |
| 775 | idle_thread->SetStatus(ThreadStatus::Ready); | 774 | idle_thread->SetState(ThreadState::Runnable); |
| 776 | } | 775 | } |
| 777 | } | 776 | } |
| 778 | 777 | ||
diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h index e84abc84c..783665123 100644 --- a/src/core/hle/kernel/k_scheduler.h +++ b/src/core/hle/kernel/k_scheduler.h | |||
| @@ -100,11 +100,10 @@ public: | |||
| 100 | void YieldToAnyThread(); | 100 | void YieldToAnyThread(); |
| 101 | 101 | ||
| 102 | /// Notify the scheduler a thread's status has changed. | 102 | /// Notify the scheduler a thread's status has changed. |
| 103 | static void OnThreadStateChanged(KernelCore& kernel, Thread* thread, u32 old_state); | 103 | static void OnThreadStateChanged(KernelCore& kernel, Thread* thread, ThreadState old_state); |
| 104 | 104 | ||
| 105 | /// Notify the scheduler a thread's priority has changed. | 105 | /// Notify the scheduler a thread's priority has changed. |
| 106 | static void OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, Thread* current_thread, | 106 | static void OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, s32 old_priority); |
| 107 | u32 old_priority); | ||
| 108 | 107 | ||
| 109 | /// Notify the scheduler a thread's core and/or affinity mask has changed. | 108 | /// Notify the scheduler a thread's core and/or affinity mask has changed. |
| 110 | static void OnThreadAffinityMaskChanged(KernelCore& kernel, Thread* thread, | 109 | static void OnThreadAffinityMaskChanged(KernelCore& kernel, Thread* thread, |
diff --git a/src/core/hle/kernel/k_scheduler_lock.h b/src/core/hle/kernel/k_scheduler_lock.h index 2f1c1f691..9b40bd22c 100644 --- a/src/core/hle/kernel/k_scheduler_lock.h +++ b/src/core/hle/kernel/k_scheduler_lock.h | |||
| @@ -19,7 +19,7 @@ class KernelCore; | |||
| 19 | template <typename SchedulerType> | 19 | template <typename SchedulerType> |
| 20 | class KAbstractSchedulerLock { | 20 | class KAbstractSchedulerLock { |
| 21 | public: | 21 | public: |
| 22 | explicit KAbstractSchedulerLock(KernelCore& kernel) : kernel{kernel} {} | 22 | explicit KAbstractSchedulerLock(KernelCore& kernel_) : kernel{kernel_} {} |
| 23 | 23 | ||
| 24 | bool IsLockedByCurrentThread() const { | 24 | bool IsLockedByCurrentThread() const { |
| 25 | return this->owner_thread == kernel.GetCurrentEmuThreadID(); | 25 | return this->owner_thread == kernel.GetCurrentEmuThreadID(); |
diff --git a/src/core/hle/kernel/k_synchronization_object.cpp b/src/core/hle/kernel/k_synchronization_object.cpp new file mode 100644 index 000000000..1c508cb55 --- /dev/null +++ b/src/core/hle/kernel/k_synchronization_object.cpp | |||
| @@ -0,0 +1,172 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/assert.h" | ||
| 6 | #include "common/common_types.h" | ||
| 7 | #include "core/hle/kernel/k_scheduler.h" | ||
| 8 | #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" | ||
| 9 | #include "core/hle/kernel/k_synchronization_object.h" | ||
| 10 | #include "core/hle/kernel/kernel.h" | ||
| 11 | #include "core/hle/kernel/svc_results.h" | ||
| 12 | #include "core/hle/kernel/thread.h" | ||
| 13 | |||
| 14 | namespace Kernel { | ||
| 15 | |||
| 16 | ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index, | ||
| 17 | KSynchronizationObject** objects, const s32 num_objects, | ||
| 18 | s64 timeout) { | ||
| 19 | // Allocate space on stack for thread nodes. | ||
| 20 | std::vector<ThreadListNode> thread_nodes(num_objects); | ||
| 21 | |||
| 22 | // Prepare for wait. | ||
| 23 | Thread* thread = kernel.CurrentScheduler()->GetCurrentThread(); | ||
| 24 | Handle timer = InvalidHandle; | ||
| 25 | |||
| 26 | { | ||
| 27 | // Setup the scheduling lock and sleep. | ||
| 28 | KScopedSchedulerLockAndSleep slp(kernel, timer, thread, timeout); | ||
| 29 | |||
| 30 | // Check if any of the objects are already signaled. | ||
| 31 | for (auto i = 0; i < num_objects; ++i) { | ||
| 32 | ASSERT(objects[i] != nullptr); | ||
| 33 | |||
| 34 | if (objects[i]->IsSignaled()) { | ||
| 35 | *out_index = i; | ||
| 36 | slp.CancelSleep(); | ||
| 37 | return RESULT_SUCCESS; | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | // Check if the timeout is zero. | ||
| 42 | if (timeout == 0) { | ||
| 43 | slp.CancelSleep(); | ||
| 44 | return Svc::ResultTimedOut; | ||
| 45 | } | ||
| 46 | |||
| 47 | // Check if the thread should terminate. | ||
| 48 | if (thread->IsTerminationRequested()) { | ||
| 49 | slp.CancelSleep(); | ||
| 50 | return Svc::ResultTerminationRequested; | ||
| 51 | } | ||
| 52 | |||
| 53 | // Check if waiting was canceled. | ||
| 54 | if (thread->IsWaitCancelled()) { | ||
| 55 | slp.CancelSleep(); | ||
| 56 | thread->ClearWaitCancelled(); | ||
| 57 | return Svc::ResultCancelled; | ||
| 58 | } | ||
| 59 | |||
| 60 | // Add the waiters. | ||
| 61 | for (auto i = 0; i < num_objects; ++i) { | ||
| 62 | thread_nodes[i].thread = thread; | ||
| 63 | thread_nodes[i].next = nullptr; | ||
| 64 | |||
| 65 | if (objects[i]->thread_list_tail == nullptr) { | ||
| 66 | objects[i]->thread_list_head = std::addressof(thread_nodes[i]); | ||
| 67 | } else { | ||
| 68 | objects[i]->thread_list_tail->next = std::addressof(thread_nodes[i]); | ||
| 69 | } | ||
| 70 | |||
| 71 | objects[i]->thread_list_tail = std::addressof(thread_nodes[i]); | ||
| 72 | } | ||
| 73 | |||
| 74 | // For debugging only | ||
| 75 | thread->SetWaitObjectsForDebugging({objects, static_cast<std::size_t>(num_objects)}); | ||
| 76 | |||
| 77 | // Mark the thread as waiting. | ||
| 78 | thread->SetCancellable(); | ||
| 79 | thread->SetSyncedObject(nullptr, Svc::ResultTimedOut); | ||
| 80 | thread->SetState(ThreadState::Waiting); | ||
| 81 | thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Synchronization); | ||
| 82 | } | ||
| 83 | |||
| 84 | // The lock/sleep is done, so we should be able to get our result. | ||
| 85 | |||
| 86 | // Thread is no longer cancellable. | ||
| 87 | thread->ClearCancellable(); | ||
| 88 | |||
| 89 | // For debugging only | ||
| 90 | thread->SetWaitObjectsForDebugging({}); | ||
| 91 | |||
| 92 | // Cancel the timer as needed. | ||
| 93 | if (timer != InvalidHandle) { | ||
| 94 | auto& time_manager = kernel.TimeManager(); | ||
| 95 | time_manager.UnscheduleTimeEvent(timer); | ||
| 96 | } | ||
| 97 | |||
| 98 | // Get the wait result. | ||
| 99 | ResultCode wait_result{RESULT_SUCCESS}; | ||
| 100 | s32 sync_index = -1; | ||
| 101 | { | ||
| 102 | KScopedSchedulerLock lock(kernel); | ||
| 103 | KSynchronizationObject* synced_obj; | ||
| 104 | wait_result = thread->GetWaitResult(std::addressof(synced_obj)); | ||
| 105 | |||
| 106 | for (auto i = 0; i < num_objects; ++i) { | ||
| 107 | // Unlink the object from the list. | ||
| 108 | ThreadListNode* prev_ptr = | ||
| 109 | reinterpret_cast<ThreadListNode*>(std::addressof(objects[i]->thread_list_head)); | ||
| 110 | ThreadListNode* prev_val = nullptr; | ||
| 111 | ThreadListNode *prev, *tail_prev; | ||
| 112 | |||
| 113 | do { | ||
| 114 | prev = prev_ptr; | ||
| 115 | prev_ptr = prev_ptr->next; | ||
| 116 | tail_prev = prev_val; | ||
| 117 | prev_val = prev_ptr; | ||
| 118 | } while (prev_ptr != std::addressof(thread_nodes[i])); | ||
| 119 | |||
| 120 | if (objects[i]->thread_list_tail == std::addressof(thread_nodes[i])) { | ||
| 121 | objects[i]->thread_list_tail = tail_prev; | ||
| 122 | } | ||
| 123 | |||
| 124 | prev->next = thread_nodes[i].next; | ||
| 125 | |||
| 126 | if (objects[i] == synced_obj) { | ||
| 127 | sync_index = i; | ||
| 128 | } | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | // Set output. | ||
| 133 | *out_index = sync_index; | ||
| 134 | return wait_result; | ||
| 135 | } | ||
| 136 | |||
| 137 | KSynchronizationObject::KSynchronizationObject(KernelCore& kernel) : Object{kernel} {} | ||
| 138 | |||
| 139 | KSynchronizationObject ::~KSynchronizationObject() = default; | ||
| 140 | |||
| 141 | void KSynchronizationObject::NotifyAvailable(ResultCode result) { | ||
| 142 | KScopedSchedulerLock lock(kernel); | ||
| 143 | |||
| 144 | // If we're not signaled, we've nothing to notify. | ||
| 145 | if (!this->IsSignaled()) { | ||
| 146 | return; | ||
| 147 | } | ||
| 148 | |||
| 149 | // Iterate over each thread. | ||
| 150 | for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) { | ||
| 151 | Thread* thread = cur_node->thread; | ||
| 152 | if (thread->GetState() == ThreadState::Waiting) { | ||
| 153 | thread->SetSyncedObject(this, result); | ||
| 154 | thread->SetState(ThreadState::Runnable); | ||
| 155 | } | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | std::vector<Thread*> KSynchronizationObject::GetWaitingThreadsForDebugging() const { | ||
| 160 | std::vector<Thread*> threads; | ||
| 161 | |||
| 162 | // If debugging, dump the list of waiters. | ||
| 163 | { | ||
| 164 | KScopedSchedulerLock lock(kernel); | ||
| 165 | for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) { | ||
| 166 | threads.emplace_back(cur_node->thread); | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | return threads; | ||
| 171 | } | ||
| 172 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_synchronization_object.h b/src/core/hle/kernel/k_synchronization_object.h new file mode 100644 index 000000000..14d80ebf1 --- /dev/null +++ b/src/core/hle/kernel/k_synchronization_object.h | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <vector> | ||
| 8 | |||
| 9 | #include "core/hle/kernel/object.h" | ||
| 10 | #include "core/hle/result.h" | ||
| 11 | |||
| 12 | namespace Kernel { | ||
| 13 | |||
| 14 | class KernelCore; | ||
| 15 | class Synchronization; | ||
| 16 | class Thread; | ||
| 17 | |||
| 18 | /// Class that represents a Kernel object that a thread can be waiting on | ||
| 19 | class KSynchronizationObject : public Object { | ||
| 20 | public: | ||
| 21 | struct ThreadListNode { | ||
| 22 | ThreadListNode* next{}; | ||
| 23 | Thread* thread{}; | ||
| 24 | }; | ||
| 25 | |||
| 26 | [[nodiscard]] static ResultCode Wait(KernelCore& kernel, s32* out_index, | ||
| 27 | KSynchronizationObject** objects, const s32 num_objects, | ||
| 28 | s64 timeout); | ||
| 29 | |||
| 30 | [[nodiscard]] virtual bool IsSignaled() const = 0; | ||
| 31 | |||
| 32 | [[nodiscard]] std::vector<Thread*> GetWaitingThreadsForDebugging() const; | ||
| 33 | |||
| 34 | protected: | ||
| 35 | explicit KSynchronizationObject(KernelCore& kernel); | ||
| 36 | virtual ~KSynchronizationObject(); | ||
| 37 | |||
| 38 | void NotifyAvailable(ResultCode result); | ||
| 39 | void NotifyAvailable() { | ||
| 40 | return this->NotifyAvailable(RESULT_SUCCESS); | ||
| 41 | } | ||
| 42 | |||
| 43 | private: | ||
| 44 | ThreadListNode* thread_list_head{}; | ||
| 45 | ThreadListNode* thread_list_tail{}; | ||
| 46 | }; | ||
| 47 | |||
| 48 | // Specialization of DynamicObjectCast for KSynchronizationObjects | ||
| 49 | template <> | ||
| 50 | inline std::shared_ptr<KSynchronizationObject> DynamicObjectCast<KSynchronizationObject>( | ||
| 51 | std::shared_ptr<Object> object) { | ||
| 52 | if (object != nullptr && object->IsWaitable()) { | ||
| 53 | return std::static_pointer_cast<KSynchronizationObject>(object); | ||
| 54 | } | ||
| 55 | return nullptr; | ||
| 56 | } | ||
| 57 | |||
| 58 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index e8ece8164..c0ff287a6 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -38,7 +38,6 @@ | |||
| 38 | #include "core/hle/kernel/resource_limit.h" | 38 | #include "core/hle/kernel/resource_limit.h" |
| 39 | #include "core/hle/kernel/service_thread.h" | 39 | #include "core/hle/kernel/service_thread.h" |
| 40 | #include "core/hle/kernel/shared_memory.h" | 40 | #include "core/hle/kernel/shared_memory.h" |
| 41 | #include "core/hle/kernel/synchronization.h" | ||
| 42 | #include "core/hle/kernel/thread.h" | 41 | #include "core/hle/kernel/thread.h" |
| 43 | #include "core/hle/kernel/time_manager.h" | 42 | #include "core/hle/kernel/time_manager.h" |
| 44 | #include "core/hle/lock.h" | 43 | #include "core/hle/lock.h" |
| @@ -51,8 +50,7 @@ namespace Kernel { | |||
| 51 | 50 | ||
| 52 | struct KernelCore::Impl { | 51 | struct KernelCore::Impl { |
| 53 | explicit Impl(Core::System& system, KernelCore& kernel) | 52 | explicit Impl(Core::System& system, KernelCore& kernel) |
| 54 | : synchronization{system}, time_manager{system}, global_handle_table{kernel}, system{ | 53 | : time_manager{system}, global_handle_table{kernel}, system{system} {} |
| 55 | system} {} | ||
| 56 | 54 | ||
| 57 | void SetMulticore(bool is_multicore) { | 55 | void SetMulticore(bool is_multicore) { |
| 58 | this->is_multicore = is_multicore; | 56 | this->is_multicore = is_multicore; |
| @@ -307,7 +305,6 @@ struct KernelCore::Impl { | |||
| 307 | std::vector<std::shared_ptr<Process>> process_list; | 305 | std::vector<std::shared_ptr<Process>> process_list; |
| 308 | Process* current_process = nullptr; | 306 | Process* current_process = nullptr; |
| 309 | std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context; | 307 | std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context; |
| 310 | Kernel::Synchronization synchronization; | ||
| 311 | Kernel::TimeManager time_manager; | 308 | Kernel::TimeManager time_manager; |
| 312 | 309 | ||
| 313 | std::shared_ptr<ResourceLimit> system_resource_limit; | 310 | std::shared_ptr<ResourceLimit> system_resource_limit; |
| @@ -461,14 +458,6 @@ const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Kern | |||
| 461 | return impl->interrupts; | 458 | return impl->interrupts; |
| 462 | } | 459 | } |
| 463 | 460 | ||
| 464 | Kernel::Synchronization& KernelCore::Synchronization() { | ||
| 465 | return impl->synchronization; | ||
| 466 | } | ||
| 467 | |||
| 468 | const Kernel::Synchronization& KernelCore::Synchronization() const { | ||
| 469 | return impl->synchronization; | ||
| 470 | } | ||
| 471 | |||
| 472 | Kernel::TimeManager& KernelCore::TimeManager() { | 461 | Kernel::TimeManager& KernelCore::TimeManager() { |
| 473 | return impl->time_manager; | 462 | return impl->time_manager; |
| 474 | } | 463 | } |
| @@ -613,9 +602,11 @@ void KernelCore::Suspend(bool in_suspention) { | |||
| 613 | const bool should_suspend = exception_exited || in_suspention; | 602 | const bool should_suspend = exception_exited || in_suspention; |
| 614 | { | 603 | { |
| 615 | KScopedSchedulerLock lock(*this); | 604 | KScopedSchedulerLock lock(*this); |
| 616 | ThreadStatus status = should_suspend ? ThreadStatus::Ready : ThreadStatus::WaitSleep; | 605 | const auto state = should_suspend ? ThreadState::Runnable : ThreadState::Waiting; |
| 617 | for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { | 606 | for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { |
| 618 | impl->suspend_threads[i]->SetStatus(status); | 607 | impl->suspend_threads[i]->SetState(state); |
| 608 | impl->suspend_threads[i]->SetWaitReasonForDebugging( | ||
| 609 | ThreadWaitReasonForDebugging::Suspended); | ||
| 619 | } | 610 | } |
| 620 | } | 611 | } |
| 621 | } | 612 | } |
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index e3169f5a7..933d9a7d6 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -33,7 +33,6 @@ template <typename T> | |||
| 33 | class SlabHeap; | 33 | class SlabHeap; |
| 34 | } // namespace Memory | 34 | } // namespace Memory |
| 35 | 35 | ||
| 36 | class AddressArbiter; | ||
| 37 | class ClientPort; | 36 | class ClientPort; |
| 38 | class GlobalSchedulerContext; | 37 | class GlobalSchedulerContext; |
| 39 | class HandleTable; | 38 | class HandleTable; |
| @@ -129,12 +128,6 @@ public: | |||
| 129 | /// Gets the an instance of the current physical CPU core. | 128 | /// Gets the an instance of the current physical CPU core. |
| 130 | const Kernel::PhysicalCore& CurrentPhysicalCore() const; | 129 | const Kernel::PhysicalCore& CurrentPhysicalCore() const; |
| 131 | 130 | ||
| 132 | /// Gets the an instance of the Synchronization Interface. | ||
| 133 | Kernel::Synchronization& Synchronization(); | ||
| 134 | |||
| 135 | /// Gets the an instance of the Synchronization Interface. | ||
| 136 | const Kernel::Synchronization& Synchronization() const; | ||
| 137 | |||
| 138 | /// Gets the an instance of the TimeManager Interface. | 131 | /// Gets the an instance of the TimeManager Interface. |
| 139 | Kernel::TimeManager& TimeManager(); | 132 | Kernel::TimeManager& TimeManager(); |
| 140 | 133 | ||
diff --git a/src/core/hle/kernel/memory/address_space_info.cpp b/src/core/hle/kernel/memory/address_space_info.cpp index e4288cab4..6cf43ba24 100644 --- a/src/core/hle/kernel/memory/address_space_info.cpp +++ b/src/core/hle/kernel/memory/address_space_info.cpp | |||
| @@ -96,6 +96,7 @@ u64 AddressSpaceInfo::GetAddressSpaceStart(std::size_t width, Type type) { | |||
| 96 | return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].address; | 96 | return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].address; |
| 97 | } | 97 | } |
| 98 | UNREACHABLE(); | 98 | UNREACHABLE(); |
| 99 | return 0; | ||
| 99 | } | 100 | } |
| 100 | 101 | ||
| 101 | std::size_t AddressSpaceInfo::GetAddressSpaceSize(std::size_t width, Type type) { | 102 | std::size_t AddressSpaceInfo::GetAddressSpaceSize(std::size_t width, Type type) { |
| @@ -112,6 +113,7 @@ std::size_t AddressSpaceInfo::GetAddressSpaceSize(std::size_t width, Type type) | |||
| 112 | return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].size; | 113 | return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].size; |
| 113 | } | 114 | } |
| 114 | UNREACHABLE(); | 115 | UNREACHABLE(); |
| 116 | return 0; | ||
| 115 | } | 117 | } |
| 116 | 118 | ||
| 117 | } // namespace Kernel::Memory | 119 | } // namespace Kernel::Memory |
diff --git a/src/core/hle/kernel/memory/memory_block.h b/src/core/hle/kernel/memory/memory_block.h index 37fe19916..83acece1e 100644 --- a/src/core/hle/kernel/memory/memory_block.h +++ b/src/core/hle/kernel/memory/memory_block.h | |||
| @@ -73,12 +73,12 @@ enum class MemoryState : u32 { | |||
| 73 | ThreadLocal = | 73 | ThreadLocal = |
| 74 | static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagReferenceCounted, | 74 | static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagReferenceCounted, |
| 75 | 75 | ||
| 76 | Transfered = static_cast<u32>(Svc::MemoryState::Transfered) | FlagsMisc | | 76 | Transferred = static_cast<u32>(Svc::MemoryState::Transferred) | FlagsMisc | |
| 77 | FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc | | 77 | FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc | |
| 78 | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, | 78 | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, |
| 79 | 79 | ||
| 80 | SharedTransfered = static_cast<u32>(Svc::MemoryState::SharedTransfered) | FlagsMisc | | 80 | SharedTransferred = static_cast<u32>(Svc::MemoryState::SharedTransferred) | FlagsMisc | |
| 81 | FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, | 81 | FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, |
| 82 | 82 | ||
| 83 | SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped | | 83 | SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped | |
| 84 | FlagReferenceCounted | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, | 84 | FlagReferenceCounted | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, |
| @@ -111,8 +111,8 @@ static_assert(static_cast<u32>(MemoryState::AliasCodeData) == 0x03FFBD09); | |||
| 111 | static_assert(static_cast<u32>(MemoryState::Ipc) == 0x005C3C0A); | 111 | static_assert(static_cast<u32>(MemoryState::Ipc) == 0x005C3C0A); |
| 112 | static_assert(static_cast<u32>(MemoryState::Stack) == 0x005C3C0B); | 112 | static_assert(static_cast<u32>(MemoryState::Stack) == 0x005C3C0B); |
| 113 | static_assert(static_cast<u32>(MemoryState::ThreadLocal) == 0x0040200C); | 113 | static_assert(static_cast<u32>(MemoryState::ThreadLocal) == 0x0040200C); |
| 114 | static_assert(static_cast<u32>(MemoryState::Transfered) == 0x015C3C0D); | 114 | static_assert(static_cast<u32>(MemoryState::Transferred) == 0x015C3C0D); |
| 115 | static_assert(static_cast<u32>(MemoryState::SharedTransfered) == 0x005C380E); | 115 | static_assert(static_cast<u32>(MemoryState::SharedTransferred) == 0x005C380E); |
| 116 | static_assert(static_cast<u32>(MemoryState::SharedCode) == 0x0040380F); | 116 | static_assert(static_cast<u32>(MemoryState::SharedCode) == 0x0040380F); |
| 117 | static_assert(static_cast<u32>(MemoryState::Inaccessible) == 0x00000010); | 117 | static_assert(static_cast<u32>(MemoryState::Inaccessible) == 0x00000010); |
| 118 | static_assert(static_cast<u32>(MemoryState::NonSecureIpc) == 0x005C3811); | 118 | static_assert(static_cast<u32>(MemoryState::NonSecureIpc) == 0x005C3811); |
diff --git a/src/core/hle/kernel/memory/memory_layout.h b/src/core/hle/kernel/memory/memory_layout.h index 9b3d6267a..c7c0b2f49 100644 --- a/src/core/hle/kernel/memory/memory_layout.h +++ b/src/core/hle/kernel/memory/memory_layout.h | |||
| @@ -5,9 +5,28 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "core/device_memory.h" | ||
| 8 | 9 | ||
| 9 | namespace Kernel::Memory { | 10 | namespace Kernel::Memory { |
| 10 | 11 | ||
| 12 | constexpr std::size_t KernelAslrAlignment = 2 * 1024 * 1024; | ||
| 13 | constexpr std::size_t KernelVirtualAddressSpaceWidth = 1ULL << 39; | ||
| 14 | constexpr std::size_t KernelPhysicalAddressSpaceWidth = 1ULL << 48; | ||
| 15 | constexpr std::size_t KernelVirtualAddressSpaceBase = 0ULL - KernelVirtualAddressSpaceWidth; | ||
| 16 | constexpr std::size_t KernelVirtualAddressSpaceEnd = | ||
| 17 | KernelVirtualAddressSpaceBase + (KernelVirtualAddressSpaceWidth - KernelAslrAlignment); | ||
| 18 | constexpr std::size_t KernelVirtualAddressSpaceLast = KernelVirtualAddressSpaceEnd - 1; | ||
| 19 | constexpr std::size_t KernelVirtualAddressSpaceSize = | ||
| 20 | KernelVirtualAddressSpaceEnd - KernelVirtualAddressSpaceBase; | ||
| 21 | |||
| 22 | constexpr bool IsKernelAddressKey(VAddr key) { | ||
| 23 | return KernelVirtualAddressSpaceBase <= key && key <= KernelVirtualAddressSpaceLast; | ||
| 24 | } | ||
| 25 | |||
| 26 | constexpr bool IsKernelAddress(VAddr address) { | ||
| 27 | return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd; | ||
| 28 | } | ||
| 29 | |||
| 11 | class MemoryRegion final { | 30 | class MemoryRegion final { |
| 12 | friend class MemoryLayout; | 31 | friend class MemoryLayout; |
| 13 | 32 | ||
diff --git a/src/core/hle/kernel/memory/page_heap.h b/src/core/hle/kernel/memory/page_heap.h index 22b0de860..131093284 100644 --- a/src/core/hle/kernel/memory/page_heap.h +++ b/src/core/hle/kernel/memory/page_heap.h | |||
| @@ -8,11 +8,11 @@ | |||
| 8 | #pragma once | 8 | #pragma once |
| 9 | 9 | ||
| 10 | #include <array> | 10 | #include <array> |
| 11 | #include <bit> | ||
| 11 | #include <vector> | 12 | #include <vector> |
| 12 | 13 | ||
| 13 | #include "common/alignment.h" | 14 | #include "common/alignment.h" |
| 14 | #include "common/assert.h" | 15 | #include "common/assert.h" |
| 15 | #include "common/bit_util.h" | ||
| 16 | #include "common/common_funcs.h" | 16 | #include "common/common_funcs.h" |
| 17 | #include "common/common_types.h" | 17 | #include "common/common_types.h" |
| 18 | #include "core/hle/kernel/memory/memory_types.h" | 18 | #include "core/hle/kernel/memory/memory_types.h" |
| @@ -105,7 +105,7 @@ private: | |||
| 105 | ASSERT(depth == 0); | 105 | ASSERT(depth == 0); |
| 106 | return -1; | 106 | return -1; |
| 107 | } | 107 | } |
| 108 | offset = offset * 64 + Common::CountTrailingZeroes64(v); | 108 | offset = offset * 64 + static_cast<u32>(std::countr_zero(v)); |
| 109 | ++depth; | 109 | ++depth; |
| 110 | } while (depth < static_cast<s32>(used_depths)); | 110 | } while (depth < static_cast<s32>(used_depths)); |
| 111 | 111 | ||
diff --git a/src/core/hle/kernel/memory/page_table.cpp b/src/core/hle/kernel/memory/page_table.cpp index f3e8bc333..080886554 100644 --- a/src/core/hle/kernel/memory/page_table.cpp +++ b/src/core/hle/kernel/memory/page_table.cpp | |||
| @@ -1007,8 +1007,8 @@ constexpr VAddr PageTable::GetRegionAddress(MemoryState state) const { | |||
| 1007 | case MemoryState::Shared: | 1007 | case MemoryState::Shared: |
| 1008 | case MemoryState::AliasCode: | 1008 | case MemoryState::AliasCode: |
| 1009 | case MemoryState::AliasCodeData: | 1009 | case MemoryState::AliasCodeData: |
| 1010 | case MemoryState::Transfered: | 1010 | case MemoryState::Transferred: |
| 1011 | case MemoryState::SharedTransfered: | 1011 | case MemoryState::SharedTransferred: |
| 1012 | case MemoryState::SharedCode: | 1012 | case MemoryState::SharedCode: |
| 1013 | case MemoryState::GeneratedCode: | 1013 | case MemoryState::GeneratedCode: |
| 1014 | case MemoryState::CodeOut: | 1014 | case MemoryState::CodeOut: |
| @@ -1042,8 +1042,8 @@ constexpr std::size_t PageTable::GetRegionSize(MemoryState state) const { | |||
| 1042 | case MemoryState::Shared: | 1042 | case MemoryState::Shared: |
| 1043 | case MemoryState::AliasCode: | 1043 | case MemoryState::AliasCode: |
| 1044 | case MemoryState::AliasCodeData: | 1044 | case MemoryState::AliasCodeData: |
| 1045 | case MemoryState::Transfered: | 1045 | case MemoryState::Transferred: |
| 1046 | case MemoryState::SharedTransfered: | 1046 | case MemoryState::SharedTransferred: |
| 1047 | case MemoryState::SharedCode: | 1047 | case MemoryState::SharedCode: |
| 1048 | case MemoryState::GeneratedCode: | 1048 | case MemoryState::GeneratedCode: |
| 1049 | case MemoryState::CodeOut: | 1049 | case MemoryState::CodeOut: |
| @@ -1080,8 +1080,8 @@ constexpr bool PageTable::CanContain(VAddr addr, std::size_t size, MemoryState s | |||
| 1080 | case MemoryState::AliasCodeData: | 1080 | case MemoryState::AliasCodeData: |
| 1081 | case MemoryState::Stack: | 1081 | case MemoryState::Stack: |
| 1082 | case MemoryState::ThreadLocal: | 1082 | case MemoryState::ThreadLocal: |
| 1083 | case MemoryState::Transfered: | 1083 | case MemoryState::Transferred: |
| 1084 | case MemoryState::SharedTransfered: | 1084 | case MemoryState::SharedTransferred: |
| 1085 | case MemoryState::SharedCode: | 1085 | case MemoryState::SharedCode: |
| 1086 | case MemoryState::GeneratedCode: | 1086 | case MemoryState::GeneratedCode: |
| 1087 | case MemoryState::CodeOut: | 1087 | case MemoryState::CodeOut: |
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp deleted file mode 100644 index 4f8075e0e..000000000 --- a/src/core/hle/kernel/mutex.cpp +++ /dev/null | |||
| @@ -1,170 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <memory> | ||
| 6 | #include <utility> | ||
| 7 | #include <vector> | ||
| 8 | |||
| 9 | #include "common/assert.h" | ||
| 10 | #include "common/logging/log.h" | ||
| 11 | #include "core/core.h" | ||
| 12 | #include "core/hle/kernel/errors.h" | ||
| 13 | #include "core/hle/kernel/handle_table.h" | ||
| 14 | #include "core/hle/kernel/k_scheduler.h" | ||
| 15 | #include "core/hle/kernel/kernel.h" | ||
| 16 | #include "core/hle/kernel/mutex.h" | ||
| 17 | #include "core/hle/kernel/object.h" | ||
| 18 | #include "core/hle/kernel/process.h" | ||
| 19 | #include "core/hle/kernel/thread.h" | ||
| 20 | #include "core/hle/result.h" | ||
| 21 | #include "core/memory.h" | ||
| 22 | |||
| 23 | namespace Kernel { | ||
| 24 | |||
| 25 | /// Returns the number of threads that are waiting for a mutex, and the highest priority one among | ||
| 26 | /// those. | ||
| 27 | static std::pair<std::shared_ptr<Thread>, u32> GetHighestPriorityMutexWaitingThread( | ||
| 28 | const std::shared_ptr<Thread>& current_thread, VAddr mutex_addr) { | ||
| 29 | |||
| 30 | std::shared_ptr<Thread> highest_priority_thread; | ||
| 31 | u32 num_waiters = 0; | ||
| 32 | |||
| 33 | for (const auto& thread : current_thread->GetMutexWaitingThreads()) { | ||
| 34 | if (thread->GetMutexWaitAddress() != mutex_addr) | ||
| 35 | continue; | ||
| 36 | |||
| 37 | ++num_waiters; | ||
| 38 | if (highest_priority_thread == nullptr || | ||
| 39 | thread->GetPriority() < highest_priority_thread->GetPriority()) { | ||
| 40 | highest_priority_thread = thread; | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | return {highest_priority_thread, num_waiters}; | ||
| 45 | } | ||
| 46 | |||
| 47 | /// Update the mutex owner field of all threads waiting on the mutex to point to the new owner. | ||
| 48 | static void TransferMutexOwnership(VAddr mutex_addr, std::shared_ptr<Thread> current_thread, | ||
| 49 | std::shared_ptr<Thread> new_owner) { | ||
| 50 | current_thread->RemoveMutexWaiter(new_owner); | ||
| 51 | const auto threads = current_thread->GetMutexWaitingThreads(); | ||
| 52 | for (const auto& thread : threads) { | ||
| 53 | if (thread->GetMutexWaitAddress() != mutex_addr) | ||
| 54 | continue; | ||
| 55 | |||
| 56 | ASSERT(thread->GetLockOwner() == current_thread.get()); | ||
| 57 | current_thread->RemoveMutexWaiter(thread); | ||
| 58 | if (new_owner != thread) | ||
| 59 | new_owner->AddMutexWaiter(thread); | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | Mutex::Mutex(Core::System& system) : system{system} {} | ||
| 64 | Mutex::~Mutex() = default; | ||
| 65 | |||
| 66 | ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle, | ||
| 67 | Handle requesting_thread_handle) { | ||
| 68 | // The mutex address must be 4-byte aligned | ||
| 69 | if ((address % sizeof(u32)) != 0) { | ||
| 70 | LOG_ERROR(Kernel, "Address is not 4-byte aligned! address={:016X}", address); | ||
| 71 | return ERR_INVALID_ADDRESS; | ||
| 72 | } | ||
| 73 | |||
| 74 | auto& kernel = system.Kernel(); | ||
| 75 | std::shared_ptr<Thread> current_thread = | ||
| 76 | SharedFrom(kernel.CurrentScheduler()->GetCurrentThread()); | ||
| 77 | { | ||
| 78 | KScopedSchedulerLock lock(kernel); | ||
| 79 | // The mutex address must be 4-byte aligned | ||
| 80 | if ((address % sizeof(u32)) != 0) { | ||
| 81 | return ERR_INVALID_ADDRESS; | ||
| 82 | } | ||
| 83 | |||
| 84 | const auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); | ||
| 85 | std::shared_ptr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle); | ||
| 86 | std::shared_ptr<Thread> requesting_thread = | ||
| 87 | handle_table.Get<Thread>(requesting_thread_handle); | ||
| 88 | |||
| 89 | // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of | ||
| 90 | // another thread. | ||
| 91 | ASSERT(requesting_thread == current_thread); | ||
| 92 | |||
| 93 | current_thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS); | ||
| 94 | |||
| 95 | const u32 addr_value = system.Memory().Read32(address); | ||
| 96 | |||
| 97 | // If the mutex isn't being held, just return success. | ||
| 98 | if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) { | ||
| 99 | return RESULT_SUCCESS; | ||
| 100 | } | ||
| 101 | |||
| 102 | if (holding_thread == nullptr) { | ||
| 103 | return ERR_INVALID_HANDLE; | ||
| 104 | } | ||
| 105 | |||
| 106 | // Wait until the mutex is released | ||
| 107 | current_thread->SetMutexWaitAddress(address); | ||
| 108 | current_thread->SetWaitHandle(requesting_thread_handle); | ||
| 109 | |||
| 110 | current_thread->SetStatus(ThreadStatus::WaitMutex); | ||
| 111 | |||
| 112 | // Update the lock holder thread's priority to prevent priority inversion. | ||
| 113 | holding_thread->AddMutexWaiter(current_thread); | ||
| 114 | } | ||
| 115 | |||
| 116 | { | ||
| 117 | KScopedSchedulerLock lock(kernel); | ||
| 118 | auto* owner = current_thread->GetLockOwner(); | ||
| 119 | if (owner != nullptr) { | ||
| 120 | owner->RemoveMutexWaiter(current_thread); | ||
| 121 | } | ||
| 122 | } | ||
| 123 | return current_thread->GetSignalingResult(); | ||
| 124 | } | ||
| 125 | |||
| 126 | std::pair<ResultCode, std::shared_ptr<Thread>> Mutex::Unlock(std::shared_ptr<Thread> owner, | ||
| 127 | VAddr address) { | ||
| 128 | // The mutex address must be 4-byte aligned | ||
| 129 | if ((address % sizeof(u32)) != 0) { | ||
| 130 | LOG_ERROR(Kernel, "Address is not 4-byte aligned! address={:016X}", address); | ||
| 131 | return {ERR_INVALID_ADDRESS, nullptr}; | ||
| 132 | } | ||
| 133 | |||
| 134 | auto [new_owner, num_waiters] = GetHighestPriorityMutexWaitingThread(owner, address); | ||
| 135 | if (new_owner == nullptr) { | ||
| 136 | system.Memory().Write32(address, 0); | ||
| 137 | return {RESULT_SUCCESS, nullptr}; | ||
| 138 | } | ||
| 139 | // Transfer the ownership of the mutex from the previous owner to the new one. | ||
| 140 | TransferMutexOwnership(address, owner, new_owner); | ||
| 141 | u32 mutex_value = new_owner->GetWaitHandle(); | ||
| 142 | if (num_waiters >= 2) { | ||
| 143 | // Notify the guest that there are still some threads waiting for the mutex | ||
| 144 | mutex_value |= Mutex::MutexHasWaitersFlag; | ||
| 145 | } | ||
| 146 | new_owner->SetSynchronizationResults(nullptr, RESULT_SUCCESS); | ||
| 147 | new_owner->SetLockOwner(nullptr); | ||
| 148 | new_owner->ResumeFromWait(); | ||
| 149 | |||
| 150 | system.Memory().Write32(address, mutex_value); | ||
| 151 | return {RESULT_SUCCESS, new_owner}; | ||
| 152 | } | ||
| 153 | |||
| 154 | ResultCode Mutex::Release(VAddr address) { | ||
| 155 | auto& kernel = system.Kernel(); | ||
| 156 | KScopedSchedulerLock lock(kernel); | ||
| 157 | |||
| 158 | std::shared_ptr<Thread> current_thread = | ||
| 159 | SharedFrom(kernel.CurrentScheduler()->GetCurrentThread()); | ||
| 160 | |||
| 161 | auto [result, new_owner] = Unlock(current_thread, address); | ||
| 162 | |||
| 163 | if (result != RESULT_SUCCESS && new_owner != nullptr) { | ||
| 164 | new_owner->SetSynchronizationResults(nullptr, result); | ||
| 165 | } | ||
| 166 | |||
| 167 | return result; | ||
| 168 | } | ||
| 169 | |||
| 170 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h deleted file mode 100644 index 3b81dc3df..000000000 --- a/src/core/hle/kernel/mutex.h +++ /dev/null | |||
| @@ -1,42 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | union ResultCode; | ||
| 10 | |||
| 11 | namespace Core { | ||
| 12 | class System; | ||
| 13 | } | ||
| 14 | |||
| 15 | namespace Kernel { | ||
| 16 | |||
| 17 | class Mutex final { | ||
| 18 | public: | ||
| 19 | explicit Mutex(Core::System& system); | ||
| 20 | ~Mutex(); | ||
| 21 | |||
| 22 | /// Flag that indicates that a mutex still has threads waiting for it. | ||
| 23 | static constexpr u32 MutexHasWaitersFlag = 0x40000000; | ||
| 24 | /// Mask of the bits in a mutex address value that contain the mutex owner. | ||
| 25 | static constexpr u32 MutexOwnerMask = 0xBFFFFFFF; | ||
| 26 | |||
| 27 | /// Attempts to acquire a mutex at the specified address. | ||
| 28 | ResultCode TryAcquire(VAddr address, Handle holding_thread_handle, | ||
| 29 | Handle requesting_thread_handle); | ||
| 30 | |||
| 31 | /// Unlocks a mutex for owner at address | ||
| 32 | std::pair<ResultCode, std::shared_ptr<Thread>> Unlock(std::shared_ptr<Thread> owner, | ||
| 33 | VAddr address); | ||
| 34 | |||
| 35 | /// Releases the mutex at the specified address. | ||
| 36 | ResultCode Release(VAddr address); | ||
| 37 | |||
| 38 | private: | ||
| 39 | Core::System& system; | ||
| 40 | }; | ||
| 41 | |||
| 42 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h index e3391e2af..27124ef67 100644 --- a/src/core/hle/kernel/object.h +++ b/src/core/hle/kernel/object.h | |||
| @@ -50,6 +50,11 @@ public: | |||
| 50 | } | 50 | } |
| 51 | virtual HandleType GetHandleType() const = 0; | 51 | virtual HandleType GetHandleType() const = 0; |
| 52 | 52 | ||
| 53 | void Close() { | ||
| 54 | // TODO(bunnei): This is a placeholder to decrement the reference count, which we will use | ||
| 55 | // when we implement KAutoObject instead of using shared_ptr. | ||
| 56 | } | ||
| 57 | |||
| 53 | /** | 58 | /** |
| 54 | * Check if a thread can wait on the object | 59 | * Check if a thread can wait on the object |
| 55 | * @return True if a thread can wait on the object, otherwise false | 60 | * @return True if a thread can wait on the object, otherwise false |
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index b905b486a..37b77fa6e 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp | |||
| @@ -55,7 +55,7 @@ void SetupMainThread(Core::System& system, Process& owner_process, u32 priority, | |||
| 55 | // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires | 55 | // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires |
| 56 | { | 56 | { |
| 57 | KScopedSchedulerLock lock{kernel}; | 57 | KScopedSchedulerLock lock{kernel}; |
| 58 | thread->SetStatus(ThreadStatus::Ready); | 58 | thread->SetState(ThreadState::Runnable); |
| 59 | } | 59 | } |
| 60 | } | 60 | } |
| 61 | } // Anonymous namespace | 61 | } // Anonymous namespace |
| @@ -162,48 +162,6 @@ u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const { | |||
| 162 | return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage(); | 162 | return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage(); |
| 163 | } | 163 | } |
| 164 | 164 | ||
| 165 | void Process::InsertConditionVariableThread(std::shared_ptr<Thread> thread) { | ||
| 166 | VAddr cond_var_addr = thread->GetCondVarWaitAddress(); | ||
| 167 | std::list<std::shared_ptr<Thread>>& thread_list = cond_var_threads[cond_var_addr]; | ||
| 168 | auto it = thread_list.begin(); | ||
| 169 | while (it != thread_list.end()) { | ||
| 170 | const std::shared_ptr<Thread> current_thread = *it; | ||
| 171 | if (current_thread->GetPriority() > thread->GetPriority()) { | ||
| 172 | thread_list.insert(it, thread); | ||
| 173 | return; | ||
| 174 | } | ||
| 175 | ++it; | ||
| 176 | } | ||
| 177 | thread_list.push_back(thread); | ||
| 178 | } | ||
| 179 | |||
| 180 | void Process::RemoveConditionVariableThread(std::shared_ptr<Thread> thread) { | ||
| 181 | VAddr cond_var_addr = thread->GetCondVarWaitAddress(); | ||
| 182 | std::list<std::shared_ptr<Thread>>& thread_list = cond_var_threads[cond_var_addr]; | ||
| 183 | auto it = thread_list.begin(); | ||
| 184 | while (it != thread_list.end()) { | ||
| 185 | const std::shared_ptr<Thread> current_thread = *it; | ||
| 186 | if (current_thread.get() == thread.get()) { | ||
| 187 | thread_list.erase(it); | ||
| 188 | return; | ||
| 189 | } | ||
| 190 | ++it; | ||
| 191 | } | ||
| 192 | } | ||
| 193 | |||
| 194 | std::vector<std::shared_ptr<Thread>> Process::GetConditionVariableThreads( | ||
| 195 | const VAddr cond_var_addr) { | ||
| 196 | std::vector<std::shared_ptr<Thread>> result{}; | ||
| 197 | std::list<std::shared_ptr<Thread>>& thread_list = cond_var_threads[cond_var_addr]; | ||
| 198 | auto it = thread_list.begin(); | ||
| 199 | while (it != thread_list.end()) { | ||
| 200 | std::shared_ptr<Thread> current_thread = *it; | ||
| 201 | result.push_back(current_thread); | ||
| 202 | ++it; | ||
| 203 | } | ||
| 204 | return result; | ||
| 205 | } | ||
| 206 | |||
| 207 | void Process::RegisterThread(const Thread* thread) { | 165 | void Process::RegisterThread(const Thread* thread) { |
| 208 | thread_list.push_back(thread); | 166 | thread_list.push_back(thread); |
| 209 | } | 167 | } |
| @@ -318,7 +276,7 @@ void Process::PrepareForTermination() { | |||
| 318 | continue; | 276 | continue; |
| 319 | 277 | ||
| 320 | // TODO(Subv): When are the other running/ready threads terminated? | 278 | // TODO(Subv): When are the other running/ready threads terminated? |
| 321 | ASSERT_MSG(thread->GetStatus() == ThreadStatus::WaitSynch, | 279 | ASSERT_MSG(thread->GetState() == ThreadState::Waiting, |
| 322 | "Exiting processes with non-waiting threads is currently unimplemented"); | 280 | "Exiting processes with non-waiting threads is currently unimplemented"); |
| 323 | 281 | ||
| 324 | thread->Stop(); | 282 | thread->Stop(); |
| @@ -406,21 +364,18 @@ void Process::LoadModule(CodeSet code_set, VAddr base_addr) { | |||
| 406 | ReprotectSegment(code_set.DataSegment(), Memory::MemoryPermission::ReadAndWrite); | 364 | ReprotectSegment(code_set.DataSegment(), Memory::MemoryPermission::ReadAndWrite); |
| 407 | } | 365 | } |
| 408 | 366 | ||
| 367 | bool Process::IsSignaled() const { | ||
| 368 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); | ||
| 369 | return is_signaled; | ||
| 370 | } | ||
| 371 | |||
| 409 | Process::Process(Core::System& system) | 372 | Process::Process(Core::System& system) |
| 410 | : SynchronizationObject{system.Kernel()}, page_table{std::make_unique<Memory::PageTable>( | 373 | : KSynchronizationObject{system.Kernel()}, |
| 411 | system)}, | 374 | page_table{std::make_unique<Memory::PageTable>(system)}, handle_table{system.Kernel()}, |
| 412 | handle_table{system.Kernel()}, address_arbiter{system}, mutex{system}, system{system} {} | 375 | address_arbiter{system}, condition_var{system}, system{system} {} |
| 413 | 376 | ||
| 414 | Process::~Process() = default; | 377 | Process::~Process() = default; |
| 415 | 378 | ||
| 416 | void Process::Acquire(Thread* thread) { | ||
| 417 | ASSERT_MSG(!ShouldWait(thread), "Object unavailable!"); | ||
| 418 | } | ||
| 419 | |||
| 420 | bool Process::ShouldWait(const Thread* thread) const { | ||
| 421 | return !is_signaled; | ||
| 422 | } | ||
| 423 | |||
| 424 | void Process::ChangeStatus(ProcessStatus new_status) { | 379 | void Process::ChangeStatus(ProcessStatus new_status) { |
| 425 | if (status == new_status) { | 380 | if (status == new_status) { |
| 426 | return; | 381 | return; |
| @@ -428,7 +383,7 @@ void Process::ChangeStatus(ProcessStatus new_status) { | |||
| 428 | 383 | ||
| 429 | status = new_status; | 384 | status = new_status; |
| 430 | is_signaled = true; | 385 | is_signaled = true; |
| 431 | Signal(); | 386 | NotifyAvailable(); |
| 432 | } | 387 | } |
| 433 | 388 | ||
| 434 | ResultCode Process::AllocateMainThreadStack(std::size_t stack_size) { | 389 | ResultCode Process::AllocateMainThreadStack(std::size_t stack_size) { |
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index e412e58aa..564e1f27d 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h | |||
| @@ -11,11 +11,11 @@ | |||
| 11 | #include <unordered_map> | 11 | #include <unordered_map> |
| 12 | #include <vector> | 12 | #include <vector> |
| 13 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 14 | #include "core/hle/kernel/address_arbiter.h" | ||
| 15 | #include "core/hle/kernel/handle_table.h" | 14 | #include "core/hle/kernel/handle_table.h" |
| 16 | #include "core/hle/kernel/mutex.h" | 15 | #include "core/hle/kernel/k_address_arbiter.h" |
| 16 | #include "core/hle/kernel/k_condition_variable.h" | ||
| 17 | #include "core/hle/kernel/k_synchronization_object.h" | ||
| 17 | #include "core/hle/kernel/process_capability.h" | 18 | #include "core/hle/kernel/process_capability.h" |
| 18 | #include "core/hle/kernel/synchronization_object.h" | ||
| 19 | #include "core/hle/result.h" | 19 | #include "core/hle/result.h" |
| 20 | 20 | ||
| 21 | namespace Core { | 21 | namespace Core { |
| @@ -63,7 +63,7 @@ enum class ProcessStatus { | |||
| 63 | DebugBreak, | 63 | DebugBreak, |
| 64 | }; | 64 | }; |
| 65 | 65 | ||
| 66 | class Process final : public SynchronizationObject { | 66 | class Process final : public KSynchronizationObject { |
| 67 | public: | 67 | public: |
| 68 | explicit Process(Core::System& system); | 68 | explicit Process(Core::System& system); |
| 69 | ~Process() override; | 69 | ~Process() override; |
| @@ -123,24 +123,30 @@ public: | |||
| 123 | return handle_table; | 123 | return handle_table; |
| 124 | } | 124 | } |
| 125 | 125 | ||
| 126 | /// Gets a reference to the process' address arbiter. | 126 | ResultCode SignalToAddress(VAddr address) { |
| 127 | AddressArbiter& GetAddressArbiter() { | 127 | return condition_var.SignalToAddress(address); |
| 128 | return address_arbiter; | ||
| 129 | } | 128 | } |
| 130 | 129 | ||
| 131 | /// Gets a const reference to the process' address arbiter. | 130 | ResultCode WaitForAddress(Handle handle, VAddr address, u32 tag) { |
| 132 | const AddressArbiter& GetAddressArbiter() const { | 131 | return condition_var.WaitForAddress(handle, address, tag); |
| 133 | return address_arbiter; | ||
| 134 | } | 132 | } |
| 135 | 133 | ||
| 136 | /// Gets a reference to the process' mutex lock. | 134 | void SignalConditionVariable(u64 cv_key, int32_t count) { |
| 137 | Mutex& GetMutex() { | 135 | return condition_var.Signal(cv_key, count); |
| 138 | return mutex; | ||
| 139 | } | 136 | } |
| 140 | 137 | ||
| 141 | /// Gets a const reference to the process' mutex lock | 138 | ResultCode WaitConditionVariable(VAddr address, u64 cv_key, u32 tag, s64 ns) { |
| 142 | const Mutex& GetMutex() const { | 139 | return condition_var.Wait(address, cv_key, tag, ns); |
| 143 | return mutex; | 140 | } |
| 141 | |||
| 142 | ResultCode SignalAddressArbiter(VAddr address, Svc::SignalType signal_type, s32 value, | ||
| 143 | s32 count) { | ||
| 144 | return address_arbiter.SignalToAddress(address, signal_type, value, count); | ||
| 145 | } | ||
| 146 | |||
| 147 | ResultCode WaitAddressArbiter(VAddr address, Svc::ArbitrationType arb_type, s32 value, | ||
| 148 | s64 timeout) { | ||
| 149 | return address_arbiter.WaitForAddress(address, arb_type, value, timeout); | ||
| 144 | } | 150 | } |
| 145 | 151 | ||
| 146 | /// Gets the address to the process' dedicated TLS region. | 152 | /// Gets the address to the process' dedicated TLS region. |
| @@ -250,15 +256,6 @@ public: | |||
| 250 | return thread_list; | 256 | return thread_list; |
| 251 | } | 257 | } |
| 252 | 258 | ||
| 253 | /// Insert a thread into the condition variable wait container | ||
| 254 | void InsertConditionVariableThread(std::shared_ptr<Thread> thread); | ||
| 255 | |||
| 256 | /// Remove a thread from the condition variable wait container | ||
| 257 | void RemoveConditionVariableThread(std::shared_ptr<Thread> thread); | ||
| 258 | |||
| 259 | /// Obtain all condition variable threads waiting for some address | ||
| 260 | std::vector<std::shared_ptr<Thread>> GetConditionVariableThreads(VAddr cond_var_addr); | ||
| 261 | |||
| 262 | /// Registers a thread as being created under this process, | 259 | /// Registers a thread as being created under this process, |
| 263 | /// adding it to this process' thread list. | 260 | /// adding it to this process' thread list. |
| 264 | void RegisterThread(const Thread* thread); | 261 | void RegisterThread(const Thread* thread); |
| @@ -304,6 +301,8 @@ public: | |||
| 304 | 301 | ||
| 305 | void LoadModule(CodeSet code_set, VAddr base_addr); | 302 | void LoadModule(CodeSet code_set, VAddr base_addr); |
| 306 | 303 | ||
| 304 | bool IsSignaled() const override; | ||
| 305 | |||
| 307 | /////////////////////////////////////////////////////////////////////////////////////////////// | 306 | /////////////////////////////////////////////////////////////////////////////////////////////// |
| 308 | // Thread-local storage management | 307 | // Thread-local storage management |
| 309 | 308 | ||
| @@ -314,12 +313,6 @@ public: | |||
| 314 | void FreeTLSRegion(VAddr tls_address); | 313 | void FreeTLSRegion(VAddr tls_address); |
| 315 | 314 | ||
| 316 | private: | 315 | private: |
| 317 | /// Checks if the specified thread should wait until this process is available. | ||
| 318 | bool ShouldWait(const Thread* thread) const override; | ||
| 319 | |||
| 320 | /// Acquires/locks this process for the specified thread if it's available. | ||
| 321 | void Acquire(Thread* thread) override; | ||
| 322 | |||
| 323 | /// Changes the process status. If the status is different | 316 | /// Changes the process status. If the status is different |
| 324 | /// from the current process status, then this will trigger | 317 | /// from the current process status, then this will trigger |
| 325 | /// a process signal. | 318 | /// a process signal. |
| @@ -373,12 +366,12 @@ private: | |||
| 373 | HandleTable handle_table; | 366 | HandleTable handle_table; |
| 374 | 367 | ||
| 375 | /// Per-process address arbiter. | 368 | /// Per-process address arbiter. |
| 376 | AddressArbiter address_arbiter; | 369 | KAddressArbiter address_arbiter; |
| 377 | 370 | ||
| 378 | /// The per-process mutex lock instance used for handling various | 371 | /// The per-process mutex lock instance used for handling various |
| 379 | /// forms of services, such as lock arbitration, and condition | 372 | /// forms of services, such as lock arbitration, and condition |
| 380 | /// variable related facilities. | 373 | /// variable related facilities. |
| 381 | Mutex mutex; | 374 | KConditionVariable condition_var; |
| 382 | 375 | ||
| 383 | /// Address indicating the location of the process' dedicated TLS region. | 376 | /// Address indicating the location of the process' dedicated TLS region. |
| 384 | VAddr tls_region_address = 0; | 377 | VAddr tls_region_address = 0; |
| @@ -389,9 +382,6 @@ private: | |||
| 389 | /// List of threads that are running with this process as their owner. | 382 | /// List of threads that are running with this process as their owner. |
| 390 | std::list<const Thread*> thread_list; | 383 | std::list<const Thread*> thread_list; |
| 391 | 384 | ||
| 392 | /// List of threads waiting for a condition variable | ||
| 393 | std::unordered_map<VAddr, std::list<std::shared_ptr<Thread>>> cond_var_threads; | ||
| 394 | |||
| 395 | /// Address of the top of the main thread's stack | 385 | /// Address of the top of the main thread's stack |
| 396 | VAddr main_thread_stack_top{}; | 386 | VAddr main_thread_stack_top{}; |
| 397 | 387 | ||
| @@ -410,6 +400,8 @@ private: | |||
| 410 | /// Schedule count of this process | 400 | /// Schedule count of this process |
| 411 | s64 schedule_count{}; | 401 | s64 schedule_count{}; |
| 412 | 402 | ||
| 403 | bool is_signaled{}; | ||
| 404 | |||
| 413 | /// System context | 405 | /// System context |
| 414 | Core::System& system; | 406 | Core::System& system; |
| 415 | }; | 407 | }; |
diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp index 0f128c586..0566311b6 100644 --- a/src/core/hle/kernel/process_capability.cpp +++ b/src/core/hle/kernel/process_capability.cpp | |||
| @@ -2,6 +2,8 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <bit> | ||
| 6 | |||
| 5 | #include "common/bit_util.h" | 7 | #include "common/bit_util.h" |
| 6 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 7 | #include "core/hle/kernel/errors.h" | 9 | #include "core/hle/kernel/errors.h" |
| @@ -60,7 +62,7 @@ constexpr CapabilityType GetCapabilityType(u32 value) { | |||
| 60 | 62 | ||
| 61 | u32 GetFlagBitOffset(CapabilityType type) { | 63 | u32 GetFlagBitOffset(CapabilityType type) { |
| 62 | const auto value = static_cast<u32>(type); | 64 | const auto value = static_cast<u32>(type); |
| 63 | return static_cast<u32>(Common::BitSize<u32>() - Common::CountLeadingZeroes32(value)); | 65 | return static_cast<u32>(Common::BitSize<u32>() - static_cast<u32>(std::countl_zero(value))); |
| 64 | } | 66 | } |
| 65 | 67 | ||
| 66 | } // Anonymous namespace | 68 | } // Anonymous namespace |
diff --git a/src/core/hle/kernel/readable_event.cpp b/src/core/hle/kernel/readable_event.cpp index cea262ce0..99ed0857e 100644 --- a/src/core/hle/kernel/readable_event.cpp +++ b/src/core/hle/kernel/readable_event.cpp | |||
| @@ -14,24 +14,22 @@ | |||
| 14 | 14 | ||
| 15 | namespace Kernel { | 15 | namespace Kernel { |
| 16 | 16 | ||
| 17 | ReadableEvent::ReadableEvent(KernelCore& kernel) : SynchronizationObject{kernel} {} | 17 | ReadableEvent::ReadableEvent(KernelCore& kernel) : KSynchronizationObject{kernel} {} |
| 18 | ReadableEvent::~ReadableEvent() = default; | 18 | ReadableEvent::~ReadableEvent() = default; |
| 19 | 19 | ||
| 20 | bool ReadableEvent::ShouldWait(const Thread* thread) const { | ||
| 21 | return !is_signaled; | ||
| 22 | } | ||
| 23 | |||
| 24 | void ReadableEvent::Acquire(Thread* thread) { | ||
| 25 | ASSERT_MSG(IsSignaled(), "object unavailable!"); | ||
| 26 | } | ||
| 27 | |||
| 28 | void ReadableEvent::Signal() { | 20 | void ReadableEvent::Signal() { |
| 29 | if (is_signaled) { | 21 | if (is_signaled) { |
| 30 | return; | 22 | return; |
| 31 | } | 23 | } |
| 32 | 24 | ||
| 33 | is_signaled = true; | 25 | is_signaled = true; |
| 34 | SynchronizationObject::Signal(); | 26 | NotifyAvailable(); |
| 27 | } | ||
| 28 | |||
| 29 | bool ReadableEvent::IsSignaled() const { | ||
| 30 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); | ||
| 31 | |||
| 32 | return is_signaled; | ||
| 35 | } | 33 | } |
| 36 | 34 | ||
| 37 | void ReadableEvent::Clear() { | 35 | void ReadableEvent::Clear() { |
diff --git a/src/core/hle/kernel/readable_event.h b/src/core/hle/kernel/readable_event.h index 3264dd066..34e477274 100644 --- a/src/core/hle/kernel/readable_event.h +++ b/src/core/hle/kernel/readable_event.h | |||
| @@ -4,8 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "core/hle/kernel/k_synchronization_object.h" | ||
| 7 | #include "core/hle/kernel/object.h" | 8 | #include "core/hle/kernel/object.h" |
| 8 | #include "core/hle/kernel/synchronization_object.h" | ||
| 9 | 9 | ||
| 10 | union ResultCode; | 10 | union ResultCode; |
| 11 | 11 | ||
| @@ -14,7 +14,7 @@ namespace Kernel { | |||
| 14 | class KernelCore; | 14 | class KernelCore; |
| 15 | class WritableEvent; | 15 | class WritableEvent; |
| 16 | 16 | ||
| 17 | class ReadableEvent final : public SynchronizationObject { | 17 | class ReadableEvent final : public KSynchronizationObject { |
| 18 | friend class WritableEvent; | 18 | friend class WritableEvent; |
| 19 | 19 | ||
| 20 | public: | 20 | public: |
| @@ -32,9 +32,6 @@ public: | |||
| 32 | return HANDLE_TYPE; | 32 | return HANDLE_TYPE; |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | bool ShouldWait(const Thread* thread) const override; | ||
| 36 | void Acquire(Thread* thread) override; | ||
| 37 | |||
| 38 | /// Unconditionally clears the readable event's state. | 35 | /// Unconditionally clears the readable event's state. |
| 39 | void Clear(); | 36 | void Clear(); |
| 40 | 37 | ||
| @@ -46,11 +43,14 @@ public: | |||
| 46 | /// then ERR_INVALID_STATE will be returned. | 43 | /// then ERR_INVALID_STATE will be returned. |
| 47 | ResultCode Reset(); | 44 | ResultCode Reset(); |
| 48 | 45 | ||
| 49 | void Signal() override; | 46 | void Signal(); |
| 47 | |||
| 48 | bool IsSignaled() const override; | ||
| 50 | 49 | ||
| 51 | private: | 50 | private: |
| 52 | explicit ReadableEvent(KernelCore& kernel); | 51 | explicit ReadableEvent(KernelCore& kernel); |
| 53 | 52 | ||
| 53 | bool is_signaled{}; | ||
| 54 | std::string name; ///< Name of event (optional) | 54 | std::string name; ///< Name of event (optional) |
| 55 | }; | 55 | }; |
| 56 | 56 | ||
diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp index a549ae9d7..82857f93b 100644 --- a/src/core/hle/kernel/server_port.cpp +++ b/src/core/hle/kernel/server_port.cpp | |||
| @@ -13,7 +13,7 @@ | |||
| 13 | 13 | ||
| 14 | namespace Kernel { | 14 | namespace Kernel { |
| 15 | 15 | ||
| 16 | ServerPort::ServerPort(KernelCore& kernel) : SynchronizationObject{kernel} {} | 16 | ServerPort::ServerPort(KernelCore& kernel) : KSynchronizationObject{kernel} {} |
| 17 | ServerPort::~ServerPort() = default; | 17 | ServerPort::~ServerPort() = default; |
| 18 | 18 | ||
| 19 | ResultVal<std::shared_ptr<ServerSession>> ServerPort::Accept() { | 19 | ResultVal<std::shared_ptr<ServerSession>> ServerPort::Accept() { |
| @@ -28,15 +28,9 @@ ResultVal<std::shared_ptr<ServerSession>> ServerPort::Accept() { | |||
| 28 | 28 | ||
| 29 | void ServerPort::AppendPendingSession(std::shared_ptr<ServerSession> pending_session) { | 29 | void ServerPort::AppendPendingSession(std::shared_ptr<ServerSession> pending_session) { |
| 30 | pending_sessions.push_back(std::move(pending_session)); | 30 | pending_sessions.push_back(std::move(pending_session)); |
| 31 | } | 31 | if (pending_sessions.size() == 1) { |
| 32 | 32 | NotifyAvailable(); | |
| 33 | bool ServerPort::ShouldWait(const Thread* thread) const { | 33 | } |
| 34 | // If there are no pending sessions, we wait until a new one is added. | ||
| 35 | return pending_sessions.empty(); | ||
| 36 | } | ||
| 37 | |||
| 38 | void ServerPort::Acquire(Thread* thread) { | ||
| 39 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); | ||
| 40 | } | 34 | } |
| 41 | 35 | ||
| 42 | bool ServerPort::IsSignaled() const { | 36 | bool ServerPort::IsSignaled() const { |
diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h index 41b191b86..6470df993 100644 --- a/src/core/hle/kernel/server_port.h +++ b/src/core/hle/kernel/server_port.h | |||
| @@ -9,8 +9,8 @@ | |||
| 9 | #include <utility> | 9 | #include <utility> |
| 10 | #include <vector> | 10 | #include <vector> |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "core/hle/kernel/k_synchronization_object.h" | ||
| 12 | #include "core/hle/kernel/object.h" | 13 | #include "core/hle/kernel/object.h" |
| 13 | #include "core/hle/kernel/synchronization_object.h" | ||
| 14 | #include "core/hle/result.h" | 14 | #include "core/hle/result.h" |
| 15 | 15 | ||
| 16 | namespace Kernel { | 16 | namespace Kernel { |
| @@ -20,7 +20,7 @@ class KernelCore; | |||
| 20 | class ServerSession; | 20 | class ServerSession; |
| 21 | class SessionRequestHandler; | 21 | class SessionRequestHandler; |
| 22 | 22 | ||
| 23 | class ServerPort final : public SynchronizationObject { | 23 | class ServerPort final : public KSynchronizationObject { |
| 24 | public: | 24 | public: |
| 25 | explicit ServerPort(KernelCore& kernel); | 25 | explicit ServerPort(KernelCore& kernel); |
| 26 | ~ServerPort() override; | 26 | ~ServerPort() override; |
| @@ -79,9 +79,6 @@ public: | |||
| 79 | /// waiting to be accepted by this port. | 79 | /// waiting to be accepted by this port. |
| 80 | void AppendPendingSession(std::shared_ptr<ServerSession> pending_session); | 80 | void AppendPendingSession(std::shared_ptr<ServerSession> pending_session); |
| 81 | 81 | ||
| 82 | bool ShouldWait(const Thread* thread) const override; | ||
| 83 | void Acquire(Thread* thread) override; | ||
| 84 | |||
| 85 | bool IsSignaled() const override; | 82 | bool IsSignaled() const override; |
| 86 | 83 | ||
| 87 | private: | 84 | private: |
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index b40fe3916..4f2bb7822 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp | |||
| @@ -24,7 +24,7 @@ | |||
| 24 | 24 | ||
| 25 | namespace Kernel { | 25 | namespace Kernel { |
| 26 | 26 | ||
| 27 | ServerSession::ServerSession(KernelCore& kernel) : SynchronizationObject{kernel} {} | 27 | ServerSession::ServerSession(KernelCore& kernel) : KSynchronizationObject{kernel} {} |
| 28 | 28 | ||
| 29 | ServerSession::~ServerSession() { | 29 | ServerSession::~ServerSession() { |
| 30 | kernel.ReleaseServiceThread(service_thread); | 30 | kernel.ReleaseServiceThread(service_thread); |
| @@ -42,16 +42,6 @@ ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kern | |||
| 42 | return MakeResult(std::move(session)); | 42 | return MakeResult(std::move(session)); |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | bool ServerSession::ShouldWait(const Thread* thread) const { | ||
| 46 | // Closed sessions should never wait, an error will be returned from svcReplyAndReceive. | ||
| 47 | if (!parent->Client()) { | ||
| 48 | return false; | ||
| 49 | } | ||
| 50 | |||
| 51 | // Wait if we have no pending requests, or if we're currently handling a request. | ||
| 52 | return pending_requesting_threads.empty() || currently_handling != nullptr; | ||
| 53 | } | ||
| 54 | |||
| 55 | bool ServerSession::IsSignaled() const { | 45 | bool ServerSession::IsSignaled() const { |
| 56 | // Closed sessions should never wait, an error will be returned from svcReplyAndReceive. | 46 | // Closed sessions should never wait, an error will be returned from svcReplyAndReceive. |
| 57 | if (!parent->Client()) { | 47 | if (!parent->Client()) { |
| @@ -62,15 +52,6 @@ bool ServerSession::IsSignaled() const { | |||
| 62 | return !pending_requesting_threads.empty() && currently_handling == nullptr; | 52 | return !pending_requesting_threads.empty() && currently_handling == nullptr; |
| 63 | } | 53 | } |
| 64 | 54 | ||
| 65 | void ServerSession::Acquire(Thread* thread) { | ||
| 66 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); | ||
| 67 | // We are now handling a request, pop it from the stack. | ||
| 68 | // TODO(Subv): What happens if the client endpoint is closed before any requests are made? | ||
| 69 | ASSERT(!pending_requesting_threads.empty()); | ||
| 70 | currently_handling = pending_requesting_threads.back(); | ||
| 71 | pending_requesting_threads.pop_back(); | ||
| 72 | } | ||
| 73 | |||
| 74 | void ServerSession::ClientDisconnected() { | 55 | void ServerSession::ClientDisconnected() { |
| 75 | // We keep a shared pointer to the hle handler to keep it alive throughout | 56 | // We keep a shared pointer to the hle handler to keep it alive throughout |
| 76 | // the call to ClientDisconnected, as ClientDisconnected invalidates the | 57 | // the call to ClientDisconnected, as ClientDisconnected invalidates the |
| @@ -172,7 +153,7 @@ ResultCode ServerSession::CompleteSyncRequest(HLERequestContext& context) { | |||
| 172 | { | 153 | { |
| 173 | KScopedSchedulerLock lock(kernel); | 154 | KScopedSchedulerLock lock(kernel); |
| 174 | if (!context.IsThreadWaiting()) { | 155 | if (!context.IsThreadWaiting()) { |
| 175 | context.GetThread().ResumeFromWait(); | 156 | context.GetThread().Wakeup(); |
| 176 | context.GetThread().SetSynchronizationResults(nullptr, result); | 157 | context.GetThread().SetSynchronizationResults(nullptr, result); |
| 177 | } | 158 | } |
| 178 | } | 159 | } |
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index e8d1d99ea..9155cf7f5 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h | |||
| @@ -10,8 +10,8 @@ | |||
| 10 | #include <vector> | 10 | #include <vector> |
| 11 | 11 | ||
| 12 | #include "common/threadsafe_queue.h" | 12 | #include "common/threadsafe_queue.h" |
| 13 | #include "core/hle/kernel/k_synchronization_object.h" | ||
| 13 | #include "core/hle/kernel/service_thread.h" | 14 | #include "core/hle/kernel/service_thread.h" |
| 14 | #include "core/hle/kernel/synchronization_object.h" | ||
| 15 | #include "core/hle/result.h" | 15 | #include "core/hle/result.h" |
| 16 | 16 | ||
| 17 | namespace Core::Memory { | 17 | namespace Core::Memory { |
| @@ -43,7 +43,7 @@ class Thread; | |||
| 43 | * After the server replies to the request, the response is marshalled back to the caller's | 43 | * After the server replies to the request, the response is marshalled back to the caller's |
| 44 | * TLS buffer and control is transferred back to it. | 44 | * TLS buffer and control is transferred back to it. |
| 45 | */ | 45 | */ |
| 46 | class ServerSession final : public SynchronizationObject { | 46 | class ServerSession final : public KSynchronizationObject { |
| 47 | friend class ServiceThread; | 47 | friend class ServiceThread; |
| 48 | 48 | ||
| 49 | public: | 49 | public: |
| @@ -77,8 +77,6 @@ public: | |||
| 77 | return parent.get(); | 77 | return parent.get(); |
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | bool IsSignaled() const override; | ||
| 81 | |||
| 82 | /** | 80 | /** |
| 83 | * Sets the HLE handler for the session. This handler will be called to service IPC requests | 81 | * Sets the HLE handler for the session. This handler will be called to service IPC requests |
| 84 | * instead of the regular IPC machinery. (The regular IPC machinery is currently not | 82 | * instead of the regular IPC machinery. (The regular IPC machinery is currently not |
| @@ -100,10 +98,6 @@ public: | |||
| 100 | ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory, | 98 | ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory, |
| 101 | Core::Timing::CoreTiming& core_timing); | 99 | Core::Timing::CoreTiming& core_timing); |
| 102 | 100 | ||
| 103 | bool ShouldWait(const Thread* thread) const override; | ||
| 104 | |||
| 105 | void Acquire(Thread* thread) override; | ||
| 106 | |||
| 107 | /// Called when a client disconnection occurs. | 101 | /// Called when a client disconnection occurs. |
| 108 | void ClientDisconnected(); | 102 | void ClientDisconnected(); |
| 109 | 103 | ||
| @@ -130,6 +124,8 @@ public: | |||
| 130 | convert_to_domain = true; | 124 | convert_to_domain = true; |
| 131 | } | 125 | } |
| 132 | 126 | ||
| 127 | bool IsSignaled() const override; | ||
| 128 | |||
| 133 | private: | 129 | private: |
| 134 | /// Queues a sync request from the emulated application. | 130 | /// Queues a sync request from the emulated application. |
| 135 | ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory); | 131 | ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory); |
diff --git a/src/core/hle/kernel/session.cpp b/src/core/hle/kernel/session.cpp index e4dd53e24..75304b961 100644 --- a/src/core/hle/kernel/session.cpp +++ b/src/core/hle/kernel/session.cpp | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | 9 | ||
| 10 | namespace Kernel { | 10 | namespace Kernel { |
| 11 | 11 | ||
| 12 | Session::Session(KernelCore& kernel) : SynchronizationObject{kernel} {} | 12 | Session::Session(KernelCore& kernel) : KSynchronizationObject{kernel} {} |
| 13 | Session::~Session() = default; | 13 | Session::~Session() = default; |
| 14 | 14 | ||
| 15 | Session::SessionPair Session::Create(KernelCore& kernel, std::string name) { | 15 | Session::SessionPair Session::Create(KernelCore& kernel, std::string name) { |
| @@ -24,18 +24,9 @@ Session::SessionPair Session::Create(KernelCore& kernel, std::string name) { | |||
| 24 | return std::make_pair(std::move(client_session), std::move(server_session)); | 24 | return std::make_pair(std::move(client_session), std::move(server_session)); |
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | bool Session::ShouldWait(const Thread* thread) const { | ||
| 28 | UNIMPLEMENTED(); | ||
| 29 | return {}; | ||
| 30 | } | ||
| 31 | |||
| 32 | bool Session::IsSignaled() const { | 27 | bool Session::IsSignaled() const { |
| 33 | UNIMPLEMENTED(); | 28 | UNIMPLEMENTED(); |
| 34 | return true; | 29 | return true; |
| 35 | } | 30 | } |
| 36 | 31 | ||
| 37 | void Session::Acquire(Thread* thread) { | ||
| 38 | UNIMPLEMENTED(); | ||
| 39 | } | ||
| 40 | |||
| 41 | } // namespace Kernel | 32 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h index 7cd9c0d77..f6dd2c1d2 100644 --- a/src/core/hle/kernel/session.h +++ b/src/core/hle/kernel/session.h | |||
| @@ -8,7 +8,7 @@ | |||
| 8 | #include <string> | 8 | #include <string> |
| 9 | #include <utility> | 9 | #include <utility> |
| 10 | 10 | ||
| 11 | #include "core/hle/kernel/synchronization_object.h" | 11 | #include "core/hle/kernel/k_synchronization_object.h" |
| 12 | 12 | ||
| 13 | namespace Kernel { | 13 | namespace Kernel { |
| 14 | 14 | ||
| @@ -19,7 +19,7 @@ class ServerSession; | |||
| 19 | * Parent structure to link the client and server endpoints of a session with their associated | 19 | * Parent structure to link the client and server endpoints of a session with their associated |
| 20 | * client port. | 20 | * client port. |
| 21 | */ | 21 | */ |
| 22 | class Session final : public SynchronizationObject { | 22 | class Session final : public KSynchronizationObject { |
| 23 | public: | 23 | public: |
| 24 | explicit Session(KernelCore& kernel); | 24 | explicit Session(KernelCore& kernel); |
| 25 | ~Session() override; | 25 | ~Session() override; |
| @@ -37,12 +37,8 @@ public: | |||
| 37 | return HANDLE_TYPE; | 37 | return HANDLE_TYPE; |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | bool ShouldWait(const Thread* thread) const override; | ||
| 41 | |||
| 42 | bool IsSignaled() const override; | 40 | bool IsSignaled() const override; |
| 43 | 41 | ||
| 44 | void Acquire(Thread* thread) override; | ||
| 45 | |||
| 46 | std::shared_ptr<ClientSession> Client() { | 42 | std::shared_ptr<ClientSession> Client() { |
| 47 | if (auto result{client.lock()}) { | 43 | if (auto result{client.lock()}) { |
| 48 | return result; | 44 | return result; |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index de3ed25da..cc8b661af 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | 10 | ||
| 11 | #include "common/alignment.h" | 11 | #include "common/alignment.h" |
| 12 | #include "common/assert.h" | 12 | #include "common/assert.h" |
| 13 | #include "common/common_funcs.h" | ||
| 13 | #include "common/fiber.h" | 14 | #include "common/fiber.h" |
| 14 | #include "common/logging/log.h" | 15 | #include "common/logging/log.h" |
| 15 | #include "common/microprofile.h" | 16 | #include "common/microprofile.h" |
| @@ -19,26 +20,28 @@ | |||
| 19 | #include "core/core_timing.h" | 20 | #include "core/core_timing.h" |
| 20 | #include "core/core_timing_util.h" | 21 | #include "core/core_timing_util.h" |
| 21 | #include "core/cpu_manager.h" | 22 | #include "core/cpu_manager.h" |
| 22 | #include "core/hle/kernel/address_arbiter.h" | ||
| 23 | #include "core/hle/kernel/client_port.h" | 23 | #include "core/hle/kernel/client_port.h" |
| 24 | #include "core/hle/kernel/client_session.h" | 24 | #include "core/hle/kernel/client_session.h" |
| 25 | #include "core/hle/kernel/errors.h" | 25 | #include "core/hle/kernel/errors.h" |
| 26 | #include "core/hle/kernel/handle_table.h" | 26 | #include "core/hle/kernel/handle_table.h" |
| 27 | #include "core/hle/kernel/k_address_arbiter.h" | ||
| 28 | #include "core/hle/kernel/k_condition_variable.h" | ||
| 27 | #include "core/hle/kernel/k_scheduler.h" | 29 | #include "core/hle/kernel/k_scheduler.h" |
| 28 | #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" | 30 | #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" |
| 31 | #include "core/hle/kernel/k_synchronization_object.h" | ||
| 29 | #include "core/hle/kernel/kernel.h" | 32 | #include "core/hle/kernel/kernel.h" |
| 30 | #include "core/hle/kernel/memory/memory_block.h" | 33 | #include "core/hle/kernel/memory/memory_block.h" |
| 34 | #include "core/hle/kernel/memory/memory_layout.h" | ||
| 31 | #include "core/hle/kernel/memory/page_table.h" | 35 | #include "core/hle/kernel/memory/page_table.h" |
| 32 | #include "core/hle/kernel/mutex.h" | ||
| 33 | #include "core/hle/kernel/physical_core.h" | 36 | #include "core/hle/kernel/physical_core.h" |
| 34 | #include "core/hle/kernel/process.h" | 37 | #include "core/hle/kernel/process.h" |
| 35 | #include "core/hle/kernel/readable_event.h" | 38 | #include "core/hle/kernel/readable_event.h" |
| 36 | #include "core/hle/kernel/resource_limit.h" | 39 | #include "core/hle/kernel/resource_limit.h" |
| 37 | #include "core/hle/kernel/shared_memory.h" | 40 | #include "core/hle/kernel/shared_memory.h" |
| 38 | #include "core/hle/kernel/svc.h" | 41 | #include "core/hle/kernel/svc.h" |
| 42 | #include "core/hle/kernel/svc_results.h" | ||
| 39 | #include "core/hle/kernel/svc_types.h" | 43 | #include "core/hle/kernel/svc_types.h" |
| 40 | #include "core/hle/kernel/svc_wrap.h" | 44 | #include "core/hle/kernel/svc_wrap.h" |
| 41 | #include "core/hle/kernel/synchronization.h" | ||
| 42 | #include "core/hle/kernel/thread.h" | 45 | #include "core/hle/kernel/thread.h" |
| 43 | #include "core/hle/kernel/time_manager.h" | 46 | #include "core/hle/kernel/time_manager.h" |
| 44 | #include "core/hle/kernel/transfer_memory.h" | 47 | #include "core/hle/kernel/transfer_memory.h" |
| @@ -343,27 +346,11 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) { | |||
| 343 | auto thread = kernel.CurrentScheduler()->GetCurrentThread(); | 346 | auto thread = kernel.CurrentScheduler()->GetCurrentThread(); |
| 344 | { | 347 | { |
| 345 | KScopedSchedulerLock lock(kernel); | 348 | KScopedSchedulerLock lock(kernel); |
| 346 | thread->InvalidateHLECallback(); | 349 | thread->SetState(ThreadState::Waiting); |
| 347 | thread->SetStatus(ThreadStatus::WaitIPC); | 350 | thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC); |
| 348 | session->SendSyncRequest(SharedFrom(thread), system.Memory(), system.CoreTiming()); | 351 | session->SendSyncRequest(SharedFrom(thread), system.Memory(), system.CoreTiming()); |
| 349 | } | 352 | } |
| 350 | 353 | ||
| 351 | if (thread->HasHLECallback()) { | ||
| 352 | Handle event_handle = thread->GetHLETimeEvent(); | ||
| 353 | if (event_handle != InvalidHandle) { | ||
| 354 | auto& time_manager = kernel.TimeManager(); | ||
| 355 | time_manager.UnscheduleTimeEvent(event_handle); | ||
| 356 | } | ||
| 357 | |||
| 358 | { | ||
| 359 | KScopedSchedulerLock lock(kernel); | ||
| 360 | auto* sync_object = thread->GetHLESyncObject(); | ||
| 361 | sync_object->RemoveWaitingThread(SharedFrom(thread)); | ||
| 362 | } | ||
| 363 | |||
| 364 | thread->InvokeHLECallback(SharedFrom(thread)); | ||
| 365 | } | ||
| 366 | |||
| 367 | return thread->GetSignalingResult(); | 354 | return thread->GetSignalingResult(); |
| 368 | } | 355 | } |
| 369 | 356 | ||
| @@ -436,7 +423,7 @@ static ResultCode GetProcessId32(Core::System& system, u32* process_id_low, u32* | |||
| 436 | } | 423 | } |
| 437 | 424 | ||
| 438 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds | 425 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds |
| 439 | static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr handles_address, | 426 | static ResultCode WaitSynchronization(Core::System& system, s32* index, VAddr handles_address, |
| 440 | u64 handle_count, s64 nano_seconds) { | 427 | u64 handle_count, s64 nano_seconds) { |
| 441 | LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}", | 428 | LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}", |
| 442 | handles_address, handle_count, nano_seconds); | 429 | handles_address, handle_count, nano_seconds); |
| @@ -458,28 +445,26 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr | |||
| 458 | } | 445 | } |
| 459 | 446 | ||
| 460 | auto& kernel = system.Kernel(); | 447 | auto& kernel = system.Kernel(); |
| 461 | Thread::ThreadSynchronizationObjects objects(handle_count); | 448 | std::vector<KSynchronizationObject*> objects(handle_count); |
| 462 | const auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); | 449 | const auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); |
| 463 | 450 | ||
| 464 | for (u64 i = 0; i < handle_count; ++i) { | 451 | for (u64 i = 0; i < handle_count; ++i) { |
| 465 | const Handle handle = memory.Read32(handles_address + i * sizeof(Handle)); | 452 | const Handle handle = memory.Read32(handles_address + i * sizeof(Handle)); |
| 466 | const auto object = handle_table.Get<SynchronizationObject>(handle); | 453 | const auto object = handle_table.Get<KSynchronizationObject>(handle); |
| 467 | 454 | ||
| 468 | if (object == nullptr) { | 455 | if (object == nullptr) { |
| 469 | LOG_ERROR(Kernel_SVC, "Object is a nullptr"); | 456 | LOG_ERROR(Kernel_SVC, "Object is a nullptr"); |
| 470 | return ERR_INVALID_HANDLE; | 457 | return ERR_INVALID_HANDLE; |
| 471 | } | 458 | } |
| 472 | 459 | ||
| 473 | objects[i] = object; | 460 | objects[i] = object.get(); |
| 474 | } | 461 | } |
| 475 | auto& synchronization = kernel.Synchronization(); | 462 | return KSynchronizationObject::Wait(kernel, index, objects.data(), |
| 476 | const auto [result, handle_result] = synchronization.WaitFor(objects, nano_seconds); | 463 | static_cast<s32>(objects.size()), nano_seconds); |
| 477 | *index = handle_result; | ||
| 478 | return result; | ||
| 479 | } | 464 | } |
| 480 | 465 | ||
| 481 | static ResultCode WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address, | 466 | static ResultCode WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address, |
| 482 | s32 handle_count, u32 timeout_high, Handle* index) { | 467 | s32 handle_count, u32 timeout_high, s32* index) { |
| 483 | const s64 nano_seconds{(static_cast<s64>(timeout_high) << 32) | static_cast<s64>(timeout_low)}; | 468 | const s64 nano_seconds{(static_cast<s64>(timeout_high) << 32) | static_cast<s64>(timeout_low)}; |
| 484 | return WaitSynchronization(system, index, handles_address, handle_count, nano_seconds); | 469 | return WaitSynchronization(system, index, handles_address, handle_count, nano_seconds); |
| 485 | } | 470 | } |
| @@ -504,56 +489,37 @@ static ResultCode CancelSynchronization32(Core::System& system, Handle thread_ha | |||
| 504 | return CancelSynchronization(system, thread_handle); | 489 | return CancelSynchronization(system, thread_handle); |
| 505 | } | 490 | } |
| 506 | 491 | ||
| 507 | /// Attempts to locks a mutex, creating it if it does not already exist | 492 | /// Attempts to locks a mutex |
| 508 | static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_handle, | 493 | static ResultCode ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address, |
| 509 | VAddr mutex_addr, Handle requesting_thread_handle) { | 494 | u32 tag) { |
| 510 | LOG_TRACE(Kernel_SVC, | 495 | LOG_TRACE(Kernel_SVC, "called thread_handle=0x{:08X}, address=0x{:X}, tag=0x{:08X}", |
| 511 | "called holding_thread_handle=0x{:08X}, mutex_addr=0x{:X}, " | 496 | thread_handle, address, tag); |
| 512 | "requesting_current_thread_handle=0x{:08X}", | ||
| 513 | holding_thread_handle, mutex_addr, requesting_thread_handle); | ||
| 514 | |||
| 515 | if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) { | ||
| 516 | LOG_ERROR(Kernel_SVC, "Mutex Address is a kernel virtual address, mutex_addr={:016X}", | ||
| 517 | mutex_addr); | ||
| 518 | return ERR_INVALID_ADDRESS_STATE; | ||
| 519 | } | ||
| 520 | 497 | ||
| 521 | if (!Common::IsWordAligned(mutex_addr)) { | 498 | // Validate the input address. |
| 522 | LOG_ERROR(Kernel_SVC, "Mutex Address is not word aligned, mutex_addr={:016X}", mutex_addr); | 499 | R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory); |
| 523 | return ERR_INVALID_ADDRESS; | 500 | R_UNLESS(Common::IsAligned(address, sizeof(u32)), Svc::ResultInvalidAddress); |
| 524 | } | ||
| 525 | 501 | ||
| 526 | auto* const current_process = system.Kernel().CurrentProcess(); | 502 | return system.Kernel().CurrentProcess()->WaitForAddress(thread_handle, address, tag); |
| 527 | return current_process->GetMutex().TryAcquire(mutex_addr, holding_thread_handle, | ||
| 528 | requesting_thread_handle); | ||
| 529 | } | 503 | } |
| 530 | 504 | ||
| 531 | static ResultCode ArbitrateLock32(Core::System& system, Handle holding_thread_handle, | 505 | static ResultCode ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address, |
| 532 | u32 mutex_addr, Handle requesting_thread_handle) { | 506 | u32 tag) { |
| 533 | return ArbitrateLock(system, holding_thread_handle, mutex_addr, requesting_thread_handle); | 507 | return ArbitrateLock(system, thread_handle, address, tag); |
| 534 | } | 508 | } |
| 535 | 509 | ||
| 536 | /// Unlock a mutex | 510 | /// Unlock a mutex |
| 537 | static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) { | 511 | static ResultCode ArbitrateUnlock(Core::System& system, VAddr address) { |
| 538 | LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); | 512 | LOG_TRACE(Kernel_SVC, "called address=0x{:X}", address); |
| 539 | |||
| 540 | if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) { | ||
| 541 | LOG_ERROR(Kernel_SVC, "Mutex Address is a kernel virtual address, mutex_addr={:016X}", | ||
| 542 | mutex_addr); | ||
| 543 | return ERR_INVALID_ADDRESS_STATE; | ||
| 544 | } | ||
| 545 | 513 | ||
| 546 | if (!Common::IsWordAligned(mutex_addr)) { | 514 | // Validate the input address. |
| 547 | LOG_ERROR(Kernel_SVC, "Mutex Address is not word aligned, mutex_addr={:016X}", mutex_addr); | 515 | R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory); |
| 548 | return ERR_INVALID_ADDRESS; | 516 | R_UNLESS(Common::IsAligned(address, sizeof(u32)), Svc::ResultInvalidAddress); |
| 549 | } | ||
| 550 | 517 | ||
| 551 | auto* const current_process = system.Kernel().CurrentProcess(); | 518 | return system.Kernel().CurrentProcess()->SignalToAddress(address); |
| 552 | return current_process->GetMutex().Release(mutex_addr); | ||
| 553 | } | 519 | } |
| 554 | 520 | ||
| 555 | static ResultCode ArbitrateUnlock32(Core::System& system, u32 mutex_addr) { | 521 | static ResultCode ArbitrateUnlock32(Core::System& system, u32 address) { |
| 556 | return ArbitrateUnlock(system, mutex_addr); | 522 | return ArbitrateUnlock(system, address); |
| 557 | } | 523 | } |
| 558 | 524 | ||
| 559 | enum class BreakType : u32 { | 525 | enum class BreakType : u32 { |
| @@ -1180,7 +1146,7 @@ static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 pri | |||
| 1180 | return ERR_INVALID_HANDLE; | 1146 | return ERR_INVALID_HANDLE; |
| 1181 | } | 1147 | } |
| 1182 | 1148 | ||
| 1183 | thread->SetPriority(priority); | 1149 | thread->SetBasePriority(priority); |
| 1184 | 1150 | ||
| 1185 | return RESULT_SUCCESS; | 1151 | return RESULT_SUCCESS; |
| 1186 | } | 1152 | } |
| @@ -1559,7 +1525,7 @@ static ResultCode StartThread(Core::System& system, Handle thread_handle) { | |||
| 1559 | return ERR_INVALID_HANDLE; | 1525 | return ERR_INVALID_HANDLE; |
| 1560 | } | 1526 | } |
| 1561 | 1527 | ||
| 1562 | ASSERT(thread->GetStatus() == ThreadStatus::Dormant); | 1528 | ASSERT(thread->GetState() == ThreadState::Initialized); |
| 1563 | 1529 | ||
| 1564 | return thread->Start(); | 1530 | return thread->Start(); |
| 1565 | } | 1531 | } |
| @@ -1620,224 +1586,135 @@ static void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanosec | |||
| 1620 | } | 1586 | } |
| 1621 | 1587 | ||
| 1622 | /// Wait process wide key atomic | 1588 | /// Wait process wide key atomic |
| 1623 | static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_addr, | 1589 | static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key, |
| 1624 | VAddr condition_variable_addr, Handle thread_handle, | 1590 | u32 tag, s64 timeout_ns) { |
| 1625 | s64 nano_seconds) { | 1591 | LOG_TRACE(Kernel_SVC, "called address={:X}, cv_key={:X}, tag=0x{:08X}, timeout_ns={}", address, |
| 1626 | LOG_TRACE( | 1592 | cv_key, tag, timeout_ns); |
| 1627 | Kernel_SVC, | 1593 | |
| 1628 | "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", | 1594 | // Validate input. |
| 1629 | mutex_addr, condition_variable_addr, thread_handle, nano_seconds); | 1595 | R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory); |
| 1630 | 1596 | R_UNLESS(Common::IsAligned(address, sizeof(int32_t)), Svc::ResultInvalidAddress); | |
| 1631 | if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) { | 1597 | |
| 1632 | LOG_ERROR( | 1598 | // Convert timeout from nanoseconds to ticks. |
| 1633 | Kernel_SVC, | 1599 | s64 timeout{}; |
| 1634 | "Given mutex address must not be within the kernel address space. address=0x{:016X}", | 1600 | if (timeout_ns > 0) { |
| 1635 | mutex_addr); | 1601 | const s64 offset_tick(timeout_ns); |
| 1636 | return ERR_INVALID_ADDRESS_STATE; | 1602 | if (offset_tick > 0) { |
| 1637 | } | 1603 | timeout = offset_tick + 2; |
| 1638 | 1604 | if (timeout <= 0) { | |
| 1639 | if (!Common::IsWordAligned(mutex_addr)) { | 1605 | timeout = std::numeric_limits<s64>::max(); |
| 1640 | LOG_ERROR(Kernel_SVC, "Given mutex address must be word-aligned. address=0x{:016X}", | 1606 | } |
| 1641 | mutex_addr); | 1607 | } else { |
| 1642 | return ERR_INVALID_ADDRESS; | 1608 | timeout = std::numeric_limits<s64>::max(); |
| 1643 | } | ||
| 1644 | |||
| 1645 | ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4)); | ||
| 1646 | auto& kernel = system.Kernel(); | ||
| 1647 | Handle event_handle; | ||
| 1648 | Thread* current_thread = kernel.CurrentScheduler()->GetCurrentThread(); | ||
| 1649 | auto* const current_process = kernel.CurrentProcess(); | ||
| 1650 | { | ||
| 1651 | KScopedSchedulerLockAndSleep lock(kernel, event_handle, current_thread, nano_seconds); | ||
| 1652 | const auto& handle_table = current_process->GetHandleTable(); | ||
| 1653 | std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle); | ||
| 1654 | ASSERT(thread); | ||
| 1655 | |||
| 1656 | current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); | ||
| 1657 | |||
| 1658 | if (thread->IsPendingTermination()) { | ||
| 1659 | lock.CancelSleep(); | ||
| 1660 | return ERR_THREAD_TERMINATING; | ||
| 1661 | } | ||
| 1662 | |||
| 1663 | const auto release_result = current_process->GetMutex().Release(mutex_addr); | ||
| 1664 | if (release_result.IsError()) { | ||
| 1665 | lock.CancelSleep(); | ||
| 1666 | return release_result; | ||
| 1667 | } | ||
| 1668 | |||
| 1669 | if (nano_seconds == 0) { | ||
| 1670 | lock.CancelSleep(); | ||
| 1671 | return RESULT_TIMEOUT; | ||
| 1672 | } | ||
| 1673 | |||
| 1674 | current_thread->SetCondVarWaitAddress(condition_variable_addr); | ||
| 1675 | current_thread->SetMutexWaitAddress(mutex_addr); | ||
| 1676 | current_thread->SetWaitHandle(thread_handle); | ||
| 1677 | current_thread->SetStatus(ThreadStatus::WaitCondVar); | ||
| 1678 | current_process->InsertConditionVariableThread(SharedFrom(current_thread)); | ||
| 1679 | } | ||
| 1680 | |||
| 1681 | if (event_handle != InvalidHandle) { | ||
| 1682 | auto& time_manager = kernel.TimeManager(); | ||
| 1683 | time_manager.UnscheduleTimeEvent(event_handle); | ||
| 1684 | } | ||
| 1685 | |||
| 1686 | { | ||
| 1687 | KScopedSchedulerLock lock(kernel); | ||
| 1688 | |||
| 1689 | auto* owner = current_thread->GetLockOwner(); | ||
| 1690 | if (owner != nullptr) { | ||
| 1691 | owner->RemoveMutexWaiter(SharedFrom(current_thread)); | ||
| 1692 | } | 1609 | } |
| 1693 | 1610 | } else { | |
| 1694 | current_process->RemoveConditionVariableThread(SharedFrom(current_thread)); | 1611 | timeout = timeout_ns; |
| 1695 | } | 1612 | } |
| 1696 | // Note: Deliberately don't attempt to inherit the lock owner's priority. | ||
| 1697 | 1613 | ||
| 1698 | return current_thread->GetSignalingResult(); | 1614 | // Wait on the condition variable. |
| 1615 | return system.Kernel().CurrentProcess()->WaitConditionVariable( | ||
| 1616 | address, Common::AlignDown(cv_key, sizeof(u32)), tag, timeout); | ||
| 1699 | } | 1617 | } |
| 1700 | 1618 | ||
| 1701 | static ResultCode WaitProcessWideKeyAtomic32(Core::System& system, u32 mutex_addr, | 1619 | static ResultCode WaitProcessWideKeyAtomic32(Core::System& system, u32 address, u32 cv_key, u32 tag, |
| 1702 | u32 condition_variable_addr, Handle thread_handle, | 1620 | u32 timeout_ns_low, u32 timeout_ns_high) { |
| 1703 | u32 nanoseconds_low, u32 nanoseconds_high) { | 1621 | const auto timeout_ns = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32)); |
| 1704 | const auto nanoseconds = static_cast<s64>(nanoseconds_low | (u64{nanoseconds_high} << 32)); | 1622 | return WaitProcessWideKeyAtomic(system, address, cv_key, tag, timeout_ns); |
| 1705 | return WaitProcessWideKeyAtomic(system, mutex_addr, condition_variable_addr, thread_handle, | ||
| 1706 | nanoseconds); | ||
| 1707 | } | 1623 | } |
| 1708 | 1624 | ||
| 1709 | /// Signal process wide key | 1625 | /// Signal process wide key |
| 1710 | static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_addr, s32 target) { | 1626 | static void SignalProcessWideKey(Core::System& system, VAddr cv_key, s32 count) { |
| 1711 | LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}", | 1627 | LOG_TRACE(Kernel_SVC, "called, cv_key=0x{:X}, count=0x{:08X}", cv_key, count); |
| 1712 | condition_variable_addr, target); | ||
| 1713 | 1628 | ||
| 1714 | ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4)); | 1629 | // Signal the condition variable. |
| 1630 | return system.Kernel().CurrentProcess()->SignalConditionVariable( | ||
| 1631 | Common::AlignDown(cv_key, sizeof(u32)), count); | ||
| 1632 | } | ||
| 1715 | 1633 | ||
| 1716 | // Retrieve a list of all threads that are waiting for this condition variable. | 1634 | static void SignalProcessWideKey32(Core::System& system, u32 cv_key, s32 count) { |
| 1717 | auto& kernel = system.Kernel(); | 1635 | SignalProcessWideKey(system, cv_key, count); |
| 1718 | KScopedSchedulerLock lock(kernel); | 1636 | } |
| 1719 | auto* const current_process = kernel.CurrentProcess(); | ||
| 1720 | std::vector<std::shared_ptr<Thread>> waiting_threads = | ||
| 1721 | current_process->GetConditionVariableThreads(condition_variable_addr); | ||
| 1722 | |||
| 1723 | // Only process up to 'target' threads, unless 'target' is less equal 0, in which case process | ||
| 1724 | // them all. | ||
| 1725 | std::size_t last = waiting_threads.size(); | ||
| 1726 | if (target > 0) { | ||
| 1727 | last = std::min(waiting_threads.size(), static_cast<std::size_t>(target)); | ||
| 1728 | } | ||
| 1729 | for (std::size_t index = 0; index < last; ++index) { | ||
| 1730 | auto& thread = waiting_threads[index]; | ||
| 1731 | |||
| 1732 | ASSERT(thread->GetCondVarWaitAddress() == condition_variable_addr); | ||
| 1733 | |||
| 1734 | // liberate Cond Var Thread. | ||
| 1735 | current_process->RemoveConditionVariableThread(thread); | ||
| 1736 | |||
| 1737 | const std::size_t current_core = system.CurrentCoreIndex(); | ||
| 1738 | auto& monitor = system.Monitor(); | ||
| 1739 | |||
| 1740 | // Atomically read the value of the mutex. | ||
| 1741 | u32 mutex_val = 0; | ||
| 1742 | u32 update_val = 0; | ||
| 1743 | const VAddr mutex_address = thread->GetMutexWaitAddress(); | ||
| 1744 | do { | ||
| 1745 | // If the mutex is not yet acquired, acquire it. | ||
| 1746 | mutex_val = monitor.ExclusiveRead32(current_core, mutex_address); | ||
| 1747 | |||
| 1748 | if (mutex_val != 0) { | ||
| 1749 | update_val = mutex_val | Mutex::MutexHasWaitersFlag; | ||
| 1750 | } else { | ||
| 1751 | update_val = thread->GetWaitHandle(); | ||
| 1752 | } | ||
| 1753 | } while (!monitor.ExclusiveWrite32(current_core, mutex_address, update_val)); | ||
| 1754 | monitor.ClearExclusive(); | ||
| 1755 | if (mutex_val == 0) { | ||
| 1756 | // We were able to acquire the mutex, resume this thread. | ||
| 1757 | auto* const lock_owner = thread->GetLockOwner(); | ||
| 1758 | if (lock_owner != nullptr) { | ||
| 1759 | lock_owner->RemoveMutexWaiter(thread); | ||
| 1760 | } | ||
| 1761 | 1637 | ||
| 1762 | thread->SetLockOwner(nullptr); | 1638 | namespace { |
| 1763 | thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS); | ||
| 1764 | thread->ResumeFromWait(); | ||
| 1765 | } else { | ||
| 1766 | // The mutex is already owned by some other thread, make this thread wait on it. | ||
| 1767 | const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); | ||
| 1768 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | ||
| 1769 | auto owner = handle_table.Get<Thread>(owner_handle); | ||
| 1770 | ASSERT(owner); | ||
| 1771 | if (thread->GetStatus() == ThreadStatus::WaitCondVar) { | ||
| 1772 | thread->SetStatus(ThreadStatus::WaitMutex); | ||
| 1773 | } | ||
| 1774 | 1639 | ||
| 1775 | owner->AddMutexWaiter(thread); | 1640 | constexpr bool IsValidSignalType(Svc::SignalType type) { |
| 1776 | } | 1641 | switch (type) { |
| 1642 | case Svc::SignalType::Signal: | ||
| 1643 | case Svc::SignalType::SignalAndIncrementIfEqual: | ||
| 1644 | case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual: | ||
| 1645 | return true; | ||
| 1646 | default: | ||
| 1647 | return false; | ||
| 1777 | } | 1648 | } |
| 1778 | } | 1649 | } |
| 1779 | 1650 | ||
| 1780 | static void SignalProcessWideKey32(Core::System& system, u32 condition_variable_addr, s32 target) { | 1651 | constexpr bool IsValidArbitrationType(Svc::ArbitrationType type) { |
| 1781 | SignalProcessWideKey(system, condition_variable_addr, target); | 1652 | switch (type) { |
| 1653 | case Svc::ArbitrationType::WaitIfLessThan: | ||
| 1654 | case Svc::ArbitrationType::DecrementAndWaitIfLessThan: | ||
| 1655 | case Svc::ArbitrationType::WaitIfEqual: | ||
| 1656 | return true; | ||
| 1657 | default: | ||
| 1658 | return false; | ||
| 1659 | } | ||
| 1782 | } | 1660 | } |
| 1783 | 1661 | ||
| 1784 | // Wait for an address (via Address Arbiter) | 1662 | } // namespace |
| 1785 | static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, s32 value, | ||
| 1786 | s64 timeout) { | ||
| 1787 | LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}", address, | ||
| 1788 | type, value, timeout); | ||
| 1789 | |||
| 1790 | // If the passed address is a kernel virtual address, return invalid memory state. | ||
| 1791 | if (Core::Memory::IsKernelVirtualAddress(address)) { | ||
| 1792 | LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address); | ||
| 1793 | return ERR_INVALID_ADDRESS_STATE; | ||
| 1794 | } | ||
| 1795 | 1663 | ||
| 1796 | // If the address is not properly aligned to 4 bytes, return invalid address. | 1664 | // Wait for an address (via Address Arbiter) |
| 1797 | if (!Common::IsWordAligned(address)) { | 1665 | static ResultCode WaitForAddress(Core::System& system, VAddr address, Svc::ArbitrationType arb_type, |
| 1798 | LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address); | 1666 | s32 value, s64 timeout_ns) { |
| 1799 | return ERR_INVALID_ADDRESS; | 1667 | LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, arb_type=0x{:X}, value=0x{:X}, timeout_ns={}", |
| 1668 | address, arb_type, value, timeout_ns); | ||
| 1669 | |||
| 1670 | // Validate input. | ||
| 1671 | R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory); | ||
| 1672 | R_UNLESS(Common::IsAligned(address, sizeof(int32_t)), Svc::ResultInvalidAddress); | ||
| 1673 | R_UNLESS(IsValidArbitrationType(arb_type), Svc::ResultInvalidEnumValue); | ||
| 1674 | |||
| 1675 | // Convert timeout from nanoseconds to ticks. | ||
| 1676 | s64 timeout{}; | ||
| 1677 | if (timeout_ns > 0) { | ||
| 1678 | const s64 offset_tick(timeout_ns); | ||
| 1679 | if (offset_tick > 0) { | ||
| 1680 | timeout = offset_tick + 2; | ||
| 1681 | if (timeout <= 0) { | ||
| 1682 | timeout = std::numeric_limits<s64>::max(); | ||
| 1683 | } | ||
| 1684 | } else { | ||
| 1685 | timeout = std::numeric_limits<s64>::max(); | ||
| 1686 | } | ||
| 1687 | } else { | ||
| 1688 | timeout = timeout_ns; | ||
| 1800 | } | 1689 | } |
| 1801 | 1690 | ||
| 1802 | const auto arbitration_type = static_cast<AddressArbiter::ArbitrationType>(type); | 1691 | return system.Kernel().CurrentProcess()->WaitAddressArbiter(address, arb_type, value, timeout); |
| 1803 | auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter(); | ||
| 1804 | const ResultCode result = | ||
| 1805 | address_arbiter.WaitForAddress(address, arbitration_type, value, timeout); | ||
| 1806 | return result; | ||
| 1807 | } | 1692 | } |
| 1808 | 1693 | ||
| 1809 | static ResultCode WaitForAddress32(Core::System& system, u32 address, u32 type, s32 value, | 1694 | static ResultCode WaitForAddress32(Core::System& system, u32 address, Svc::ArbitrationType arb_type, |
| 1810 | u32 timeout_low, u32 timeout_high) { | 1695 | s32 value, u32 timeout_ns_low, u32 timeout_ns_high) { |
| 1811 | const auto timeout = static_cast<s64>(timeout_low | (u64{timeout_high} << 32)); | 1696 | const auto timeout = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32)); |
| 1812 | return WaitForAddress(system, address, type, value, timeout); | 1697 | return WaitForAddress(system, address, arb_type, value, timeout); |
| 1813 | } | 1698 | } |
| 1814 | 1699 | ||
| 1815 | // Signals to an address (via Address Arbiter) | 1700 | // Signals to an address (via Address Arbiter) |
| 1816 | static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, s32 value, | 1701 | static ResultCode SignalToAddress(Core::System& system, VAddr address, Svc::SignalType signal_type, |
| 1817 | s32 num_to_wake) { | 1702 | s32 value, s32 count) { |
| 1818 | LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}", | 1703 | LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, signal_type=0x{:X}, value=0x{:X}, count=0x{:X}", |
| 1819 | address, type, value, num_to_wake); | 1704 | address, signal_type, value, count); |
| 1820 | |||
| 1821 | // If the passed address is a kernel virtual address, return invalid memory state. | ||
| 1822 | if (Core::Memory::IsKernelVirtualAddress(address)) { | ||
| 1823 | LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address); | ||
| 1824 | return ERR_INVALID_ADDRESS_STATE; | ||
| 1825 | } | ||
| 1826 | 1705 | ||
| 1827 | // If the address is not properly aligned to 4 bytes, return invalid address. | 1706 | // Validate input. |
| 1828 | if (!Common::IsWordAligned(address)) { | 1707 | R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory); |
| 1829 | LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address); | 1708 | R_UNLESS(Common::IsAligned(address, sizeof(s32)), Svc::ResultInvalidAddress); |
| 1830 | return ERR_INVALID_ADDRESS; | 1709 | R_UNLESS(IsValidSignalType(signal_type), Svc::ResultInvalidEnumValue); |
| 1831 | } | ||
| 1832 | 1710 | ||
| 1833 | const auto signal_type = static_cast<AddressArbiter::SignalType>(type); | 1711 | return system.Kernel().CurrentProcess()->SignalAddressArbiter(address, signal_type, value, |
| 1834 | auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter(); | 1712 | count); |
| 1835 | return address_arbiter.SignalToAddress(address, signal_type, value, num_to_wake); | ||
| 1836 | } | 1713 | } |
| 1837 | 1714 | ||
| 1838 | static ResultCode SignalToAddress32(Core::System& system, u32 address, u32 type, s32 value, | 1715 | static ResultCode SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type, |
| 1839 | s32 num_to_wake) { | 1716 | s32 value, s32 count) { |
| 1840 | return SignalToAddress(system, address, type, value, num_to_wake); | 1717 | return SignalToAddress(system, address, signal_type, value, count); |
| 1841 | } | 1718 | } |
| 1842 | 1719 | ||
| 1843 | static void KernelDebug([[maybe_unused]] Core::System& system, | 1720 | static void KernelDebug([[maybe_unused]] Core::System& system, |
diff --git a/src/core/hle/kernel/svc_common.h b/src/core/hle/kernel/svc_common.h new file mode 100644 index 000000000..4af049551 --- /dev/null +++ b/src/core/hle/kernel/svc_common.h | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | namespace Kernel::Svc { | ||
| 10 | |||
| 11 | constexpr s32 ArgumentHandleCountMax = 0x40; | ||
| 12 | constexpr u32 HandleWaitMask{1u << 30}; | ||
| 13 | |||
| 14 | } // namespace Kernel::Svc | ||
diff --git a/src/core/hle/kernel/svc_results.h b/src/core/hle/kernel/svc_results.h new file mode 100644 index 000000000..78282f021 --- /dev/null +++ b/src/core/hle/kernel/svc_results.h | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/result.h" | ||
| 8 | |||
| 9 | namespace Kernel::Svc { | ||
| 10 | |||
| 11 | constexpr ResultCode ResultTerminationRequested{ErrorModule::Kernel, 59}; | ||
| 12 | constexpr ResultCode ResultInvalidAddress{ErrorModule::Kernel, 102}; | ||
| 13 | constexpr ResultCode ResultInvalidCurrentMemory{ErrorModule::Kernel, 106}; | ||
| 14 | constexpr ResultCode ResultInvalidHandle{ErrorModule::Kernel, 114}; | ||
| 15 | constexpr ResultCode ResultTimedOut{ErrorModule::Kernel, 117}; | ||
| 16 | constexpr ResultCode ResultCancelled{ErrorModule::Kernel, 118}; | ||
| 17 | constexpr ResultCode ResultInvalidEnumValue{ErrorModule::Kernel, 120}; | ||
| 18 | constexpr ResultCode ResultInvalidState{ErrorModule::Kernel, 125}; | ||
| 19 | |||
| 20 | } // namespace Kernel::Svc | ||
diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h index 986724beb..d623f7a50 100644 --- a/src/core/hle/kernel/svc_types.h +++ b/src/core/hle/kernel/svc_types.h | |||
| @@ -23,8 +23,8 @@ enum class MemoryState : u32 { | |||
| 23 | Ipc = 0x0A, | 23 | Ipc = 0x0A, |
| 24 | Stack = 0x0B, | 24 | Stack = 0x0B, |
| 25 | ThreadLocal = 0x0C, | 25 | ThreadLocal = 0x0C, |
| 26 | Transfered = 0x0D, | 26 | Transferred = 0x0D, |
| 27 | SharedTransfered = 0x0E, | 27 | SharedTransferred = 0x0E, |
| 28 | SharedCode = 0x0F, | 28 | SharedCode = 0x0F, |
| 29 | Inaccessible = 0x10, | 29 | Inaccessible = 0x10, |
| 30 | NonSecureIpc = 0x11, | 30 | NonSecureIpc = 0x11, |
| @@ -65,4 +65,16 @@ struct MemoryInfo { | |||
| 65 | u32 padding{}; | 65 | u32 padding{}; |
| 66 | }; | 66 | }; |
| 67 | 67 | ||
| 68 | enum class SignalType : u32 { | ||
| 69 | Signal = 0, | ||
| 70 | SignalAndIncrementIfEqual = 1, | ||
| 71 | SignalAndModifyByWaitingCountIfEqual = 2, | ||
| 72 | }; | ||
| 73 | |||
| 74 | enum class ArbitrationType : u32 { | ||
| 75 | WaitIfLessThan = 0, | ||
| 76 | DecrementAndWaitIfLessThan = 1, | ||
| 77 | WaitIfEqual = 2, | ||
| 78 | }; | ||
| 79 | |||
| 68 | } // namespace Kernel::Svc | 80 | } // namespace Kernel::Svc |
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 0b6dd9df0..a32750ed7 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "core/arm/arm_interface.h" | 8 | #include "core/arm/arm_interface.h" |
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "core/hle/kernel/svc_types.h" | ||
| 10 | #include "core/hle/result.h" | 11 | #include "core/hle/result.h" |
| 11 | 12 | ||
| 12 | namespace Kernel { | 13 | namespace Kernel { |
| @@ -215,9 +216,10 @@ void SvcWrap64(Core::System& system) { | |||
| 215 | func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)).raw); | 216 | func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)).raw); |
| 216 | } | 217 | } |
| 217 | 218 | ||
| 218 | template <ResultCode func(Core::System&, u32*, u64, u64, s64)> | 219 | // Used by WaitSynchronization |
| 220 | template <ResultCode func(Core::System&, s32*, u64, u64, s64)> | ||
| 219 | void SvcWrap64(Core::System& system) { | 221 | void SvcWrap64(Core::System& system) { |
| 220 | u32 param_1 = 0; | 222 | s32 param_1 = 0; |
| 221 | const u32 retval = func(system, ¶m_1, Param(system, 1), static_cast<u32>(Param(system, 2)), | 223 | const u32 retval = func(system, ¶m_1, Param(system, 1), static_cast<u32>(Param(system, 2)), |
| 222 | static_cast<s64>(Param(system, 3))) | 224 | static_cast<s64>(Param(system, 3))) |
| 223 | .raw; | 225 | .raw; |
| @@ -276,18 +278,22 @@ void SvcWrap64(Core::System& system) { | |||
| 276 | FuncReturn(system, retval); | 278 | FuncReturn(system, retval); |
| 277 | } | 279 | } |
| 278 | 280 | ||
| 279 | template <ResultCode func(Core::System&, u64, u32, s32, s64)> | 281 | // Used by WaitForAddress |
| 282 | template <ResultCode func(Core::System&, u64, Svc::ArbitrationType, s32, s64)> | ||
| 280 | void SvcWrap64(Core::System& system) { | 283 | void SvcWrap64(Core::System& system) { |
| 281 | FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)), | 284 | FuncReturn(system, |
| 282 | static_cast<s32>(Param(system, 2)), static_cast<s64>(Param(system, 3))) | 285 | func(system, Param(system, 0), static_cast<Svc::ArbitrationType>(Param(system, 1)), |
| 283 | .raw); | 286 | static_cast<s32>(Param(system, 2)), static_cast<s64>(Param(system, 3))) |
| 287 | .raw); | ||
| 284 | } | 288 | } |
| 285 | 289 | ||
| 286 | template <ResultCode func(Core::System&, u64, u32, s32, s32)> | 290 | // Used by SignalToAddress |
| 291 | template <ResultCode func(Core::System&, u64, Svc::SignalType, s32, s32)> | ||
| 287 | void SvcWrap64(Core::System& system) { | 292 | void SvcWrap64(Core::System& system) { |
| 288 | FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)), | 293 | FuncReturn(system, |
| 289 | static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3))) | 294 | func(system, Param(system, 0), static_cast<Svc::SignalType>(Param(system, 1)), |
| 290 | .raw); | 295 | static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3))) |
| 296 | .raw); | ||
| 291 | } | 297 | } |
| 292 | 298 | ||
| 293 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 299 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -503,22 +509,23 @@ void SvcWrap32(Core::System& system) { | |||
| 503 | } | 509 | } |
| 504 | 510 | ||
| 505 | // Used by WaitForAddress32 | 511 | // Used by WaitForAddress32 |
| 506 | template <ResultCode func(Core::System&, u32, u32, s32, u32, u32)> | 512 | template <ResultCode func(Core::System&, u32, Svc::ArbitrationType, s32, u32, u32)> |
| 507 | void SvcWrap32(Core::System& system) { | 513 | void SvcWrap32(Core::System& system) { |
| 508 | const u32 retval = func(system, static_cast<u32>(Param(system, 0)), | 514 | const u32 retval = func(system, static_cast<u32>(Param(system, 0)), |
| 509 | static_cast<u32>(Param(system, 1)), static_cast<s32>(Param(system, 2)), | 515 | static_cast<Svc::ArbitrationType>(Param(system, 1)), |
| 510 | static_cast<u32>(Param(system, 3)), static_cast<u32>(Param(system, 4))) | 516 | static_cast<s32>(Param(system, 2)), static_cast<u32>(Param(system, 3)), |
| 517 | static_cast<u32>(Param(system, 4))) | ||
| 511 | .raw; | 518 | .raw; |
| 512 | FuncReturn(system, retval); | 519 | FuncReturn(system, retval); |
| 513 | } | 520 | } |
| 514 | 521 | ||
| 515 | // Used by SignalToAddress32 | 522 | // Used by SignalToAddress32 |
| 516 | template <ResultCode func(Core::System&, u32, u32, s32, s32)> | 523 | template <ResultCode func(Core::System&, u32, Svc::SignalType, s32, s32)> |
| 517 | void SvcWrap32(Core::System& system) { | 524 | void SvcWrap32(Core::System& system) { |
| 518 | const u32 retval = | 525 | const u32 retval = func(system, static_cast<u32>(Param(system, 0)), |
| 519 | func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1)), | 526 | static_cast<Svc::SignalType>(Param(system, 1)), |
| 520 | static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3))) | 527 | static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3))) |
| 521 | .raw; | 528 | .raw; |
| 522 | FuncReturn(system, retval); | 529 | FuncReturn(system, retval); |
| 523 | } | 530 | } |
| 524 | 531 | ||
| @@ -539,9 +546,9 @@ void SvcWrap32(Core::System& system) { | |||
| 539 | } | 546 | } |
| 540 | 547 | ||
| 541 | // Used by WaitSynchronization32 | 548 | // Used by WaitSynchronization32 |
| 542 | template <ResultCode func(Core::System&, u32, u32, s32, u32, Handle*)> | 549 | template <ResultCode func(Core::System&, u32, u32, s32, u32, s32*)> |
| 543 | void SvcWrap32(Core::System& system) { | 550 | void SvcWrap32(Core::System& system) { |
| 544 | u32 param_1 = 0; | 551 | s32 param_1 = 0; |
| 545 | const u32 retval = func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2), | 552 | const u32 retval = func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2), |
| 546 | Param32(system, 3), ¶m_1) | 553 | Param32(system, 3), ¶m_1) |
| 547 | .raw; | 554 | .raw; |
diff --git a/src/core/hle/kernel/synchronization.cpp b/src/core/hle/kernel/synchronization.cpp deleted file mode 100644 index d3f520ea2..000000000 --- a/src/core/hle/kernel/synchronization.cpp +++ /dev/null | |||
| @@ -1,116 +0,0 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/core.h" | ||
| 6 | #include "core/hle/kernel/errors.h" | ||
| 7 | #include "core/hle/kernel/handle_table.h" | ||
| 8 | #include "core/hle/kernel/k_scheduler.h" | ||
| 9 | #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" | ||
| 10 | #include "core/hle/kernel/kernel.h" | ||
| 11 | #include "core/hle/kernel/synchronization.h" | ||
| 12 | #include "core/hle/kernel/synchronization_object.h" | ||
| 13 | #include "core/hle/kernel/thread.h" | ||
| 14 | #include "core/hle/kernel/time_manager.h" | ||
| 15 | |||
| 16 | namespace Kernel { | ||
| 17 | |||
| 18 | Synchronization::Synchronization(Core::System& system) : system{system} {} | ||
| 19 | |||
| 20 | void Synchronization::SignalObject(SynchronizationObject& obj) const { | ||
| 21 | auto& kernel = system.Kernel(); | ||
| 22 | KScopedSchedulerLock lock(kernel); | ||
| 23 | if (obj.IsSignaled()) { | ||
| 24 | for (auto thread : obj.GetWaitingThreads()) { | ||
| 25 | if (thread->GetSchedulingStatus() == ThreadSchedStatus::Paused) { | ||
| 26 | if (thread->GetStatus() != ThreadStatus::WaitHLEEvent) { | ||
| 27 | ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch); | ||
| 28 | ASSERT(thread->IsWaitingSync()); | ||
| 29 | } | ||
| 30 | thread->SetSynchronizationResults(&obj, RESULT_SUCCESS); | ||
| 31 | thread->ResumeFromWait(); | ||
| 32 | } | ||
| 33 | } | ||
| 34 | obj.ClearWaitingThreads(); | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | std::pair<ResultCode, Handle> Synchronization::WaitFor( | ||
| 39 | std::vector<std::shared_ptr<SynchronizationObject>>& sync_objects, s64 nano_seconds) { | ||
| 40 | auto& kernel = system.Kernel(); | ||
| 41 | auto* const thread = kernel.CurrentScheduler()->GetCurrentThread(); | ||
| 42 | Handle event_handle = InvalidHandle; | ||
| 43 | { | ||
| 44 | KScopedSchedulerLockAndSleep lock(kernel, event_handle, thread, nano_seconds); | ||
| 45 | const auto itr = | ||
| 46 | std::find_if(sync_objects.begin(), sync_objects.end(), | ||
| 47 | [thread](const std::shared_ptr<SynchronizationObject>& object) { | ||
| 48 | return object->IsSignaled(); | ||
| 49 | }); | ||
| 50 | |||
| 51 | if (itr != sync_objects.end()) { | ||
| 52 | // We found a ready object, acquire it and set the result value | ||
| 53 | SynchronizationObject* object = itr->get(); | ||
| 54 | object->Acquire(thread); | ||
| 55 | const u32 index = static_cast<s32>(std::distance(sync_objects.begin(), itr)); | ||
| 56 | lock.CancelSleep(); | ||
| 57 | return {RESULT_SUCCESS, index}; | ||
| 58 | } | ||
| 59 | |||
| 60 | if (nano_seconds == 0) { | ||
| 61 | lock.CancelSleep(); | ||
| 62 | return {RESULT_TIMEOUT, InvalidHandle}; | ||
| 63 | } | ||
| 64 | |||
| 65 | if (thread->IsPendingTermination()) { | ||
| 66 | lock.CancelSleep(); | ||
| 67 | return {ERR_THREAD_TERMINATING, InvalidHandle}; | ||
| 68 | } | ||
| 69 | |||
| 70 | if (thread->IsSyncCancelled()) { | ||
| 71 | thread->SetSyncCancelled(false); | ||
| 72 | lock.CancelSleep(); | ||
| 73 | return {ERR_SYNCHRONIZATION_CANCELED, InvalidHandle}; | ||
| 74 | } | ||
| 75 | |||
| 76 | for (auto& object : sync_objects) { | ||
| 77 | object->AddWaitingThread(SharedFrom(thread)); | ||
| 78 | } | ||
| 79 | |||
| 80 | thread->SetSynchronizationObjects(&sync_objects); | ||
| 81 | thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); | ||
| 82 | thread->SetStatus(ThreadStatus::WaitSynch); | ||
| 83 | thread->SetWaitingSync(true); | ||
| 84 | } | ||
| 85 | thread->SetWaitingSync(false); | ||
| 86 | |||
| 87 | if (event_handle != InvalidHandle) { | ||
| 88 | auto& time_manager = kernel.TimeManager(); | ||
| 89 | time_manager.UnscheduleTimeEvent(event_handle); | ||
| 90 | } | ||
| 91 | |||
| 92 | { | ||
| 93 | KScopedSchedulerLock lock(kernel); | ||
| 94 | ResultCode signaling_result = thread->GetSignalingResult(); | ||
| 95 | SynchronizationObject* signaling_object = thread->GetSignalingObject(); | ||
| 96 | thread->SetSynchronizationObjects(nullptr); | ||
| 97 | auto shared_thread = SharedFrom(thread); | ||
| 98 | for (auto& obj : sync_objects) { | ||
| 99 | obj->RemoveWaitingThread(shared_thread); | ||
| 100 | } | ||
| 101 | if (signaling_object != nullptr) { | ||
| 102 | const auto itr = std::find_if( | ||
| 103 | sync_objects.begin(), sync_objects.end(), | ||
| 104 | [signaling_object](const std::shared_ptr<SynchronizationObject>& object) { | ||
| 105 | return object.get() == signaling_object; | ||
| 106 | }); | ||
| 107 | ASSERT(itr != sync_objects.end()); | ||
| 108 | signaling_object->Acquire(thread); | ||
| 109 | const u32 index = static_cast<s32>(std::distance(sync_objects.begin(), itr)); | ||
| 110 | return {signaling_result, index}; | ||
| 111 | } | ||
| 112 | return {signaling_result, -1}; | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/synchronization.h b/src/core/hle/kernel/synchronization.h deleted file mode 100644 index 379f4b1d3..000000000 --- a/src/core/hle/kernel/synchronization.h +++ /dev/null | |||
| @@ -1,44 +0,0 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <utility> | ||
| 9 | #include <vector> | ||
| 10 | |||
| 11 | #include "core/hle/kernel/object.h" | ||
| 12 | #include "core/hle/result.h" | ||
| 13 | |||
| 14 | namespace Core { | ||
| 15 | class System; | ||
| 16 | } // namespace Core | ||
| 17 | |||
| 18 | namespace Kernel { | ||
| 19 | |||
| 20 | class SynchronizationObject; | ||
| 21 | |||
| 22 | /** | ||
| 23 | * The 'Synchronization' class is an interface for handling synchronization methods | ||
| 24 | * used by Synchronization objects and synchronization SVCs. This centralizes processing of | ||
| 25 | * such | ||
| 26 | */ | ||
| 27 | class Synchronization { | ||
| 28 | public: | ||
| 29 | explicit Synchronization(Core::System& system); | ||
| 30 | |||
| 31 | /// Signals a synchronization object, waking up all its waiting threads | ||
| 32 | void SignalObject(SynchronizationObject& obj) const; | ||
| 33 | |||
| 34 | /// Tries to see if waiting for any of the sync_objects is necessary, if not | ||
| 35 | /// it returns Success and the handle index of the signaled sync object. In | ||
| 36 | /// case not, the current thread will be locked and wait for nano_seconds or | ||
| 37 | /// for a synchronization object to signal. | ||
| 38 | std::pair<ResultCode, Handle> WaitFor( | ||
| 39 | std::vector<std::shared_ptr<SynchronizationObject>>& sync_objects, s64 nano_seconds); | ||
| 40 | |||
| 41 | private: | ||
| 42 | Core::System& system; | ||
| 43 | }; | ||
| 44 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/synchronization_object.cpp b/src/core/hle/kernel/synchronization_object.cpp deleted file mode 100644 index ba4d39157..000000000 --- a/src/core/hle/kernel/synchronization_object.cpp +++ /dev/null | |||
| @@ -1,49 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include "common/assert.h" | ||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "core/core.h" | ||
| 10 | #include "core/hle/kernel/kernel.h" | ||
| 11 | #include "core/hle/kernel/object.h" | ||
| 12 | #include "core/hle/kernel/process.h" | ||
| 13 | #include "core/hle/kernel/synchronization.h" | ||
| 14 | #include "core/hle/kernel/synchronization_object.h" | ||
| 15 | #include "core/hle/kernel/thread.h" | ||
| 16 | |||
| 17 | namespace Kernel { | ||
| 18 | |||
| 19 | SynchronizationObject::SynchronizationObject(KernelCore& kernel) : Object{kernel} {} | ||
| 20 | SynchronizationObject::~SynchronizationObject() = default; | ||
| 21 | |||
| 22 | void SynchronizationObject::Signal() { | ||
| 23 | kernel.Synchronization().SignalObject(*this); | ||
| 24 | } | ||
| 25 | |||
| 26 | void SynchronizationObject::AddWaitingThread(std::shared_ptr<Thread> thread) { | ||
| 27 | auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); | ||
| 28 | if (itr == waiting_threads.end()) | ||
| 29 | waiting_threads.push_back(std::move(thread)); | ||
| 30 | } | ||
| 31 | |||
| 32 | void SynchronizationObject::RemoveWaitingThread(std::shared_ptr<Thread> thread) { | ||
| 33 | auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); | ||
| 34 | // If a thread passed multiple handles to the same object, | ||
| 35 | // the kernel might attempt to remove the thread from the object's | ||
| 36 | // waiting threads list multiple times. | ||
| 37 | if (itr != waiting_threads.end()) | ||
| 38 | waiting_threads.erase(itr); | ||
| 39 | } | ||
| 40 | |||
| 41 | void SynchronizationObject::ClearWaitingThreads() { | ||
| 42 | waiting_threads.clear(); | ||
| 43 | } | ||
| 44 | |||
| 45 | const std::vector<std::shared_ptr<Thread>>& SynchronizationObject::GetWaitingThreads() const { | ||
| 46 | return waiting_threads; | ||
| 47 | } | ||
| 48 | |||
| 49 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/synchronization_object.h b/src/core/hle/kernel/synchronization_object.h deleted file mode 100644 index 7408ed51f..000000000 --- a/src/core/hle/kernel/synchronization_object.h +++ /dev/null | |||
| @@ -1,77 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <atomic> | ||
| 8 | #include <memory> | ||
| 9 | #include <vector> | ||
| 10 | |||
| 11 | #include "core/hle/kernel/object.h" | ||
| 12 | |||
| 13 | namespace Kernel { | ||
| 14 | |||
| 15 | class KernelCore; | ||
| 16 | class Synchronization; | ||
| 17 | class Thread; | ||
| 18 | |||
| 19 | /// Class that represents a Kernel object that a thread can be waiting on | ||
| 20 | class SynchronizationObject : public Object { | ||
| 21 | public: | ||
| 22 | explicit SynchronizationObject(KernelCore& kernel); | ||
| 23 | ~SynchronizationObject() override; | ||
| 24 | |||
| 25 | /** | ||
| 26 | * Check if the specified thread should wait until the object is available | ||
| 27 | * @param thread The thread about which we're deciding. | ||
| 28 | * @return True if the current thread should wait due to this object being unavailable | ||
| 29 | */ | ||
| 30 | virtual bool ShouldWait(const Thread* thread) const = 0; | ||
| 31 | |||
| 32 | /// Acquire/lock the object for the specified thread if it is available | ||
| 33 | virtual void Acquire(Thread* thread) = 0; | ||
| 34 | |||
| 35 | /// Signal this object | ||
| 36 | virtual void Signal(); | ||
| 37 | |||
| 38 | virtual bool IsSignaled() const { | ||
| 39 | return is_signaled; | ||
| 40 | } | ||
| 41 | |||
| 42 | /** | ||
| 43 | * Add a thread to wait on this object | ||
| 44 | * @param thread Pointer to thread to add | ||
| 45 | */ | ||
| 46 | void AddWaitingThread(std::shared_ptr<Thread> thread); | ||
| 47 | |||
| 48 | /** | ||
| 49 | * Removes a thread from waiting on this object (e.g. if it was resumed already) | ||
| 50 | * @param thread Pointer to thread to remove | ||
| 51 | */ | ||
| 52 | void RemoveWaitingThread(std::shared_ptr<Thread> thread); | ||
| 53 | |||
| 54 | /// Get a const reference to the waiting threads list for debug use | ||
| 55 | const std::vector<std::shared_ptr<Thread>>& GetWaitingThreads() const; | ||
| 56 | |||
| 57 | void ClearWaitingThreads(); | ||
| 58 | |||
| 59 | protected: | ||
| 60 | std::atomic_bool is_signaled{}; // Tells if this sync object is signaled | ||
| 61 | |||
| 62 | private: | ||
| 63 | /// Threads waiting for this object to become available | ||
| 64 | std::vector<std::shared_ptr<Thread>> waiting_threads; | ||
| 65 | }; | ||
| 66 | |||
| 67 | // Specialization of DynamicObjectCast for SynchronizationObjects | ||
| 68 | template <> | ||
| 69 | inline std::shared_ptr<SynchronizationObject> DynamicObjectCast<SynchronizationObject>( | ||
| 70 | std::shared_ptr<Object> object) { | ||
| 71 | if (object != nullptr && object->IsWaitable()) { | ||
| 72 | return std::static_pointer_cast<SynchronizationObject>(object); | ||
| 73 | } | ||
| 74 | return nullptr; | ||
| 75 | } | ||
| 76 | |||
| 77 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index a4f9e0d97..d97323255 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -17,9 +17,11 @@ | |||
| 17 | #include "core/hardware_properties.h" | 17 | #include "core/hardware_properties.h" |
| 18 | #include "core/hle/kernel/errors.h" | 18 | #include "core/hle/kernel/errors.h" |
| 19 | #include "core/hle/kernel/handle_table.h" | 19 | #include "core/hle/kernel/handle_table.h" |
| 20 | #include "core/hle/kernel/k_condition_variable.h" | ||
| 20 | #include "core/hle/kernel/k_scheduler.h" | 21 | #include "core/hle/kernel/k_scheduler.h" |
| 21 | #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" | 22 | #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" |
| 22 | #include "core/hle/kernel/kernel.h" | 23 | #include "core/hle/kernel/kernel.h" |
| 24 | #include "core/hle/kernel/memory/memory_layout.h" | ||
| 23 | #include "core/hle/kernel/object.h" | 25 | #include "core/hle/kernel/object.h" |
| 24 | #include "core/hle/kernel/process.h" | 26 | #include "core/hle/kernel/process.h" |
| 25 | #include "core/hle/kernel/thread.h" | 27 | #include "core/hle/kernel/thread.h" |
| @@ -34,26 +36,19 @@ | |||
| 34 | 36 | ||
| 35 | namespace Kernel { | 37 | namespace Kernel { |
| 36 | 38 | ||
| 37 | bool Thread::ShouldWait(const Thread* thread) const { | ||
| 38 | return status != ThreadStatus::Dead; | ||
| 39 | } | ||
| 40 | |||
| 41 | bool Thread::IsSignaled() const { | 39 | bool Thread::IsSignaled() const { |
| 42 | return status == ThreadStatus::Dead; | 40 | return signaled; |
| 43 | } | ||
| 44 | |||
| 45 | void Thread::Acquire(Thread* thread) { | ||
| 46 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); | ||
| 47 | } | 41 | } |
| 48 | 42 | ||
| 49 | Thread::Thread(KernelCore& kernel) : SynchronizationObject{kernel} {} | 43 | Thread::Thread(KernelCore& kernel) : KSynchronizationObject{kernel} {} |
| 50 | Thread::~Thread() = default; | 44 | Thread::~Thread() = default; |
| 51 | 45 | ||
| 52 | void Thread::Stop() { | 46 | void Thread::Stop() { |
| 53 | { | 47 | { |
| 54 | KScopedSchedulerLock lock(kernel); | 48 | KScopedSchedulerLock lock(kernel); |
| 55 | SetStatus(ThreadStatus::Dead); | 49 | SetState(ThreadState::Terminated); |
| 56 | Signal(); | 50 | signaled = true; |
| 51 | NotifyAvailable(); | ||
| 57 | kernel.GlobalHandleTable().Close(global_handle); | 52 | kernel.GlobalHandleTable().Close(global_handle); |
| 58 | 53 | ||
| 59 | if (owner_process) { | 54 | if (owner_process) { |
| @@ -67,59 +62,27 @@ void Thread::Stop() { | |||
| 67 | global_handle = 0; | 62 | global_handle = 0; |
| 68 | } | 63 | } |
| 69 | 64 | ||
| 70 | void Thread::ResumeFromWait() { | 65 | void Thread::Wakeup() { |
| 71 | KScopedSchedulerLock lock(kernel); | 66 | KScopedSchedulerLock lock(kernel); |
| 72 | switch (status) { | 67 | SetState(ThreadState::Runnable); |
| 73 | case ThreadStatus::Paused: | ||
| 74 | case ThreadStatus::WaitSynch: | ||
| 75 | case ThreadStatus::WaitHLEEvent: | ||
| 76 | case ThreadStatus::WaitSleep: | ||
| 77 | case ThreadStatus::WaitIPC: | ||
| 78 | case ThreadStatus::WaitMutex: | ||
| 79 | case ThreadStatus::WaitCondVar: | ||
| 80 | case ThreadStatus::WaitArb: | ||
| 81 | case ThreadStatus::Dormant: | ||
| 82 | break; | ||
| 83 | |||
| 84 | case ThreadStatus::Ready: | ||
| 85 | // The thread's wakeup callback must have already been cleared when the thread was first | ||
| 86 | // awoken. | ||
| 87 | ASSERT(hle_callback == nullptr); | ||
| 88 | // If the thread is waiting on multiple wait objects, it might be awoken more than once | ||
| 89 | // before actually resuming. We can ignore subsequent wakeups if the thread status has | ||
| 90 | // already been set to ThreadStatus::Ready. | ||
| 91 | return; | ||
| 92 | case ThreadStatus::Dead: | ||
| 93 | // This should never happen, as threads must complete before being stopped. | ||
| 94 | DEBUG_ASSERT_MSG(false, "Thread with object id {} cannot be resumed because it's DEAD.", | ||
| 95 | GetObjectId()); | ||
| 96 | return; | ||
| 97 | } | ||
| 98 | |||
| 99 | SetStatus(ThreadStatus::Ready); | ||
| 100 | } | ||
| 101 | |||
| 102 | void Thread::OnWakeUp() { | ||
| 103 | KScopedSchedulerLock lock(kernel); | ||
| 104 | SetStatus(ThreadStatus::Ready); | ||
| 105 | } | 68 | } |
| 106 | 69 | ||
| 107 | ResultCode Thread::Start() { | 70 | ResultCode Thread::Start() { |
| 108 | KScopedSchedulerLock lock(kernel); | 71 | KScopedSchedulerLock lock(kernel); |
| 109 | SetStatus(ThreadStatus::Ready); | 72 | SetState(ThreadState::Runnable); |
| 110 | return RESULT_SUCCESS; | 73 | return RESULT_SUCCESS; |
| 111 | } | 74 | } |
| 112 | 75 | ||
| 113 | void Thread::CancelWait() { | 76 | void Thread::CancelWait() { |
| 114 | KScopedSchedulerLock lock(kernel); | 77 | KScopedSchedulerLock lock(kernel); |
| 115 | if (GetSchedulingStatus() != ThreadSchedStatus::Paused || !is_waiting_on_sync) { | 78 | if (GetState() != ThreadState::Waiting || !is_cancellable) { |
| 116 | is_sync_cancelled = true; | 79 | is_sync_cancelled = true; |
| 117 | return; | 80 | return; |
| 118 | } | 81 | } |
| 119 | // TODO(Blinkhawk): Implement cancel of server session | 82 | // TODO(Blinkhawk): Implement cancel of server session |
| 120 | is_sync_cancelled = false; | 83 | is_sync_cancelled = false; |
| 121 | SetSynchronizationResults(nullptr, ERR_SYNCHRONIZATION_CANCELED); | 84 | SetSynchronizationResults(nullptr, ERR_SYNCHRONIZATION_CANCELED); |
| 122 | SetStatus(ThreadStatus::Ready); | 85 | SetState(ThreadState::Runnable); |
| 123 | } | 86 | } |
| 124 | 87 | ||
| 125 | static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top, | 88 | static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top, |
| @@ -183,25 +146,24 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadTy | |||
| 183 | std::shared_ptr<Thread> thread = std::make_shared<Thread>(kernel); | 146 | std::shared_ptr<Thread> thread = std::make_shared<Thread>(kernel); |
| 184 | 147 | ||
| 185 | thread->thread_id = kernel.CreateNewThreadID(); | 148 | thread->thread_id = kernel.CreateNewThreadID(); |
| 186 | thread->status = ThreadStatus::Dormant; | 149 | thread->thread_state = ThreadState::Initialized; |
| 187 | thread->entry_point = entry_point; | 150 | thread->entry_point = entry_point; |
| 188 | thread->stack_top = stack_top; | 151 | thread->stack_top = stack_top; |
| 189 | thread->disable_count = 1; | 152 | thread->disable_count = 1; |
| 190 | thread->tpidr_el0 = 0; | 153 | thread->tpidr_el0 = 0; |
| 191 | thread->nominal_priority = thread->current_priority = priority; | 154 | thread->current_priority = priority; |
| 155 | thread->base_priority = priority; | ||
| 156 | thread->lock_owner = nullptr; | ||
| 192 | thread->schedule_count = -1; | 157 | thread->schedule_count = -1; |
| 193 | thread->last_scheduled_tick = 0; | 158 | thread->last_scheduled_tick = 0; |
| 194 | thread->processor_id = processor_id; | 159 | thread->processor_id = processor_id; |
| 195 | thread->ideal_core = processor_id; | 160 | thread->ideal_core = processor_id; |
| 196 | thread->affinity_mask.SetAffinity(processor_id, true); | 161 | thread->affinity_mask.SetAffinity(processor_id, true); |
| 197 | thread->wait_objects = nullptr; | ||
| 198 | thread->mutex_wait_address = 0; | ||
| 199 | thread->condvar_wait_address = 0; | ||
| 200 | thread->wait_handle = 0; | ||
| 201 | thread->name = std::move(name); | 162 | thread->name = std::move(name); |
| 202 | thread->global_handle = kernel.GlobalHandleTable().Create(thread).Unwrap(); | 163 | thread->global_handle = kernel.GlobalHandleTable().Create(thread).Unwrap(); |
| 203 | thread->owner_process = owner_process; | 164 | thread->owner_process = owner_process; |
| 204 | thread->type = type_flags; | 165 | thread->type = type_flags; |
| 166 | thread->signaled = false; | ||
| 205 | if ((type_flags & THREADTYPE_IDLE) == 0) { | 167 | if ((type_flags & THREADTYPE_IDLE) == 0) { |
| 206 | auto& scheduler = kernel.GlobalSchedulerContext(); | 168 | auto& scheduler = kernel.GlobalSchedulerContext(); |
| 207 | scheduler.AddThread(thread); | 169 | scheduler.AddThread(thread); |
| @@ -226,153 +188,185 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadTy | |||
| 226 | return MakeResult<std::shared_ptr<Thread>>(std::move(thread)); | 188 | return MakeResult<std::shared_ptr<Thread>>(std::move(thread)); |
| 227 | } | 189 | } |
| 228 | 190 | ||
| 229 | void Thread::SetPriority(u32 priority) { | 191 | void Thread::SetBasePriority(u32 priority) { |
| 230 | KScopedSchedulerLock lock(kernel); | ||
| 231 | ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, | 192 | ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, |
| 232 | "Invalid priority value."); | 193 | "Invalid priority value."); |
| 233 | nominal_priority = priority; | 194 | |
| 234 | UpdatePriority(); | 195 | KScopedSchedulerLock lock(kernel); |
| 196 | |||
| 197 | // Change our base priority. | ||
| 198 | base_priority = priority; | ||
| 199 | |||
| 200 | // Perform a priority restoration. | ||
| 201 | RestorePriority(kernel, this); | ||
| 235 | } | 202 | } |
| 236 | 203 | ||
| 237 | void Thread::SetSynchronizationResults(SynchronizationObject* object, ResultCode result) { | 204 | void Thread::SetSynchronizationResults(KSynchronizationObject* object, ResultCode result) { |
| 238 | signaling_object = object; | 205 | signaling_object = object; |
| 239 | signaling_result = result; | 206 | signaling_result = result; |
| 240 | } | 207 | } |
| 241 | 208 | ||
| 242 | s32 Thread::GetSynchronizationObjectIndex(std::shared_ptr<SynchronizationObject> object) const { | ||
| 243 | ASSERT_MSG(!wait_objects->empty(), "Thread is not waiting for anything"); | ||
| 244 | const auto match = std::find(wait_objects->rbegin(), wait_objects->rend(), object); | ||
| 245 | return static_cast<s32>(std::distance(match, wait_objects->rend()) - 1); | ||
| 246 | } | ||
| 247 | |||
| 248 | VAddr Thread::GetCommandBufferAddress() const { | 209 | VAddr Thread::GetCommandBufferAddress() const { |
| 249 | // Offset from the start of TLS at which the IPC command buffer begins. | 210 | // Offset from the start of TLS at which the IPC command buffer begins. |
| 250 | constexpr u64 command_header_offset = 0x80; | 211 | constexpr u64 command_header_offset = 0x80; |
| 251 | return GetTLSAddress() + command_header_offset; | 212 | return GetTLSAddress() + command_header_offset; |
| 252 | } | 213 | } |
| 253 | 214 | ||
| 254 | void Thread::SetStatus(ThreadStatus new_status) { | 215 | void Thread::SetState(ThreadState state) { |
| 255 | if (new_status == status) { | 216 | KScopedSchedulerLock sl(kernel); |
| 256 | return; | ||
| 257 | } | ||
| 258 | 217 | ||
| 259 | switch (new_status) { | 218 | // Clear debugging state |
| 260 | case ThreadStatus::Ready: | 219 | SetMutexWaitAddressForDebugging({}); |
| 261 | SetSchedulingStatus(ThreadSchedStatus::Runnable); | 220 | SetWaitReasonForDebugging({}); |
| 262 | break; | ||
| 263 | case ThreadStatus::Dormant: | ||
| 264 | SetSchedulingStatus(ThreadSchedStatus::None); | ||
| 265 | break; | ||
| 266 | case ThreadStatus::Dead: | ||
| 267 | SetSchedulingStatus(ThreadSchedStatus::Exited); | ||
| 268 | break; | ||
| 269 | default: | ||
| 270 | SetSchedulingStatus(ThreadSchedStatus::Paused); | ||
| 271 | break; | ||
| 272 | } | ||
| 273 | 221 | ||
| 274 | status = new_status; | 222 | const ThreadState old_state = thread_state; |
| 223 | thread_state = | ||
| 224 | static_cast<ThreadState>((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask)); | ||
| 225 | if (thread_state != old_state) { | ||
| 226 | KScheduler::OnThreadStateChanged(kernel, this, old_state); | ||
| 227 | } | ||
| 275 | } | 228 | } |
| 276 | 229 | ||
| 277 | void Thread::AddMutexWaiter(std::shared_ptr<Thread> thread) { | 230 | void Thread::AddWaiterImpl(Thread* thread) { |
| 278 | if (thread->lock_owner.get() == this) { | 231 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); |
| 279 | // If the thread is already waiting for this thread to release the mutex, ensure that the | 232 | |
| 280 | // waiters list is consistent and return without doing anything. | 233 | // Find the right spot to insert the waiter. |
| 281 | const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread); | 234 | auto it = waiter_list.begin(); |
| 282 | ASSERT(iter != wait_mutex_threads.end()); | 235 | while (it != waiter_list.end()) { |
| 283 | return; | 236 | if (it->GetPriority() > thread->GetPriority()) { |
| 237 | break; | ||
| 238 | } | ||
| 239 | it++; | ||
| 284 | } | 240 | } |
| 285 | 241 | ||
| 286 | // A thread can't wait on two different mutexes at the same time. | 242 | // Keep track of how many kernel waiters we have. |
| 287 | ASSERT(thread->lock_owner == nullptr); | 243 | if (Memory::IsKernelAddressKey(thread->GetAddressKey())) { |
| 244 | ASSERT((num_kernel_waiters++) >= 0); | ||
| 245 | } | ||
| 288 | 246 | ||
| 289 | // Ensure that the thread is not already in the list of mutex waiters | 247 | // Insert the waiter. |
| 290 | const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread); | 248 | waiter_list.insert(it, *thread); |
| 291 | ASSERT(iter == wait_mutex_threads.end()); | 249 | thread->SetLockOwner(this); |
| 250 | } | ||
| 292 | 251 | ||
| 293 | // Keep the list in an ordered fashion | 252 | void Thread::RemoveWaiterImpl(Thread* thread) { |
| 294 | const auto insertion_point = std::find_if( | 253 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); |
| 295 | wait_mutex_threads.begin(), wait_mutex_threads.end(), | ||
| 296 | [&thread](const auto& entry) { return entry->GetPriority() > thread->GetPriority(); }); | ||
| 297 | wait_mutex_threads.insert(insertion_point, thread); | ||
| 298 | thread->lock_owner = SharedFrom(this); | ||
| 299 | 254 | ||
| 300 | UpdatePriority(); | 255 | // Keep track of how many kernel waiters we have. |
| 301 | } | 256 | if (Memory::IsKernelAddressKey(thread->GetAddressKey())) { |
| 257 | ASSERT((num_kernel_waiters--) > 0); | ||
| 258 | } | ||
| 302 | 259 | ||
| 303 | void Thread::RemoveMutexWaiter(std::shared_ptr<Thread> thread) { | 260 | // Remove the waiter. |
| 304 | ASSERT(thread->lock_owner.get() == this); | 261 | waiter_list.erase(waiter_list.iterator_to(*thread)); |
| 262 | thread->SetLockOwner(nullptr); | ||
| 263 | } | ||
| 305 | 264 | ||
| 306 | // Ensure that the thread is in the list of mutex waiters | 265 | void Thread::RestorePriority(KernelCore& kernel, Thread* thread) { |
| 307 | const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread); | 266 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); |
| 308 | ASSERT(iter != wait_mutex_threads.end()); | ||
| 309 | 267 | ||
| 310 | wait_mutex_threads.erase(iter); | 268 | while (true) { |
| 269 | // We want to inherit priority where possible. | ||
| 270 | s32 new_priority = thread->GetBasePriority(); | ||
| 271 | if (thread->HasWaiters()) { | ||
| 272 | new_priority = std::min(new_priority, thread->waiter_list.front().GetPriority()); | ||
| 273 | } | ||
| 311 | 274 | ||
| 312 | thread->lock_owner = nullptr; | 275 | // If the priority we would inherit is not different from ours, don't do anything. |
| 313 | UpdatePriority(); | 276 | if (new_priority == thread->GetPriority()) { |
| 314 | } | 277 | return; |
| 278 | } | ||
| 315 | 279 | ||
| 316 | void Thread::UpdatePriority() { | 280 | // Ensure we don't violate condition variable red black tree invariants. |
| 317 | // If any of the threads waiting on the mutex have a higher priority | 281 | if (auto* cv_tree = thread->GetConditionVariableTree(); cv_tree != nullptr) { |
| 318 | // (taking into account priority inheritance), then this thread inherits | 282 | BeforeUpdatePriority(kernel, cv_tree, thread); |
| 319 | // that thread's priority. | ||
| 320 | u32 new_priority = nominal_priority; | ||
| 321 | if (!wait_mutex_threads.empty()) { | ||
| 322 | if (wait_mutex_threads.front()->current_priority < new_priority) { | ||
| 323 | new_priority = wait_mutex_threads.front()->current_priority; | ||
| 324 | } | 283 | } |
| 325 | } | ||
| 326 | 284 | ||
| 327 | if (new_priority == current_priority) { | 285 | // Change the priority. |
| 328 | return; | 286 | const s32 old_priority = thread->GetPriority(); |
| 329 | } | 287 | thread->SetPriority(new_priority); |
| 330 | 288 | ||
| 331 | if (GetStatus() == ThreadStatus::WaitCondVar) { | 289 | // Restore the condition variable, if relevant. |
| 332 | owner_process->RemoveConditionVariableThread(SharedFrom(this)); | 290 | if (auto* cv_tree = thread->GetConditionVariableTree(); cv_tree != nullptr) { |
| 333 | } | 291 | AfterUpdatePriority(kernel, cv_tree, thread); |
| 292 | } | ||
| 334 | 293 | ||
| 335 | SetCurrentPriority(new_priority); | 294 | // Update the scheduler. |
| 295 | KScheduler::OnThreadPriorityChanged(kernel, thread, old_priority); | ||
| 336 | 296 | ||
| 337 | if (GetStatus() == ThreadStatus::WaitCondVar) { | 297 | // Keep the lock owner up to date. |
| 338 | owner_process->InsertConditionVariableThread(SharedFrom(this)); | 298 | Thread* lock_owner = thread->GetLockOwner(); |
| 339 | } | 299 | if (lock_owner == nullptr) { |
| 300 | return; | ||
| 301 | } | ||
| 340 | 302 | ||
| 341 | if (!lock_owner) { | 303 | // Update the thread in the lock owner's sorted list, and continue inheriting. |
| 342 | return; | 304 | lock_owner->RemoveWaiterImpl(thread); |
| 305 | lock_owner->AddWaiterImpl(thread); | ||
| 306 | thread = lock_owner; | ||
| 343 | } | 307 | } |
| 308 | } | ||
| 344 | 309 | ||
| 345 | // Ensure that the thread is within the correct location in the waiting list. | 310 | void Thread::AddWaiter(Thread* thread) { |
| 346 | auto old_owner = lock_owner; | 311 | AddWaiterImpl(thread); |
| 347 | lock_owner->RemoveMutexWaiter(SharedFrom(this)); | 312 | RestorePriority(kernel, this); |
| 348 | old_owner->AddMutexWaiter(SharedFrom(this)); | ||
| 349 | |||
| 350 | // Recursively update the priority of the thread that depends on the priority of this one. | ||
| 351 | lock_owner->UpdatePriority(); | ||
| 352 | } | 313 | } |
| 353 | 314 | ||
| 354 | bool Thread::AllSynchronizationObjectsReady() const { | 315 | void Thread::RemoveWaiter(Thread* thread) { |
| 355 | return std::none_of(wait_objects->begin(), wait_objects->end(), | 316 | RemoveWaiterImpl(thread); |
| 356 | [this](const std::shared_ptr<SynchronizationObject>& object) { | 317 | RestorePriority(kernel, this); |
| 357 | return object->ShouldWait(this); | ||
| 358 | }); | ||
| 359 | } | 318 | } |
| 360 | 319 | ||
| 361 | bool Thread::InvokeHLECallback(std::shared_ptr<Thread> thread) { | 320 | Thread* Thread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) { |
| 362 | ASSERT(hle_callback); | 321 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); |
| 363 | return hle_callback(std::move(thread)); | 322 | |
| 323 | s32 num_waiters{}; | ||
| 324 | Thread* next_lock_owner{}; | ||
| 325 | auto it = waiter_list.begin(); | ||
| 326 | while (it != waiter_list.end()) { | ||
| 327 | if (it->GetAddressKey() == key) { | ||
| 328 | Thread* thread = std::addressof(*it); | ||
| 329 | |||
| 330 | // Keep track of how many kernel waiters we have. | ||
| 331 | if (Memory::IsKernelAddressKey(thread->GetAddressKey())) { | ||
| 332 | ASSERT((num_kernel_waiters--) > 0); | ||
| 333 | } | ||
| 334 | it = waiter_list.erase(it); | ||
| 335 | |||
| 336 | // Update the next lock owner. | ||
| 337 | if (next_lock_owner == nullptr) { | ||
| 338 | next_lock_owner = thread; | ||
| 339 | next_lock_owner->SetLockOwner(nullptr); | ||
| 340 | } else { | ||
| 341 | next_lock_owner->AddWaiterImpl(thread); | ||
| 342 | } | ||
| 343 | num_waiters++; | ||
| 344 | } else { | ||
| 345 | it++; | ||
| 346 | } | ||
| 347 | } | ||
| 348 | |||
| 349 | // Do priority updates, if we have a next owner. | ||
| 350 | if (next_lock_owner) { | ||
| 351 | RestorePriority(kernel, this); | ||
| 352 | RestorePriority(kernel, next_lock_owner); | ||
| 353 | } | ||
| 354 | |||
| 355 | // Return output. | ||
| 356 | *out_num_waiters = num_waiters; | ||
| 357 | return next_lock_owner; | ||
| 364 | } | 358 | } |
| 365 | 359 | ||
| 366 | ResultCode Thread::SetActivity(ThreadActivity value) { | 360 | ResultCode Thread::SetActivity(ThreadActivity value) { |
| 367 | KScopedSchedulerLock lock(kernel); | 361 | KScopedSchedulerLock lock(kernel); |
| 368 | 362 | ||
| 369 | auto sched_status = GetSchedulingStatus(); | 363 | auto sched_status = GetState(); |
| 370 | 364 | ||
| 371 | if (sched_status != ThreadSchedStatus::Runnable && sched_status != ThreadSchedStatus::Paused) { | 365 | if (sched_status != ThreadState::Runnable && sched_status != ThreadState::Waiting) { |
| 372 | return ERR_INVALID_STATE; | 366 | return ERR_INVALID_STATE; |
| 373 | } | 367 | } |
| 374 | 368 | ||
| 375 | if (IsPendingTermination()) { | 369 | if (IsTerminationRequested()) { |
| 376 | return RESULT_SUCCESS; | 370 | return RESULT_SUCCESS; |
| 377 | } | 371 | } |
| 378 | 372 | ||
| @@ -394,7 +388,8 @@ ResultCode Thread::Sleep(s64 nanoseconds) { | |||
| 394 | Handle event_handle{}; | 388 | Handle event_handle{}; |
| 395 | { | 389 | { |
| 396 | KScopedSchedulerLockAndSleep lock(kernel, event_handle, this, nanoseconds); | 390 | KScopedSchedulerLockAndSleep lock(kernel, event_handle, this, nanoseconds); |
| 397 | SetStatus(ThreadStatus::WaitSleep); | 391 | SetState(ThreadState::Waiting); |
| 392 | SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Sleep); | ||
| 398 | } | 393 | } |
| 399 | 394 | ||
| 400 | if (event_handle != InvalidHandle) { | 395 | if (event_handle != InvalidHandle) { |
| @@ -405,34 +400,21 @@ ResultCode Thread::Sleep(s64 nanoseconds) { | |||
| 405 | } | 400 | } |
| 406 | 401 | ||
| 407 | void Thread::AddSchedulingFlag(ThreadSchedFlags flag) { | 402 | void Thread::AddSchedulingFlag(ThreadSchedFlags flag) { |
| 408 | const u32 old_state = scheduling_state; | 403 | const auto old_state = GetRawState(); |
| 409 | pausing_state |= static_cast<u32>(flag); | 404 | pausing_state |= static_cast<u32>(flag); |
| 410 | const u32 base_scheduling = static_cast<u32>(GetSchedulingStatus()); | 405 | const auto base_scheduling = GetState(); |
| 411 | scheduling_state = base_scheduling | pausing_state; | 406 | thread_state = base_scheduling | static_cast<ThreadState>(pausing_state); |
| 412 | KScheduler::OnThreadStateChanged(kernel, this, old_state); | 407 | KScheduler::OnThreadStateChanged(kernel, this, old_state); |
| 413 | } | 408 | } |
| 414 | 409 | ||
| 415 | void Thread::RemoveSchedulingFlag(ThreadSchedFlags flag) { | 410 | void Thread::RemoveSchedulingFlag(ThreadSchedFlags flag) { |
| 416 | const u32 old_state = scheduling_state; | 411 | const auto old_state = GetRawState(); |
| 417 | pausing_state &= ~static_cast<u32>(flag); | 412 | pausing_state &= ~static_cast<u32>(flag); |
| 418 | const u32 base_scheduling = static_cast<u32>(GetSchedulingStatus()); | 413 | const auto base_scheduling = GetState(); |
| 419 | scheduling_state = base_scheduling | pausing_state; | 414 | thread_state = base_scheduling | static_cast<ThreadState>(pausing_state); |
| 420 | KScheduler::OnThreadStateChanged(kernel, this, old_state); | 415 | KScheduler::OnThreadStateChanged(kernel, this, old_state); |
| 421 | } | 416 | } |
| 422 | 417 | ||
| 423 | void Thread::SetSchedulingStatus(ThreadSchedStatus new_status) { | ||
| 424 | const u32 old_state = scheduling_state; | ||
| 425 | scheduling_state = (scheduling_state & static_cast<u32>(ThreadSchedMasks::HighMask)) | | ||
| 426 | static_cast<u32>(new_status); | ||
| 427 | KScheduler::OnThreadStateChanged(kernel, this, old_state); | ||
| 428 | } | ||
| 429 | |||
| 430 | void Thread::SetCurrentPriority(u32 new_priority) { | ||
| 431 | const u32 old_priority = std::exchange(current_priority, new_priority); | ||
| 432 | KScheduler::OnThreadPriorityChanged(kernel, this, kernel.CurrentScheduler()->GetCurrentThread(), | ||
| 433 | old_priority); | ||
| 434 | } | ||
| 435 | |||
| 436 | ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) { | 418 | ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) { |
| 437 | KScopedSchedulerLock lock(kernel); | 419 | KScopedSchedulerLock lock(kernel); |
| 438 | const auto HighestSetCore = [](u64 mask, u32 max_cores) { | 420 | const auto HighestSetCore = [](u64 mask, u32 max_cores) { |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 11ef29888..6b66c9a0e 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -6,16 +6,21 @@ | |||
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <functional> | 8 | #include <functional> |
| 9 | #include <span> | ||
| 9 | #include <string> | 10 | #include <string> |
| 10 | #include <utility> | 11 | #include <utility> |
| 11 | #include <vector> | 12 | #include <vector> |
| 12 | 13 | ||
| 14 | #include <boost/intrusive/list.hpp> | ||
| 15 | |||
| 13 | #include "common/common_types.h" | 16 | #include "common/common_types.h" |
| 17 | #include "common/intrusive_red_black_tree.h" | ||
| 14 | #include "common/spin_lock.h" | 18 | #include "common/spin_lock.h" |
| 15 | #include "core/arm/arm_interface.h" | 19 | #include "core/arm/arm_interface.h" |
| 16 | #include "core/hle/kernel/k_affinity_mask.h" | 20 | #include "core/hle/kernel/k_affinity_mask.h" |
| 21 | #include "core/hle/kernel/k_synchronization_object.h" | ||
| 17 | #include "core/hle/kernel/object.h" | 22 | #include "core/hle/kernel/object.h" |
| 18 | #include "core/hle/kernel/synchronization_object.h" | 23 | #include "core/hle/kernel/svc_common.h" |
| 19 | #include "core/hle/result.h" | 24 | #include "core/hle/result.h" |
| 20 | 25 | ||
| 21 | namespace Common { | 26 | namespace Common { |
| @@ -73,19 +78,24 @@ enum ThreadProcessorId : s32 { | |||
| 73 | (1 << THREADPROCESSORID_2) | (1 << THREADPROCESSORID_3) | 78 | (1 << THREADPROCESSORID_2) | (1 << THREADPROCESSORID_3) |
| 74 | }; | 79 | }; |
| 75 | 80 | ||
| 76 | enum class ThreadStatus { | 81 | enum class ThreadState : u16 { |
| 77 | Ready, ///< Ready to run | 82 | Initialized = 0, |
| 78 | Paused, ///< Paused by SetThreadActivity or debug | 83 | Waiting = 1, |
| 79 | WaitHLEEvent, ///< Waiting for hle event to finish | 84 | Runnable = 2, |
| 80 | WaitSleep, ///< Waiting due to a SleepThread SVC | 85 | Terminated = 3, |
| 81 | WaitIPC, ///< Waiting for the reply from an IPC request | 86 | |
| 82 | WaitSynch, ///< Waiting due to WaitSynchronization | 87 | SuspendShift = 4, |
| 83 | WaitMutex, ///< Waiting due to an ArbitrateLock svc | 88 | Mask = (1 << SuspendShift) - 1, |
| 84 | WaitCondVar, ///< Waiting due to an WaitProcessWideKey svc | 89 | |
| 85 | WaitArb, ///< Waiting due to a SignalToAddress/WaitForAddress svc | 90 | ProcessSuspended = (1 << (0 + SuspendShift)), |
| 86 | Dormant, ///< Created but not yet made ready | 91 | ThreadSuspended = (1 << (1 + SuspendShift)), |
| 87 | Dead ///< Run to completion, or forcefully terminated | 92 | DebugSuspended = (1 << (2 + SuspendShift)), |
| 93 | BacktraceSuspended = (1 << (3 + SuspendShift)), | ||
| 94 | InitSuspended = (1 << (4 + SuspendShift)), | ||
| 95 | |||
| 96 | SuspendFlagMask = ((1 << 5) - 1) << SuspendShift, | ||
| 88 | }; | 97 | }; |
| 98 | DECLARE_ENUM_FLAG_OPERATORS(ThreadState); | ||
| 89 | 99 | ||
| 90 | enum class ThreadWakeupReason { | 100 | enum class ThreadWakeupReason { |
| 91 | Signal, // The thread was woken up by WakeupAllWaitingThreads due to an object signal. | 101 | Signal, // The thread was woken up by WakeupAllWaitingThreads due to an object signal. |
| @@ -97,13 +107,6 @@ enum class ThreadActivity : u32 { | |||
| 97 | Paused = 1, | 107 | Paused = 1, |
| 98 | }; | 108 | }; |
| 99 | 109 | ||
| 100 | enum class ThreadSchedStatus : u32 { | ||
| 101 | None = 0, | ||
| 102 | Paused = 1, | ||
| 103 | Runnable = 2, | ||
| 104 | Exited = 3, | ||
| 105 | }; | ||
| 106 | |||
| 107 | enum class ThreadSchedFlags : u32 { | 110 | enum class ThreadSchedFlags : u32 { |
| 108 | ProcessPauseFlag = 1 << 4, | 111 | ProcessPauseFlag = 1 << 4, |
| 109 | ThreadPauseFlag = 1 << 5, | 112 | ThreadPauseFlag = 1 << 5, |
| @@ -111,13 +114,20 @@ enum class ThreadSchedFlags : u32 { | |||
| 111 | KernelInitPauseFlag = 1 << 8, | 114 | KernelInitPauseFlag = 1 << 8, |
| 112 | }; | 115 | }; |
| 113 | 116 | ||
| 114 | enum class ThreadSchedMasks : u32 { | 117 | enum class ThreadWaitReasonForDebugging : u32 { |
| 115 | LowMask = 0x000f, | 118 | None, ///< Thread is not waiting |
| 116 | HighMask = 0xfff0, | 119 | Sleep, ///< Thread is waiting due to a SleepThread SVC |
| 117 | ForcePauseMask = 0x0070, | 120 | IPC, ///< Thread is waiting for the reply from an IPC request |
| 121 | Synchronization, ///< Thread is waiting due to a WaitSynchronization SVC | ||
| 122 | ConditionVar, ///< Thread is waiting due to a WaitProcessWideKey SVC | ||
| 123 | Arbitration, ///< Thread is waiting due to a SignalToAddress/WaitForAddress SVC | ||
| 124 | Suspended, ///< Thread is waiting due to process suspension | ||
| 118 | }; | 125 | }; |
| 119 | 126 | ||
| 120 | class Thread final : public SynchronizationObject { | 127 | class Thread final : public KSynchronizationObject, public boost::intrusive::list_base_hook<> { |
| 128 | friend class KScheduler; | ||
| 129 | friend class Process; | ||
| 130 | |||
| 121 | public: | 131 | public: |
| 122 | explicit Thread(KernelCore& kernel); | 132 | explicit Thread(KernelCore& kernel); |
| 123 | ~Thread() override; | 133 | ~Thread() override; |
| @@ -127,10 +137,6 @@ public: | |||
| 127 | using ThreadContext32 = Core::ARM_Interface::ThreadContext32; | 137 | using ThreadContext32 = Core::ARM_Interface::ThreadContext32; |
| 128 | using ThreadContext64 = Core::ARM_Interface::ThreadContext64; | 138 | using ThreadContext64 = Core::ARM_Interface::ThreadContext64; |
| 129 | 139 | ||
| 130 | using ThreadSynchronizationObjects = std::vector<std::shared_ptr<SynchronizationObject>>; | ||
| 131 | |||
| 132 | using HLECallback = std::function<bool(std::shared_ptr<Thread> thread)>; | ||
| 133 | |||
| 134 | /** | 140 | /** |
| 135 | * Creates and returns a new thread. The new thread is immediately scheduled | 141 | * Creates and returns a new thread. The new thread is immediately scheduled |
| 136 | * @param system The instance of the whole system | 142 | * @param system The instance of the whole system |
| @@ -186,59 +192,54 @@ public: | |||
| 186 | return HANDLE_TYPE; | 192 | return HANDLE_TYPE; |
| 187 | } | 193 | } |
| 188 | 194 | ||
| 189 | bool ShouldWait(const Thread* thread) const override; | ||
| 190 | void Acquire(Thread* thread) override; | ||
| 191 | bool IsSignaled() const override; | ||
| 192 | |||
| 193 | /** | 195 | /** |
| 194 | * Gets the thread's current priority | 196 | * Gets the thread's current priority |
| 195 | * @return The current thread's priority | 197 | * @return The current thread's priority |
| 196 | */ | 198 | */ |
| 197 | u32 GetPriority() const { | 199 | [[nodiscard]] s32 GetPriority() const { |
| 198 | return current_priority; | 200 | return current_priority; |
| 199 | } | 201 | } |
| 200 | 202 | ||
| 201 | /** | 203 | /** |
| 204 | * Sets the thread's current priority. | ||
| 205 | * @param priority The new priority. | ||
| 206 | */ | ||
| 207 | void SetPriority(s32 priority) { | ||
| 208 | current_priority = priority; | ||
| 209 | } | ||
| 210 | |||
| 211 | /** | ||
| 202 | * Gets the thread's nominal priority. | 212 | * Gets the thread's nominal priority. |
| 203 | * @return The current thread's nominal priority. | 213 | * @return The current thread's nominal priority. |
| 204 | */ | 214 | */ |
| 205 | u32 GetNominalPriority() const { | 215 | [[nodiscard]] s32 GetBasePriority() const { |
| 206 | return nominal_priority; | 216 | return base_priority; |
| 207 | } | 217 | } |
| 208 | 218 | ||
| 209 | /** | 219 | /** |
| 210 | * Sets the thread's current priority | 220 | * Sets the thread's nominal priority. |
| 211 | * @param priority The new priority | 221 | * @param priority The new priority. |
| 212 | */ | 222 | */ |
| 213 | void SetPriority(u32 priority); | 223 | void SetBasePriority(u32 priority); |
| 214 | |||
| 215 | /// Adds a thread to the list of threads that are waiting for a lock held by this thread. | ||
| 216 | void AddMutexWaiter(std::shared_ptr<Thread> thread); | ||
| 217 | |||
| 218 | /// Removes a thread from the list of threads that are waiting for a lock held by this thread. | ||
| 219 | void RemoveMutexWaiter(std::shared_ptr<Thread> thread); | ||
| 220 | |||
| 221 | /// Recalculates the current priority taking into account priority inheritance. | ||
| 222 | void UpdatePriority(); | ||
| 223 | 224 | ||
| 224 | /// Changes the core that the thread is running or scheduled to run on. | 225 | /// Changes the core that the thread is running or scheduled to run on. |
| 225 | ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask); | 226 | [[nodiscard]] ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask); |
| 226 | 227 | ||
| 227 | /** | 228 | /** |
| 228 | * Gets the thread's thread ID | 229 | * Gets the thread's thread ID |
| 229 | * @return The thread's ID | 230 | * @return The thread's ID |
| 230 | */ | 231 | */ |
| 231 | u64 GetThreadID() const { | 232 | [[nodiscard]] u64 GetThreadID() const { |
| 232 | return thread_id; | 233 | return thread_id; |
| 233 | } | 234 | } |
| 234 | 235 | ||
| 235 | /// Resumes a thread from waiting | 236 | /// Resumes a thread from waiting |
| 236 | void ResumeFromWait(); | 237 | void Wakeup(); |
| 237 | |||
| 238 | void OnWakeUp(); | ||
| 239 | 238 | ||
| 240 | ResultCode Start(); | 239 | ResultCode Start(); |
| 241 | 240 | ||
| 241 | virtual bool IsSignaled() const override; | ||
| 242 | |||
| 242 | /// Cancels a waiting operation that this thread may or may not be within. | 243 | /// Cancels a waiting operation that this thread may or may not be within. |
| 243 | /// | 244 | /// |
| 244 | /// When the thread is within a waiting state, this will set the thread's | 245 | /// When the thread is within a waiting state, this will set the thread's |
| @@ -247,29 +248,20 @@ public: | |||
| 247 | /// | 248 | /// |
| 248 | void CancelWait(); | 249 | void CancelWait(); |
| 249 | 250 | ||
| 250 | void SetSynchronizationResults(SynchronizationObject* object, ResultCode result); | 251 | void SetSynchronizationResults(KSynchronizationObject* object, ResultCode result); |
| 251 | 252 | ||
| 252 | SynchronizationObject* GetSignalingObject() const { | 253 | void SetSyncedObject(KSynchronizationObject* object, ResultCode result) { |
| 253 | return signaling_object; | 254 | SetSynchronizationResults(object, result); |
| 254 | } | 255 | } |
| 255 | 256 | ||
| 256 | ResultCode GetSignalingResult() const { | 257 | ResultCode GetWaitResult(KSynchronizationObject** out) const { |
| 258 | *out = signaling_object; | ||
| 257 | return signaling_result; | 259 | return signaling_result; |
| 258 | } | 260 | } |
| 259 | 261 | ||
| 260 | /** | 262 | ResultCode GetSignalingResult() const { |
| 261 | * Retrieves the index that this particular object occupies in the list of objects | 263 | return signaling_result; |
| 262 | * that the thread passed to WaitSynchronization, starting the search from the last element. | 264 | } |
| 263 | * | ||
| 264 | * It is used to set the output index of WaitSynchronization when the thread is awakened. | ||
| 265 | * | ||
| 266 | * When a thread wakes up due to an object signal, the kernel will use the index of the last | ||
| 267 | * matching object in the wait objects list in case of having multiple instances of the same | ||
| 268 | * object in the list. | ||
| 269 | * | ||
| 270 | * @param object Object to query the index of. | ||
| 271 | */ | ||
| 272 | s32 GetSynchronizationObjectIndex(std::shared_ptr<SynchronizationObject> object) const; | ||
| 273 | 265 | ||
| 274 | /** | 266 | /** |
| 275 | * Stops a thread, invalidating it from further use | 267 | * Stops a thread, invalidating it from further use |
| @@ -341,18 +333,22 @@ public: | |||
| 341 | 333 | ||
| 342 | std::shared_ptr<Common::Fiber>& GetHostContext(); | 334 | std::shared_ptr<Common::Fiber>& GetHostContext(); |
| 343 | 335 | ||
| 344 | ThreadStatus GetStatus() const { | 336 | ThreadState GetState() const { |
| 345 | return status; | 337 | return thread_state & ThreadState::Mask; |
| 338 | } | ||
| 339 | |||
| 340 | ThreadState GetRawState() const { | ||
| 341 | return thread_state; | ||
| 346 | } | 342 | } |
| 347 | 343 | ||
| 348 | void SetStatus(ThreadStatus new_status); | 344 | void SetState(ThreadState state); |
| 349 | 345 | ||
| 350 | s64 GetLastScheduledTick() const { | 346 | s64 GetLastScheduledTick() const { |
| 351 | return this->last_scheduled_tick; | 347 | return last_scheduled_tick; |
| 352 | } | 348 | } |
| 353 | 349 | ||
| 354 | void SetLastScheduledTick(s64 tick) { | 350 | void SetLastScheduledTick(s64 tick) { |
| 355 | this->last_scheduled_tick = tick; | 351 | last_scheduled_tick = tick; |
| 356 | } | 352 | } |
| 357 | 353 | ||
| 358 | u64 GetTotalCPUTimeTicks() const { | 354 | u64 GetTotalCPUTimeTicks() const { |
| @@ -387,98 +383,18 @@ public: | |||
| 387 | return owner_process; | 383 | return owner_process; |
| 388 | } | 384 | } |
| 389 | 385 | ||
| 390 | const ThreadSynchronizationObjects& GetSynchronizationObjects() const { | ||
| 391 | return *wait_objects; | ||
| 392 | } | ||
| 393 | |||
| 394 | void SetSynchronizationObjects(ThreadSynchronizationObjects* objects) { | ||
| 395 | wait_objects = objects; | ||
| 396 | } | ||
| 397 | |||
| 398 | void ClearSynchronizationObjects() { | ||
| 399 | for (const auto& waiting_object : *wait_objects) { | ||
| 400 | waiting_object->RemoveWaitingThread(SharedFrom(this)); | ||
| 401 | } | ||
| 402 | wait_objects->clear(); | ||
| 403 | } | ||
| 404 | |||
| 405 | /// Determines whether all the objects this thread is waiting on are ready. | ||
| 406 | bool AllSynchronizationObjectsReady() const; | ||
| 407 | |||
| 408 | const MutexWaitingThreads& GetMutexWaitingThreads() const { | 386 | const MutexWaitingThreads& GetMutexWaitingThreads() const { |
| 409 | return wait_mutex_threads; | 387 | return wait_mutex_threads; |
| 410 | } | 388 | } |
| 411 | 389 | ||
| 412 | Thread* GetLockOwner() const { | 390 | Thread* GetLockOwner() const { |
| 413 | return lock_owner.get(); | 391 | return lock_owner; |
| 414 | } | ||
| 415 | |||
| 416 | void SetLockOwner(std::shared_ptr<Thread> owner) { | ||
| 417 | lock_owner = std::move(owner); | ||
| 418 | } | ||
| 419 | |||
| 420 | VAddr GetCondVarWaitAddress() const { | ||
| 421 | return condvar_wait_address; | ||
| 422 | } | ||
| 423 | |||
| 424 | void SetCondVarWaitAddress(VAddr address) { | ||
| 425 | condvar_wait_address = address; | ||
| 426 | } | ||
| 427 | |||
| 428 | VAddr GetMutexWaitAddress() const { | ||
| 429 | return mutex_wait_address; | ||
| 430 | } | ||
| 431 | |||
| 432 | void SetMutexWaitAddress(VAddr address) { | ||
| 433 | mutex_wait_address = address; | ||
| 434 | } | ||
| 435 | |||
| 436 | Handle GetWaitHandle() const { | ||
| 437 | return wait_handle; | ||
| 438 | } | ||
| 439 | |||
| 440 | void SetWaitHandle(Handle handle) { | ||
| 441 | wait_handle = handle; | ||
| 442 | } | ||
| 443 | |||
| 444 | VAddr GetArbiterWaitAddress() const { | ||
| 445 | return arb_wait_address; | ||
| 446 | } | ||
| 447 | |||
| 448 | void SetArbiterWaitAddress(VAddr address) { | ||
| 449 | arb_wait_address = address; | ||
| 450 | } | ||
| 451 | |||
| 452 | bool HasHLECallback() const { | ||
| 453 | return hle_callback != nullptr; | ||
| 454 | } | ||
| 455 | |||
| 456 | void SetHLECallback(HLECallback callback) { | ||
| 457 | hle_callback = std::move(callback); | ||
| 458 | } | ||
| 459 | |||
| 460 | void SetHLETimeEvent(Handle time_event) { | ||
| 461 | hle_time_event = time_event; | ||
| 462 | } | ||
| 463 | |||
| 464 | void SetHLESyncObject(SynchronizationObject* object) { | ||
| 465 | hle_object = object; | ||
| 466 | } | ||
| 467 | |||
| 468 | Handle GetHLETimeEvent() const { | ||
| 469 | return hle_time_event; | ||
| 470 | } | ||
| 471 | |||
| 472 | SynchronizationObject* GetHLESyncObject() const { | ||
| 473 | return hle_object; | ||
| 474 | } | 392 | } |
| 475 | 393 | ||
| 476 | void InvalidateHLECallback() { | 394 | void SetLockOwner(Thread* owner) { |
| 477 | SetHLECallback(nullptr); | 395 | lock_owner = owner; |
| 478 | } | 396 | } |
| 479 | 397 | ||
| 480 | bool InvokeHLECallback(std::shared_ptr<Thread> thread); | ||
| 481 | |||
| 482 | u32 GetIdealCore() const { | 398 | u32 GetIdealCore() const { |
| 483 | return ideal_core; | 399 | return ideal_core; |
| 484 | } | 400 | } |
| @@ -493,20 +409,11 @@ public: | |||
| 493 | ResultCode Sleep(s64 nanoseconds); | 409 | ResultCode Sleep(s64 nanoseconds); |
| 494 | 410 | ||
| 495 | s64 GetYieldScheduleCount() const { | 411 | s64 GetYieldScheduleCount() const { |
| 496 | return this->schedule_count; | 412 | return schedule_count; |
| 497 | } | 413 | } |
| 498 | 414 | ||
| 499 | void SetYieldScheduleCount(s64 count) { | 415 | void SetYieldScheduleCount(s64 count) { |
| 500 | this->schedule_count = count; | 416 | schedule_count = count; |
| 501 | } | ||
| 502 | |||
| 503 | ThreadSchedStatus GetSchedulingStatus() const { | ||
| 504 | return static_cast<ThreadSchedStatus>(scheduling_state & | ||
| 505 | static_cast<u32>(ThreadSchedMasks::LowMask)); | ||
| 506 | } | ||
| 507 | |||
| 508 | bool IsRunnable() const { | ||
| 509 | return scheduling_state == static_cast<u32>(ThreadSchedStatus::Runnable); | ||
| 510 | } | 417 | } |
| 511 | 418 | ||
| 512 | bool IsRunning() const { | 419 | bool IsRunning() const { |
| @@ -517,36 +424,32 @@ public: | |||
| 517 | is_running = value; | 424 | is_running = value; |
| 518 | } | 425 | } |
| 519 | 426 | ||
| 520 | bool IsSyncCancelled() const { | 427 | bool IsWaitCancelled() const { |
| 521 | return is_sync_cancelled; | 428 | return is_sync_cancelled; |
| 522 | } | 429 | } |
| 523 | 430 | ||
| 524 | void SetSyncCancelled(bool value) { | 431 | void ClearWaitCancelled() { |
| 525 | is_sync_cancelled = value; | 432 | is_sync_cancelled = false; |
| 526 | } | 433 | } |
| 527 | 434 | ||
| 528 | Handle GetGlobalHandle() const { | 435 | Handle GetGlobalHandle() const { |
| 529 | return global_handle; | 436 | return global_handle; |
| 530 | } | 437 | } |
| 531 | 438 | ||
| 532 | bool IsWaitingForArbitration() const { | 439 | bool IsCancellable() const { |
| 533 | return waiting_for_arbitration; | 440 | return is_cancellable; |
| 534 | } | 441 | } |
| 535 | 442 | ||
| 536 | void WaitForArbitration(bool set) { | 443 | void SetCancellable() { |
| 537 | waiting_for_arbitration = set; | 444 | is_cancellable = true; |
| 538 | } | 445 | } |
| 539 | 446 | ||
| 540 | bool IsWaitingSync() const { | 447 | void ClearCancellable() { |
| 541 | return is_waiting_on_sync; | 448 | is_cancellable = false; |
| 542 | } | 449 | } |
| 543 | 450 | ||
| 544 | void SetWaitingSync(bool is_waiting) { | 451 | bool IsTerminationRequested() const { |
| 545 | is_waiting_on_sync = is_waiting; | 452 | return will_be_terminated || GetRawState() == ThreadState::Terminated; |
| 546 | } | ||
| 547 | |||
| 548 | bool IsPendingTermination() const { | ||
| 549 | return will_be_terminated || GetSchedulingStatus() == ThreadSchedStatus::Exited; | ||
| 550 | } | 453 | } |
| 551 | 454 | ||
| 552 | bool IsPaused() const { | 455 | bool IsPaused() const { |
| @@ -578,21 +481,21 @@ public: | |||
| 578 | constexpr QueueEntry() = default; | 481 | constexpr QueueEntry() = default; |
| 579 | 482 | ||
| 580 | constexpr void Initialize() { | 483 | constexpr void Initialize() { |
| 581 | this->prev = nullptr; | 484 | prev = nullptr; |
| 582 | this->next = nullptr; | 485 | next = nullptr; |
| 583 | } | 486 | } |
| 584 | 487 | ||
| 585 | constexpr Thread* GetPrev() const { | 488 | constexpr Thread* GetPrev() const { |
| 586 | return this->prev; | 489 | return prev; |
| 587 | } | 490 | } |
| 588 | constexpr Thread* GetNext() const { | 491 | constexpr Thread* GetNext() const { |
| 589 | return this->next; | 492 | return next; |
| 590 | } | 493 | } |
| 591 | constexpr void SetPrev(Thread* thread) { | 494 | constexpr void SetPrev(Thread* thread) { |
| 592 | this->prev = thread; | 495 | prev = thread; |
| 593 | } | 496 | } |
| 594 | constexpr void SetNext(Thread* thread) { | 497 | constexpr void SetNext(Thread* thread) { |
| 595 | this->next = thread; | 498 | next = thread; |
| 596 | } | 499 | } |
| 597 | 500 | ||
| 598 | private: | 501 | private: |
| @@ -601,11 +504,11 @@ public: | |||
| 601 | }; | 504 | }; |
| 602 | 505 | ||
| 603 | QueueEntry& GetPriorityQueueEntry(s32 core) { | 506 | QueueEntry& GetPriorityQueueEntry(s32 core) { |
| 604 | return this->per_core_priority_queue_entry[core]; | 507 | return per_core_priority_queue_entry[core]; |
| 605 | } | 508 | } |
| 606 | 509 | ||
| 607 | const QueueEntry& GetPriorityQueueEntry(s32 core) const { | 510 | const QueueEntry& GetPriorityQueueEntry(s32 core) const { |
| 608 | return this->per_core_priority_queue_entry[core]; | 511 | return per_core_priority_queue_entry[core]; |
| 609 | } | 512 | } |
| 610 | 513 | ||
| 611 | s32 GetDisableDispatchCount() const { | 514 | s32 GetDisableDispatchCount() const { |
| @@ -622,24 +525,170 @@ public: | |||
| 622 | disable_count--; | 525 | disable_count--; |
| 623 | } | 526 | } |
| 624 | 527 | ||
| 528 | void SetWaitReasonForDebugging(ThreadWaitReasonForDebugging reason) { | ||
| 529 | wait_reason_for_debugging = reason; | ||
| 530 | } | ||
| 531 | |||
| 532 | [[nodiscard]] ThreadWaitReasonForDebugging GetWaitReasonForDebugging() const { | ||
| 533 | return wait_reason_for_debugging; | ||
| 534 | } | ||
| 535 | |||
| 536 | void SetWaitObjectsForDebugging(const std::span<KSynchronizationObject*>& objects) { | ||
| 537 | wait_objects_for_debugging.clear(); | ||
| 538 | wait_objects_for_debugging.reserve(objects.size()); | ||
| 539 | for (const auto& object : objects) { | ||
| 540 | wait_objects_for_debugging.emplace_back(object); | ||
| 541 | } | ||
| 542 | } | ||
| 543 | |||
| 544 | [[nodiscard]] const std::vector<KSynchronizationObject*>& GetWaitObjectsForDebugging() const { | ||
| 545 | return wait_objects_for_debugging; | ||
| 546 | } | ||
| 547 | |||
| 548 | void SetMutexWaitAddressForDebugging(VAddr address) { | ||
| 549 | mutex_wait_address_for_debugging = address; | ||
| 550 | } | ||
| 551 | |||
| 552 | [[nodiscard]] VAddr GetMutexWaitAddressForDebugging() const { | ||
| 553 | return mutex_wait_address_for_debugging; | ||
| 554 | } | ||
| 555 | |||
| 556 | void AddWaiter(Thread* thread); | ||
| 557 | |||
| 558 | void RemoveWaiter(Thread* thread); | ||
| 559 | |||
| 560 | [[nodiscard]] Thread* RemoveWaiterByKey(s32* out_num_waiters, VAddr key); | ||
| 561 | |||
| 562 | [[nodiscard]] VAddr GetAddressKey() const { | ||
| 563 | return address_key; | ||
| 564 | } | ||
| 565 | |||
| 566 | [[nodiscard]] u32 GetAddressKeyValue() const { | ||
| 567 | return address_key_value; | ||
| 568 | } | ||
| 569 | |||
| 570 | void SetAddressKey(VAddr key) { | ||
| 571 | address_key = key; | ||
| 572 | } | ||
| 573 | |||
| 574 | void SetAddressKey(VAddr key, u32 val) { | ||
| 575 | address_key = key; | ||
| 576 | address_key_value = val; | ||
| 577 | } | ||
| 578 | |||
| 625 | private: | 579 | private: |
| 626 | friend class GlobalSchedulerContext; | 580 | static constexpr size_t PriorityInheritanceCountMax = 10; |
| 627 | friend class KScheduler; | 581 | union SyncObjectBuffer { |
| 628 | friend class Process; | 582 | std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> sync_objects{}; |
| 583 | std::array<Handle, | ||
| 584 | Svc::ArgumentHandleCountMax*(sizeof(KSynchronizationObject*) / sizeof(Handle))> | ||
| 585 | handles; | ||
| 586 | constexpr SyncObjectBuffer() {} | ||
| 587 | }; | ||
| 588 | static_assert(sizeof(SyncObjectBuffer::sync_objects) == sizeof(SyncObjectBuffer::handles)); | ||
| 589 | |||
| 590 | struct ConditionVariableComparator { | ||
| 591 | struct LightCompareType { | ||
| 592 | u64 cv_key{}; | ||
| 593 | s32 priority{}; | ||
| 594 | |||
| 595 | [[nodiscard]] constexpr u64 GetConditionVariableKey() const { | ||
| 596 | return cv_key; | ||
| 597 | } | ||
| 598 | |||
| 599 | [[nodiscard]] constexpr s32 GetPriority() const { | ||
| 600 | return priority; | ||
| 601 | } | ||
| 602 | }; | ||
| 603 | |||
| 604 | template <typename T> | ||
| 605 | requires( | ||
| 606 | std::same_as<T, Thread> || | ||
| 607 | std::same_as<T, LightCompareType>) static constexpr int Compare(const T& lhs, | ||
| 608 | const Thread& rhs) { | ||
| 609 | const uintptr_t l_key = lhs.GetConditionVariableKey(); | ||
| 610 | const uintptr_t r_key = rhs.GetConditionVariableKey(); | ||
| 611 | |||
| 612 | if (l_key < r_key) { | ||
| 613 | // Sort first by key | ||
| 614 | return -1; | ||
| 615 | } else if (l_key == r_key && lhs.GetPriority() < rhs.GetPriority()) { | ||
| 616 | // And then by priority. | ||
| 617 | return -1; | ||
| 618 | } else { | ||
| 619 | return 1; | ||
| 620 | } | ||
| 621 | } | ||
| 622 | }; | ||
| 623 | |||
| 624 | Common::IntrusiveRedBlackTreeNode condvar_arbiter_tree_node{}; | ||
| 625 | |||
| 626 | using ConditionVariableThreadTreeTraits = | ||
| 627 | Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&Thread::condvar_arbiter_tree_node>; | ||
| 628 | using ConditionVariableThreadTree = | ||
| 629 | ConditionVariableThreadTreeTraits::TreeType<ConditionVariableComparator>; | ||
| 630 | |||
| 631 | public: | ||
| 632 | using ConditionVariableThreadTreeType = ConditionVariableThreadTree; | ||
| 633 | |||
| 634 | [[nodiscard]] uintptr_t GetConditionVariableKey() const { | ||
| 635 | return condvar_key; | ||
| 636 | } | ||
| 637 | |||
| 638 | [[nodiscard]] uintptr_t GetAddressArbiterKey() const { | ||
| 639 | return condvar_key; | ||
| 640 | } | ||
| 629 | 641 | ||
| 630 | void SetSchedulingStatus(ThreadSchedStatus new_status); | 642 | void SetConditionVariable(ConditionVariableThreadTree* tree, VAddr address, uintptr_t cv_key, |
| 643 | u32 value) { | ||
| 644 | condvar_tree = tree; | ||
| 645 | condvar_key = cv_key; | ||
| 646 | address_key = address; | ||
| 647 | address_key_value = value; | ||
| 648 | } | ||
| 649 | |||
| 650 | void ClearConditionVariable() { | ||
| 651 | condvar_tree = nullptr; | ||
| 652 | } | ||
| 653 | |||
| 654 | [[nodiscard]] bool IsWaitingForConditionVariable() const { | ||
| 655 | return condvar_tree != nullptr; | ||
| 656 | } | ||
| 657 | |||
| 658 | void SetAddressArbiter(ConditionVariableThreadTree* tree, uintptr_t address) { | ||
| 659 | condvar_tree = tree; | ||
| 660 | condvar_key = address; | ||
| 661 | } | ||
| 662 | |||
| 663 | void ClearAddressArbiter() { | ||
| 664 | condvar_tree = nullptr; | ||
| 665 | } | ||
| 666 | |||
| 667 | [[nodiscard]] bool IsWaitingForAddressArbiter() const { | ||
| 668 | return condvar_tree != nullptr; | ||
| 669 | } | ||
| 670 | |||
| 671 | [[nodiscard]] ConditionVariableThreadTree* GetConditionVariableTree() const { | ||
| 672 | return condvar_tree; | ||
| 673 | } | ||
| 674 | |||
| 675 | [[nodiscard]] bool HasWaiters() const { | ||
| 676 | return !waiter_list.empty(); | ||
| 677 | } | ||
| 678 | |||
| 679 | private: | ||
| 631 | void AddSchedulingFlag(ThreadSchedFlags flag); | 680 | void AddSchedulingFlag(ThreadSchedFlags flag); |
| 632 | void RemoveSchedulingFlag(ThreadSchedFlags flag); | 681 | void RemoveSchedulingFlag(ThreadSchedFlags flag); |
| 633 | 682 | void AddWaiterImpl(Thread* thread); | |
| 634 | void SetCurrentPriority(u32 new_priority); | 683 | void RemoveWaiterImpl(Thread* thread); |
| 684 | static void RestorePriority(KernelCore& kernel, Thread* thread); | ||
| 635 | 685 | ||
| 636 | Common::SpinLock context_guard{}; | 686 | Common::SpinLock context_guard{}; |
| 637 | ThreadContext32 context_32{}; | 687 | ThreadContext32 context_32{}; |
| 638 | ThreadContext64 context_64{}; | 688 | ThreadContext64 context_64{}; |
| 639 | std::shared_ptr<Common::Fiber> host_context{}; | 689 | std::shared_ptr<Common::Fiber> host_context{}; |
| 640 | 690 | ||
| 641 | ThreadStatus status = ThreadStatus::Dormant; | 691 | ThreadState thread_state = ThreadState::Initialized; |
| 642 | u32 scheduling_state = 0; | ||
| 643 | 692 | ||
| 644 | u64 thread_id = 0; | 693 | u64 thread_id = 0; |
| 645 | 694 | ||
| @@ -652,11 +701,11 @@ private: | |||
| 652 | /// Nominal thread priority, as set by the emulated application. | 701 | /// Nominal thread priority, as set by the emulated application. |
| 653 | /// The nominal priority is the thread priority without priority | 702 | /// The nominal priority is the thread priority without priority |
| 654 | /// inheritance taken into account. | 703 | /// inheritance taken into account. |
| 655 | u32 nominal_priority = 0; | 704 | s32 base_priority{}; |
| 656 | 705 | ||
| 657 | /// Current thread priority. This may change over the course of the | 706 | /// Current thread priority. This may change over the course of the |
| 658 | /// thread's lifetime in order to facilitate priority inheritance. | 707 | /// thread's lifetime in order to facilitate priority inheritance. |
| 659 | u32 current_priority = 0; | 708 | s32 current_priority{}; |
| 660 | 709 | ||
| 661 | u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks. | 710 | u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks. |
| 662 | s64 schedule_count{}; | 711 | s64 schedule_count{}; |
| @@ -671,37 +720,27 @@ private: | |||
| 671 | Process* owner_process; | 720 | Process* owner_process; |
| 672 | 721 | ||
| 673 | /// Objects that the thread is waiting on, in the same order as they were | 722 | /// Objects that the thread is waiting on, in the same order as they were |
| 674 | /// passed to WaitSynchronization. | 723 | /// passed to WaitSynchronization. This is used for debugging only. |
| 675 | ThreadSynchronizationObjects* wait_objects; | 724 | std::vector<KSynchronizationObject*> wait_objects_for_debugging; |
| 676 | 725 | ||
| 677 | SynchronizationObject* signaling_object; | 726 | /// The current mutex wait address. This is used for debugging only. |
| 727 | VAddr mutex_wait_address_for_debugging{}; | ||
| 728 | |||
| 729 | /// The reason the thread is waiting. This is used for debugging only. | ||
| 730 | ThreadWaitReasonForDebugging wait_reason_for_debugging{}; | ||
| 731 | |||
| 732 | KSynchronizationObject* signaling_object; | ||
| 678 | ResultCode signaling_result{RESULT_SUCCESS}; | 733 | ResultCode signaling_result{RESULT_SUCCESS}; |
| 679 | 734 | ||
| 680 | /// List of threads that are waiting for a mutex that is held by this thread. | 735 | /// List of threads that are waiting for a mutex that is held by this thread. |
| 681 | MutexWaitingThreads wait_mutex_threads; | 736 | MutexWaitingThreads wait_mutex_threads; |
| 682 | 737 | ||
| 683 | /// Thread that owns the lock that this thread is waiting for. | 738 | /// Thread that owns the lock that this thread is waiting for. |
| 684 | std::shared_ptr<Thread> lock_owner; | 739 | Thread* lock_owner{}; |
| 685 | |||
| 686 | /// If waiting on a ConditionVariable, this is the ConditionVariable address | ||
| 687 | VAddr condvar_wait_address = 0; | ||
| 688 | /// If waiting on a Mutex, this is the mutex address | ||
| 689 | VAddr mutex_wait_address = 0; | ||
| 690 | /// The handle used to wait for the mutex. | ||
| 691 | Handle wait_handle = 0; | ||
| 692 | |||
| 693 | /// If waiting for an AddressArbiter, this is the address being waited on. | ||
| 694 | VAddr arb_wait_address{0}; | ||
| 695 | bool waiting_for_arbitration{}; | ||
| 696 | 740 | ||
| 697 | /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. | 741 | /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. |
| 698 | Handle global_handle = 0; | 742 | Handle global_handle = 0; |
| 699 | 743 | ||
| 700 | /// Callback for HLE Events | ||
| 701 | HLECallback hle_callback; | ||
| 702 | Handle hle_time_event; | ||
| 703 | SynchronizationObject* hle_object; | ||
| 704 | |||
| 705 | KScheduler* scheduler = nullptr; | 744 | KScheduler* scheduler = nullptr; |
| 706 | 745 | ||
| 707 | std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> per_core_priority_queue_entry{}; | 746 | std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> per_core_priority_queue_entry{}; |
| @@ -714,7 +753,7 @@ private: | |||
| 714 | 753 | ||
| 715 | u32 pausing_state = 0; | 754 | u32 pausing_state = 0; |
| 716 | bool is_running = false; | 755 | bool is_running = false; |
| 717 | bool is_waiting_on_sync = false; | 756 | bool is_cancellable = false; |
| 718 | bool is_sync_cancelled = false; | 757 | bool is_sync_cancelled = false; |
| 719 | 758 | ||
| 720 | bool is_continuous_on_svc = false; | 759 | bool is_continuous_on_svc = false; |
| @@ -725,6 +764,18 @@ private: | |||
| 725 | 764 | ||
| 726 | bool was_running = false; | 765 | bool was_running = false; |
| 727 | 766 | ||
| 767 | bool signaled{}; | ||
| 768 | |||
| 769 | ConditionVariableThreadTree* condvar_tree{}; | ||
| 770 | uintptr_t condvar_key{}; | ||
| 771 | VAddr address_key{}; | ||
| 772 | u32 address_key_value{}; | ||
| 773 | s32 num_kernel_waiters{}; | ||
| 774 | |||
| 775 | using WaiterList = boost::intrusive::list<Thread>; | ||
| 776 | WaiterList waiter_list{}; | ||
| 777 | WaiterList pinned_waiter_list{}; | ||
| 778 | |||
| 728 | std::string name; | 779 | std::string name; |
| 729 | }; | 780 | }; |
| 730 | 781 | ||
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp index 79628e2b4..832edd629 100644 --- a/src/core/hle/kernel/time_manager.cpp +++ b/src/core/hle/kernel/time_manager.cpp | |||
| @@ -18,12 +18,10 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} { | |||
| 18 | time_manager_event_type = Core::Timing::CreateEvent( | 18 | time_manager_event_type = Core::Timing::CreateEvent( |
| 19 | "Kernel::TimeManagerCallback", | 19 | "Kernel::TimeManagerCallback", |
| 20 | [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) { | 20 | [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) { |
| 21 | const KScopedSchedulerLock lock(system.Kernel()); | ||
| 22 | const auto proper_handle = static_cast<Handle>(thread_handle); | ||
| 23 | |||
| 24 | std::shared_ptr<Thread> thread; | 21 | std::shared_ptr<Thread> thread; |
| 25 | { | 22 | { |
| 26 | std::lock_guard lock{mutex}; | 23 | std::lock_guard lock{mutex}; |
| 24 | const auto proper_handle = static_cast<Handle>(thread_handle); | ||
| 27 | if (cancelled_events[proper_handle]) { | 25 | if (cancelled_events[proper_handle]) { |
| 28 | return; | 26 | return; |
| 29 | } | 27 | } |
| @@ -32,7 +30,7 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} { | |||
| 32 | 30 | ||
| 33 | if (thread) { | 31 | if (thread) { |
| 34 | // Thread can be null if process has exited | 32 | // Thread can be null if process has exited |
| 35 | thread->OnWakeUp(); | 33 | thread->Wakeup(); |
| 36 | } | 34 | } |
| 37 | }); | 35 | }); |
| 38 | } | 36 | } |
| @@ -42,8 +40,7 @@ void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 | |||
| 42 | event_handle = timetask->GetGlobalHandle(); | 40 | event_handle = timetask->GetGlobalHandle(); |
| 43 | if (nanoseconds > 0) { | 41 | if (nanoseconds > 0) { |
| 44 | ASSERT(timetask); | 42 | ASSERT(timetask); |
| 45 | ASSERT(timetask->GetStatus() != ThreadStatus::Ready); | 43 | ASSERT(timetask->GetState() != ThreadState::Runnable); |
| 46 | ASSERT(timetask->GetStatus() != ThreadStatus::WaitMutex); | ||
| 47 | system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{nanoseconds}, | 44 | system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{nanoseconds}, |
| 48 | time_manager_event_type, event_handle); | 45 | time_manager_event_type, event_handle); |
| 49 | } else { | 46 | } else { |
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index cb13210e5..c9808060a 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -560,14 +560,14 @@ void ISelfController::GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequest | |||
| 560 | 560 | ||
| 561 | AppletMessageQueue::AppletMessageQueue(Kernel::KernelCore& kernel) { | 561 | AppletMessageQueue::AppletMessageQueue(Kernel::KernelCore& kernel) { |
| 562 | on_new_message = | 562 | on_new_message = |
| 563 | Kernel::WritableEvent::CreateEventPair(kernel, "AMMessageQueue:OnMessageRecieved"); | 563 | Kernel::WritableEvent::CreateEventPair(kernel, "AMMessageQueue:OnMessageReceived"); |
| 564 | on_operation_mode_changed = | 564 | on_operation_mode_changed = |
| 565 | Kernel::WritableEvent::CreateEventPair(kernel, "AMMessageQueue:OperationModeChanged"); | 565 | Kernel::WritableEvent::CreateEventPair(kernel, "AMMessageQueue:OperationModeChanged"); |
| 566 | } | 566 | } |
| 567 | 567 | ||
| 568 | AppletMessageQueue::~AppletMessageQueue() = default; | 568 | AppletMessageQueue::~AppletMessageQueue() = default; |
| 569 | 569 | ||
| 570 | const std::shared_ptr<Kernel::ReadableEvent>& AppletMessageQueue::GetMesssageRecieveEvent() const { | 570 | const std::shared_ptr<Kernel::ReadableEvent>& AppletMessageQueue::GetMessageReceiveEvent() const { |
| 571 | return on_new_message.readable; | 571 | return on_new_message.readable; |
| 572 | } | 572 | } |
| 573 | 573 | ||
| @@ -675,7 +675,7 @@ void ICommonStateGetter::GetEventHandle(Kernel::HLERequestContext& ctx) { | |||
| 675 | 675 | ||
| 676 | IPC::ResponseBuilder rb{ctx, 2, 1}; | 676 | IPC::ResponseBuilder rb{ctx, 2, 1}; |
| 677 | rb.Push(RESULT_SUCCESS); | 677 | rb.Push(RESULT_SUCCESS); |
| 678 | rb.PushCopyObjects(msg_queue->GetMesssageRecieveEvent()); | 678 | rb.PushCopyObjects(msg_queue->GetMessageReceiveEvent()); |
| 679 | } | 679 | } |
| 680 | 680 | ||
| 681 | void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) { | 681 | void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) { |
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index b1da0d081..f51aca1af 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h | |||
| @@ -55,7 +55,7 @@ public: | |||
| 55 | explicit AppletMessageQueue(Kernel::KernelCore& kernel); | 55 | explicit AppletMessageQueue(Kernel::KernelCore& kernel); |
| 56 | ~AppletMessageQueue(); | 56 | ~AppletMessageQueue(); |
| 57 | 57 | ||
| 58 | const std::shared_ptr<Kernel::ReadableEvent>& GetMesssageRecieveEvent() const; | 58 | const std::shared_ptr<Kernel::ReadableEvent>& GetMessageReceiveEvent() const; |
| 59 | const std::shared_ptr<Kernel::ReadableEvent>& GetOperationModeChangedEvent() const; | 59 | const std::shared_ptr<Kernel::ReadableEvent>& GetOperationModeChangedEvent() const; |
| 60 | void PushMessage(AppletMessage msg); | 60 | void PushMessage(AppletMessage msg); |
| 61 | AppletMessage PopMessage(); | 61 | AppletMessage PopMessage(); |
diff --git a/src/core/hle/service/am/applets/error.cpp b/src/core/hle/service/am/applets/error.cpp index d85505082..0c8b632e8 100644 --- a/src/core/hle/service/am/applets/error.cpp +++ b/src/core/hle/service/am/applets/error.cpp | |||
| @@ -20,9 +20,9 @@ namespace Service::AM::Applets { | |||
| 20 | struct ShowError { | 20 | struct ShowError { |
| 21 | u8 mode; | 21 | u8 mode; |
| 22 | bool jump; | 22 | bool jump; |
| 23 | INSERT_UNION_PADDING_BYTES(4); | 23 | INSERT_PADDING_BYTES_NOINIT(4); |
| 24 | bool use_64bit_error_code; | 24 | bool use_64bit_error_code; |
| 25 | INSERT_UNION_PADDING_BYTES(1); | 25 | INSERT_PADDING_BYTES_NOINIT(1); |
| 26 | u64 error_code_64; | 26 | u64 error_code_64; |
| 27 | u32 error_code_32; | 27 | u32 error_code_32; |
| 28 | }; | 28 | }; |
| @@ -32,7 +32,7 @@ static_assert(sizeof(ShowError) == 0x14, "ShowError has incorrect size."); | |||
| 32 | struct ShowErrorRecord { | 32 | struct ShowErrorRecord { |
| 33 | u8 mode; | 33 | u8 mode; |
| 34 | bool jump; | 34 | bool jump; |
| 35 | INSERT_UNION_PADDING_BYTES(6); | 35 | INSERT_PADDING_BYTES_NOINIT(6); |
| 36 | u64 error_code_64; | 36 | u64 error_code_64; |
| 37 | u64 posix_time; | 37 | u64 posix_time; |
| 38 | }; | 38 | }; |
| @@ -41,7 +41,7 @@ static_assert(sizeof(ShowErrorRecord) == 0x18, "ShowErrorRecord has incorrect si | |||
| 41 | struct SystemErrorArg { | 41 | struct SystemErrorArg { |
| 42 | u8 mode; | 42 | u8 mode; |
| 43 | bool jump; | 43 | bool jump; |
| 44 | INSERT_UNION_PADDING_BYTES(6); | 44 | INSERT_PADDING_BYTES_NOINIT(6); |
| 45 | u64 error_code_64; | 45 | u64 error_code_64; |
| 46 | std::array<char, 8> language_code; | 46 | std::array<char, 8> language_code; |
| 47 | std::array<char, 0x800> main_text; | 47 | std::array<char, 0x800> main_text; |
| @@ -52,7 +52,7 @@ static_assert(sizeof(SystemErrorArg) == 0x1018, "SystemErrorArg has incorrect si | |||
| 52 | struct ApplicationErrorArg { | 52 | struct ApplicationErrorArg { |
| 53 | u8 mode; | 53 | u8 mode; |
| 54 | bool jump; | 54 | bool jump; |
| 55 | INSERT_UNION_PADDING_BYTES(6); | 55 | INSERT_PADDING_BYTES_NOINIT(6); |
| 56 | u32 error_code; | 56 | u32 error_code; |
| 57 | std::array<char, 8> language_code; | 57 | std::array<char, 8> language_code; |
| 58 | std::array<char, 0x800> main_text; | 58 | std::array<char, 0x800> main_text; |
diff --git a/src/core/hle/service/apm/interface.cpp b/src/core/hle/service/apm/interface.cpp index 298f6d520..0bff97a37 100644 --- a/src/core/hle/service/apm/interface.cpp +++ b/src/core/hle/service/apm/interface.cpp | |||
| @@ -56,7 +56,7 @@ APM::APM(Core::System& system_, std::shared_ptr<Module> apm_, Controller& contro | |||
| 56 | static const FunctionInfo functions[] = { | 56 | static const FunctionInfo functions[] = { |
| 57 | {0, &APM::OpenSession, "OpenSession"}, | 57 | {0, &APM::OpenSession, "OpenSession"}, |
| 58 | {1, &APM::GetPerformanceMode, "GetPerformanceMode"}, | 58 | {1, &APM::GetPerformanceMode, "GetPerformanceMode"}, |
| 59 | {6, nullptr, "IsCpuOverclockEnabled"}, | 59 | {6, &APM::IsCpuOverclockEnabled, "IsCpuOverclockEnabled"}, |
| 60 | }; | 60 | }; |
| 61 | RegisterHandlers(functions); | 61 | RegisterHandlers(functions); |
| 62 | } | 62 | } |
| @@ -78,6 +78,14 @@ void APM::GetPerformanceMode(Kernel::HLERequestContext& ctx) { | |||
| 78 | rb.PushEnum(controller.GetCurrentPerformanceMode()); | 78 | rb.PushEnum(controller.GetCurrentPerformanceMode()); |
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | void APM::IsCpuOverclockEnabled(Kernel::HLERequestContext& ctx) { | ||
| 82 | LOG_WARNING(Service_APM, "(STUBBED) called"); | ||
| 83 | |||
| 84 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 85 | rb.Push(RESULT_SUCCESS); | ||
| 86 | rb.Push(false); | ||
| 87 | } | ||
| 88 | |||
| 81 | APM_Sys::APM_Sys(Core::System& system_, Controller& controller_) | 89 | APM_Sys::APM_Sys(Core::System& system_, Controller& controller_) |
| 82 | : ServiceFramework{system_, "apm:sys"}, controller{controller_} { | 90 | : ServiceFramework{system_, "apm:sys"}, controller{controller_} { |
| 83 | // clang-format off | 91 | // clang-format off |
diff --git a/src/core/hle/service/apm/interface.h b/src/core/hle/service/apm/interface.h index 7d57c4978..063ad5308 100644 --- a/src/core/hle/service/apm/interface.h +++ b/src/core/hle/service/apm/interface.h | |||
| @@ -20,6 +20,7 @@ public: | |||
| 20 | private: | 20 | private: |
| 21 | void OpenSession(Kernel::HLERequestContext& ctx); | 21 | void OpenSession(Kernel::HLERequestContext& ctx); |
| 22 | void GetPerformanceMode(Kernel::HLERequestContext& ctx); | 22 | void GetPerformanceMode(Kernel::HLERequestContext& ctx); |
| 23 | void IsCpuOverclockEnabled(Kernel::HLERequestContext& ctx); | ||
| 23 | 24 | ||
| 24 | std::shared_ptr<Module> apm; | 25 | std::shared_ptr<Module> apm; |
| 25 | Controller& controller; | 26 | Controller& controller; |
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 5557da72e..641bcadea 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp | |||
| @@ -190,12 +190,6 @@ private: | |||
| 190 | void GetDeviceState(Kernel::HLERequestContext& ctx) { | 190 | void GetDeviceState(Kernel::HLERequestContext& ctx) { |
| 191 | LOG_DEBUG(Service_NFP, "called"); | 191 | LOG_DEBUG(Service_NFP, "called"); |
| 192 | 192 | ||
| 193 | auto nfc_event = nfp_interface.GetNFCEvent(); | ||
| 194 | if (!nfc_event->ShouldWait(&ctx.GetThread()) && !has_attached_handle) { | ||
| 195 | device_state = DeviceState::TagFound; | ||
| 196 | nfc_event->Clear(); | ||
| 197 | } | ||
| 198 | |||
| 199 | IPC::ResponseBuilder rb{ctx, 3}; | 193 | IPC::ResponseBuilder rb{ctx, 3}; |
| 200 | rb.Push(RESULT_SUCCESS); | 194 | rb.Push(RESULT_SUCCESS); |
| 201 | rb.Push<u32>(static_cast<u32>(device_state)); | 195 | rb.Push<u32>(static_cast<u32>(device_state)); |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index d8735491c..36970f828 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp | |||
| @@ -11,8 +11,9 @@ | |||
| 11 | 11 | ||
| 12 | namespace Service::Nvidia::Devices { | 12 | namespace Service::Nvidia::Devices { |
| 13 | 13 | ||
| 14 | nvhost_nvdec::nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) | 14 | nvhost_nvdec::nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev, |
| 15 | : nvhost_nvdec_common(system, std::move(nvmap_dev)) {} | 15 | SyncpointManager& syncpoint_manager) |
| 16 | : nvhost_nvdec_common(system, std::move(nvmap_dev), syncpoint_manager) {} | ||
| 16 | nvhost_nvdec::~nvhost_nvdec() = default; | 17 | nvhost_nvdec::~nvhost_nvdec() = default; |
| 17 | 18 | ||
| 18 | NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input, | 19 | NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input, |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h index 79b8b6de1..77ef53cdd 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h | |||
| @@ -11,7 +11,8 @@ namespace Service::Nvidia::Devices { | |||
| 11 | 11 | ||
| 12 | class nvhost_nvdec final : public nvhost_nvdec_common { | 12 | class nvhost_nvdec final : public nvhost_nvdec_common { |
| 13 | public: | 13 | public: |
| 14 | explicit nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); | 14 | explicit nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev, |
| 15 | SyncpointManager& syncpoint_manager); | ||
| 15 | ~nvhost_nvdec() override; | 16 | ~nvhost_nvdec() override; |
| 16 | 17 | ||
| 17 | NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; | 18 | NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp index b49cecb42..4898dc27a 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include "core/core.h" | 11 | #include "core/core.h" |
| 12 | #include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h" | 12 | #include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h" |
| 13 | #include "core/hle/service/nvdrv/devices/nvmap.h" | 13 | #include "core/hle/service/nvdrv/devices/nvmap.h" |
| 14 | #include "core/hle/service/nvdrv/syncpoint_manager.h" | ||
| 14 | #include "core/memory.h" | 15 | #include "core/memory.h" |
| 15 | #include "video_core/memory_manager.h" | 16 | #include "video_core/memory_manager.h" |
| 16 | #include "video_core/renderer_base.h" | 17 | #include "video_core/renderer_base.h" |
| @@ -36,8 +37,9 @@ std::size_t WriteVectors(std::vector<u8>& dst, const std::vector<T>& src, std::s | |||
| 36 | } | 37 | } |
| 37 | } // Anonymous namespace | 38 | } // Anonymous namespace |
| 38 | 39 | ||
| 39 | nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) | 40 | nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev, |
| 40 | : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} | 41 | SyncpointManager& syncpoint_manager) |
| 42 | : nvdevice(system), nvmap_dev(std::move(nvmap_dev)), syncpoint_manager(syncpoint_manager) {} | ||
| 41 | nvhost_nvdec_common::~nvhost_nvdec_common() = default; | 43 | nvhost_nvdec_common::~nvhost_nvdec_common() = default; |
| 42 | 44 | ||
| 43 | NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) { | 45 | NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) { |
| @@ -71,10 +73,15 @@ NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u | |||
| 71 | offset = SpliceVectors(input, wait_checks, params.syncpoint_count, offset); | 73 | offset = SpliceVectors(input, wait_checks, params.syncpoint_count, offset); |
| 72 | offset = SpliceVectors(input, fences, params.fence_count, offset); | 74 | offset = SpliceVectors(input, fences, params.fence_count, offset); |
| 73 | 75 | ||
| 74 | // TODO(ameerj): For async gpu, utilize fences for syncpoint 'max' increment | ||
| 75 | |||
| 76 | auto& gpu = system.GPU(); | 76 | auto& gpu = system.GPU(); |
| 77 | 77 | if (gpu.UseNvdec()) { | |
| 78 | for (std::size_t i = 0; i < syncpt_increments.size(); i++) { | ||
| 79 | const SyncptIncr& syncpt_incr = syncpt_increments[i]; | ||
| 80 | fences[i].id = syncpt_incr.id; | ||
| 81 | fences[i].value = | ||
| 82 | syncpoint_manager.IncreaseSyncpoint(syncpt_incr.id, syncpt_incr.increments); | ||
| 83 | } | ||
| 84 | } | ||
| 78 | for (const auto& cmd_buffer : command_buffers) { | 85 | for (const auto& cmd_buffer : command_buffers) { |
| 79 | auto object = nvmap_dev->GetObject(cmd_buffer.memory_id); | 86 | auto object = nvmap_dev->GetObject(cmd_buffer.memory_id); |
| 80 | ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;); | 87 | ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;); |
| @@ -89,7 +96,13 @@ NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u | |||
| 89 | cmdlist.size() * sizeof(u32)); | 96 | cmdlist.size() * sizeof(u32)); |
| 90 | gpu.PushCommandBuffer(cmdlist); | 97 | gpu.PushCommandBuffer(cmdlist); |
| 91 | } | 98 | } |
| 99 | if (gpu.UseNvdec()) { | ||
| 92 | 100 | ||
| 101 | fences[0].value = syncpoint_manager.IncreaseSyncpoint(fences[0].id, 1); | ||
| 102 | |||
| 103 | Tegra::ChCommandHeaderList cmdlist{{(4 << 28) | fences[0].id}}; | ||
| 104 | gpu.PushCommandBuffer(cmdlist); | ||
| 105 | } | ||
| 93 | std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmit)); | 106 | std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmit)); |
| 94 | // Some games expect command_buffers to be written back | 107 | // Some games expect command_buffers to be written back |
| 95 | offset = sizeof(IoctlSubmit); | 108 | offset = sizeof(IoctlSubmit); |
| @@ -98,6 +111,7 @@ NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u | |||
| 98 | offset = WriteVectors(output, reloc_shifts, offset); | 111 | offset = WriteVectors(output, reloc_shifts, offset); |
| 99 | offset = WriteVectors(output, syncpt_increments, offset); | 112 | offset = WriteVectors(output, syncpt_increments, offset); |
| 100 | offset = WriteVectors(output, wait_checks, offset); | 113 | offset = WriteVectors(output, wait_checks, offset); |
| 114 | offset = WriteVectors(output, fences, offset); | ||
| 101 | 115 | ||
| 102 | return NvResult::Success; | 116 | return NvResult::Success; |
| 103 | } | 117 | } |
| @@ -107,9 +121,10 @@ NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::ve | |||
| 107 | std::memcpy(¶ms, input.data(), sizeof(IoctlGetSyncpoint)); | 121 | std::memcpy(¶ms, input.data(), sizeof(IoctlGetSyncpoint)); |
| 108 | LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param); | 122 | LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param); |
| 109 | 123 | ||
| 110 | // We found that implementing this causes deadlocks with async gpu, along with degraded | 124 | if (device_syncpoints[params.param] == 0 && system.GPU().UseNvdec()) { |
| 111 | // performance. TODO: RE the nvdec async implementation | 125 | device_syncpoints[params.param] = syncpoint_manager.AllocateSyncpoint(); |
| 112 | params.value = 0; | 126 | } |
| 127 | params.value = device_syncpoints[params.param]; | ||
| 113 | std::memcpy(output.data(), ¶ms, sizeof(IoctlGetSyncpoint)); | 128 | std::memcpy(output.data(), ¶ms, sizeof(IoctlGetSyncpoint)); |
| 114 | 129 | ||
| 115 | return NvResult::Success; | 130 | return NvResult::Success; |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h index d9f95ba58..4c9d4ba41 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h | |||
| @@ -10,12 +10,16 @@ | |||
| 10 | #include "common/swap.h" | 10 | #include "common/swap.h" |
| 11 | #include "core/hle/service/nvdrv/devices/nvdevice.h" | 11 | #include "core/hle/service/nvdrv/devices/nvdevice.h" |
| 12 | 12 | ||
| 13 | namespace Service::Nvidia::Devices { | 13 | namespace Service::Nvidia { |
| 14 | class SyncpointManager; | ||
| 15 | |||
| 16 | namespace Devices { | ||
| 14 | class nvmap; | 17 | class nvmap; |
| 15 | 18 | ||
| 16 | class nvhost_nvdec_common : public nvdevice { | 19 | class nvhost_nvdec_common : public nvdevice { |
| 17 | public: | 20 | public: |
| 18 | explicit nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); | 21 | explicit nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev, |
| 22 | SyncpointManager& syncpoint_manager); | ||
| 19 | ~nvhost_nvdec_common() override; | 23 | ~nvhost_nvdec_common() override; |
| 20 | 24 | ||
| 21 | protected: | 25 | protected: |
| @@ -157,8 +161,10 @@ protected: | |||
| 157 | s32_le nvmap_fd{}; | 161 | s32_le nvmap_fd{}; |
| 158 | u32_le submit_timeout{}; | 162 | u32_le submit_timeout{}; |
| 159 | std::shared_ptr<nvmap> nvmap_dev; | 163 | std::shared_ptr<nvmap> nvmap_dev; |
| 160 | 164 | SyncpointManager& syncpoint_manager; | |
| 165 | std::array<u32, MaxSyncPoints> device_syncpoints{}; | ||
| 161 | // This is expected to be ordered, therefore we must use a map, not unordered_map | 166 | // This is expected to be ordered, therefore we must use a map, not unordered_map |
| 162 | std::map<GPUVAddr, BufferMap> buffer_mappings; | 167 | std::map<GPUVAddr, BufferMap> buffer_mappings; |
| 163 | }; | 168 | }; |
| 164 | }; // namespace Service::Nvidia::Devices | 169 | }; // namespace Devices |
| 170 | } // namespace Service::Nvidia | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp index 805fe86ae..72499654c 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp | |||
| @@ -10,8 +10,9 @@ | |||
| 10 | #include "video_core/renderer_base.h" | 10 | #include "video_core/renderer_base.h" |
| 11 | 11 | ||
| 12 | namespace Service::Nvidia::Devices { | 12 | namespace Service::Nvidia::Devices { |
| 13 | nvhost_vic::nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) | 13 | nvhost_vic::nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev, |
| 14 | : nvhost_nvdec_common(system, std::move(nvmap_dev)) {} | 14 | SyncpointManager& syncpoint_manager) |
| 15 | : nvhost_nvdec_common(system, std::move(nvmap_dev), syncpoint_manager) {} | ||
| 15 | 16 | ||
| 16 | nvhost_vic::~nvhost_vic() = default; | 17 | nvhost_vic::~nvhost_vic() = default; |
| 17 | 18 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h index b2e11f4d4..f401c61fa 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h | |||
| @@ -7,11 +7,11 @@ | |||
| 7 | #include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h" | 7 | #include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h" |
| 8 | 8 | ||
| 9 | namespace Service::Nvidia::Devices { | 9 | namespace Service::Nvidia::Devices { |
| 10 | class nvmap; | ||
| 11 | 10 | ||
| 12 | class nvhost_vic final : public nvhost_nvdec_common { | 11 | class nvhost_vic final : public nvhost_nvdec_common { |
| 13 | public: | 12 | public: |
| 14 | explicit nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); | 13 | explicit nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev, |
| 14 | SyncpointManager& syncpoint_manager); | ||
| 15 | ~nvhost_vic(); | 15 | ~nvhost_vic(); |
| 16 | 16 | ||
| 17 | NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; | 17 | NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; |
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index e03195afe..620c18728 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp | |||
| @@ -55,9 +55,11 @@ Module::Module(Core::System& system) : syncpoint_manager{system.GPU()} { | |||
| 55 | devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(system, nvmap_dev); | 55 | devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(system, nvmap_dev); |
| 56 | devices["/dev/nvhost-ctrl"] = | 56 | devices["/dev/nvhost-ctrl"] = |
| 57 | std::make_shared<Devices::nvhost_ctrl>(system, events_interface, syncpoint_manager); | 57 | std::make_shared<Devices::nvhost_ctrl>(system, events_interface, syncpoint_manager); |
| 58 | devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>(system, nvmap_dev); | 58 | devices["/dev/nvhost-nvdec"] = |
| 59 | std::make_shared<Devices::nvhost_nvdec>(system, nvmap_dev, syncpoint_manager); | ||
| 59 | devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(system); | 60 | devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(system); |
| 60 | devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(system, nvmap_dev); | 61 | devices["/dev/nvhost-vic"] = |
| 62 | std::make_shared<Devices::nvhost_vic>(system, nvmap_dev, syncpoint_manager); | ||
| 61 | } | 63 | } |
| 62 | 64 | ||
| 63 | Module::~Module() = default; | 65 | Module::~Module() = default; |
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp index c8c6a4d64..5578181a4 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue.cpp | |||
| @@ -26,10 +26,10 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) | |||
| 26 | LOG_WARNING(Service, "Adding graphics buffer {}", slot); | 26 | LOG_WARNING(Service, "Adding graphics buffer {}", slot); |
| 27 | 27 | ||
| 28 | { | 28 | { |
| 29 | std::unique_lock lock{queue_mutex}; | 29 | std::unique_lock lock{free_buffers_mutex}; |
| 30 | free_buffers.push_back(slot); | 30 | free_buffers.push_back(slot); |
| 31 | } | 31 | } |
| 32 | condition.notify_one(); | 32 | free_buffers_condition.notify_one(); |
| 33 | 33 | ||
| 34 | buffers[slot] = { | 34 | buffers[slot] = { |
| 35 | .slot = slot, | 35 | .slot = slot, |
| @@ -48,8 +48,8 @@ std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::Dequeue | |||
| 48 | u32 height) { | 48 | u32 height) { |
| 49 | // Wait for first request before trying to dequeue | 49 | // Wait for first request before trying to dequeue |
| 50 | { | 50 | { |
| 51 | std::unique_lock lock{queue_mutex}; | 51 | std::unique_lock lock{free_buffers_mutex}; |
| 52 | condition.wait(lock, [this] { return !free_buffers.empty() || !is_connect; }); | 52 | free_buffers_condition.wait(lock, [this] { return !free_buffers.empty() || !is_connect; }); |
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | if (!is_connect) { | 55 | if (!is_connect) { |
| @@ -58,7 +58,7 @@ std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::Dequeue | |||
| 58 | return std::nullopt; | 58 | return std::nullopt; |
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | std::unique_lock lock{queue_mutex}; | 61 | std::unique_lock lock{free_buffers_mutex}; |
| 62 | 62 | ||
| 63 | auto f_itr = free_buffers.begin(); | 63 | auto f_itr = free_buffers.begin(); |
| 64 | auto slot = buffers.size(); | 64 | auto slot = buffers.size(); |
| @@ -100,6 +100,7 @@ void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform, | |||
| 100 | buffers[slot].crop_rect = crop_rect; | 100 | buffers[slot].crop_rect = crop_rect; |
| 101 | buffers[slot].swap_interval = swap_interval; | 101 | buffers[slot].swap_interval = swap_interval; |
| 102 | buffers[slot].multi_fence = multi_fence; | 102 | buffers[slot].multi_fence = multi_fence; |
| 103 | std::unique_lock lock{queue_sequence_mutex}; | ||
| 103 | queue_sequence.push_back(slot); | 104 | queue_sequence.push_back(slot); |
| 104 | } | 105 | } |
| 105 | 106 | ||
| @@ -113,15 +114,16 @@ void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& mult | |||
| 113 | buffers[slot].swap_interval = 0; | 114 | buffers[slot].swap_interval = 0; |
| 114 | 115 | ||
| 115 | { | 116 | { |
| 116 | std::unique_lock lock{queue_mutex}; | 117 | std::unique_lock lock{free_buffers_mutex}; |
| 117 | free_buffers.push_back(slot); | 118 | free_buffers.push_back(slot); |
| 118 | } | 119 | } |
| 119 | condition.notify_one(); | 120 | free_buffers_condition.notify_one(); |
| 120 | 121 | ||
| 121 | buffer_wait_event.writable->Signal(); | 122 | buffer_wait_event.writable->Signal(); |
| 122 | } | 123 | } |
| 123 | 124 | ||
| 124 | std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() { | 125 | std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() { |
| 126 | std::unique_lock lock{queue_sequence_mutex}; | ||
| 125 | std::size_t buffer_slot = buffers.size(); | 127 | std::size_t buffer_slot = buffers.size(); |
| 126 | // Iterate to find a queued buffer matching the requested slot. | 128 | // Iterate to find a queued buffer matching the requested slot. |
| 127 | while (buffer_slot == buffers.size() && !queue_sequence.empty()) { | 129 | while (buffer_slot == buffers.size() && !queue_sequence.empty()) { |
| @@ -147,27 +149,29 @@ void BufferQueue::ReleaseBuffer(u32 slot) { | |||
| 147 | 149 | ||
| 148 | buffers[slot].status = Buffer::Status::Free; | 150 | buffers[slot].status = Buffer::Status::Free; |
| 149 | { | 151 | { |
| 150 | std::unique_lock lock{queue_mutex}; | 152 | std::unique_lock lock{free_buffers_mutex}; |
| 151 | free_buffers.push_back(slot); | 153 | free_buffers.push_back(slot); |
| 152 | } | 154 | } |
| 153 | condition.notify_one(); | 155 | free_buffers_condition.notify_one(); |
| 154 | 156 | ||
| 155 | buffer_wait_event.writable->Signal(); | 157 | buffer_wait_event.writable->Signal(); |
| 156 | } | 158 | } |
| 157 | 159 | ||
| 158 | void BufferQueue::Connect() { | 160 | void BufferQueue::Connect() { |
| 161 | std::unique_lock lock{queue_sequence_mutex}; | ||
| 159 | queue_sequence.clear(); | 162 | queue_sequence.clear(); |
| 160 | id = 1; | ||
| 161 | layer_id = 1; | ||
| 162 | is_connect = true; | 163 | is_connect = true; |
| 163 | } | 164 | } |
| 164 | 165 | ||
| 165 | void BufferQueue::Disconnect() { | 166 | void BufferQueue::Disconnect() { |
| 166 | buffers.fill({}); | 167 | buffers.fill({}); |
| 167 | queue_sequence.clear(); | 168 | { |
| 169 | std::unique_lock lock{queue_sequence_mutex}; | ||
| 170 | queue_sequence.clear(); | ||
| 171 | } | ||
| 168 | buffer_wait_event.writable->Signal(); | 172 | buffer_wait_event.writable->Signal(); |
| 169 | is_connect = false; | 173 | is_connect = false; |
| 170 | condition.notify_one(); | 174 | free_buffers_condition.notify_one(); |
| 171 | } | 175 | } |
| 172 | 176 | ||
| 173 | u32 BufferQueue::Query(QueryType type) { | 177 | u32 BufferQueue::Query(QueryType type) { |
| @@ -176,9 +180,11 @@ u32 BufferQueue::Query(QueryType type) { | |||
| 176 | switch (type) { | 180 | switch (type) { |
| 177 | case QueryType::NativeWindowFormat: | 181 | case QueryType::NativeWindowFormat: |
| 178 | return static_cast<u32>(PixelFormat::RGBA8888); | 182 | return static_cast<u32>(PixelFormat::RGBA8888); |
| 183 | case QueryType::NativeWindowWidth: | ||
| 184 | case QueryType::NativeWindowHeight: | ||
| 185 | break; | ||
| 179 | } | 186 | } |
| 180 | 187 | UNIMPLEMENTED_MSG("Unimplemented query type={}", type); | |
| 181 | UNIMPLEMENTED(); | ||
| 182 | return 0; | 188 | return 0; |
| 183 | } | 189 | } |
| 184 | 190 | ||
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h index a2f60d9eb..ad7469277 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.h +++ b/src/core/hle/service/nvflinger/buffer_queue.h | |||
| @@ -129,8 +129,10 @@ private: | |||
| 129 | std::list<u32> queue_sequence; | 129 | std::list<u32> queue_sequence; |
| 130 | Kernel::EventPair buffer_wait_event; | 130 | Kernel::EventPair buffer_wait_event; |
| 131 | 131 | ||
| 132 | std::mutex queue_mutex; | 132 | std::mutex free_buffers_mutex; |
| 133 | std::condition_variable condition; | 133 | std::condition_variable free_buffers_condition; |
| 134 | |||
| 135 | std::mutex queue_sequence_mutex; | ||
| 134 | }; | 136 | }; |
| 135 | 137 | ||
| 136 | } // namespace Service::NVFlinger | 138 | } // namespace Service::NVFlinger |
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 4b3581949..ceaa93d28 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp | |||
| @@ -38,6 +38,10 @@ void NVFlinger::SplitVSync() { | |||
| 38 | system.RegisterHostThread(); | 38 | system.RegisterHostThread(); |
| 39 | std::string name = "yuzu:VSyncThread"; | 39 | std::string name = "yuzu:VSyncThread"; |
| 40 | MicroProfileOnThreadCreate(name.c_str()); | 40 | MicroProfileOnThreadCreate(name.c_str()); |
| 41 | |||
| 42 | // Cleanup | ||
| 43 | SCOPE_EXIT({ MicroProfileOnThreadExit(); }); | ||
| 44 | |||
| 41 | Common::SetCurrentThreadName(name.c_str()); | 45 | Common::SetCurrentThreadName(name.c_str()); |
| 42 | Common::SetCurrentThreadPriority(Common::ThreadPriority::High); | 46 | Common::SetCurrentThreadPriority(Common::ThreadPriority::High); |
| 43 | s64 delay = 0; | 47 | s64 delay = 0; |
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 4da69f503..2b91a89d1 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp | |||
| @@ -139,9 +139,6 @@ void SM::GetService(Kernel::HLERequestContext& ctx) { | |||
| 139 | server_port->AppendPendingSession(server); | 139 | server_port->AppendPendingSession(server); |
| 140 | } | 140 | } |
| 141 | 141 | ||
| 142 | // Wake the threads waiting on the ServerPort | ||
| 143 | server_port->Signal(); | ||
| 144 | |||
| 145 | LOG_DEBUG(Service_SM, "called service={} -> session={}", name, client->GetObjectId()); | 142 | LOG_DEBUG(Service_SM, "called service={} -> session={}", name, client->GetObjectId()); |
| 146 | IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; | 143 | IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; |
| 147 | rb.Push(RESULT_SUCCESS); | 144 | rb.Push(RESULT_SUCCESS); |
diff --git a/src/core/hle/service/sockets/sockets_translate.cpp b/src/core/hle/service/sockets/sockets_translate.cpp index c822d21b8..ca61d72ca 100644 --- a/src/core/hle/service/sockets/sockets_translate.cpp +++ b/src/core/hle/service/sockets/sockets_translate.cpp | |||
| @@ -64,6 +64,7 @@ Network::Type Translate(Type type) { | |||
| 64 | return Network::Type::DGRAM; | 64 | return Network::Type::DGRAM; |
| 65 | default: | 65 | default: |
| 66 | UNIMPLEMENTED_MSG("Unimplemented type={}", type); | 66 | UNIMPLEMENTED_MSG("Unimplemented type={}", type); |
| 67 | return Network::Type{}; | ||
| 67 | } | 68 | } |
| 68 | } | 69 | } |
| 69 | 70 | ||
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index d91c15561..e4f5fd40c 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp | |||
| @@ -185,6 +185,10 @@ constexpr std::array<const char*, 66> RESULT_MESSAGES{ | |||
| 185 | "The INI file contains more than the maximum allowable number of KIP files.", | 185 | "The INI file contains more than the maximum allowable number of KIP files.", |
| 186 | }; | 186 | }; |
| 187 | 187 | ||
| 188 | std::string GetResultStatusString(ResultStatus status) { | ||
| 189 | return RESULT_MESSAGES.at(static_cast<std::size_t>(status)); | ||
| 190 | } | ||
| 191 | |||
| 188 | std::ostream& operator<<(std::ostream& os, ResultStatus status) { | 192 | std::ostream& operator<<(std::ostream& os, ResultStatus status) { |
| 189 | os << RESULT_MESSAGES.at(static_cast<std::size_t>(status)); | 193 | os << RESULT_MESSAGES.at(static_cast<std::size_t>(status)); |
| 190 | return os; | 194 | return os; |
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 36e79e71d..b2e5b13de 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h | |||
| @@ -135,6 +135,7 @@ enum class ResultStatus : u16 { | |||
| 135 | ErrorINITooManyKIPs, | 135 | ErrorINITooManyKIPs, |
| 136 | }; | 136 | }; |
| 137 | 137 | ||
| 138 | std::string GetResultStatusString(ResultStatus status); | ||
| 138 | std::ostream& operator<<(std::ostream& os, ResultStatus status); | 139 | std::ostream& operator<<(std::ostream& os, ResultStatus status); |
| 139 | 140 | ||
| 140 | /// Interface for loading an application | 141 | /// Interface for loading an application |
diff --git a/src/core/settings.h b/src/core/settings.h index 0cd3c0c84..a324530bd 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -131,6 +131,7 @@ struct Values { | |||
| 131 | 131 | ||
| 132 | bool cpuopt_unsafe_unfuse_fma; | 132 | bool cpuopt_unsafe_unfuse_fma; |
| 133 | bool cpuopt_unsafe_reduce_fp_error; | 133 | bool cpuopt_unsafe_reduce_fp_error; |
| 134 | bool cpuopt_unsafe_inaccurate_nan; | ||
| 134 | 135 | ||
| 135 | // Renderer | 136 | // Renderer |
| 136 | Setting<RendererBackend> renderer_backend; | 137 | Setting<RendererBackend> renderer_backend; |
| @@ -221,7 +222,7 @@ struct Values { | |||
| 221 | bool disable_macro_jit; | 222 | bool disable_macro_jit; |
| 222 | bool extended_logging; | 223 | bool extended_logging; |
| 223 | 224 | ||
| 224 | // Misceallaneous | 225 | // Miscellaneous |
| 225 | std::string log_filter; | 226 | std::string log_filter; |
| 226 | bool use_dev_keys; | 227 | bool use_dev_keys; |
| 227 | 228 | ||
diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h index f1256c9da..7a6c545bd 100644 --- a/src/input_common/gcadapter/gc_adapter.h +++ b/src/input_common/gcadapter/gc_adapter.h | |||
| @@ -120,17 +120,17 @@ private: | |||
| 120 | /// For use in initialization, querying devices to find the adapter | 120 | /// For use in initialization, querying devices to find the adapter |
| 121 | void Setup(); | 121 | void Setup(); |
| 122 | 122 | ||
| 123 | /// Resets status of all GC controller devices to a disconected state | 123 | /// Resets status of all GC controller devices to a disconnected state |
| 124 | void ResetDevices(); | 124 | void ResetDevices(); |
| 125 | 125 | ||
| 126 | /// Resets status of device connected to a disconected state | 126 | /// Resets status of device connected to a disconnected state |
| 127 | void ResetDevice(std::size_t port); | 127 | void ResetDevice(std::size_t port); |
| 128 | 128 | ||
| 129 | /// Returns true if we successfully gain access to GC Adapter | 129 | /// Returns true if we successfully gain access to GC Adapter |
| 130 | bool CheckDeviceAccess(); | 130 | bool CheckDeviceAccess(); |
| 131 | 131 | ||
| 132 | /// Captures GC Adapter endpoint address | 132 | /// Captures GC Adapter endpoint address |
| 133 | /// Returns true if the endpoind was set correctly | 133 | /// Returns true if the endpoint was set correctly |
| 134 | bool GetGCEndpoint(libusb_device* device); | 134 | bool GetGCEndpoint(libusb_device* device); |
| 135 | 135 | ||
| 136 | /// For shutting down, clear all data, join all threads, release usb | 136 | /// For shutting down, clear all data, join all threads, release usb |
diff --git a/src/input_common/motion_input.cpp b/src/input_common/motion_input.cpp index f77ba535d..6a65f175e 100644 --- a/src/input_common/motion_input.cpp +++ b/src/input_common/motion_input.cpp | |||
| @@ -129,7 +129,7 @@ void MotionInput::UpdateOrientation(u64 elapsed_time) { | |||
| 129 | rad_gyro += ki * integral_error; | 129 | rad_gyro += ki * integral_error; |
| 130 | rad_gyro += kd * derivative_error; | 130 | rad_gyro += kd * derivative_error; |
| 131 | } else { | 131 | } else { |
| 132 | // Give more weight to acelerometer values to compensate for the lack of gyro | 132 | // Give more weight to accelerometer values to compensate for the lack of gyro |
| 133 | rad_gyro += 35.0f * kp * real_error; | 133 | rad_gyro += 35.0f * kp * real_error; |
| 134 | rad_gyro += 10.0f * ki * integral_error; | 134 | rad_gyro += 10.0f * ki * integral_error; |
| 135 | rad_gyro += 10.0f * kd * derivative_error; | 135 | rad_gyro += 10.0f * kd * derivative_error; |
diff --git a/src/input_common/mouse/mouse_input.h b/src/input_common/mouse/mouse_input.h index 65e64bee7..58803c1bf 100644 --- a/src/input_common/mouse/mouse_input.h +++ b/src/input_common/mouse/mouse_input.h | |||
| @@ -20,7 +20,7 @@ enum class MouseButton { | |||
| 20 | Left, | 20 | Left, |
| 21 | Wheel, | 21 | Wheel, |
| 22 | Right, | 22 | Right, |
| 23 | Foward, | 23 | Forward, |
| 24 | Backward, | 24 | Backward, |
| 25 | Undefined, | 25 | Undefined, |
| 26 | }; | 26 | }; |
diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp index 8686a059c..c5da27a38 100644 --- a/src/input_common/udp/udp.cpp +++ b/src/input_common/udp/udp.cpp | |||
| @@ -28,14 +28,14 @@ private: | |||
| 28 | mutable std::mutex mutex; | 28 | mutable std::mutex mutex; |
| 29 | }; | 29 | }; |
| 30 | 30 | ||
| 31 | /// A motion device factory that creates motion devices from JC Adapter | 31 | /// A motion device factory that creates motion devices from a UDP client |
| 32 | UDPMotionFactory::UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_) | 32 | UDPMotionFactory::UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_) |
| 33 | : client(std::move(client_)) {} | 33 | : client(std::move(client_)) {} |
| 34 | 34 | ||
| 35 | /** | 35 | /** |
| 36 | * Creates motion device | 36 | * Creates motion device |
| 37 | * @param params contains parameters for creating the device: | 37 | * @param params contains parameters for creating the device: |
| 38 | * - "port": the nth jcpad on the adapter | 38 | * - "port": the UDP port number |
| 39 | */ | 39 | */ |
| 40 | std::unique_ptr<Input::MotionDevice> UDPMotionFactory::Create(const Common::ParamPackage& params) { | 40 | std::unique_ptr<Input::MotionDevice> UDPMotionFactory::Create(const Common::ParamPackage& params) { |
| 41 | auto ip = params.Get("ip", "127.0.0.1"); | 41 | auto ip = params.Get("ip", "127.0.0.1"); |
| @@ -90,14 +90,14 @@ private: | |||
| 90 | mutable std::mutex mutex; | 90 | mutable std::mutex mutex; |
| 91 | }; | 91 | }; |
| 92 | 92 | ||
| 93 | /// A motion device factory that creates motion devices from JC Adapter | 93 | /// A motion device factory that creates motion devices from a UDP client |
| 94 | UDPTouchFactory::UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_) | 94 | UDPTouchFactory::UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_) |
| 95 | : client(std::move(client_)) {} | 95 | : client(std::move(client_)) {} |
| 96 | 96 | ||
| 97 | /** | 97 | /** |
| 98 | * Creates motion device | 98 | * Creates motion device |
| 99 | * @param params contains parameters for creating the device: | 99 | * @param params contains parameters for creating the device: |
| 100 | * - "port": the nth jcpad on the adapter | 100 | * - "port": the UDP port number |
| 101 | */ | 101 | */ |
| 102 | std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamPackage& params) { | 102 | std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamPackage& params) { |
| 103 | auto ip = params.Get("ip", "127.0.0.1"); | 103 | auto ip = params.Get("ip", "127.0.0.1"); |
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 8a606b448..33fa89583 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | add_executable(tests | 1 | add_executable(tests |
| 2 | common/bit_field.cpp | 2 | common/bit_field.cpp |
| 3 | common/bit_utils.cpp | ||
| 4 | common/fibers.cpp | 3 | common/fibers.cpp |
| 5 | common/param_package.cpp | 4 | common/param_package.cpp |
| 6 | common/ring_buffer.cpp | 5 | common/ring_buffer.cpp |
diff --git a/src/tests/common/bit_utils.cpp b/src/tests/common/bit_utils.cpp deleted file mode 100644 index 479b5995a..000000000 --- a/src/tests/common/bit_utils.cpp +++ /dev/null | |||
| @@ -1,23 +0,0 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <catch2/catch.hpp> | ||
| 6 | #include <math.h> | ||
| 7 | #include "common/bit_util.h" | ||
| 8 | |||
| 9 | namespace Common { | ||
| 10 | |||
| 11 | TEST_CASE("BitUtils::CountTrailingZeroes", "[common]") { | ||
| 12 | REQUIRE(Common::CountTrailingZeroes32(0) == 32); | ||
| 13 | REQUIRE(Common::CountTrailingZeroes64(0) == 64); | ||
| 14 | REQUIRE(Common::CountTrailingZeroes32(9) == 0); | ||
| 15 | REQUIRE(Common::CountTrailingZeroes32(8) == 3); | ||
| 16 | REQUIRE(Common::CountTrailingZeroes32(0x801000) == 12); | ||
| 17 | REQUIRE(Common::CountTrailingZeroes64(9) == 0); | ||
| 18 | REQUIRE(Common::CountTrailingZeroes64(8) == 3); | ||
| 19 | REQUIRE(Common::CountTrailingZeroes64(0x801000) == 12); | ||
| 20 | REQUIRE(Common::CountTrailingZeroes64(0x801000000000UL) == 36); | ||
| 21 | } | ||
| 22 | |||
| 23 | } // namespace Common | ||
diff --git a/src/tests/common/fibers.cpp b/src/tests/common/fibers.cpp index 4757dd2b4..d94492fc6 100644 --- a/src/tests/common/fibers.cpp +++ b/src/tests/common/fibers.cpp | |||
| @@ -207,7 +207,7 @@ static void ThreadStart2_2(u32 id, TestControl2& test_control) { | |||
| 207 | } | 207 | } |
| 208 | 208 | ||
| 209 | /** This test checks for fiber thread exchange configuration and validates that fibers are | 209 | /** This test checks for fiber thread exchange configuration and validates that fibers are |
| 210 | * that a fiber has been succesfully transfered from one thread to another and that the TLS | 210 | * that a fiber has been successfully transferred from one thread to another and that the TLS |
| 211 | * region of the thread is kept while changing fibers. | 211 | * region of the thread is kept while changing fibers. |
| 212 | */ | 212 | */ |
| 213 | TEST_CASE("Fibers::InterExchange", "[common]") { | 213 | TEST_CASE("Fibers::InterExchange", "[common]") { |
| @@ -299,7 +299,7 @@ static void ThreadStart3(u32 id, TestControl3& test_control) { | |||
| 299 | } | 299 | } |
| 300 | 300 | ||
| 301 | /** This test checks for one two threads racing for starting the same fiber. | 301 | /** This test checks for one two threads racing for starting the same fiber. |
| 302 | * It checks execution occured in an ordered manner and by no time there were | 302 | * It checks execution occurred in an ordered manner and by no time there were |
| 303 | * two contexts at the same time. | 303 | * two contexts at the same time. |
| 304 | */ | 304 | */ |
| 305 | TEST_CASE("Fibers::StartRace", "[common]") { | 305 | TEST_CASE("Fibers::StartRace", "[common]") { |
diff --git a/src/tests/common/ring_buffer.cpp b/src/tests/common/ring_buffer.cpp index c883c4d56..54def22da 100644 --- a/src/tests/common/ring_buffer.cpp +++ b/src/tests/common/ring_buffer.cpp | |||
| @@ -20,60 +20,60 @@ TEST_CASE("RingBuffer: Basic Tests", "[common]") { | |||
| 20 | for (std::size_t i = 0; i < 4; i++) { | 20 | for (std::size_t i = 0; i < 4; i++) { |
| 21 | const char elem = static_cast<char>(i); | 21 | const char elem = static_cast<char>(i); |
| 22 | const std::size_t count = buf.Push(&elem, 1); | 22 | const std::size_t count = buf.Push(&elem, 1); |
| 23 | REQUIRE(count == 1); | 23 | REQUIRE(count == 1U); |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | REQUIRE(buf.Size() == 4); | 26 | REQUIRE(buf.Size() == 4U); |
| 27 | 27 | ||
| 28 | // Pushing values into a full ring buffer should fail. | 28 | // Pushing values into a full ring buffer should fail. |
| 29 | { | 29 | { |
| 30 | const char elem = static_cast<char>(42); | 30 | const char elem = static_cast<char>(42); |
| 31 | const std::size_t count = buf.Push(&elem, 1); | 31 | const std::size_t count = buf.Push(&elem, 1); |
| 32 | REQUIRE(count == 0); | 32 | REQUIRE(count == 0U); |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | REQUIRE(buf.Size() == 4); | 35 | REQUIRE(buf.Size() == 4U); |
| 36 | 36 | ||
| 37 | // Popping multiple values from a ring buffer with values should succeed. | 37 | // Popping multiple values from a ring buffer with values should succeed. |
| 38 | { | 38 | { |
| 39 | const std::vector<char> popped = buf.Pop(2); | 39 | const std::vector<char> popped = buf.Pop(2); |
| 40 | REQUIRE(popped.size() == 2); | 40 | REQUIRE(popped.size() == 2U); |
| 41 | REQUIRE(popped[0] == 0); | 41 | REQUIRE(popped[0] == 0); |
| 42 | REQUIRE(popped[1] == 1); | 42 | REQUIRE(popped[1] == 1); |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | REQUIRE(buf.Size() == 2); | 45 | REQUIRE(buf.Size() == 2U); |
| 46 | 46 | ||
| 47 | // Popping a single value from a ring buffer with values should succeed. | 47 | // Popping a single value from a ring buffer with values should succeed. |
| 48 | { | 48 | { |
| 49 | const std::vector<char> popped = buf.Pop(1); | 49 | const std::vector<char> popped = buf.Pop(1); |
| 50 | REQUIRE(popped.size() == 1); | 50 | REQUIRE(popped.size() == 1U); |
| 51 | REQUIRE(popped[0] == 2); | 51 | REQUIRE(popped[0] == 2); |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | REQUIRE(buf.Size() == 1); | 54 | REQUIRE(buf.Size() == 1U); |
| 55 | 55 | ||
| 56 | // Pushing more values than space available should partially suceed. | 56 | // Pushing more values than space available should partially suceed. |
| 57 | { | 57 | { |
| 58 | std::vector<char> to_push(6); | 58 | std::vector<char> to_push(6); |
| 59 | std::iota(to_push.begin(), to_push.end(), 88); | 59 | std::iota(to_push.begin(), to_push.end(), 88); |
| 60 | const std::size_t count = buf.Push(to_push); | 60 | const std::size_t count = buf.Push(to_push); |
| 61 | REQUIRE(count == 3); | 61 | REQUIRE(count == 3U); |
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | REQUIRE(buf.Size() == 4); | 64 | REQUIRE(buf.Size() == 4U); |
| 65 | 65 | ||
| 66 | // Doing an unlimited pop should pop all values. | 66 | // Doing an unlimited pop should pop all values. |
| 67 | { | 67 | { |
| 68 | const std::vector<char> popped = buf.Pop(); | 68 | const std::vector<char> popped = buf.Pop(); |
| 69 | REQUIRE(popped.size() == 4); | 69 | REQUIRE(popped.size() == 4U); |
| 70 | REQUIRE(popped[0] == 3); | 70 | REQUIRE(popped[0] == 3); |
| 71 | REQUIRE(popped[1] == 88); | 71 | REQUIRE(popped[1] == 88); |
| 72 | REQUIRE(popped[2] == 89); | 72 | REQUIRE(popped[2] == 89); |
| 73 | REQUIRE(popped[3] == 90); | 73 | REQUIRE(popped[3] == 90); |
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | REQUIRE(buf.Size() == 0); | 76 | REQUIRE(buf.Size() == 0U); |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | TEST_CASE("RingBuffer: Threaded Test", "[common]") { | 79 | TEST_CASE("RingBuffer: Threaded Test", "[common]") { |
| @@ -93,7 +93,7 @@ TEST_CASE("RingBuffer: Threaded Test", "[common]") { | |||
| 93 | std::size_t i = 0; | 93 | std::size_t i = 0; |
| 94 | while (i < count) { | 94 | while (i < count) { |
| 95 | if (const std::size_t c = buf.Push(&value[0], 1); c > 0) { | 95 | if (const std::size_t c = buf.Push(&value[0], 1); c > 0) { |
| 96 | REQUIRE(c == 1); | 96 | REQUIRE(c == 1U); |
| 97 | i++; | 97 | i++; |
| 98 | next_value(value); | 98 | next_value(value); |
| 99 | } else { | 99 | } else { |
| @@ -108,7 +108,7 @@ TEST_CASE("RingBuffer: Threaded Test", "[common]") { | |||
| 108 | std::size_t i = 0; | 108 | std::size_t i = 0; |
| 109 | while (i < count) { | 109 | while (i < count) { |
| 110 | if (const std::vector<char> v = buf.Pop(1); v.size() > 0) { | 110 | if (const std::vector<char> v = buf.Pop(1); v.size() > 0) { |
| 111 | REQUIRE(v.size() == 2); | 111 | REQUIRE(v.size() == 2U); |
| 112 | REQUIRE(v[0] == value[0]); | 112 | REQUIRE(v[0] == value[0]); |
| 113 | REQUIRE(v[1] == value[1]); | 113 | REQUIRE(v[1] == value[1]); |
| 114 | i++; | 114 | i++; |
| @@ -123,7 +123,7 @@ TEST_CASE("RingBuffer: Threaded Test", "[common]") { | |||
| 123 | producer.join(); | 123 | producer.join(); |
| 124 | consumer.join(); | 124 | consumer.join(); |
| 125 | 125 | ||
| 126 | REQUIRE(buf.Size() == 0); | 126 | REQUIRE(buf.Size() == 0U); |
| 127 | printf("RingBuffer: Threaded Test: full: %zu, empty: %zu\n", full, empty); | 127 | printf("RingBuffer: Threaded Test: full: %zu, empty: %zu\n", full, empty); |
| 128 | } | 128 | } |
| 129 | 129 | ||
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index e050f9aed..7a20d3a79 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -115,8 +115,6 @@ add_library(video_core STATIC | |||
| 115 | renderer_vulkan/fixed_pipeline_state.h | 115 | renderer_vulkan/fixed_pipeline_state.h |
| 116 | renderer_vulkan/maxwell_to_vk.cpp | 116 | renderer_vulkan/maxwell_to_vk.cpp |
| 117 | renderer_vulkan/maxwell_to_vk.h | 117 | renderer_vulkan/maxwell_to_vk.h |
| 118 | renderer_vulkan/nsight_aftermath_tracker.cpp | ||
| 119 | renderer_vulkan/nsight_aftermath_tracker.h | ||
| 120 | renderer_vulkan/renderer_vulkan.h | 118 | renderer_vulkan/renderer_vulkan.h |
| 121 | renderer_vulkan/renderer_vulkan.cpp | 119 | renderer_vulkan/renderer_vulkan.cpp |
| 122 | renderer_vulkan/vk_blit_screen.cpp | 120 | renderer_vulkan/vk_blit_screen.cpp |
| @@ -131,16 +129,12 @@ add_library(video_core STATIC | |||
| 131 | renderer_vulkan/vk_compute_pipeline.h | 129 | renderer_vulkan/vk_compute_pipeline.h |
| 132 | renderer_vulkan/vk_descriptor_pool.cpp | 130 | renderer_vulkan/vk_descriptor_pool.cpp |
| 133 | renderer_vulkan/vk_descriptor_pool.h | 131 | renderer_vulkan/vk_descriptor_pool.h |
| 134 | renderer_vulkan/vk_device.cpp | ||
| 135 | renderer_vulkan/vk_device.h | ||
| 136 | renderer_vulkan/vk_fence_manager.cpp | 132 | renderer_vulkan/vk_fence_manager.cpp |
| 137 | renderer_vulkan/vk_fence_manager.h | 133 | renderer_vulkan/vk_fence_manager.h |
| 138 | renderer_vulkan/vk_graphics_pipeline.cpp | 134 | renderer_vulkan/vk_graphics_pipeline.cpp |
| 139 | renderer_vulkan/vk_graphics_pipeline.h | 135 | renderer_vulkan/vk_graphics_pipeline.h |
| 140 | renderer_vulkan/vk_master_semaphore.cpp | 136 | renderer_vulkan/vk_master_semaphore.cpp |
| 141 | renderer_vulkan/vk_master_semaphore.h | 137 | renderer_vulkan/vk_master_semaphore.h |
| 142 | renderer_vulkan/vk_memory_manager.cpp | ||
| 143 | renderer_vulkan/vk_memory_manager.h | ||
| 144 | renderer_vulkan/vk_pipeline_cache.cpp | 138 | renderer_vulkan/vk_pipeline_cache.cpp |
| 145 | renderer_vulkan/vk_pipeline_cache.h | 139 | renderer_vulkan/vk_pipeline_cache.h |
| 146 | renderer_vulkan/vk_query_cache.cpp | 140 | renderer_vulkan/vk_query_cache.cpp |
| @@ -167,8 +161,6 @@ add_library(video_core STATIC | |||
| 167 | renderer_vulkan/vk_texture_cache.h | 161 | renderer_vulkan/vk_texture_cache.h |
| 168 | renderer_vulkan/vk_update_descriptor.cpp | 162 | renderer_vulkan/vk_update_descriptor.cpp |
| 169 | renderer_vulkan/vk_update_descriptor.h | 163 | renderer_vulkan/vk_update_descriptor.h |
| 170 | renderer_vulkan/wrapper.cpp | ||
| 171 | renderer_vulkan/wrapper.h | ||
| 172 | shader_cache.h | 164 | shader_cache.h |
| 173 | shader_notify.cpp | 165 | shader_notify.cpp |
| 174 | shader_notify.h | 166 | shader_notify.h |
| @@ -257,6 +249,22 @@ add_library(video_core STATIC | |||
| 257 | textures/texture.h | 249 | textures/texture.h |
| 258 | video_core.cpp | 250 | video_core.cpp |
| 259 | video_core.h | 251 | video_core.h |
| 252 | vulkan_common/vulkan_debug_callback.cpp | ||
| 253 | vulkan_common/vulkan_debug_callback.h | ||
| 254 | vulkan_common/vulkan_device.cpp | ||
| 255 | vulkan_common/vulkan_device.h | ||
| 256 | vulkan_common/vulkan_instance.cpp | ||
| 257 | vulkan_common/vulkan_instance.h | ||
| 258 | vulkan_common/vulkan_library.cpp | ||
| 259 | vulkan_common/vulkan_library.h | ||
| 260 | vulkan_common/vulkan_memory_allocator.cpp | ||
| 261 | vulkan_common/vulkan_memory_allocator.h | ||
| 262 | vulkan_common/vulkan_surface.cpp | ||
| 263 | vulkan_common/vulkan_surface.h | ||
| 264 | vulkan_common/vulkan_wrapper.cpp | ||
| 265 | vulkan_common/vulkan_wrapper.h | ||
| 266 | vulkan_common/nsight_aftermath_tracker.cpp | ||
| 267 | vulkan_common/nsight_aftermath_tracker.h | ||
| 260 | ) | 268 | ) |
| 261 | 269 | ||
| 262 | create_target_directory_groups(video_core) | 270 | create_target_directory_groups(video_core) |
| @@ -304,9 +312,7 @@ else() | |||
| 304 | -Werror=pessimizing-move | 312 | -Werror=pessimizing-move |
| 305 | -Werror=redundant-move | 313 | -Werror=redundant-move |
| 306 | -Werror=shadow | 314 | -Werror=shadow |
| 307 | -Werror=switch | ||
| 308 | -Werror=type-limits | 315 | -Werror=type-limits |
| 309 | -Werror=unused-variable | ||
| 310 | 316 | ||
| 311 | $<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess> | 317 | $<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess> |
| 312 | $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter> | 318 | $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter> |
diff --git a/src/video_core/cdma_pusher.cpp b/src/video_core/cdma_pusher.cpp index e3e7432f7..33b3c060b 100644 --- a/src/video_core/cdma_pusher.cpp +++ b/src/video_core/cdma_pusher.cpp | |||
| @@ -18,10 +18,10 @@ | |||
| 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 19 | // | 19 | // |
| 20 | 20 | ||
| 21 | #include <bit> | ||
| 21 | #include "command_classes/host1x.h" | 22 | #include "command_classes/host1x.h" |
| 22 | #include "command_classes/nvdec.h" | 23 | #include "command_classes/nvdec.h" |
| 23 | #include "command_classes/vic.h" | 24 | #include "command_classes/vic.h" |
| 24 | #include "common/bit_util.h" | ||
| 25 | #include "video_core/cdma_pusher.h" | 25 | #include "video_core/cdma_pusher.h" |
| 26 | #include "video_core/command_classes/nvdec_common.h" | 26 | #include "video_core/command_classes/nvdec_common.h" |
| 27 | #include "video_core/engines/maxwell_3d.h" | 27 | #include "video_core/engines/maxwell_3d.h" |
| @@ -33,8 +33,7 @@ CDmaPusher::CDmaPusher(GPU& gpu_) | |||
| 33 | : gpu{gpu_}, nvdec_processor(std::make_shared<Nvdec>(gpu)), | 33 | : gpu{gpu_}, nvdec_processor(std::make_shared<Nvdec>(gpu)), |
| 34 | vic_processor(std::make_unique<Vic>(gpu, nvdec_processor)), | 34 | vic_processor(std::make_unique<Vic>(gpu, nvdec_processor)), |
| 35 | host1x_processor(std::make_unique<Host1x>(gpu)), | 35 | host1x_processor(std::make_unique<Host1x>(gpu)), |
| 36 | nvdec_sync(std::make_unique<SyncptIncrManager>(gpu)), | 36 | sync_manager(std::make_unique<SyncptIncrManager>(gpu)) {} |
| 37 | vic_sync(std::make_unique<SyncptIncrManager>(gpu)) {} | ||
| 38 | 37 | ||
| 39 | CDmaPusher::~CDmaPusher() = default; | 38 | CDmaPusher::~CDmaPusher() = default; |
| 40 | 39 | ||
| @@ -57,7 +56,7 @@ void CDmaPusher::Step() { | |||
| 57 | 56 | ||
| 58 | for (const u32 value : values) { | 57 | for (const u32 value : values) { |
| 59 | if (mask != 0) { | 58 | if (mask != 0) { |
| 60 | const u32 lbs = Common::CountTrailingZeroes32(mask); | 59 | const auto lbs = static_cast<u32>(std::countr_zero(mask)); |
| 61 | mask &= ~(1U << lbs); | 60 | mask &= ~(1U << lbs); |
| 62 | ExecuteCommand(static_cast<u32>(offset + lbs), value); | 61 | ExecuteCommand(static_cast<u32>(offset + lbs), value); |
| 63 | continue; | 62 | continue; |
| @@ -110,10 +109,10 @@ void CDmaPusher::ExecuteCommand(u32 state_offset, u32 data) { | |||
| 110 | const auto syncpoint_id = static_cast<u32>(data & 0xFF); | 109 | const auto syncpoint_id = static_cast<u32>(data & 0xFF); |
| 111 | const auto cond = static_cast<u32>((data >> 8) & 0xFF); | 110 | const auto cond = static_cast<u32>((data >> 8) & 0xFF); |
| 112 | if (cond == 0) { | 111 | if (cond == 0) { |
| 113 | nvdec_sync->Increment(syncpoint_id); | 112 | sync_manager->Increment(syncpoint_id); |
| 114 | } else { | 113 | } else { |
| 115 | nvdec_sync->IncrementWhenDone(static_cast<u32>(current_class), syncpoint_id); | 114 | sync_manager->SignalDone( |
| 116 | nvdec_sync->SignalDone(syncpoint_id); | 115 | sync_manager->IncrementWhenDone(static_cast<u32>(current_class), syncpoint_id)); |
| 117 | } | 116 | } |
| 118 | break; | 117 | break; |
| 119 | } | 118 | } |
| @@ -135,10 +134,10 @@ void CDmaPusher::ExecuteCommand(u32 state_offset, u32 data) { | |||
| 135 | const auto syncpoint_id = static_cast<u32>(data & 0xFF); | 134 | const auto syncpoint_id = static_cast<u32>(data & 0xFF); |
| 136 | const auto cond = static_cast<u32>((data >> 8) & 0xFF); | 135 | const auto cond = static_cast<u32>((data >> 8) & 0xFF); |
| 137 | if (cond == 0) { | 136 | if (cond == 0) { |
| 138 | vic_sync->Increment(syncpoint_id); | 137 | sync_manager->Increment(syncpoint_id); |
| 139 | } else { | 138 | } else { |
| 140 | vic_sync->IncrementWhenDone(static_cast<u32>(current_class), syncpoint_id); | 139 | sync_manager->SignalDone( |
| 141 | vic_sync->SignalDone(syncpoint_id); | 140 | sync_manager->IncrementWhenDone(static_cast<u32>(current_class), syncpoint_id)); |
| 142 | } | 141 | } |
| 143 | break; | 142 | break; |
| 144 | } | 143 | } |
diff --git a/src/video_core/cdma_pusher.h b/src/video_core/cdma_pusher.h index 0db1cd646..e5f212c1a 100644 --- a/src/video_core/cdma_pusher.h +++ b/src/video_core/cdma_pusher.h | |||
| @@ -116,19 +116,17 @@ private: | |||
| 116 | void ThiStateWrite(ThiRegisters& state, u32 state_offset, const std::vector<u32>& arguments); | 116 | void ThiStateWrite(ThiRegisters& state, u32 state_offset, const std::vector<u32>& arguments); |
| 117 | 117 | ||
| 118 | GPU& gpu; | 118 | GPU& gpu; |
| 119 | 119 | std::shared_ptr<Tegra::Nvdec> nvdec_processor; | |
| 120 | std::shared_ptr<Nvdec> nvdec_processor; | 120 | std::unique_ptr<Tegra::Vic> vic_processor; |
| 121 | std::unique_ptr<Vic> vic_processor; | 121 | std::unique_ptr<Tegra::Host1x> host1x_processor; |
| 122 | std::unique_ptr<Host1x> host1x_processor; | 122 | std::unique_ptr<SyncptIncrManager> sync_manager; |
| 123 | std::unique_ptr<SyncptIncrManager> nvdec_sync; | ||
| 124 | std::unique_ptr<SyncptIncrManager> vic_sync; | ||
| 125 | ChClassId current_class{}; | 123 | ChClassId current_class{}; |
| 126 | ThiRegisters vic_thi_state{}; | 124 | ThiRegisters vic_thi_state{}; |
| 127 | ThiRegisters nvdec_thi_state{}; | 125 | ThiRegisters nvdec_thi_state{}; |
| 128 | 126 | ||
| 129 | s32 count{}; | 127 | s32 count{}; |
| 130 | s32 offset{}; | 128 | s32 offset{}; |
| 131 | s32 mask{}; | 129 | u32 mask{}; |
| 132 | bool incrementing{}; | 130 | bool incrementing{}; |
| 133 | 131 | ||
| 134 | // Queue of command lists to be processed | 132 | // Queue of command lists to be processed |
diff --git a/src/video_core/command_classes/codecs/h264.cpp b/src/video_core/command_classes/codecs/h264.cpp index 65bbeac78..fea6aed98 100644 --- a/src/video_core/command_classes/codecs/h264.cpp +++ b/src/video_core/command_classes/codecs/h264.cpp | |||
| @@ -19,7 +19,7 @@ | |||
| 19 | // | 19 | // |
| 20 | 20 | ||
| 21 | #include <array> | 21 | #include <array> |
| 22 | #include "common/bit_util.h" | 22 | #include <bit> |
| 23 | #include "video_core/command_classes/codecs/h264.h" | 23 | #include "video_core/command_classes/codecs/h264.h" |
| 24 | #include "video_core/gpu.h" | 24 | #include "video_core/gpu.h" |
| 25 | #include "video_core/memory_manager.h" | 25 | #include "video_core/memory_manager.h" |
| @@ -266,7 +266,7 @@ void H264BitWriter::WriteExpGolombCodedInt(s32 value) { | |||
| 266 | } | 266 | } |
| 267 | 267 | ||
| 268 | void H264BitWriter::WriteExpGolombCodedUInt(u32 value) { | 268 | void H264BitWriter::WriteExpGolombCodedUInt(u32 value) { |
| 269 | const s32 size = 32 - Common::CountLeadingZeroes32(static_cast<s32>(value + 1)); | 269 | const s32 size = 32 - std::countl_zero(value + 1); |
| 270 | WriteBits(1, size); | 270 | WriteBits(1, size); |
| 271 | 271 | ||
| 272 | value -= (1U << (size - 1)) - 1; | 272 | value -= (1U << (size - 1)) - 1; |
diff --git a/src/video_core/command_classes/host1x.cpp b/src/video_core/command_classes/host1x.cpp index c4dd4881a..b12494528 100644 --- a/src/video_core/command_classes/host1x.cpp +++ b/src/video_core/command_classes/host1x.cpp | |||
| @@ -10,22 +10,14 @@ Tegra::Host1x::Host1x(GPU& gpu_) : gpu(gpu_) {} | |||
| 10 | 10 | ||
| 11 | Tegra::Host1x::~Host1x() = default; | 11 | Tegra::Host1x::~Host1x() = default; |
| 12 | 12 | ||
| 13 | void Tegra::Host1x::StateWrite(u32 offset, u32 arguments) { | 13 | void Tegra::Host1x::ProcessMethod(Method method, u32 argument) { |
| 14 | u8* const state_offset = reinterpret_cast<u8*>(&state) + offset * sizeof(u32); | ||
| 15 | std::memcpy(state_offset, &arguments, sizeof(u32)); | ||
| 16 | } | ||
| 17 | |||
| 18 | void Tegra::Host1x::ProcessMethod(Method method, const std::vector<u32>& arguments) { | ||
| 19 | StateWrite(static_cast<u32>(method), arguments[0]); | ||
| 20 | switch (method) { | 14 | switch (method) { |
| 21 | case Method::WaitSyncpt: | ||
| 22 | Execute(arguments[0]); | ||
| 23 | break; | ||
| 24 | case Method::LoadSyncptPayload32: | 15 | case Method::LoadSyncptPayload32: |
| 25 | syncpoint_value = arguments[0]; | 16 | syncpoint_value = argument; |
| 26 | break; | 17 | break; |
| 18 | case Method::WaitSyncpt: | ||
| 27 | case Method::WaitSyncpt32: | 19 | case Method::WaitSyncpt32: |
| 28 | Execute(arguments[0]); | 20 | Execute(argument); |
| 29 | break; | 21 | break; |
| 30 | default: | 22 | default: |
| 31 | UNIMPLEMENTED_MSG("Host1x method 0x{:X}", static_cast<u32>(method)); | 23 | UNIMPLEMENTED_MSG("Host1x method 0x{:X}", static_cast<u32>(method)); |
| @@ -34,6 +26,5 @@ void Tegra::Host1x::ProcessMethod(Method method, const std::vector<u32>& argumen | |||
| 34 | } | 26 | } |
| 35 | 27 | ||
| 36 | void Tegra::Host1x::Execute(u32 data) { | 28 | void Tegra::Host1x::Execute(u32 data) { |
| 37 | // This method waits on a valid syncpoint. | 29 | gpu.WaitFence(data, syncpoint_value); |
| 38 | // TODO: Implement when proper Async is in place | ||
| 39 | } | 30 | } |
diff --git a/src/video_core/command_classes/host1x.h b/src/video_core/command_classes/host1x.h index 013eaa0c1..7e94799dd 100644 --- a/src/video_core/command_classes/host1x.h +++ b/src/video_core/command_classes/host1x.h | |||
| @@ -14,64 +14,23 @@ class Nvdec; | |||
| 14 | 14 | ||
| 15 | class Host1x { | 15 | class Host1x { |
| 16 | public: | 16 | public: |
| 17 | struct Host1xClassRegisters { | ||
| 18 | u32 incr_syncpt{}; | ||
| 19 | u32 incr_syncpt_ctrl{}; | ||
| 20 | u32 incr_syncpt_error{}; | ||
| 21 | INSERT_PADDING_WORDS(5); | ||
| 22 | u32 wait_syncpt{}; | ||
| 23 | u32 wait_syncpt_base{}; | ||
| 24 | u32 wait_syncpt_incr{}; | ||
| 25 | u32 load_syncpt_base{}; | ||
| 26 | u32 incr_syncpt_base{}; | ||
| 27 | u32 clear{}; | ||
| 28 | u32 wait{}; | ||
| 29 | u32 wait_with_interrupt{}; | ||
| 30 | u32 delay_use{}; | ||
| 31 | u32 tick_count_high{}; | ||
| 32 | u32 tick_count_low{}; | ||
| 33 | u32 tick_ctrl{}; | ||
| 34 | INSERT_PADDING_WORDS(23); | ||
| 35 | u32 ind_ctrl{}; | ||
| 36 | u32 ind_off2{}; | ||
| 37 | u32 ind_off{}; | ||
| 38 | std::array<u32, 31> ind_data{}; | ||
| 39 | INSERT_PADDING_WORDS(1); | ||
| 40 | u32 load_syncpoint_payload32{}; | ||
| 41 | u32 stall_ctrl{}; | ||
| 42 | u32 wait_syncpt32{}; | ||
| 43 | u32 wait_syncpt_base32{}; | ||
| 44 | u32 load_syncpt_base32{}; | ||
| 45 | u32 incr_syncpt_base32{}; | ||
| 46 | u32 stall_count_high{}; | ||
| 47 | u32 stall_count_low{}; | ||
| 48 | u32 xref_ctrl{}; | ||
| 49 | u32 channel_xref_high{}; | ||
| 50 | u32 channel_xref_low{}; | ||
| 51 | }; | ||
| 52 | static_assert(sizeof(Host1xClassRegisters) == 0x164, "Host1xClassRegisters is an invalid size"); | ||
| 53 | |||
| 54 | enum class Method : u32 { | 17 | enum class Method : u32 { |
| 55 | WaitSyncpt = offsetof(Host1xClassRegisters, wait_syncpt) / 4, | 18 | WaitSyncpt = 0x8, |
| 56 | LoadSyncptPayload32 = offsetof(Host1xClassRegisters, load_syncpoint_payload32) / 4, | 19 | LoadSyncptPayload32 = 0x4e, |
| 57 | WaitSyncpt32 = offsetof(Host1xClassRegisters, wait_syncpt32) / 4, | 20 | WaitSyncpt32 = 0x50, |
| 58 | }; | 21 | }; |
| 59 | 22 | ||
| 60 | explicit Host1x(GPU& gpu); | 23 | explicit Host1x(GPU& gpu); |
| 61 | ~Host1x(); | 24 | ~Host1x(); |
| 62 | 25 | ||
| 63 | /// Writes the method into the state, Invoke Execute() if encountered | 26 | /// Writes the method into the state, Invoke Execute() if encountered |
| 64 | void ProcessMethod(Method method, const std::vector<u32>& arguments); | 27 | void ProcessMethod(Method method, u32 argument); |
| 65 | 28 | ||
| 66 | private: | 29 | private: |
| 67 | /// For Host1x, execute is waiting on a syncpoint previously written into the state | 30 | /// For Host1x, execute is waiting on a syncpoint previously written into the state |
| 68 | void Execute(u32 data); | 31 | void Execute(u32 data); |
| 69 | 32 | ||
| 70 | /// Write argument into the provided offset | ||
| 71 | void StateWrite(u32 offset, u32 arguments); | ||
| 72 | |||
| 73 | u32 syncpoint_value{}; | 33 | u32 syncpoint_value{}; |
| 74 | Host1xClassRegisters state{}; | ||
| 75 | GPU& gpu; | 34 | GPU& gpu; |
| 76 | }; | 35 | }; |
| 77 | 36 | ||
diff --git a/src/video_core/command_classes/vic.cpp b/src/video_core/command_classes/vic.cpp index aa8c9f9de..55e632346 100644 --- a/src/video_core/command_classes/vic.cpp +++ b/src/video_core/command_classes/vic.cpp | |||
| @@ -53,7 +53,7 @@ void Vic::ProcessMethod(Method method, const std::vector<u32>& arguments) { | |||
| 53 | 53 | ||
| 54 | void Vic::Execute() { | 54 | void Vic::Execute() { |
| 55 | if (output_surface_luma_address == 0) { | 55 | if (output_surface_luma_address == 0) { |
| 56 | LOG_ERROR(Service_NVDRV, "VIC Luma address not set. Recieved 0x{:X}", | 56 | LOG_ERROR(Service_NVDRV, "VIC Luma address not set. Received 0x{:X}", |
| 57 | vic_state.output_surface.luma_offset); | 57 | vic_state.output_surface.luma_offset); |
| 58 | return; | 58 | return; |
| 59 | } | 59 | } |
diff --git a/src/video_core/compatible_formats.cpp b/src/video_core/compatible_formats.cpp index 1619d8664..acf2668dc 100644 --- a/src/video_core/compatible_formats.cpp +++ b/src/video_core/compatible_formats.cpp | |||
| @@ -10,9 +10,7 @@ | |||
| 10 | #include "video_core/surface.h" | 10 | #include "video_core/surface.h" |
| 11 | 11 | ||
| 12 | namespace VideoCore::Surface { | 12 | namespace VideoCore::Surface { |
| 13 | |||
| 14 | namespace { | 13 | namespace { |
| 15 | |||
| 16 | using Table = std::array<std::array<u64, 2>, MaxPixelFormat>; | 14 | using Table = std::array<std::array<u64, 2>, MaxPixelFormat>; |
| 17 | 15 | ||
| 18 | // Compatibility table taken from Table 3.X.2 in: | 16 | // Compatibility table taken from Table 3.X.2 in: |
| @@ -233,10 +231,13 @@ constexpr Table MakeCopyTable() { | |||
| 233 | EnableRange(copy, COPY_CLASS_64_BITS); | 231 | EnableRange(copy, COPY_CLASS_64_BITS); |
| 234 | return copy; | 232 | return copy; |
| 235 | } | 233 | } |
| 236 | |||
| 237 | } // Anonymous namespace | 234 | } // Anonymous namespace |
| 238 | 235 | ||
| 239 | bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b) { | 236 | bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views) { |
| 237 | if (broken_views) { | ||
| 238 | // If format views are broken, only accept formats that are identical. | ||
| 239 | return format_a == format_b; | ||
| 240 | } | ||
| 240 | static constexpr Table TABLE = MakeViewTable(); | 241 | static constexpr Table TABLE = MakeViewTable(); |
| 241 | return IsSupported(TABLE, format_a, format_b); | 242 | return IsSupported(TABLE, format_a, format_b); |
| 242 | } | 243 | } |
diff --git a/src/video_core/compatible_formats.h b/src/video_core/compatible_formats.h index b5eb03bea..9a0522988 100644 --- a/src/video_core/compatible_formats.h +++ b/src/video_core/compatible_formats.h | |||
| @@ -8,7 +8,7 @@ | |||
| 8 | 8 | ||
| 9 | namespace VideoCore::Surface { | 9 | namespace VideoCore::Surface { |
| 10 | 10 | ||
| 11 | bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b); | 11 | bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views); |
| 12 | 12 | ||
| 13 | bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b); | 13 | bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b); |
| 14 | 14 | ||
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h index 81522988e..0de3280a2 100644 --- a/src/video_core/engines/fermi_2d.h +++ b/src/video_core/engines/fermi_2d.h | |||
| @@ -171,30 +171,30 @@ public: | |||
| 171 | static constexpr std::size_t NUM_REGS = 0x258; | 171 | static constexpr std::size_t NUM_REGS = 0x258; |
| 172 | struct { | 172 | struct { |
| 173 | u32 object; | 173 | u32 object; |
| 174 | INSERT_UNION_PADDING_WORDS(0x3F); | 174 | INSERT_PADDING_WORDS_NOINIT(0x3F); |
| 175 | u32 no_operation; | 175 | u32 no_operation; |
| 176 | NotifyType notify; | 176 | NotifyType notify; |
| 177 | INSERT_UNION_PADDING_WORDS(0x2); | 177 | INSERT_PADDING_WORDS_NOINIT(0x2); |
| 178 | u32 wait_for_idle; | 178 | u32 wait_for_idle; |
| 179 | INSERT_UNION_PADDING_WORDS(0xB); | 179 | INSERT_PADDING_WORDS_NOINIT(0xB); |
| 180 | u32 pm_trigger; | 180 | u32 pm_trigger; |
| 181 | INSERT_UNION_PADDING_WORDS(0xF); | 181 | INSERT_PADDING_WORDS_NOINIT(0xF); |
| 182 | u32 context_dma_notify; | 182 | u32 context_dma_notify; |
| 183 | u32 dst_context_dma; | 183 | u32 dst_context_dma; |
| 184 | u32 src_context_dma; | 184 | u32 src_context_dma; |
| 185 | u32 semaphore_context_dma; | 185 | u32 semaphore_context_dma; |
| 186 | INSERT_UNION_PADDING_WORDS(0x1C); | 186 | INSERT_PADDING_WORDS_NOINIT(0x1C); |
| 187 | Surface dst; | 187 | Surface dst; |
| 188 | CpuIndexWrap pixels_from_cpu_index_wrap; | 188 | CpuIndexWrap pixels_from_cpu_index_wrap; |
| 189 | u32 kind2d_check_enable; | 189 | u32 kind2d_check_enable; |
| 190 | Surface src; | 190 | Surface src; |
| 191 | SectorPromotion pixels_from_memory_sector_promotion; | 191 | SectorPromotion pixels_from_memory_sector_promotion; |
| 192 | INSERT_UNION_PADDING_WORDS(0x1); | 192 | INSERT_PADDING_WORDS_NOINIT(0x1); |
| 193 | NumTpcs num_tpcs; | 193 | NumTpcs num_tpcs; |
| 194 | u32 render_enable_addr_upper; | 194 | u32 render_enable_addr_upper; |
| 195 | u32 render_enable_addr_lower; | 195 | u32 render_enable_addr_lower; |
| 196 | RenderEnableMode render_enable_mode; | 196 | RenderEnableMode render_enable_mode; |
| 197 | INSERT_UNION_PADDING_WORDS(0x4); | 197 | INSERT_PADDING_WORDS_NOINIT(0x4); |
| 198 | u32 clip_x0; | 198 | u32 clip_x0; |
| 199 | u32 clip_y0; | 199 | u32 clip_y0; |
| 200 | u32 clip_width; | 200 | u32 clip_width; |
| @@ -212,7 +212,7 @@ public: | |||
| 212 | BitField<8, 6, u32> y; | 212 | BitField<8, 6, u32> y; |
| 213 | } pattern_offset; | 213 | } pattern_offset; |
| 214 | BitField<0, 2, PatternSelect> pattern_select; | 214 | BitField<0, 2, PatternSelect> pattern_select; |
| 215 | INSERT_UNION_PADDING_WORDS(0xC); | 215 | INSERT_PADDING_WORDS_NOINIT(0xC); |
| 216 | struct { | 216 | struct { |
| 217 | BitField<0, 3, MonochromePatternColorFormat> color_format; | 217 | BitField<0, 3, MonochromePatternColorFormat> color_format; |
| 218 | BitField<0, 1, MonochromePatternFormat> format; | 218 | BitField<0, 1, MonochromePatternFormat> format; |
| @@ -227,15 +227,15 @@ public: | |||
| 227 | std::array<u32, 0x20> X1R5G5B5; | 227 | std::array<u32, 0x20> X1R5G5B5; |
| 228 | std::array<u32, 0x10> Y8; | 228 | std::array<u32, 0x10> Y8; |
| 229 | } color_pattern; | 229 | } color_pattern; |
| 230 | INSERT_UNION_PADDING_WORDS(0x10); | 230 | INSERT_PADDING_WORDS_NOINIT(0x10); |
| 231 | struct { | 231 | struct { |
| 232 | u32 prim_mode; | 232 | u32 prim_mode; |
| 233 | u32 prim_color_format; | 233 | u32 prim_color_format; |
| 234 | u32 prim_color; | 234 | u32 prim_color; |
| 235 | u32 line_tie_break_bits; | 235 | u32 line_tie_break_bits; |
| 236 | INSERT_UNION_PADDING_WORDS(0x14); | 236 | INSERT_PADDING_WORDS_NOINIT(0x14); |
| 237 | u32 prim_point_xy; | 237 | u32 prim_point_xy; |
| 238 | INSERT_UNION_PADDING_WORDS(0x7); | 238 | INSERT_PADDING_WORDS_NOINIT(0x7); |
| 239 | std::array<Point, 0x40> prim_point; | 239 | std::array<Point, 0x40> prim_point; |
| 240 | } render_solid; | 240 | } render_solid; |
| 241 | struct { | 241 | struct { |
| @@ -247,7 +247,7 @@ public: | |||
| 247 | u32 color0; | 247 | u32 color0; |
| 248 | u32 color1; | 248 | u32 color1; |
| 249 | u32 mono_opacity; | 249 | u32 mono_opacity; |
| 250 | INSERT_UNION_PADDING_WORDS(0x6); | 250 | INSERT_PADDING_WORDS_NOINIT(0x6); |
| 251 | u32 src_width; | 251 | u32 src_width; |
| 252 | u32 src_height; | 252 | u32 src_height; |
| 253 | u32 dx_du_frac; | 253 | u32 dx_du_frac; |
| @@ -260,9 +260,9 @@ public: | |||
| 260 | u32 dst_y0_int; | 260 | u32 dst_y0_int; |
| 261 | u32 data; | 261 | u32 data; |
| 262 | } pixels_from_cpu; | 262 | } pixels_from_cpu; |
| 263 | INSERT_UNION_PADDING_WORDS(0x3); | 263 | INSERT_PADDING_WORDS_NOINIT(0x3); |
| 264 | u32 big_endian_control; | 264 | u32 big_endian_control; |
| 265 | INSERT_UNION_PADDING_WORDS(0x3); | 265 | INSERT_PADDING_WORDS_NOINIT(0x3); |
| 266 | struct { | 266 | struct { |
| 267 | BitField<0, 3, u32> block_shape; | 267 | BitField<0, 3, u32> block_shape; |
| 268 | BitField<0, 5, u32> corral_size; | 268 | BitField<0, 5, u32> corral_size; |
| @@ -271,7 +271,7 @@ public: | |||
| 271 | BitField<0, 1, Origin> origin; | 271 | BitField<0, 1, Origin> origin; |
| 272 | BitField<4, 1, Filter> filter; | 272 | BitField<4, 1, Filter> filter; |
| 273 | } sample_mode; | 273 | } sample_mode; |
| 274 | INSERT_UNION_PADDING_WORDS(0x8); | 274 | INSERT_PADDING_WORDS_NOINIT(0x8); |
| 275 | s32 dst_x0; | 275 | s32 dst_x0; |
| 276 | s32 dst_y0; | 276 | s32 dst_y0; |
| 277 | s32 dst_width; | 277 | s32 dst_width; |
diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h index 51a041202..9f0a7b76d 100644 --- a/src/video_core/engines/kepler_compute.h +++ b/src/video_core/engines/kepler_compute.h | |||
| @@ -55,7 +55,7 @@ public: | |||
| 55 | 55 | ||
| 56 | union { | 56 | union { |
| 57 | struct { | 57 | struct { |
| 58 | INSERT_UNION_PADDING_WORDS(0x60); | 58 | INSERT_PADDING_WORDS_NOINIT(0x60); |
| 59 | 59 | ||
| 60 | Upload::Registers upload; | 60 | Upload::Registers upload; |
| 61 | 61 | ||
| @@ -67,7 +67,7 @@ public: | |||
| 67 | 67 | ||
| 68 | u32 data_upload; | 68 | u32 data_upload; |
| 69 | 69 | ||
| 70 | INSERT_UNION_PADDING_WORDS(0x3F); | 70 | INSERT_PADDING_WORDS_NOINIT(0x3F); |
| 71 | 71 | ||
| 72 | struct { | 72 | struct { |
| 73 | u32 address; | 73 | u32 address; |
| @@ -76,11 +76,11 @@ public: | |||
| 76 | } | 76 | } |
| 77 | } launch_desc_loc; | 77 | } launch_desc_loc; |
| 78 | 78 | ||
| 79 | INSERT_UNION_PADDING_WORDS(0x1); | 79 | INSERT_PADDING_WORDS_NOINIT(0x1); |
| 80 | 80 | ||
| 81 | u32 launch; | 81 | u32 launch; |
| 82 | 82 | ||
| 83 | INSERT_UNION_PADDING_WORDS(0x4A7); | 83 | INSERT_PADDING_WORDS_NOINIT(0x4A7); |
| 84 | 84 | ||
| 85 | struct { | 85 | struct { |
| 86 | u32 address_high; | 86 | u32 address_high; |
| @@ -92,7 +92,7 @@ public: | |||
| 92 | } | 92 | } |
| 93 | } tsc; | 93 | } tsc; |
| 94 | 94 | ||
| 95 | INSERT_UNION_PADDING_WORDS(0x3); | 95 | INSERT_PADDING_WORDS_NOINIT(0x3); |
| 96 | 96 | ||
| 97 | struct { | 97 | struct { |
| 98 | u32 address_high; | 98 | u32 address_high; |
| @@ -104,7 +104,7 @@ public: | |||
| 104 | } | 104 | } |
| 105 | } tic; | 105 | } tic; |
| 106 | 106 | ||
| 107 | INSERT_UNION_PADDING_WORDS(0x22); | 107 | INSERT_PADDING_WORDS_NOINIT(0x22); |
| 108 | 108 | ||
| 109 | struct { | 109 | struct { |
| 110 | u32 address_high; | 110 | u32 address_high; |
| @@ -115,11 +115,11 @@ public: | |||
| 115 | } | 115 | } |
| 116 | } code_loc; | 116 | } code_loc; |
| 117 | 117 | ||
| 118 | INSERT_UNION_PADDING_WORDS(0x3FE); | 118 | INSERT_PADDING_WORDS_NOINIT(0x3FE); |
| 119 | 119 | ||
| 120 | u32 tex_cb_index; | 120 | u32 tex_cb_index; |
| 121 | 121 | ||
| 122 | INSERT_UNION_PADDING_WORDS(0x374); | 122 | INSERT_PADDING_WORDS_NOINIT(0x374); |
| 123 | }; | 123 | }; |
| 124 | std::array<u32, NUM_REGS> reg_array; | 124 | std::array<u32, NUM_REGS> reg_array; |
| 125 | }; | 125 | }; |
diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h index 62483589e..19808a5c6 100644 --- a/src/video_core/engines/kepler_memory.h +++ b/src/video_core/engines/kepler_memory.h | |||
| @@ -50,7 +50,7 @@ public: | |||
| 50 | 50 | ||
| 51 | union { | 51 | union { |
| 52 | struct { | 52 | struct { |
| 53 | INSERT_UNION_PADDING_WORDS(0x60); | 53 | INSERT_PADDING_WORDS_NOINIT(0x60); |
| 54 | 54 | ||
| 55 | Upload::Registers upload; | 55 | Upload::Registers upload; |
| 56 | 56 | ||
| @@ -62,7 +62,7 @@ public: | |||
| 62 | 62 | ||
| 63 | u32 data; | 63 | u32 data; |
| 64 | 64 | ||
| 65 | INSERT_UNION_PADDING_WORDS(0x11); | 65 | INSERT_PADDING_WORDS_NOINIT(0x11); |
| 66 | }; | 66 | }; |
| 67 | std::array<u32, NUM_REGS> reg_array; | 67 | std::array<u32, NUM_REGS> reg_array; |
| 68 | }; | 68 | }; |
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index bf9e07c9b..326b32228 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h | |||
| @@ -536,7 +536,7 @@ public: | |||
| 536 | Equation equation_a; | 536 | Equation equation_a; |
| 537 | Factor factor_source_a; | 537 | Factor factor_source_a; |
| 538 | Factor factor_dest_a; | 538 | Factor factor_dest_a; |
| 539 | INSERT_UNION_PADDING_WORDS(1); | 539 | INSERT_PADDING_WORDS_NOINIT(1); |
| 540 | }; | 540 | }; |
| 541 | 541 | ||
| 542 | enum class TessellationPrimitive : u32 { | 542 | enum class TessellationPrimitive : u32 { |
| @@ -608,7 +608,7 @@ public: | |||
| 608 | }; | 608 | }; |
| 609 | u32 layer_stride; | 609 | u32 layer_stride; |
| 610 | u32 base_layer; | 610 | u32 base_layer; |
| 611 | INSERT_UNION_PADDING_WORDS(7); | 611 | INSERT_PADDING_WORDS_NOINIT(7); |
| 612 | 612 | ||
| 613 | GPUVAddr Address() const { | 613 | GPUVAddr Address() const { |
| 614 | return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | | 614 | return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | |
| @@ -640,7 +640,7 @@ public: | |||
| 640 | BitField<8, 3, ViewportSwizzle> z; | 640 | BitField<8, 3, ViewportSwizzle> z; |
| 641 | BitField<12, 3, ViewportSwizzle> w; | 641 | BitField<12, 3, ViewportSwizzle> w; |
| 642 | } swizzle; | 642 | } swizzle; |
| 643 | INSERT_UNION_PADDING_WORDS(1); | 643 | INSERT_PADDING_WORDS_NOINIT(1); |
| 644 | 644 | ||
| 645 | Common::Rectangle<f32> GetRect() const { | 645 | Common::Rectangle<f32> GetRect() const { |
| 646 | return { | 646 | return { |
| @@ -700,7 +700,7 @@ public: | |||
| 700 | u32 address_low; | 700 | u32 address_low; |
| 701 | s32 buffer_size; | 701 | s32 buffer_size; |
| 702 | s32 buffer_offset; | 702 | s32 buffer_offset; |
| 703 | INSERT_UNION_PADDING_WORDS(3); | 703 | INSERT_PADDING_WORDS_NOINIT(3); |
| 704 | 704 | ||
| 705 | GPUVAddr Address() const { | 705 | GPUVAddr Address() const { |
| 706 | return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | | 706 | return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | |
| @@ -713,7 +713,7 @@ public: | |||
| 713 | u32 stream; | 713 | u32 stream; |
| 714 | u32 varying_count; | 714 | u32 varying_count; |
| 715 | u32 stride; | 715 | u32 stride; |
| 716 | INSERT_UNION_PADDING_WORDS(1); | 716 | INSERT_PADDING_WORDS_NOINIT(1); |
| 717 | }; | 717 | }; |
| 718 | static_assert(sizeof(TransformFeedbackLayout) == 16); | 718 | static_assert(sizeof(TransformFeedbackLayout) == 16); |
| 719 | 719 | ||
| @@ -731,7 +731,7 @@ public: | |||
| 731 | 731 | ||
| 732 | union { | 732 | union { |
| 733 | struct { | 733 | struct { |
| 734 | INSERT_UNION_PADDING_WORDS(0x44); | 734 | INSERT_PADDING_WORDS_NOINIT(0x44); |
| 735 | 735 | ||
| 736 | u32 wait_for_idle; | 736 | u32 wait_for_idle; |
| 737 | 737 | ||
| @@ -744,7 +744,7 @@ public: | |||
| 744 | 744 | ||
| 745 | ShadowRamControl shadow_ram_control; | 745 | ShadowRamControl shadow_ram_control; |
| 746 | 746 | ||
| 747 | INSERT_UNION_PADDING_WORDS(0x16); | 747 | INSERT_PADDING_WORDS_NOINIT(0x16); |
| 748 | 748 | ||
| 749 | Upload::Registers upload; | 749 | Upload::Registers upload; |
| 750 | struct { | 750 | struct { |
| @@ -755,11 +755,11 @@ public: | |||
| 755 | 755 | ||
| 756 | u32 data_upload; | 756 | u32 data_upload; |
| 757 | 757 | ||
| 758 | INSERT_UNION_PADDING_WORDS(0x16); | 758 | INSERT_PADDING_WORDS_NOINIT(0x16); |
| 759 | 759 | ||
| 760 | u32 force_early_fragment_tests; | 760 | u32 force_early_fragment_tests; |
| 761 | 761 | ||
| 762 | INSERT_UNION_PADDING_WORDS(0x2D); | 762 | INSERT_PADDING_WORDS_NOINIT(0x2D); |
| 763 | 763 | ||
| 764 | struct { | 764 | struct { |
| 765 | union { | 765 | union { |
| @@ -769,7 +769,7 @@ public: | |||
| 769 | }; | 769 | }; |
| 770 | } sync_info; | 770 | } sync_info; |
| 771 | 771 | ||
| 772 | INSERT_UNION_PADDING_WORDS(0x15); | 772 | INSERT_PADDING_WORDS_NOINIT(0x15); |
| 773 | 773 | ||
| 774 | union { | 774 | union { |
| 775 | BitField<0, 2, TessellationPrimitive> prim; | 775 | BitField<0, 2, TessellationPrimitive> prim; |
| @@ -781,21 +781,21 @@ public: | |||
| 781 | std::array<f32, 4> tess_level_outer; | 781 | std::array<f32, 4> tess_level_outer; |
| 782 | std::array<f32, 2> tess_level_inner; | 782 | std::array<f32, 2> tess_level_inner; |
| 783 | 783 | ||
| 784 | INSERT_UNION_PADDING_WORDS(0x10); | 784 | INSERT_PADDING_WORDS_NOINIT(0x10); |
| 785 | 785 | ||
| 786 | u32 rasterize_enable; | 786 | u32 rasterize_enable; |
| 787 | 787 | ||
| 788 | std::array<TransformFeedbackBinding, NumTransformFeedbackBuffers> tfb_bindings; | 788 | std::array<TransformFeedbackBinding, NumTransformFeedbackBuffers> tfb_bindings; |
| 789 | 789 | ||
| 790 | INSERT_UNION_PADDING_WORDS(0xC0); | 790 | INSERT_PADDING_WORDS_NOINIT(0xC0); |
| 791 | 791 | ||
| 792 | std::array<TransformFeedbackLayout, NumTransformFeedbackBuffers> tfb_layouts; | 792 | std::array<TransformFeedbackLayout, NumTransformFeedbackBuffers> tfb_layouts; |
| 793 | 793 | ||
| 794 | INSERT_UNION_PADDING_WORDS(0x1); | 794 | INSERT_PADDING_WORDS_NOINIT(0x1); |
| 795 | 795 | ||
| 796 | u32 tfb_enabled; | 796 | u32 tfb_enabled; |
| 797 | 797 | ||
| 798 | INSERT_UNION_PADDING_WORDS(0x2E); | 798 | INSERT_PADDING_WORDS_NOINIT(0x2E); |
| 799 | 799 | ||
| 800 | std::array<RenderTargetConfig, NumRenderTargets> rt; | 800 | std::array<RenderTargetConfig, NumRenderTargets> rt; |
| 801 | 801 | ||
| @@ -803,7 +803,7 @@ public: | |||
| 803 | 803 | ||
| 804 | std::array<ViewPort, NumViewports> viewports; | 804 | std::array<ViewPort, NumViewports> viewports; |
| 805 | 805 | ||
| 806 | INSERT_UNION_PADDING_WORDS(0x1D); | 806 | INSERT_PADDING_WORDS_NOINIT(0x1D); |
| 807 | 807 | ||
| 808 | struct { | 808 | struct { |
| 809 | u32 first; | 809 | u32 first; |
| @@ -815,16 +815,16 @@ public: | |||
| 815 | float clear_color[4]; | 815 | float clear_color[4]; |
| 816 | float clear_depth; | 816 | float clear_depth; |
| 817 | 817 | ||
| 818 | INSERT_UNION_PADDING_WORDS(0x3); | 818 | INSERT_PADDING_WORDS_NOINIT(0x3); |
| 819 | 819 | ||
| 820 | s32 clear_stencil; | 820 | s32 clear_stencil; |
| 821 | 821 | ||
| 822 | INSERT_UNION_PADDING_WORDS(0x2); | 822 | INSERT_PADDING_WORDS_NOINIT(0x2); |
| 823 | 823 | ||
| 824 | PolygonMode polygon_mode_front; | 824 | PolygonMode polygon_mode_front; |
| 825 | PolygonMode polygon_mode_back; | 825 | PolygonMode polygon_mode_back; |
| 826 | 826 | ||
| 827 | INSERT_UNION_PADDING_WORDS(0x3); | 827 | INSERT_PADDING_WORDS_NOINIT(0x3); |
| 828 | 828 | ||
| 829 | u32 polygon_offset_point_enable; | 829 | u32 polygon_offset_point_enable; |
| 830 | u32 polygon_offset_line_enable; | 830 | u32 polygon_offset_line_enable; |
| @@ -832,47 +832,47 @@ public: | |||
| 832 | 832 | ||
| 833 | u32 patch_vertices; | 833 | u32 patch_vertices; |
| 834 | 834 | ||
| 835 | INSERT_UNION_PADDING_WORDS(0x4); | 835 | INSERT_PADDING_WORDS_NOINIT(0x4); |
| 836 | 836 | ||
| 837 | u32 fragment_barrier; | 837 | u32 fragment_barrier; |
| 838 | 838 | ||
| 839 | INSERT_UNION_PADDING_WORDS(0x7); | 839 | INSERT_PADDING_WORDS_NOINIT(0x7); |
| 840 | 840 | ||
| 841 | std::array<ScissorTest, NumViewports> scissor_test; | 841 | std::array<ScissorTest, NumViewports> scissor_test; |
| 842 | 842 | ||
| 843 | INSERT_UNION_PADDING_WORDS(0x15); | 843 | INSERT_PADDING_WORDS_NOINIT(0x15); |
| 844 | 844 | ||
| 845 | s32 stencil_back_func_ref; | 845 | s32 stencil_back_func_ref; |
| 846 | u32 stencil_back_mask; | 846 | u32 stencil_back_mask; |
| 847 | u32 stencil_back_func_mask; | 847 | u32 stencil_back_func_mask; |
| 848 | 848 | ||
| 849 | INSERT_UNION_PADDING_WORDS(0x5); | 849 | INSERT_PADDING_WORDS_NOINIT(0x5); |
| 850 | 850 | ||
| 851 | u32 invalidate_texture_data_cache; | 851 | u32 invalidate_texture_data_cache; |
| 852 | 852 | ||
| 853 | INSERT_UNION_PADDING_WORDS(0x1); | 853 | INSERT_PADDING_WORDS_NOINIT(0x1); |
| 854 | 854 | ||
| 855 | u32 tiled_cache_barrier; | 855 | u32 tiled_cache_barrier; |
| 856 | 856 | ||
| 857 | INSERT_UNION_PADDING_WORDS(0x4); | 857 | INSERT_PADDING_WORDS_NOINIT(0x4); |
| 858 | 858 | ||
| 859 | u32 color_mask_common; | 859 | u32 color_mask_common; |
| 860 | 860 | ||
| 861 | INSERT_UNION_PADDING_WORDS(0x2); | 861 | INSERT_PADDING_WORDS_NOINIT(0x2); |
| 862 | 862 | ||
| 863 | f32 depth_bounds[2]; | 863 | f32 depth_bounds[2]; |
| 864 | 864 | ||
| 865 | INSERT_UNION_PADDING_WORDS(0x2); | 865 | INSERT_PADDING_WORDS_NOINIT(0x2); |
| 866 | 866 | ||
| 867 | u32 rt_separate_frag_data; | 867 | u32 rt_separate_frag_data; |
| 868 | 868 | ||
| 869 | INSERT_UNION_PADDING_WORDS(0x1); | 869 | INSERT_PADDING_WORDS_NOINIT(0x1); |
| 870 | 870 | ||
| 871 | u32 multisample_raster_enable; | 871 | u32 multisample_raster_enable; |
| 872 | u32 multisample_raster_samples; | 872 | u32 multisample_raster_samples; |
| 873 | std::array<u32, 4> multisample_sample_mask; | 873 | std::array<u32, 4> multisample_sample_mask; |
| 874 | 874 | ||
| 875 | INSERT_UNION_PADDING_WORDS(0x5); | 875 | INSERT_PADDING_WORDS_NOINIT(0x5); |
| 876 | 876 | ||
| 877 | struct { | 877 | struct { |
| 878 | u32 address_high; | 878 | u32 address_high; |
| @@ -898,7 +898,7 @@ public: | |||
| 898 | }; | 898 | }; |
| 899 | } render_area; | 899 | } render_area; |
| 900 | 900 | ||
| 901 | INSERT_UNION_PADDING_WORDS(0x3F); | 901 | INSERT_PADDING_WORDS_NOINIT(0x3F); |
| 902 | 902 | ||
| 903 | union { | 903 | union { |
| 904 | BitField<0, 4, u32> stencil; | 904 | BitField<0, 4, u32> stencil; |
| @@ -907,24 +907,24 @@ public: | |||
| 907 | BitField<12, 4, u32> viewport; | 907 | BitField<12, 4, u32> viewport; |
| 908 | } clear_flags; | 908 | } clear_flags; |
| 909 | 909 | ||
| 910 | INSERT_UNION_PADDING_WORDS(0x10); | 910 | INSERT_PADDING_WORDS_NOINIT(0x10); |
| 911 | 911 | ||
| 912 | u32 fill_rectangle; | 912 | u32 fill_rectangle; |
| 913 | 913 | ||
| 914 | INSERT_UNION_PADDING_WORDS(0x8); | 914 | INSERT_PADDING_WORDS_NOINIT(0x8); |
| 915 | 915 | ||
| 916 | std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format; | 916 | std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format; |
| 917 | 917 | ||
| 918 | std::array<MsaaSampleLocation, 4> multisample_sample_locations; | 918 | std::array<MsaaSampleLocation, 4> multisample_sample_locations; |
| 919 | 919 | ||
| 920 | INSERT_UNION_PADDING_WORDS(0x2); | 920 | INSERT_PADDING_WORDS_NOINIT(0x2); |
| 921 | 921 | ||
| 922 | union { | 922 | union { |
| 923 | BitField<0, 1, u32> enable; | 923 | BitField<0, 1, u32> enable; |
| 924 | BitField<4, 3, u32> target; | 924 | BitField<4, 3, u32> target; |
| 925 | } multisample_coverage_to_color; | 925 | } multisample_coverage_to_color; |
| 926 | 926 | ||
| 927 | INSERT_UNION_PADDING_WORDS(0x8); | 927 | INSERT_PADDING_WORDS_NOINIT(0x8); |
| 928 | 928 | ||
| 929 | struct { | 929 | struct { |
| 930 | union { | 930 | union { |
| @@ -947,7 +947,7 @@ public: | |||
| 947 | } | 947 | } |
| 948 | } rt_control; | 948 | } rt_control; |
| 949 | 949 | ||
| 950 | INSERT_UNION_PADDING_WORDS(0x2); | 950 | INSERT_PADDING_WORDS_NOINIT(0x2); |
| 951 | 951 | ||
| 952 | u32 zeta_width; | 952 | u32 zeta_width; |
| 953 | u32 zeta_height; | 953 | u32 zeta_height; |
| @@ -958,11 +958,11 @@ public: | |||
| 958 | 958 | ||
| 959 | SamplerIndex sampler_index; | 959 | SamplerIndex sampler_index; |
| 960 | 960 | ||
| 961 | INSERT_UNION_PADDING_WORDS(0x25); | 961 | INSERT_PADDING_WORDS_NOINIT(0x25); |
| 962 | 962 | ||
| 963 | u32 depth_test_enable; | 963 | u32 depth_test_enable; |
| 964 | 964 | ||
| 965 | INSERT_UNION_PADDING_WORDS(0x5); | 965 | INSERT_PADDING_WORDS_NOINIT(0x5); |
| 966 | 966 | ||
| 967 | u32 independent_blend_enable; | 967 | u32 independent_blend_enable; |
| 968 | 968 | ||
| @@ -970,7 +970,7 @@ public: | |||
| 970 | 970 | ||
| 971 | u32 alpha_test_enabled; | 971 | u32 alpha_test_enabled; |
| 972 | 972 | ||
| 973 | INSERT_UNION_PADDING_WORDS(0x6); | 973 | INSERT_PADDING_WORDS_NOINIT(0x6); |
| 974 | 974 | ||
| 975 | u32 d3d_cull_mode; | 975 | u32 d3d_cull_mode; |
| 976 | 976 | ||
| @@ -985,7 +985,7 @@ public: | |||
| 985 | float a; | 985 | float a; |
| 986 | } blend_color; | 986 | } blend_color; |
| 987 | 987 | ||
| 988 | INSERT_UNION_PADDING_WORDS(0x4); | 988 | INSERT_PADDING_WORDS_NOINIT(0x4); |
| 989 | 989 | ||
| 990 | struct { | 990 | struct { |
| 991 | u32 separate_alpha; | 991 | u32 separate_alpha; |
| @@ -994,7 +994,7 @@ public: | |||
| 994 | Blend::Factor factor_dest_rgb; | 994 | Blend::Factor factor_dest_rgb; |
| 995 | Blend::Equation equation_a; | 995 | Blend::Equation equation_a; |
| 996 | Blend::Factor factor_source_a; | 996 | Blend::Factor factor_source_a; |
| 997 | INSERT_UNION_PADDING_WORDS(1); | 997 | INSERT_PADDING_WORDS_NOINIT(1); |
| 998 | Blend::Factor factor_dest_a; | 998 | Blend::Factor factor_dest_a; |
| 999 | 999 | ||
| 1000 | u32 enable_common; | 1000 | u32 enable_common; |
| @@ -1010,7 +1010,7 @@ public: | |||
| 1010 | u32 stencil_front_func_mask; | 1010 | u32 stencil_front_func_mask; |
| 1011 | u32 stencil_front_mask; | 1011 | u32 stencil_front_mask; |
| 1012 | 1012 | ||
| 1013 | INSERT_UNION_PADDING_WORDS(0x2); | 1013 | INSERT_PADDING_WORDS_NOINIT(0x2); |
| 1014 | 1014 | ||
| 1015 | u32 frag_color_clamp; | 1015 | u32 frag_color_clamp; |
| 1016 | 1016 | ||
| @@ -1022,17 +1022,17 @@ public: | |||
| 1022 | float line_width_smooth; | 1022 | float line_width_smooth; |
| 1023 | float line_width_aliased; | 1023 | float line_width_aliased; |
| 1024 | 1024 | ||
| 1025 | INSERT_UNION_PADDING_WORDS(0x1B); | 1025 | INSERT_PADDING_WORDS_NOINIT(0x1B); |
| 1026 | 1026 | ||
| 1027 | u32 invalidate_sampler_cache_no_wfi; | 1027 | u32 invalidate_sampler_cache_no_wfi; |
| 1028 | u32 invalidate_texture_header_cache_no_wfi; | 1028 | u32 invalidate_texture_header_cache_no_wfi; |
| 1029 | 1029 | ||
| 1030 | INSERT_UNION_PADDING_WORDS(0x2); | 1030 | INSERT_PADDING_WORDS_NOINIT(0x2); |
| 1031 | 1031 | ||
| 1032 | u32 vb_element_base; | 1032 | u32 vb_element_base; |
| 1033 | u32 vb_base_instance; | 1033 | u32 vb_base_instance; |
| 1034 | 1034 | ||
| 1035 | INSERT_UNION_PADDING_WORDS(0x35); | 1035 | INSERT_PADDING_WORDS_NOINIT(0x35); |
| 1036 | 1036 | ||
| 1037 | u32 clip_distance_enabled; | 1037 | u32 clip_distance_enabled; |
| 1038 | 1038 | ||
| @@ -1040,11 +1040,11 @@ public: | |||
| 1040 | 1040 | ||
| 1041 | float point_size; | 1041 | float point_size; |
| 1042 | 1042 | ||
| 1043 | INSERT_UNION_PADDING_WORDS(0x1); | 1043 | INSERT_PADDING_WORDS_NOINIT(0x1); |
| 1044 | 1044 | ||
| 1045 | u32 point_sprite_enable; | 1045 | u32 point_sprite_enable; |
| 1046 | 1046 | ||
| 1047 | INSERT_UNION_PADDING_WORDS(0x3); | 1047 | INSERT_PADDING_WORDS_NOINIT(0x3); |
| 1048 | 1048 | ||
| 1049 | CounterReset counter_reset; | 1049 | CounterReset counter_reset; |
| 1050 | 1050 | ||
| @@ -1057,7 +1057,7 @@ public: | |||
| 1057 | BitField<4, 1, u32> alpha_to_one; | 1057 | BitField<4, 1, u32> alpha_to_one; |
| 1058 | } multisample_control; | 1058 | } multisample_control; |
| 1059 | 1059 | ||
| 1060 | INSERT_UNION_PADDING_WORDS(0x4); | 1060 | INSERT_PADDING_WORDS_NOINIT(0x4); |
| 1061 | 1061 | ||
| 1062 | struct { | 1062 | struct { |
| 1063 | u32 address_high; | 1063 | u32 address_high; |
| @@ -1081,7 +1081,7 @@ public: | |||
| 1081 | } | 1081 | } |
| 1082 | } tsc; | 1082 | } tsc; |
| 1083 | 1083 | ||
| 1084 | INSERT_UNION_PADDING_WORDS(0x1); | 1084 | INSERT_PADDING_WORDS_NOINIT(0x1); |
| 1085 | 1085 | ||
| 1086 | float polygon_offset_factor; | 1086 | float polygon_offset_factor; |
| 1087 | 1087 | ||
| @@ -1098,7 +1098,7 @@ public: | |||
| 1098 | } | 1098 | } |
| 1099 | } tic; | 1099 | } tic; |
| 1100 | 1100 | ||
| 1101 | INSERT_UNION_PADDING_WORDS(0x5); | 1101 | INSERT_PADDING_WORDS_NOINIT(0x5); |
| 1102 | 1102 | ||
| 1103 | u32 stencil_two_side_enable; | 1103 | u32 stencil_two_side_enable; |
| 1104 | StencilOp stencil_back_op_fail; | 1104 | StencilOp stencil_back_op_fail; |
| @@ -1106,17 +1106,17 @@ public: | |||
| 1106 | StencilOp stencil_back_op_zpass; | 1106 | StencilOp stencil_back_op_zpass; |
| 1107 | ComparisonOp stencil_back_func_func; | 1107 | ComparisonOp stencil_back_func_func; |
| 1108 | 1108 | ||
| 1109 | INSERT_UNION_PADDING_WORDS(0x4); | 1109 | INSERT_PADDING_WORDS_NOINIT(0x4); |
| 1110 | 1110 | ||
| 1111 | u32 framebuffer_srgb; | 1111 | u32 framebuffer_srgb; |
| 1112 | 1112 | ||
| 1113 | float polygon_offset_units; | 1113 | float polygon_offset_units; |
| 1114 | 1114 | ||
| 1115 | INSERT_UNION_PADDING_WORDS(0x4); | 1115 | INSERT_PADDING_WORDS_NOINIT(0x4); |
| 1116 | 1116 | ||
| 1117 | Tegra::Texture::MsaaMode multisample_mode; | 1117 | Tegra::Texture::MsaaMode multisample_mode; |
| 1118 | 1118 | ||
| 1119 | INSERT_UNION_PADDING_WORDS(0xC); | 1119 | INSERT_PADDING_WORDS_NOINIT(0xC); |
| 1120 | 1120 | ||
| 1121 | union { | 1121 | union { |
| 1122 | BitField<2, 1, u32> coord_origin; | 1122 | BitField<2, 1, u32> coord_origin; |
| @@ -1132,7 +1132,7 @@ public: | |||
| 1132 | (static_cast<GPUVAddr>(code_address_high) << 32) | code_address_low); | 1132 | (static_cast<GPUVAddr>(code_address_high) << 32) | code_address_low); |
| 1133 | } | 1133 | } |
| 1134 | } code_address; | 1134 | } code_address; |
| 1135 | INSERT_UNION_PADDING_WORDS(1); | 1135 | INSERT_PADDING_WORDS_NOINIT(1); |
| 1136 | 1136 | ||
| 1137 | struct { | 1137 | struct { |
| 1138 | u32 vertex_end_gl; | 1138 | u32 vertex_end_gl; |
| @@ -1144,14 +1144,14 @@ public: | |||
| 1144 | }; | 1144 | }; |
| 1145 | } draw; | 1145 | } draw; |
| 1146 | 1146 | ||
| 1147 | INSERT_UNION_PADDING_WORDS(0xA); | 1147 | INSERT_PADDING_WORDS_NOINIT(0xA); |
| 1148 | 1148 | ||
| 1149 | struct { | 1149 | struct { |
| 1150 | u32 enabled; | 1150 | u32 enabled; |
| 1151 | u32 index; | 1151 | u32 index; |
| 1152 | } primitive_restart; | 1152 | } primitive_restart; |
| 1153 | 1153 | ||
| 1154 | INSERT_UNION_PADDING_WORDS(0x5F); | 1154 | INSERT_PADDING_WORDS_NOINIT(0x5F); |
| 1155 | 1155 | ||
| 1156 | struct { | 1156 | struct { |
| 1157 | u32 start_addr_high; | 1157 | u32 start_addr_high; |
| @@ -1192,9 +1192,9 @@ public: | |||
| 1192 | } | 1192 | } |
| 1193 | } index_array; | 1193 | } index_array; |
| 1194 | 1194 | ||
| 1195 | INSERT_UNION_PADDING_WORDS(0x7); | 1195 | INSERT_PADDING_WORDS_NOINIT(0x7); |
| 1196 | 1196 | ||
| 1197 | INSERT_UNION_PADDING_WORDS(0x1F); | 1197 | INSERT_PADDING_WORDS_NOINIT(0x1F); |
| 1198 | 1198 | ||
| 1199 | float polygon_offset_clamp; | 1199 | float polygon_offset_clamp; |
| 1200 | 1200 | ||
| @@ -1208,14 +1208,14 @@ public: | |||
| 1208 | } | 1208 | } |
| 1209 | } instanced_arrays; | 1209 | } instanced_arrays; |
| 1210 | 1210 | ||
| 1211 | INSERT_UNION_PADDING_WORDS(0x4); | 1211 | INSERT_PADDING_WORDS_NOINIT(0x4); |
| 1212 | 1212 | ||
| 1213 | union { | 1213 | union { |
| 1214 | BitField<0, 1, u32> enable; | 1214 | BitField<0, 1, u32> enable; |
| 1215 | BitField<4, 8, u32> unk4; | 1215 | BitField<4, 8, u32> unk4; |
| 1216 | } vp_point_size; | 1216 | } vp_point_size; |
| 1217 | 1217 | ||
| 1218 | INSERT_UNION_PADDING_WORDS(1); | 1218 | INSERT_PADDING_WORDS_NOINIT(1); |
| 1219 | 1219 | ||
| 1220 | u32 cull_test_enabled; | 1220 | u32 cull_test_enabled; |
| 1221 | FrontFace front_face; | 1221 | FrontFace front_face; |
| @@ -1223,11 +1223,11 @@ public: | |||
| 1223 | 1223 | ||
| 1224 | u32 pixel_center_integer; | 1224 | u32 pixel_center_integer; |
| 1225 | 1225 | ||
| 1226 | INSERT_UNION_PADDING_WORDS(0x1); | 1226 | INSERT_PADDING_WORDS_NOINIT(0x1); |
| 1227 | 1227 | ||
| 1228 | u32 viewport_transform_enabled; | 1228 | u32 viewport_transform_enabled; |
| 1229 | 1229 | ||
| 1230 | INSERT_UNION_PADDING_WORDS(0x3); | 1230 | INSERT_PADDING_WORDS_NOINIT(0x3); |
| 1231 | 1231 | ||
| 1232 | union { | 1232 | union { |
| 1233 | BitField<0, 1, u32> depth_range_0_1; | 1233 | BitField<0, 1, u32> depth_range_0_1; |
| @@ -1236,18 +1236,18 @@ public: | |||
| 1236 | BitField<11, 1, u32> depth_clamp_disabled; | 1236 | BitField<11, 1, u32> depth_clamp_disabled; |
| 1237 | } view_volume_clip_control; | 1237 | } view_volume_clip_control; |
| 1238 | 1238 | ||
| 1239 | INSERT_UNION_PADDING_WORDS(0x1F); | 1239 | INSERT_PADDING_WORDS_NOINIT(0x1F); |
| 1240 | 1240 | ||
| 1241 | u32 depth_bounds_enable; | 1241 | u32 depth_bounds_enable; |
| 1242 | 1242 | ||
| 1243 | INSERT_UNION_PADDING_WORDS(1); | 1243 | INSERT_PADDING_WORDS_NOINIT(1); |
| 1244 | 1244 | ||
| 1245 | struct { | 1245 | struct { |
| 1246 | u32 enable; | 1246 | u32 enable; |
| 1247 | LogicOperation operation; | 1247 | LogicOperation operation; |
| 1248 | } logic_op; | 1248 | } logic_op; |
| 1249 | 1249 | ||
| 1250 | INSERT_UNION_PADDING_WORDS(0x1); | 1250 | INSERT_PADDING_WORDS_NOINIT(0x1); |
| 1251 | 1251 | ||
| 1252 | union { | 1252 | union { |
| 1253 | u32 raw; | 1253 | u32 raw; |
| @@ -1260,9 +1260,9 @@ public: | |||
| 1260 | BitField<6, 4, u32> RT; | 1260 | BitField<6, 4, u32> RT; |
| 1261 | BitField<10, 11, u32> layer; | 1261 | BitField<10, 11, u32> layer; |
| 1262 | } clear_buffers; | 1262 | } clear_buffers; |
| 1263 | INSERT_UNION_PADDING_WORDS(0xB); | 1263 | INSERT_PADDING_WORDS_NOINIT(0xB); |
| 1264 | std::array<ColorMask, NumRenderTargets> color_mask; | 1264 | std::array<ColorMask, NumRenderTargets> color_mask; |
| 1265 | INSERT_UNION_PADDING_WORDS(0x38); | 1265 | INSERT_PADDING_WORDS_NOINIT(0x38); |
| 1266 | 1266 | ||
| 1267 | struct { | 1267 | struct { |
| 1268 | u32 query_address_high; | 1268 | u32 query_address_high; |
| @@ -1284,7 +1284,7 @@ public: | |||
| 1284 | } | 1284 | } |
| 1285 | } query; | 1285 | } query; |
| 1286 | 1286 | ||
| 1287 | INSERT_UNION_PADDING_WORDS(0x3C); | 1287 | INSERT_PADDING_WORDS_NOINIT(0x3C); |
| 1288 | 1288 | ||
| 1289 | struct { | 1289 | struct { |
| 1290 | union { | 1290 | union { |
| @@ -1325,10 +1325,10 @@ public: | |||
| 1325 | BitField<4, 4, ShaderProgram> program; | 1325 | BitField<4, 4, ShaderProgram> program; |
| 1326 | }; | 1326 | }; |
| 1327 | u32 offset; | 1327 | u32 offset; |
| 1328 | INSERT_UNION_PADDING_WORDS(14); | 1328 | INSERT_PADDING_WORDS_NOINIT(14); |
| 1329 | } shader_config[MaxShaderProgram]; | 1329 | } shader_config[MaxShaderProgram]; |
| 1330 | 1330 | ||
| 1331 | INSERT_UNION_PADDING_WORDS(0x60); | 1331 | INSERT_PADDING_WORDS_NOINIT(0x60); |
| 1332 | 1332 | ||
| 1333 | u32 firmware[0x20]; | 1333 | u32 firmware[0x20]; |
| 1334 | 1334 | ||
| @@ -1345,7 +1345,7 @@ public: | |||
| 1345 | } | 1345 | } |
| 1346 | } const_buffer; | 1346 | } const_buffer; |
| 1347 | 1347 | ||
| 1348 | INSERT_UNION_PADDING_WORDS(0x10); | 1348 | INSERT_PADDING_WORDS_NOINIT(0x10); |
| 1349 | 1349 | ||
| 1350 | struct { | 1350 | struct { |
| 1351 | union { | 1351 | union { |
| @@ -1353,18 +1353,18 @@ public: | |||
| 1353 | BitField<0, 1, u32> valid; | 1353 | BitField<0, 1, u32> valid; |
| 1354 | BitField<4, 5, u32> index; | 1354 | BitField<4, 5, u32> index; |
| 1355 | }; | 1355 | }; |
| 1356 | INSERT_UNION_PADDING_WORDS(7); | 1356 | INSERT_PADDING_WORDS_NOINIT(7); |
| 1357 | } cb_bind[MaxShaderStage]; | 1357 | } cb_bind[MaxShaderStage]; |
| 1358 | 1358 | ||
| 1359 | INSERT_UNION_PADDING_WORDS(0x56); | 1359 | INSERT_PADDING_WORDS_NOINIT(0x56); |
| 1360 | 1360 | ||
| 1361 | u32 tex_cb_index; | 1361 | u32 tex_cb_index; |
| 1362 | 1362 | ||
| 1363 | INSERT_UNION_PADDING_WORDS(0x7D); | 1363 | INSERT_PADDING_WORDS_NOINIT(0x7D); |
| 1364 | 1364 | ||
| 1365 | std::array<std::array<u8, 128>, NumTransformFeedbackBuffers> tfb_varying_locs; | 1365 | std::array<std::array<u8, 128>, NumTransformFeedbackBuffers> tfb_varying_locs; |
| 1366 | 1366 | ||
| 1367 | INSERT_UNION_PADDING_WORDS(0x298); | 1367 | INSERT_PADDING_WORDS_NOINIT(0x298); |
| 1368 | 1368 | ||
| 1369 | struct { | 1369 | struct { |
| 1370 | /// Compressed address of a buffer that holds information about bound SSBOs. | 1370 | /// Compressed address of a buffer that holds information about bound SSBOs. |
| @@ -1376,14 +1376,14 @@ public: | |||
| 1376 | } | 1376 | } |
| 1377 | } ssbo_info; | 1377 | } ssbo_info; |
| 1378 | 1378 | ||
| 1379 | INSERT_UNION_PADDING_WORDS(0x11); | 1379 | INSERT_PADDING_WORDS_NOINIT(0x11); |
| 1380 | 1380 | ||
| 1381 | struct { | 1381 | struct { |
| 1382 | u32 address[MaxShaderStage]; | 1382 | u32 address[MaxShaderStage]; |
| 1383 | u32 size[MaxShaderStage]; | 1383 | u32 size[MaxShaderStage]; |
| 1384 | } tex_info_buffers; | 1384 | } tex_info_buffers; |
| 1385 | 1385 | ||
| 1386 | INSERT_UNION_PADDING_WORDS(0xCC); | 1386 | INSERT_PADDING_WORDS_NOINIT(0xCC); |
| 1387 | }; | 1387 | }; |
| 1388 | std::array<u32, NUM_REGS> reg_array; | 1388 | std::array<u32, NUM_REGS> reg_array; |
| 1389 | }; | 1389 | }; |
diff --git a/src/video_core/engines/shader_header.h b/src/video_core/engines/shader_header.h index ceec05459..e0d7b89c5 100644 --- a/src/video_core/engines/shader_header.h +++ b/src/video_core/engines/shader_header.h | |||
| @@ -68,10 +68,10 @@ struct Header { | |||
| 68 | 68 | ||
| 69 | union { | 69 | union { |
| 70 | struct { | 70 | struct { |
| 71 | INSERT_UNION_PADDING_BYTES(3); // ImapSystemValuesA | 71 | INSERT_PADDING_BYTES_NOINIT(3); // ImapSystemValuesA |
| 72 | INSERT_UNION_PADDING_BYTES(1); // ImapSystemValuesB | 72 | INSERT_PADDING_BYTES_NOINIT(1); // ImapSystemValuesB |
| 73 | INSERT_UNION_PADDING_BYTES(16); // ImapGenericVector[32] | 73 | INSERT_PADDING_BYTES_NOINIT(16); // ImapGenericVector[32] |
| 74 | INSERT_UNION_PADDING_BYTES(2); // ImapColor | 74 | INSERT_PADDING_BYTES_NOINIT(2); // ImapColor |
| 75 | union { | 75 | union { |
| 76 | BitField<0, 8, u16> clip_distances; | 76 | BitField<0, 8, u16> clip_distances; |
| 77 | BitField<8, 1, u16> point_sprite_s; | 77 | BitField<8, 1, u16> point_sprite_s; |
| @@ -82,20 +82,20 @@ struct Header { | |||
| 82 | BitField<14, 1, u16> instance_id; | 82 | BitField<14, 1, u16> instance_id; |
| 83 | BitField<15, 1, u16> vertex_id; | 83 | BitField<15, 1, u16> vertex_id; |
| 84 | }; | 84 | }; |
| 85 | INSERT_UNION_PADDING_BYTES(5); // ImapFixedFncTexture[10] | 85 | INSERT_PADDING_BYTES_NOINIT(5); // ImapFixedFncTexture[10] |
| 86 | INSERT_UNION_PADDING_BYTES(1); // ImapReserved | 86 | INSERT_PADDING_BYTES_NOINIT(1); // ImapReserved |
| 87 | INSERT_UNION_PADDING_BYTES(3); // OmapSystemValuesA | 87 | INSERT_PADDING_BYTES_NOINIT(3); // OmapSystemValuesA |
| 88 | INSERT_UNION_PADDING_BYTES(1); // OmapSystemValuesB | 88 | INSERT_PADDING_BYTES_NOINIT(1); // OmapSystemValuesB |
| 89 | INSERT_UNION_PADDING_BYTES(16); // OmapGenericVector[32] | 89 | INSERT_PADDING_BYTES_NOINIT(16); // OmapGenericVector[32] |
| 90 | INSERT_UNION_PADDING_BYTES(2); // OmapColor | 90 | INSERT_PADDING_BYTES_NOINIT(2); // OmapColor |
| 91 | INSERT_UNION_PADDING_BYTES(2); // OmapSystemValuesC | 91 | INSERT_PADDING_BYTES_NOINIT(2); // OmapSystemValuesC |
| 92 | INSERT_UNION_PADDING_BYTES(5); // OmapFixedFncTexture[10] | 92 | INSERT_PADDING_BYTES_NOINIT(5); // OmapFixedFncTexture[10] |
| 93 | INSERT_UNION_PADDING_BYTES(1); // OmapReserved | 93 | INSERT_PADDING_BYTES_NOINIT(1); // OmapReserved |
| 94 | } vtg; | 94 | } vtg; |
| 95 | 95 | ||
| 96 | struct { | 96 | struct { |
| 97 | INSERT_UNION_PADDING_BYTES(3); // ImapSystemValuesA | 97 | INSERT_PADDING_BYTES_NOINIT(3); // ImapSystemValuesA |
| 98 | INSERT_UNION_PADDING_BYTES(1); // ImapSystemValuesB | 98 | INSERT_PADDING_BYTES_NOINIT(1); // ImapSystemValuesB |
| 99 | 99 | ||
| 100 | union { | 100 | union { |
| 101 | BitField<0, 2, PixelImap> x; | 101 | BitField<0, 2, PixelImap> x; |
| @@ -105,10 +105,10 @@ struct Header { | |||
| 105 | u8 raw; | 105 | u8 raw; |
| 106 | } imap_generic_vector[32]; | 106 | } imap_generic_vector[32]; |
| 107 | 107 | ||
| 108 | INSERT_UNION_PADDING_BYTES(2); // ImapColor | 108 | INSERT_PADDING_BYTES_NOINIT(2); // ImapColor |
| 109 | INSERT_UNION_PADDING_BYTES(2); // ImapSystemValuesC | 109 | INSERT_PADDING_BYTES_NOINIT(2); // ImapSystemValuesC |
| 110 | INSERT_UNION_PADDING_BYTES(10); // ImapFixedFncTexture[10] | 110 | INSERT_PADDING_BYTES_NOINIT(10); // ImapFixedFncTexture[10] |
| 111 | INSERT_UNION_PADDING_BYTES(2); // ImapReserved | 111 | INSERT_PADDING_BYTES_NOINIT(2); // ImapReserved |
| 112 | 112 | ||
| 113 | struct { | 113 | struct { |
| 114 | u32 target; | 114 | u32 target; |
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index d81e38680..b4ce6b154 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h | |||
| @@ -270,7 +270,7 @@ public: | |||
| 270 | 270 | ||
| 271 | union { | 271 | union { |
| 272 | struct { | 272 | struct { |
| 273 | INSERT_UNION_PADDING_WORDS(0x4); | 273 | INSERT_PADDING_WORDS_NOINIT(0x4); |
| 274 | struct { | 274 | struct { |
| 275 | u32 address_high; | 275 | u32 address_high; |
| 276 | u32 address_low; | 276 | u32 address_low; |
| @@ -283,18 +283,18 @@ public: | |||
| 283 | 283 | ||
| 284 | u32 semaphore_sequence; | 284 | u32 semaphore_sequence; |
| 285 | u32 semaphore_trigger; | 285 | u32 semaphore_trigger; |
| 286 | INSERT_UNION_PADDING_WORDS(0xC); | 286 | INSERT_PADDING_WORDS_NOINIT(0xC); |
| 287 | 287 | ||
| 288 | // The pusher and the puller share the reference counter, the pusher only has read | 288 | // The pusher and the puller share the reference counter, the pusher only has read |
| 289 | // access | 289 | // access |
| 290 | u32 reference_count; | 290 | u32 reference_count; |
| 291 | INSERT_UNION_PADDING_WORDS(0x5); | 291 | INSERT_PADDING_WORDS_NOINIT(0x5); |
| 292 | 292 | ||
| 293 | u32 semaphore_acquire; | 293 | u32 semaphore_acquire; |
| 294 | u32 semaphore_release; | 294 | u32 semaphore_release; |
| 295 | u32 fence_value; | 295 | u32 fence_value; |
| 296 | FenceAction fence_action; | 296 | FenceAction fence_action; |
| 297 | INSERT_UNION_PADDING_WORDS(0xE2); | 297 | INSERT_PADDING_WORDS_NOINIT(0xE2); |
| 298 | 298 | ||
| 299 | // Puller state | 299 | // Puller state |
| 300 | u32 acquire_mode; | 300 | u32 acquire_mode; |
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index b24179d59..81b71edfb 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp | |||
| @@ -208,6 +208,7 @@ Device::Device() | |||
| 208 | 208 | ||
| 209 | const bool is_nvidia = vendor == "NVIDIA Corporation"; | 209 | const bool is_nvidia = vendor == "NVIDIA Corporation"; |
| 210 | const bool is_amd = vendor == "ATI Technologies Inc."; | 210 | const bool is_amd = vendor == "ATI Technologies Inc."; |
| 211 | const bool is_intel = vendor == "Intel"; | ||
| 211 | 212 | ||
| 212 | bool disable_fast_buffer_sub_data = false; | 213 | bool disable_fast_buffer_sub_data = false; |
| 213 | if (is_nvidia && version == "4.6.0 NVIDIA 443.24") { | 214 | if (is_nvidia && version == "4.6.0 NVIDIA 443.24") { |
| @@ -231,6 +232,7 @@ Device::Device() | |||
| 231 | has_variable_aoffi = TestVariableAoffi(); | 232 | has_variable_aoffi = TestVariableAoffi(); |
| 232 | has_component_indexing_bug = is_amd; | 233 | has_component_indexing_bug = is_amd; |
| 233 | has_precise_bug = TestPreciseBug(); | 234 | has_precise_bug = TestPreciseBug(); |
| 235 | has_broken_texture_view_formats = is_amd || is_intel; | ||
| 234 | has_nv_viewport_array2 = GLAD_GL_NV_viewport_array2; | 236 | has_nv_viewport_array2 = GLAD_GL_NV_viewport_array2; |
| 235 | has_vertex_buffer_unified_memory = GLAD_GL_NV_vertex_buffer_unified_memory; | 237 | has_vertex_buffer_unified_memory = GLAD_GL_NV_vertex_buffer_unified_memory; |
| 236 | has_debugging_tool_attached = IsDebugToolAttached(extensions); | 238 | has_debugging_tool_attached = IsDebugToolAttached(extensions); |
| @@ -248,6 +250,8 @@ Device::Device() | |||
| 248 | LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi); | 250 | LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi); |
| 249 | LOG_INFO(Render_OpenGL, "Renderer_ComponentIndexingBug: {}", has_component_indexing_bug); | 251 | LOG_INFO(Render_OpenGL, "Renderer_ComponentIndexingBug: {}", has_component_indexing_bug); |
| 250 | LOG_INFO(Render_OpenGL, "Renderer_PreciseBug: {}", has_precise_bug); | 252 | LOG_INFO(Render_OpenGL, "Renderer_PreciseBug: {}", has_precise_bug); |
| 253 | LOG_INFO(Render_OpenGL, "Renderer_BrokenTextureViewFormats: {}", | ||
| 254 | has_broken_texture_view_formats); | ||
| 251 | 255 | ||
| 252 | if (Settings::values.use_assembly_shaders.GetValue() && !use_assembly_shaders) { | 256 | if (Settings::values.use_assembly_shaders.GetValue() && !use_assembly_shaders) { |
| 253 | LOG_ERROR(Render_OpenGL, "Assembly shaders enabled but not supported"); | 257 | LOG_ERROR(Render_OpenGL, "Assembly shaders enabled but not supported"); |
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h index 13e66846c..3e79d1e37 100644 --- a/src/video_core/renderer_opengl/gl_device.h +++ b/src/video_core/renderer_opengl/gl_device.h | |||
| @@ -96,6 +96,10 @@ public: | |||
| 96 | return has_precise_bug; | 96 | return has_precise_bug; |
| 97 | } | 97 | } |
| 98 | 98 | ||
| 99 | bool HasBrokenTextureViewFormats() const { | ||
| 100 | return has_broken_texture_view_formats; | ||
| 101 | } | ||
| 102 | |||
| 99 | bool HasFastBufferSubData() const { | 103 | bool HasFastBufferSubData() const { |
| 100 | return has_fast_buffer_sub_data; | 104 | return has_fast_buffer_sub_data; |
| 101 | } | 105 | } |
| @@ -137,6 +141,7 @@ private: | |||
| 137 | bool has_variable_aoffi{}; | 141 | bool has_variable_aoffi{}; |
| 138 | bool has_component_indexing_bug{}; | 142 | bool has_component_indexing_bug{}; |
| 139 | bool has_precise_bug{}; | 143 | bool has_precise_bug{}; |
| 144 | bool has_broken_texture_view_formats{}; | ||
| 140 | bool has_fast_buffer_sub_data{}; | 145 | bool has_fast_buffer_sub_data{}; |
| 141 | bool has_nv_viewport_array2{}; | 146 | bool has_nv_viewport_array2{}; |
| 142 | bool has_debugging_tool_attached{}; | 147 | bool has_debugging_tool_attached{}; |
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index 4c690418c..546cb6d00 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp | |||
| @@ -61,100 +61,99 @@ struct FormatTuple { | |||
| 61 | GLenum internal_format; | 61 | GLenum internal_format; |
| 62 | GLenum format = GL_NONE; | 62 | GLenum format = GL_NONE; |
| 63 | GLenum type = GL_NONE; | 63 | GLenum type = GL_NONE; |
| 64 | GLenum store_format = internal_format; | ||
| 65 | }; | 64 | }; |
| 66 | 65 | ||
| 67 | constexpr std::array<FormatTuple, MaxPixelFormat> FORMAT_TABLE = {{ | 66 | constexpr std::array<FormatTuple, MaxPixelFormat> FORMAT_TABLE = {{ |
| 68 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV}, // A8B8G8R8_UNORM | 67 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV}, // A8B8G8R8_UNORM |
| 69 | {GL_RGBA8_SNORM, GL_RGBA, GL_BYTE}, // A8B8G8R8_SNORM | 68 | {GL_RGBA8_SNORM, GL_RGBA, GL_BYTE}, // A8B8G8R8_SNORM |
| 70 | {GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE}, // A8B8G8R8_SINT | 69 | {GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE}, // A8B8G8R8_SINT |
| 71 | {GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE}, // A8B8G8R8_UINT | 70 | {GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE}, // A8B8G8R8_UINT |
| 72 | {GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, // R5G6B5_UNORM | 71 | {GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, // R5G6B5_UNORM |
| 73 | {GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV}, // B5G6R5_UNORM | 72 | {GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV}, // B5G6R5_UNORM |
| 74 | {GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // A1R5G5B5_UNORM | 73 | {GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // A1R5G5B5_UNORM |
| 75 | {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UNORM | 74 | {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UNORM |
| 76 | {GL_RGB10_A2UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UINT | 75 | {GL_RGB10_A2UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UINT |
| 77 | {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // A1B5G5R5_UNORM | 76 | {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // A1B5G5R5_UNORM |
| 78 | {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R8_UNORM | 77 | {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R8_UNORM |
| 79 | {GL_R8_SNORM, GL_RED, GL_BYTE}, // R8_SNORM | 78 | {GL_R8_SNORM, GL_RED, GL_BYTE}, // R8_SNORM |
| 80 | {GL_R8I, GL_RED_INTEGER, GL_BYTE}, // R8_SINT | 79 | {GL_R8I, GL_RED_INTEGER, GL_BYTE}, // R8_SINT |
| 81 | {GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE}, // R8_UINT | 80 | {GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE}, // R8_UINT |
| 82 | {GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT}, // R16G16B16A16_FLOAT | 81 | {GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT}, // R16G16B16A16_FLOAT |
| 83 | {GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT}, // R16G16B16A16_UNORM | 82 | {GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT}, // R16G16B16A16_UNORM |
| 84 | {GL_RGBA16_SNORM, GL_RGBA, GL_SHORT}, // R16G16B16A16_SNORM | 83 | {GL_RGBA16_SNORM, GL_RGBA, GL_SHORT}, // R16G16B16A16_SNORM |
| 85 | {GL_RGBA16I, GL_RGBA_INTEGER, GL_SHORT}, // R16G16B16A16_SINT | 84 | {GL_RGBA16I, GL_RGBA_INTEGER, GL_SHORT}, // R16G16B16A16_SINT |
| 86 | {GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT}, // R16G16B16A16_UINT | 85 | {GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT}, // R16G16B16A16_UINT |
| 87 | {GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV}, // B10G11R11_FLOAT | 86 | {GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV}, // B10G11R11_FLOAT |
| 88 | {GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT}, // R32G32B32A32_UINT | 87 | {GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT}, // R32G32B32A32_UINT |
| 89 | {GL_COMPRESSED_RGBA_S3TC_DXT1_EXT}, // BC1_RGBA_UNORM | 88 | {GL_COMPRESSED_RGBA_S3TC_DXT1_EXT}, // BC1_RGBA_UNORM |
| 90 | {GL_COMPRESSED_RGBA_S3TC_DXT3_EXT}, // BC2_UNORM | 89 | {GL_COMPRESSED_RGBA_S3TC_DXT3_EXT}, // BC2_UNORM |
| 91 | {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT}, // BC3_UNORM | 90 | {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT}, // BC3_UNORM |
| 92 | {GL_COMPRESSED_RED_RGTC1}, // BC4_UNORM | 91 | {GL_COMPRESSED_RED_RGTC1}, // BC4_UNORM |
| 93 | {GL_COMPRESSED_SIGNED_RED_RGTC1}, // BC4_SNORM | 92 | {GL_COMPRESSED_SIGNED_RED_RGTC1}, // BC4_SNORM |
| 94 | {GL_COMPRESSED_RG_RGTC2}, // BC5_UNORM | 93 | {GL_COMPRESSED_RG_RGTC2}, // BC5_UNORM |
| 95 | {GL_COMPRESSED_SIGNED_RG_RGTC2}, // BC5_SNORM | 94 | {GL_COMPRESSED_SIGNED_RG_RGTC2}, // BC5_SNORM |
| 96 | {GL_COMPRESSED_RGBA_BPTC_UNORM}, // BC7_UNORM | 95 | {GL_COMPRESSED_RGBA_BPTC_UNORM}, // BC7_UNORM |
| 97 | {GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT}, // BC6H_UFLOAT | 96 | {GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT}, // BC6H_UFLOAT |
| 98 | {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT}, // BC6H_SFLOAT | 97 | {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT}, // BC6H_SFLOAT |
| 99 | {GL_COMPRESSED_RGBA_ASTC_4x4_KHR}, // ASTC_2D_4X4_UNORM | 98 | {GL_COMPRESSED_RGBA_ASTC_4x4_KHR}, // ASTC_2D_4X4_UNORM |
| 100 | {GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE}, // B8G8R8A8_UNORM | 99 | {GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE}, // B8G8R8A8_UNORM |
| 101 | {GL_RGBA32F, GL_RGBA, GL_FLOAT}, // R32G32B32A32_FLOAT | 100 | {GL_RGBA32F, GL_RGBA, GL_FLOAT}, // R32G32B32A32_FLOAT |
| 102 | {GL_RGBA32I, GL_RGBA_INTEGER, GL_INT}, // R32G32B32A32_SINT | 101 | {GL_RGBA32I, GL_RGBA_INTEGER, GL_INT}, // R32G32B32A32_SINT |
| 103 | {GL_RG32F, GL_RG, GL_FLOAT}, // R32G32_FLOAT | 102 | {GL_RG32F, GL_RG, GL_FLOAT}, // R32G32_FLOAT |
| 104 | {GL_RG32I, GL_RG_INTEGER, GL_INT}, // R32G32_SINT | 103 | {GL_RG32I, GL_RG_INTEGER, GL_INT}, // R32G32_SINT |
| 105 | {GL_R32F, GL_RED, GL_FLOAT}, // R32_FLOAT | 104 | {GL_R32F, GL_RED, GL_FLOAT}, // R32_FLOAT |
| 106 | {GL_R16F, GL_RED, GL_HALF_FLOAT}, // R16_FLOAT | 105 | {GL_R16F, GL_RED, GL_HALF_FLOAT}, // R16_FLOAT |
| 107 | {GL_R16, GL_RED, GL_UNSIGNED_SHORT}, // R16_UNORM | 106 | {GL_R16, GL_RED, GL_UNSIGNED_SHORT}, // R16_UNORM |
| 108 | {GL_R16_SNORM, GL_RED, GL_SHORT}, // R16_SNORM | 107 | {GL_R16_SNORM, GL_RED, GL_SHORT}, // R16_SNORM |
| 109 | {GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT}, // R16_UINT | 108 | {GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT}, // R16_UINT |
| 110 | {GL_R16I, GL_RED_INTEGER, GL_SHORT}, // R16_SINT | 109 | {GL_R16I, GL_RED_INTEGER, GL_SHORT}, // R16_SINT |
| 111 | {GL_RG16, GL_RG, GL_UNSIGNED_SHORT}, // R16G16_UNORM | 110 | {GL_RG16, GL_RG, GL_UNSIGNED_SHORT}, // R16G16_UNORM |
| 112 | {GL_RG16F, GL_RG, GL_HALF_FLOAT}, // R16G16_FLOAT | 111 | {GL_RG16F, GL_RG, GL_HALF_FLOAT}, // R16G16_FLOAT |
| 113 | {GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT}, // R16G16_UINT | 112 | {GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT}, // R16G16_UINT |
| 114 | {GL_RG16I, GL_RG_INTEGER, GL_SHORT}, // R16G16_SINT | 113 | {GL_RG16I, GL_RG_INTEGER, GL_SHORT}, // R16G16_SINT |
| 115 | {GL_RG16_SNORM, GL_RG, GL_SHORT}, // R16G16_SNORM | 114 | {GL_RG16_SNORM, GL_RG, GL_SHORT}, // R16G16_SNORM |
| 116 | {GL_RGB32F, GL_RGB, GL_FLOAT}, // R32G32B32_FLOAT | 115 | {GL_RGB32F, GL_RGB, GL_FLOAT}, // R32G32B32_FLOAT |
| 117 | {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, GL_RGBA8}, // A8B8G8R8_SRGB | 116 | {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV}, // A8B8G8R8_SRGB |
| 118 | {GL_RG8, GL_RG, GL_UNSIGNED_BYTE}, // R8G8_UNORM | 117 | {GL_RG8, GL_RG, GL_UNSIGNED_BYTE}, // R8G8_UNORM |
| 119 | {GL_RG8_SNORM, GL_RG, GL_BYTE}, // R8G8_SNORM | 118 | {GL_RG8_SNORM, GL_RG, GL_BYTE}, // R8G8_SNORM |
| 120 | {GL_RG8I, GL_RG_INTEGER, GL_BYTE}, // R8G8_SINT | 119 | {GL_RG8I, GL_RG_INTEGER, GL_BYTE}, // R8G8_SINT |
| 121 | {GL_RG8UI, GL_RG_INTEGER, GL_UNSIGNED_BYTE}, // R8G8_UINT | 120 | {GL_RG8UI, GL_RG_INTEGER, GL_UNSIGNED_BYTE}, // R8G8_UINT |
| 122 | {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT}, // R32G32_UINT | 121 | {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT}, // R32G32_UINT |
| 123 | {GL_RGB16F, GL_RGBA, GL_HALF_FLOAT}, // R16G16B16X16_FLOAT | 122 | {GL_RGB16F, GL_RGBA, GL_HALF_FLOAT}, // R16G16B16X16_FLOAT |
| 124 | {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT}, // R32_UINT | 123 | {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT}, // R32_UINT |
| 125 | {GL_R32I, GL_RED_INTEGER, GL_INT}, // R32_SINT | 124 | {GL_R32I, GL_RED_INTEGER, GL_INT}, // R32_SINT |
| 126 | {GL_COMPRESSED_RGBA_ASTC_8x8_KHR}, // ASTC_2D_8X8_UNORM | 125 | {GL_COMPRESSED_RGBA_ASTC_8x8_KHR}, // ASTC_2D_8X8_UNORM |
| 127 | {GL_COMPRESSED_RGBA_ASTC_8x5_KHR}, // ASTC_2D_8X5_UNORM | 126 | {GL_COMPRESSED_RGBA_ASTC_8x5_KHR}, // ASTC_2D_8X5_UNORM |
| 128 | {GL_COMPRESSED_RGBA_ASTC_5x4_KHR}, // ASTC_2D_5X4_UNORM | 127 | {GL_COMPRESSED_RGBA_ASTC_5x4_KHR}, // ASTC_2D_5X4_UNORM |
| 129 | {GL_SRGB8_ALPHA8, GL_BGRA, GL_UNSIGNED_BYTE, GL_RGBA8}, // B8G8R8A8_UNORM | 128 | {GL_SRGB8_ALPHA8, GL_BGRA, GL_UNSIGNED_BYTE}, // B8G8R8A8_UNORM |
| 130 | {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT}, // BC1_RGBA_SRGB | 129 | {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT}, // BC1_RGBA_SRGB |
| 131 | {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT}, // BC2_SRGB | 130 | {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT}, // BC2_SRGB |
| 132 | {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}, // BC3_SRGB | 131 | {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}, // BC3_SRGB |
| 133 | {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM}, // BC7_SRGB | 132 | {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM}, // BC7_SRGB |
| 134 | {GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, // A4B4G4R4_UNORM | 133 | {GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, // A4B4G4R4_UNORM |
| 135 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR}, // ASTC_2D_4X4_SRGB | 134 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR}, // ASTC_2D_4X4_SRGB |
| 136 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR}, // ASTC_2D_8X8_SRGB | 135 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR}, // ASTC_2D_8X8_SRGB |
| 137 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR}, // ASTC_2D_8X5_SRGB | 136 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR}, // ASTC_2D_8X5_SRGB |
| 138 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR}, // ASTC_2D_5X4_SRGB | 137 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR}, // ASTC_2D_5X4_SRGB |
| 139 | {GL_COMPRESSED_RGBA_ASTC_5x5_KHR}, // ASTC_2D_5X5_UNORM | 138 | {GL_COMPRESSED_RGBA_ASTC_5x5_KHR}, // ASTC_2D_5X5_UNORM |
| 140 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR}, // ASTC_2D_5X5_SRGB | 139 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR}, // ASTC_2D_5X5_SRGB |
| 141 | {GL_COMPRESSED_RGBA_ASTC_10x8_KHR}, // ASTC_2D_10X8_UNORM | 140 | {GL_COMPRESSED_RGBA_ASTC_10x8_KHR}, // ASTC_2D_10X8_UNORM |
| 142 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR}, // ASTC_2D_10X8_SRGB | 141 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR}, // ASTC_2D_10X8_SRGB |
| 143 | {GL_COMPRESSED_RGBA_ASTC_6x6_KHR}, // ASTC_2D_6X6_UNORM | 142 | {GL_COMPRESSED_RGBA_ASTC_6x6_KHR}, // ASTC_2D_6X6_UNORM |
| 144 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR}, // ASTC_2D_6X6_SRGB | 143 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR}, // ASTC_2D_6X6_SRGB |
| 145 | {GL_COMPRESSED_RGBA_ASTC_10x10_KHR}, // ASTC_2D_10X10_UNORM | 144 | {GL_COMPRESSED_RGBA_ASTC_10x10_KHR}, // ASTC_2D_10X10_UNORM |
| 146 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR}, // ASTC_2D_10X10_SRGB | 145 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR}, // ASTC_2D_10X10_SRGB |
| 147 | {GL_COMPRESSED_RGBA_ASTC_12x12_KHR}, // ASTC_2D_12X12_UNORM | 146 | {GL_COMPRESSED_RGBA_ASTC_12x12_KHR}, // ASTC_2D_12X12_UNORM |
| 148 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR}, // ASTC_2D_12X12_SRGB | 147 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR}, // ASTC_2D_12X12_SRGB |
| 149 | {GL_COMPRESSED_RGBA_ASTC_8x6_KHR}, // ASTC_2D_8X6_UNORM | 148 | {GL_COMPRESSED_RGBA_ASTC_8x6_KHR}, // ASTC_2D_8X6_UNORM |
| 150 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR}, // ASTC_2D_8X6_SRGB | 149 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR}, // ASTC_2D_8X6_SRGB |
| 151 | {GL_COMPRESSED_RGBA_ASTC_6x5_KHR}, // ASTC_2D_6X5_UNORM | 150 | {GL_COMPRESSED_RGBA_ASTC_6x5_KHR}, // ASTC_2D_6X5_UNORM |
| 152 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR}, // ASTC_2D_6X5_SRGB | 151 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR}, // ASTC_2D_6X5_SRGB |
| 153 | {GL_RGB9_E5, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV}, // E5B9G9R9_FLOAT | 152 | {GL_RGB9_E5, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV}, // E5B9G9R9_FLOAT |
| 154 | {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT}, // D32_FLOAT | 153 | {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT}, // D32_FLOAT |
| 155 | {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // D16_UNORM | 154 | {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // D16_UNORM |
| 156 | {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // D24_UNORM_S8_UINT | 155 | {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // D24_UNORM_S8_UINT |
| 157 | {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // S8_UINT_D24_UNORM | 156 | {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // S8_UINT_D24_UNORM |
| 158 | {GL_DEPTH32F_STENCIL8, GL_DEPTH_STENCIL, | 157 | {GL_DEPTH32F_STENCIL8, GL_DEPTH_STENCIL, |
| 159 | GL_FLOAT_32_UNSIGNED_INT_24_8_REV}, // D32_FLOAT_S8_UINT | 158 | GL_FLOAT_32_UNSIGNED_INT_24_8_REV}, // D32_FLOAT_S8_UINT |
| 160 | }}; | 159 | }}; |
| @@ -431,6 +430,8 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, ProgramManager& | |||
| 431 | format_properties[i].emplace(format, properties); | 430 | format_properties[i].emplace(format, properties); |
| 432 | } | 431 | } |
| 433 | } | 432 | } |
| 433 | has_broken_texture_view_formats = device.HasBrokenTextureViewFormats(); | ||
| 434 | |||
| 434 | null_image_1d_array.Create(GL_TEXTURE_1D_ARRAY); | 435 | null_image_1d_array.Create(GL_TEXTURE_1D_ARRAY); |
| 435 | null_image_cube_array.Create(GL_TEXTURE_CUBE_MAP_ARRAY); | 436 | null_image_cube_array.Create(GL_TEXTURE_CUBE_MAP_ARRAY); |
| 436 | null_image_3d.Create(GL_TEXTURE_3D); | 437 | null_image_3d.Create(GL_TEXTURE_3D); |
| @@ -651,13 +652,11 @@ Image::Image(TextureCacheRuntime& runtime, const VideoCommon::ImageInfo& info_, | |||
| 651 | if (IsConverted(runtime.device, info.format, info.type)) { | 652 | if (IsConverted(runtime.device, info.format, info.type)) { |
| 652 | flags |= ImageFlagBits::Converted; | 653 | flags |= ImageFlagBits::Converted; |
| 653 | gl_internal_format = IsPixelFormatSRGB(info.format) ? GL_SRGB8_ALPHA8 : GL_RGBA8; | 654 | gl_internal_format = IsPixelFormatSRGB(info.format) ? GL_SRGB8_ALPHA8 : GL_RGBA8; |
| 654 | gl_store_format = GL_RGBA8; | ||
| 655 | gl_format = GL_RGBA; | 655 | gl_format = GL_RGBA; |
| 656 | gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; | 656 | gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; |
| 657 | } else { | 657 | } else { |
| 658 | const auto& tuple = GetFormatTuple(info.format); | 658 | const auto& tuple = GetFormatTuple(info.format); |
| 659 | gl_internal_format = tuple.internal_format; | 659 | gl_internal_format = tuple.internal_format; |
| 660 | gl_store_format = tuple.store_format; | ||
| 661 | gl_format = tuple.format; | 660 | gl_format = tuple.format; |
| 662 | gl_type = tuple.type; | 661 | gl_type = tuple.type; |
| 663 | } | 662 | } |
| @@ -677,23 +676,23 @@ Image::Image(TextureCacheRuntime& runtime, const VideoCommon::ImageInfo& info_, | |||
| 677 | } | 676 | } |
| 678 | switch (target) { | 677 | switch (target) { |
| 679 | case GL_TEXTURE_1D_ARRAY: | 678 | case GL_TEXTURE_1D_ARRAY: |
| 680 | glTextureStorage2D(handle, num_levels, gl_store_format, width, num_layers); | 679 | glTextureStorage2D(handle, num_levels, gl_internal_format, width, num_layers); |
| 681 | break; | 680 | break; |
| 682 | case GL_TEXTURE_2D_ARRAY: | 681 | case GL_TEXTURE_2D_ARRAY: |
| 683 | glTextureStorage3D(handle, num_levels, gl_store_format, width, height, num_layers); | 682 | glTextureStorage3D(handle, num_levels, gl_internal_format, width, height, num_layers); |
| 684 | break; | 683 | break; |
| 685 | case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: { | 684 | case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: { |
| 686 | // TODO: Where should 'fixedsamplelocations' come from? | 685 | // TODO: Where should 'fixedsamplelocations' come from? |
| 687 | const auto [samples_x, samples_y] = SamplesLog2(info.num_samples); | 686 | const auto [samples_x, samples_y] = SamplesLog2(info.num_samples); |
| 688 | glTextureStorage3DMultisample(handle, num_samples, gl_store_format, width >> samples_x, | 687 | glTextureStorage3DMultisample(handle, num_samples, gl_internal_format, width >> samples_x, |
| 689 | height >> samples_y, num_layers, GL_FALSE); | 688 | height >> samples_y, num_layers, GL_FALSE); |
| 690 | break; | 689 | break; |
| 691 | } | 690 | } |
| 692 | case GL_TEXTURE_RECTANGLE: | 691 | case GL_TEXTURE_RECTANGLE: |
| 693 | glTextureStorage2D(handle, num_levels, gl_store_format, width, height); | 692 | glTextureStorage2D(handle, num_levels, gl_internal_format, width, height); |
| 694 | break; | 693 | break; |
| 695 | case GL_TEXTURE_3D: | 694 | case GL_TEXTURE_3D: |
| 696 | glTextureStorage3D(handle, num_levels, gl_store_format, width, height, depth); | 695 | glTextureStorage3D(handle, num_levels, gl_internal_format, width, height, depth); |
| 697 | break; | 696 | break; |
| 698 | case GL_TEXTURE_BUFFER: | 697 | case GL_TEXTURE_BUFFER: |
| 699 | buffer.Create(); | 698 | buffer.Create(); |
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h index 04193e31e..15b7c3676 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.h +++ b/src/video_core/renderer_opengl/gl_texture_cache.h | |||
| @@ -96,6 +96,10 @@ public: | |||
| 96 | 96 | ||
| 97 | FormatProperties FormatInfo(VideoCommon::ImageType type, GLenum internal_format) const; | 97 | FormatProperties FormatInfo(VideoCommon::ImageType type, GLenum internal_format) const; |
| 98 | 98 | ||
| 99 | bool HasBrokenTextureViewFormats() const noexcept { | ||
| 100 | return has_broken_texture_view_formats; | ||
| 101 | } | ||
| 102 | |||
| 99 | private: | 103 | private: |
| 100 | struct StagingBuffers { | 104 | struct StagingBuffers { |
| 101 | explicit StagingBuffers(GLenum storage_flags_, GLenum map_flags_); | 105 | explicit StagingBuffers(GLenum storage_flags_, GLenum map_flags_); |
| @@ -120,6 +124,7 @@ private: | |||
| 120 | UtilShaders util_shaders; | 124 | UtilShaders util_shaders; |
| 121 | 125 | ||
| 122 | std::array<std::unordered_map<GLenum, FormatProperties>, 3> format_properties; | 126 | std::array<std::unordered_map<GLenum, FormatProperties>, 3> format_properties; |
| 127 | bool has_broken_texture_view_formats = false; | ||
| 123 | 128 | ||
| 124 | StagingBuffers upload_buffers{GL_MAP_WRITE_BIT, GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT}; | 129 | StagingBuffers upload_buffers{GL_MAP_WRITE_BIT, GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT}; |
| 125 | StagingBuffers download_buffers{GL_MAP_READ_BIT, GL_MAP_READ_BIT}; | 130 | StagingBuffers download_buffers{GL_MAP_READ_BIT, GL_MAP_READ_BIT}; |
| @@ -165,7 +170,6 @@ private: | |||
| 165 | OGLTextureView store_view; | 170 | OGLTextureView store_view; |
| 166 | OGLBuffer buffer; | 171 | OGLBuffer buffer; |
| 167 | GLenum gl_internal_format = GL_NONE; | 172 | GLenum gl_internal_format = GL_NONE; |
| 168 | GLenum gl_store_format = GL_NONE; | ||
| 169 | GLenum gl_format = GL_NONE; | 173 | GLenum gl_format = GL_NONE; |
| 170 | GLenum gl_type = GL_NONE; | 174 | GLenum gl_type = GL_NONE; |
| 171 | }; | 175 | }; |
diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index 87c8e5693..1f6a169ae 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp | |||
| @@ -11,14 +11,14 @@ | |||
| 11 | #include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h" | 11 | #include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h" |
| 12 | #include "video_core/renderer_vulkan/blit_image.h" | 12 | #include "video_core/renderer_vulkan/blit_image.h" |
| 13 | #include "video_core/renderer_vulkan/maxwell_to_vk.h" | 13 | #include "video_core/renderer_vulkan/maxwell_to_vk.h" |
| 14 | #include "video_core/renderer_vulkan/vk_device.h" | ||
| 15 | #include "video_core/renderer_vulkan/vk_scheduler.h" | 14 | #include "video_core/renderer_vulkan/vk_scheduler.h" |
| 16 | #include "video_core/renderer_vulkan/vk_shader_util.h" | 15 | #include "video_core/renderer_vulkan/vk_shader_util.h" |
| 17 | #include "video_core/renderer_vulkan/vk_state_tracker.h" | 16 | #include "video_core/renderer_vulkan/vk_state_tracker.h" |
| 18 | #include "video_core/renderer_vulkan/vk_texture_cache.h" | 17 | #include "video_core/renderer_vulkan/vk_texture_cache.h" |
| 19 | #include "video_core/renderer_vulkan/vk_update_descriptor.h" | 18 | #include "video_core/renderer_vulkan/vk_update_descriptor.h" |
| 20 | #include "video_core/renderer_vulkan/wrapper.h" | ||
| 21 | #include "video_core/surface.h" | 19 | #include "video_core/surface.h" |
| 20 | #include "video_core/vulkan_common/vulkan_device.h" | ||
| 21 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 22 | 22 | ||
| 23 | namespace Vulkan { | 23 | namespace Vulkan { |
| 24 | 24 | ||
| @@ -225,7 +225,7 @@ constexpr std::array<VkPipelineShaderStageCreateInfo, 2> MakeStages( | |||
| 225 | }; | 225 | }; |
| 226 | } | 226 | } |
| 227 | 227 | ||
| 228 | void UpdateOneTextureDescriptorSet(const VKDevice& device, VkDescriptorSet descriptor_set, | 228 | void UpdateOneTextureDescriptorSet(const Device& device, VkDescriptorSet descriptor_set, |
| 229 | VkSampler sampler, VkImageView image_view) { | 229 | VkSampler sampler, VkImageView image_view) { |
| 230 | const VkDescriptorImageInfo image_info{ | 230 | const VkDescriptorImageInfo image_info{ |
| 231 | .sampler = sampler, | 231 | .sampler = sampler, |
| @@ -247,7 +247,7 @@ void UpdateOneTextureDescriptorSet(const VKDevice& device, VkDescriptorSet descr | |||
| 247 | device.GetLogical().UpdateDescriptorSets(write_descriptor_set, nullptr); | 247 | device.GetLogical().UpdateDescriptorSets(write_descriptor_set, nullptr); |
| 248 | } | 248 | } |
| 249 | 249 | ||
| 250 | void UpdateTwoTexturesDescriptorSet(const VKDevice& device, VkDescriptorSet descriptor_set, | 250 | void UpdateTwoTexturesDescriptorSet(const Device& device, VkDescriptorSet descriptor_set, |
| 251 | VkSampler sampler, VkImageView image_view_0, | 251 | VkSampler sampler, VkImageView image_view_0, |
| 252 | VkImageView image_view_1) { | 252 | VkImageView image_view_1) { |
| 253 | const VkDescriptorImageInfo image_info_0{ | 253 | const VkDescriptorImageInfo image_info_0{ |
| @@ -326,7 +326,7 @@ void BindBlitState(vk::CommandBuffer cmdbuf, VkPipelineLayout layout, | |||
| 326 | 326 | ||
| 327 | } // Anonymous namespace | 327 | } // Anonymous namespace |
| 328 | 328 | ||
| 329 | BlitImageHelper::BlitImageHelper(const VKDevice& device_, VKScheduler& scheduler_, | 329 | BlitImageHelper::BlitImageHelper(const Device& device_, VKScheduler& scheduler_, |
| 330 | StateTracker& state_tracker_, VKDescriptorPool& descriptor_pool) | 330 | StateTracker& state_tracker_, VKDescriptorPool& descriptor_pool) |
| 331 | : device{device_}, scheduler{scheduler_}, state_tracker{state_tracker_}, | 331 | : device{device_}, scheduler{scheduler_}, state_tracker{state_tracker_}, |
| 332 | one_texture_set_layout(device.GetLogical().CreateDescriptorSetLayout( | 332 | one_texture_set_layout(device.GetLogical().CreateDescriptorSetLayout( |
diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h index 2c2790bf9..43fd3d737 100644 --- a/src/video_core/renderer_vulkan/blit_image.h +++ b/src/video_core/renderer_vulkan/blit_image.h | |||
| @@ -8,19 +8,18 @@ | |||
| 8 | 8 | ||
| 9 | #include "video_core/engines/fermi_2d.h" | 9 | #include "video_core/engines/fermi_2d.h" |
| 10 | #include "video_core/renderer_vulkan/vk_descriptor_pool.h" | 10 | #include "video_core/renderer_vulkan/vk_descriptor_pool.h" |
| 11 | #include "video_core/renderer_vulkan/wrapper.h" | ||
| 12 | #include "video_core/texture_cache/types.h" | 11 | #include "video_core/texture_cache/types.h" |
| 12 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 13 | 13 | ||
| 14 | namespace Vulkan { | 14 | namespace Vulkan { |
| 15 | 15 | ||
| 16 | using VideoCommon::Offset2D; | 16 | using VideoCommon::Offset2D; |
| 17 | 17 | ||
| 18 | class VKDevice; | 18 | class Device; |
| 19 | class VKScheduler; | ||
| 20 | class StateTracker; | ||
| 21 | |||
| 22 | class Framebuffer; | 19 | class Framebuffer; |
| 23 | class ImageView; | 20 | class ImageView; |
| 21 | class StateTracker; | ||
| 22 | class VKScheduler; | ||
| 24 | 23 | ||
| 25 | struct BlitImagePipelineKey { | 24 | struct BlitImagePipelineKey { |
| 26 | constexpr auto operator<=>(const BlitImagePipelineKey&) const noexcept = default; | 25 | constexpr auto operator<=>(const BlitImagePipelineKey&) const noexcept = default; |
| @@ -31,7 +30,7 @@ struct BlitImagePipelineKey { | |||
| 31 | 30 | ||
| 32 | class BlitImageHelper { | 31 | class BlitImageHelper { |
| 33 | public: | 32 | public: |
| 34 | explicit BlitImageHelper(const VKDevice& device, VKScheduler& scheduler, | 33 | explicit BlitImageHelper(const Device& device, VKScheduler& scheduler, |
| 35 | StateTracker& state_tracker, VKDescriptorPool& descriptor_pool); | 34 | StateTracker& state_tracker, VKDescriptorPool& descriptor_pool); |
| 36 | ~BlitImageHelper(); | 35 | ~BlitImageHelper(); |
| 37 | 36 | ||
| @@ -67,7 +66,7 @@ private: | |||
| 67 | 66 | ||
| 68 | void ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass); | 67 | void ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass); |
| 69 | 68 | ||
| 70 | const VKDevice& device; | 69 | const Device& device; |
| 71 | VKScheduler& scheduler; | 70 | VKScheduler& scheduler; |
| 72 | StateTracker& state_tracker; | 71 | StateTracker& state_tracker; |
| 73 | 72 | ||
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index 67dd10500..5be6dabd9 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp | |||
| @@ -76,7 +76,7 @@ void FixedPipelineState::Fill(const Maxwell& regs, bool has_extended_dynamic_sta | |||
| 76 | regs.instanced_arrays.IsInstancingEnabled(index) ? regs.vertex_array[index].divisor : 0; | 76 | regs.instanced_arrays.IsInstancingEnabled(index) ? regs.vertex_array[index].divisor : 0; |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | for (std::size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) { | 79 | for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) { |
| 80 | const auto& input = regs.vertex_attrib_format[index]; | 80 | const auto& input = regs.vertex_attrib_format[index]; |
| 81 | auto& attribute = attributes[index]; | 81 | auto& attribute = attributes[index]; |
| 82 | attribute.raw = 0; | 82 | attribute.raw = 0; |
| @@ -85,6 +85,7 @@ void FixedPipelineState::Fill(const Maxwell& regs, bool has_extended_dynamic_sta | |||
| 85 | attribute.offset.Assign(input.offset); | 85 | attribute.offset.Assign(input.offset); |
| 86 | attribute.type.Assign(static_cast<u32>(input.type.Value())); | 86 | attribute.type.Assign(static_cast<u32>(input.type.Value())); |
| 87 | attribute.size.Assign(static_cast<u32>(input.size.Value())); | 87 | attribute.size.Assign(static_cast<u32>(input.size.Value())); |
| 88 | attribute.binding_index_enabled.Assign(regs.vertex_array[index].IsEnabled() ? 1 : 0); | ||
| 88 | } | 89 | } |
| 89 | 90 | ||
| 90 | for (std::size_t index = 0; index < std::size(attachments); ++index) { | 91 | for (std::size_t index = 0; index < std::size(attachments); ++index) { |
| @@ -172,14 +173,9 @@ void FixedPipelineState::DynamicState::Fill(const Maxwell& regs) { | |||
| 172 | depth_test_func.Assign(PackComparisonOp(regs.depth_test_func)); | 173 | depth_test_func.Assign(PackComparisonOp(regs.depth_test_func)); |
| 173 | cull_face.Assign(PackCullFace(regs.cull_face)); | 174 | cull_face.Assign(PackCullFace(regs.cull_face)); |
| 174 | cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0); | 175 | cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0); |
| 175 | 176 | std::ranges::transform(regs.vertex_array, vertex_strides.begin(), [](const auto& array) { | |
| 176 | for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { | 177 | return static_cast<u16>(array.stride.Value()); |
| 177 | const auto& input = regs.vertex_array[index]; | 178 | }); |
| 178 | VertexBinding& binding = vertex_bindings[index]; | ||
| 179 | binding.raw = 0; | ||
| 180 | binding.enabled.Assign(input.IsEnabled() ? 1 : 0); | ||
| 181 | binding.stride.Assign(static_cast<u16>(input.stride.Value())); | ||
| 182 | } | ||
| 183 | } | 179 | } |
| 184 | 180 | ||
| 185 | std::size_t FixedPipelineState::Hash() const noexcept { | 181 | std::size_t FixedPipelineState::Hash() const noexcept { |
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index 7e95e6fce..465a55fdb 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h | |||
| @@ -96,6 +96,8 @@ struct FixedPipelineState { | |||
| 96 | BitField<6, 14, u32> offset; | 96 | BitField<6, 14, u32> offset; |
| 97 | BitField<20, 3, u32> type; | 97 | BitField<20, 3, u32> type; |
| 98 | BitField<23, 6, u32> size; | 98 | BitField<23, 6, u32> size; |
| 99 | // Not really an element of a vertex attribute, but it can be packed here | ||
| 100 | BitField<29, 1, u32> binding_index_enabled; | ||
| 99 | 101 | ||
| 100 | constexpr Maxwell::VertexAttribute::Type Type() const noexcept { | 102 | constexpr Maxwell::VertexAttribute::Type Type() const noexcept { |
| 101 | return static_cast<Maxwell::VertexAttribute::Type>(type.Value()); | 103 | return static_cast<Maxwell::VertexAttribute::Type>(type.Value()); |
| @@ -130,12 +132,6 @@ struct FixedPipelineState { | |||
| 130 | } | 132 | } |
| 131 | }; | 133 | }; |
| 132 | 134 | ||
| 133 | union VertexBinding { | ||
| 134 | u16 raw; | ||
| 135 | BitField<0, 12, u16> stride; | ||
| 136 | BitField<12, 1, u16> enabled; | ||
| 137 | }; | ||
| 138 | |||
| 139 | struct DynamicState { | 135 | struct DynamicState { |
| 140 | union { | 136 | union { |
| 141 | u32 raw1; | 137 | u32 raw1; |
| @@ -153,7 +149,8 @@ struct FixedPipelineState { | |||
| 153 | BitField<0, 2, u32> cull_face; | 149 | BitField<0, 2, u32> cull_face; |
| 154 | BitField<2, 1, u32> cull_enable; | 150 | BitField<2, 1, u32> cull_enable; |
| 155 | }; | 151 | }; |
| 156 | std::array<VertexBinding, Maxwell::NumVertexArrays> vertex_bindings; | 152 | // Vertex stride is a 12 bits value, we have 4 bits to spare per element |
| 153 | std::array<u16, Maxwell::NumVertexArrays> vertex_strides; | ||
| 157 | 154 | ||
| 158 | void Fill(const Maxwell& regs); | 155 | void Fill(const Maxwell& regs); |
| 159 | 156 | ||
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index 4c988429f..ca7c2c579 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp | |||
| @@ -9,9 +9,9 @@ | |||
| 9 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 10 | #include "video_core/engines/maxwell_3d.h" | 10 | #include "video_core/engines/maxwell_3d.h" |
| 11 | #include "video_core/renderer_vulkan/maxwell_to_vk.h" | 11 | #include "video_core/renderer_vulkan/maxwell_to_vk.h" |
| 12 | #include "video_core/renderer_vulkan/vk_device.h" | ||
| 13 | #include "video_core/renderer_vulkan/wrapper.h" | ||
| 14 | #include "video_core/surface.h" | 12 | #include "video_core/surface.h" |
| 13 | #include "video_core/vulkan_common/vulkan_device.h" | ||
| 14 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 15 | 15 | ||
| 16 | namespace Vulkan::MaxwellToVK { | 16 | namespace Vulkan::MaxwellToVK { |
| 17 | 17 | ||
| @@ -47,7 +47,7 @@ VkSamplerMipmapMode MipmapMode(Tegra::Texture::TextureMipmapFilter mipmap_filter | |||
| 47 | return {}; | 47 | return {}; |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | VkSamplerAddressMode WrapMode(const VKDevice& device, Tegra::Texture::WrapMode wrap_mode, | 50 | VkSamplerAddressMode WrapMode(const Device& device, Tegra::Texture::WrapMode wrap_mode, |
| 51 | Tegra::Texture::TextureFilter filter) { | 51 | Tegra::Texture::TextureFilter filter) { |
| 52 | switch (wrap_mode) { | 52 | switch (wrap_mode) { |
| 53 | case Tegra::Texture::WrapMode::Wrap: | 53 | case Tegra::Texture::WrapMode::Wrap: |
| @@ -222,7 +222,7 @@ constexpr bool IsZetaFormat(PixelFormat pixel_format) { | |||
| 222 | 222 | ||
| 223 | } // Anonymous namespace | 223 | } // Anonymous namespace |
| 224 | 224 | ||
| 225 | FormatInfo SurfaceFormat(const VKDevice& device, FormatType format_type, PixelFormat pixel_format) { | 225 | FormatInfo SurfaceFormat(const Device& device, FormatType format_type, PixelFormat pixel_format) { |
| 226 | ASSERT(static_cast<std::size_t>(pixel_format) < std::size(tex_format_tuples)); | 226 | ASSERT(static_cast<std::size_t>(pixel_format) < std::size(tex_format_tuples)); |
| 227 | 227 | ||
| 228 | auto tuple = tex_format_tuples[static_cast<std::size_t>(pixel_format)]; | 228 | auto tuple = tex_format_tuples[static_cast<std::size_t>(pixel_format)]; |
| @@ -280,7 +280,7 @@ VkShaderStageFlagBits ShaderStage(Tegra::Engines::ShaderType stage) { | |||
| 280 | return {}; | 280 | return {}; |
| 281 | } | 281 | } |
| 282 | 282 | ||
| 283 | VkPrimitiveTopology PrimitiveTopology([[maybe_unused]] const VKDevice& device, | 283 | VkPrimitiveTopology PrimitiveTopology([[maybe_unused]] const Device& device, |
| 284 | Maxwell::PrimitiveTopology topology) { | 284 | Maxwell::PrimitiveTopology topology) { |
| 285 | switch (topology) { | 285 | switch (topology) { |
| 286 | case Maxwell::PrimitiveTopology::Points: | 286 | case Maxwell::PrimitiveTopology::Points: |
| @@ -526,7 +526,7 @@ VkCompareOp ComparisonOp(Maxwell::ComparisonOp comparison) { | |||
| 526 | return {}; | 526 | return {}; |
| 527 | } | 527 | } |
| 528 | 528 | ||
| 529 | VkIndexType IndexFormat(const VKDevice& device, Maxwell::IndexFormat index_format) { | 529 | VkIndexType IndexFormat(const Device& device, Maxwell::IndexFormat index_format) { |
| 530 | switch (index_format) { | 530 | switch (index_format) { |
| 531 | case Maxwell::IndexFormat::UnsignedByte: | 531 | case Maxwell::IndexFormat::UnsignedByte: |
| 532 | if (!device.IsExtIndexTypeUint8Supported()) { | 532 | if (!device.IsExtIndexTypeUint8Supported()) { |
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.h b/src/video_core/renderer_vulkan/maxwell_to_vk.h index 1a90f192e..537969840 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.h +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.h | |||
| @@ -6,10 +6,10 @@ | |||
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "video_core/engines/maxwell_3d.h" | 8 | #include "video_core/engines/maxwell_3d.h" |
| 9 | #include "video_core/renderer_vulkan/vk_device.h" | ||
| 10 | #include "video_core/renderer_vulkan/wrapper.h" | ||
| 11 | #include "video_core/surface.h" | 9 | #include "video_core/surface.h" |
| 12 | #include "video_core/textures/texture.h" | 10 | #include "video_core/textures/texture.h" |
| 11 | #include "video_core/vulkan_common/vulkan_device.h" | ||
| 12 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 13 | 13 | ||
| 14 | namespace Vulkan::MaxwellToVK { | 14 | namespace Vulkan::MaxwellToVK { |
| 15 | 15 | ||
| @@ -22,7 +22,7 @@ VkFilter Filter(Tegra::Texture::TextureFilter filter); | |||
| 22 | 22 | ||
| 23 | VkSamplerMipmapMode MipmapMode(Tegra::Texture::TextureMipmapFilter mipmap_filter); | 23 | VkSamplerMipmapMode MipmapMode(Tegra::Texture::TextureMipmapFilter mipmap_filter); |
| 24 | 24 | ||
| 25 | VkSamplerAddressMode WrapMode(const VKDevice& device, Tegra::Texture::WrapMode wrap_mode, | 25 | VkSamplerAddressMode WrapMode(const Device& device, Tegra::Texture::WrapMode wrap_mode, |
| 26 | Tegra::Texture::TextureFilter filter); | 26 | Tegra::Texture::TextureFilter filter); |
| 27 | 27 | ||
| 28 | VkCompareOp DepthCompareFunction(Tegra::Texture::DepthCompareFunc depth_compare_func); | 28 | VkCompareOp DepthCompareFunction(Tegra::Texture::DepthCompareFunc depth_compare_func); |
| @@ -35,17 +35,17 @@ struct FormatInfo { | |||
| 35 | bool storage; | 35 | bool storage; |
| 36 | }; | 36 | }; |
| 37 | 37 | ||
| 38 | FormatInfo SurfaceFormat(const VKDevice& device, FormatType format_type, PixelFormat pixel_format); | 38 | FormatInfo SurfaceFormat(const Device& device, FormatType format_type, PixelFormat pixel_format); |
| 39 | 39 | ||
| 40 | VkShaderStageFlagBits ShaderStage(Tegra::Engines::ShaderType stage); | 40 | VkShaderStageFlagBits ShaderStage(Tegra::Engines::ShaderType stage); |
| 41 | 41 | ||
| 42 | VkPrimitiveTopology PrimitiveTopology(const VKDevice& device, Maxwell::PrimitiveTopology topology); | 42 | VkPrimitiveTopology PrimitiveTopology(const Device& device, Maxwell::PrimitiveTopology topology); |
| 43 | 43 | ||
| 44 | VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size); | 44 | VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size); |
| 45 | 45 | ||
| 46 | VkCompareOp ComparisonOp(Maxwell::ComparisonOp comparison); | 46 | VkCompareOp ComparisonOp(Maxwell::ComparisonOp comparison); |
| 47 | 47 | ||
| 48 | VkIndexType IndexFormat(const VKDevice& device, Maxwell::IndexFormat index_format); | 48 | VkIndexType IndexFormat(const Device& device, Maxwell::IndexFormat index_format); |
| 49 | 49 | ||
| 50 | VkStencilOp StencilOp(Maxwell::StencilOp stencil_op); | 50 | VkStencilOp StencilOp(Maxwell::StencilOp stencil_op); |
| 51 | 51 | ||
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 7f521cb9b..61796e33a 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp | |||
| @@ -12,8 +12,6 @@ | |||
| 12 | 12 | ||
| 13 | #include <fmt/format.h> | 13 | #include <fmt/format.h> |
| 14 | 14 | ||
| 15 | #include "common/dynamic_library.h" | ||
| 16 | #include "common/file_util.h" | ||
| 17 | #include "common/logging/log.h" | 15 | #include "common/logging/log.h" |
| 18 | #include "common/telemetry.h" | 16 | #include "common/telemetry.h" |
| 19 | #include "core/core.h" | 17 | #include "core/core.h" |
| @@ -24,182 +22,27 @@ | |||
| 24 | #include "video_core/gpu.h" | 22 | #include "video_core/gpu.h" |
| 25 | #include "video_core/renderer_vulkan/renderer_vulkan.h" | 23 | #include "video_core/renderer_vulkan/renderer_vulkan.h" |
| 26 | #include "video_core/renderer_vulkan/vk_blit_screen.h" | 24 | #include "video_core/renderer_vulkan/vk_blit_screen.h" |
| 27 | #include "video_core/renderer_vulkan/vk_device.h" | ||
| 28 | #include "video_core/renderer_vulkan/vk_master_semaphore.h" | 25 | #include "video_core/renderer_vulkan/vk_master_semaphore.h" |
| 29 | #include "video_core/renderer_vulkan/vk_memory_manager.h" | ||
| 30 | #include "video_core/renderer_vulkan/vk_rasterizer.h" | 26 | #include "video_core/renderer_vulkan/vk_rasterizer.h" |
| 31 | #include "video_core/renderer_vulkan/vk_scheduler.h" | 27 | #include "video_core/renderer_vulkan/vk_scheduler.h" |
| 32 | #include "video_core/renderer_vulkan/vk_state_tracker.h" | 28 | #include "video_core/renderer_vulkan/vk_state_tracker.h" |
| 33 | #include "video_core/renderer_vulkan/vk_swapchain.h" | 29 | #include "video_core/renderer_vulkan/vk_swapchain.h" |
| 34 | #include "video_core/renderer_vulkan/wrapper.h" | 30 | #include "video_core/vulkan_common/vulkan_debug_callback.h" |
| 35 | 31 | #include "video_core/vulkan_common/vulkan_device.h" | |
| 36 | // Include these late to avoid polluting previous headers | 32 | #include "video_core/vulkan_common/vulkan_instance.h" |
| 37 | #ifdef _WIN32 | 33 | #include "video_core/vulkan_common/vulkan_library.h" |
| 38 | #include <windows.h> | 34 | #include "video_core/vulkan_common/vulkan_memory_allocator.h" |
| 39 | // ensure include order | 35 | #include "video_core/vulkan_common/vulkan_surface.h" |
| 40 | #include <vulkan/vulkan_win32.h> | 36 | #include "video_core/vulkan_common/vulkan_wrapper.h" |
| 41 | #endif | ||
| 42 | |||
| 43 | #if !defined(_WIN32) && !defined(__APPLE__) | ||
| 44 | #include <X11/Xlib.h> | ||
| 45 | #include <vulkan/vulkan_wayland.h> | ||
| 46 | #include <vulkan/vulkan_xlib.h> | ||
| 47 | #endif | ||
| 48 | 37 | ||
| 49 | namespace Vulkan { | 38 | namespace Vulkan { |
| 50 | |||
| 51 | namespace { | 39 | namespace { |
| 52 | |||
| 53 | using Core::Frontend::WindowSystemType; | ||
| 54 | |||
| 55 | VkBool32 DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity, | ||
| 56 | VkDebugUtilsMessageTypeFlagsEXT type, | ||
| 57 | const VkDebugUtilsMessengerCallbackDataEXT* data, | ||
| 58 | [[maybe_unused]] void* user_data) { | ||
| 59 | const char* const message{data->pMessage}; | ||
| 60 | |||
| 61 | if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) { | ||
| 62 | LOG_CRITICAL(Render_Vulkan, "{}", message); | ||
| 63 | } else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) { | ||
| 64 | LOG_WARNING(Render_Vulkan, "{}", message); | ||
| 65 | } else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) { | ||
| 66 | LOG_INFO(Render_Vulkan, "{}", message); | ||
| 67 | } else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) { | ||
| 68 | LOG_DEBUG(Render_Vulkan, "{}", message); | ||
| 69 | } | ||
| 70 | return VK_FALSE; | ||
| 71 | } | ||
| 72 | |||
| 73 | Common::DynamicLibrary OpenVulkanLibrary() { | ||
| 74 | Common::DynamicLibrary library; | ||
| 75 | #ifdef __APPLE__ | ||
| 76 | // Check if a path to a specific Vulkan library has been specified. | ||
| 77 | char* libvulkan_env = getenv("LIBVULKAN_PATH"); | ||
| 78 | if (!libvulkan_env || !library.Open(libvulkan_env)) { | ||
| 79 | // Use the libvulkan.dylib from the application bundle. | ||
| 80 | const std::string filename = | ||
| 81 | Common::FS::GetBundleDirectory() + "/Contents/Frameworks/libvulkan.dylib"; | ||
| 82 | library.Open(filename.c_str()); | ||
| 83 | } | ||
| 84 | #else | ||
| 85 | std::string filename = Common::DynamicLibrary::GetVersionedFilename("vulkan", 1); | ||
| 86 | if (!library.Open(filename.c_str())) { | ||
| 87 | // Android devices may not have libvulkan.so.1, only libvulkan.so. | ||
| 88 | filename = Common::DynamicLibrary::GetVersionedFilename("vulkan"); | ||
| 89 | (void)library.Open(filename.c_str()); | ||
| 90 | } | ||
| 91 | #endif | ||
| 92 | return library; | ||
| 93 | } | ||
| 94 | |||
| 95 | std::pair<vk::Instance, u32> CreateInstance(Common::DynamicLibrary& library, | ||
| 96 | vk::InstanceDispatch& dld, WindowSystemType window_type, | ||
| 97 | bool enable_debug_utils, bool enable_layers) { | ||
| 98 | if (!library.IsOpen()) { | ||
| 99 | LOG_ERROR(Render_Vulkan, "Vulkan library not available"); | ||
| 100 | return {}; | ||
| 101 | } | ||
| 102 | if (!library.GetSymbol("vkGetInstanceProcAddr", &dld.vkGetInstanceProcAddr)) { | ||
| 103 | LOG_ERROR(Render_Vulkan, "vkGetInstanceProcAddr not present in Vulkan"); | ||
| 104 | return {}; | ||
| 105 | } | ||
| 106 | if (!vk::Load(dld)) { | ||
| 107 | LOG_ERROR(Render_Vulkan, "Failed to load Vulkan function pointers"); | ||
| 108 | return {}; | ||
| 109 | } | ||
| 110 | |||
| 111 | std::vector<const char*> extensions; | ||
| 112 | extensions.reserve(6); | ||
| 113 | switch (window_type) { | ||
| 114 | case Core::Frontend::WindowSystemType::Headless: | ||
| 115 | break; | ||
| 116 | #ifdef _WIN32 | ||
| 117 | case Core::Frontend::WindowSystemType::Windows: | ||
| 118 | extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); | ||
| 119 | break; | ||
| 120 | #endif | ||
| 121 | #if !defined(_WIN32) && !defined(__APPLE__) | ||
| 122 | case Core::Frontend::WindowSystemType::X11: | ||
| 123 | extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); | ||
| 124 | break; | ||
| 125 | case Core::Frontend::WindowSystemType::Wayland: | ||
| 126 | extensions.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); | ||
| 127 | break; | ||
| 128 | #endif | ||
| 129 | default: | ||
| 130 | LOG_ERROR(Render_Vulkan, "Presentation not supported on this platform"); | ||
| 131 | break; | ||
| 132 | } | ||
| 133 | if (window_type != Core::Frontend::WindowSystemType::Headless) { | ||
| 134 | extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); | ||
| 135 | } | ||
| 136 | if (enable_debug_utils) { | ||
| 137 | extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); | ||
| 138 | } | ||
| 139 | extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); | ||
| 140 | |||
| 141 | const std::optional properties = vk::EnumerateInstanceExtensionProperties(dld); | ||
| 142 | if (!properties) { | ||
| 143 | LOG_ERROR(Render_Vulkan, "Failed to query extension properties"); | ||
| 144 | return {}; | ||
| 145 | } | ||
| 146 | |||
| 147 | for (const char* extension : extensions) { | ||
| 148 | const auto it = | ||
| 149 | std::find_if(properties->begin(), properties->end(), [extension](const auto& prop) { | ||
| 150 | return !std::strcmp(extension, prop.extensionName); | ||
| 151 | }); | ||
| 152 | if (it == properties->end()) { | ||
| 153 | LOG_ERROR(Render_Vulkan, "Required instance extension {} is not available", extension); | ||
| 154 | return {}; | ||
| 155 | } | ||
| 156 | } | ||
| 157 | |||
| 158 | std::vector<const char*> layers; | ||
| 159 | layers.reserve(1); | ||
| 160 | if (enable_layers) { | ||
| 161 | layers.push_back("VK_LAYER_KHRONOS_validation"); | ||
| 162 | } | ||
| 163 | |||
| 164 | const std::optional layer_properties = vk::EnumerateInstanceLayerProperties(dld); | ||
| 165 | if (!layer_properties) { | ||
| 166 | LOG_ERROR(Render_Vulkan, "Failed to query layer properties, disabling layers"); | ||
| 167 | layers.clear(); | ||
| 168 | } | ||
| 169 | |||
| 170 | for (auto layer_it = layers.begin(); layer_it != layers.end();) { | ||
| 171 | const char* const layer = *layer_it; | ||
| 172 | const auto it = std::find_if( | ||
| 173 | layer_properties->begin(), layer_properties->end(), | ||
| 174 | [layer](const VkLayerProperties& prop) { return !std::strcmp(layer, prop.layerName); }); | ||
| 175 | if (it == layer_properties->end()) { | ||
| 176 | LOG_ERROR(Render_Vulkan, "Layer {} not available, removing it", layer); | ||
| 177 | layer_it = layers.erase(layer_it); | ||
| 178 | } else { | ||
| 179 | ++layer_it; | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | // Limit the maximum version of Vulkan to avoid using untested version. | ||
| 184 | const u32 version = std::min(vk::AvailableVersion(dld), static_cast<u32>(VK_API_VERSION_1_1)); | ||
| 185 | |||
| 186 | vk::Instance instance = vk::Instance::Create(version, layers, extensions, dld); | ||
| 187 | if (!instance) { | ||
| 188 | LOG_ERROR(Render_Vulkan, "Failed to create Vulkan instance"); | ||
| 189 | return {}; | ||
| 190 | } | ||
| 191 | if (!vk::Load(*instance, dld)) { | ||
| 192 | LOG_ERROR(Render_Vulkan, "Failed to load Vulkan instance function pointers"); | ||
| 193 | } | ||
| 194 | return std::make_pair(std::move(instance), version); | ||
| 195 | } | ||
| 196 | |||
| 197 | std::string GetReadableVersion(u32 version) { | 40 | std::string GetReadableVersion(u32 version) { |
| 198 | return fmt::format("{}.{}.{}", VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version), | 41 | return fmt::format("{}.{}.{}", VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version), |
| 199 | VK_VERSION_PATCH(version)); | 42 | VK_VERSION_PATCH(version)); |
| 200 | } | 43 | } |
| 201 | 44 | ||
| 202 | std::string GetDriverVersion(const VKDevice& device) { | 45 | std::string GetDriverVersion(const Device& device) { |
| 203 | // Extracted from | 46 | // Extracted from |
| 204 | // https://github.com/SaschaWillems/vulkan.gpuinfo.org/blob/5dddea46ea1120b0df14eef8f15ff8e318e35462/functions.php#L308-L314 | 47 | // https://github.com/SaschaWillems/vulkan.gpuinfo.org/blob/5dddea46ea1120b0df14eef8f15ff8e318e35462/functions.php#L308-L314 |
| 205 | const u32 version = device.GetDriverVersion(); | 48 | const u32 version = device.GetDriverVersion(); |
| @@ -216,7 +59,6 @@ std::string GetDriverVersion(const VKDevice& device) { | |||
| 216 | const u32 minor = version & 0x3fff; | 59 | const u32 minor = version & 0x3fff; |
| 217 | return fmt::format("{}.{}", major, minor); | 60 | return fmt::format("{}.{}", major, minor); |
| 218 | } | 61 | } |
| 219 | |||
| 220 | return GetReadableVersion(version); | 62 | return GetReadableVersion(version); |
| 221 | } | 63 | } |
| 222 | 64 | ||
| @@ -255,7 +97,6 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | |||
| 255 | if (!framebuffer) { | 97 | if (!framebuffer) { |
| 256 | return; | 98 | return; |
| 257 | } | 99 | } |
| 258 | |||
| 259 | const auto& layout = render_window.GetFramebufferLayout(); | 100 | const auto& layout = render_window.GetFramebufferLayout(); |
| 260 | if (layout.width > 0 && layout.height > 0 && render_window.IsShown()) { | 101 | if (layout.width > 0 && layout.height > 0 && render_window.IsShown()) { |
| 261 | const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset; | 102 | const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset; |
| @@ -284,17 +125,19 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | |||
| 284 | render_window.OnFrameDisplayed(); | 125 | render_window.OnFrameDisplayed(); |
| 285 | } | 126 | } |
| 286 | 127 | ||
| 287 | bool RendererVulkan::Init() { | 128 | bool RendererVulkan::Init() try { |
| 288 | library = OpenVulkanLibrary(); | 129 | library = OpenLibrary(); |
| 289 | std::tie(instance, instance_version) = CreateInstance( | 130 | instance = CreateInstance(library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type, |
| 290 | library, dld, render_window.GetWindowInfo().type, true, Settings::values.renderer_debug); | 131 | true, Settings::values.renderer_debug); |
| 291 | if (!instance || !CreateDebugCallback() || !CreateSurface() || !PickDevices()) { | 132 | if (Settings::values.renderer_debug) { |
| 292 | return false; | 133 | debug_callback = CreateDebugCallback(instance); |
| 293 | } | 134 | } |
| 135 | surface = CreateSurface(instance, render_window); | ||
| 294 | 136 | ||
| 137 | InitializeDevice(); | ||
| 295 | Report(); | 138 | Report(); |
| 296 | 139 | ||
| 297 | memory_manager = std::make_unique<VKMemoryManager>(*device); | 140 | memory_allocator = std::make_unique<MemoryAllocator>(*device); |
| 298 | 141 | ||
| 299 | state_tracker = std::make_unique<StateTracker>(gpu); | 142 | state_tracker = std::make_unique<StateTracker>(gpu); |
| 300 | 143 | ||
| @@ -306,13 +149,16 @@ bool RendererVulkan::Init() { | |||
| 306 | 149 | ||
| 307 | rasterizer = std::make_unique<RasterizerVulkan>(render_window, gpu, gpu.MemoryManager(), | 150 | rasterizer = std::make_unique<RasterizerVulkan>(render_window, gpu, gpu.MemoryManager(), |
| 308 | cpu_memory, screen_info, *device, | 151 | cpu_memory, screen_info, *device, |
| 309 | *memory_manager, *state_tracker, *scheduler); | 152 | *memory_allocator, *state_tracker, *scheduler); |
| 310 | 153 | ||
| 311 | blit_screen = | 154 | blit_screen = |
| 312 | std::make_unique<VKBlitScreen>(cpu_memory, render_window, *rasterizer, *device, | 155 | std::make_unique<VKBlitScreen>(cpu_memory, render_window, *rasterizer, *device, |
| 313 | *memory_manager, *swapchain, *scheduler, screen_info); | 156 | *memory_allocator, *swapchain, *scheduler, screen_info); |
| 314 | |||
| 315 | return true; | 157 | return true; |
| 158 | |||
| 159 | } catch (const vk::Exception& exception) { | ||
| 160 | LOG_ERROR(Render_Vulkan, "Vulkan initialization failed with error: {}", exception.what()); | ||
| 161 | return false; | ||
| 316 | } | 162 | } |
| 317 | 163 | ||
| 318 | void RendererVulkan::ShutDown() { | 164 | void RendererVulkan::ShutDown() { |
| @@ -322,104 +168,23 @@ void RendererVulkan::ShutDown() { | |||
| 322 | if (const auto& dev = device->GetLogical()) { | 168 | if (const auto& dev = device->GetLogical()) { |
| 323 | dev.WaitIdle(); | 169 | dev.WaitIdle(); |
| 324 | } | 170 | } |
| 325 | |||
| 326 | rasterizer.reset(); | 171 | rasterizer.reset(); |
| 327 | blit_screen.reset(); | 172 | blit_screen.reset(); |
| 328 | scheduler.reset(); | 173 | scheduler.reset(); |
| 329 | swapchain.reset(); | 174 | swapchain.reset(); |
| 330 | memory_manager.reset(); | 175 | memory_allocator.reset(); |
| 331 | device.reset(); | 176 | device.reset(); |
| 332 | } | 177 | } |
| 333 | 178 | ||
| 334 | bool RendererVulkan::CreateDebugCallback() { | 179 | void RendererVulkan::InitializeDevice() { |
| 335 | if (!Settings::values.renderer_debug) { | 180 | const std::vector<VkPhysicalDevice> devices = instance.EnumeratePhysicalDevices(); |
| 336 | return true; | ||
| 337 | } | ||
| 338 | debug_callback = instance.TryCreateDebugCallback(DebugCallback); | ||
| 339 | if (!debug_callback) { | ||
| 340 | LOG_ERROR(Render_Vulkan, "Failed to create debug callback"); | ||
| 341 | return false; | ||
| 342 | } | ||
| 343 | return true; | ||
| 344 | } | ||
| 345 | |||
| 346 | bool RendererVulkan::CreateSurface() { | ||
| 347 | [[maybe_unused]] const auto& window_info = render_window.GetWindowInfo(); | ||
| 348 | VkSurfaceKHR unsafe_surface = nullptr; | ||
| 349 | |||
| 350 | #ifdef _WIN32 | ||
| 351 | if (window_info.type == Core::Frontend::WindowSystemType::Windows) { | ||
| 352 | const HWND hWnd = static_cast<HWND>(window_info.render_surface); | ||
| 353 | const VkWin32SurfaceCreateInfoKHR win32_ci{VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, | ||
| 354 | nullptr, 0, nullptr, hWnd}; | ||
| 355 | const auto vkCreateWin32SurfaceKHR = reinterpret_cast<PFN_vkCreateWin32SurfaceKHR>( | ||
| 356 | dld.vkGetInstanceProcAddr(*instance, "vkCreateWin32SurfaceKHR")); | ||
| 357 | if (!vkCreateWin32SurfaceKHR || | ||
| 358 | vkCreateWin32SurfaceKHR(*instance, &win32_ci, nullptr, &unsafe_surface) != VK_SUCCESS) { | ||
| 359 | LOG_ERROR(Render_Vulkan, "Failed to initialize Win32 surface"); | ||
| 360 | return false; | ||
| 361 | } | ||
| 362 | } | ||
| 363 | #endif | ||
| 364 | #if !defined(_WIN32) && !defined(__APPLE__) | ||
| 365 | if (window_info.type == Core::Frontend::WindowSystemType::X11) { | ||
| 366 | const VkXlibSurfaceCreateInfoKHR xlib_ci{ | ||
| 367 | VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, nullptr, 0, | ||
| 368 | static_cast<Display*>(window_info.display_connection), | ||
| 369 | reinterpret_cast<Window>(window_info.render_surface)}; | ||
| 370 | const auto vkCreateXlibSurfaceKHR = reinterpret_cast<PFN_vkCreateXlibSurfaceKHR>( | ||
| 371 | dld.vkGetInstanceProcAddr(*instance, "vkCreateXlibSurfaceKHR")); | ||
| 372 | if (!vkCreateXlibSurfaceKHR || | ||
| 373 | vkCreateXlibSurfaceKHR(*instance, &xlib_ci, nullptr, &unsafe_surface) != VK_SUCCESS) { | ||
| 374 | LOG_ERROR(Render_Vulkan, "Failed to initialize Xlib surface"); | ||
| 375 | return false; | ||
| 376 | } | ||
| 377 | } | ||
| 378 | if (window_info.type == Core::Frontend::WindowSystemType::Wayland) { | ||
| 379 | const VkWaylandSurfaceCreateInfoKHR wayland_ci{ | ||
| 380 | VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, nullptr, 0, | ||
| 381 | static_cast<wl_display*>(window_info.display_connection), | ||
| 382 | static_cast<wl_surface*>(window_info.render_surface)}; | ||
| 383 | const auto vkCreateWaylandSurfaceKHR = reinterpret_cast<PFN_vkCreateWaylandSurfaceKHR>( | ||
| 384 | dld.vkGetInstanceProcAddr(*instance, "vkCreateWaylandSurfaceKHR")); | ||
| 385 | if (!vkCreateWaylandSurfaceKHR || | ||
| 386 | vkCreateWaylandSurfaceKHR(*instance, &wayland_ci, nullptr, &unsafe_surface) != | ||
| 387 | VK_SUCCESS) { | ||
| 388 | LOG_ERROR(Render_Vulkan, "Failed to initialize Wayland surface"); | ||
| 389 | return false; | ||
| 390 | } | ||
| 391 | } | ||
| 392 | #endif | ||
| 393 | if (!unsafe_surface) { | ||
| 394 | LOG_ERROR(Render_Vulkan, "Presentation not supported on this platform"); | ||
| 395 | return false; | ||
| 396 | } | ||
| 397 | |||
| 398 | surface = vk::SurfaceKHR(unsafe_surface, *instance, dld); | ||
| 399 | return true; | ||
| 400 | } | ||
| 401 | |||
| 402 | bool RendererVulkan::PickDevices() { | ||
| 403 | const auto devices = instance.EnumeratePhysicalDevices(); | ||
| 404 | if (!devices) { | ||
| 405 | LOG_ERROR(Render_Vulkan, "Failed to enumerate physical devices"); | ||
| 406 | return false; | ||
| 407 | } | ||
| 408 | |||
| 409 | const s32 device_index = Settings::values.vulkan_device.GetValue(); | 181 | const s32 device_index = Settings::values.vulkan_device.GetValue(); |
| 410 | if (device_index < 0 || device_index >= static_cast<s32>(devices->size())) { | 182 | if (device_index < 0 || device_index >= static_cast<s32>(devices.size())) { |
| 411 | LOG_ERROR(Render_Vulkan, "Invalid device index {}!", device_index); | 183 | LOG_ERROR(Render_Vulkan, "Invalid device index {}!", device_index); |
| 412 | return false; | 184 | throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); |
| 413 | } | ||
| 414 | const vk::PhysicalDevice physical_device((*devices)[static_cast<std::size_t>(device_index)], | ||
| 415 | dld); | ||
| 416 | if (!VKDevice::IsSuitable(physical_device, *surface)) { | ||
| 417 | return false; | ||
| 418 | } | 185 | } |
| 419 | 186 | const vk::PhysicalDevice physical_device(devices[static_cast<size_t>(device_index)], dld); | |
| 420 | device = | 187 | device = std::make_unique<Device>(*instance, physical_device, *surface, dld); |
| 421 | std::make_unique<VKDevice>(*instance, instance_version, physical_device, *surface, dld); | ||
| 422 | return device->Create(); | ||
| 423 | } | 188 | } |
| 424 | 189 | ||
| 425 | void RendererVulkan::Report() const { | 190 | void RendererVulkan::Report() const { |
| @@ -444,26 +209,21 @@ void RendererVulkan::Report() const { | |||
| 444 | telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); | 209 | telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); |
| 445 | } | 210 | } |
| 446 | 211 | ||
| 447 | std::vector<std::string> RendererVulkan::EnumerateDevices() { | 212 | std::vector<std::string> RendererVulkan::EnumerateDevices() try { |
| 448 | vk::InstanceDispatch dld; | 213 | vk::InstanceDispatch dld; |
| 449 | Common::DynamicLibrary library = OpenVulkanLibrary(); | 214 | const Common::DynamicLibrary library = OpenLibrary(); |
| 450 | vk::Instance instance = | 215 | const vk::Instance instance = CreateInstance(library, dld, VK_API_VERSION_1_0); |
| 451 | CreateInstance(library, dld, WindowSystemType::Headless, false, false).first; | 216 | const std::vector<VkPhysicalDevice> physical_devices = instance.EnumeratePhysicalDevices(); |
| 452 | if (!instance) { | ||
| 453 | return {}; | ||
| 454 | } | ||
| 455 | |||
| 456 | const std::optional physical_devices = instance.EnumeratePhysicalDevices(); | ||
| 457 | if (!physical_devices) { | ||
| 458 | return {}; | ||
| 459 | } | ||
| 460 | |||
| 461 | std::vector<std::string> names; | 217 | std::vector<std::string> names; |
| 462 | names.reserve(physical_devices->size()); | 218 | names.reserve(physical_devices.size()); |
| 463 | for (const auto& device : *physical_devices) { | 219 | for (const VkPhysicalDevice device : physical_devices) { |
| 464 | names.push_back(vk::PhysicalDevice(device, dld).GetProperties().deviceName); | 220 | names.push_back(vk::PhysicalDevice(device, dld).GetProperties().deviceName); |
| 465 | } | 221 | } |
| 466 | return names; | 222 | return names; |
| 223 | |||
| 224 | } catch (const vk::Exception& exception) { | ||
| 225 | LOG_ERROR(Render_Vulkan, "Failed to enumerate devices with error: {}", exception.what()); | ||
| 226 | return {}; | ||
| 467 | } | 227 | } |
| 468 | 228 | ||
| 469 | } // namespace Vulkan | 229 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index 74642fba4..daf55b9b4 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h | |||
| @@ -11,7 +11,7 @@ | |||
| 11 | #include "common/dynamic_library.h" | 11 | #include "common/dynamic_library.h" |
| 12 | 12 | ||
| 13 | #include "video_core/renderer_base.h" | 13 | #include "video_core/renderer_base.h" |
| 14 | #include "video_core/renderer_vulkan/wrapper.h" | 14 | #include "video_core/vulkan_common/vulkan_wrapper.h" |
| 15 | 15 | ||
| 16 | namespace Core { | 16 | namespace Core { |
| 17 | class TelemetrySession; | 17 | class TelemetrySession; |
| @@ -27,10 +27,10 @@ class GPU; | |||
| 27 | 27 | ||
| 28 | namespace Vulkan { | 28 | namespace Vulkan { |
| 29 | 29 | ||
| 30 | class Device; | ||
| 30 | class StateTracker; | 31 | class StateTracker; |
| 32 | class MemoryAllocator; | ||
| 31 | class VKBlitScreen; | 33 | class VKBlitScreen; |
| 32 | class VKDevice; | ||
| 33 | class VKMemoryManager; | ||
| 34 | class VKSwapchain; | 34 | class VKSwapchain; |
| 35 | class VKScheduler; | 35 | class VKScheduler; |
| 36 | 36 | ||
| @@ -56,11 +56,7 @@ public: | |||
| 56 | static std::vector<std::string> EnumerateDevices(); | 56 | static std::vector<std::string> EnumerateDevices(); |
| 57 | 57 | ||
| 58 | private: | 58 | private: |
| 59 | bool CreateDebugCallback(); | 59 | void InitializeDevice(); |
| 60 | |||
| 61 | bool CreateSurface(); | ||
| 62 | |||
| 63 | bool PickDevices(); | ||
| 64 | 60 | ||
| 65 | void Report() const; | 61 | void Report() const; |
| 66 | 62 | ||
| @@ -72,15 +68,14 @@ private: | |||
| 72 | vk::InstanceDispatch dld; | 68 | vk::InstanceDispatch dld; |
| 73 | 69 | ||
| 74 | vk::Instance instance; | 70 | vk::Instance instance; |
| 75 | u32 instance_version{}; | ||
| 76 | 71 | ||
| 77 | vk::SurfaceKHR surface; | 72 | vk::SurfaceKHR surface; |
| 78 | 73 | ||
| 79 | VKScreenInfo screen_info; | 74 | VKScreenInfo screen_info; |
| 80 | 75 | ||
| 81 | vk::DebugCallback debug_callback; | 76 | vk::DebugUtilsMessenger debug_callback; |
| 82 | std::unique_ptr<VKDevice> device; | 77 | std::unique_ptr<Device> device; |
| 83 | std::unique_ptr<VKMemoryManager> memory_manager; | 78 | std::unique_ptr<MemoryAllocator> memory_allocator; |
| 84 | std::unique_ptr<StateTracker> state_tracker; | 79 | std::unique_ptr<StateTracker> state_tracker; |
| 85 | std::unique_ptr<VKScheduler> scheduler; | 80 | std::unique_ptr<VKScheduler> scheduler; |
| 86 | std::unique_ptr<VKSwapchain> swapchain; | 81 | std::unique_ptr<VKSwapchain> swapchain; |
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index d3a83f22f..3e3b895e0 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp | |||
| @@ -21,15 +21,15 @@ | |||
| 21 | #include "video_core/rasterizer_interface.h" | 21 | #include "video_core/rasterizer_interface.h" |
| 22 | #include "video_core/renderer_vulkan/renderer_vulkan.h" | 22 | #include "video_core/renderer_vulkan/renderer_vulkan.h" |
| 23 | #include "video_core/renderer_vulkan/vk_blit_screen.h" | 23 | #include "video_core/renderer_vulkan/vk_blit_screen.h" |
| 24 | #include "video_core/renderer_vulkan/vk_device.h" | ||
| 25 | #include "video_core/renderer_vulkan/vk_master_semaphore.h" | 24 | #include "video_core/renderer_vulkan/vk_master_semaphore.h" |
| 26 | #include "video_core/renderer_vulkan/vk_memory_manager.h" | ||
| 27 | #include "video_core/renderer_vulkan/vk_scheduler.h" | 25 | #include "video_core/renderer_vulkan/vk_scheduler.h" |
| 28 | #include "video_core/renderer_vulkan/vk_shader_util.h" | 26 | #include "video_core/renderer_vulkan/vk_shader_util.h" |
| 29 | #include "video_core/renderer_vulkan/vk_swapchain.h" | 27 | #include "video_core/renderer_vulkan/vk_swapchain.h" |
| 30 | #include "video_core/renderer_vulkan/wrapper.h" | ||
| 31 | #include "video_core/surface.h" | 28 | #include "video_core/surface.h" |
| 32 | #include "video_core/textures/decoders.h" | 29 | #include "video_core/textures/decoders.h" |
| 30 | #include "video_core/vulkan_common/vulkan_device.h" | ||
| 31 | #include "video_core/vulkan_common/vulkan_memory_allocator.h" | ||
| 32 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 33 | 33 | ||
| 34 | namespace Vulkan { | 34 | namespace Vulkan { |
| 35 | 35 | ||
| @@ -114,11 +114,11 @@ struct VKBlitScreen::BufferData { | |||
| 114 | 114 | ||
| 115 | VKBlitScreen::VKBlitScreen(Core::Memory::Memory& cpu_memory_, | 115 | VKBlitScreen::VKBlitScreen(Core::Memory::Memory& cpu_memory_, |
| 116 | Core::Frontend::EmuWindow& render_window_, | 116 | Core::Frontend::EmuWindow& render_window_, |
| 117 | VideoCore::RasterizerInterface& rasterizer_, const VKDevice& device_, | 117 | VideoCore::RasterizerInterface& rasterizer_, const Device& device_, |
| 118 | VKMemoryManager& memory_manager_, VKSwapchain& swapchain_, | 118 | MemoryAllocator& memory_allocator_, VKSwapchain& swapchain_, |
| 119 | VKScheduler& scheduler_, const VKScreenInfo& screen_info_) | 119 | VKScheduler& scheduler_, const VKScreenInfo& screen_info_) |
| 120 | : cpu_memory{cpu_memory_}, render_window{render_window_}, rasterizer{rasterizer_}, | 120 | : cpu_memory{cpu_memory_}, render_window{render_window_}, rasterizer{rasterizer_}, |
| 121 | device{device_}, memory_manager{memory_manager_}, swapchain{swapchain_}, | 121 | device{device_}, memory_allocator{memory_allocator_}, swapchain{swapchain_}, |
| 122 | scheduler{scheduler_}, image_count{swapchain.GetImageCount()}, screen_info{screen_info_} { | 122 | scheduler{scheduler_}, image_count{swapchain.GetImageCount()}, screen_info{screen_info_} { |
| 123 | resource_ticks.resize(image_count); | 123 | resource_ticks.resize(image_count); |
| 124 | 124 | ||
| @@ -150,8 +150,8 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool | |||
| 150 | SetUniformData(data, framebuffer); | 150 | SetUniformData(data, framebuffer); |
| 151 | SetVertexData(data, framebuffer); | 151 | SetVertexData(data, framebuffer); |
| 152 | 152 | ||
| 153 | auto map = buffer_commit->Map(); | 153 | const std::span<u8> map = buffer_commit.Map(); |
| 154 | std::memcpy(map.Address(), &data, sizeof(data)); | 154 | std::memcpy(map.data(), &data, sizeof(data)); |
| 155 | 155 | ||
| 156 | if (!use_accelerated) { | 156 | if (!use_accelerated) { |
| 157 | const u64 image_offset = GetRawImageOffset(framebuffer, image_index); | 157 | const u64 image_offset = GetRawImageOffset(framebuffer, image_index); |
| @@ -165,8 +165,8 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool | |||
| 165 | constexpr u32 block_height_log2 = 4; | 165 | constexpr u32 block_height_log2 = 4; |
| 166 | const u32 bytes_per_pixel = GetBytesPerPixel(framebuffer); | 166 | const u32 bytes_per_pixel = GetBytesPerPixel(framebuffer); |
| 167 | Tegra::Texture::UnswizzleTexture( | 167 | Tegra::Texture::UnswizzleTexture( |
| 168 | std::span(map.Address() + image_offset, size_bytes), std::span(host_ptr, size_bytes), | 168 | map.subspan(image_offset, size_bytes), std::span(host_ptr, size_bytes), bytes_per_pixel, |
| 169 | bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0); | 169 | framebuffer.width, framebuffer.height, 1, block_height_log2, 0); |
| 170 | 170 | ||
| 171 | const VkBufferImageCopy copy{ | 171 | const VkBufferImageCopy copy{ |
| 172 | .bufferOffset = image_offset, | 172 | .bufferOffset = image_offset, |
| @@ -224,8 +224,6 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool | |||
| 224 | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, write_barrier); | 224 | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, write_barrier); |
| 225 | }); | 225 | }); |
| 226 | } | 226 | } |
| 227 | map.Release(); | ||
| 228 | |||
| 229 | scheduler.Record([renderpass = *renderpass, framebuffer = *framebuffers[image_index], | 227 | scheduler.Record([renderpass = *renderpass, framebuffer = *framebuffers[image_index], |
| 230 | descriptor_set = descriptor_sets[image_index], buffer = *buffer, | 228 | descriptor_set = descriptor_sets[image_index], buffer = *buffer, |
| 231 | size = swapchain.GetSize(), pipeline = *pipeline, | 229 | size = swapchain.GetSize(), pipeline = *pipeline, |
| @@ -642,7 +640,7 @@ void VKBlitScreen::ReleaseRawImages() { | |||
| 642 | raw_images.clear(); | 640 | raw_images.clear(); |
| 643 | raw_buffer_commits.clear(); | 641 | raw_buffer_commits.clear(); |
| 644 | buffer.reset(); | 642 | buffer.reset(); |
| 645 | buffer_commit.reset(); | 643 | buffer_commit = MemoryCommit{}; |
| 646 | } | 644 | } |
| 647 | 645 | ||
| 648 | void VKBlitScreen::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer) { | 646 | void VKBlitScreen::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer) { |
| @@ -659,7 +657,7 @@ void VKBlitScreen::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuff | |||
| 659 | }; | 657 | }; |
| 660 | 658 | ||
| 661 | buffer = device.GetLogical().CreateBuffer(ci); | 659 | buffer = device.GetLogical().CreateBuffer(ci); |
| 662 | buffer_commit = memory_manager.Commit(buffer, true); | 660 | buffer_commit = memory_allocator.Commit(buffer, MemoryUsage::Upload); |
| 663 | } | 661 | } |
| 664 | 662 | ||
| 665 | void VKBlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) { | 663 | void VKBlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) { |
| @@ -690,7 +688,7 @@ void VKBlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) | |||
| 690 | .pQueueFamilyIndices = nullptr, | 688 | .pQueueFamilyIndices = nullptr, |
| 691 | .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, | 689 | .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, |
| 692 | }); | 690 | }); |
| 693 | raw_buffer_commits[i] = memory_manager.Commit(raw_images[i], false); | 691 | raw_buffer_commits[i] = memory_allocator.Commit(raw_images[i], MemoryUsage::DeviceLocal); |
| 694 | raw_image_views[i] = device.GetLogical().CreateImageView(VkImageViewCreateInfo{ | 692 | raw_image_views[i] = device.GetLogical().CreateImageView(VkImageViewCreateInfo{ |
| 695 | .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, | 693 | .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, |
| 696 | .pNext = nullptr, | 694 | .pNext = nullptr, |
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h index 2ee374247..b52576957 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.h +++ b/src/video_core/renderer_vulkan/vk_blit_screen.h | |||
| @@ -6,8 +6,8 @@ | |||
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | 8 | ||
| 9 | #include "video_core/renderer_vulkan/vk_memory_manager.h" | 9 | #include "video_core/vulkan_common/vulkan_memory_allocator.h" |
| 10 | #include "video_core/renderer_vulkan/wrapper.h" | 10 | #include "video_core/vulkan_common/vulkan_wrapper.h" |
| 11 | 11 | ||
| 12 | namespace Core { | 12 | namespace Core { |
| 13 | class System; | 13 | class System; |
| @@ -33,8 +33,8 @@ namespace Vulkan { | |||
| 33 | 33 | ||
| 34 | struct ScreenInfo; | 34 | struct ScreenInfo; |
| 35 | 35 | ||
| 36 | class Device; | ||
| 36 | class RasterizerVulkan; | 37 | class RasterizerVulkan; |
| 37 | class VKDevice; | ||
| 38 | class VKScheduler; | 38 | class VKScheduler; |
| 39 | class VKSwapchain; | 39 | class VKSwapchain; |
| 40 | 40 | ||
| @@ -42,8 +42,8 @@ class VKBlitScreen final { | |||
| 42 | public: | 42 | public: |
| 43 | explicit VKBlitScreen(Core::Memory::Memory& cpu_memory, | 43 | explicit VKBlitScreen(Core::Memory::Memory& cpu_memory, |
| 44 | Core::Frontend::EmuWindow& render_window, | 44 | Core::Frontend::EmuWindow& render_window, |
| 45 | VideoCore::RasterizerInterface& rasterizer, const VKDevice& device, | 45 | VideoCore::RasterizerInterface& rasterizer, const Device& device, |
| 46 | VKMemoryManager& memory_manager, VKSwapchain& swapchain, | 46 | MemoryAllocator& memory_allocator, VKSwapchain& swapchain, |
| 47 | VKScheduler& scheduler, const VKScreenInfo& screen_info); | 47 | VKScheduler& scheduler, const VKScreenInfo& screen_info); |
| 48 | ~VKBlitScreen(); | 48 | ~VKBlitScreen(); |
| 49 | 49 | ||
| @@ -85,8 +85,8 @@ private: | |||
| 85 | Core::Memory::Memory& cpu_memory; | 85 | Core::Memory::Memory& cpu_memory; |
| 86 | Core::Frontend::EmuWindow& render_window; | 86 | Core::Frontend::EmuWindow& render_window; |
| 87 | VideoCore::RasterizerInterface& rasterizer; | 87 | VideoCore::RasterizerInterface& rasterizer; |
| 88 | const VKDevice& device; | 88 | const Device& device; |
| 89 | VKMemoryManager& memory_manager; | 89 | MemoryAllocator& memory_allocator; |
| 90 | VKSwapchain& swapchain; | 90 | VKSwapchain& swapchain; |
| 91 | VKScheduler& scheduler; | 91 | VKScheduler& scheduler; |
| 92 | const std::size_t image_count; | 92 | const std::size_t image_count; |
| @@ -104,14 +104,14 @@ private: | |||
| 104 | vk::Sampler sampler; | 104 | vk::Sampler sampler; |
| 105 | 105 | ||
| 106 | vk::Buffer buffer; | 106 | vk::Buffer buffer; |
| 107 | VKMemoryCommit buffer_commit; | 107 | MemoryCommit buffer_commit; |
| 108 | 108 | ||
| 109 | std::vector<u64> resource_ticks; | 109 | std::vector<u64> resource_ticks; |
| 110 | 110 | ||
| 111 | std::vector<vk::Semaphore> semaphores; | 111 | std::vector<vk::Semaphore> semaphores; |
| 112 | std::vector<vk::Image> raw_images; | 112 | std::vector<vk::Image> raw_images; |
| 113 | std::vector<vk::ImageView> raw_image_views; | 113 | std::vector<vk::ImageView> raw_image_views; |
| 114 | std::vector<VKMemoryCommit> raw_buffer_commits; | 114 | std::vector<MemoryCommit> raw_buffer_commits; |
| 115 | u32 raw_width = 0; | 115 | u32 raw_width = 0; |
| 116 | u32 raw_height = 0; | 116 | u32 raw_height = 0; |
| 117 | }; | 117 | }; |
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 10d296c2f..d8ad40a0f 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp | |||
| @@ -9,10 +9,10 @@ | |||
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "video_core/buffer_cache/buffer_cache.h" | 10 | #include "video_core/buffer_cache/buffer_cache.h" |
| 11 | #include "video_core/renderer_vulkan/vk_buffer_cache.h" | 11 | #include "video_core/renderer_vulkan/vk_buffer_cache.h" |
| 12 | #include "video_core/renderer_vulkan/vk_device.h" | ||
| 13 | #include "video_core/renderer_vulkan/vk_scheduler.h" | 12 | #include "video_core/renderer_vulkan/vk_scheduler.h" |
| 14 | #include "video_core/renderer_vulkan/vk_stream_buffer.h" | 13 | #include "video_core/renderer_vulkan/vk_stream_buffer.h" |
| 15 | #include "video_core/renderer_vulkan/wrapper.h" | 14 | #include "video_core/vulkan_common/vulkan_device.h" |
| 15 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 16 | 16 | ||
| 17 | namespace Vulkan { | 17 | namespace Vulkan { |
| 18 | 18 | ||
| @@ -34,17 +34,13 @@ constexpr VkAccessFlags UPLOAD_ACCESS_BARRIERS = | |||
| 34 | constexpr VkAccessFlags TRANSFORM_FEEDBACK_WRITE_ACCESS = | 34 | constexpr VkAccessFlags TRANSFORM_FEEDBACK_WRITE_ACCESS = |
| 35 | VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT | VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT; | 35 | VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT | VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT; |
| 36 | 36 | ||
| 37 | std::unique_ptr<VKStreamBuffer> CreateStreamBuffer(const VKDevice& device, VKScheduler& scheduler) { | ||
| 38 | return std::make_unique<VKStreamBuffer>(device, scheduler); | ||
| 39 | } | ||
| 40 | |||
| 41 | } // Anonymous namespace | 37 | } // Anonymous namespace |
| 42 | 38 | ||
| 43 | Buffer::Buffer(const VKDevice& device_, VKMemoryManager& memory_manager, VKScheduler& scheduler_, | 39 | Buffer::Buffer(const Device& device_, MemoryAllocator& memory_allocator, VKScheduler& scheduler_, |
| 44 | VKStagingBufferPool& staging_pool_, VAddr cpu_addr_, std::size_t size_) | 40 | StagingBufferPool& staging_pool_, VAddr cpu_addr_, std::size_t size_) |
| 45 | : BufferBlock{cpu_addr_, size_}, device{device_}, scheduler{scheduler_}, staging_pool{ | 41 | : BufferBlock{cpu_addr_, size_}, device{device_}, scheduler{scheduler_}, staging_pool{ |
| 46 | staging_pool_} { | 42 | staging_pool_} { |
| 47 | const VkBufferCreateInfo ci{ | 43 | buffer = device.GetLogical().CreateBuffer(VkBufferCreateInfo{ |
| 48 | .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, | 44 | .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, |
| 49 | .pNext = nullptr, | 45 | .pNext = nullptr, |
| 50 | .flags = 0, | 46 | .flags = 0, |
| @@ -53,22 +49,20 @@ Buffer::Buffer(const VKDevice& device_, VKMemoryManager& memory_manager, VKSched | |||
| 53 | .sharingMode = VK_SHARING_MODE_EXCLUSIVE, | 49 | .sharingMode = VK_SHARING_MODE_EXCLUSIVE, |
| 54 | .queueFamilyIndexCount = 0, | 50 | .queueFamilyIndexCount = 0, |
| 55 | .pQueueFamilyIndices = nullptr, | 51 | .pQueueFamilyIndices = nullptr, |
| 56 | }; | 52 | }); |
| 57 | 53 | commit = memory_allocator.Commit(buffer, MemoryUsage::DeviceLocal); | |
| 58 | buffer.handle = device.GetLogical().CreateBuffer(ci); | ||
| 59 | buffer.commit = memory_manager.Commit(buffer.handle, false); | ||
| 60 | } | 54 | } |
| 61 | 55 | ||
| 62 | Buffer::~Buffer() = default; | 56 | Buffer::~Buffer() = default; |
| 63 | 57 | ||
| 64 | void Buffer::Upload(std::size_t offset, std::size_t data_size, const u8* data) { | 58 | void Buffer::Upload(std::size_t offset, std::size_t data_size, const u8* data) { |
| 65 | const auto& staging = staging_pool.GetUnusedBuffer(data_size, true); | 59 | const auto& staging = staging_pool.Request(data_size, MemoryUsage::Upload); |
| 66 | std::memcpy(staging.commit->Map(data_size), data, data_size); | 60 | std::memcpy(staging.mapped_span.data(), data, data_size); |
| 67 | 61 | ||
| 68 | scheduler.RequestOutsideRenderPassOperationContext(); | 62 | scheduler.RequestOutsideRenderPassOperationContext(); |
| 69 | 63 | ||
| 70 | const VkBuffer handle = Handle(); | 64 | const VkBuffer handle = Handle(); |
| 71 | scheduler.Record([staging = *staging.handle, handle, offset, data_size, | 65 | scheduler.Record([staging = staging.buffer, handle, offset, data_size, |
| 72 | &device = device](vk::CommandBuffer cmdbuf) { | 66 | &device = device](vk::CommandBuffer cmdbuf) { |
| 73 | const VkBufferMemoryBarrier read_barrier{ | 67 | const VkBufferMemoryBarrier read_barrier{ |
| 74 | .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, | 68 | .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, |
| @@ -104,12 +98,12 @@ void Buffer::Upload(std::size_t offset, std::size_t data_size, const u8* data) { | |||
| 104 | } | 98 | } |
| 105 | 99 | ||
| 106 | void Buffer::Download(std::size_t offset, std::size_t data_size, u8* data) { | 100 | void Buffer::Download(std::size_t offset, std::size_t data_size, u8* data) { |
| 107 | const auto& staging = staging_pool.GetUnusedBuffer(data_size, true); | 101 | auto staging = staging_pool.Request(data_size, MemoryUsage::Download); |
| 108 | scheduler.RequestOutsideRenderPassOperationContext(); | 102 | scheduler.RequestOutsideRenderPassOperationContext(); |
| 109 | 103 | ||
| 110 | const VkBuffer handle = Handle(); | 104 | const VkBuffer handle = Handle(); |
| 111 | scheduler.Record( | 105 | scheduler.Record( |
| 112 | [staging = *staging.handle, handle, offset, data_size](vk::CommandBuffer cmdbuf) { | 106 | [staging = staging.buffer, handle, offset, data_size](vk::CommandBuffer cmdbuf) { |
| 113 | const VkBufferMemoryBarrier barrier{ | 107 | const VkBufferMemoryBarrier barrier{ |
| 114 | .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, | 108 | .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, |
| 115 | .pNext = nullptr, | 109 | .pNext = nullptr, |
| @@ -130,7 +124,7 @@ void Buffer::Download(std::size_t offset, std::size_t data_size, u8* data) { | |||
| 130 | }); | 124 | }); |
| 131 | scheduler.Finish(); | 125 | scheduler.Finish(); |
| 132 | 126 | ||
| 133 | std::memcpy(data, staging.commit->Map(data_size), data_size); | 127 | std::memcpy(data, staging.mapped_span.data(), data_size); |
| 134 | } | 128 | } |
| 135 | 129 | ||
| 136 | void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, | 130 | void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, |
| @@ -168,29 +162,29 @@ void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst | |||
| 168 | 162 | ||
| 169 | VKBufferCache::VKBufferCache(VideoCore::RasterizerInterface& rasterizer_, | 163 | VKBufferCache::VKBufferCache(VideoCore::RasterizerInterface& rasterizer_, |
| 170 | Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_, | 164 | Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_, |
| 171 | const VKDevice& device_, VKMemoryManager& memory_manager_, | 165 | const Device& device_, MemoryAllocator& memory_allocator_, |
| 172 | VKScheduler& scheduler_, VKStreamBuffer& stream_buffer_, | 166 | VKScheduler& scheduler_, VKStreamBuffer& stream_buffer_, |
| 173 | VKStagingBufferPool& staging_pool_) | 167 | StagingBufferPool& staging_pool_) |
| 174 | : VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer>{rasterizer_, gpu_memory_, | 168 | : VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer>{rasterizer_, gpu_memory_, |
| 175 | cpu_memory_, stream_buffer_}, | 169 | cpu_memory_, stream_buffer_}, |
| 176 | device{device_}, memory_manager{memory_manager_}, scheduler{scheduler_}, staging_pool{ | 170 | device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_}, |
| 177 | staging_pool_} {} | 171 | staging_pool{staging_pool_} {} |
| 178 | 172 | ||
| 179 | VKBufferCache::~VKBufferCache() = default; | 173 | VKBufferCache::~VKBufferCache() = default; |
| 180 | 174 | ||
| 181 | std::shared_ptr<Buffer> VKBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) { | 175 | std::shared_ptr<Buffer> VKBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) { |
| 182 | return std::make_shared<Buffer>(device, memory_manager, scheduler, staging_pool, cpu_addr, | 176 | return std::make_shared<Buffer>(device, memory_allocator, scheduler, staging_pool, cpu_addr, |
| 183 | size); | 177 | size); |
| 184 | } | 178 | } |
| 185 | 179 | ||
| 186 | VKBufferCache::BufferInfo VKBufferCache::GetEmptyBuffer(std::size_t size) { | 180 | VKBufferCache::BufferInfo VKBufferCache::GetEmptyBuffer(std::size_t size) { |
| 187 | size = std::max(size, std::size_t(4)); | 181 | size = std::max(size, std::size_t(4)); |
| 188 | const auto& empty = staging_pool.GetUnusedBuffer(size, false); | 182 | const auto& empty = staging_pool.Request(size, MemoryUsage::DeviceLocal); |
| 189 | scheduler.RequestOutsideRenderPassOperationContext(); | 183 | scheduler.RequestOutsideRenderPassOperationContext(); |
| 190 | scheduler.Record([size, buffer = *empty.handle](vk::CommandBuffer cmdbuf) { | 184 | scheduler.Record([size, buffer = empty.buffer](vk::CommandBuffer cmdbuf) { |
| 191 | cmdbuf.FillBuffer(buffer, 0, size, 0); | 185 | cmdbuf.FillBuffer(buffer, 0, size, 0); |
| 192 | }); | 186 | }); |
| 193 | return {*empty.handle, 0, 0}; | 187 | return {empty.buffer, 0, 0}; |
| 194 | } | 188 | } |
| 195 | 189 | ||
| 196 | } // namespace Vulkan | 190 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index daf498222..41d577510 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h | |||
| @@ -8,21 +8,20 @@ | |||
| 8 | 8 | ||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "video_core/buffer_cache/buffer_cache.h" | 10 | #include "video_core/buffer_cache/buffer_cache.h" |
| 11 | #include "video_core/renderer_vulkan/vk_memory_manager.h" | ||
| 12 | #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" | 11 | #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" |
| 13 | #include "video_core/renderer_vulkan/vk_stream_buffer.h" | 12 | #include "video_core/renderer_vulkan/vk_stream_buffer.h" |
| 14 | #include "video_core/renderer_vulkan/wrapper.h" | 13 | #include "video_core/vulkan_common/vulkan_memory_allocator.h" |
| 14 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 15 | 15 | ||
| 16 | namespace Vulkan { | 16 | namespace Vulkan { |
| 17 | 17 | ||
| 18 | class VKDevice; | 18 | class Device; |
| 19 | class VKMemoryManager; | ||
| 20 | class VKScheduler; | 19 | class VKScheduler; |
| 21 | 20 | ||
| 22 | class Buffer final : public VideoCommon::BufferBlock { | 21 | class Buffer final : public VideoCommon::BufferBlock { |
| 23 | public: | 22 | public: |
| 24 | explicit Buffer(const VKDevice& device, VKMemoryManager& memory_manager, VKScheduler& scheduler, | 23 | explicit Buffer(const Device& device, MemoryAllocator& memory_allocator, VKScheduler& scheduler, |
| 25 | VKStagingBufferPool& staging_pool, VAddr cpu_addr_, std::size_t size_); | 24 | StagingBufferPool& staging_pool, VAddr cpu_addr_, std::size_t size_); |
| 26 | ~Buffer(); | 25 | ~Buffer(); |
| 27 | 26 | ||
| 28 | void Upload(std::size_t offset, std::size_t data_size, const u8* data); | 27 | void Upload(std::size_t offset, std::size_t data_size, const u8* data); |
| @@ -33,7 +32,7 @@ public: | |||
| 33 | std::size_t copy_size); | 32 | std::size_t copy_size); |
| 34 | 33 | ||
| 35 | VkBuffer Handle() const { | 34 | VkBuffer Handle() const { |
| 36 | return *buffer.handle; | 35 | return *buffer; |
| 37 | } | 36 | } |
| 38 | 37 | ||
| 39 | u64 Address() const { | 38 | u64 Address() const { |
| @@ -41,20 +40,21 @@ public: | |||
| 41 | } | 40 | } |
| 42 | 41 | ||
| 43 | private: | 42 | private: |
| 44 | const VKDevice& device; | 43 | const Device& device; |
| 45 | VKScheduler& scheduler; | 44 | VKScheduler& scheduler; |
| 46 | VKStagingBufferPool& staging_pool; | 45 | StagingBufferPool& staging_pool; |
| 47 | 46 | ||
| 48 | VKBuffer buffer; | 47 | vk::Buffer buffer; |
| 48 | MemoryCommit commit; | ||
| 49 | }; | 49 | }; |
| 50 | 50 | ||
| 51 | class VKBufferCache final : public VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer> { | 51 | class VKBufferCache final : public VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer> { |
| 52 | public: | 52 | public: |
| 53 | explicit VKBufferCache(VideoCore::RasterizerInterface& rasterizer, | 53 | explicit VKBufferCache(VideoCore::RasterizerInterface& rasterizer, |
| 54 | Tegra::MemoryManager& gpu_memory, Core::Memory::Memory& cpu_memory, | 54 | Tegra::MemoryManager& gpu_memory, Core::Memory::Memory& cpu_memory, |
| 55 | const VKDevice& device, VKMemoryManager& memory_manager, | 55 | const Device& device, MemoryAllocator& memory_allocator, |
| 56 | VKScheduler& scheduler, VKStreamBuffer& stream_buffer, | 56 | VKScheduler& scheduler, VKStreamBuffer& stream_buffer, |
| 57 | VKStagingBufferPool& staging_pool); | 57 | StagingBufferPool& staging_pool); |
| 58 | ~VKBufferCache(); | 58 | ~VKBufferCache(); |
| 59 | 59 | ||
| 60 | BufferInfo GetEmptyBuffer(std::size_t size) override; | 60 | BufferInfo GetEmptyBuffer(std::size_t size) override; |
| @@ -63,10 +63,10 @@ protected: | |||
| 63 | std::shared_ptr<Buffer> CreateBlock(VAddr cpu_addr, std::size_t size) override; | 63 | std::shared_ptr<Buffer> CreateBlock(VAddr cpu_addr, std::size_t size) override; |
| 64 | 64 | ||
| 65 | private: | 65 | private: |
| 66 | const VKDevice& device; | 66 | const Device& device; |
| 67 | VKMemoryManager& memory_manager; | 67 | MemoryAllocator& memory_allocator; |
| 68 | VKScheduler& scheduler; | 68 | VKScheduler& scheduler; |
| 69 | VKStagingBufferPool& staging_pool; | 69 | StagingBufferPool& staging_pool; |
| 70 | }; | 70 | }; |
| 71 | 71 | ||
| 72 | } // namespace Vulkan | 72 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/vk_command_pool.cpp b/src/video_core/renderer_vulkan/vk_command_pool.cpp index 8f7d6410e..a99df9323 100644 --- a/src/video_core/renderer_vulkan/vk_command_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_command_pool.cpp | |||
| @@ -5,8 +5,8 @@ | |||
| 5 | #include <cstddef> | 5 | #include <cstddef> |
| 6 | 6 | ||
| 7 | #include "video_core/renderer_vulkan/vk_command_pool.h" | 7 | #include "video_core/renderer_vulkan/vk_command_pool.h" |
| 8 | #include "video_core/renderer_vulkan/vk_device.h" | 8 | #include "video_core/vulkan_common/vulkan_device.h" |
| 9 | #include "video_core/renderer_vulkan/wrapper.h" | 9 | #include "video_core/vulkan_common/vulkan_wrapper.h" |
| 10 | 10 | ||
| 11 | namespace Vulkan { | 11 | namespace Vulkan { |
| 12 | 12 | ||
| @@ -17,7 +17,7 @@ struct CommandPool::Pool { | |||
| 17 | vk::CommandBuffers cmdbufs; | 17 | vk::CommandBuffers cmdbufs; |
| 18 | }; | 18 | }; |
| 19 | 19 | ||
| 20 | CommandPool::CommandPool(MasterSemaphore& master_semaphore_, const VKDevice& device_) | 20 | CommandPool::CommandPool(MasterSemaphore& master_semaphore_, const Device& device_) |
| 21 | : ResourcePool(master_semaphore_, COMMAND_BUFFER_POOL_SIZE), device{device_} {} | 21 | : ResourcePool(master_semaphore_, COMMAND_BUFFER_POOL_SIZE), device{device_} {} |
| 22 | 22 | ||
| 23 | CommandPool::~CommandPool() = default; | 23 | CommandPool::~CommandPool() = default; |
diff --git a/src/video_core/renderer_vulkan/vk_command_pool.h b/src/video_core/renderer_vulkan/vk_command_pool.h index 62a7ce3f1..61c26a22a 100644 --- a/src/video_core/renderer_vulkan/vk_command_pool.h +++ b/src/video_core/renderer_vulkan/vk_command_pool.h | |||
| @@ -8,16 +8,16 @@ | |||
| 8 | #include <vector> | 8 | #include <vector> |
| 9 | 9 | ||
| 10 | #include "video_core/renderer_vulkan/vk_resource_pool.h" | 10 | #include "video_core/renderer_vulkan/vk_resource_pool.h" |
| 11 | #include "video_core/renderer_vulkan/wrapper.h" | 11 | #include "video_core/vulkan_common/vulkan_wrapper.h" |
| 12 | 12 | ||
| 13 | namespace Vulkan { | 13 | namespace Vulkan { |
| 14 | 14 | ||
| 15 | class Device; | ||
| 15 | class MasterSemaphore; | 16 | class MasterSemaphore; |
| 16 | class VKDevice; | ||
| 17 | 17 | ||
| 18 | class CommandPool final : public ResourcePool { | 18 | class CommandPool final : public ResourcePool { |
| 19 | public: | 19 | public: |
| 20 | explicit CommandPool(MasterSemaphore& master_semaphore_, const VKDevice& device_); | 20 | explicit CommandPool(MasterSemaphore& master_semaphore_, const Device& device_); |
| 21 | ~CommandPool() override; | 21 | ~CommandPool() override; |
| 22 | 22 | ||
| 23 | void Allocate(size_t begin, size_t end) override; | 23 | void Allocate(size_t begin, size_t end) override; |
| @@ -27,7 +27,7 @@ public: | |||
| 27 | private: | 27 | private: |
| 28 | struct Pool; | 28 | struct Pool; |
| 29 | 29 | ||
| 30 | const VKDevice& device; | 30 | const Device& device; |
| 31 | std::vector<Pool> pools; | 31 | std::vector<Pool> pools; |
| 32 | }; | 32 | }; |
| 33 | 33 | ||
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index 2c030e910..5eb6a54be 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp | |||
| @@ -15,11 +15,11 @@ | |||
| 15 | #include "video_core/host_shaders/vulkan_uint8_comp_spv.h" | 15 | #include "video_core/host_shaders/vulkan_uint8_comp_spv.h" |
| 16 | #include "video_core/renderer_vulkan/vk_compute_pass.h" | 16 | #include "video_core/renderer_vulkan/vk_compute_pass.h" |
| 17 | #include "video_core/renderer_vulkan/vk_descriptor_pool.h" | 17 | #include "video_core/renderer_vulkan/vk_descriptor_pool.h" |
| 18 | #include "video_core/renderer_vulkan/vk_device.h" | ||
| 19 | #include "video_core/renderer_vulkan/vk_scheduler.h" | 18 | #include "video_core/renderer_vulkan/vk_scheduler.h" |
| 20 | #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" | 19 | #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" |
| 21 | #include "video_core/renderer_vulkan/vk_update_descriptor.h" | 20 | #include "video_core/renderer_vulkan/vk_update_descriptor.h" |
| 22 | #include "video_core/renderer_vulkan/wrapper.h" | 21 | #include "video_core/vulkan_common/vulkan_device.h" |
| 22 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 23 | 23 | ||
| 24 | namespace Vulkan { | 24 | namespace Vulkan { |
| 25 | 25 | ||
| @@ -86,7 +86,7 @@ VkDescriptorUpdateTemplateEntryKHR BuildInputOutputDescriptorUpdateTemplate() { | |||
| 86 | 86 | ||
| 87 | } // Anonymous namespace | 87 | } // Anonymous namespace |
| 88 | 88 | ||
| 89 | VKComputePass::VKComputePass(const VKDevice& device, VKDescriptorPool& descriptor_pool, | 89 | VKComputePass::VKComputePass(const Device& device, VKDescriptorPool& descriptor_pool, |
| 90 | vk::Span<VkDescriptorSetLayoutBinding> bindings, | 90 | vk::Span<VkDescriptorSetLayoutBinding> bindings, |
| 91 | vk::Span<VkDescriptorUpdateTemplateEntryKHR> templates, | 91 | vk::Span<VkDescriptorUpdateTemplateEntryKHR> templates, |
| 92 | vk::Span<VkPushConstantRange> push_constants, | 92 | vk::Span<VkPushConstantRange> push_constants, |
| @@ -162,9 +162,9 @@ VkDescriptorSet VKComputePass::CommitDescriptorSet( | |||
| 162 | return set; | 162 | return set; |
| 163 | } | 163 | } |
| 164 | 164 | ||
| 165 | QuadArrayPass::QuadArrayPass(const VKDevice& device_, VKScheduler& scheduler_, | 165 | QuadArrayPass::QuadArrayPass(const Device& device_, VKScheduler& scheduler_, |
| 166 | VKDescriptorPool& descriptor_pool_, | 166 | VKDescriptorPool& descriptor_pool_, |
| 167 | VKStagingBufferPool& staging_buffer_pool_, | 167 | StagingBufferPool& staging_buffer_pool_, |
| 168 | VKUpdateDescriptorQueue& update_descriptor_queue_) | 168 | VKUpdateDescriptorQueue& update_descriptor_queue_) |
| 169 | : VKComputePass(device_, descriptor_pool_, BuildQuadArrayPassDescriptorSetLayoutBinding(), | 169 | : VKComputePass(device_, descriptor_pool_, BuildQuadArrayPassDescriptorSetLayoutBinding(), |
| 170 | BuildQuadArrayPassDescriptorUpdateTemplateEntry(), | 170 | BuildQuadArrayPassDescriptorUpdateTemplateEntry(), |
| @@ -177,18 +177,18 @@ QuadArrayPass::~QuadArrayPass() = default; | |||
| 177 | std::pair<VkBuffer, VkDeviceSize> QuadArrayPass::Assemble(u32 num_vertices, u32 first) { | 177 | std::pair<VkBuffer, VkDeviceSize> QuadArrayPass::Assemble(u32 num_vertices, u32 first) { |
| 178 | const u32 num_triangle_vertices = (num_vertices / 4) * 6; | 178 | const u32 num_triangle_vertices = (num_vertices / 4) * 6; |
| 179 | const std::size_t staging_size = num_triangle_vertices * sizeof(u32); | 179 | const std::size_t staging_size = num_triangle_vertices * sizeof(u32); |
| 180 | auto& buffer = staging_buffer_pool.GetUnusedBuffer(staging_size, false); | 180 | const auto staging_ref = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal); |
| 181 | 181 | ||
| 182 | update_descriptor_queue.Acquire(); | 182 | update_descriptor_queue.Acquire(); |
| 183 | update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size); | 183 | update_descriptor_queue.AddBuffer(staging_ref.buffer, 0, staging_size); |
| 184 | const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue); | 184 | const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue); |
| 185 | 185 | ||
| 186 | scheduler.RequestOutsideRenderPassOperationContext(); | 186 | scheduler.RequestOutsideRenderPassOperationContext(); |
| 187 | 187 | ||
| 188 | ASSERT(num_vertices % 4 == 0); | 188 | ASSERT(num_vertices % 4 == 0); |
| 189 | const u32 num_quads = num_vertices / 4; | 189 | const u32 num_quads = num_vertices / 4; |
| 190 | scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = *buffer.handle, num_quads, | 190 | scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = staging_ref.buffer, |
| 191 | first, set](vk::CommandBuffer cmdbuf) { | 191 | num_quads, first, set](vk::CommandBuffer cmdbuf) { |
| 192 | constexpr u32 dispatch_size = 1024; | 192 | constexpr u32 dispatch_size = 1024; |
| 193 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); | 193 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); |
| 194 | cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, layout, 0, set, {}); | 194 | cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, layout, 0, set, {}); |
| @@ -208,11 +208,11 @@ std::pair<VkBuffer, VkDeviceSize> QuadArrayPass::Assemble(u32 num_vertices, u32 | |||
| 208 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, | 208 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, |
| 209 | VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, {}, {barrier}, {}); | 209 | VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, {}, {barrier}, {}); |
| 210 | }); | 210 | }); |
| 211 | return {*buffer.handle, 0}; | 211 | return {staging_ref.buffer, 0}; |
| 212 | } | 212 | } |
| 213 | 213 | ||
| 214 | Uint8Pass::Uint8Pass(const VKDevice& device, VKScheduler& scheduler_, | 214 | Uint8Pass::Uint8Pass(const Device& device, VKScheduler& scheduler_, |
| 215 | VKDescriptorPool& descriptor_pool, VKStagingBufferPool& staging_buffer_pool_, | 215 | VKDescriptorPool& descriptor_pool, StagingBufferPool& staging_buffer_pool_, |
| 216 | VKUpdateDescriptorQueue& update_descriptor_queue_) | 216 | VKUpdateDescriptorQueue& update_descriptor_queue_) |
| 217 | : VKComputePass(device, descriptor_pool, BuildInputOutputDescriptorSetBindings(), | 217 | : VKComputePass(device, descriptor_pool, BuildInputOutputDescriptorSetBindings(), |
| 218 | BuildInputOutputDescriptorUpdateTemplate(), {}, VULKAN_UINT8_COMP_SPV), | 218 | BuildInputOutputDescriptorUpdateTemplate(), {}, VULKAN_UINT8_COMP_SPV), |
| @@ -224,15 +224,15 @@ Uint8Pass::~Uint8Pass() = default; | |||
| 224 | std::pair<VkBuffer, u64> Uint8Pass::Assemble(u32 num_vertices, VkBuffer src_buffer, | 224 | std::pair<VkBuffer, u64> Uint8Pass::Assemble(u32 num_vertices, VkBuffer src_buffer, |
| 225 | u64 src_offset) { | 225 | u64 src_offset) { |
| 226 | const u32 staging_size = static_cast<u32>(num_vertices * sizeof(u16)); | 226 | const u32 staging_size = static_cast<u32>(num_vertices * sizeof(u16)); |
| 227 | auto& buffer = staging_buffer_pool.GetUnusedBuffer(staging_size, false); | 227 | const auto staging_ref = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal); |
| 228 | 228 | ||
| 229 | update_descriptor_queue.Acquire(); | 229 | update_descriptor_queue.Acquire(); |
| 230 | update_descriptor_queue.AddBuffer(src_buffer, src_offset, num_vertices); | 230 | update_descriptor_queue.AddBuffer(src_buffer, src_offset, num_vertices); |
| 231 | update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size); | 231 | update_descriptor_queue.AddBuffer(staging_ref.buffer, 0, staging_size); |
| 232 | const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue); | 232 | const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue); |
| 233 | 233 | ||
| 234 | scheduler.RequestOutsideRenderPassOperationContext(); | 234 | scheduler.RequestOutsideRenderPassOperationContext(); |
| 235 | scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = *buffer.handle, set, | 235 | scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = staging_ref.buffer, set, |
| 236 | num_vertices](vk::CommandBuffer cmdbuf) { | 236 | num_vertices](vk::CommandBuffer cmdbuf) { |
| 237 | constexpr u32 dispatch_size = 1024; | 237 | constexpr u32 dispatch_size = 1024; |
| 238 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); | 238 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); |
| @@ -252,12 +252,12 @@ std::pair<VkBuffer, u64> Uint8Pass::Assemble(u32 num_vertices, VkBuffer src_buff | |||
| 252 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, | 252 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, |
| 253 | VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, {}, barrier, {}); | 253 | VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, {}, barrier, {}); |
| 254 | }); | 254 | }); |
| 255 | return {*buffer.handle, 0}; | 255 | return {staging_ref.buffer, 0}; |
| 256 | } | 256 | } |
| 257 | 257 | ||
| 258 | QuadIndexedPass::QuadIndexedPass(const VKDevice& device_, VKScheduler& scheduler_, | 258 | QuadIndexedPass::QuadIndexedPass(const Device& device_, VKScheduler& scheduler_, |
| 259 | VKDescriptorPool& descriptor_pool_, | 259 | VKDescriptorPool& descriptor_pool_, |
| 260 | VKStagingBufferPool& staging_buffer_pool_, | 260 | StagingBufferPool& staging_buffer_pool_, |
| 261 | VKUpdateDescriptorQueue& update_descriptor_queue_) | 261 | VKUpdateDescriptorQueue& update_descriptor_queue_) |
| 262 | : VKComputePass(device_, descriptor_pool_, BuildInputOutputDescriptorSetBindings(), | 262 | : VKComputePass(device_, descriptor_pool_, BuildInputOutputDescriptorSetBindings(), |
| 263 | BuildInputOutputDescriptorUpdateTemplate(), | 263 | BuildInputOutputDescriptorUpdateTemplate(), |
| @@ -286,15 +286,15 @@ std::pair<VkBuffer, u64> QuadIndexedPass::Assemble( | |||
| 286 | const u32 num_tri_vertices = (num_vertices / 4) * 6; | 286 | const u32 num_tri_vertices = (num_vertices / 4) * 6; |
| 287 | 287 | ||
| 288 | const std::size_t staging_size = num_tri_vertices * sizeof(u32); | 288 | const std::size_t staging_size = num_tri_vertices * sizeof(u32); |
| 289 | auto& buffer = staging_buffer_pool.GetUnusedBuffer(staging_size, false); | 289 | const auto staging_ref = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal); |
| 290 | 290 | ||
| 291 | update_descriptor_queue.Acquire(); | 291 | update_descriptor_queue.Acquire(); |
| 292 | update_descriptor_queue.AddBuffer(src_buffer, src_offset, input_size); | 292 | update_descriptor_queue.AddBuffer(src_buffer, src_offset, input_size); |
| 293 | update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size); | 293 | update_descriptor_queue.AddBuffer(staging_ref.buffer, 0, staging_size); |
| 294 | const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue); | 294 | const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue); |
| 295 | 295 | ||
| 296 | scheduler.RequestOutsideRenderPassOperationContext(); | 296 | scheduler.RequestOutsideRenderPassOperationContext(); |
| 297 | scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = *buffer.handle, set, | 297 | scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = staging_ref.buffer, set, |
| 298 | num_tri_vertices, base_vertex, index_shift](vk::CommandBuffer cmdbuf) { | 298 | num_tri_vertices, base_vertex, index_shift](vk::CommandBuffer cmdbuf) { |
| 299 | static constexpr u32 dispatch_size = 1024; | 299 | static constexpr u32 dispatch_size = 1024; |
| 300 | const std::array push_constants = {base_vertex, index_shift}; | 300 | const std::array push_constants = {base_vertex, index_shift}; |
| @@ -317,7 +317,7 @@ std::pair<VkBuffer, u64> QuadIndexedPass::Assemble( | |||
| 317 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, | 317 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, |
| 318 | VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, {}, barrier, {}); | 318 | VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, {}, barrier, {}); |
| 319 | }); | 319 | }); |
| 320 | return {*buffer.handle, 0}; | 320 | return {staging_ref.buffer, 0}; |
| 321 | } | 321 | } |
| 322 | 322 | ||
| 323 | } // namespace Vulkan | 323 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.h b/src/video_core/renderer_vulkan/vk_compute_pass.h index abdf61e2c..f5c6f5f17 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.h +++ b/src/video_core/renderer_vulkan/vk_compute_pass.h | |||
| @@ -11,18 +11,18 @@ | |||
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "video_core/engines/maxwell_3d.h" | 12 | #include "video_core/engines/maxwell_3d.h" |
| 13 | #include "video_core/renderer_vulkan/vk_descriptor_pool.h" | 13 | #include "video_core/renderer_vulkan/vk_descriptor_pool.h" |
| 14 | #include "video_core/renderer_vulkan/wrapper.h" | 14 | #include "video_core/vulkan_common/vulkan_wrapper.h" |
| 15 | 15 | ||
| 16 | namespace Vulkan { | 16 | namespace Vulkan { |
| 17 | 17 | ||
| 18 | class VKDevice; | 18 | class Device; |
| 19 | class StagingBufferPool; | ||
| 19 | class VKScheduler; | 20 | class VKScheduler; |
| 20 | class VKStagingBufferPool; | ||
| 21 | class VKUpdateDescriptorQueue; | 21 | class VKUpdateDescriptorQueue; |
| 22 | 22 | ||
| 23 | class VKComputePass { | 23 | class VKComputePass { |
| 24 | public: | 24 | public: |
| 25 | explicit VKComputePass(const VKDevice& device, VKDescriptorPool& descriptor_pool, | 25 | explicit VKComputePass(const Device& device, VKDescriptorPool& descriptor_pool, |
| 26 | vk::Span<VkDescriptorSetLayoutBinding> bindings, | 26 | vk::Span<VkDescriptorSetLayoutBinding> bindings, |
| 27 | vk::Span<VkDescriptorUpdateTemplateEntryKHR> templates, | 27 | vk::Span<VkDescriptorUpdateTemplateEntryKHR> templates, |
| 28 | vk::Span<VkPushConstantRange> push_constants, std::span<const u32> code); | 28 | vk::Span<VkPushConstantRange> push_constants, std::span<const u32> code); |
| @@ -43,9 +43,9 @@ private: | |||
| 43 | 43 | ||
| 44 | class QuadArrayPass final : public VKComputePass { | 44 | class QuadArrayPass final : public VKComputePass { |
| 45 | public: | 45 | public: |
| 46 | explicit QuadArrayPass(const VKDevice& device_, VKScheduler& scheduler_, | 46 | explicit QuadArrayPass(const Device& device_, VKScheduler& scheduler_, |
| 47 | VKDescriptorPool& descriptor_pool_, | 47 | VKDescriptorPool& descriptor_pool_, |
| 48 | VKStagingBufferPool& staging_buffer_pool_, | 48 | StagingBufferPool& staging_buffer_pool_, |
| 49 | VKUpdateDescriptorQueue& update_descriptor_queue_); | 49 | VKUpdateDescriptorQueue& update_descriptor_queue_); |
| 50 | ~QuadArrayPass(); | 50 | ~QuadArrayPass(); |
| 51 | 51 | ||
| @@ -53,15 +53,14 @@ public: | |||
| 53 | 53 | ||
| 54 | private: | 54 | private: |
| 55 | VKScheduler& scheduler; | 55 | VKScheduler& scheduler; |
| 56 | VKStagingBufferPool& staging_buffer_pool; | 56 | StagingBufferPool& staging_buffer_pool; |
| 57 | VKUpdateDescriptorQueue& update_descriptor_queue; | 57 | VKUpdateDescriptorQueue& update_descriptor_queue; |
| 58 | }; | 58 | }; |
| 59 | 59 | ||
| 60 | class Uint8Pass final : public VKComputePass { | 60 | class Uint8Pass final : public VKComputePass { |
| 61 | public: | 61 | public: |
| 62 | explicit Uint8Pass(const VKDevice& device_, VKScheduler& scheduler_, | 62 | explicit Uint8Pass(const Device& device_, VKScheduler& scheduler_, |
| 63 | VKDescriptorPool& descriptor_pool_, | 63 | VKDescriptorPool& descriptor_pool_, StagingBufferPool& staging_buffer_pool_, |
| 64 | VKStagingBufferPool& staging_buffer_pool_, | ||
| 65 | VKUpdateDescriptorQueue& update_descriptor_queue_); | 64 | VKUpdateDescriptorQueue& update_descriptor_queue_); |
| 66 | ~Uint8Pass(); | 65 | ~Uint8Pass(); |
| 67 | 66 | ||
| @@ -69,15 +68,15 @@ public: | |||
| 69 | 68 | ||
| 70 | private: | 69 | private: |
| 71 | VKScheduler& scheduler; | 70 | VKScheduler& scheduler; |
| 72 | VKStagingBufferPool& staging_buffer_pool; | 71 | StagingBufferPool& staging_buffer_pool; |
| 73 | VKUpdateDescriptorQueue& update_descriptor_queue; | 72 | VKUpdateDescriptorQueue& update_descriptor_queue; |
| 74 | }; | 73 | }; |
| 75 | 74 | ||
| 76 | class QuadIndexedPass final : public VKComputePass { | 75 | class QuadIndexedPass final : public VKComputePass { |
| 77 | public: | 76 | public: |
| 78 | explicit QuadIndexedPass(const VKDevice& device_, VKScheduler& scheduler_, | 77 | explicit QuadIndexedPass(const Device& device_, VKScheduler& scheduler_, |
| 79 | VKDescriptorPool& descriptor_pool_, | 78 | VKDescriptorPool& descriptor_pool_, |
| 80 | VKStagingBufferPool& staging_buffer_pool_, | 79 | StagingBufferPool& staging_buffer_pool_, |
| 81 | VKUpdateDescriptorQueue& update_descriptor_queue_); | 80 | VKUpdateDescriptorQueue& update_descriptor_queue_); |
| 82 | ~QuadIndexedPass(); | 81 | ~QuadIndexedPass(); |
| 83 | 82 | ||
| @@ -87,7 +86,7 @@ public: | |||
| 87 | 86 | ||
| 88 | private: | 87 | private: |
| 89 | VKScheduler& scheduler; | 88 | VKScheduler& scheduler; |
| 90 | VKStagingBufferPool& staging_buffer_pool; | 89 | StagingBufferPool& staging_buffer_pool; |
| 91 | VKUpdateDescriptorQueue& update_descriptor_queue; | 90 | VKUpdateDescriptorQueue& update_descriptor_queue; |
| 92 | }; | 91 | }; |
| 93 | 92 | ||
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index 62f44d6da..3a48219b7 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp | |||
| @@ -6,16 +6,16 @@ | |||
| 6 | 6 | ||
| 7 | #include "video_core/renderer_vulkan/vk_compute_pipeline.h" | 7 | #include "video_core/renderer_vulkan/vk_compute_pipeline.h" |
| 8 | #include "video_core/renderer_vulkan/vk_descriptor_pool.h" | 8 | #include "video_core/renderer_vulkan/vk_descriptor_pool.h" |
| 9 | #include "video_core/renderer_vulkan/vk_device.h" | ||
| 10 | #include "video_core/renderer_vulkan/vk_pipeline_cache.h" | 9 | #include "video_core/renderer_vulkan/vk_pipeline_cache.h" |
| 11 | #include "video_core/renderer_vulkan/vk_scheduler.h" | 10 | #include "video_core/renderer_vulkan/vk_scheduler.h" |
| 12 | #include "video_core/renderer_vulkan/vk_shader_decompiler.h" | 11 | #include "video_core/renderer_vulkan/vk_shader_decompiler.h" |
| 13 | #include "video_core/renderer_vulkan/vk_update_descriptor.h" | 12 | #include "video_core/renderer_vulkan/vk_update_descriptor.h" |
| 14 | #include "video_core/renderer_vulkan/wrapper.h" | 13 | #include "video_core/vulkan_common/vulkan_device.h" |
| 14 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 15 | 15 | ||
| 16 | namespace Vulkan { | 16 | namespace Vulkan { |
| 17 | 17 | ||
| 18 | VKComputePipeline::VKComputePipeline(const VKDevice& device_, VKScheduler& scheduler_, | 18 | VKComputePipeline::VKComputePipeline(const Device& device_, VKScheduler& scheduler_, |
| 19 | VKDescriptorPool& descriptor_pool_, | 19 | VKDescriptorPool& descriptor_pool_, |
| 20 | VKUpdateDescriptorQueue& update_descriptor_queue_, | 20 | VKUpdateDescriptorQueue& update_descriptor_queue_, |
| 21 | const SPIRVShader& shader_) | 21 | const SPIRVShader& shader_) |
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.h b/src/video_core/renderer_vulkan/vk_compute_pipeline.h index 49e2113a2..7e16575ac 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.h | |||
| @@ -7,17 +7,17 @@ | |||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "video_core/renderer_vulkan/vk_descriptor_pool.h" | 8 | #include "video_core/renderer_vulkan/vk_descriptor_pool.h" |
| 9 | #include "video_core/renderer_vulkan/vk_shader_decompiler.h" | 9 | #include "video_core/renderer_vulkan/vk_shader_decompiler.h" |
| 10 | #include "video_core/renderer_vulkan/wrapper.h" | 10 | #include "video_core/vulkan_common/vulkan_wrapper.h" |
| 11 | 11 | ||
| 12 | namespace Vulkan { | 12 | namespace Vulkan { |
| 13 | 13 | ||
| 14 | class VKDevice; | 14 | class Device; |
| 15 | class VKScheduler; | 15 | class VKScheduler; |
| 16 | class VKUpdateDescriptorQueue; | 16 | class VKUpdateDescriptorQueue; |
| 17 | 17 | ||
| 18 | class VKComputePipeline final { | 18 | class VKComputePipeline final { |
| 19 | public: | 19 | public: |
| 20 | explicit VKComputePipeline(const VKDevice& device_, VKScheduler& scheduler_, | 20 | explicit VKComputePipeline(const Device& device_, VKScheduler& scheduler_, |
| 21 | VKDescriptorPool& descriptor_pool_, | 21 | VKDescriptorPool& descriptor_pool_, |
| 22 | VKUpdateDescriptorQueue& update_descriptor_queue_, | 22 | VKUpdateDescriptorQueue& update_descriptor_queue_, |
| 23 | const SPIRVShader& shader_); | 23 | const SPIRVShader& shader_); |
| @@ -48,7 +48,7 @@ private: | |||
| 48 | 48 | ||
| 49 | vk::Pipeline CreatePipeline() const; | 49 | vk::Pipeline CreatePipeline() const; |
| 50 | 50 | ||
| 51 | const VKDevice& device; | 51 | const Device& device; |
| 52 | VKScheduler& scheduler; | 52 | VKScheduler& scheduler; |
| 53 | ShaderEntries entries; | 53 | ShaderEntries entries; |
| 54 | 54 | ||
diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp index f38e089d5..ef9fb5910 100644 --- a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp | |||
| @@ -6,10 +6,10 @@ | |||
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "video_core/renderer_vulkan/vk_descriptor_pool.h" | 8 | #include "video_core/renderer_vulkan/vk_descriptor_pool.h" |
| 9 | #include "video_core/renderer_vulkan/vk_device.h" | ||
| 10 | #include "video_core/renderer_vulkan/vk_resource_pool.h" | 9 | #include "video_core/renderer_vulkan/vk_resource_pool.h" |
| 11 | #include "video_core/renderer_vulkan/vk_scheduler.h" | 10 | #include "video_core/renderer_vulkan/vk_scheduler.h" |
| 12 | #include "video_core/renderer_vulkan/wrapper.h" | 11 | #include "video_core/vulkan_common/vulkan_device.h" |
| 12 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 13 | 13 | ||
| 14 | namespace Vulkan { | 14 | namespace Vulkan { |
| 15 | 15 | ||
| @@ -32,7 +32,7 @@ void DescriptorAllocator::Allocate(std::size_t begin, std::size_t end) { | |||
| 32 | descriptors_allocations.push_back(descriptor_pool.AllocateDescriptors(layout, end - begin)); | 32 | descriptors_allocations.push_back(descriptor_pool.AllocateDescriptors(layout, end - begin)); |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | VKDescriptorPool::VKDescriptorPool(const VKDevice& device_, VKScheduler& scheduler) | 35 | VKDescriptorPool::VKDescriptorPool(const Device& device_, VKScheduler& scheduler) |
| 36 | : device{device_}, master_semaphore{scheduler.GetMasterSemaphore()}, active_pool{ | 36 | : device{device_}, master_semaphore{scheduler.GetMasterSemaphore()}, active_pool{ |
| 37 | AllocateNewPool()} {} | 37 | AllocateNewPool()} {} |
| 38 | 38 | ||
diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.h b/src/video_core/renderer_vulkan/vk_descriptor_pool.h index 544f32a20..f892be7be 100644 --- a/src/video_core/renderer_vulkan/vk_descriptor_pool.h +++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.h | |||
| @@ -7,11 +7,11 @@ | |||
| 7 | #include <vector> | 7 | #include <vector> |
| 8 | 8 | ||
| 9 | #include "video_core/renderer_vulkan/vk_resource_pool.h" | 9 | #include "video_core/renderer_vulkan/vk_resource_pool.h" |
| 10 | #include "video_core/renderer_vulkan/wrapper.h" | 10 | #include "video_core/vulkan_common/vulkan_wrapper.h" |
| 11 | 11 | ||
| 12 | namespace Vulkan { | 12 | namespace Vulkan { |
| 13 | 13 | ||
| 14 | class VKDevice; | 14 | class Device; |
| 15 | class VKDescriptorPool; | 15 | class VKDescriptorPool; |
| 16 | class VKScheduler; | 16 | class VKScheduler; |
| 17 | 17 | ||
| @@ -39,7 +39,7 @@ class VKDescriptorPool final { | |||
| 39 | friend DescriptorAllocator; | 39 | friend DescriptorAllocator; |
| 40 | 40 | ||
| 41 | public: | 41 | public: |
| 42 | explicit VKDescriptorPool(const VKDevice& device, VKScheduler& scheduler); | 42 | explicit VKDescriptorPool(const Device& device, VKScheduler& scheduler); |
| 43 | ~VKDescriptorPool(); | 43 | ~VKDescriptorPool(); |
| 44 | 44 | ||
| 45 | VKDescriptorPool(const VKDescriptorPool&) = delete; | 45 | VKDescriptorPool(const VKDescriptorPool&) = delete; |
| @@ -50,7 +50,7 @@ private: | |||
| 50 | 50 | ||
| 51 | vk::DescriptorSets AllocateDescriptors(VkDescriptorSetLayout layout, std::size_t count); | 51 | vk::DescriptorSets AllocateDescriptors(VkDescriptorSetLayout layout, std::size_t count); |
| 52 | 52 | ||
| 53 | const VKDevice& device; | 53 | const Device& device; |
| 54 | MasterSemaphore& master_semaphore; | 54 | MasterSemaphore& master_semaphore; |
| 55 | 55 | ||
| 56 | std::vector<vk::DescriptorPool> pools; | 56 | std::vector<vk::DescriptorPool> pools; |
diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.cpp b/src/video_core/renderer_vulkan/vk_fence_manager.cpp index 774a12a53..6cd00884d 100644 --- a/src/video_core/renderer_vulkan/vk_fence_manager.cpp +++ b/src/video_core/renderer_vulkan/vk_fence_manager.cpp | |||
| @@ -3,24 +3,21 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <memory> | 5 | #include <memory> |
| 6 | #include <thread> | ||
| 7 | 6 | ||
| 8 | #include "video_core/renderer_vulkan/vk_buffer_cache.h" | 7 | #include "video_core/renderer_vulkan/vk_buffer_cache.h" |
| 9 | #include "video_core/renderer_vulkan/vk_device.h" | ||
| 10 | #include "video_core/renderer_vulkan/vk_fence_manager.h" | 8 | #include "video_core/renderer_vulkan/vk_fence_manager.h" |
| 11 | #include "video_core/renderer_vulkan/vk_scheduler.h" | 9 | #include "video_core/renderer_vulkan/vk_scheduler.h" |
| 12 | #include "video_core/renderer_vulkan/vk_texture_cache.h" | 10 | #include "video_core/renderer_vulkan/vk_texture_cache.h" |
| 13 | #include "video_core/renderer_vulkan/wrapper.h" | 11 | #include "video_core/vulkan_common/vulkan_device.h" |
| 12 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 14 | 13 | ||
| 15 | namespace Vulkan { | 14 | namespace Vulkan { |
| 16 | 15 | ||
| 17 | InnerFence::InnerFence(const VKDevice& device_, VKScheduler& scheduler_, u32 payload_, | 16 | InnerFence::InnerFence(VKScheduler& scheduler_, u32 payload_, bool is_stubbed_) |
| 18 | bool is_stubbed_) | 17 | : FenceBase{payload_, is_stubbed_}, scheduler{scheduler_} {} |
| 19 | : FenceBase{payload_, is_stubbed_}, device{device_}, scheduler{scheduler_} {} | ||
| 20 | 18 | ||
| 21 | InnerFence::InnerFence(const VKDevice& device_, VKScheduler& scheduler_, GPUVAddr address_, | 19 | InnerFence::InnerFence(VKScheduler& scheduler_, GPUVAddr address_, u32 payload_, bool is_stubbed_) |
| 22 | u32 payload_, bool is_stubbed_) | 20 | : FenceBase{address_, payload_, is_stubbed_}, scheduler{scheduler_} {} |
| 23 | : FenceBase{address_, payload_, is_stubbed_}, device{device_}, scheduler{scheduler_} {} | ||
| 24 | 21 | ||
| 25 | InnerFence::~InnerFence() = default; | 22 | InnerFence::~InnerFence() = default; |
| 26 | 23 | ||
| @@ -28,63 +25,38 @@ void InnerFence::Queue() { | |||
| 28 | if (is_stubbed) { | 25 | if (is_stubbed) { |
| 29 | return; | 26 | return; |
| 30 | } | 27 | } |
| 31 | ASSERT(!event); | 28 | // Get the current tick so we can wait for it |
| 32 | 29 | wait_tick = scheduler.CurrentTick(); | |
| 33 | event = device.GetLogical().CreateEvent(); | 30 | scheduler.Flush(); |
| 34 | ticks = scheduler.CurrentTick(); | ||
| 35 | |||
| 36 | scheduler.RequestOutsideRenderPassOperationContext(); | ||
| 37 | scheduler.Record([event = *event](vk::CommandBuffer cmdbuf) { | ||
| 38 | cmdbuf.SetEvent(event, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); | ||
| 39 | }); | ||
| 40 | } | 31 | } |
| 41 | 32 | ||
| 42 | bool InnerFence::IsSignaled() const { | 33 | bool InnerFence::IsSignaled() const { |
| 43 | if (is_stubbed) { | 34 | if (is_stubbed) { |
| 44 | return true; | 35 | return true; |
| 45 | } | 36 | } |
| 46 | ASSERT(event); | 37 | return scheduler.IsFree(wait_tick); |
| 47 | return IsEventSignalled(); | ||
| 48 | } | 38 | } |
| 49 | 39 | ||
| 50 | void InnerFence::Wait() { | 40 | void InnerFence::Wait() { |
| 51 | if (is_stubbed) { | 41 | if (is_stubbed) { |
| 52 | return; | 42 | return; |
| 53 | } | 43 | } |
| 54 | ASSERT(event); | 44 | scheduler.Wait(wait_tick); |
| 55 | |||
| 56 | if (ticks >= scheduler.CurrentTick()) { | ||
| 57 | scheduler.Flush(); | ||
| 58 | } | ||
| 59 | while (!IsEventSignalled()) { | ||
| 60 | std::this_thread::yield(); | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | bool InnerFence::IsEventSignalled() const { | ||
| 65 | switch (const VkResult result = event.GetStatus()) { | ||
| 66 | case VK_EVENT_SET: | ||
| 67 | return true; | ||
| 68 | case VK_EVENT_RESET: | ||
| 69 | return false; | ||
| 70 | default: | ||
| 71 | throw vk::Exception(result); | ||
| 72 | } | ||
| 73 | } | 45 | } |
| 74 | 46 | ||
| 75 | VKFenceManager::VKFenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::GPU& gpu_, | 47 | VKFenceManager::VKFenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::GPU& gpu_, |
| 76 | Tegra::MemoryManager& memory_manager_, TextureCache& texture_cache_, | 48 | Tegra::MemoryManager& memory_manager_, TextureCache& texture_cache_, |
| 77 | VKBufferCache& buffer_cache_, VKQueryCache& query_cache_, | 49 | VKBufferCache& buffer_cache_, VKQueryCache& query_cache_, |
| 78 | const VKDevice& device_, VKScheduler& scheduler_) | 50 | VKScheduler& scheduler_) |
| 79 | : GenericFenceManager{rasterizer_, gpu_, texture_cache_, buffer_cache_, query_cache_}, | 51 | : GenericFenceManager{rasterizer_, gpu_, texture_cache_, buffer_cache_, query_cache_}, |
| 80 | device{device_}, scheduler{scheduler_} {} | 52 | scheduler{scheduler_} {} |
| 81 | 53 | ||
| 82 | Fence VKFenceManager::CreateFence(u32 value, bool is_stubbed) { | 54 | Fence VKFenceManager::CreateFence(u32 value, bool is_stubbed) { |
| 83 | return std::make_shared<InnerFence>(device, scheduler, value, is_stubbed); | 55 | return std::make_shared<InnerFence>(scheduler, value, is_stubbed); |
| 84 | } | 56 | } |
| 85 | 57 | ||
| 86 | Fence VKFenceManager::CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) { | 58 | Fence VKFenceManager::CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) { |
| 87 | return std::make_shared<InnerFence>(device, scheduler, addr, value, is_stubbed); | 59 | return std::make_shared<InnerFence>(scheduler, addr, value, is_stubbed); |
| 88 | } | 60 | } |
| 89 | 61 | ||
| 90 | void VKFenceManager::QueueFence(Fence& fence) { | 62 | void VKFenceManager::QueueFence(Fence& fence) { |
diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.h b/src/video_core/renderer_vulkan/vk_fence_manager.h index c2869e8e3..9c5e5aa8f 100644 --- a/src/video_core/renderer_vulkan/vk_fence_manager.h +++ b/src/video_core/renderer_vulkan/vk_fence_manager.h | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | #include "video_core/fence_manager.h" | 9 | #include "video_core/fence_manager.h" |
| 10 | #include "video_core/renderer_vulkan/vk_buffer_cache.h" | 10 | #include "video_core/renderer_vulkan/vk_buffer_cache.h" |
| 11 | #include "video_core/renderer_vulkan/vk_texture_cache.h" | 11 | #include "video_core/renderer_vulkan/vk_texture_cache.h" |
| 12 | #include "video_core/renderer_vulkan/wrapper.h" | 12 | #include "video_core/vulkan_common/vulkan_wrapper.h" |
| 13 | 13 | ||
| 14 | namespace Core { | 14 | namespace Core { |
| 15 | class System; | 15 | class System; |
| @@ -21,17 +21,15 @@ class RasterizerInterface; | |||
| 21 | 21 | ||
| 22 | namespace Vulkan { | 22 | namespace Vulkan { |
| 23 | 23 | ||
| 24 | class Device; | ||
| 24 | class VKBufferCache; | 25 | class VKBufferCache; |
| 25 | class VKDevice; | ||
| 26 | class VKQueryCache; | 26 | class VKQueryCache; |
| 27 | class VKScheduler; | 27 | class VKScheduler; |
| 28 | 28 | ||
| 29 | class InnerFence : public VideoCommon::FenceBase { | 29 | class InnerFence : public VideoCommon::FenceBase { |
| 30 | public: | 30 | public: |
| 31 | explicit InnerFence(const VKDevice& device_, VKScheduler& scheduler_, u32 payload_, | 31 | explicit InnerFence(VKScheduler& scheduler_, u32 payload_, bool is_stubbed_); |
| 32 | bool is_stubbed_); | 32 | explicit InnerFence(VKScheduler& scheduler_, GPUVAddr address_, u32 payload_, bool is_stubbed_); |
| 33 | explicit InnerFence(const VKDevice& device_, VKScheduler& scheduler_, GPUVAddr address_, | ||
| 34 | u32 payload_, bool is_stubbed_); | ||
| 35 | ~InnerFence(); | 33 | ~InnerFence(); |
| 36 | 34 | ||
| 37 | void Queue(); | 35 | void Queue(); |
| @@ -41,12 +39,8 @@ public: | |||
| 41 | void Wait(); | 39 | void Wait(); |
| 42 | 40 | ||
| 43 | private: | 41 | private: |
| 44 | bool IsEventSignalled() const; | ||
| 45 | |||
| 46 | const VKDevice& device; | ||
| 47 | VKScheduler& scheduler; | 42 | VKScheduler& scheduler; |
| 48 | vk::Event event; | 43 | u64 wait_tick = 0; |
| 49 | u64 ticks = 0; | ||
| 50 | }; | 44 | }; |
| 51 | using Fence = std::shared_ptr<InnerFence>; | 45 | using Fence = std::shared_ptr<InnerFence>; |
| 52 | 46 | ||
| @@ -58,7 +52,7 @@ public: | |||
| 58 | explicit VKFenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::GPU& gpu_, | 52 | explicit VKFenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::GPU& gpu_, |
| 59 | Tegra::MemoryManager& memory_manager_, TextureCache& texture_cache_, | 53 | Tegra::MemoryManager& memory_manager_, TextureCache& texture_cache_, |
| 60 | VKBufferCache& buffer_cache_, VKQueryCache& query_cache_, | 54 | VKBufferCache& buffer_cache_, VKQueryCache& query_cache_, |
| 61 | const VKDevice& device_, VKScheduler& scheduler_); | 55 | VKScheduler& scheduler_); |
| 62 | 56 | ||
| 63 | protected: | 57 | protected: |
| 64 | Fence CreateFence(u32 value, bool is_stubbed) override; | 58 | Fence CreateFence(u32 value, bool is_stubbed) override; |
| @@ -68,7 +62,6 @@ protected: | |||
| 68 | void WaitFence(Fence& fence) override; | 62 | void WaitFence(Fence& fence) override; |
| 69 | 63 | ||
| 70 | private: | 64 | private: |
| 71 | const VKDevice& device; | ||
| 72 | VKScheduler& scheduler; | 65 | VKScheduler& scheduler; |
| 73 | }; | 66 | }; |
| 74 | 67 | ||
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 7979df3a8..a5214d0bc 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp | |||
| @@ -12,12 +12,12 @@ | |||
| 12 | #include "video_core/renderer_vulkan/fixed_pipeline_state.h" | 12 | #include "video_core/renderer_vulkan/fixed_pipeline_state.h" |
| 13 | #include "video_core/renderer_vulkan/maxwell_to_vk.h" | 13 | #include "video_core/renderer_vulkan/maxwell_to_vk.h" |
| 14 | #include "video_core/renderer_vulkan/vk_descriptor_pool.h" | 14 | #include "video_core/renderer_vulkan/vk_descriptor_pool.h" |
| 15 | #include "video_core/renderer_vulkan/vk_device.h" | ||
| 16 | #include "video_core/renderer_vulkan/vk_graphics_pipeline.h" | 15 | #include "video_core/renderer_vulkan/vk_graphics_pipeline.h" |
| 17 | #include "video_core/renderer_vulkan/vk_pipeline_cache.h" | 16 | #include "video_core/renderer_vulkan/vk_pipeline_cache.h" |
| 18 | #include "video_core/renderer_vulkan/vk_scheduler.h" | 17 | #include "video_core/renderer_vulkan/vk_scheduler.h" |
| 19 | #include "video_core/renderer_vulkan/vk_update_descriptor.h" | 18 | #include "video_core/renderer_vulkan/vk_update_descriptor.h" |
| 20 | #include "video_core/renderer_vulkan/wrapper.h" | 19 | #include "video_core/vulkan_common/vulkan_device.h" |
| 20 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 21 | 21 | ||
| 22 | namespace Vulkan { | 22 | namespace Vulkan { |
| 23 | 23 | ||
| @@ -94,7 +94,7 @@ VkSampleCountFlagBits ConvertMsaaMode(Tegra::Texture::MsaaMode msaa_mode) { | |||
| 94 | 94 | ||
| 95 | } // Anonymous namespace | 95 | } // Anonymous namespace |
| 96 | 96 | ||
| 97 | VKGraphicsPipeline::VKGraphicsPipeline(const VKDevice& device_, VKScheduler& scheduler_, | 97 | VKGraphicsPipeline::VKGraphicsPipeline(const Device& device_, VKScheduler& scheduler_, |
| 98 | VKDescriptorPool& descriptor_pool_, | 98 | VKDescriptorPool& descriptor_pool_, |
| 99 | VKUpdateDescriptorQueue& update_descriptor_queue_, | 99 | VKUpdateDescriptorQueue& update_descriptor_queue_, |
| 100 | const GraphicsPipelineCacheKey& key, | 100 | const GraphicsPipelineCacheKey& key, |
| @@ -212,11 +212,7 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const SPIRVProgram& program, | |||
| 212 | // state is ignored | 212 | // state is ignored |
| 213 | dynamic.raw1 = 0; | 213 | dynamic.raw1 = 0; |
| 214 | dynamic.raw2 = 0; | 214 | dynamic.raw2 = 0; |
| 215 | for (FixedPipelineState::VertexBinding& binding : dynamic.vertex_bindings) { | 215 | dynamic.vertex_strides.fill(0); |
| 216 | // Enable all vertex bindings | ||
| 217 | binding.raw = 0; | ||
| 218 | binding.enabled.Assign(1); | ||
| 219 | } | ||
| 220 | } else { | 216 | } else { |
| 221 | dynamic = state.dynamic_state; | 217 | dynamic = state.dynamic_state; |
| 222 | } | 218 | } |
| @@ -224,19 +220,16 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const SPIRVProgram& program, | |||
| 224 | std::vector<VkVertexInputBindingDescription> vertex_bindings; | 220 | std::vector<VkVertexInputBindingDescription> vertex_bindings; |
| 225 | std::vector<VkVertexInputBindingDivisorDescriptionEXT> vertex_binding_divisors; | 221 | std::vector<VkVertexInputBindingDivisorDescriptionEXT> vertex_binding_divisors; |
| 226 | for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { | 222 | for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { |
| 227 | const auto& binding = dynamic.vertex_bindings[index]; | 223 | if (state.attributes[index].binding_index_enabled == 0) { |
| 228 | if (!binding.enabled) { | ||
| 229 | continue; | 224 | continue; |
| 230 | } | 225 | } |
| 231 | const bool instanced = state.binding_divisors[index] != 0; | 226 | const bool instanced = state.binding_divisors[index] != 0; |
| 232 | const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; | 227 | const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; |
| 233 | |||
| 234 | vertex_bindings.push_back({ | 228 | vertex_bindings.push_back({ |
| 235 | .binding = static_cast<u32>(index), | 229 | .binding = static_cast<u32>(index), |
| 236 | .stride = binding.stride, | 230 | .stride = dynamic.vertex_strides[index], |
| 237 | .inputRate = rate, | 231 | .inputRate = rate, |
| 238 | }); | 232 | }); |
| 239 | |||
| 240 | if (instanced) { | 233 | if (instanced) { |
| 241 | vertex_binding_divisors.push_back({ | 234 | vertex_binding_divisors.push_back({ |
| 242 | .binding = static_cast<u32>(index), | 235 | .binding = static_cast<u32>(index), |
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index 214d06b4c..8b6a98fe0 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h | |||
| @@ -13,7 +13,7 @@ | |||
| 13 | #include "video_core/renderer_vulkan/fixed_pipeline_state.h" | 13 | #include "video_core/renderer_vulkan/fixed_pipeline_state.h" |
| 14 | #include "video_core/renderer_vulkan/vk_descriptor_pool.h" | 14 | #include "video_core/renderer_vulkan/vk_descriptor_pool.h" |
| 15 | #include "video_core/renderer_vulkan/vk_shader_decompiler.h" | 15 | #include "video_core/renderer_vulkan/vk_shader_decompiler.h" |
| 16 | #include "video_core/renderer_vulkan/wrapper.h" | 16 | #include "video_core/vulkan_common/vulkan_wrapper.h" |
| 17 | 17 | ||
| 18 | namespace Vulkan { | 18 | namespace Vulkan { |
| 19 | 19 | ||
| @@ -40,8 +40,8 @@ static_assert(std::has_unique_object_representations_v<GraphicsPipelineCacheKey> | |||
| 40 | static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>); | 40 | static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>); |
| 41 | static_assert(std::is_trivially_constructible_v<GraphicsPipelineCacheKey>); | 41 | static_assert(std::is_trivially_constructible_v<GraphicsPipelineCacheKey>); |
| 42 | 42 | ||
| 43 | class Device; | ||
| 43 | class VKDescriptorPool; | 44 | class VKDescriptorPool; |
| 44 | class VKDevice; | ||
| 45 | class VKScheduler; | 45 | class VKScheduler; |
| 46 | class VKUpdateDescriptorQueue; | 46 | class VKUpdateDescriptorQueue; |
| 47 | 47 | ||
| @@ -49,7 +49,7 @@ using SPIRVProgram = std::array<std::optional<SPIRVShader>, Maxwell::MaxShaderSt | |||
| 49 | 49 | ||
| 50 | class VKGraphicsPipeline final { | 50 | class VKGraphicsPipeline final { |
| 51 | public: | 51 | public: |
| 52 | explicit VKGraphicsPipeline(const VKDevice& device_, VKScheduler& scheduler_, | 52 | explicit VKGraphicsPipeline(const Device& device_, VKScheduler& scheduler_, |
| 53 | VKDescriptorPool& descriptor_pool, | 53 | VKDescriptorPool& descriptor_pool, |
| 54 | VKUpdateDescriptorQueue& update_descriptor_queue_, | 54 | VKUpdateDescriptorQueue& update_descriptor_queue_, |
| 55 | const GraphicsPipelineCacheKey& key, | 55 | const GraphicsPipelineCacheKey& key, |
| @@ -85,7 +85,7 @@ private: | |||
| 85 | vk::Pipeline CreatePipeline(const SPIRVProgram& program, VkRenderPass renderpass, | 85 | vk::Pipeline CreatePipeline(const SPIRVProgram& program, VkRenderPass renderpass, |
| 86 | u32 num_color_buffers) const; | 86 | u32 num_color_buffers) const; |
| 87 | 87 | ||
| 88 | const VKDevice& device; | 88 | const Device& device; |
| 89 | VKScheduler& scheduler; | 89 | VKScheduler& scheduler; |
| 90 | const GraphicsPipelineCacheKey cache_key; | 90 | const GraphicsPipelineCacheKey cache_key; |
| 91 | const u64 hash; | 91 | const u64 hash; |
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp index ae26e558d..56ec5e380 100644 --- a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp +++ b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp | |||
| @@ -6,15 +6,15 @@ | |||
| 6 | #include <chrono> | 6 | #include <chrono> |
| 7 | 7 | ||
| 8 | #include "core/settings.h" | 8 | #include "core/settings.h" |
| 9 | #include "video_core/renderer_vulkan/vk_device.h" | ||
| 10 | #include "video_core/renderer_vulkan/vk_master_semaphore.h" | 9 | #include "video_core/renderer_vulkan/vk_master_semaphore.h" |
| 11 | #include "video_core/renderer_vulkan/wrapper.h" | 10 | #include "video_core/vulkan_common/vulkan_device.h" |
| 11 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 12 | 12 | ||
| 13 | namespace Vulkan { | 13 | namespace Vulkan { |
| 14 | 14 | ||
| 15 | using namespace std::chrono_literals; | 15 | using namespace std::chrono_literals; |
| 16 | 16 | ||
| 17 | MasterSemaphore::MasterSemaphore(const VKDevice& device) { | 17 | MasterSemaphore::MasterSemaphore(const Device& device) { |
| 18 | static constexpr VkSemaphoreTypeCreateInfoKHR semaphore_type_ci{ | 18 | static constexpr VkSemaphoreTypeCreateInfoKHR semaphore_type_ci{ |
| 19 | .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO_KHR, | 19 | .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO_KHR, |
| 20 | .pNext = nullptr, | 20 | .pNext = nullptr, |
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.h b/src/video_core/renderer_vulkan/vk_master_semaphore.h index 0e93706d7..f336f1862 100644 --- a/src/video_core/renderer_vulkan/vk_master_semaphore.h +++ b/src/video_core/renderer_vulkan/vk_master_semaphore.h | |||
| @@ -8,15 +8,15 @@ | |||
| 8 | #include <thread> | 8 | #include <thread> |
| 9 | 9 | ||
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "video_core/renderer_vulkan/wrapper.h" | 11 | #include "video_core/vulkan_common/vulkan_wrapper.h" |
| 12 | 12 | ||
| 13 | namespace Vulkan { | 13 | namespace Vulkan { |
| 14 | 14 | ||
| 15 | class VKDevice; | 15 | class Device; |
| 16 | 16 | ||
| 17 | class MasterSemaphore { | 17 | class MasterSemaphore { |
| 18 | public: | 18 | public: |
| 19 | explicit MasterSemaphore(const VKDevice& device); | 19 | explicit MasterSemaphore(const Device& device); |
| 20 | ~MasterSemaphore(); | 20 | ~MasterSemaphore(); |
| 21 | 21 | ||
| 22 | /// Returns the current logical tick. | 22 | /// Returns the current logical tick. |
diff --git a/src/video_core/renderer_vulkan/vk_memory_manager.cpp b/src/video_core/renderer_vulkan/vk_memory_manager.cpp deleted file mode 100644 index 56b24b70f..000000000 --- a/src/video_core/renderer_vulkan/vk_memory_manager.cpp +++ /dev/null | |||
| @@ -1,230 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <optional> | ||
| 7 | #include <tuple> | ||
| 8 | #include <vector> | ||
| 9 | |||
| 10 | #include "common/alignment.h" | ||
| 11 | #include "common/assert.h" | ||
| 12 | #include "common/common_types.h" | ||
| 13 | #include "common/logging/log.h" | ||
| 14 | #include "video_core/renderer_vulkan/vk_device.h" | ||
| 15 | #include "video_core/renderer_vulkan/vk_memory_manager.h" | ||
| 16 | #include "video_core/renderer_vulkan/wrapper.h" | ||
| 17 | |||
| 18 | namespace Vulkan { | ||
| 19 | |||
| 20 | namespace { | ||
| 21 | |||
| 22 | u64 GetAllocationChunkSize(u64 required_size) { | ||
| 23 | static constexpr u64 sizes[] = {16ULL << 20, 32ULL << 20, 64ULL << 20, 128ULL << 20}; | ||
| 24 | auto it = std::lower_bound(std::begin(sizes), std::end(sizes), required_size); | ||
| 25 | return it != std::end(sizes) ? *it : Common::AlignUp(required_size, 256ULL << 20); | ||
| 26 | } | ||
| 27 | |||
| 28 | } // Anonymous namespace | ||
| 29 | |||
| 30 | class VKMemoryAllocation final { | ||
| 31 | public: | ||
| 32 | explicit VKMemoryAllocation(const VKDevice& device_, vk::DeviceMemory memory_, | ||
| 33 | VkMemoryPropertyFlags properties_, u64 allocation_size_, u32 type_) | ||
| 34 | : device{device_}, memory{std::move(memory_)}, properties{properties_}, | ||
| 35 | allocation_size{allocation_size_}, shifted_type{ShiftType(type_)} {} | ||
| 36 | |||
| 37 | VKMemoryCommit Commit(VkDeviceSize commit_size, VkDeviceSize alignment) { | ||
| 38 | auto found = TryFindFreeSection(free_iterator, allocation_size, | ||
| 39 | static_cast<u64>(commit_size), static_cast<u64>(alignment)); | ||
| 40 | if (!found) { | ||
| 41 | found = TryFindFreeSection(0, free_iterator, static_cast<u64>(commit_size), | ||
| 42 | static_cast<u64>(alignment)); | ||
| 43 | if (!found) { | ||
| 44 | // Signal out of memory, it'll try to do more allocations. | ||
| 45 | return nullptr; | ||
| 46 | } | ||
| 47 | } | ||
| 48 | auto commit = std::make_unique<VKMemoryCommitImpl>(device, this, memory, *found, | ||
| 49 | *found + commit_size); | ||
| 50 | commits.push_back(commit.get()); | ||
| 51 | |||
| 52 | // Last commit's address is highly probable to be free. | ||
| 53 | free_iterator = *found + commit_size; | ||
| 54 | |||
| 55 | return commit; | ||
| 56 | } | ||
| 57 | |||
| 58 | void Free(const VKMemoryCommitImpl* commit) { | ||
| 59 | ASSERT(commit); | ||
| 60 | |||
| 61 | const auto it = std::find(std::begin(commits), std::end(commits), commit); | ||
| 62 | if (it == commits.end()) { | ||
| 63 | UNREACHABLE_MSG("Freeing unallocated commit!"); | ||
| 64 | return; | ||
| 65 | } | ||
| 66 | commits.erase(it); | ||
| 67 | } | ||
| 68 | |||
| 69 | /// Returns whether this allocation is compatible with the arguments. | ||
| 70 | bool IsCompatible(VkMemoryPropertyFlags wanted_properties, u32 type_mask) const { | ||
| 71 | return (wanted_properties & properties) && (type_mask & shifted_type) != 0; | ||
| 72 | } | ||
| 73 | |||
| 74 | private: | ||
| 75 | static constexpr u32 ShiftType(u32 type) { | ||
| 76 | return 1U << type; | ||
| 77 | } | ||
| 78 | |||
| 79 | /// A memory allocator, it may return a free region between "start" and "end" with the solicited | ||
| 80 | /// requirements. | ||
| 81 | std::optional<u64> TryFindFreeSection(u64 start, u64 end, u64 size, u64 alignment) const { | ||
| 82 | u64 iterator = Common::AlignUp(start, alignment); | ||
| 83 | while (iterator + size <= end) { | ||
| 84 | const u64 try_left = iterator; | ||
| 85 | const u64 try_right = try_left + size; | ||
| 86 | |||
| 87 | bool overlap = false; | ||
| 88 | for (const auto& commit : commits) { | ||
| 89 | const auto [commit_left, commit_right] = commit->interval; | ||
| 90 | if (try_left < commit_right && commit_left < try_right) { | ||
| 91 | // There's an overlap, continue the search where the overlapping commit ends. | ||
| 92 | iterator = Common::AlignUp(commit_right, alignment); | ||
| 93 | overlap = true; | ||
| 94 | break; | ||
| 95 | } | ||
| 96 | } | ||
| 97 | if (!overlap) { | ||
| 98 | // A free address has been found. | ||
| 99 | return try_left; | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | // No free regions where found, return an empty optional. | ||
| 104 | return std::nullopt; | ||
| 105 | } | ||
| 106 | |||
| 107 | const VKDevice& device; ///< Vulkan device. | ||
| 108 | const vk::DeviceMemory memory; ///< Vulkan memory allocation handler. | ||
| 109 | const VkMemoryPropertyFlags properties; ///< Vulkan properties. | ||
| 110 | const u64 allocation_size; ///< Size of this allocation. | ||
| 111 | const u32 shifted_type; ///< Stored Vulkan type of this allocation, shifted. | ||
| 112 | |||
| 113 | /// Hints where the next free region is likely going to be. | ||
| 114 | u64 free_iterator{}; | ||
| 115 | |||
| 116 | /// Stores all commits done from this allocation. | ||
| 117 | std::vector<const VKMemoryCommitImpl*> commits; | ||
| 118 | }; | ||
| 119 | |||
| 120 | VKMemoryManager::VKMemoryManager(const VKDevice& device_) | ||
| 121 | : device{device_}, properties{device_.GetPhysical().GetMemoryProperties()} {} | ||
| 122 | |||
| 123 | VKMemoryManager::~VKMemoryManager() = default; | ||
| 124 | |||
| 125 | VKMemoryCommit VKMemoryManager::Commit(const VkMemoryRequirements& requirements, | ||
| 126 | bool host_visible) { | ||
| 127 | const u64 chunk_size = GetAllocationChunkSize(requirements.size); | ||
| 128 | |||
| 129 | // When a host visible commit is asked, search for host visible and coherent, otherwise search | ||
| 130 | // for a fast device local type. | ||
| 131 | const VkMemoryPropertyFlags wanted_properties = | ||
| 132 | host_visible ? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | ||
| 133 | : VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; | ||
| 134 | |||
| 135 | if (auto commit = TryAllocCommit(requirements, wanted_properties)) { | ||
| 136 | return commit; | ||
| 137 | } | ||
| 138 | |||
| 139 | // Commit has failed, allocate more memory. | ||
| 140 | if (!AllocMemory(wanted_properties, requirements.memoryTypeBits, chunk_size)) { | ||
| 141 | // TODO(Rodrigo): Handle these situations in some way like flushing to guest memory. | ||
| 142 | // Allocation has failed, panic. | ||
| 143 | UNREACHABLE_MSG("Ran out of VRAM!"); | ||
| 144 | return {}; | ||
| 145 | } | ||
| 146 | |||
| 147 | // Commit again, this time it won't fail since there's a fresh allocation above. If it does, | ||
| 148 | // there's a bug. | ||
| 149 | auto commit = TryAllocCommit(requirements, wanted_properties); | ||
| 150 | ASSERT(commit); | ||
| 151 | return commit; | ||
| 152 | } | ||
| 153 | |||
| 154 | VKMemoryCommit VKMemoryManager::Commit(const vk::Buffer& buffer, bool host_visible) { | ||
| 155 | auto commit = Commit(device.GetLogical().GetBufferMemoryRequirements(*buffer), host_visible); | ||
| 156 | buffer.BindMemory(commit->GetMemory(), commit->GetOffset()); | ||
| 157 | return commit; | ||
| 158 | } | ||
| 159 | |||
| 160 | VKMemoryCommit VKMemoryManager::Commit(const vk::Image& image, bool host_visible) { | ||
| 161 | auto commit = Commit(device.GetLogical().GetImageMemoryRequirements(*image), host_visible); | ||
| 162 | image.BindMemory(commit->GetMemory(), commit->GetOffset()); | ||
| 163 | return commit; | ||
| 164 | } | ||
| 165 | |||
| 166 | bool VKMemoryManager::AllocMemory(VkMemoryPropertyFlags wanted_properties, u32 type_mask, | ||
| 167 | u64 size) { | ||
| 168 | const u32 type = [&] { | ||
| 169 | for (u32 type_index = 0; type_index < properties.memoryTypeCount; ++type_index) { | ||
| 170 | const auto flags = properties.memoryTypes[type_index].propertyFlags; | ||
| 171 | if ((type_mask & (1U << type_index)) && (flags & wanted_properties)) { | ||
| 172 | // The type matches in type and in the wanted properties. | ||
| 173 | return type_index; | ||
| 174 | } | ||
| 175 | } | ||
| 176 | UNREACHABLE_MSG("Couldn't find a compatible memory type!"); | ||
| 177 | return 0U; | ||
| 178 | }(); | ||
| 179 | |||
| 180 | // Try to allocate found type. | ||
| 181 | vk::DeviceMemory memory = device.GetLogical().TryAllocateMemory({ | ||
| 182 | .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, | ||
| 183 | .pNext = nullptr, | ||
| 184 | .allocationSize = size, | ||
| 185 | .memoryTypeIndex = type, | ||
| 186 | }); | ||
| 187 | if (!memory) { | ||
| 188 | LOG_CRITICAL(Render_Vulkan, "Device allocation failed!"); | ||
| 189 | return false; | ||
| 190 | } | ||
| 191 | |||
| 192 | allocations.push_back(std::make_unique<VKMemoryAllocation>(device, std::move(memory), | ||
| 193 | wanted_properties, size, type)); | ||
| 194 | return true; | ||
| 195 | } | ||
| 196 | |||
| 197 | VKMemoryCommit VKMemoryManager::TryAllocCommit(const VkMemoryRequirements& requirements, | ||
| 198 | VkMemoryPropertyFlags wanted_properties) { | ||
| 199 | for (auto& allocation : allocations) { | ||
| 200 | if (!allocation->IsCompatible(wanted_properties, requirements.memoryTypeBits)) { | ||
| 201 | continue; | ||
| 202 | } | ||
| 203 | if (auto commit = allocation->Commit(requirements.size, requirements.alignment)) { | ||
| 204 | return commit; | ||
| 205 | } | ||
| 206 | } | ||
| 207 | return {}; | ||
| 208 | } | ||
| 209 | |||
| 210 | VKMemoryCommitImpl::VKMemoryCommitImpl(const VKDevice& device_, VKMemoryAllocation* allocation_, | ||
| 211 | const vk::DeviceMemory& memory_, u64 begin_, u64 end_) | ||
| 212 | : device{device_}, memory{memory_}, interval{begin_, end_}, allocation{allocation_} {} | ||
| 213 | |||
| 214 | VKMemoryCommitImpl::~VKMemoryCommitImpl() { | ||
| 215 | allocation->Free(this); | ||
| 216 | } | ||
| 217 | |||
| 218 | MemoryMap VKMemoryCommitImpl::Map(u64 size, u64 offset_) const { | ||
| 219 | return MemoryMap(this, std::span<u8>(memory.Map(interval.first + offset_, size), size)); | ||
| 220 | } | ||
| 221 | |||
| 222 | void VKMemoryCommitImpl::Unmap() const { | ||
| 223 | memory.Unmap(); | ||
| 224 | } | ||
| 225 | |||
| 226 | MemoryMap VKMemoryCommitImpl::Map() const { | ||
| 227 | return Map(interval.second - interval.first); | ||
| 228 | } | ||
| 229 | |||
| 230 | } // namespace Vulkan | ||
diff --git a/src/video_core/renderer_vulkan/vk_memory_manager.h b/src/video_core/renderer_vulkan/vk_memory_manager.h deleted file mode 100644 index 318f8b43e..000000000 --- a/src/video_core/renderer_vulkan/vk_memory_manager.h +++ /dev/null | |||
| @@ -1,132 +0,0 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <span> | ||
| 9 | #include <utility> | ||
| 10 | #include <vector> | ||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "video_core/renderer_vulkan/wrapper.h" | ||
| 13 | |||
| 14 | namespace Vulkan { | ||
| 15 | |||
| 16 | class MemoryMap; | ||
| 17 | class VKDevice; | ||
| 18 | class VKMemoryAllocation; | ||
| 19 | class VKMemoryCommitImpl; | ||
| 20 | |||
| 21 | using VKMemoryCommit = std::unique_ptr<VKMemoryCommitImpl>; | ||
| 22 | |||
| 23 | class VKMemoryManager final { | ||
| 24 | public: | ||
| 25 | explicit VKMemoryManager(const VKDevice& device_); | ||
| 26 | VKMemoryManager(const VKMemoryManager&) = delete; | ||
| 27 | ~VKMemoryManager(); | ||
| 28 | |||
| 29 | /** | ||
| 30 | * Commits a memory with the specified requeriments. | ||
| 31 | * @param requirements Requirements returned from a Vulkan call. | ||
| 32 | * @param host_visible Signals the allocator that it *must* use host visible and coherent | ||
| 33 | * memory. When passing false, it will try to allocate device local memory. | ||
| 34 | * @returns A memory commit. | ||
| 35 | */ | ||
| 36 | VKMemoryCommit Commit(const VkMemoryRequirements& requirements, bool host_visible); | ||
| 37 | |||
| 38 | /// Commits memory required by the buffer and binds it. | ||
| 39 | VKMemoryCommit Commit(const vk::Buffer& buffer, bool host_visible); | ||
| 40 | |||
| 41 | /// Commits memory required by the image and binds it. | ||
| 42 | VKMemoryCommit Commit(const vk::Image& image, bool host_visible); | ||
| 43 | |||
| 44 | private: | ||
| 45 | /// Allocates a chunk of memory. | ||
| 46 | bool AllocMemory(VkMemoryPropertyFlags wanted_properties, u32 type_mask, u64 size); | ||
| 47 | |||
| 48 | /// Tries to allocate a memory commit. | ||
| 49 | VKMemoryCommit TryAllocCommit(const VkMemoryRequirements& requirements, | ||
| 50 | VkMemoryPropertyFlags wanted_properties); | ||
| 51 | |||
| 52 | const VKDevice& device; ///< Device handler. | ||
| 53 | const VkPhysicalDeviceMemoryProperties properties; ///< Physical device properties. | ||
| 54 | std::vector<std::unique_ptr<VKMemoryAllocation>> allocations; ///< Current allocations. | ||
| 55 | }; | ||
| 56 | |||
| 57 | class VKMemoryCommitImpl final { | ||
| 58 | friend VKMemoryAllocation; | ||
| 59 | friend MemoryMap; | ||
| 60 | |||
| 61 | public: | ||
| 62 | explicit VKMemoryCommitImpl(const VKDevice& device_, VKMemoryAllocation* allocation_, | ||
| 63 | const vk::DeviceMemory& memory_, u64 begin_, u64 end_); | ||
| 64 | ~VKMemoryCommitImpl(); | ||
| 65 | |||
| 66 | /// Maps a memory region and returns a pointer to it. | ||
| 67 | /// It's illegal to have more than one memory map at the same time. | ||
| 68 | MemoryMap Map(u64 size, u64 offset = 0) const; | ||
| 69 | |||
| 70 | /// Maps the whole commit and returns a pointer to it. | ||
| 71 | /// It's illegal to have more than one memory map at the same time. | ||
| 72 | MemoryMap Map() const; | ||
| 73 | |||
| 74 | /// Returns the Vulkan memory handler. | ||
| 75 | VkDeviceMemory GetMemory() const { | ||
| 76 | return *memory; | ||
| 77 | } | ||
| 78 | |||
| 79 | /// Returns the start position of the commit relative to the allocation. | ||
| 80 | VkDeviceSize GetOffset() const { | ||
| 81 | return static_cast<VkDeviceSize>(interval.first); | ||
| 82 | } | ||
| 83 | |||
| 84 | private: | ||
| 85 | /// Unmaps memory. | ||
| 86 | void Unmap() const; | ||
| 87 | |||
| 88 | const VKDevice& device; ///< Vulkan device. | ||
| 89 | const vk::DeviceMemory& memory; ///< Vulkan device memory handler. | ||
| 90 | std::pair<u64, u64> interval{}; ///< Interval where the commit exists. | ||
| 91 | VKMemoryAllocation* allocation{}; ///< Pointer to the large memory allocation. | ||
| 92 | }; | ||
| 93 | |||
| 94 | /// Holds ownership of a memory map. | ||
| 95 | class MemoryMap final { | ||
| 96 | public: | ||
| 97 | explicit MemoryMap(const VKMemoryCommitImpl* commit_, std::span<u8> span_) | ||
| 98 | : commit{commit_}, span{span_} {} | ||
| 99 | |||
| 100 | ~MemoryMap() { | ||
| 101 | if (commit) { | ||
| 102 | commit->Unmap(); | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | /// Prematurely releases the memory map. | ||
| 107 | void Release() { | ||
| 108 | commit->Unmap(); | ||
| 109 | commit = nullptr; | ||
| 110 | } | ||
| 111 | |||
| 112 | /// Returns a span to the memory map. | ||
| 113 | [[nodiscard]] std::span<u8> Span() const noexcept { | ||
| 114 | return span; | ||
| 115 | } | ||
| 116 | |||
| 117 | /// Returns the address of the memory map. | ||
| 118 | [[nodiscard]] u8* Address() const noexcept { | ||
| 119 | return span.data(); | ||
| 120 | } | ||
| 121 | |||
| 122 | /// Returns the address of the memory map; | ||
| 123 | [[nodiscard]] operator u8*() const noexcept { | ||
| 124 | return span.data(); | ||
| 125 | } | ||
| 126 | |||
| 127 | private: | ||
| 128 | const VKMemoryCommitImpl* commit{}; ///< Mapped memory commit. | ||
| 129 | std::span<u8> span; ///< Address to the mapped memory. | ||
| 130 | }; | ||
| 131 | |||
| 132 | } // 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 083796d05..02282e36f 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | |||
| @@ -19,17 +19,17 @@ | |||
| 19 | #include "video_core/renderer_vulkan/maxwell_to_vk.h" | 19 | #include "video_core/renderer_vulkan/maxwell_to_vk.h" |
| 20 | #include "video_core/renderer_vulkan/vk_compute_pipeline.h" | 20 | #include "video_core/renderer_vulkan/vk_compute_pipeline.h" |
| 21 | #include "video_core/renderer_vulkan/vk_descriptor_pool.h" | 21 | #include "video_core/renderer_vulkan/vk_descriptor_pool.h" |
| 22 | #include "video_core/renderer_vulkan/vk_device.h" | ||
| 23 | #include "video_core/renderer_vulkan/vk_graphics_pipeline.h" | 22 | #include "video_core/renderer_vulkan/vk_graphics_pipeline.h" |
| 24 | #include "video_core/renderer_vulkan/vk_pipeline_cache.h" | 23 | #include "video_core/renderer_vulkan/vk_pipeline_cache.h" |
| 25 | #include "video_core/renderer_vulkan/vk_rasterizer.h" | 24 | #include "video_core/renderer_vulkan/vk_rasterizer.h" |
| 26 | #include "video_core/renderer_vulkan/vk_scheduler.h" | 25 | #include "video_core/renderer_vulkan/vk_scheduler.h" |
| 27 | #include "video_core/renderer_vulkan/vk_update_descriptor.h" | 26 | #include "video_core/renderer_vulkan/vk_update_descriptor.h" |
| 28 | #include "video_core/renderer_vulkan/wrapper.h" | ||
| 29 | #include "video_core/shader/compiler_settings.h" | 27 | #include "video_core/shader/compiler_settings.h" |
| 30 | #include "video_core/shader/memory_util.h" | 28 | #include "video_core/shader/memory_util.h" |
| 31 | #include "video_core/shader_cache.h" | 29 | #include "video_core/shader_cache.h" |
| 32 | #include "video_core/shader_notify.h" | 30 | #include "video_core/shader_notify.h" |
| 31 | #include "video_core/vulkan_common/vulkan_device.h" | ||
| 32 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 33 | 33 | ||
| 34 | namespace Vulkan { | 34 | namespace Vulkan { |
| 35 | 35 | ||
| @@ -149,7 +149,7 @@ Shader::~Shader() = default; | |||
| 149 | VKPipelineCache::VKPipelineCache(RasterizerVulkan& rasterizer_, Tegra::GPU& gpu_, | 149 | VKPipelineCache::VKPipelineCache(RasterizerVulkan& rasterizer_, Tegra::GPU& gpu_, |
| 150 | Tegra::Engines::Maxwell3D& maxwell3d_, | 150 | Tegra::Engines::Maxwell3D& maxwell3d_, |
| 151 | Tegra::Engines::KeplerCompute& kepler_compute_, | 151 | Tegra::Engines::KeplerCompute& kepler_compute_, |
| 152 | Tegra::MemoryManager& gpu_memory_, const VKDevice& device_, | 152 | Tegra::MemoryManager& gpu_memory_, const Device& device_, |
| 153 | VKScheduler& scheduler_, VKDescriptorPool& descriptor_pool_, | 153 | VKScheduler& scheduler_, VKDescriptorPool& descriptor_pool_, |
| 154 | VKUpdateDescriptorQueue& update_descriptor_queue_) | 154 | VKUpdateDescriptorQueue& update_descriptor_queue_) |
| 155 | : VideoCommon::ShaderCache<Shader>{rasterizer_}, gpu{gpu_}, maxwell3d{maxwell3d_}, | 155 | : VideoCommon::ShaderCache<Shader>{rasterizer_}, gpu{gpu_}, maxwell3d{maxwell3d_}, |
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index fbaa8257c..89d635a3d 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h | |||
| @@ -20,12 +20,12 @@ | |||
| 20 | #include "video_core/renderer_vulkan/fixed_pipeline_state.h" | 20 | #include "video_core/renderer_vulkan/fixed_pipeline_state.h" |
| 21 | #include "video_core/renderer_vulkan/vk_graphics_pipeline.h" | 21 | #include "video_core/renderer_vulkan/vk_graphics_pipeline.h" |
| 22 | #include "video_core/renderer_vulkan/vk_shader_decompiler.h" | 22 | #include "video_core/renderer_vulkan/vk_shader_decompiler.h" |
| 23 | #include "video_core/renderer_vulkan/wrapper.h" | ||
| 24 | #include "video_core/shader/async_shaders.h" | 23 | #include "video_core/shader/async_shaders.h" |
| 25 | #include "video_core/shader/memory_util.h" | 24 | #include "video_core/shader/memory_util.h" |
| 26 | #include "video_core/shader/registry.h" | 25 | #include "video_core/shader/registry.h" |
| 27 | #include "video_core/shader/shader_ir.h" | 26 | #include "video_core/shader/shader_ir.h" |
| 28 | #include "video_core/shader_cache.h" | 27 | #include "video_core/shader_cache.h" |
| 28 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 29 | 29 | ||
| 30 | namespace Core { | 30 | namespace Core { |
| 31 | class System; | 31 | class System; |
| @@ -33,10 +33,10 @@ class System; | |||
| 33 | 33 | ||
| 34 | namespace Vulkan { | 34 | namespace Vulkan { |
| 35 | 35 | ||
| 36 | class Device; | ||
| 36 | class RasterizerVulkan; | 37 | class RasterizerVulkan; |
| 37 | class VKComputePipeline; | 38 | class VKComputePipeline; |
| 38 | class VKDescriptorPool; | 39 | class VKDescriptorPool; |
| 39 | class VKDevice; | ||
| 40 | class VKScheduler; | 40 | class VKScheduler; |
| 41 | class VKUpdateDescriptorQueue; | 41 | class VKUpdateDescriptorQueue; |
| 42 | 42 | ||
| @@ -121,7 +121,7 @@ public: | |||
| 121 | explicit VKPipelineCache(RasterizerVulkan& rasterizer, Tegra::GPU& gpu, | 121 | explicit VKPipelineCache(RasterizerVulkan& rasterizer, Tegra::GPU& gpu, |
| 122 | Tegra::Engines::Maxwell3D& maxwell3d, | 122 | Tegra::Engines::Maxwell3D& maxwell3d, |
| 123 | Tegra::Engines::KeplerCompute& kepler_compute, | 123 | Tegra::Engines::KeplerCompute& kepler_compute, |
| 124 | Tegra::MemoryManager& gpu_memory, const VKDevice& device, | 124 | Tegra::MemoryManager& gpu_memory, const Device& device, |
| 125 | VKScheduler& scheduler, VKDescriptorPool& descriptor_pool, | 125 | VKScheduler& scheduler, VKDescriptorPool& descriptor_pool, |
| 126 | VKUpdateDescriptorQueue& update_descriptor_queue); | 126 | VKUpdateDescriptorQueue& update_descriptor_queue); |
| 127 | ~VKPipelineCache() override; | 127 | ~VKPipelineCache() override; |
| @@ -148,7 +148,7 @@ private: | |||
| 148 | Tegra::Engines::KeplerCompute& kepler_compute; | 148 | Tegra::Engines::KeplerCompute& kepler_compute; |
| 149 | Tegra::MemoryManager& gpu_memory; | 149 | Tegra::MemoryManager& gpu_memory; |
| 150 | 150 | ||
| 151 | const VKDevice& device; | 151 | const Device& device; |
| 152 | VKScheduler& scheduler; | 152 | VKScheduler& scheduler; |
| 153 | VKDescriptorPool& descriptor_pool; | 153 | VKDescriptorPool& descriptor_pool; |
| 154 | VKUpdateDescriptorQueue& update_descriptor_queue; | 154 | VKUpdateDescriptorQueue& update_descriptor_queue; |
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index 038760de3..7cadd5147 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp | |||
| @@ -7,11 +7,11 @@ | |||
| 7 | #include <utility> | 7 | #include <utility> |
| 8 | #include <vector> | 8 | #include <vector> |
| 9 | 9 | ||
| 10 | #include "video_core/renderer_vulkan/vk_device.h" | ||
| 11 | #include "video_core/renderer_vulkan/vk_query_cache.h" | 10 | #include "video_core/renderer_vulkan/vk_query_cache.h" |
| 12 | #include "video_core/renderer_vulkan/vk_resource_pool.h" | 11 | #include "video_core/renderer_vulkan/vk_resource_pool.h" |
| 13 | #include "video_core/renderer_vulkan/vk_scheduler.h" | 12 | #include "video_core/renderer_vulkan/vk_scheduler.h" |
| 14 | #include "video_core/renderer_vulkan/wrapper.h" | 13 | #include "video_core/vulkan_common/vulkan_device.h" |
| 14 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 15 | 15 | ||
| 16 | namespace Vulkan { | 16 | namespace Vulkan { |
| 17 | 17 | ||
| @@ -27,7 +27,7 @@ constexpr VkQueryType GetTarget(QueryType type) { | |||
| 27 | 27 | ||
| 28 | } // Anonymous namespace | 28 | } // Anonymous namespace |
| 29 | 29 | ||
| 30 | QueryPool::QueryPool(const VKDevice& device_, VKScheduler& scheduler, QueryType type_) | 30 | QueryPool::QueryPool(const Device& device_, VKScheduler& scheduler, QueryType type_) |
| 31 | : ResourcePool{scheduler.GetMasterSemaphore(), GROW_STEP}, device{device_}, type{type_} {} | 31 | : ResourcePool{scheduler.GetMasterSemaphore(), GROW_STEP}, device{device_}, type{type_} {} |
| 32 | 32 | ||
| 33 | QueryPool::~QueryPool() = default; | 33 | QueryPool::~QueryPool() = default; |
| @@ -68,7 +68,7 @@ void QueryPool::Reserve(std::pair<VkQueryPool, u32> query) { | |||
| 68 | 68 | ||
| 69 | VKQueryCache::VKQueryCache(VideoCore::RasterizerInterface& rasterizer_, | 69 | VKQueryCache::VKQueryCache(VideoCore::RasterizerInterface& rasterizer_, |
| 70 | Tegra::Engines::Maxwell3D& maxwell3d_, Tegra::MemoryManager& gpu_memory_, | 70 | Tegra::Engines::Maxwell3D& maxwell3d_, Tegra::MemoryManager& gpu_memory_, |
| 71 | const VKDevice& device_, VKScheduler& scheduler_) | 71 | const Device& device_, VKScheduler& scheduler_) |
| 72 | : QueryCacheBase{rasterizer_, maxwell3d_, gpu_memory_}, device{device_}, scheduler{scheduler_}, | 72 | : QueryCacheBase{rasterizer_, maxwell3d_, gpu_memory_}, device{device_}, scheduler{scheduler_}, |
| 73 | query_pools{ | 73 | query_pools{ |
| 74 | QueryPool{device_, scheduler_, QueryType::SamplesPassed}, | 74 | QueryPool{device_, scheduler_, QueryType::SamplesPassed}, |
| @@ -96,9 +96,9 @@ void VKQueryCache::Reserve(QueryType type, std::pair<VkQueryPool, u32> query) { | |||
| 96 | HostCounter::HostCounter(VKQueryCache& cache_, std::shared_ptr<HostCounter> dependency_, | 96 | HostCounter::HostCounter(VKQueryCache& cache_, std::shared_ptr<HostCounter> dependency_, |
| 97 | QueryType type_) | 97 | QueryType type_) |
| 98 | : HostCounterBase{std::move(dependency_)}, cache{cache_}, type{type_}, | 98 | : HostCounterBase{std::move(dependency_)}, cache{cache_}, type{type_}, |
| 99 | query{cache_.AllocateQuery(type_)}, tick{cache_.Scheduler().CurrentTick()} { | 99 | query{cache_.AllocateQuery(type_)}, tick{cache_.GetScheduler().CurrentTick()} { |
| 100 | const vk::Device* logical = &cache_.Device().GetLogical(); | 100 | const vk::Device* logical = &cache.GetDevice().GetLogical(); |
| 101 | cache_.Scheduler().Record([logical, query = query](vk::CommandBuffer cmdbuf) { | 101 | cache.GetScheduler().Record([logical, query = query](vk::CommandBuffer cmdbuf) { |
| 102 | logical->ResetQueryPoolEXT(query.first, query.second, 1); | 102 | logical->ResetQueryPoolEXT(query.first, query.second, 1); |
| 103 | cmdbuf.BeginQuery(query.first, query.second, VK_QUERY_CONTROL_PRECISE_BIT); | 103 | cmdbuf.BeginQuery(query.first, query.second, VK_QUERY_CONTROL_PRECISE_BIT); |
| 104 | }); | 104 | }); |
| @@ -109,17 +109,17 @@ HostCounter::~HostCounter() { | |||
| 109 | } | 109 | } |
| 110 | 110 | ||
| 111 | void HostCounter::EndQuery() { | 111 | void HostCounter::EndQuery() { |
| 112 | cache.Scheduler().Record( | 112 | cache.GetScheduler().Record( |
| 113 | [query = query](vk::CommandBuffer cmdbuf) { cmdbuf.EndQuery(query.first, query.second); }); | 113 | [query = query](vk::CommandBuffer cmdbuf) { cmdbuf.EndQuery(query.first, query.second); }); |
| 114 | } | 114 | } |
| 115 | 115 | ||
| 116 | u64 HostCounter::BlockingQuery() const { | 116 | u64 HostCounter::BlockingQuery() const { |
| 117 | if (tick >= cache.Scheduler().CurrentTick()) { | 117 | if (tick >= cache.GetScheduler().CurrentTick()) { |
| 118 | cache.Scheduler().Flush(); | 118 | cache.GetScheduler().Flush(); |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | u64 data; | 121 | u64 data; |
| 122 | const VkResult query_result = cache.Device().GetLogical().GetQueryResults( | 122 | const VkResult query_result = cache.GetDevice().GetLogical().GetQueryResults( |
| 123 | query.first, query.second, 1, sizeof(data), &data, sizeof(data), | 123 | query.first, query.second, 1, sizeof(data), &data, sizeof(data), |
| 124 | VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT); | 124 | VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT); |
| 125 | 125 | ||
| @@ -127,7 +127,7 @@ u64 HostCounter::BlockingQuery() const { | |||
| 127 | case VK_SUCCESS: | 127 | case VK_SUCCESS: |
| 128 | return data; | 128 | return data; |
| 129 | case VK_ERROR_DEVICE_LOST: | 129 | case VK_ERROR_DEVICE_LOST: |
| 130 | cache.Device().ReportLoss(); | 130 | cache.GetDevice().ReportLoss(); |
| 131 | [[fallthrough]]; | 131 | [[fallthrough]]; |
| 132 | default: | 132 | default: |
| 133 | throw vk::Exception(query_result); | 133 | throw vk::Exception(query_result); |
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.h b/src/video_core/renderer_vulkan/vk_query_cache.h index 837fe9ebf..7190946b9 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.h +++ b/src/video_core/renderer_vulkan/vk_query_cache.h | |||
| @@ -12,7 +12,7 @@ | |||
| 12 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 13 | #include "video_core/query_cache.h" | 13 | #include "video_core/query_cache.h" |
| 14 | #include "video_core/renderer_vulkan/vk_resource_pool.h" | 14 | #include "video_core/renderer_vulkan/vk_resource_pool.h" |
| 15 | #include "video_core/renderer_vulkan/wrapper.h" | 15 | #include "video_core/vulkan_common/vulkan_wrapper.h" |
| 16 | 16 | ||
| 17 | namespace VideoCore { | 17 | namespace VideoCore { |
| 18 | class RasterizerInterface; | 18 | class RasterizerInterface; |
| @@ -21,8 +21,8 @@ class RasterizerInterface; | |||
| 21 | namespace Vulkan { | 21 | namespace Vulkan { |
| 22 | 22 | ||
| 23 | class CachedQuery; | 23 | class CachedQuery; |
| 24 | class Device; | ||
| 24 | class HostCounter; | 25 | class HostCounter; |
| 25 | class VKDevice; | ||
| 26 | class VKQueryCache; | 26 | class VKQueryCache; |
| 27 | class VKScheduler; | 27 | class VKScheduler; |
| 28 | 28 | ||
| @@ -30,7 +30,7 @@ using CounterStream = VideoCommon::CounterStreamBase<VKQueryCache, HostCounter>; | |||
| 30 | 30 | ||
| 31 | class QueryPool final : public ResourcePool { | 31 | class QueryPool final : public ResourcePool { |
| 32 | public: | 32 | public: |
| 33 | explicit QueryPool(const VKDevice& device, VKScheduler& scheduler, VideoCore::QueryType type); | 33 | explicit QueryPool(const Device& device, VKScheduler& scheduler, VideoCore::QueryType type); |
| 34 | ~QueryPool() override; | 34 | ~QueryPool() override; |
| 35 | 35 | ||
| 36 | std::pair<VkQueryPool, u32> Commit(); | 36 | std::pair<VkQueryPool, u32> Commit(); |
| @@ -43,7 +43,7 @@ protected: | |||
| 43 | private: | 43 | private: |
| 44 | static constexpr std::size_t GROW_STEP = 512; | 44 | static constexpr std::size_t GROW_STEP = 512; |
| 45 | 45 | ||
| 46 | const VKDevice& device; | 46 | const Device& device; |
| 47 | const VideoCore::QueryType type; | 47 | const VideoCore::QueryType type; |
| 48 | 48 | ||
| 49 | std::vector<vk::QueryPool> pools; | 49 | std::vector<vk::QueryPool> pools; |
| @@ -55,23 +55,23 @@ class VKQueryCache final | |||
| 55 | public: | 55 | public: |
| 56 | explicit VKQueryCache(VideoCore::RasterizerInterface& rasterizer_, | 56 | explicit VKQueryCache(VideoCore::RasterizerInterface& rasterizer_, |
| 57 | Tegra::Engines::Maxwell3D& maxwell3d_, Tegra::MemoryManager& gpu_memory_, | 57 | Tegra::Engines::Maxwell3D& maxwell3d_, Tegra::MemoryManager& gpu_memory_, |
| 58 | const VKDevice& device_, VKScheduler& scheduler_); | 58 | const Device& device_, VKScheduler& scheduler_); |
| 59 | ~VKQueryCache(); | 59 | ~VKQueryCache(); |
| 60 | 60 | ||
| 61 | std::pair<VkQueryPool, u32> AllocateQuery(VideoCore::QueryType type); | 61 | std::pair<VkQueryPool, u32> AllocateQuery(VideoCore::QueryType type); |
| 62 | 62 | ||
| 63 | void Reserve(VideoCore::QueryType type, std::pair<VkQueryPool, u32> query); | 63 | void Reserve(VideoCore::QueryType type, std::pair<VkQueryPool, u32> query); |
| 64 | 64 | ||
| 65 | const VKDevice& Device() const noexcept { | 65 | const Device& GetDevice() const noexcept { |
| 66 | return device; | 66 | return device; |
| 67 | } | 67 | } |
| 68 | 68 | ||
| 69 | VKScheduler& Scheduler() const noexcept { | 69 | VKScheduler& GetScheduler() const noexcept { |
| 70 | return scheduler; | 70 | return scheduler; |
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | private: | 73 | private: |
| 74 | const VKDevice& device; | 74 | const Device& device; |
| 75 | VKScheduler& scheduler; | 75 | VKScheduler& scheduler; |
| 76 | std::array<QueryPool, VideoCore::NumQueryTypes> query_pools; | 76 | std::array<QueryPool, VideoCore::NumQueryTypes> query_pools; |
| 77 | }; | 77 | }; |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 04c5c859c..f0a111829 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -27,7 +27,6 @@ | |||
| 27 | #include "video_core/renderer_vulkan/vk_compute_pass.h" | 27 | #include "video_core/renderer_vulkan/vk_compute_pass.h" |
| 28 | #include "video_core/renderer_vulkan/vk_compute_pipeline.h" | 28 | #include "video_core/renderer_vulkan/vk_compute_pipeline.h" |
| 29 | #include "video_core/renderer_vulkan/vk_descriptor_pool.h" | 29 | #include "video_core/renderer_vulkan/vk_descriptor_pool.h" |
| 30 | #include "video_core/renderer_vulkan/vk_device.h" | ||
| 31 | #include "video_core/renderer_vulkan/vk_graphics_pipeline.h" | 30 | #include "video_core/renderer_vulkan/vk_graphics_pipeline.h" |
| 32 | #include "video_core/renderer_vulkan/vk_pipeline_cache.h" | 31 | #include "video_core/renderer_vulkan/vk_pipeline_cache.h" |
| 33 | #include "video_core/renderer_vulkan/vk_rasterizer.h" | 32 | #include "video_core/renderer_vulkan/vk_rasterizer.h" |
| @@ -36,9 +35,10 @@ | |||
| 36 | #include "video_core/renderer_vulkan/vk_state_tracker.h" | 35 | #include "video_core/renderer_vulkan/vk_state_tracker.h" |
| 37 | #include "video_core/renderer_vulkan/vk_texture_cache.h" | 36 | #include "video_core/renderer_vulkan/vk_texture_cache.h" |
| 38 | #include "video_core/renderer_vulkan/vk_update_descriptor.h" | 37 | #include "video_core/renderer_vulkan/vk_update_descriptor.h" |
| 39 | #include "video_core/renderer_vulkan/wrapper.h" | ||
| 40 | #include "video_core/shader_cache.h" | 38 | #include "video_core/shader_cache.h" |
| 41 | #include "video_core/texture_cache/texture_cache.h" | 39 | #include "video_core/texture_cache/texture_cache.h" |
| 40 | #include "video_core/vulkan_common/vulkan_device.h" | ||
| 41 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 42 | 42 | ||
| 43 | namespace Vulkan { | 43 | namespace Vulkan { |
| 44 | 44 | ||
| @@ -62,7 +62,7 @@ namespace { | |||
| 62 | 62 | ||
| 63 | constexpr auto COMPUTE_SHADER_INDEX = static_cast<size_t>(Tegra::Engines::ShaderType::Compute); | 63 | constexpr auto COMPUTE_SHADER_INDEX = static_cast<size_t>(Tegra::Engines::ShaderType::Compute); |
| 64 | 64 | ||
| 65 | VkViewport GetViewportState(const VKDevice& device, const Maxwell& regs, size_t index) { | 65 | VkViewport GetViewportState(const Device& device, const Maxwell& regs, size_t index) { |
| 66 | const auto& src = regs.viewport_transform[index]; | 66 | const auto& src = regs.viewport_transform[index]; |
| 67 | const float width = src.scale_x * 2.0f; | 67 | const float width = src.scale_x * 2.0f; |
| 68 | const float height = src.scale_y * 2.0f; | 68 | const float height = src.scale_y * 2.0f; |
| @@ -239,7 +239,7 @@ public: | |||
| 239 | index.type = type; | 239 | index.type = type; |
| 240 | } | 240 | } |
| 241 | 241 | ||
| 242 | void Bind(const VKDevice& device, VKScheduler& scheduler) const { | 242 | void Bind(const Device& device, VKScheduler& scheduler) const { |
| 243 | // Use this large switch case to avoid dispatching more memory in the record lambda than | 243 | // Use this large switch case to avoid dispatching more memory in the record lambda than |
| 244 | // what we need. It looks horrible, but it's the best we can do on standard C++. | 244 | // what we need. It looks horrible, but it's the best we can do on standard C++. |
| 245 | switch (vertex.num_buffers) { | 245 | switch (vertex.num_buffers) { |
| @@ -330,7 +330,7 @@ private: | |||
| 330 | } index; | 330 | } index; |
| 331 | 331 | ||
| 332 | template <size_t N> | 332 | template <size_t N> |
| 333 | void BindStatic(const VKDevice& device, VKScheduler& scheduler) const { | 333 | void BindStatic(const Device& device, VKScheduler& scheduler) const { |
| 334 | if (device.IsExtExtendedDynamicStateSupported()) { | 334 | if (device.IsExtExtendedDynamicStateSupported()) { |
| 335 | if (index.buffer) { | 335 | if (index.buffer) { |
| 336 | BindStatic<N, true, true>(scheduler); | 336 | BindStatic<N, true, true>(scheduler); |
| @@ -409,27 +409,26 @@ void RasterizerVulkan::DrawParameters::Draw(vk::CommandBuffer cmdbuf) const { | |||
| 409 | RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, | 409 | RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, |
| 410 | Tegra::MemoryManager& gpu_memory_, | 410 | Tegra::MemoryManager& gpu_memory_, |
| 411 | Core::Memory::Memory& cpu_memory_, VKScreenInfo& screen_info_, | 411 | Core::Memory::Memory& cpu_memory_, VKScreenInfo& screen_info_, |
| 412 | const VKDevice& device_, VKMemoryManager& memory_manager_, | 412 | const Device& device_, MemoryAllocator& memory_allocator_, |
| 413 | StateTracker& state_tracker_, VKScheduler& scheduler_) | 413 | StateTracker& state_tracker_, VKScheduler& scheduler_) |
| 414 | : RasterizerAccelerated{cpu_memory_}, gpu{gpu_}, | 414 | : RasterizerAccelerated{cpu_memory_}, gpu{gpu_}, |
| 415 | gpu_memory{gpu_memory_}, maxwell3d{gpu.Maxwell3D()}, kepler_compute{gpu.KeplerCompute()}, | 415 | gpu_memory{gpu_memory_}, maxwell3d{gpu.Maxwell3D()}, kepler_compute{gpu.KeplerCompute()}, |
| 416 | screen_info{screen_info_}, device{device_}, memory_manager{memory_manager_}, | 416 | screen_info{screen_info_}, device{device_}, memory_allocator{memory_allocator_}, |
| 417 | state_tracker{state_tracker_}, scheduler{scheduler_}, stream_buffer(device, scheduler), | 417 | state_tracker{state_tracker_}, scheduler{scheduler_}, stream_buffer(device, scheduler), |
| 418 | staging_pool(device, memory_manager, scheduler), descriptor_pool(device, scheduler), | 418 | staging_pool(device, memory_allocator, scheduler), descriptor_pool(device, scheduler), |
| 419 | update_descriptor_queue(device, scheduler), | 419 | update_descriptor_queue(device, scheduler), |
| 420 | blit_image(device, scheduler, state_tracker, descriptor_pool), | 420 | blit_image(device, scheduler, state_tracker, descriptor_pool), |
| 421 | quad_array_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), | 421 | quad_array_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), |
| 422 | quad_indexed_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), | 422 | quad_indexed_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), |
| 423 | uint8_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), | 423 | uint8_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), |
| 424 | texture_cache_runtime{device, scheduler, memory_manager, staging_pool, blit_image}, | 424 | texture_cache_runtime{device, scheduler, memory_allocator, staging_pool, blit_image}, |
| 425 | texture_cache(texture_cache_runtime, *this, maxwell3d, kepler_compute, gpu_memory), | 425 | texture_cache(texture_cache_runtime, *this, maxwell3d, kepler_compute, gpu_memory), |
| 426 | pipeline_cache(*this, gpu, maxwell3d, kepler_compute, gpu_memory, device, scheduler, | 426 | pipeline_cache(*this, gpu, maxwell3d, kepler_compute, gpu_memory, device, scheduler, |
| 427 | descriptor_pool, update_descriptor_queue), | 427 | descriptor_pool, update_descriptor_queue), |
| 428 | buffer_cache(*this, gpu_memory, cpu_memory_, device, memory_manager, scheduler, stream_buffer, | 428 | buffer_cache(*this, gpu_memory, cpu_memory_, device, memory_allocator, scheduler, |
| 429 | staging_pool), | 429 | stream_buffer, staging_pool), |
| 430 | query_cache{*this, maxwell3d, gpu_memory, device, scheduler}, | 430 | query_cache{*this, maxwell3d, gpu_memory, device, scheduler}, |
| 431 | fence_manager(*this, gpu, gpu_memory, texture_cache, buffer_cache, query_cache, device, | 431 | fence_manager(*this, gpu, gpu_memory, texture_cache, buffer_cache, query_cache, scheduler), |
| 432 | scheduler), | ||
| 433 | wfi_event(device.GetLogical().CreateEvent()), async_shaders(emu_window_) { | 432 | wfi_event(device.GetLogical().CreateEvent()), async_shaders(emu_window_) { |
| 434 | scheduler.SetQueryCache(query_cache); | 433 | scheduler.SetQueryCache(query_cache); |
| 435 | if (device.UseAsynchronousShaders()) { | 434 | if (device.UseAsynchronousShaders()) { |
| @@ -628,8 +627,10 @@ void RasterizerVulkan::DispatchCompute(GPUVAddr code_addr) { | |||
| 628 | grid_z = launch_desc.grid_dim_z, pipeline_handle, pipeline_layout, | 627 | grid_z = launch_desc.grid_dim_z, pipeline_handle, pipeline_layout, |
| 629 | descriptor_set](vk::CommandBuffer cmdbuf) { | 628 | descriptor_set](vk::CommandBuffer cmdbuf) { |
| 630 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_handle); | 629 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_handle); |
| 631 | cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_layout, DESCRIPTOR_SET, | 630 | if (descriptor_set) { |
| 632 | descriptor_set, {}); | 631 | cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_layout, |
| 632 | DESCRIPTOR_SET, descriptor_set, nullptr); | ||
| 633 | } | ||
| 633 | cmdbuf.Dispatch(grid_x, grid_y, grid_z); | 634 | cmdbuf.Dispatch(grid_x, grid_y, grid_z); |
| 634 | }); | 635 | }); |
| 635 | } | 636 | } |
| @@ -1444,7 +1445,7 @@ VkBuffer RasterizerVulkan::DefaultBuffer() { | |||
| 1444 | .queueFamilyIndexCount = 0, | 1445 | .queueFamilyIndexCount = 0, |
| 1445 | .pQueueFamilyIndices = nullptr, | 1446 | .pQueueFamilyIndices = nullptr, |
| 1446 | }); | 1447 | }); |
| 1447 | default_buffer_commit = memory_manager.Commit(default_buffer, false); | 1448 | default_buffer_commit = memory_allocator.Commit(default_buffer, MemoryUsage::DeviceLocal); |
| 1448 | 1449 | ||
| 1449 | scheduler.RequestOutsideRenderPassOperationContext(); | 1450 | scheduler.RequestOutsideRenderPassOperationContext(); |
| 1450 | scheduler.Record([buffer = *default_buffer](vk::CommandBuffer cmdbuf) { | 1451 | scheduler.Record([buffer = *default_buffer](vk::CommandBuffer cmdbuf) { |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 990f9e031..8e261b9bd 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h | |||
| @@ -21,7 +21,6 @@ | |||
| 21 | #include "video_core/renderer_vulkan/vk_compute_pass.h" | 21 | #include "video_core/renderer_vulkan/vk_compute_pass.h" |
| 22 | #include "video_core/renderer_vulkan/vk_descriptor_pool.h" | 22 | #include "video_core/renderer_vulkan/vk_descriptor_pool.h" |
| 23 | #include "video_core/renderer_vulkan/vk_fence_manager.h" | 23 | #include "video_core/renderer_vulkan/vk_fence_manager.h" |
| 24 | #include "video_core/renderer_vulkan/vk_memory_manager.h" | ||
| 25 | #include "video_core/renderer_vulkan/vk_pipeline_cache.h" | 24 | #include "video_core/renderer_vulkan/vk_pipeline_cache.h" |
| 26 | #include "video_core/renderer_vulkan/vk_query_cache.h" | 25 | #include "video_core/renderer_vulkan/vk_query_cache.h" |
| 27 | #include "video_core/renderer_vulkan/vk_scheduler.h" | 26 | #include "video_core/renderer_vulkan/vk_scheduler.h" |
| @@ -29,8 +28,9 @@ | |||
| 29 | #include "video_core/renderer_vulkan/vk_stream_buffer.h" | 28 | #include "video_core/renderer_vulkan/vk_stream_buffer.h" |
| 30 | #include "video_core/renderer_vulkan/vk_texture_cache.h" | 29 | #include "video_core/renderer_vulkan/vk_texture_cache.h" |
| 31 | #include "video_core/renderer_vulkan/vk_update_descriptor.h" | 30 | #include "video_core/renderer_vulkan/vk_update_descriptor.h" |
| 32 | #include "video_core/renderer_vulkan/wrapper.h" | ||
| 33 | #include "video_core/shader/async_shaders.h" | 31 | #include "video_core/shader/async_shaders.h" |
| 32 | #include "video_core/vulkan_common/vulkan_memory_allocator.h" | ||
| 33 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 34 | 34 | ||
| 35 | namespace Core { | 35 | namespace Core { |
| 36 | class System; | 36 | class System; |
| @@ -55,8 +55,8 @@ class RasterizerVulkan final : public VideoCore::RasterizerAccelerated { | |||
| 55 | public: | 55 | public: |
| 56 | explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, | 56 | explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, |
| 57 | Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_, | 57 | Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_, |
| 58 | VKScreenInfo& screen_info_, const VKDevice& device_, | 58 | VKScreenInfo& screen_info_, const Device& device_, |
| 59 | VKMemoryManager& memory_manager_, StateTracker& state_tracker_, | 59 | MemoryAllocator& memory_allocator_, StateTracker& state_tracker_, |
| 60 | VKScheduler& scheduler_); | 60 | VKScheduler& scheduler_); |
| 61 | ~RasterizerVulkan() override; | 61 | ~RasterizerVulkan() override; |
| 62 | 62 | ||
| @@ -212,13 +212,13 @@ private: | |||
| 212 | Tegra::Engines::KeplerCompute& kepler_compute; | 212 | Tegra::Engines::KeplerCompute& kepler_compute; |
| 213 | 213 | ||
| 214 | VKScreenInfo& screen_info; | 214 | VKScreenInfo& screen_info; |
| 215 | const VKDevice& device; | 215 | const Device& device; |
| 216 | VKMemoryManager& memory_manager; | 216 | MemoryAllocator& memory_allocator; |
| 217 | StateTracker& state_tracker; | 217 | StateTracker& state_tracker; |
| 218 | VKScheduler& scheduler; | 218 | VKScheduler& scheduler; |
| 219 | 219 | ||
| 220 | VKStreamBuffer stream_buffer; | 220 | VKStreamBuffer stream_buffer; |
| 221 | VKStagingBufferPool staging_pool; | 221 | StagingBufferPool staging_pool; |
| 222 | VKDescriptorPool descriptor_pool; | 222 | VKDescriptorPool descriptor_pool; |
| 223 | VKUpdateDescriptorQueue update_descriptor_queue; | 223 | VKUpdateDescriptorQueue update_descriptor_queue; |
| 224 | BlitImageHelper blit_image; | 224 | BlitImageHelper blit_image; |
| @@ -234,7 +234,7 @@ private: | |||
| 234 | VKFenceManager fence_manager; | 234 | VKFenceManager fence_manager; |
| 235 | 235 | ||
| 236 | vk::Buffer default_buffer; | 236 | vk::Buffer default_buffer; |
| 237 | VKMemoryCommit default_buffer_commit; | 237 | MemoryCommit default_buffer_commit; |
| 238 | vk::Event wfi_event; | 238 | vk::Event wfi_event; |
| 239 | VideoCommon::Shader::AsyncShaders async_shaders; | 239 | VideoCommon::Shader::AsyncShaders async_shaders; |
| 240 | 240 | ||
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index c104c6fe3..66004f9c0 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp | |||
| @@ -11,13 +11,13 @@ | |||
| 11 | #include "common/microprofile.h" | 11 | #include "common/microprofile.h" |
| 12 | #include "common/thread.h" | 12 | #include "common/thread.h" |
| 13 | #include "video_core/renderer_vulkan/vk_command_pool.h" | 13 | #include "video_core/renderer_vulkan/vk_command_pool.h" |
| 14 | #include "video_core/renderer_vulkan/vk_device.h" | ||
| 15 | #include "video_core/renderer_vulkan/vk_master_semaphore.h" | 14 | #include "video_core/renderer_vulkan/vk_master_semaphore.h" |
| 16 | #include "video_core/renderer_vulkan/vk_query_cache.h" | 15 | #include "video_core/renderer_vulkan/vk_query_cache.h" |
| 17 | #include "video_core/renderer_vulkan/vk_scheduler.h" | 16 | #include "video_core/renderer_vulkan/vk_scheduler.h" |
| 18 | #include "video_core/renderer_vulkan/vk_state_tracker.h" | 17 | #include "video_core/renderer_vulkan/vk_state_tracker.h" |
| 19 | #include "video_core/renderer_vulkan/vk_texture_cache.h" | 18 | #include "video_core/renderer_vulkan/vk_texture_cache.h" |
| 20 | #include "video_core/renderer_vulkan/wrapper.h" | 19 | #include "video_core/vulkan_common/vulkan_device.h" |
| 20 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 21 | 21 | ||
| 22 | namespace Vulkan { | 22 | namespace Vulkan { |
| 23 | 23 | ||
| @@ -37,7 +37,7 @@ void VKScheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf) { | |||
| 37 | last = nullptr; | 37 | last = nullptr; |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | VKScheduler::VKScheduler(const VKDevice& device_, StateTracker& state_tracker_) | 40 | VKScheduler::VKScheduler(const Device& device_, StateTracker& state_tracker_) |
| 41 | : device{device_}, state_tracker{state_tracker_}, | 41 | : device{device_}, state_tracker{state_tracker_}, |
| 42 | master_semaphore{std::make_unique<MasterSemaphore>(device)}, | 42 | master_semaphore{std::make_unique<MasterSemaphore>(device)}, |
| 43 | command_pool{std::make_unique<CommandPool>(*master_semaphore, device)} { | 43 | command_pool{std::make_unique<CommandPool>(*master_semaphore, device)} { |
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index 0a36c8fad..4cd43e425 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h | |||
| @@ -12,22 +12,22 @@ | |||
| 12 | #include <utility> | 12 | #include <utility> |
| 13 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 14 | #include "common/threadsafe_queue.h" | 14 | #include "common/threadsafe_queue.h" |
| 15 | #include "video_core/renderer_vulkan/wrapper.h" | 15 | #include "video_core/vulkan_common/vulkan_wrapper.h" |
| 16 | 16 | ||
| 17 | namespace Vulkan { | 17 | namespace Vulkan { |
| 18 | 18 | ||
| 19 | class CommandPool; | 19 | class CommandPool; |
| 20 | class Device; | ||
| 20 | class Framebuffer; | 21 | class Framebuffer; |
| 21 | class MasterSemaphore; | 22 | class MasterSemaphore; |
| 22 | class StateTracker; | 23 | class StateTracker; |
| 23 | class VKDevice; | ||
| 24 | class VKQueryCache; | 24 | class VKQueryCache; |
| 25 | 25 | ||
| 26 | /// The scheduler abstracts command buffer and fence management with an interface that's able to do | 26 | /// The scheduler abstracts command buffer and fence management with an interface that's able to do |
| 27 | /// OpenGL-like operations on Vulkan command buffers. | 27 | /// OpenGL-like operations on Vulkan command buffers. |
| 28 | class VKScheduler { | 28 | class VKScheduler { |
| 29 | public: | 29 | public: |
| 30 | explicit VKScheduler(const VKDevice& device, StateTracker& state_tracker); | 30 | explicit VKScheduler(const Device& device, StateTracker& state_tracker); |
| 31 | ~VKScheduler(); | 31 | ~VKScheduler(); |
| 32 | 32 | ||
| 33 | /// Returns the current command buffer tick. | 33 | /// Returns the current command buffer tick. |
| @@ -179,7 +179,7 @@ private: | |||
| 179 | 179 | ||
| 180 | void AcquireNewChunk(); | 180 | void AcquireNewChunk(); |
| 181 | 181 | ||
| 182 | const VKDevice& device; | 182 | const Device& device; |
| 183 | StateTracker& state_tracker; | 183 | StateTracker& state_tracker; |
| 184 | 184 | ||
| 185 | std::unique_ptr<MasterSemaphore> master_semaphore; | 185 | std::unique_ptr<MasterSemaphore> master_semaphore; |
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index 09d6f9f35..89cbe01ad 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp | |||
| @@ -22,11 +22,11 @@ | |||
| 22 | #include "video_core/engines/shader_bytecode.h" | 22 | #include "video_core/engines/shader_bytecode.h" |
| 23 | #include "video_core/engines/shader_header.h" | 23 | #include "video_core/engines/shader_header.h" |
| 24 | #include "video_core/engines/shader_type.h" | 24 | #include "video_core/engines/shader_type.h" |
| 25 | #include "video_core/renderer_vulkan/vk_device.h" | ||
| 26 | #include "video_core/renderer_vulkan/vk_shader_decompiler.h" | 25 | #include "video_core/renderer_vulkan/vk_shader_decompiler.h" |
| 27 | #include "video_core/shader/node.h" | 26 | #include "video_core/shader/node.h" |
| 28 | #include "video_core/shader/shader_ir.h" | 27 | #include "video_core/shader/shader_ir.h" |
| 29 | #include "video_core/shader/transform_feedback.h" | 28 | #include "video_core/shader/transform_feedback.h" |
| 29 | #include "video_core/vulkan_common/vulkan_device.h" | ||
| 30 | 30 | ||
| 31 | namespace Vulkan { | 31 | namespace Vulkan { |
| 32 | 32 | ||
| @@ -272,19 +272,12 @@ bool IsPrecise(Operation operand) { | |||
| 272 | return false; | 272 | return false; |
| 273 | } | 273 | } |
| 274 | 274 | ||
| 275 | u32 ShaderVersion(const VKDevice& device) { | ||
| 276 | if (device.InstanceApiVersion() < VK_API_VERSION_1_1) { | ||
| 277 | return 0x00010000; | ||
| 278 | } | ||
| 279 | return 0x00010300; | ||
| 280 | } | ||
| 281 | |||
| 282 | class SPIRVDecompiler final : public Sirit::Module { | 275 | class SPIRVDecompiler final : public Sirit::Module { |
| 283 | public: | 276 | public: |
| 284 | explicit SPIRVDecompiler(const VKDevice& device_, const ShaderIR& ir_, ShaderType stage_, | 277 | explicit SPIRVDecompiler(const Device& device_, const ShaderIR& ir_, ShaderType stage_, |
| 285 | const Registry& registry_, const Specialization& specialization_) | 278 | const Registry& registry_, const Specialization& specialization_) |
| 286 | : Module(ShaderVersion(device_)), device{device_}, ir{ir_}, stage{stage_}, | 279 | : Module(0x00010300), device{device_}, ir{ir_}, stage{stage_}, header{ir_.GetHeader()}, |
| 287 | header{ir_.GetHeader()}, registry{registry_}, specialization{specialization_} { | 280 | registry{registry_}, specialization{specialization_} { |
| 288 | if (stage_ != ShaderType::Compute) { | 281 | if (stage_ != ShaderType::Compute) { |
| 289 | transform_feedback = BuildTransformFeedback(registry_.GetGraphicsInfo()); | 282 | transform_feedback = BuildTransformFeedback(registry_.GetGraphicsInfo()); |
| 290 | } | 283 | } |
| @@ -2749,7 +2742,7 @@ private: | |||
| 2749 | }; | 2742 | }; |
| 2750 | static_assert(operation_decompilers.size() == static_cast<std::size_t>(OperationCode::Amount)); | 2743 | static_assert(operation_decompilers.size() == static_cast<std::size_t>(OperationCode::Amount)); |
| 2751 | 2744 | ||
| 2752 | const VKDevice& device; | 2745 | const Device& device; |
| 2753 | const ShaderIR& ir; | 2746 | const ShaderIR& ir; |
| 2754 | const ShaderType stage; | 2747 | const ShaderType stage; |
| 2755 | const Tegra::Shader::Header header; | 2748 | const Tegra::Shader::Header header; |
| @@ -3137,7 +3130,7 @@ ShaderEntries GenerateShaderEntries(const VideoCommon::Shader::ShaderIR& ir) { | |||
| 3137 | return entries; | 3130 | return entries; |
| 3138 | } | 3131 | } |
| 3139 | 3132 | ||
| 3140 | std::vector<u32> Decompile(const VKDevice& device, const VideoCommon::Shader::ShaderIR& ir, | 3133 | std::vector<u32> Decompile(const Device& device, const VideoCommon::Shader::ShaderIR& ir, |
| 3141 | ShaderType stage, const VideoCommon::Shader::Registry& registry, | 3134 | ShaderType stage, const VideoCommon::Shader::Registry& registry, |
| 3142 | const Specialization& specialization) { | 3135 | const Specialization& specialization) { |
| 3143 | return SPIRVDecompiler(device, ir, stage, registry, specialization).Assemble(); | 3136 | return SPIRVDecompiler(device, ir, stage, registry, specialization).Assemble(); |
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.h b/src/video_core/renderer_vulkan/vk_shader_decompiler.h index ad91ad5de..26381e444 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.h +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.h | |||
| @@ -15,10 +15,8 @@ | |||
| 15 | #include "video_core/shader/shader_ir.h" | 15 | #include "video_core/shader/shader_ir.h" |
| 16 | 16 | ||
| 17 | namespace Vulkan { | 17 | namespace Vulkan { |
| 18 | class VKDevice; | ||
| 19 | } | ||
| 20 | 18 | ||
| 21 | namespace Vulkan { | 19 | class Device; |
| 22 | 20 | ||
| 23 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; | 21 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; |
| 24 | using UniformTexelEntry = VideoCommon::Shader::SamplerEntry; | 22 | using UniformTexelEntry = VideoCommon::Shader::SamplerEntry; |
| @@ -109,7 +107,7 @@ struct SPIRVShader { | |||
| 109 | 107 | ||
| 110 | ShaderEntries GenerateShaderEntries(const VideoCommon::Shader::ShaderIR& ir); | 108 | ShaderEntries GenerateShaderEntries(const VideoCommon::Shader::ShaderIR& ir); |
| 111 | 109 | ||
| 112 | std::vector<u32> Decompile(const VKDevice& device, const VideoCommon::Shader::ShaderIR& ir, | 110 | std::vector<u32> Decompile(const Device& device, const VideoCommon::Shader::ShaderIR& ir, |
| 113 | Tegra::Engines::ShaderType stage, | 111 | Tegra::Engines::ShaderType stage, |
| 114 | const VideoCommon::Shader::Registry& registry, | 112 | const VideoCommon::Shader::Registry& registry, |
| 115 | const Specialization& specialization); | 113 | const Specialization& specialization); |
diff --git a/src/video_core/renderer_vulkan/vk_shader_util.cpp b/src/video_core/renderer_vulkan/vk_shader_util.cpp index 38a0be7f2..aaad4f292 100644 --- a/src/video_core/renderer_vulkan/vk_shader_util.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_util.cpp | |||
| @@ -7,13 +7,13 @@ | |||
| 7 | 7 | ||
| 8 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "video_core/renderer_vulkan/vk_device.h" | ||
| 11 | #include "video_core/renderer_vulkan/vk_shader_util.h" | 10 | #include "video_core/renderer_vulkan/vk_shader_util.h" |
| 12 | #include "video_core/renderer_vulkan/wrapper.h" | 11 | #include "video_core/vulkan_common/vulkan_device.h" |
| 12 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 13 | 13 | ||
| 14 | namespace Vulkan { | 14 | namespace Vulkan { |
| 15 | 15 | ||
| 16 | vk::ShaderModule BuildShader(const VKDevice& device, std::span<const u32> code) { | 16 | vk::ShaderModule BuildShader(const Device& device, std::span<const u32> code) { |
| 17 | return device.GetLogical().CreateShaderModule({ | 17 | return device.GetLogical().CreateShaderModule({ |
| 18 | .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, | 18 | .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, |
| 19 | .pNext = nullptr, | 19 | .pNext = nullptr, |
diff --git a/src/video_core/renderer_vulkan/vk_shader_util.h b/src/video_core/renderer_vulkan/vk_shader_util.h index dce34a140..9517cbe84 100644 --- a/src/video_core/renderer_vulkan/vk_shader_util.h +++ b/src/video_core/renderer_vulkan/vk_shader_util.h | |||
| @@ -7,12 +7,12 @@ | |||
| 7 | #include <span> | 7 | #include <span> |
| 8 | 8 | ||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "video_core/renderer_vulkan/wrapper.h" | 10 | #include "video_core/vulkan_common/vulkan_wrapper.h" |
| 11 | 11 | ||
| 12 | namespace Vulkan { | 12 | namespace Vulkan { |
| 13 | 13 | ||
| 14 | class VKDevice; | 14 | class Device; |
| 15 | 15 | ||
| 16 | vk::ShaderModule BuildShader(const VKDevice& device, std::span<const u32> code); | 16 | vk::ShaderModule BuildShader(const Device& device, std::span<const u32> code); |
| 17 | 17 | ||
| 18 | } // namespace Vulkan | 18 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp index 2fd3b7f39..97fd41cc1 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp | |||
| @@ -3,58 +3,66 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <unordered_map> | ||
| 7 | #include <utility> | 6 | #include <utility> |
| 8 | #include <vector> | 7 | #include <vector> |
| 9 | 8 | ||
| 9 | #include <fmt/format.h> | ||
| 10 | |||
| 11 | #include "common/assert.h" | ||
| 10 | #include "common/bit_util.h" | 12 | #include "common/bit_util.h" |
| 11 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 12 | #include "video_core/renderer_vulkan/vk_device.h" | ||
| 13 | #include "video_core/renderer_vulkan/vk_scheduler.h" | 14 | #include "video_core/renderer_vulkan/vk_scheduler.h" |
| 14 | #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" | 15 | #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" |
| 15 | #include "video_core/renderer_vulkan/wrapper.h" | 16 | #include "video_core/vulkan_common/vulkan_device.h" |
| 17 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 16 | 18 | ||
| 17 | namespace Vulkan { | 19 | namespace Vulkan { |
| 18 | 20 | ||
| 19 | VKStagingBufferPool::StagingBuffer::StagingBuffer(std::unique_ptr<VKBuffer> buffer_) | 21 | StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& memory_allocator_, |
| 20 | : buffer{std::move(buffer_)} {} | 22 | VKScheduler& scheduler_) |
| 21 | 23 | : device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_} {} | |
| 22 | VKStagingBufferPool::VKStagingBufferPool(const VKDevice& device_, VKMemoryManager& memory_manager_, | ||
| 23 | VKScheduler& scheduler_) | ||
| 24 | : device{device_}, memory_manager{memory_manager_}, scheduler{scheduler_} {} | ||
| 25 | 24 | ||
| 26 | VKStagingBufferPool::~VKStagingBufferPool() = default; | 25 | StagingBufferPool::~StagingBufferPool() = default; |
| 27 | 26 | ||
| 28 | VKBuffer& VKStagingBufferPool::GetUnusedBuffer(std::size_t size, bool host_visible) { | 27 | StagingBufferRef StagingBufferPool::Request(size_t size, MemoryUsage usage) { |
| 29 | if (const auto buffer = TryGetReservedBuffer(size, host_visible)) { | 28 | if (const std::optional<StagingBufferRef> ref = TryGetReservedBuffer(size, usage)) { |
| 30 | return *buffer; | 29 | return *ref; |
| 31 | } | 30 | } |
| 32 | return CreateStagingBuffer(size, host_visible); | 31 | return CreateStagingBuffer(size, usage); |
| 33 | } | 32 | } |
| 34 | 33 | ||
| 35 | void VKStagingBufferPool::TickFrame() { | 34 | void StagingBufferPool::TickFrame() { |
| 36 | current_delete_level = (current_delete_level + 1) % NumLevels; | 35 | current_delete_level = (current_delete_level + 1) % NUM_LEVELS; |
| 37 | 36 | ||
| 38 | ReleaseCache(true); | 37 | ReleaseCache(MemoryUsage::DeviceLocal); |
| 39 | ReleaseCache(false); | 38 | ReleaseCache(MemoryUsage::Upload); |
| 39 | ReleaseCache(MemoryUsage::Download); | ||
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | VKBuffer* VKStagingBufferPool::TryGetReservedBuffer(std::size_t size, bool host_visible) { | 42 | std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t size, |
| 43 | for (StagingBuffer& entry : GetCache(host_visible)[Common::Log2Ceil64(size)].entries) { | 43 | MemoryUsage usage) { |
| 44 | if (!scheduler.IsFree(entry.tick)) { | 44 | StagingBuffers& cache_level = GetCache(usage)[Common::Log2Ceil64(size)]; |
| 45 | continue; | 45 | |
| 46 | const auto is_free = [this](const StagingBuffer& entry) { | ||
| 47 | return scheduler.IsFree(entry.tick); | ||
| 48 | }; | ||
| 49 | auto& entries = cache_level.entries; | ||
| 50 | const auto hint_it = entries.begin() + cache_level.iterate_index; | ||
| 51 | auto it = std::find_if(entries.begin() + cache_level.iterate_index, entries.end(), is_free); | ||
| 52 | if (it == entries.end()) { | ||
| 53 | it = std::find_if(entries.begin(), hint_it, is_free); | ||
| 54 | if (it == hint_it) { | ||
| 55 | return std::nullopt; | ||
| 46 | } | 56 | } |
| 47 | entry.tick = scheduler.CurrentTick(); | ||
| 48 | return &*entry.buffer; | ||
| 49 | } | 57 | } |
| 50 | return nullptr; | 58 | cache_level.iterate_index = std::distance(entries.begin(), it) + 1; |
| 59 | it->tick = scheduler.CurrentTick(); | ||
| 60 | return it->Ref(); | ||
| 51 | } | 61 | } |
| 52 | 62 | ||
| 53 | VKBuffer& VKStagingBufferPool::CreateStagingBuffer(std::size_t size, bool host_visible) { | 63 | StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage usage) { |
| 54 | const u32 log2 = Common::Log2Ceil64(size); | 64 | const u32 log2 = Common::Log2Ceil64(size); |
| 55 | 65 | vk::Buffer buffer = device.GetLogical().CreateBuffer({ | |
| 56 | auto buffer = std::make_unique<VKBuffer>(); | ||
| 57 | buffer->handle = device.GetLogical().CreateBuffer({ | ||
| 58 | .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, | 66 | .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, |
| 59 | .pNext = nullptr, | 67 | .pNext = nullptr, |
| 60 | .flags = 0, | 68 | .flags = 0, |
| @@ -66,49 +74,63 @@ VKBuffer& VKStagingBufferPool::CreateStagingBuffer(std::size_t size, bool host_v | |||
| 66 | .queueFamilyIndexCount = 0, | 74 | .queueFamilyIndexCount = 0, |
| 67 | .pQueueFamilyIndices = nullptr, | 75 | .pQueueFamilyIndices = nullptr, |
| 68 | }); | 76 | }); |
| 69 | buffer->commit = memory_manager.Commit(buffer->handle, host_visible); | 77 | if (device.HasDebuggingToolAttached()) { |
| 70 | 78 | ++buffer_index; | |
| 71 | std::vector<StagingBuffer>& entries = GetCache(host_visible)[log2].entries; | 79 | buffer.SetObjectNameEXT(fmt::format("Staging Buffer {}", buffer_index).c_str()); |
| 72 | StagingBuffer& entry = entries.emplace_back(std::move(buffer)); | 80 | } |
| 73 | entry.tick = scheduler.CurrentTick(); | 81 | MemoryCommit commit = memory_allocator.Commit(buffer, usage); |
| 74 | return *entry.buffer; | 82 | const std::span<u8> mapped_span = IsHostVisible(usage) ? commit.Map() : std::span<u8>{}; |
| 75 | } | 83 | |
| 76 | 84 | StagingBuffer& entry = GetCache(usage)[log2].entries.emplace_back(StagingBuffer{ | |
| 77 | VKStagingBufferPool::StagingBuffersCache& VKStagingBufferPool::GetCache(bool host_visible) { | 85 | .buffer = std::move(buffer), |
| 78 | return host_visible ? host_staging_buffers : device_staging_buffers; | 86 | .commit = std::move(commit), |
| 87 | .mapped_span = mapped_span, | ||
| 88 | .tick = scheduler.CurrentTick(), | ||
| 89 | }); | ||
| 90 | return entry.Ref(); | ||
| 79 | } | 91 | } |
| 80 | 92 | ||
| 81 | void VKStagingBufferPool::ReleaseCache(bool host_visible) { | 93 | StagingBufferPool::StagingBuffersCache& StagingBufferPool::GetCache(MemoryUsage usage) { |
| 82 | auto& cache = GetCache(host_visible); | 94 | switch (usage) { |
| 83 | const u64 size = ReleaseLevel(cache, current_delete_level); | 95 | case MemoryUsage::DeviceLocal: |
| 84 | if (size == 0) { | 96 | return device_local_cache; |
| 85 | return; | 97 | case MemoryUsage::Upload: |
| 98 | return upload_cache; | ||
| 99 | case MemoryUsage::Download: | ||
| 100 | return download_cache; | ||
| 101 | default: | ||
| 102 | UNREACHABLE_MSG("Invalid memory usage={}", usage); | ||
| 103 | return upload_cache; | ||
| 86 | } | 104 | } |
| 87 | } | 105 | } |
| 88 | 106 | ||
| 89 | u64 VKStagingBufferPool::ReleaseLevel(StagingBuffersCache& cache, std::size_t log2) { | 107 | void StagingBufferPool::ReleaseCache(MemoryUsage usage) { |
| 90 | static constexpr std::size_t deletions_per_tick = 16; | 108 | ReleaseLevel(GetCache(usage), current_delete_level); |
| 109 | } | ||
| 91 | 110 | ||
| 111 | void StagingBufferPool::ReleaseLevel(StagingBuffersCache& cache, size_t log2) { | ||
| 112 | constexpr size_t deletions_per_tick = 16; | ||
| 92 | auto& staging = cache[log2]; | 113 | auto& staging = cache[log2]; |
| 93 | auto& entries = staging.entries; | 114 | auto& entries = staging.entries; |
| 94 | const std::size_t old_size = entries.size(); | 115 | const size_t old_size = entries.size(); |
| 95 | 116 | ||
| 96 | const auto is_deleteable = [this](const StagingBuffer& entry) { | 117 | const auto is_deleteable = [this](const StagingBuffer& entry) { |
| 97 | return scheduler.IsFree(entry.tick); | 118 | return scheduler.IsFree(entry.tick); |
| 98 | }; | 119 | }; |
| 99 | const std::size_t begin_offset = staging.delete_index; | 120 | const size_t begin_offset = staging.delete_index; |
| 100 | const std::size_t end_offset = std::min(begin_offset + deletions_per_tick, old_size); | 121 | const size_t end_offset = std::min(begin_offset + deletions_per_tick, old_size); |
| 101 | const auto begin = std::begin(entries) + begin_offset; | 122 | const auto begin = entries.begin() + begin_offset; |
| 102 | const auto end = std::begin(entries) + end_offset; | 123 | const auto end = entries.begin() + end_offset; |
| 103 | entries.erase(std::remove_if(begin, end, is_deleteable), end); | 124 | entries.erase(std::remove_if(begin, end, is_deleteable), end); |
| 104 | 125 | ||
| 105 | const std::size_t new_size = entries.size(); | 126 | const size_t new_size = entries.size(); |
| 106 | staging.delete_index += deletions_per_tick; | 127 | staging.delete_index += deletions_per_tick; |
| 107 | if (staging.delete_index >= new_size) { | 128 | if (staging.delete_index >= new_size) { |
| 108 | staging.delete_index = 0; | 129 | staging.delete_index = 0; |
| 109 | } | 130 | } |
| 110 | 131 | if (staging.iterate_index > new_size) { | |
| 111 | return (1ULL << log2) * (old_size - new_size); | 132 | staging.iterate_index = 0; |
| 133 | } | ||
| 112 | } | 134 | } |
| 113 | 135 | ||
| 114 | } // namespace Vulkan | 136 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h index 2dd5049ac..d42918a47 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h | |||
| @@ -9,63 +9,73 @@ | |||
| 9 | 9 | ||
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | 11 | ||
| 12 | #include "video_core/renderer_vulkan/vk_memory_manager.h" | 12 | #include "video_core/vulkan_common/vulkan_memory_allocator.h" |
| 13 | #include "video_core/renderer_vulkan/wrapper.h" | 13 | #include "video_core/vulkan_common/vulkan_wrapper.h" |
| 14 | 14 | ||
| 15 | namespace Vulkan { | 15 | namespace Vulkan { |
| 16 | 16 | ||
| 17 | class VKDevice; | 17 | class Device; |
| 18 | class VKScheduler; | 18 | class VKScheduler; |
| 19 | 19 | ||
| 20 | struct VKBuffer final { | 20 | struct StagingBufferRef { |
| 21 | vk::Buffer handle; | 21 | VkBuffer buffer; |
| 22 | VKMemoryCommit commit; | 22 | std::span<u8> mapped_span; |
| 23 | }; | 23 | }; |
| 24 | 24 | ||
| 25 | class VKStagingBufferPool final { | 25 | class StagingBufferPool { |
| 26 | public: | 26 | public: |
| 27 | explicit VKStagingBufferPool(const VKDevice& device, VKMemoryManager& memory_manager, | 27 | explicit StagingBufferPool(const Device& device, MemoryAllocator& memory_allocator, |
| 28 | VKScheduler& scheduler); | 28 | VKScheduler& scheduler); |
| 29 | ~VKStagingBufferPool(); | 29 | ~StagingBufferPool(); |
| 30 | 30 | ||
| 31 | VKBuffer& GetUnusedBuffer(std::size_t size, bool host_visible); | 31 | StagingBufferRef Request(size_t size, MemoryUsage usage); |
| 32 | 32 | ||
| 33 | void TickFrame(); | 33 | void TickFrame(); |
| 34 | 34 | ||
| 35 | private: | 35 | private: |
| 36 | struct StagingBuffer final { | 36 | struct StagingBuffer { |
| 37 | explicit StagingBuffer(std::unique_ptr<VKBuffer> buffer); | 37 | vk::Buffer buffer; |
| 38 | 38 | MemoryCommit commit; | |
| 39 | std::unique_ptr<VKBuffer> buffer; | 39 | std::span<u8> mapped_span; |
| 40 | u64 tick = 0; | 40 | u64 tick = 0; |
| 41 | |||
| 42 | StagingBufferRef Ref() const noexcept { | ||
| 43 | return { | ||
| 44 | .buffer = *buffer, | ||
| 45 | .mapped_span = mapped_span, | ||
| 46 | }; | ||
| 47 | } | ||
| 41 | }; | 48 | }; |
| 42 | 49 | ||
| 43 | struct StagingBuffers final { | 50 | struct StagingBuffers { |
| 44 | std::vector<StagingBuffer> entries; | 51 | std::vector<StagingBuffer> entries; |
| 45 | std::size_t delete_index = 0; | 52 | size_t delete_index = 0; |
| 53 | size_t iterate_index = 0; | ||
| 46 | }; | 54 | }; |
| 47 | 55 | ||
| 48 | static constexpr std::size_t NumLevels = sizeof(std::size_t) * CHAR_BIT; | 56 | static constexpr size_t NUM_LEVELS = sizeof(size_t) * CHAR_BIT; |
| 49 | using StagingBuffersCache = std::array<StagingBuffers, NumLevels>; | 57 | using StagingBuffersCache = std::array<StagingBuffers, NUM_LEVELS>; |
| 50 | 58 | ||
| 51 | VKBuffer* TryGetReservedBuffer(std::size_t size, bool host_visible); | 59 | std::optional<StagingBufferRef> TryGetReservedBuffer(size_t size, MemoryUsage usage); |
| 52 | 60 | ||
| 53 | VKBuffer& CreateStagingBuffer(std::size_t size, bool host_visible); | 61 | StagingBufferRef CreateStagingBuffer(size_t size, MemoryUsage usage); |
| 54 | 62 | ||
| 55 | StagingBuffersCache& GetCache(bool host_visible); | 63 | StagingBuffersCache& GetCache(MemoryUsage usage); |
| 56 | 64 | ||
| 57 | void ReleaseCache(bool host_visible); | 65 | void ReleaseCache(MemoryUsage usage); |
| 58 | 66 | ||
| 59 | u64 ReleaseLevel(StagingBuffersCache& cache, std::size_t log2); | 67 | void ReleaseLevel(StagingBuffersCache& cache, size_t log2); |
| 60 | 68 | ||
| 61 | const VKDevice& device; | 69 | const Device& device; |
| 62 | VKMemoryManager& memory_manager; | 70 | MemoryAllocator& memory_allocator; |
| 63 | VKScheduler& scheduler; | 71 | VKScheduler& scheduler; |
| 64 | 72 | ||
| 65 | StagingBuffersCache host_staging_buffers; | 73 | StagingBuffersCache device_local_cache; |
| 66 | StagingBuffersCache device_staging_buffers; | 74 | StagingBuffersCache upload_cache; |
| 75 | StagingBuffersCache download_cache; | ||
| 67 | 76 | ||
| 68 | std::size_t current_delete_level = 0; | 77 | size_t current_delete_level = 0; |
| 78 | u64 buffer_index = 0; | ||
| 69 | }; | 79 | }; |
| 70 | 80 | ||
| 71 | } // namespace Vulkan | 81 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp index 419cb154d..a09fe084e 100644 --- a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp +++ b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp | |||
| @@ -10,10 +10,10 @@ | |||
| 10 | 10 | ||
| 11 | #include "common/alignment.h" | 11 | #include "common/alignment.h" |
| 12 | #include "common/assert.h" | 12 | #include "common/assert.h" |
| 13 | #include "video_core/renderer_vulkan/vk_device.h" | ||
| 14 | #include "video_core/renderer_vulkan/vk_scheduler.h" | 13 | #include "video_core/renderer_vulkan/vk_scheduler.h" |
| 15 | #include "video_core/renderer_vulkan/vk_stream_buffer.h" | 14 | #include "video_core/renderer_vulkan/vk_stream_buffer.h" |
| 16 | #include "video_core/renderer_vulkan/wrapper.h" | 15 | #include "video_core/vulkan_common/vulkan_device.h" |
| 16 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 17 | 17 | ||
| 18 | namespace Vulkan { | 18 | namespace Vulkan { |
| 19 | 19 | ||
| @@ -60,7 +60,7 @@ u32 GetMemoryType(const VkPhysicalDeviceMemoryProperties& properties, | |||
| 60 | 60 | ||
| 61 | } // Anonymous namespace | 61 | } // Anonymous namespace |
| 62 | 62 | ||
| 63 | VKStreamBuffer::VKStreamBuffer(const VKDevice& device_, VKScheduler& scheduler_) | 63 | VKStreamBuffer::VKStreamBuffer(const Device& device_, VKScheduler& scheduler_) |
| 64 | : device{device_}, scheduler{scheduler_} { | 64 | : device{device_}, scheduler{scheduler_} { |
| 65 | CreateBuffers(); | 65 | CreateBuffers(); |
| 66 | ReserveWatches(current_watches, WATCHES_INITIAL_RESERVE); | 66 | ReserveWatches(current_watches, WATCHES_INITIAL_RESERVE); |
diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.h b/src/video_core/renderer_vulkan/vk_stream_buffer.h index 1428f77bf..2e9c8cb46 100644 --- a/src/video_core/renderer_vulkan/vk_stream_buffer.h +++ b/src/video_core/renderer_vulkan/vk_stream_buffer.h | |||
| @@ -9,17 +9,17 @@ | |||
| 9 | #include <vector> | 9 | #include <vector> |
| 10 | 10 | ||
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "video_core/renderer_vulkan/wrapper.h" | 12 | #include "video_core/vulkan_common/vulkan_wrapper.h" |
| 13 | 13 | ||
| 14 | namespace Vulkan { | 14 | namespace Vulkan { |
| 15 | 15 | ||
| 16 | class VKDevice; | 16 | class Device; |
| 17 | class VKFenceWatch; | 17 | class VKFenceWatch; |
| 18 | class VKScheduler; | 18 | class VKScheduler; |
| 19 | 19 | ||
| 20 | class VKStreamBuffer final { | 20 | class VKStreamBuffer final { |
| 21 | public: | 21 | public: |
| 22 | explicit VKStreamBuffer(const VKDevice& device, VKScheduler& scheduler); | 22 | explicit VKStreamBuffer(const Device& device, VKScheduler& scheduler); |
| 23 | ~VKStreamBuffer(); | 23 | ~VKStreamBuffer(); |
| 24 | 24 | ||
| 25 | /** | 25 | /** |
| @@ -54,7 +54,7 @@ private: | |||
| 54 | 54 | ||
| 55 | void WaitPendingOperations(u64 requested_upper_bound); | 55 | void WaitPendingOperations(u64 requested_upper_bound); |
| 56 | 56 | ||
| 57 | const VKDevice& device; ///< Vulkan device manager. | 57 | const Device& device; ///< Vulkan device manager. |
| 58 | VKScheduler& scheduler; ///< Command scheduler. | 58 | VKScheduler& scheduler; ///< Command scheduler. |
| 59 | 59 | ||
| 60 | vk::Buffer buffer; ///< Mapped buffer. | 60 | vk::Buffer buffer; ///< Mapped buffer. |
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index 9636a7c65..725a2a05d 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp | |||
| @@ -11,10 +11,10 @@ | |||
| 11 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 12 | #include "core/core.h" | 12 | #include "core/core.h" |
| 13 | #include "core/frontend/framebuffer_layout.h" | 13 | #include "core/frontend/framebuffer_layout.h" |
| 14 | #include "video_core/renderer_vulkan/vk_device.h" | ||
| 15 | #include "video_core/renderer_vulkan/vk_scheduler.h" | 14 | #include "video_core/renderer_vulkan/vk_scheduler.h" |
| 16 | #include "video_core/renderer_vulkan/vk_swapchain.h" | 15 | #include "video_core/renderer_vulkan/vk_swapchain.h" |
| 17 | #include "video_core/renderer_vulkan/wrapper.h" | 16 | #include "video_core/vulkan_common/vulkan_device.h" |
| 17 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 18 | 18 | ||
| 19 | namespace Vulkan { | 19 | namespace Vulkan { |
| 20 | 20 | ||
| @@ -56,7 +56,7 @@ VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, u32 wi | |||
| 56 | 56 | ||
| 57 | } // Anonymous namespace | 57 | } // Anonymous namespace |
| 58 | 58 | ||
| 59 | VKSwapchain::VKSwapchain(VkSurfaceKHR surface_, const VKDevice& device_, VKScheduler& scheduler_) | 59 | VKSwapchain::VKSwapchain(VkSurfaceKHR surface_, const Device& device_, VKScheduler& scheduler_) |
| 60 | : surface{surface_}, device{device_}, scheduler{scheduler_} {} | 60 | : surface{surface_}, device{device_}, scheduler{scheduler_} {} |
| 61 | 61 | ||
| 62 | VKSwapchain::~VKSwapchain() = default; | 62 | VKSwapchain::~VKSwapchain() = default; |
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h index 6b39befdf..2eadd62b3 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.h +++ b/src/video_core/renderer_vulkan/vk_swapchain.h | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | #include <vector> | 7 | #include <vector> |
| 8 | 8 | ||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "video_core/renderer_vulkan/wrapper.h" | 10 | #include "video_core/vulkan_common/vulkan_wrapper.h" |
| 11 | 11 | ||
| 12 | namespace Layout { | 12 | namespace Layout { |
| 13 | struct FramebufferLayout; | 13 | struct FramebufferLayout; |
| @@ -15,12 +15,12 @@ struct FramebufferLayout; | |||
| 15 | 15 | ||
| 16 | namespace Vulkan { | 16 | namespace Vulkan { |
| 17 | 17 | ||
| 18 | class VKDevice; | 18 | class Device; |
| 19 | class VKScheduler; | 19 | class VKScheduler; |
| 20 | 20 | ||
| 21 | class VKSwapchain { | 21 | class VKSwapchain { |
| 22 | public: | 22 | public: |
| 23 | explicit VKSwapchain(VkSurfaceKHR surface, const VKDevice& device, VKScheduler& scheduler); | 23 | explicit VKSwapchain(VkSurfaceKHR surface, const Device& device, VKScheduler& scheduler); |
| 24 | ~VKSwapchain(); | 24 | ~VKSwapchain(); |
| 25 | 25 | ||
| 26 | /// Creates (or recreates) the swapchain with a given size. | 26 | /// Creates (or recreates) the swapchain with a given size. |
| @@ -73,7 +73,7 @@ private: | |||
| 73 | void Destroy(); | 73 | void Destroy(); |
| 74 | 74 | ||
| 75 | const VkSurfaceKHR surface; | 75 | const VkSurfaceKHR surface; |
| 76 | const VKDevice& device; | 76 | const Device& device; |
| 77 | VKScheduler& scheduler; | 77 | VKScheduler& scheduler; |
| 78 | 78 | ||
| 79 | vk::SwapchainKHR swapchain; | 79 | vk::SwapchainKHR swapchain; |
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 261808391..ab14922d7 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp | |||
| @@ -10,11 +10,13 @@ | |||
| 10 | #include "video_core/engines/fermi_2d.h" | 10 | #include "video_core/engines/fermi_2d.h" |
| 11 | #include "video_core/renderer_vulkan/blit_image.h" | 11 | #include "video_core/renderer_vulkan/blit_image.h" |
| 12 | #include "video_core/renderer_vulkan/maxwell_to_vk.h" | 12 | #include "video_core/renderer_vulkan/maxwell_to_vk.h" |
| 13 | #include "video_core/renderer_vulkan/vk_device.h" | 13 | #include "video_core/renderer_vulkan/vk_rasterizer.h" |
| 14 | #include "video_core/renderer_vulkan/vk_scheduler.h" | 14 | #include "video_core/renderer_vulkan/vk_scheduler.h" |
| 15 | #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" | 15 | #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" |
| 16 | #include "video_core/renderer_vulkan/vk_texture_cache.h" | 16 | #include "video_core/renderer_vulkan/vk_texture_cache.h" |
| 17 | #include "video_core/renderer_vulkan/wrapper.h" | 17 | #include "video_core/vulkan_common/vulkan_device.h" |
| 18 | #include "video_core/vulkan_common/vulkan_memory_allocator.h" | ||
| 19 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 18 | 20 | ||
| 19 | namespace Vulkan { | 21 | namespace Vulkan { |
| 20 | 22 | ||
| @@ -93,7 +95,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) { | |||
| 93 | } | 95 | } |
| 94 | } | 96 | } |
| 95 | 97 | ||
| 96 | [[nodiscard]] VkImageCreateInfo MakeImageCreateInfo(const VKDevice& device, const ImageInfo& info) { | 98 | [[nodiscard]] VkImageCreateInfo MakeImageCreateInfo(const Device& device, const ImageInfo& info) { |
| 97 | const auto format_info = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, info.format); | 99 | const auto format_info = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, info.format); |
| 98 | VkImageCreateFlags flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; | 100 | VkImageCreateFlags flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; |
| 99 | if (info.type == ImageType::e2D && info.resources.layers >= 6 && | 101 | if (info.type == ImageType::e2D && info.resources.layers >= 6 && |
| @@ -146,14 +148,14 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) { | |||
| 146 | }; | 148 | }; |
| 147 | } | 149 | } |
| 148 | 150 | ||
| 149 | [[nodiscard]] vk::Image MakeImage(const VKDevice& device, const ImageInfo& info) { | 151 | [[nodiscard]] vk::Image MakeImage(const Device& device, const ImageInfo& info) { |
| 150 | if (info.type == ImageType::Buffer) { | 152 | if (info.type == ImageType::Buffer) { |
| 151 | return vk::Image{}; | 153 | return vk::Image{}; |
| 152 | } | 154 | } |
| 153 | return device.GetLogical().CreateImage(MakeImageCreateInfo(device, info)); | 155 | return device.GetLogical().CreateImage(MakeImageCreateInfo(device, info)); |
| 154 | } | 156 | } |
| 155 | 157 | ||
| 156 | [[nodiscard]] vk::Buffer MakeBuffer(const VKDevice& device, const ImageInfo& info) { | 158 | [[nodiscard]] vk::Buffer MakeBuffer(const Device& device, const ImageInfo& info) { |
| 157 | if (info.type != ImageType::Buffer) { | 159 | if (info.type != ImageType::Buffer) { |
| 158 | return vk::Buffer{}; | 160 | return vk::Buffer{}; |
| 159 | } | 161 | } |
| @@ -205,7 +207,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) { | |||
| 205 | } | 207 | } |
| 206 | } | 208 | } |
| 207 | 209 | ||
| 208 | [[nodiscard]] VkAttachmentDescription AttachmentDescription(const VKDevice& device, | 210 | [[nodiscard]] VkAttachmentDescription AttachmentDescription(const Device& device, |
| 209 | const ImageView* image_view) { | 211 | const ImageView* image_view) { |
| 210 | const auto pixel_format = image_view->format; | 212 | const auto pixel_format = image_view->format; |
| 211 | return VkAttachmentDescription{ | 213 | return VkAttachmentDescription{ |
| @@ -552,10 +554,18 @@ void TextureCacheRuntime::Finish() { | |||
| 552 | } | 554 | } |
| 553 | 555 | ||
| 554 | ImageBufferMap TextureCacheRuntime::MapUploadBuffer(size_t size) { | 556 | ImageBufferMap TextureCacheRuntime::MapUploadBuffer(size_t size) { |
| 555 | const auto& buffer = staging_buffer_pool.GetUnusedBuffer(size, true); | 557 | const auto staging_ref = staging_buffer_pool.Request(size, MemoryUsage::Upload); |
| 556 | return ImageBufferMap{ | 558 | return { |
| 557 | .handle = *buffer.handle, | 559 | .handle = staging_ref.buffer, |
| 558 | .map = buffer.commit->Map(size), | 560 | .span = staging_ref.mapped_span, |
| 561 | }; | ||
| 562 | } | ||
| 563 | |||
| 564 | ImageBufferMap TextureCacheRuntime::MapDownloadBuffer(size_t size) { | ||
| 565 | const auto staging_ref = staging_buffer_pool.Request(size, MemoryUsage::Download); | ||
| 566 | return { | ||
| 567 | .handle = staging_ref.buffer, | ||
| 568 | .span = staging_ref.mapped_span, | ||
| 559 | }; | 569 | }; |
| 560 | } | 570 | } |
| 561 | 571 | ||
| @@ -786,9 +796,9 @@ Image::Image(TextureCacheRuntime& runtime, const ImageInfo& info_, GPUVAddr gpu_ | |||
| 786 | image(MakeImage(runtime.device, info)), buffer(MakeBuffer(runtime.device, info)), | 796 | image(MakeImage(runtime.device, info)), buffer(MakeBuffer(runtime.device, info)), |
| 787 | aspect_mask(ImageAspectMask(info.format)) { | 797 | aspect_mask(ImageAspectMask(info.format)) { |
| 788 | if (image) { | 798 | if (image) { |
| 789 | commit = runtime.memory_manager.Commit(image, false); | 799 | commit = runtime.memory_allocator.Commit(image, MemoryUsage::DeviceLocal); |
| 790 | } else { | 800 | } else { |
| 791 | commit = runtime.memory_manager.Commit(buffer, false); | 801 | commit = runtime.memory_allocator.Commit(buffer, MemoryUsage::DeviceLocal); |
| 792 | } | 802 | } |
| 793 | if (IsPixelFormatASTC(info.format) && !runtime.device.IsOptimalAstcSupported()) { | 803 | if (IsPixelFormatASTC(info.format) && !runtime.device.IsOptimalAstcSupported()) { |
| 794 | flags |= VideoCommon::ImageFlagBits::Converted; | 804 | flags |= VideoCommon::ImageFlagBits::Converted; |
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index edc3d80c0..a55d405d1 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h | |||
| @@ -7,9 +7,9 @@ | |||
| 7 | #include <compare> | 7 | #include <compare> |
| 8 | #include <span> | 8 | #include <span> |
| 9 | 9 | ||
| 10 | #include "video_core/renderer_vulkan/vk_memory_manager.h" | ||
| 11 | #include "video_core/renderer_vulkan/wrapper.h" | ||
| 12 | #include "video_core/texture_cache/texture_cache.h" | 10 | #include "video_core/texture_cache/texture_cache.h" |
| 11 | #include "video_core/vulkan_common/vulkan_memory_allocator.h" | ||
| 12 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 13 | 13 | ||
| 14 | namespace Vulkan { | 14 | namespace Vulkan { |
| 15 | 15 | ||
| @@ -19,14 +19,13 @@ using VideoCommon::Offset2D; | |||
| 19 | using VideoCommon::RenderTargets; | 19 | using VideoCommon::RenderTargets; |
| 20 | using VideoCore::Surface::PixelFormat; | 20 | using VideoCore::Surface::PixelFormat; |
| 21 | 21 | ||
| 22 | class VKDevice; | ||
| 23 | class VKScheduler; | ||
| 24 | class VKStagingBufferPool; | ||
| 25 | |||
| 26 | class BlitImageHelper; | 22 | class BlitImageHelper; |
| 23 | class Device; | ||
| 27 | class Image; | 24 | class Image; |
| 28 | class ImageView; | 25 | class ImageView; |
| 29 | class Framebuffer; | 26 | class Framebuffer; |
| 27 | class StagingBufferPool; | ||
| 28 | class VKScheduler; | ||
| 30 | 29 | ||
| 31 | struct RenderPassKey { | 30 | struct RenderPassKey { |
| 32 | constexpr auto operator<=>(const RenderPassKey&) const noexcept = default; | 31 | constexpr auto operator<=>(const RenderPassKey&) const noexcept = default; |
| @@ -60,18 +59,18 @@ struct ImageBufferMap { | |||
| 60 | } | 59 | } |
| 61 | 60 | ||
| 62 | [[nodiscard]] std::span<u8> Span() const noexcept { | 61 | [[nodiscard]] std::span<u8> Span() const noexcept { |
| 63 | return map.Span(); | 62 | return span; |
| 64 | } | 63 | } |
| 65 | 64 | ||
| 66 | VkBuffer handle; | 65 | VkBuffer handle; |
| 67 | MemoryMap map; | 66 | std::span<u8> span; |
| 68 | }; | 67 | }; |
| 69 | 68 | ||
| 70 | struct TextureCacheRuntime { | 69 | struct TextureCacheRuntime { |
| 71 | const VKDevice& device; | 70 | const Device& device; |
| 72 | VKScheduler& scheduler; | 71 | VKScheduler& scheduler; |
| 73 | VKMemoryManager& memory_manager; | 72 | MemoryAllocator& memory_allocator; |
| 74 | VKStagingBufferPool& staging_buffer_pool; | 73 | StagingBufferPool& staging_buffer_pool; |
| 75 | BlitImageHelper& blit_image_helper; | 74 | BlitImageHelper& blit_image_helper; |
| 76 | std::unordered_map<RenderPassKey, vk::RenderPass> renderpass_cache; | 75 | std::unordered_map<RenderPassKey, vk::RenderPass> renderpass_cache; |
| 77 | 76 | ||
| @@ -79,10 +78,7 @@ struct TextureCacheRuntime { | |||
| 79 | 78 | ||
| 80 | [[nodiscard]] ImageBufferMap MapUploadBuffer(size_t size); | 79 | [[nodiscard]] ImageBufferMap MapUploadBuffer(size_t size); |
| 81 | 80 | ||
| 82 | [[nodiscard]] ImageBufferMap MapDownloadBuffer(size_t size) { | 81 | [[nodiscard]] ImageBufferMap MapDownloadBuffer(size_t size); |
| 83 | // TODO: Have a special function for this | ||
| 84 | return MapUploadBuffer(size); | ||
| 85 | } | ||
| 86 | 82 | ||
| 87 | void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src, | 83 | void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src, |
| 88 | const std::array<Offset2D, 2>& dst_region, | 84 | const std::array<Offset2D, 2>& dst_region, |
| @@ -104,6 +100,11 @@ struct TextureCacheRuntime { | |||
| 104 | } | 100 | } |
| 105 | 101 | ||
| 106 | void InsertUploadMemoryBarrier() {} | 102 | void InsertUploadMemoryBarrier() {} |
| 103 | |||
| 104 | bool HasBrokenTextureViewFormats() const noexcept { | ||
| 105 | // No known Vulkan driver has broken image views | ||
| 106 | return false; | ||
| 107 | } | ||
| 107 | }; | 108 | }; |
| 108 | 109 | ||
| 109 | class Image : public VideoCommon::ImageBase { | 110 | class Image : public VideoCommon::ImageBase { |
| @@ -136,7 +137,7 @@ private: | |||
| 136 | VKScheduler* scheduler; | 137 | VKScheduler* scheduler; |
| 137 | vk::Image image; | 138 | vk::Image image; |
| 138 | vk::Buffer buffer; | 139 | vk::Buffer buffer; |
| 139 | VKMemoryCommit commit; | 140 | MemoryCommit commit; |
| 140 | VkImageAspectFlags aspect_mask = 0; | 141 | VkImageAspectFlags aspect_mask = 0; |
| 141 | bool initialized = false; | 142 | bool initialized = false; |
| 142 | }; | 143 | }; |
| @@ -177,7 +178,7 @@ public: | |||
| 177 | private: | 178 | private: |
| 178 | [[nodiscard]] vk::ImageView MakeDepthStencilView(VkImageAspectFlags aspect_mask); | 179 | [[nodiscard]] vk::ImageView MakeDepthStencilView(VkImageAspectFlags aspect_mask); |
| 179 | 180 | ||
| 180 | const VKDevice* device = nullptr; | 181 | const Device* device = nullptr; |
| 181 | std::array<vk::ImageView, VideoCommon::NUM_IMAGE_VIEW_TYPES> image_views; | 182 | std::array<vk::ImageView, VideoCommon::NUM_IMAGE_VIEW_TYPES> image_views; |
| 182 | vk::ImageView depth_view; | 183 | vk::ImageView depth_view; |
| 183 | vk::ImageView stencil_view; | 184 | vk::ImageView stencil_view; |
diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.cpp b/src/video_core/renderer_vulkan/vk_update_descriptor.cpp index 8826da325..f99273c6a 100644 --- a/src/video_core/renderer_vulkan/vk_update_descriptor.cpp +++ b/src/video_core/renderer_vulkan/vk_update_descriptor.cpp | |||
| @@ -7,14 +7,14 @@ | |||
| 7 | 7 | ||
| 8 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 9 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 10 | #include "video_core/renderer_vulkan/vk_device.h" | ||
| 11 | #include "video_core/renderer_vulkan/vk_scheduler.h" | 10 | #include "video_core/renderer_vulkan/vk_scheduler.h" |
| 12 | #include "video_core/renderer_vulkan/vk_update_descriptor.h" | 11 | #include "video_core/renderer_vulkan/vk_update_descriptor.h" |
| 13 | #include "video_core/renderer_vulkan/wrapper.h" | 12 | #include "video_core/vulkan_common/vulkan_device.h" |
| 13 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 14 | 14 | ||
| 15 | namespace Vulkan { | 15 | namespace Vulkan { |
| 16 | 16 | ||
| 17 | VKUpdateDescriptorQueue::VKUpdateDescriptorQueue(const VKDevice& device_, VKScheduler& scheduler_) | 17 | VKUpdateDescriptorQueue::VKUpdateDescriptorQueue(const Device& device_, VKScheduler& scheduler_) |
| 18 | : device{device_}, scheduler{scheduler_} {} | 18 | : device{device_}, scheduler{scheduler_} {} |
| 19 | 19 | ||
| 20 | VKUpdateDescriptorQueue::~VKUpdateDescriptorQueue() = default; | 20 | VKUpdateDescriptorQueue::~VKUpdateDescriptorQueue() = default; |
diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.h b/src/video_core/renderer_vulkan/vk_update_descriptor.h index f098a8540..e214f7195 100644 --- a/src/video_core/renderer_vulkan/vk_update_descriptor.h +++ b/src/video_core/renderer_vulkan/vk_update_descriptor.h | |||
| @@ -8,11 +8,11 @@ | |||
| 8 | #include <boost/container/static_vector.hpp> | 8 | #include <boost/container/static_vector.hpp> |
| 9 | 9 | ||
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "video_core/renderer_vulkan/wrapper.h" | 11 | #include "video_core/vulkan_common/vulkan_wrapper.h" |
| 12 | 12 | ||
| 13 | namespace Vulkan { | 13 | namespace Vulkan { |
| 14 | 14 | ||
| 15 | class VKDevice; | 15 | class Device; |
| 16 | class VKScheduler; | 16 | class VKScheduler; |
| 17 | 17 | ||
| 18 | struct DescriptorUpdateEntry { | 18 | struct DescriptorUpdateEntry { |
| @@ -31,7 +31,7 @@ struct DescriptorUpdateEntry { | |||
| 31 | 31 | ||
| 32 | class VKUpdateDescriptorQueue final { | 32 | class VKUpdateDescriptorQueue final { |
| 33 | public: | 33 | public: |
| 34 | explicit VKUpdateDescriptorQueue(const VKDevice& device_, VKScheduler& scheduler_); | 34 | explicit VKUpdateDescriptorQueue(const Device& device_, VKScheduler& scheduler_); |
| 35 | ~VKUpdateDescriptorQueue(); | 35 | ~VKUpdateDescriptorQueue(); |
| 36 | 36 | ||
| 37 | void TickFrame(); | 37 | void TickFrame(); |
| @@ -69,7 +69,7 @@ public: | |||
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | private: | 71 | private: |
| 72 | const VKDevice& device; | 72 | const Device& device; |
| 73 | VKScheduler& scheduler; | 73 | VKScheduler& scheduler; |
| 74 | 74 | ||
| 75 | const DescriptorUpdateEntry* upload_start = nullptr; | 75 | const DescriptorUpdateEntry* upload_start = nullptr; |
diff --git a/src/video_core/shader/async_shaders.cpp b/src/video_core/shader/async_shaders.cpp index 09f93463b..9707136e9 100644 --- a/src/video_core/shader/async_shaders.cpp +++ b/src/video_core/shader/async_shaders.cpp | |||
| @@ -134,7 +134,7 @@ void AsyncShaders::QueueOpenGLShader(const OpenGL::Device& device, | |||
| 134 | } | 134 | } |
| 135 | 135 | ||
| 136 | void AsyncShaders::QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache, | 136 | void AsyncShaders::QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache, |
| 137 | const Vulkan::VKDevice& device, Vulkan::VKScheduler& scheduler, | 137 | const Vulkan::Device& device, Vulkan::VKScheduler& scheduler, |
| 138 | Vulkan::VKDescriptorPool& descriptor_pool, | 138 | Vulkan::VKDescriptorPool& descriptor_pool, |
| 139 | Vulkan::VKUpdateDescriptorQueue& update_descriptor_queue, | 139 | Vulkan::VKUpdateDescriptorQueue& update_descriptor_queue, |
| 140 | std::vector<VkDescriptorSetLayoutBinding> bindings, | 140 | std::vector<VkDescriptorSetLayoutBinding> bindings, |
diff --git a/src/video_core/shader/async_shaders.h b/src/video_core/shader/async_shaders.h index 004e214a8..0dbb1a31f 100644 --- a/src/video_core/shader/async_shaders.h +++ b/src/video_core/shader/async_shaders.h | |||
| @@ -24,9 +24,9 @@ | |||
| 24 | #include "video_core/renderer_opengl/gl_device.h" | 24 | #include "video_core/renderer_opengl/gl_device.h" |
| 25 | #include "video_core/renderer_opengl/gl_resource_manager.h" | 25 | #include "video_core/renderer_opengl/gl_resource_manager.h" |
| 26 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" | 26 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" |
| 27 | #include "video_core/renderer_vulkan/vk_device.h" | ||
| 28 | #include "video_core/renderer_vulkan/vk_pipeline_cache.h" | 27 | #include "video_core/renderer_vulkan/vk_pipeline_cache.h" |
| 29 | #include "video_core/renderer_vulkan/vk_scheduler.h" | 28 | #include "video_core/renderer_vulkan/vk_scheduler.h" |
| 29 | #include "video_core/vulkan_common/vulkan_device.h" | ||
| 30 | 30 | ||
| 31 | namespace Core::Frontend { | 31 | namespace Core::Frontend { |
| 32 | class EmuWindow; | 32 | class EmuWindow; |
| @@ -94,7 +94,7 @@ public: | |||
| 94 | CompilerSettings compiler_settings, const Registry& registry, | 94 | CompilerSettings compiler_settings, const Registry& registry, |
| 95 | VAddr cpu_addr); | 95 | VAddr cpu_addr); |
| 96 | 96 | ||
| 97 | void QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache, const Vulkan::VKDevice& device, | 97 | void QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache, const Vulkan::Device& device, |
| 98 | Vulkan::VKScheduler& scheduler, | 98 | Vulkan::VKScheduler& scheduler, |
| 99 | Vulkan::VKDescriptorPool& descriptor_pool, | 99 | Vulkan::VKDescriptorPool& descriptor_pool, |
| 100 | Vulkan::VKUpdateDescriptorQueue& update_descriptor_queue, | 100 | Vulkan::VKUpdateDescriptorQueue& update_descriptor_queue, |
| @@ -123,7 +123,7 @@ private: | |||
| 123 | 123 | ||
| 124 | // For Vulkan | 124 | // For Vulkan |
| 125 | Vulkan::VKPipelineCache* pp_cache; | 125 | Vulkan::VKPipelineCache* pp_cache; |
| 126 | const Vulkan::VKDevice* vk_device; | 126 | const Vulkan::Device* vk_device; |
| 127 | Vulkan::VKScheduler* scheduler; | 127 | Vulkan::VKScheduler* scheduler; |
| 128 | Vulkan::VKDescriptorPool* descriptor_pool; | 128 | Vulkan::VKDescriptorPool* descriptor_pool; |
| 129 | Vulkan::VKUpdateDescriptorQueue* update_descriptor_queue; | 129 | Vulkan::VKUpdateDescriptorQueue* update_descriptor_queue; |
diff --git a/src/video_core/texture_cache/accelerated_swizzle.cpp b/src/video_core/texture_cache/accelerated_swizzle.cpp index a4fc1184b..15585caeb 100644 --- a/src/video_core/texture_cache/accelerated_swizzle.cpp +++ b/src/video_core/texture_cache/accelerated_swizzle.cpp | |||
| @@ -27,7 +27,7 @@ BlockLinearSwizzle2DParams MakeBlockLinearSwizzle2DParams(const SwizzleParameter | |||
| 27 | const Extent3D num_tiles = swizzle.num_tiles; | 27 | const Extent3D num_tiles = swizzle.num_tiles; |
| 28 | const u32 bytes_per_block = BytesPerBlock(info.format); | 28 | const u32 bytes_per_block = BytesPerBlock(info.format); |
| 29 | const u32 stride_alignment = CalculateLevelStrideAlignment(info, swizzle.level); | 29 | const u32 stride_alignment = CalculateLevelStrideAlignment(info, swizzle.level); |
| 30 | const u32 stride = Common::AlignBits(num_tiles.width, stride_alignment) * bytes_per_block; | 30 | const u32 stride = Common::AlignUpLog2(num_tiles.width, stride_alignment) * bytes_per_block; |
| 31 | const u32 gobs_in_x = Common::DivCeilLog2(stride, GOB_SIZE_X_SHIFT); | 31 | const u32 gobs_in_x = Common::DivCeilLog2(stride, GOB_SIZE_X_SHIFT); |
| 32 | return BlockLinearSwizzle2DParams{ | 32 | return BlockLinearSwizzle2DParams{ |
| 33 | .origin{0, 0, 0}, | 33 | .origin{0, 0, 0}, |
| @@ -47,7 +47,7 @@ BlockLinearSwizzle3DParams MakeBlockLinearSwizzle3DParams(const SwizzleParameter | |||
| 47 | const Extent3D num_tiles = swizzle.num_tiles; | 47 | const Extent3D num_tiles = swizzle.num_tiles; |
| 48 | const u32 bytes_per_block = BytesPerBlock(info.format); | 48 | const u32 bytes_per_block = BytesPerBlock(info.format); |
| 49 | const u32 stride_alignment = CalculateLevelStrideAlignment(info, swizzle.level); | 49 | const u32 stride_alignment = CalculateLevelStrideAlignment(info, swizzle.level); |
| 50 | const u32 stride = Common::AlignBits(num_tiles.width, stride_alignment) * bytes_per_block; | 50 | const u32 stride = Common::AlignUpLog2(num_tiles.width, stride_alignment) * bytes_per_block; |
| 51 | 51 | ||
| 52 | const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) >> GOB_SIZE_X_SHIFT; | 52 | const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) >> GOB_SIZE_X_SHIFT; |
| 53 | const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block.height + block.depth); | 53 | const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block.height + block.depth); |
diff --git a/src/video_core/texture_cache/image_base.cpp b/src/video_core/texture_cache/image_base.cpp index 448a05fcc..959b3f115 100644 --- a/src/video_core/texture_cache/image_base.cpp +++ b/src/video_core/texture_cache/image_base.cpp | |||
| @@ -120,7 +120,9 @@ void AddImageAlias(ImageBase& lhs, ImageBase& rhs, ImageId lhs_id, ImageId rhs_i | |||
| 120 | if (lhs.info.type == ImageType::Linear) { | 120 | if (lhs.info.type == ImageType::Linear) { |
| 121 | base = SubresourceBase{.level = 0, .layer = 0}; | 121 | base = SubresourceBase{.level = 0, .layer = 0}; |
| 122 | } else { | 122 | } else { |
| 123 | base = FindSubresource(rhs.info, lhs, rhs.gpu_addr, OPTIONS); | 123 | // We are passing relaxed formats as an option, having broken views or not won't matter |
| 124 | static constexpr bool broken_views = false; | ||
| 125 | base = FindSubresource(rhs.info, lhs, rhs.gpu_addr, OPTIONS, broken_views); | ||
| 124 | } | 126 | } |
| 125 | if (!base) { | 127 | if (!base) { |
| 126 | LOG_ERROR(HW_GPU, "Image alias should have been flipped"); | 128 | LOG_ERROR(HW_GPU, "Image alias should have been flipped"); |
diff --git a/src/video_core/texture_cache/image_view_base.cpp b/src/video_core/texture_cache/image_view_base.cpp index 076a4bcfd..18f72e508 100644 --- a/src/video_core/texture_cache/image_view_base.cpp +++ b/src/video_core/texture_cache/image_view_base.cpp | |||
| @@ -24,7 +24,7 @@ ImageViewBase::ImageViewBase(const ImageViewInfo& info, const ImageInfo& image_i | |||
| 24 | .height = std::max(image_info.size.height >> range.base.level, 1u), | 24 | .height = std::max(image_info.size.height >> range.base.level, 1u), |
| 25 | .depth = std::max(image_info.size.depth >> range.base.level, 1u), | 25 | .depth = std::max(image_info.size.depth >> range.base.level, 1u), |
| 26 | } { | 26 | } { |
| 27 | ASSERT_MSG(VideoCore::Surface::IsViewCompatible(image_info.format, info.format), | 27 | ASSERT_MSG(VideoCore::Surface::IsViewCompatible(image_info.format, info.format, false), |
| 28 | "Image view format {} is incompatible with image format {}", info.format, | 28 | "Image view format {} is incompatible with image format {}", info.format, |
| 29 | image_info.format); | 29 | image_info.format); |
| 30 | const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue(); | 30 | const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue(); |
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 968059842..d1080300f 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h | |||
| @@ -61,7 +61,7 @@ using VideoCore::Surface::SurfaceType; | |||
| 61 | template <class P> | 61 | template <class P> |
| 62 | class TextureCache { | 62 | class TextureCache { |
| 63 | /// Address shift for caching images into a hash table | 63 | /// Address shift for caching images into a hash table |
| 64 | static constexpr u64 PAGE_SHIFT = 20; | 64 | static constexpr u64 PAGE_BITS = 20; |
| 65 | 65 | ||
| 66 | /// Enables debugging features to the texture cache | 66 | /// Enables debugging features to the texture cache |
| 67 | static constexpr bool ENABLE_VALIDATION = P::ENABLE_VALIDATION; | 67 | static constexpr bool ENABLE_VALIDATION = P::ENABLE_VALIDATION; |
| @@ -184,8 +184,8 @@ private: | |||
| 184 | template <typename Func> | 184 | template <typename Func> |
| 185 | static void ForEachPage(VAddr addr, size_t size, Func&& func) { | 185 | static void ForEachPage(VAddr addr, size_t size, Func&& func) { |
| 186 | static constexpr bool RETURNS_BOOL = std::is_same_v<std::invoke_result<Func, u64>, bool>; | 186 | static constexpr bool RETURNS_BOOL = std::is_same_v<std::invoke_result<Func, u64>, bool>; |
| 187 | const u64 page_end = (addr + size - 1) >> PAGE_SHIFT; | 187 | const u64 page_end = (addr + size - 1) >> PAGE_BITS; |
| 188 | for (u64 page = addr >> PAGE_SHIFT; page <= page_end; ++page) { | 188 | for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) { |
| 189 | if constexpr (RETURNS_BOOL) { | 189 | if constexpr (RETURNS_BOOL) { |
| 190 | if (func(page)) { | 190 | if (func(page)) { |
| 191 | break; | 191 | break; |
| @@ -708,7 +708,7 @@ void TextureCache<P>::InvalidateDepthBuffer() { | |||
| 708 | template <class P> | 708 | template <class P> |
| 709 | typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView(VAddr cpu_addr) { | 709 | typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView(VAddr cpu_addr) { |
| 710 | // TODO: Properly implement this | 710 | // TODO: Properly implement this |
| 711 | const auto it = page_table.find(cpu_addr >> PAGE_SHIFT); | 711 | const auto it = page_table.find(cpu_addr >> PAGE_BITS); |
| 712 | if (it == page_table.end()) { | 712 | if (it == page_table.end()) { |
| 713 | return nullptr; | 713 | return nullptr; |
| 714 | } | 714 | } |
| @@ -883,6 +883,7 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr, | |||
| 883 | if (!cpu_addr) { | 883 | if (!cpu_addr) { |
| 884 | return ImageId{}; | 884 | return ImageId{}; |
| 885 | } | 885 | } |
| 886 | const bool broken_views = runtime.HasBrokenTextureViewFormats(); | ||
| 886 | ImageId image_id; | 887 | ImageId image_id; |
| 887 | const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) { | 888 | const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) { |
| 888 | if (info.type == ImageType::Linear || existing_image.info.type == ImageType::Linear) { | 889 | if (info.type == ImageType::Linear || existing_image.info.type == ImageType::Linear) { |
| @@ -892,11 +893,11 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr, | |||
| 892 | if (existing_image.gpu_addr == gpu_addr && existing.type == info.type && | 893 | if (existing_image.gpu_addr == gpu_addr && existing.type == info.type && |
| 893 | existing.pitch == info.pitch && | 894 | existing.pitch == info.pitch && |
| 894 | IsPitchLinearSameSize(existing, info, strict_size) && | 895 | IsPitchLinearSameSize(existing, info, strict_size) && |
| 895 | IsViewCompatible(existing.format, info.format)) { | 896 | IsViewCompatible(existing.format, info.format, broken_views)) { |
| 896 | image_id = existing_image_id; | 897 | image_id = existing_image_id; |
| 897 | return true; | 898 | return true; |
| 898 | } | 899 | } |
| 899 | } else if (IsSubresource(info, existing_image, gpu_addr, options)) { | 900 | } else if (IsSubresource(info, existing_image, gpu_addr, options, broken_views)) { |
| 900 | image_id = existing_image_id; | 901 | image_id = existing_image_id; |
| 901 | return true; | 902 | return true; |
| 902 | } | 903 | } |
| @@ -926,6 +927,7 @@ template <class P> | |||
| 926 | ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VAddr cpu_addr) { | 927 | ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VAddr cpu_addr) { |
| 927 | ImageInfo new_info = info; | 928 | ImageInfo new_info = info; |
| 928 | const size_t size_bytes = CalculateGuestSizeInBytes(new_info); | 929 | const size_t size_bytes = CalculateGuestSizeInBytes(new_info); |
| 930 | const bool broken_views = runtime.HasBrokenTextureViewFormats(); | ||
| 929 | std::vector<ImageId> overlap_ids; | 931 | std::vector<ImageId> overlap_ids; |
| 930 | std::vector<ImageId> left_aliased_ids; | 932 | std::vector<ImageId> left_aliased_ids; |
| 931 | std::vector<ImageId> right_aliased_ids; | 933 | std::vector<ImageId> right_aliased_ids; |
| @@ -940,7 +942,9 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA | |||
| 940 | } | 942 | } |
| 941 | return; | 943 | return; |
| 942 | } | 944 | } |
| 943 | const auto solution = ResolveOverlap(new_info, gpu_addr, cpu_addr, overlap, true); | 945 | static constexpr bool strict_size = true; |
| 946 | const std::optional<OverlapResult> solution = | ||
| 947 | ResolveOverlap(new_info, gpu_addr, cpu_addr, overlap, strict_size, broken_views); | ||
| 944 | if (solution) { | 948 | if (solution) { |
| 945 | gpu_addr = solution->gpu_addr; | 949 | gpu_addr = solution->gpu_addr; |
| 946 | cpu_addr = solution->cpu_addr; | 950 | cpu_addr = solution->cpu_addr; |
| @@ -950,9 +954,10 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA | |||
| 950 | } | 954 | } |
| 951 | static constexpr auto options = RelaxedOptions::Size | RelaxedOptions::Format; | 955 | static constexpr auto options = RelaxedOptions::Size | RelaxedOptions::Format; |
| 952 | const ImageBase new_image_base(new_info, gpu_addr, cpu_addr); | 956 | const ImageBase new_image_base(new_info, gpu_addr, cpu_addr); |
| 953 | if (IsSubresource(new_info, overlap, gpu_addr, options)) { | 957 | if (IsSubresource(new_info, overlap, gpu_addr, options, broken_views)) { |
| 954 | left_aliased_ids.push_back(overlap_id); | 958 | left_aliased_ids.push_back(overlap_id); |
| 955 | } else if (IsSubresource(overlap.info, new_image_base, overlap.gpu_addr, options)) { | 959 | } else if (IsSubresource(overlap.info, new_image_base, overlap.gpu_addr, options, |
| 960 | broken_views)) { | ||
| 956 | right_aliased_ids.push_back(overlap_id); | 961 | right_aliased_ids.push_back(overlap_id); |
| 957 | } | 962 | } |
| 958 | }); | 963 | }); |
| @@ -1165,13 +1170,13 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) { | |||
| 1165 | ForEachPage(image.cpu_addr, image.guest_size_bytes, [this, image_id](u64 page) { | 1170 | ForEachPage(image.cpu_addr, image.guest_size_bytes, [this, image_id](u64 page) { |
| 1166 | const auto page_it = page_table.find(page); | 1171 | const auto page_it = page_table.find(page); |
| 1167 | if (page_it == page_table.end()) { | 1172 | if (page_it == page_table.end()) { |
| 1168 | UNREACHABLE_MSG("Unregistering unregistered page=0x{:x}", page << PAGE_SHIFT); | 1173 | UNREACHABLE_MSG("Unregistering unregistered page=0x{:x}", page << PAGE_BITS); |
| 1169 | return; | 1174 | return; |
| 1170 | } | 1175 | } |
| 1171 | std::vector<ImageId>& image_ids = page_it->second; | 1176 | std::vector<ImageId>& image_ids = page_it->second; |
| 1172 | const auto vector_it = std::ranges::find(image_ids, image_id); | 1177 | const auto vector_it = std::ranges::find(image_ids, image_id); |
| 1173 | if (vector_it == image_ids.end()) { | 1178 | if (vector_it == image_ids.end()) { |
| 1174 | UNREACHABLE_MSG("Unregistering unregistered image in page=0x{:x}", page << PAGE_SHIFT); | 1179 | UNREACHABLE_MSG("Unregistering unregistered image in page=0x{:x}", page << PAGE_BITS); |
| 1175 | return; | 1180 | return; |
| 1176 | } | 1181 | } |
| 1177 | image_ids.erase(vector_it); | 1182 | image_ids.erase(vector_it); |
diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp index 9ed1fc007..ce8fcfe0a 100644 --- a/src/video_core/texture_cache/util.cpp +++ b/src/video_core/texture_cache/util.cpp | |||
| @@ -279,7 +279,7 @@ template <u32 GOB_EXTENT> | |||
| 279 | const bool is_small = IsSmallerThanGobSize(blocks, gob, info.block.depth); | 279 | const bool is_small = IsSmallerThanGobSize(blocks, gob, info.block.depth); |
| 280 | const u32 alignment = is_small ? 0 : info.tile_width_spacing; | 280 | const u32 alignment = is_small ? 0 : info.tile_width_spacing; |
| 281 | return Extent2D{ | 281 | return Extent2D{ |
| 282 | .width = Common::AlignBits(gobs.width, alignment), | 282 | .width = Common::AlignUpLog2(gobs.width, alignment), |
| 283 | .height = gobs.height, | 283 | .height = gobs.height, |
| 284 | }; | 284 | }; |
| 285 | } | 285 | } |
| @@ -352,7 +352,7 @@ template <u32 GOB_EXTENT> | |||
| 352 | // https://github.com/Ryujinx/Ryujinx/blob/1c9aba6de1520aea5480c032e0ff5664ac1bb36f/Ryujinx.Graphics.Texture/SizeCalculator.cs#L134 | 352 | // https://github.com/Ryujinx/Ryujinx/blob/1c9aba6de1520aea5480c032e0ff5664ac1bb36f/Ryujinx.Graphics.Texture/SizeCalculator.cs#L134 |
| 353 | if (tile_width_spacing > 0) { | 353 | if (tile_width_spacing > 0) { |
| 354 | const u32 alignment_log2 = GOB_SIZE_SHIFT + tile_width_spacing + block.height + block.depth; | 354 | const u32 alignment_log2 = GOB_SIZE_SHIFT + tile_width_spacing + block.height + block.depth; |
| 355 | return Common::AlignBits(size_bytes, alignment_log2); | 355 | return Common::AlignUpLog2(size_bytes, alignment_log2); |
| 356 | } | 356 | } |
| 357 | const u32 aligned_height = Common::AlignUp(size.height, tile_size_y); | 357 | const u32 aligned_height = Common::AlignUp(size.height, tile_size_y); |
| 358 | while (block.height != 0 && aligned_height <= (1U << (block.height - 1)) * GOB_SIZE_Y) { | 358 | while (block.height != 0 && aligned_height <= (1U << (block.height - 1)) * GOB_SIZE_Y) { |
| @@ -528,9 +528,9 @@ template <u32 GOB_EXTENT> | |||
| 528 | const u32 alignment = StrideAlignment(num_tiles, info.block, bpp_log2, info.tile_width_spacing); | 528 | const u32 alignment = StrideAlignment(num_tiles, info.block, bpp_log2, info.tile_width_spacing); |
| 529 | const Extent3D mip_block = AdjustMipBlockSize(num_tiles, info.block, 0); | 529 | const Extent3D mip_block = AdjustMipBlockSize(num_tiles, info.block, 0); |
| 530 | return Extent3D{ | 530 | return Extent3D{ |
| 531 | .width = Common::AlignBits(num_tiles.width, alignment), | 531 | .width = Common::AlignUpLog2(num_tiles.width, alignment), |
| 532 | .height = Common::AlignBits(num_tiles.height, GOB_SIZE_Y_SHIFT + mip_block.height), | 532 | .height = Common::AlignUpLog2(num_tiles.height, GOB_SIZE_Y_SHIFT + mip_block.height), |
| 533 | .depth = Common::AlignBits(num_tiles.depth, GOB_SIZE_Z_SHIFT + mip_block.depth), | 533 | .depth = Common::AlignUpLog2(num_tiles.depth, GOB_SIZE_Z_SHIFT + mip_block.depth), |
| 534 | }; | 534 | }; |
| 535 | } | 535 | } |
| 536 | 536 | ||
| @@ -1069,13 +1069,13 @@ bool IsPitchLinearSameSize(const ImageInfo& lhs, const ImageInfo& rhs, bool stri | |||
| 1069 | 1069 | ||
| 1070 | std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info, GPUVAddr gpu_addr, | 1070 | std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info, GPUVAddr gpu_addr, |
| 1071 | VAddr cpu_addr, const ImageBase& overlap, | 1071 | VAddr cpu_addr, const ImageBase& overlap, |
| 1072 | bool strict_size) { | 1072 | bool strict_size, bool broken_views) { |
| 1073 | ASSERT(new_info.type != ImageType::Linear); | 1073 | ASSERT(new_info.type != ImageType::Linear); |
| 1074 | ASSERT(overlap.info.type != ImageType::Linear); | 1074 | ASSERT(overlap.info.type != ImageType::Linear); |
| 1075 | if (!IsLayerStrideCompatible(new_info, overlap.info)) { | 1075 | if (!IsLayerStrideCompatible(new_info, overlap.info)) { |
| 1076 | return std::nullopt; | 1076 | return std::nullopt; |
| 1077 | } | 1077 | } |
| 1078 | if (!IsViewCompatible(overlap.info.format, new_info.format)) { | 1078 | if (!IsViewCompatible(overlap.info.format, new_info.format, broken_views)) { |
| 1079 | return std::nullopt; | 1079 | return std::nullopt; |
| 1080 | } | 1080 | } |
| 1081 | if (gpu_addr == overlap.gpu_addr) { | 1081 | if (gpu_addr == overlap.gpu_addr) { |
| @@ -1118,14 +1118,15 @@ bool IsLayerStrideCompatible(const ImageInfo& lhs, const ImageInfo& rhs) { | |||
| 1118 | } | 1118 | } |
| 1119 | 1119 | ||
| 1120 | std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const ImageBase& image, | 1120 | std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const ImageBase& image, |
| 1121 | GPUVAddr candidate_addr, RelaxedOptions options) { | 1121 | GPUVAddr candidate_addr, RelaxedOptions options, |
| 1122 | bool broken_views) { | ||
| 1122 | const std::optional<SubresourceBase> base = image.TryFindBase(candidate_addr); | 1123 | const std::optional<SubresourceBase> base = image.TryFindBase(candidate_addr); |
| 1123 | if (!base) { | 1124 | if (!base) { |
| 1124 | return std::nullopt; | 1125 | return std::nullopt; |
| 1125 | } | 1126 | } |
| 1126 | const ImageInfo& existing = image.info; | 1127 | const ImageInfo& existing = image.info; |
| 1127 | if (False(options & RelaxedOptions::Format)) { | 1128 | if (False(options & RelaxedOptions::Format)) { |
| 1128 | if (!IsViewCompatible(existing.format, candidate.format)) { | 1129 | if (!IsViewCompatible(existing.format, candidate.format, broken_views)) { |
| 1129 | return std::nullopt; | 1130 | return std::nullopt; |
| 1130 | } | 1131 | } |
| 1131 | } | 1132 | } |
| @@ -1162,8 +1163,8 @@ std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const | |||
| 1162 | } | 1163 | } |
| 1163 | 1164 | ||
| 1164 | bool IsSubresource(const ImageInfo& candidate, const ImageBase& image, GPUVAddr candidate_addr, | 1165 | bool IsSubresource(const ImageInfo& candidate, const ImageBase& image, GPUVAddr candidate_addr, |
| 1165 | RelaxedOptions options) { | 1166 | RelaxedOptions options, bool broken_views) { |
| 1166 | return FindSubresource(candidate, image, candidate_addr, options).has_value(); | 1167 | return FindSubresource(candidate, image, candidate_addr, options, broken_views).has_value(); |
| 1167 | } | 1168 | } |
| 1168 | 1169 | ||
| 1169 | void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst, | 1170 | void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst, |
diff --git a/src/video_core/texture_cache/util.h b/src/video_core/texture_cache/util.h index dbbbd33cd..52a9207d6 100644 --- a/src/video_core/texture_cache/util.h +++ b/src/video_core/texture_cache/util.h | |||
| @@ -87,17 +87,19 @@ void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const Ima | |||
| 87 | [[nodiscard]] std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info, | 87 | [[nodiscard]] std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info, |
| 88 | GPUVAddr gpu_addr, VAddr cpu_addr, | 88 | GPUVAddr gpu_addr, VAddr cpu_addr, |
| 89 | const ImageBase& overlap, | 89 | const ImageBase& overlap, |
| 90 | bool strict_size); | 90 | bool strict_size, bool broken_views); |
| 91 | 91 | ||
| 92 | [[nodiscard]] bool IsLayerStrideCompatible(const ImageInfo& lhs, const ImageInfo& rhs); | 92 | [[nodiscard]] bool IsLayerStrideCompatible(const ImageInfo& lhs, const ImageInfo& rhs); |
| 93 | 93 | ||
| 94 | [[nodiscard]] std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, | 94 | [[nodiscard]] std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, |
| 95 | const ImageBase& image, | 95 | const ImageBase& image, |
| 96 | GPUVAddr candidate_addr, | 96 | GPUVAddr candidate_addr, |
| 97 | RelaxedOptions options); | 97 | RelaxedOptions options, |
| 98 | bool broken_views); | ||
| 98 | 99 | ||
| 99 | [[nodiscard]] bool IsSubresource(const ImageInfo& candidate, const ImageBase& image, | 100 | [[nodiscard]] bool IsSubresource(const ImageInfo& candidate, const ImageBase& image, |
| 100 | GPUVAddr candidate_addr, RelaxedOptions options); | 101 | GPUVAddr candidate_addr, RelaxedOptions options, |
| 102 | bool broken_views); | ||
| 101 | 103 | ||
| 102 | void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst, | 104 | void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst, |
| 103 | const ImageBase* src); | 105 | const ImageBase* src); |
diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp index acd5bdd78..3625b666c 100644 --- a/src/video_core/textures/astc.cpp +++ b/src/video_core/textures/astc.cpp | |||
| @@ -42,21 +42,24 @@ constexpr u32 Popcnt(u32 n) { | |||
| 42 | 42 | ||
| 43 | class InputBitStream { | 43 | class InputBitStream { |
| 44 | public: | 44 | public: |
| 45 | constexpr explicit InputBitStream(const u8* ptr, std::size_t start_offset = 0) | 45 | constexpr explicit InputBitStream(std::span<const u8> data, size_t start_offset = 0) |
| 46 | : cur_byte{ptr}, next_bit{start_offset % 8} {} | 46 | : cur_byte{data.data()}, total_bits{data.size()}, next_bit{start_offset % 8} {} |
| 47 | 47 | ||
| 48 | constexpr std::size_t GetBitsRead() const { | 48 | constexpr size_t GetBitsRead() const { |
| 49 | return bits_read; | 49 | return bits_read; |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | constexpr bool ReadBit() { | 52 | constexpr bool ReadBit() { |
| 53 | const bool bit = (*cur_byte >> next_bit++) & 1; | 53 | if (bits_read >= total_bits * 8) { |
| 54 | return 0; | ||
| 55 | } | ||
| 56 | const bool bit = ((*cur_byte >> next_bit) & 1) != 0; | ||
| 57 | ++next_bit; | ||
| 54 | while (next_bit >= 8) { | 58 | while (next_bit >= 8) { |
| 55 | next_bit -= 8; | 59 | next_bit -= 8; |
| 56 | cur_byte++; | 60 | ++cur_byte; |
| 57 | } | 61 | } |
| 58 | 62 | ++bits_read; | |
| 59 | bits_read++; | ||
| 60 | return bit; | 63 | return bit; |
| 61 | } | 64 | } |
| 62 | 65 | ||
| @@ -79,8 +82,9 @@ public: | |||
| 79 | 82 | ||
| 80 | private: | 83 | private: |
| 81 | const u8* cur_byte; | 84 | const u8* cur_byte; |
| 82 | std::size_t next_bit = 0; | 85 | size_t total_bits = 0; |
| 83 | std::size_t bits_read = 0; | 86 | size_t next_bit = 0; |
| 87 | size_t bits_read = 0; | ||
| 84 | }; | 88 | }; |
| 85 | 89 | ||
| 86 | class OutputBitStream { | 90 | class OutputBitStream { |
| @@ -193,15 +197,15 @@ struct IntegerEncodedValue { | |||
| 193 | }; | 197 | }; |
| 194 | }; | 198 | }; |
| 195 | using IntegerEncodedVector = boost::container::static_vector< | 199 | using IntegerEncodedVector = boost::container::static_vector< |
| 196 | IntegerEncodedValue, 64, | 200 | IntegerEncodedValue, 256, |
| 197 | boost::container::static_vector_options< | 201 | boost::container::static_vector_options< |
| 198 | boost::container::inplace_alignment<alignof(IntegerEncodedValue)>, | 202 | boost::container::inplace_alignment<alignof(IntegerEncodedValue)>, |
| 199 | boost::container::throw_on_overflow<false>>::type>; | 203 | boost::container::throw_on_overflow<false>>::type>; |
| 200 | 204 | ||
| 201 | static void DecodeTritBlock(InputBitStream& bits, IntegerEncodedVector& result, u32 nBitsPerValue) { | 205 | static void DecodeTritBlock(InputBitStream& bits, IntegerEncodedVector& result, u32 nBitsPerValue) { |
| 202 | // Implement the algorithm in section C.2.12 | 206 | // Implement the algorithm in section C.2.12 |
| 203 | u32 m[5]; | 207 | std::array<u32, 5> m; |
| 204 | u32 t[5]; | 208 | std::array<u32, 5> t; |
| 205 | u32 T; | 209 | u32 T; |
| 206 | 210 | ||
| 207 | // Read the trit encoded block according to | 211 | // Read the trit encoded block according to |
| @@ -866,7 +870,7 @@ public: | |||
| 866 | } | 870 | } |
| 867 | }; | 871 | }; |
| 868 | 872 | ||
| 869 | static void DecodeColorValues(u32* out, u8* data, const u32* modes, const u32 nPartitions, | 873 | static void DecodeColorValues(u32* out, std::span<u8> data, const u32* modes, const u32 nPartitions, |
| 870 | const u32 nBitsForColorData) { | 874 | const u32 nBitsForColorData) { |
| 871 | // First figure out how many color values we have | 875 | // First figure out how many color values we have |
| 872 | u32 nValues = 0; | 876 | u32 nValues = 0; |
| @@ -898,7 +902,7 @@ static void DecodeColorValues(u32* out, u8* data, const u32* modes, const u32 nP | |||
| 898 | // We now have enough to decode our integer sequence. | 902 | // We now have enough to decode our integer sequence. |
| 899 | IntegerEncodedVector decodedColorValues; | 903 | IntegerEncodedVector decodedColorValues; |
| 900 | 904 | ||
| 901 | InputBitStream colorStream(data); | 905 | InputBitStream colorStream(data, 0); |
| 902 | DecodeIntegerSequence(decodedColorValues, colorStream, range, nValues); | 906 | DecodeIntegerSequence(decodedColorValues, colorStream, range, nValues); |
| 903 | 907 | ||
| 904 | // Once we have the decoded values, we need to dequantize them to the 0-255 range | 908 | // Once we have the decoded values, we need to dequantize them to the 0-255 range |
| @@ -1441,7 +1445,7 @@ static void ComputeEndpos32s(Pixel& ep1, Pixel& ep2, const u32*& colorValues, | |||
| 1441 | 1445 | ||
| 1442 | static void DecompressBlock(std::span<const u8, 16> inBuf, const u32 blockWidth, | 1446 | static void DecompressBlock(std::span<const u8, 16> inBuf, const u32 blockWidth, |
| 1443 | const u32 blockHeight, std::span<u32, 12 * 12> outBuf) { | 1447 | const u32 blockHeight, std::span<u32, 12 * 12> outBuf) { |
| 1444 | InputBitStream strm(inBuf.data()); | 1448 | InputBitStream strm(inBuf); |
| 1445 | TexelWeightParams weightParams = DecodeBlockInfo(strm); | 1449 | TexelWeightParams weightParams = DecodeBlockInfo(strm); |
| 1446 | 1450 | ||
| 1447 | // Was there an error? | 1451 | // Was there an error? |
| @@ -1619,15 +1623,16 @@ static void DecompressBlock(std::span<const u8, 16> inBuf, const u32 blockWidth, | |||
| 1619 | 1623 | ||
| 1620 | // Make sure that higher non-texel bits are set to zero | 1624 | // Make sure that higher non-texel bits are set to zero |
| 1621 | const u32 clearByteStart = (weightParams.GetPackedBitSize() >> 3) + 1; | 1625 | const u32 clearByteStart = (weightParams.GetPackedBitSize() >> 3) + 1; |
| 1622 | if (clearByteStart > 0) { | 1626 | if (clearByteStart > 0 && clearByteStart <= texelWeightData.size()) { |
| 1623 | texelWeightData[clearByteStart - 1] &= | 1627 | texelWeightData[clearByteStart - 1] &= |
| 1624 | static_cast<u8>((1 << (weightParams.GetPackedBitSize() % 8)) - 1); | 1628 | static_cast<u8>((1 << (weightParams.GetPackedBitSize() % 8)) - 1); |
| 1629 | std::memset(texelWeightData.data() + clearByteStart, 0, | ||
| 1630 | std::min(16U - clearByteStart, 16U)); | ||
| 1625 | } | 1631 | } |
| 1626 | std::memset(texelWeightData.data() + clearByteStart, 0, std::min(16U - clearByteStart, 16U)); | ||
| 1627 | 1632 | ||
| 1628 | IntegerEncodedVector texelWeightValues; | 1633 | IntegerEncodedVector texelWeightValues; |
| 1629 | 1634 | ||
| 1630 | InputBitStream weightStream(texelWeightData.data()); | 1635 | InputBitStream weightStream(texelWeightData); |
| 1631 | 1636 | ||
| 1632 | DecodeIntegerSequence(texelWeightValues, weightStream, weightParams.m_MaxWeight, | 1637 | DecodeIntegerSequence(texelWeightValues, weightStream, weightParams.m_MaxWeight, |
| 1633 | weightParams.GetNumWeightValues()); | 1638 | weightParams.GetNumWeightValues()); |
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index 9f5181318..62685a183 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp | |||
| @@ -49,7 +49,7 @@ void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixe | |||
| 49 | // We can configure here a custom pitch | 49 | // We can configure here a custom pitch |
| 50 | // As it's not exposed 'width * bpp' will be the expected pitch. | 50 | // As it's not exposed 'width * bpp' will be the expected pitch. |
| 51 | const u32 pitch = width * bytes_per_pixel; | 51 | const u32 pitch = width * bytes_per_pixel; |
| 52 | const u32 stride = Common::AlignBits(width, stride_alignment) * bytes_per_pixel; | 52 | const u32 stride = Common::AlignUpLog2(width, stride_alignment) * bytes_per_pixel; |
| 53 | 53 | ||
| 54 | const u32 gobs_in_x = Common::DivCeilLog2(stride, GOB_SIZE_X_SHIFT); | 54 | const u32 gobs_in_x = Common::DivCeilLog2(stride, GOB_SIZE_X_SHIFT); |
| 55 | const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth); | 55 | const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth); |
| @@ -217,9 +217,9 @@ void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32 | |||
| 217 | std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, | 217 | std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, |
| 218 | u32 block_height, u32 block_depth) { | 218 | u32 block_height, u32 block_depth) { |
| 219 | if (tiled) { | 219 | if (tiled) { |
| 220 | const u32 aligned_width = Common::AlignBits(width * bytes_per_pixel, GOB_SIZE_X_SHIFT); | 220 | const u32 aligned_width = Common::AlignUpLog2(width * bytes_per_pixel, GOB_SIZE_X_SHIFT); |
| 221 | const u32 aligned_height = Common::AlignBits(height, GOB_SIZE_Y_SHIFT + block_height); | 221 | const u32 aligned_height = Common::AlignUpLog2(height, GOB_SIZE_Y_SHIFT + block_height); |
| 222 | const u32 aligned_depth = Common::AlignBits(depth, GOB_SIZE_Z_SHIFT + block_depth); | 222 | const u32 aligned_depth = Common::AlignUpLog2(depth, GOB_SIZE_Z_SHIFT + block_depth); |
| 223 | return aligned_width * aligned_height * aligned_depth; | 223 | return aligned_width * aligned_height * aligned_depth; |
| 224 | } else { | 224 | } else { |
| 225 | return width * height * depth * bytes_per_pixel; | 225 | return width * height * depth * bytes_per_pixel; |
diff --git a/src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp index 5b01020ec..8d10ac29e 100644 --- a/src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp +++ b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp | |||
| @@ -32,20 +32,11 @@ namespace Vulkan { | |||
| 32 | 32 | ||
| 33 | static constexpr char AFTERMATH_LIB_NAME[] = "GFSDK_Aftermath_Lib.x64.dll"; | 33 | static constexpr char AFTERMATH_LIB_NAME[] = "GFSDK_Aftermath_Lib.x64.dll"; |
| 34 | 34 | ||
| 35 | NsightAftermathTracker::NsightAftermathTracker() = default; | 35 | NsightAftermathTracker::NsightAftermathTracker() { |
| 36 | |||
| 37 | NsightAftermathTracker::~NsightAftermathTracker() { | ||
| 38 | if (initialized) { | ||
| 39 | (void)GFSDK_Aftermath_DisableGpuCrashDumps(); | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | bool NsightAftermathTracker::Initialize() { | ||
| 44 | if (!dl.Open(AFTERMATH_LIB_NAME)) { | 36 | if (!dl.Open(AFTERMATH_LIB_NAME)) { |
| 45 | LOG_ERROR(Render_Vulkan, "Failed to load Nsight Aftermath DLL"); | 37 | LOG_ERROR(Render_Vulkan, "Failed to load Nsight Aftermath DLL"); |
| 46 | return false; | 38 | return; |
| 47 | } | 39 | } |
| 48 | |||
| 49 | if (!dl.GetSymbol("GFSDK_Aftermath_DisableGpuCrashDumps", | 40 | if (!dl.GetSymbol("GFSDK_Aftermath_DisableGpuCrashDumps", |
| 50 | &GFSDK_Aftermath_DisableGpuCrashDumps) || | 41 | &GFSDK_Aftermath_DisableGpuCrashDumps) || |
| 51 | !dl.GetSymbol("GFSDK_Aftermath_EnableGpuCrashDumps", | 42 | !dl.GetSymbol("GFSDK_Aftermath_EnableGpuCrashDumps", |
| @@ -64,27 +55,28 @@ bool NsightAftermathTracker::Initialize() { | |||
| 64 | LOG_ERROR(Render_Vulkan, "Failed to load Nsight Aftermath function pointers"); | 55 | LOG_ERROR(Render_Vulkan, "Failed to load Nsight Aftermath function pointers"); |
| 65 | return false; | 56 | return false; |
| 66 | } | 57 | } |
| 67 | |||
| 68 | dump_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir) + "gpucrash"; | 58 | dump_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir) + "gpucrash"; |
| 69 | 59 | ||
| 70 | (void)Common::FS::DeleteDirRecursively(dump_dir); | 60 | void(Common::FS::DeleteDirRecursively(dump_dir)); |
| 71 | if (!Common::FS::CreateDir(dump_dir)) { | 61 | if (!Common::FS::CreateDir(dump_dir)) { |
| 72 | LOG_ERROR(Render_Vulkan, "Failed to create Nsight Aftermath dump directory"); | 62 | LOG_ERROR(Render_Vulkan, "Failed to create Nsight Aftermath dump directory"); |
| 73 | return false; | 63 | return; |
| 74 | } | 64 | } |
| 75 | |||
| 76 | if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_EnableGpuCrashDumps( | 65 | if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_EnableGpuCrashDumps( |
| 77 | GFSDK_Aftermath_Version_API, GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_Vulkan, | 66 | GFSDK_Aftermath_Version_API, GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_Vulkan, |
| 78 | GFSDK_Aftermath_GpuCrashDumpFeatureFlags_Default, GpuCrashDumpCallback, | 67 | GFSDK_Aftermath_GpuCrashDumpFeatureFlags_Default, GpuCrashDumpCallback, |
| 79 | ShaderDebugInfoCallback, CrashDumpDescriptionCallback, this))) { | 68 | ShaderDebugInfoCallback, CrashDumpDescriptionCallback, this))) { |
| 80 | LOG_ERROR(Render_Vulkan, "GFSDK_Aftermath_EnableGpuCrashDumps failed"); | 69 | LOG_ERROR(Render_Vulkan, "GFSDK_Aftermath_EnableGpuCrashDumps failed"); |
| 81 | return false; | 70 | return; |
| 82 | } | 71 | } |
| 83 | |||
| 84 | LOG_INFO(Render_Vulkan, "Nsight Aftermath dump directory is \"{}\"", dump_dir); | 72 | LOG_INFO(Render_Vulkan, "Nsight Aftermath dump directory is \"{}\"", dump_dir); |
| 85 | |||
| 86 | initialized = true; | 73 | initialized = true; |
| 87 | return true; | 74 | } |
| 75 | |||
| 76 | NsightAftermathTracker::~NsightAftermathTracker() { | ||
| 77 | if (initialized) { | ||
| 78 | (void)GFSDK_Aftermath_DisableGpuCrashDumps(); | ||
| 79 | } | ||
| 88 | } | 80 | } |
| 89 | 81 | ||
| 90 | void NsightAftermathTracker::SaveShader(const std::vector<u32>& spirv) const { | 82 | void NsightAftermathTracker::SaveShader(const std::vector<u32>& spirv) const { |
diff --git a/src/video_core/renderer_vulkan/nsight_aftermath_tracker.h b/src/video_core/vulkan_common/nsight_aftermath_tracker.h index afe7ae99e..cee3847fb 100644 --- a/src/video_core/renderer_vulkan/nsight_aftermath_tracker.h +++ b/src/video_core/vulkan_common/nsight_aftermath_tracker.h | |||
| @@ -34,8 +34,6 @@ public: | |||
| 34 | NsightAftermathTracker(NsightAftermathTracker&&) = delete; | 34 | NsightAftermathTracker(NsightAftermathTracker&&) = delete; |
| 35 | NsightAftermathTracker& operator=(NsightAftermathTracker&&) = delete; | 35 | NsightAftermathTracker& operator=(NsightAftermathTracker&&) = delete; |
| 36 | 36 | ||
| 37 | bool Initialize(); | ||
| 38 | |||
| 39 | void SaveShader(const std::vector<u32>& spirv) const; | 37 | void SaveShader(const std::vector<u32>& spirv) const; |
| 40 | 38 | ||
| 41 | private: | 39 | private: |
| @@ -78,9 +76,6 @@ private: | |||
| 78 | #ifndef HAS_NSIGHT_AFTERMATH | 76 | #ifndef HAS_NSIGHT_AFTERMATH |
| 79 | inline NsightAftermathTracker::NsightAftermathTracker() = default; | 77 | inline NsightAftermathTracker::NsightAftermathTracker() = default; |
| 80 | inline NsightAftermathTracker::~NsightAftermathTracker() = default; | 78 | inline NsightAftermathTracker::~NsightAftermathTracker() = default; |
| 81 | inline bool NsightAftermathTracker::Initialize() { | ||
| 82 | return false; | ||
| 83 | } | ||
| 84 | inline void NsightAftermathTracker::SaveShader(const std::vector<u32>&) const {} | 79 | inline void NsightAftermathTracker::SaveShader(const std::vector<u32>&) const {} |
| 85 | #endif | 80 | #endif |
| 86 | 81 | ||
diff --git a/src/video_core/vulkan_common/vulkan_debug_callback.cpp b/src/video_core/vulkan_common/vulkan_debug_callback.cpp new file mode 100644 index 000000000..ea7af8ad4 --- /dev/null +++ b/src/video_core/vulkan_common/vulkan_debug_callback.cpp | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <string_view> | ||
| 6 | #include "common/logging/log.h" | ||
| 7 | #include "video_core/vulkan_common/vulkan_debug_callback.h" | ||
| 8 | |||
| 9 | namespace Vulkan { | ||
| 10 | namespace { | ||
| 11 | VkBool32 Callback(VkDebugUtilsMessageSeverityFlagBitsEXT severity, | ||
| 12 | VkDebugUtilsMessageTypeFlagsEXT type, | ||
| 13 | const VkDebugUtilsMessengerCallbackDataEXT* data, | ||
| 14 | [[maybe_unused]] void* user_data) { | ||
| 15 | const std::string_view message{data->pMessage}; | ||
| 16 | if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) { | ||
| 17 | LOG_CRITICAL(Render_Vulkan, "{}", message); | ||
| 18 | } else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) { | ||
| 19 | LOG_WARNING(Render_Vulkan, "{}", message); | ||
| 20 | } else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) { | ||
| 21 | LOG_INFO(Render_Vulkan, "{}", message); | ||
| 22 | } else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) { | ||
| 23 | LOG_DEBUG(Render_Vulkan, "{}", message); | ||
| 24 | } | ||
| 25 | return VK_FALSE; | ||
| 26 | } | ||
| 27 | } // Anonymous namespace | ||
| 28 | |||
| 29 | vk::DebugUtilsMessenger CreateDebugCallback(const vk::Instance& instance) { | ||
| 30 | return instance.CreateDebugUtilsMessenger(VkDebugUtilsMessengerCreateInfoEXT{ | ||
| 31 | .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, | ||
| 32 | .pNext = nullptr, | ||
| 33 | .flags = 0, | ||
| 34 | .messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | | ||
| 35 | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | | ||
| 36 | VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | | ||
| 37 | VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT, | ||
| 38 | .messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | | ||
| 39 | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | | ||
| 40 | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT, | ||
| 41 | .pfnUserCallback = Callback, | ||
| 42 | }); | ||
| 43 | } | ||
| 44 | |||
| 45 | } // namespace Vulkan | ||
diff --git a/src/video_core/vulkan_common/vulkan_debug_callback.h b/src/video_core/vulkan_common/vulkan_debug_callback.h new file mode 100644 index 000000000..2efcd244c --- /dev/null +++ b/src/video_core/vulkan_common/vulkan_debug_callback.h | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 6 | |||
| 7 | namespace Vulkan { | ||
| 8 | |||
| 9 | vk::DebugUtilsMessenger CreateDebugCallback(const vk::Instance& instance); | ||
| 10 | |||
| 11 | } // namespace Vulkan | ||
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 370a63f74..37d7b45a3 100644 --- a/src/video_core/renderer_vulkan/vk_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp | |||
| @@ -13,8 +13,9 @@ | |||
| 13 | 13 | ||
| 14 | #include "common/assert.h" | 14 | #include "common/assert.h" |
| 15 | #include "core/settings.h" | 15 | #include "core/settings.h" |
| 16 | #include "video_core/renderer_vulkan/vk_device.h" | 16 | #include "video_core/vulkan_common/nsight_aftermath_tracker.h" |
| 17 | #include "video_core/renderer_vulkan/wrapper.h" | 17 | #include "video_core/vulkan_common/vulkan_device.h" |
| 18 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 18 | 19 | ||
| 19 | namespace Vulkan { | 20 | namespace Vulkan { |
| 20 | 21 | ||
| @@ -98,8 +99,7 @@ VkFormatFeatureFlags GetFormatFeatures(VkFormatProperties properties, FormatType | |||
| 98 | }); | 99 | }); |
| 99 | } | 100 | } |
| 100 | 101 | ||
| 101 | std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties( | 102 | std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::PhysicalDevice physical) { |
| 102 | vk::PhysicalDevice physical, const vk::InstanceDispatch& dld) { | ||
| 103 | static constexpr std::array formats{ | 103 | static constexpr std::array formats{ |
| 104 | VK_FORMAT_A8B8G8R8_UNORM_PACK32, | 104 | VK_FORMAT_A8B8G8R8_UNORM_PACK32, |
| 105 | VK_FORMAT_A8B8G8R8_UINT_PACK32, | 105 | VK_FORMAT_A8B8G8R8_UINT_PACK32, |
| @@ -206,23 +206,21 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties( | |||
| 206 | 206 | ||
| 207 | } // Anonymous namespace | 207 | } // Anonymous namespace |
| 208 | 208 | ||
| 209 | VKDevice::VKDevice(VkInstance instance_, u32 instance_version_, vk::PhysicalDevice physical_, | 209 | Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR surface, |
| 210 | VkSurfaceKHR surface, const vk::InstanceDispatch& dld_) | 210 | const vk::InstanceDispatch& dld_) |
| 211 | : instance{instance_}, dld{dld_}, physical{physical_}, properties{physical.GetProperties()}, | 211 | : instance{instance_}, dld{dld_}, physical{physical_}, properties{physical.GetProperties()}, |
| 212 | instance_version{instance_version_}, format_properties{GetFormatProperties(physical, dld)} { | 212 | format_properties{GetFormatProperties(physical)} { |
| 213 | CheckSuitability(); | ||
| 213 | SetupFamilies(surface); | 214 | SetupFamilies(surface); |
| 214 | SetupFeatures(); | 215 | SetupFeatures(); |
| 215 | } | ||
| 216 | |||
| 217 | VKDevice::~VKDevice() = default; | ||
| 218 | 216 | ||
| 219 | bool VKDevice::Create() { | ||
| 220 | const auto queue_cis = GetDeviceQueueCreateInfos(); | 217 | const auto queue_cis = GetDeviceQueueCreateInfos(); |
| 221 | const std::vector extensions = LoadExtensions(); | 218 | const std::vector extensions = LoadExtensions(); |
| 222 | 219 | ||
| 223 | VkPhysicalDeviceFeatures2 features2{ | 220 | VkPhysicalDeviceFeatures2 features2{ |
| 224 | .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, | 221 | .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, |
| 225 | .pNext = nullptr, | 222 | .pNext = nullptr, |
| 223 | .features{}, | ||
| 226 | }; | 224 | }; |
| 227 | const void* first_next = &features2; | 225 | const void* first_next = &features2; |
| 228 | void** next = &features2.pNext; | 226 | void** next = &features2.pNext; |
| @@ -258,7 +256,7 @@ bool VKDevice::Create() { | |||
| 258 | .shaderTessellationAndGeometryPointSize = false, | 256 | .shaderTessellationAndGeometryPointSize = false, |
| 259 | .shaderImageGatherExtended = true, | 257 | .shaderImageGatherExtended = true, |
| 260 | .shaderStorageImageExtendedFormats = false, | 258 | .shaderStorageImageExtendedFormats = false, |
| 261 | .shaderStorageImageMultisample = true, | 259 | .shaderStorageImageMultisample = is_shader_storage_image_multisample, |
| 262 | .shaderStorageImageReadWithoutFormat = is_formatless_image_load_supported, | 260 | .shaderStorageImageReadWithoutFormat = is_formatless_image_load_supported, |
| 263 | .shaderStorageImageWriteWithoutFormat = true, | 261 | .shaderStorageImageWriteWithoutFormat = true, |
| 264 | .shaderUniformBufferArrayDynamicIndexing = false, | 262 | .shaderUniformBufferArrayDynamicIndexing = false, |
| @@ -312,6 +310,7 @@ bool VKDevice::Create() { | |||
| 312 | 310 | ||
| 313 | VkPhysicalDeviceHostQueryResetFeaturesEXT host_query_reset{ | 311 | VkPhysicalDeviceHostQueryResetFeaturesEXT host_query_reset{ |
| 314 | .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT, | 312 | .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT, |
| 313 | .pNext = nullptr, | ||
| 315 | .hostQueryReset = true, | 314 | .hostQueryReset = true, |
| 316 | }; | 315 | }; |
| 317 | SetNext(next, host_query_reset); | 316 | SetNext(next, host_query_reset); |
| @@ -415,7 +414,7 @@ bool VKDevice::Create() { | |||
| 415 | 414 | ||
| 416 | VkDeviceDiagnosticsConfigCreateInfoNV diagnostics_nv; | 415 | VkDeviceDiagnosticsConfigCreateInfoNV diagnostics_nv; |
| 417 | if (nv_device_diagnostics_config) { | 416 | if (nv_device_diagnostics_config) { |
| 418 | nsight_aftermath_tracker.Initialize(); | 417 | nsight_aftermath_tracker = std::make_unique<NsightAftermathTracker>(); |
| 419 | 418 | ||
| 420 | diagnostics_nv = { | 419 | diagnostics_nv = { |
| 421 | .sType = VK_STRUCTURE_TYPE_DEVICE_DIAGNOSTICS_CONFIG_CREATE_INFO_NV, | 420 | .sType = VK_STRUCTURE_TYPE_DEVICE_DIAGNOSTICS_CONFIG_CREATE_INFO_NV, |
| @@ -426,12 +425,7 @@ bool VKDevice::Create() { | |||
| 426 | }; | 425 | }; |
| 427 | first_next = &diagnostics_nv; | 426 | first_next = &diagnostics_nv; |
| 428 | } | 427 | } |
| 429 | |||
| 430 | logical = vk::Device::Create(physical, queue_cis, extensions, first_next, dld); | 428 | logical = vk::Device::Create(physical, queue_cis, extensions, first_next, dld); |
| 431 | if (!logical) { | ||
| 432 | LOG_ERROR(Render_Vulkan, "Failed to create logical device"); | ||
| 433 | return false; | ||
| 434 | } | ||
| 435 | 429 | ||
| 436 | CollectTelemetryParameters(); | 430 | CollectTelemetryParameters(); |
| 437 | CollectToolingInfo(); | 431 | CollectToolingInfo(); |
| @@ -455,11 +449,12 @@ bool VKDevice::Create() { | |||
| 455 | present_queue = logical.GetQueue(present_family); | 449 | present_queue = logical.GetQueue(present_family); |
| 456 | 450 | ||
| 457 | use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue(); | 451 | use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue(); |
| 458 | return true; | ||
| 459 | } | 452 | } |
| 460 | 453 | ||
| 461 | VkFormat VKDevice::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage, | 454 | Device::~Device() = default; |
| 462 | FormatType format_type) const { | 455 | |
| 456 | VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage, | ||
| 457 | FormatType format_type) const { | ||
| 463 | if (IsFormatSupported(wanted_format, wanted_usage, format_type)) { | 458 | if (IsFormatSupported(wanted_format, wanted_usage, format_type)) { |
| 464 | return wanted_format; | 459 | return wanted_format; |
| 465 | } | 460 | } |
| @@ -490,18 +485,20 @@ VkFormat VKDevice::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFla | |||
| 490 | return wanted_format; | 485 | return wanted_format; |
| 491 | } | 486 | } |
| 492 | 487 | ||
| 493 | void VKDevice::ReportLoss() const { | 488 | void Device::ReportLoss() const { |
| 494 | LOG_CRITICAL(Render_Vulkan, "Device loss occured!"); | 489 | LOG_CRITICAL(Render_Vulkan, "Device loss occured!"); |
| 495 | 490 | ||
| 496 | // Wait for the log to flush and for Nsight Aftermath to dump the results | 491 | // Wait for the log to flush and for Nsight Aftermath to dump the results |
| 497 | std::this_thread::sleep_for(std::chrono::seconds{15}); | 492 | std::this_thread::sleep_for(std::chrono::seconds{15}); |
| 498 | } | 493 | } |
| 499 | 494 | ||
| 500 | void VKDevice::SaveShader(const std::vector<u32>& spirv) const { | 495 | void Device::SaveShader(const std::vector<u32>& spirv) const { |
| 501 | nsight_aftermath_tracker.SaveShader(spirv); | 496 | if (nsight_aftermath_tracker) { |
| 497 | nsight_aftermath_tracker->SaveShader(spirv); | ||
| 498 | } | ||
| 502 | } | 499 | } |
| 503 | 500 | ||
| 504 | bool VKDevice::IsOptimalAstcSupported(const VkPhysicalDeviceFeatures& features) const { | 501 | bool Device::IsOptimalAstcSupported(const VkPhysicalDeviceFeatures& features) const { |
| 505 | // Disable for now to avoid converting ASTC twice. | 502 | // Disable for now to avoid converting ASTC twice. |
| 506 | static constexpr std::array astc_formats = { | 503 | static constexpr std::array astc_formats = { |
| 507 | VK_FORMAT_ASTC_4x4_UNORM_BLOCK, VK_FORMAT_ASTC_4x4_SRGB_BLOCK, | 504 | VK_FORMAT_ASTC_4x4_UNORM_BLOCK, VK_FORMAT_ASTC_4x4_SRGB_BLOCK, |
| @@ -535,7 +532,7 @@ bool VKDevice::IsOptimalAstcSupported(const VkPhysicalDeviceFeatures& features) | |||
| 535 | return true; | 532 | return true; |
| 536 | } | 533 | } |
| 537 | 534 | ||
| 538 | bool VKDevice::TestDepthStencilBlits() const { | 535 | bool Device::TestDepthStencilBlits() const { |
| 539 | static constexpr VkFormatFeatureFlags required_features = | 536 | static constexpr VkFormatFeatureFlags required_features = |
| 540 | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT; | 537 | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT; |
| 541 | const auto test_features = [](VkFormatProperties props) { | 538 | const auto test_features = [](VkFormatProperties props) { |
| @@ -545,8 +542,8 @@ bool VKDevice::TestDepthStencilBlits() const { | |||
| 545 | test_features(format_properties.at(VK_FORMAT_D24_UNORM_S8_UINT)); | 542 | test_features(format_properties.at(VK_FORMAT_D24_UNORM_S8_UINT)); |
| 546 | } | 543 | } |
| 547 | 544 | ||
| 548 | bool VKDevice::IsFormatSupported(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage, | 545 | bool Device::IsFormatSupported(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage, |
| 549 | FormatType format_type) const { | 546 | FormatType format_type) const { |
| 550 | const auto it = format_properties.find(wanted_format); | 547 | const auto it = format_properties.find(wanted_format); |
| 551 | if (it == format_properties.end()) { | 548 | if (it == format_properties.end()) { |
| 552 | UNIMPLEMENTED_MSG("Unimplemented format query={}", wanted_format); | 549 | UNIMPLEMENTED_MSG("Unimplemented format query={}", wanted_format); |
| @@ -556,64 +553,45 @@ bool VKDevice::IsFormatSupported(VkFormat wanted_format, VkFormatFeatureFlags wa | |||
| 556 | return (supported_usage & wanted_usage) == wanted_usage; | 553 | return (supported_usage & wanted_usage) == wanted_usage; |
| 557 | } | 554 | } |
| 558 | 555 | ||
| 559 | bool VKDevice::IsSuitable(vk::PhysicalDevice physical, VkSurfaceKHR surface) { | 556 | void Device::CheckSuitability() const { |
| 560 | bool is_suitable = true; | ||
| 561 | std::bitset<REQUIRED_EXTENSIONS.size()> available_extensions; | 557 | std::bitset<REQUIRED_EXTENSIONS.size()> available_extensions; |
| 562 | 558 | for (const VkExtensionProperties& property : physical.EnumerateDeviceExtensionProperties()) { | |
| 563 | for (const auto& prop : physical.EnumerateDeviceExtensionProperties()) { | ||
| 564 | for (std::size_t i = 0; i < REQUIRED_EXTENSIONS.size(); ++i) { | 559 | for (std::size_t i = 0; i < REQUIRED_EXTENSIONS.size(); ++i) { |
| 565 | if (available_extensions[i]) { | 560 | if (available_extensions[i]) { |
| 566 | continue; | 561 | continue; |
| 567 | } | 562 | } |
| 568 | const std::string_view name{prop.extensionName}; | 563 | const std::string_view name{property.extensionName}; |
| 569 | available_extensions[i] = name == REQUIRED_EXTENSIONS[i]; | 564 | available_extensions[i] = name == REQUIRED_EXTENSIONS[i]; |
| 570 | } | 565 | } |
| 571 | } | 566 | } |
| 572 | if (!available_extensions.all()) { | 567 | for (size_t i = 0; i < REQUIRED_EXTENSIONS.size(); ++i) { |
| 573 | for (std::size_t i = 0; i < REQUIRED_EXTENSIONS.size(); ++i) { | 568 | if (available_extensions[i]) { |
| 574 | if (available_extensions[i]) { | ||
| 575 | continue; | ||
| 576 | } | ||
| 577 | LOG_ERROR(Render_Vulkan, "Missing required extension: {}", REQUIRED_EXTENSIONS[i]); | ||
| 578 | is_suitable = false; | ||
| 579 | } | ||
| 580 | } | ||
| 581 | |||
| 582 | bool has_graphics{}, has_present{}; | ||
| 583 | const std::vector queue_family_properties = physical.GetQueueFamilyProperties(); | ||
| 584 | for (u32 i = 0; i < static_cast<u32>(queue_family_properties.size()); ++i) { | ||
| 585 | const auto& family = queue_family_properties[i]; | ||
| 586 | if (family.queueCount == 0) { | ||
| 587 | continue; | 569 | continue; |
| 588 | } | 570 | } |
| 589 | has_graphics |= family.queueFlags & VK_QUEUE_GRAPHICS_BIT; | 571 | LOG_ERROR(Render_Vulkan, "Missing required extension: {}", REQUIRED_EXTENSIONS[i]); |
| 590 | has_present |= physical.GetSurfaceSupportKHR(i, surface); | 572 | throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT); |
| 591 | } | 573 | } |
| 592 | if (!has_graphics || !has_present) { | 574 | struct LimitTuple { |
| 593 | LOG_ERROR(Render_Vulkan, "Device lacks a graphics and present queue"); | 575 | u32 minimum; |
| 594 | is_suitable = false; | 576 | u32 value; |
| 595 | } | 577 | const char* name; |
| 596 | 578 | }; | |
| 597 | // TODO(Rodrigo): Check if the device matches all requeriments. | 579 | const VkPhysicalDeviceLimits& limits{properties.limits}; |
| 598 | const auto properties{physical.GetProperties()}; | 580 | const std::array limits_report{ |
| 599 | const auto& limits{properties.limits}; | 581 | LimitTuple{65536, limits.maxUniformBufferRange, "maxUniformBufferRange"}, |
| 600 | 582 | LimitTuple{16, limits.maxViewports, "maxViewports"}, | |
| 601 | constexpr u32 required_ubo_size = 65536; | 583 | LimitTuple{8, limits.maxColorAttachments, "maxColorAttachments"}, |
| 602 | if (limits.maxUniformBufferRange < required_ubo_size) { | 584 | LimitTuple{8, limits.maxClipDistances, "maxClipDistances"}, |
| 603 | LOG_ERROR(Render_Vulkan, "Device UBO size {} is too small, {} is required", | 585 | }; |
| 604 | limits.maxUniformBufferRange, required_ubo_size); | 586 | for (const auto& tuple : limits_report) { |
| 605 | is_suitable = false; | 587 | if (tuple.value < tuple.minimum) { |
| 606 | } | 588 | LOG_ERROR(Render_Vulkan, "{} has to be {} or greater but it is {}", tuple.name, |
| 607 | 589 | tuple.minimum, tuple.value); | |
| 608 | constexpr u32 required_num_viewports = 16; | 590 | throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT); |
| 609 | if (limits.maxViewports < required_num_viewports) { | 591 | } |
| 610 | LOG_INFO(Render_Vulkan, "Device number of viewports {} is too small, {} is required", | ||
| 611 | limits.maxViewports, required_num_viewports); | ||
| 612 | is_suitable = false; | ||
| 613 | } | 592 | } |
| 614 | 593 | const VkPhysicalDeviceFeatures features{physical.GetFeatures()}; | |
| 615 | const auto features{physical.GetFeatures()}; | 594 | const std::array feature_report{ |
| 616 | const std::array feature_report = { | ||
| 617 | std::make_pair(features.vertexPipelineStoresAndAtomics, "vertexPipelineStoresAndAtomics"), | 595 | std::make_pair(features.vertexPipelineStoresAndAtomics, "vertexPipelineStoresAndAtomics"), |
| 618 | std::make_pair(features.imageCubeArray, "imageCubeArray"), | 596 | std::make_pair(features.imageCubeArray, "imageCubeArray"), |
| 619 | std::make_pair(features.independentBlend, "independentBlend"), | 597 | std::make_pair(features.independentBlend, "independentBlend"), |
| @@ -627,26 +605,19 @@ bool VKDevice::IsSuitable(vk::PhysicalDevice physical, VkSurfaceKHR surface) { | |||
| 627 | std::make_pair(features.occlusionQueryPrecise, "occlusionQueryPrecise"), | 605 | std::make_pair(features.occlusionQueryPrecise, "occlusionQueryPrecise"), |
| 628 | std::make_pair(features.fragmentStoresAndAtomics, "fragmentStoresAndAtomics"), | 606 | std::make_pair(features.fragmentStoresAndAtomics, "fragmentStoresAndAtomics"), |
| 629 | std::make_pair(features.shaderImageGatherExtended, "shaderImageGatherExtended"), | 607 | std::make_pair(features.shaderImageGatherExtended, "shaderImageGatherExtended"), |
| 630 | std::make_pair(features.shaderStorageImageMultisample, "shaderStorageImageMultisample"), | ||
| 631 | std::make_pair(features.shaderStorageImageWriteWithoutFormat, | 608 | std::make_pair(features.shaderStorageImageWriteWithoutFormat, |
| 632 | "shaderStorageImageWriteWithoutFormat"), | 609 | "shaderStorageImageWriteWithoutFormat"), |
| 633 | }; | 610 | }; |
| 634 | for (const auto& [supported, name] : feature_report) { | 611 | for (const auto& [is_supported, name] : feature_report) { |
| 635 | if (supported) { | 612 | if (is_supported) { |
| 636 | continue; | 613 | continue; |
| 637 | } | 614 | } |
| 638 | LOG_ERROR(Render_Vulkan, "Missing required feature: {}", name); | 615 | LOG_ERROR(Render_Vulkan, "Missing required feature: {}", name); |
| 639 | is_suitable = false; | 616 | throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT); |
| 640 | } | 617 | } |
| 641 | |||
| 642 | if (!is_suitable) { | ||
| 643 | LOG_ERROR(Render_Vulkan, "{} is not suitable", properties.deviceName); | ||
| 644 | } | ||
| 645 | |||
| 646 | return is_suitable; | ||
| 647 | } | 618 | } |
| 648 | 619 | ||
| 649 | std::vector<const char*> VKDevice::LoadExtensions() { | 620 | std::vector<const char*> Device::LoadExtensions() { |
| 650 | std::vector<const char*> extensions; | 621 | std::vector<const char*> extensions; |
| 651 | extensions.reserve(7 + REQUIRED_EXTENSIONS.size()); | 622 | extensions.reserve(7 + REQUIRED_EXTENSIONS.size()); |
| 652 | extensions.insert(extensions.begin(), REQUIRED_EXTENSIONS.begin(), REQUIRED_EXTENSIONS.end()); | 623 | extensions.insert(extensions.begin(), REQUIRED_EXTENSIONS.begin(), REQUIRED_EXTENSIONS.end()); |
| @@ -685,9 +656,7 @@ std::vector<const char*> VKDevice::LoadExtensions() { | |||
| 685 | test(has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, false); | 656 | test(has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, false); |
| 686 | test(has_ext_extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false); | 657 | test(has_ext_extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false); |
| 687 | test(has_ext_robustness2, VK_EXT_ROBUSTNESS_2_EXTENSION_NAME, false); | 658 | test(has_ext_robustness2, VK_EXT_ROBUSTNESS_2_EXTENSION_NAME, false); |
| 688 | if (instance_version >= VK_API_VERSION_1_1) { | 659 | test(has_ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, false); |
| 689 | test(has_ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, false); | ||
| 690 | } | ||
| 691 | if (Settings::values.renderer_debug) { | 660 | if (Settings::values.renderer_debug) { |
| 692 | test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, | 661 | test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, |
| 693 | true); | 662 | true); |
| @@ -801,39 +770,46 @@ std::vector<const char*> VKDevice::LoadExtensions() { | |||
| 801 | return extensions; | 770 | return extensions; |
| 802 | } | 771 | } |
| 803 | 772 | ||
| 804 | void VKDevice::SetupFamilies(VkSurfaceKHR surface) { | 773 | void Device::SetupFamilies(VkSurfaceKHR surface) { |
| 805 | std::optional<u32> graphics_family_, present_family_; | ||
| 806 | |||
| 807 | const std::vector queue_family_properties = physical.GetQueueFamilyProperties(); | 774 | const std::vector queue_family_properties = physical.GetQueueFamilyProperties(); |
| 808 | for (u32 i = 0; i < static_cast<u32>(queue_family_properties.size()); ++i) { | 775 | std::optional<u32> graphics; |
| 809 | if (graphics_family_ && present_family_) | 776 | std::optional<u32> present; |
| 777 | for (u32 index = 0; index < static_cast<u32>(queue_family_properties.size()); ++index) { | ||
| 778 | if (graphics && (present || !surface)) { | ||
| 810 | break; | 779 | break; |
| 811 | 780 | } | |
| 812 | const auto& queue_family = queue_family_properties[i]; | 781 | const VkQueueFamilyProperties& queue_family = queue_family_properties[index]; |
| 813 | if (queue_family.queueCount == 0) | 782 | if (queue_family.queueCount == 0) { |
| 814 | continue; | 783 | continue; |
| 815 | 784 | } | |
| 816 | if (queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT) { | 785 | if (queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT) { |
| 817 | graphics_family_ = i; | 786 | graphics = index; |
| 818 | } | 787 | } |
| 819 | if (physical.GetSurfaceSupportKHR(i, surface)) { | 788 | if (surface && physical.GetSurfaceSupportKHR(index, surface)) { |
| 820 | present_family_ = i; | 789 | present = index; |
| 821 | } | 790 | } |
| 822 | } | 791 | } |
| 823 | ASSERT(graphics_family_ && present_family_); | 792 | if (!graphics) { |
| 824 | 793 | LOG_ERROR(Render_Vulkan, "Device lacks a graphics queue"); | |
| 825 | graphics_family = *graphics_family_; | 794 | throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT); |
| 826 | present_family = *present_family_; | 795 | } |
| 796 | if (surface && !present) { | ||
| 797 | LOG_ERROR(Render_Vulkan, "Device lacks a present queue"); | ||
| 798 | throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT); | ||
| 799 | } | ||
| 800 | graphics_family = *graphics; | ||
| 801 | present_family = *present; | ||
| 827 | } | 802 | } |
| 828 | 803 | ||
| 829 | void VKDevice::SetupFeatures() { | 804 | void Device::SetupFeatures() { |
| 830 | const auto supported_features{physical.GetFeatures()}; | 805 | const auto supported_features{physical.GetFeatures()}; |
| 831 | is_formatless_image_load_supported = supported_features.shaderStorageImageReadWithoutFormat; | 806 | is_formatless_image_load_supported = supported_features.shaderStorageImageReadWithoutFormat; |
| 807 | is_shader_storage_image_multisample = supported_features.shaderStorageImageMultisample; | ||
| 832 | is_blit_depth_stencil_supported = TestDepthStencilBlits(); | 808 | is_blit_depth_stencil_supported = TestDepthStencilBlits(); |
| 833 | is_optimal_astc_supported = IsOptimalAstcSupported(supported_features); | 809 | is_optimal_astc_supported = IsOptimalAstcSupported(supported_features); |
| 834 | } | 810 | } |
| 835 | 811 | ||
| 836 | void VKDevice::CollectTelemetryParameters() { | 812 | void Device::CollectTelemetryParameters() { |
| 837 | VkPhysicalDeviceDriverPropertiesKHR driver{ | 813 | VkPhysicalDeviceDriverPropertiesKHR driver{ |
| 838 | .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR, | 814 | .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR, |
| 839 | .pNext = nullptr, | 815 | .pNext = nullptr, |
| @@ -860,7 +836,7 @@ void VKDevice::CollectTelemetryParameters() { | |||
| 860 | } | 836 | } |
| 861 | } | 837 | } |
| 862 | 838 | ||
| 863 | void VKDevice::CollectToolingInfo() { | 839 | void Device::CollectToolingInfo() { |
| 864 | if (!ext_tooling_info) { | 840 | if (!ext_tooling_info) { |
| 865 | return; | 841 | return; |
| 866 | } | 842 | } |
| @@ -886,7 +862,7 @@ void VKDevice::CollectToolingInfo() { | |||
| 886 | } | 862 | } |
| 887 | } | 863 | } |
| 888 | 864 | ||
| 889 | std::vector<VkDeviceQueueCreateInfo> VKDevice::GetDeviceQueueCreateInfos() const { | 865 | std::vector<VkDeviceQueueCreateInfo> Device::GetDeviceQueueCreateInfos() const { |
| 890 | static constexpr float QUEUE_PRIORITY = 1.0f; | 866 | static constexpr float QUEUE_PRIORITY = 1.0f; |
| 891 | 867 | ||
| 892 | std::unordered_set<u32> unique_queue_families{graphics_family, present_family}; | 868 | std::unordered_set<u32> unique_queue_families{graphics_family, present_family}; |
diff --git a/src/video_core/renderer_vulkan/vk_device.h b/src/video_core/vulkan_common/vulkan_device.h index 995dcfc0f..4b66dba7a 100644 --- a/src/video_core/renderer_vulkan/vk_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h | |||
| @@ -10,11 +10,12 @@ | |||
| 10 | #include <vector> | 10 | #include <vector> |
| 11 | 11 | ||
| 12 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 13 | #include "video_core/renderer_vulkan/nsight_aftermath_tracker.h" | 13 | #include "video_core/vulkan_common/vulkan_wrapper.h" |
| 14 | #include "video_core/renderer_vulkan/wrapper.h" | ||
| 15 | 14 | ||
| 16 | namespace Vulkan { | 15 | namespace Vulkan { |
| 17 | 16 | ||
| 17 | class NsightAftermathTracker; | ||
| 18 | |||
| 18 | /// Format usage descriptor. | 19 | /// Format usage descriptor. |
| 19 | enum class FormatType { Linear, Optimal, Buffer }; | 20 | enum class FormatType { Linear, Optimal, Buffer }; |
| 20 | 21 | ||
| @@ -22,14 +23,11 @@ enum class FormatType { Linear, Optimal, Buffer }; | |||
| 22 | const u32 GuestWarpSize = 32; | 23 | const u32 GuestWarpSize = 32; |
| 23 | 24 | ||
| 24 | /// Handles data specific to a physical device. | 25 | /// Handles data specific to a physical device. |
| 25 | class VKDevice final { | 26 | class Device final { |
| 26 | public: | 27 | public: |
| 27 | explicit VKDevice(VkInstance instance, u32 instance_version, vk::PhysicalDevice physical, | 28 | explicit Device(VkInstance instance, vk::PhysicalDevice physical, VkSurfaceKHR surface, |
| 28 | VkSurfaceKHR surface, const vk::InstanceDispatch& dld); | 29 | const vk::InstanceDispatch& dld); |
| 29 | ~VKDevice(); | 30 | ~Device(); |
| 30 | |||
| 31 | /// Initializes the device. Returns true on success. | ||
| 32 | bool Create(); | ||
| 33 | 31 | ||
| 34 | /** | 32 | /** |
| 35 | * Returns a format supported by the device for the passed requeriments. | 33 | * Returns a format supported by the device for the passed requeriments. |
| @@ -82,11 +80,6 @@ public: | |||
| 82 | return present_family; | 80 | return present_family; |
| 83 | } | 81 | } |
| 84 | 82 | ||
| 85 | /// Returns the current instance Vulkan API version in Vulkan-formatted version numbers. | ||
| 86 | u32 InstanceApiVersion() const { | ||
| 87 | return instance_version; | ||
| 88 | } | ||
| 89 | |||
| 90 | /// Returns the current Vulkan API version provided in Vulkan-formatted version numbers. | 83 | /// Returns the current Vulkan API version provided in Vulkan-formatted version numbers. |
| 91 | u32 ApiVersion() const { | 84 | u32 ApiVersion() const { |
| 92 | return properties.apiVersion; | 85 | return properties.apiVersion; |
| @@ -232,10 +225,10 @@ public: | |||
| 232 | return use_asynchronous_shaders; | 225 | return use_asynchronous_shaders; |
| 233 | } | 226 | } |
| 234 | 227 | ||
| 228 | private: | ||
| 235 | /// Checks if the physical device is suitable. | 229 | /// Checks if the physical device is suitable. |
| 236 | static bool IsSuitable(vk::PhysicalDevice physical, VkSurfaceKHR surface); | 230 | void CheckSuitability() const; |
| 237 | 231 | ||
| 238 | private: | ||
| 239 | /// Loads extensions into a vector and stores available ones in this object. | 232 | /// Loads extensions into a vector and stores available ones in this object. |
| 240 | std::vector<const char*> LoadExtensions(); | 233 | std::vector<const char*> LoadExtensions(); |
| 241 | 234 | ||
| @@ -279,23 +272,24 @@ private: | |||
| 279 | bool is_optimal_astc_supported{}; ///< Support for native ASTC. | 272 | bool is_optimal_astc_supported{}; ///< Support for native ASTC. |
| 280 | bool is_float16_supported{}; ///< Support for float16 arithmetics. | 273 | bool is_float16_supported{}; ///< Support for float16 arithmetics. |
| 281 | bool is_warp_potentially_bigger{}; ///< Host warp size can be bigger than guest. | 274 | bool is_warp_potentially_bigger{}; ///< Host warp size can be bigger than guest. |
| 282 | bool is_formatless_image_load_supported{}; ///< Support for shader image read without format. | 275 | bool is_formatless_image_load_supported{}; ///< Support for shader image read without format. |
| 283 | bool is_blit_depth_stencil_supported{}; ///< Support for blitting from and to depth stencil. | 276 | bool is_shader_storage_image_multisample{}; ///< Support for image operations on MSAA images. |
| 284 | bool nv_viewport_swizzle{}; ///< Support for VK_NV_viewport_swizzle. | 277 | bool is_blit_depth_stencil_supported{}; ///< Support for blitting from and to depth stencil. |
| 285 | bool khr_uniform_buffer_standard_layout{}; ///< Support for std430 on UBOs. | 278 | bool nv_viewport_swizzle{}; ///< Support for VK_NV_viewport_swizzle. |
| 286 | bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8. | 279 | bool khr_uniform_buffer_standard_layout{}; ///< Support for std430 on UBOs. |
| 287 | bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax. | 280 | bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8. |
| 288 | bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted. | 281 | bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax. |
| 289 | bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer. | 282 | bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted. |
| 290 | bool ext_tooling_info{}; ///< Support for VK_EXT_tooling_info. | 283 | bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer. |
| 291 | bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback. | 284 | bool ext_tooling_info{}; ///< Support for VK_EXT_tooling_info. |
| 292 | bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color. | 285 | bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback. |
| 293 | bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state. | 286 | bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color. |
| 294 | bool ext_robustness2{}; ///< Support for VK_EXT_robustness2. | 287 | bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state. |
| 295 | bool ext_shader_stencil_export{}; ///< Support for VK_EXT_shader_stencil_export. | 288 | bool ext_robustness2{}; ///< Support for VK_EXT_robustness2. |
| 296 | bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config. | 289 | bool ext_shader_stencil_export{}; ///< Support for VK_EXT_shader_stencil_export. |
| 297 | bool has_renderdoc{}; ///< Has RenderDoc attached | 290 | bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config. |
| 298 | bool has_nsight_graphics{}; ///< Has Nsight Graphics attached | 291 | bool has_renderdoc{}; ///< Has RenderDoc attached |
| 292 | bool has_nsight_graphics{}; ///< Has Nsight Graphics attached | ||
| 299 | 293 | ||
| 300 | // Asynchronous Graphics Pipeline setting | 294 | // Asynchronous Graphics Pipeline setting |
| 301 | bool use_asynchronous_shaders{}; ///< Setting to use asynchronous shaders/graphics pipeline | 295 | bool use_asynchronous_shaders{}; ///< Setting to use asynchronous shaders/graphics pipeline |
| @@ -308,7 +302,7 @@ private: | |||
| 308 | std::unordered_map<VkFormat, VkFormatProperties> format_properties; | 302 | std::unordered_map<VkFormat, VkFormatProperties> format_properties; |
| 309 | 303 | ||
| 310 | /// Nsight Aftermath GPU crash tracker | 304 | /// Nsight Aftermath GPU crash tracker |
| 311 | NsightAftermathTracker nsight_aftermath_tracker; | 305 | std::unique_ptr<NsightAftermathTracker> nsight_aftermath_tracker; |
| 312 | }; | 306 | }; |
| 313 | 307 | ||
| 314 | } // namespace Vulkan | 308 | } // namespace Vulkan |
diff --git a/src/video_core/vulkan_common/vulkan_instance.cpp b/src/video_core/vulkan_common/vulkan_instance.cpp new file mode 100644 index 000000000..889ecda0c --- /dev/null +++ b/src/video_core/vulkan_common/vulkan_instance.cpp | |||
| @@ -0,0 +1,151 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <optional> | ||
| 7 | #include <span> | ||
| 8 | #include <utility> | ||
| 9 | #include <vector> | ||
| 10 | |||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "common/dynamic_library.h" | ||
| 13 | #include "common/logging/log.h" | ||
| 14 | #include "core/frontend/emu_window.h" | ||
| 15 | #include "video_core/vulkan_common/vulkan_instance.h" | ||
| 16 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 17 | |||
| 18 | // Include these late to avoid polluting previous headers | ||
| 19 | #ifdef _WIN32 | ||
| 20 | #include <windows.h> | ||
| 21 | // ensure include order | ||
| 22 | #include <vulkan/vulkan_win32.h> | ||
| 23 | #endif | ||
| 24 | |||
| 25 | #if !defined(_WIN32) && !defined(__APPLE__) | ||
| 26 | #include <X11/Xlib.h> | ||
| 27 | #include <vulkan/vulkan_wayland.h> | ||
| 28 | #include <vulkan/vulkan_xlib.h> | ||
| 29 | #endif | ||
| 30 | |||
| 31 | namespace Vulkan { | ||
| 32 | namespace { | ||
| 33 | [[nodiscard]] std::vector<const char*> RequiredExtensions( | ||
| 34 | Core::Frontend::WindowSystemType window_type, bool enable_debug_utils) { | ||
| 35 | std::vector<const char*> extensions; | ||
| 36 | extensions.reserve(6); | ||
| 37 | switch (window_type) { | ||
| 38 | case Core::Frontend::WindowSystemType::Headless: | ||
| 39 | break; | ||
| 40 | #ifdef _WIN32 | ||
| 41 | case Core::Frontend::WindowSystemType::Windows: | ||
| 42 | extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); | ||
| 43 | break; | ||
| 44 | #endif | ||
| 45 | #if !defined(_WIN32) && !defined(__APPLE__) | ||
| 46 | case Core::Frontend::WindowSystemType::X11: | ||
| 47 | extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); | ||
| 48 | break; | ||
| 49 | case Core::Frontend::WindowSystemType::Wayland: | ||
| 50 | extensions.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); | ||
| 51 | break; | ||
| 52 | #endif | ||
| 53 | default: | ||
| 54 | LOG_ERROR(Render_Vulkan, "Presentation not supported on this platform"); | ||
| 55 | break; | ||
| 56 | } | ||
| 57 | if (window_type != Core::Frontend::WindowSystemType::Headless) { | ||
| 58 | extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); | ||
| 59 | } | ||
| 60 | if (enable_debug_utils) { | ||
| 61 | extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); | ||
| 62 | } | ||
| 63 | extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); | ||
| 64 | return extensions; | ||
| 65 | } | ||
| 66 | |||
| 67 | [[nodiscard]] bool AreExtensionsSupported(const vk::InstanceDispatch& dld, | ||
| 68 | std::span<const char* const> extensions) { | ||
| 69 | const std::optional properties = vk::EnumerateInstanceExtensionProperties(dld); | ||
| 70 | if (!properties) { | ||
| 71 | LOG_ERROR(Render_Vulkan, "Failed to query extension properties"); | ||
| 72 | return false; | ||
| 73 | } | ||
| 74 | for (const char* extension : extensions) { | ||
| 75 | const auto it = std::ranges::find_if(*properties, [extension](const auto& prop) { | ||
| 76 | return std::strcmp(extension, prop.extensionName) == 0; | ||
| 77 | }); | ||
| 78 | if (it == properties->end()) { | ||
| 79 | LOG_ERROR(Render_Vulkan, "Required instance extension {} is not available", extension); | ||
| 80 | return false; | ||
| 81 | } | ||
| 82 | } | ||
| 83 | return true; | ||
| 84 | } | ||
| 85 | |||
| 86 | [[nodiscard]] std::vector<const char*> Layers(bool enable_layers) { | ||
| 87 | std::vector<const char*> layers; | ||
| 88 | if (enable_layers) { | ||
| 89 | layers.push_back("VK_LAYER_KHRONOS_validation"); | ||
| 90 | } | ||
| 91 | return layers; | ||
| 92 | } | ||
| 93 | |||
| 94 | void RemoveUnavailableLayers(const vk::InstanceDispatch& dld, std::vector<const char*>& layers) { | ||
| 95 | const std::optional layer_properties = vk::EnumerateInstanceLayerProperties(dld); | ||
| 96 | if (!layer_properties) { | ||
| 97 | LOG_ERROR(Render_Vulkan, "Failed to query layer properties, disabling layers"); | ||
| 98 | layers.clear(); | ||
| 99 | } | ||
| 100 | std::erase_if(layers, [&layer_properties](const char* layer) { | ||
| 101 | const auto comp = [layer](const VkLayerProperties& layer_property) { | ||
| 102 | return std::strcmp(layer, layer_property.layerName) == 0; | ||
| 103 | }; | ||
| 104 | const auto it = std::ranges::find_if(*layer_properties, comp); | ||
| 105 | if (it == layer_properties->end()) { | ||
| 106 | LOG_ERROR(Render_Vulkan, "Layer {} not available, removing it", layer); | ||
| 107 | return true; | ||
| 108 | } | ||
| 109 | return false; | ||
| 110 | }); | ||
| 111 | } | ||
| 112 | } // Anonymous namespace | ||
| 113 | |||
| 114 | vk::Instance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceDispatch& dld, | ||
| 115 | u32 required_version, Core::Frontend::WindowSystemType window_type, | ||
| 116 | bool enable_debug_utils, bool enable_layers) { | ||
| 117 | if (!library.IsOpen()) { | ||
| 118 | LOG_ERROR(Render_Vulkan, "Vulkan library not available"); | ||
| 119 | throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); | ||
| 120 | } | ||
| 121 | if (!library.GetSymbol("vkGetInstanceProcAddr", &dld.vkGetInstanceProcAddr)) { | ||
| 122 | LOG_ERROR(Render_Vulkan, "vkGetInstanceProcAddr not present in Vulkan"); | ||
| 123 | throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); | ||
| 124 | } | ||
| 125 | if (!vk::Load(dld)) { | ||
| 126 | LOG_ERROR(Render_Vulkan, "Failed to load Vulkan function pointers"); | ||
| 127 | throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); | ||
| 128 | } | ||
| 129 | const std::vector<const char*> extensions = RequiredExtensions(window_type, enable_debug_utils); | ||
| 130 | if (!AreExtensionsSupported(dld, extensions)) { | ||
| 131 | throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT); | ||
| 132 | } | ||
| 133 | std::vector<const char*> layers = Layers(enable_layers); | ||
| 134 | RemoveUnavailableLayers(dld, layers); | ||
| 135 | |||
| 136 | const u32 available_version = vk::AvailableVersion(dld); | ||
| 137 | if (available_version < required_version) { | ||
| 138 | LOG_ERROR(Render_Vulkan, "Vulkan {}.{} is not supported, {}.{} is required", | ||
| 139 | VK_VERSION_MAJOR(available_version), VK_VERSION_MINOR(available_version), | ||
| 140 | VK_VERSION_MAJOR(required_version), VK_VERSION_MINOR(required_version)); | ||
| 141 | throw vk::Exception(VK_ERROR_INCOMPATIBLE_DRIVER); | ||
| 142 | } | ||
| 143 | vk::Instance instance = vk::Instance::Create(required_version, layers, extensions, dld); | ||
| 144 | if (!vk::Load(*instance, dld)) { | ||
| 145 | LOG_ERROR(Render_Vulkan, "Failed to load Vulkan instance function pointers"); | ||
| 146 | throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); | ||
| 147 | } | ||
| 148 | return instance; | ||
| 149 | } | ||
| 150 | |||
| 151 | } // namespace Vulkan | ||
diff --git a/src/video_core/vulkan_common/vulkan_instance.h b/src/video_core/vulkan_common/vulkan_instance.h new file mode 100644 index 000000000..e5e3a7144 --- /dev/null +++ b/src/video_core/vulkan_common/vulkan_instance.h | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/dynamic_library.h" | ||
| 9 | #include "core/frontend/emu_window.h" | ||
| 10 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 11 | |||
| 12 | namespace Vulkan { | ||
| 13 | |||
| 14 | /** | ||
| 15 | * Create a Vulkan instance | ||
| 16 | * | ||
| 17 | * @param library Dynamic library to load the Vulkan instance from | ||
| 18 | * @param dld Dispatch table to load function pointers into | ||
| 19 | * @param required_version Required Vulkan version (for example, VK_API_VERSION_1_1) | ||
| 20 | * @param window_type Window system type's enabled extension | ||
| 21 | * @param enable_debug_utils Whether to enable VK_EXT_debug_utils_extension_name or not | ||
| 22 | * @param enable_layers Whether to enable Vulkan validation layers or not | ||
| 23 | * | ||
| 24 | * @return A new Vulkan instance | ||
| 25 | * @throw vk::Exception on failure | ||
| 26 | */ | ||
| 27 | [[nodiscard]] vk::Instance CreateInstance( | ||
| 28 | const Common::DynamicLibrary& library, vk::InstanceDispatch& dld, u32 required_version, | ||
| 29 | Core::Frontend::WindowSystemType window_type = Core::Frontend::WindowSystemType::Headless, | ||
| 30 | bool enable_debug_utils = false, bool enable_layers = false); | ||
| 31 | |||
| 32 | } // namespace Vulkan | ||
diff --git a/src/video_core/vulkan_common/vulkan_library.cpp b/src/video_core/vulkan_common/vulkan_library.cpp new file mode 100644 index 000000000..557871d81 --- /dev/null +++ b/src/video_core/vulkan_common/vulkan_library.cpp | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cstdlib> | ||
| 6 | #include <string> | ||
| 7 | |||
| 8 | #include "common/dynamic_library.h" | ||
| 9 | #include "common/file_util.h" | ||
| 10 | #include "video_core/vulkan_common/vulkan_library.h" | ||
| 11 | |||
| 12 | namespace Vulkan { | ||
| 13 | |||
| 14 | Common::DynamicLibrary OpenLibrary() { | ||
| 15 | Common::DynamicLibrary library; | ||
| 16 | #ifdef __APPLE__ | ||
| 17 | // Check if a path to a specific Vulkan library has been specified. | ||
| 18 | char* const libvulkan_env = std::getenv("LIBVULKAN_PATH"); | ||
| 19 | if (!libvulkan_env || !library.Open(libvulkan_env)) { | ||
| 20 | // Use the libvulkan.dylib from the application bundle. | ||
| 21 | const std::string filename = | ||
| 22 | Common::FS::GetBundleDirectory() + "/Contents/Frameworks/libvulkan.dylib"; | ||
| 23 | void(library.Open(filename.c_str())); | ||
| 24 | } | ||
| 25 | #else | ||
| 26 | std::string filename = Common::DynamicLibrary::GetVersionedFilename("vulkan", 1); | ||
| 27 | if (!library.Open(filename.c_str())) { | ||
| 28 | // Android devices may not have libvulkan.so.1, only libvulkan.so. | ||
| 29 | filename = Common::DynamicLibrary::GetVersionedFilename("vulkan"); | ||
| 30 | void(library.Open(filename.c_str())); | ||
| 31 | } | ||
| 32 | #endif | ||
| 33 | return library; | ||
| 34 | } | ||
| 35 | |||
| 36 | } // namespace Vulkan | ||
diff --git a/src/video_core/vulkan_common/vulkan_library.h b/src/video_core/vulkan_common/vulkan_library.h new file mode 100644 index 000000000..8b28b0e17 --- /dev/null +++ b/src/video_core/vulkan_common/vulkan_library.h | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/dynamic_library.h" | ||
| 8 | |||
| 9 | namespace Vulkan { | ||
| 10 | |||
| 11 | Common::DynamicLibrary OpenLibrary(); | ||
| 12 | |||
| 13 | } // namespace Vulkan | ||
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp new file mode 100644 index 000000000..d6eb3af31 --- /dev/null +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp | |||
| @@ -0,0 +1,268 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <bit> | ||
| 7 | #include <optional> | ||
| 8 | #include <vector> | ||
| 9 | |||
| 10 | #include "common/alignment.h" | ||
| 11 | #include "common/assert.h" | ||
| 12 | #include "common/common_types.h" | ||
| 13 | #include "common/logging/log.h" | ||
| 14 | #include "video_core/vulkan_common/vulkan_device.h" | ||
| 15 | #include "video_core/vulkan_common/vulkan_memory_allocator.h" | ||
| 16 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 17 | |||
| 18 | namespace Vulkan { | ||
| 19 | namespace { | ||
| 20 | struct Range { | ||
| 21 | u64 begin; | ||
| 22 | u64 end; | ||
| 23 | |||
| 24 | [[nodiscard]] bool Contains(u64 iterator, u64 size) const noexcept { | ||
| 25 | return iterator < end && begin < iterator + size; | ||
| 26 | } | ||
| 27 | }; | ||
| 28 | |||
| 29 | [[nodiscard]] u64 AllocationChunkSize(u64 required_size) { | ||
| 30 | static constexpr std::array sizes{ | ||
| 31 | 0x1000ULL << 10, 0x1400ULL << 10, 0x1800ULL << 10, 0x1c00ULL << 10, 0x2000ULL << 10, | ||
| 32 | 0x3200ULL << 10, 0x4000ULL << 10, 0x6000ULL << 10, 0x8000ULL << 10, 0xA000ULL << 10, | ||
| 33 | 0x10000ULL << 10, 0x18000ULL << 10, 0x20000ULL << 10, | ||
| 34 | }; | ||
| 35 | static_assert(std::is_sorted(sizes.begin(), sizes.end())); | ||
| 36 | |||
| 37 | const auto it = std::ranges::lower_bound(sizes, required_size); | ||
| 38 | return it != sizes.end() ? *it : Common::AlignUp(required_size, 4ULL << 20); | ||
| 39 | } | ||
| 40 | |||
| 41 | [[nodiscard]] VkMemoryPropertyFlags MemoryUsagePropertyFlags(MemoryUsage usage) { | ||
| 42 | switch (usage) { | ||
| 43 | case MemoryUsage::DeviceLocal: | ||
| 44 | return VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; | ||
| 45 | case MemoryUsage::Upload: | ||
| 46 | return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; | ||
| 47 | case MemoryUsage::Download: | ||
| 48 | return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | | ||
| 49 | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; | ||
| 50 | } | ||
| 51 | UNREACHABLE_MSG("Invalid memory usage={}", usage); | ||
| 52 | return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; | ||
| 53 | } | ||
| 54 | } // Anonymous namespace | ||
| 55 | |||
| 56 | class MemoryAllocation { | ||
| 57 | public: | ||
| 58 | explicit MemoryAllocation(const Device& device_, vk::DeviceMemory memory_, | ||
| 59 | VkMemoryPropertyFlags properties, u64 allocation_size_, u32 type) | ||
| 60 | : device{device_}, memory{std::move(memory_)}, allocation_size{allocation_size_}, | ||
| 61 | property_flags{properties}, shifted_memory_type{1U << type} {} | ||
| 62 | |||
| 63 | [[nodiscard]] std::optional<MemoryCommit> Commit(VkDeviceSize size, VkDeviceSize alignment) { | ||
| 64 | const std::optional<u64> alloc = FindFreeRegion(size, alignment); | ||
| 65 | if (!alloc) { | ||
| 66 | // Signal out of memory, it'll try to do more allocations. | ||
| 67 | return std::nullopt; | ||
| 68 | } | ||
| 69 | const Range range{ | ||
| 70 | .begin = *alloc, | ||
| 71 | .end = *alloc + size, | ||
| 72 | }; | ||
| 73 | commits.insert(std::ranges::upper_bound(commits, *alloc, {}, &Range::begin), range); | ||
| 74 | return std::make_optional<MemoryCommit>(this, *memory, *alloc, *alloc + size); | ||
| 75 | } | ||
| 76 | |||
| 77 | void Free(u64 begin) { | ||
| 78 | const auto it = std::ranges::find(commits, begin, &Range::begin); | ||
| 79 | ASSERT_MSG(it != commits.end(), "Invalid commit"); | ||
| 80 | commits.erase(it); | ||
| 81 | } | ||
| 82 | |||
| 83 | [[nodiscard]] std::span<u8> Map() { | ||
| 84 | if (memory_mapped_span.empty()) { | ||
| 85 | u8* const raw_pointer = memory.Map(0, allocation_size); | ||
| 86 | memory_mapped_span = std::span<u8>(raw_pointer, allocation_size); | ||
| 87 | } | ||
| 88 | return memory_mapped_span; | ||
| 89 | } | ||
| 90 | |||
| 91 | /// Returns whether this allocation is compatible with the arguments. | ||
| 92 | [[nodiscard]] bool IsCompatible(VkMemoryPropertyFlags flags, u32 type_mask) const { | ||
| 93 | return (flags & property_flags) && (type_mask & shifted_memory_type) != 0; | ||
| 94 | } | ||
| 95 | |||
| 96 | private: | ||
| 97 | [[nodiscard]] static constexpr u32 ShiftType(u32 type) { | ||
| 98 | return 1U << type; | ||
| 99 | } | ||
| 100 | |||
| 101 | [[nodiscard]] std::optional<u64> FindFreeRegion(u64 size, u64 alignment) noexcept { | ||
| 102 | ASSERT(std::has_single_bit(alignment)); | ||
| 103 | const u64 alignment_log2 = std::countr_zero(alignment); | ||
| 104 | std::optional<u64> candidate; | ||
| 105 | u64 iterator = 0; | ||
| 106 | auto commit = commits.begin(); | ||
| 107 | while (iterator + size <= allocation_size) { | ||
| 108 | candidate = candidate.value_or(iterator); | ||
| 109 | if (commit == commits.end()) { | ||
| 110 | break; | ||
| 111 | } | ||
| 112 | if (commit->Contains(*candidate, size)) { | ||
| 113 | candidate = std::nullopt; | ||
| 114 | } | ||
| 115 | iterator = Common::AlignUpLog2(commit->end, alignment_log2); | ||
| 116 | ++commit; | ||
| 117 | } | ||
| 118 | return candidate; | ||
| 119 | } | ||
| 120 | |||
| 121 | const Device& device; ///< Vulkan device. | ||
| 122 | const vk::DeviceMemory memory; ///< Vulkan memory allocation handler. | ||
| 123 | const u64 allocation_size; ///< Size of this allocation. | ||
| 124 | const VkMemoryPropertyFlags property_flags; ///< Vulkan memory property flags. | ||
| 125 | const u32 shifted_memory_type; ///< Shifted Vulkan memory type. | ||
| 126 | std::vector<Range> commits; ///< All commit ranges done from this allocation. | ||
| 127 | std::span<u8> memory_mapped_span; ///< Memory mapped span. Empty if not queried before. | ||
| 128 | }; | ||
| 129 | |||
| 130 | MemoryCommit::MemoryCommit(MemoryAllocation* allocation_, VkDeviceMemory memory_, u64 begin_, | ||
| 131 | u64 end_) noexcept | ||
| 132 | : allocation{allocation_}, memory{memory_}, begin{begin_}, end{end_} {} | ||
| 133 | |||
| 134 | MemoryCommit::~MemoryCommit() { | ||
| 135 | Release(); | ||
| 136 | } | ||
| 137 | |||
| 138 | MemoryCommit& MemoryCommit::operator=(MemoryCommit&& rhs) noexcept { | ||
| 139 | Release(); | ||
| 140 | allocation = std::exchange(rhs.allocation, nullptr); | ||
| 141 | memory = rhs.memory; | ||
| 142 | begin = rhs.begin; | ||
| 143 | end = rhs.end; | ||
| 144 | span = std::exchange(rhs.span, std::span<u8>{}); | ||
| 145 | return *this; | ||
| 146 | } | ||
| 147 | |||
| 148 | MemoryCommit::MemoryCommit(MemoryCommit&& rhs) noexcept | ||
| 149 | : allocation{std::exchange(rhs.allocation, nullptr)}, memory{rhs.memory}, begin{rhs.begin}, | ||
| 150 | end{rhs.end}, span{std::exchange(rhs.span, std::span<u8>{})} {} | ||
| 151 | |||
| 152 | std::span<u8> MemoryCommit::Map() { | ||
| 153 | if (span.empty()) { | ||
| 154 | span = allocation->Map().subspan(begin, end - begin); | ||
| 155 | } | ||
| 156 | return span; | ||
| 157 | } | ||
| 158 | |||
| 159 | void MemoryCommit::Release() { | ||
| 160 | if (allocation) { | ||
| 161 | allocation->Free(begin); | ||
| 162 | } | ||
| 163 | } | ||
| 164 | |||
| 165 | MemoryAllocator::MemoryAllocator(const Device& device_) | ||
| 166 | : device{device_}, properties{device_.GetPhysical().GetMemoryProperties()} {} | ||
| 167 | |||
| 168 | MemoryAllocator::~MemoryAllocator() = default; | ||
| 169 | |||
| 170 | MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements& requirements, MemoryUsage usage) { | ||
| 171 | // Find the fastest memory flags we can afford with the current requirements | ||
| 172 | const VkMemoryPropertyFlags flags = MemoryPropertyFlags(requirements.memoryTypeBits, usage); | ||
| 173 | if (std::optional<MemoryCommit> commit = TryCommit(requirements, flags)) { | ||
| 174 | return std::move(*commit); | ||
| 175 | } | ||
| 176 | // Commit has failed, allocate more memory. | ||
| 177 | // TODO(Rodrigo): Handle out of memory situations in some way like flushing to guest memory. | ||
| 178 | AllocMemory(flags, requirements.memoryTypeBits, AllocationChunkSize(requirements.size)); | ||
| 179 | |||
| 180 | // Commit again, this time it won't fail since there's a fresh allocation above. | ||
| 181 | // If it does, there's a bug. | ||
| 182 | return TryCommit(requirements, flags).value(); | ||
| 183 | } | ||
| 184 | |||
| 185 | MemoryCommit MemoryAllocator::Commit(const vk::Buffer& buffer, MemoryUsage usage) { | ||
| 186 | auto commit = Commit(device.GetLogical().GetBufferMemoryRequirements(*buffer), usage); | ||
| 187 | buffer.BindMemory(commit.Memory(), commit.Offset()); | ||
| 188 | return commit; | ||
| 189 | } | ||
| 190 | |||
| 191 | MemoryCommit MemoryAllocator::Commit(const vk::Image& image, MemoryUsage usage) { | ||
| 192 | auto commit = Commit(device.GetLogical().GetImageMemoryRequirements(*image), usage); | ||
| 193 | image.BindMemory(commit.Memory(), commit.Offset()); | ||
| 194 | return commit; | ||
| 195 | } | ||
| 196 | |||
| 197 | void MemoryAllocator::AllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size) { | ||
| 198 | const u32 type = FindType(flags, type_mask).value(); | ||
| 199 | vk::DeviceMemory memory = device.GetLogical().AllocateMemory({ | ||
| 200 | .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, | ||
| 201 | .pNext = nullptr, | ||
| 202 | .allocationSize = size, | ||
| 203 | .memoryTypeIndex = type, | ||
| 204 | }); | ||
| 205 | allocations.push_back( | ||
| 206 | std::make_unique<MemoryAllocation>(device, std::move(memory), flags, size, type)); | ||
| 207 | } | ||
| 208 | |||
| 209 | std::optional<MemoryCommit> MemoryAllocator::TryCommit(const VkMemoryRequirements& requirements, | ||
| 210 | VkMemoryPropertyFlags flags) { | ||
| 211 | for (auto& allocation : allocations) { | ||
| 212 | if (!allocation->IsCompatible(flags, requirements.memoryTypeBits)) { | ||
| 213 | continue; | ||
| 214 | } | ||
| 215 | if (auto commit = allocation->Commit(requirements.size, requirements.alignment)) { | ||
| 216 | return commit; | ||
| 217 | } | ||
| 218 | } | ||
| 219 | return std::nullopt; | ||
| 220 | } | ||
| 221 | |||
| 222 | VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask, MemoryUsage usage) const { | ||
| 223 | return MemoryPropertyFlags(type_mask, MemoryUsagePropertyFlags(usage)); | ||
| 224 | } | ||
| 225 | |||
| 226 | VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask, | ||
| 227 | VkMemoryPropertyFlags flags) const { | ||
| 228 | if (FindType(flags, type_mask)) { | ||
| 229 | // Found a memory type with those requirements | ||
| 230 | return flags; | ||
| 231 | } | ||
| 232 | if (flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) { | ||
| 233 | // Remove host cached bit in case it's not supported | ||
| 234 | return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_HOST_CACHED_BIT); | ||
| 235 | } | ||
| 236 | if (flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) { | ||
| 237 | // Remove device local, if it's not supported by the requested resource | ||
| 238 | return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); | ||
| 239 | } | ||
| 240 | UNREACHABLE_MSG("No compatible memory types found"); | ||
| 241 | return 0; | ||
| 242 | } | ||
| 243 | |||
| 244 | std::optional<u32> MemoryAllocator::FindType(VkMemoryPropertyFlags flags, u32 type_mask) const { | ||
| 245 | for (u32 type_index = 0; type_index < properties.memoryTypeCount; ++type_index) { | ||
| 246 | const VkMemoryPropertyFlags type_flags = properties.memoryTypes[type_index].propertyFlags; | ||
| 247 | if ((type_mask & (1U << type_index)) && (type_flags & flags)) { | ||
| 248 | // The type matches in type and in the wanted properties. | ||
| 249 | return type_index; | ||
| 250 | } | ||
| 251 | } | ||
| 252 | // Failed to find index | ||
| 253 | return std::nullopt; | ||
| 254 | } | ||
| 255 | |||
| 256 | bool IsHostVisible(MemoryUsage usage) noexcept { | ||
| 257 | switch (usage) { | ||
| 258 | case MemoryUsage::DeviceLocal: | ||
| 259 | return false; | ||
| 260 | case MemoryUsage::Upload: | ||
| 261 | case MemoryUsage::Download: | ||
| 262 | return true; | ||
| 263 | } | ||
| 264 | UNREACHABLE_MSG("Invalid memory usage={}", usage); | ||
| 265 | return false; | ||
| 266 | } | ||
| 267 | |||
| 268 | } // namespace Vulkan | ||
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.h b/src/video_core/vulkan_common/vulkan_memory_allocator.h new file mode 100644 index 000000000..53b3b275a --- /dev/null +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.h | |||
| @@ -0,0 +1,118 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <span> | ||
| 9 | #include <utility> | ||
| 10 | #include <vector> | ||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 13 | |||
| 14 | namespace Vulkan { | ||
| 15 | |||
| 16 | class Device; | ||
| 17 | class MemoryMap; | ||
| 18 | class MemoryAllocation; | ||
| 19 | |||
| 20 | /// Hints and requirements for the backing memory type of a commit | ||
| 21 | enum class MemoryUsage { | ||
| 22 | DeviceLocal, ///< Hints device local usages, fastest memory type to read and write from the GPU | ||
| 23 | Upload, ///< Requires a host visible memory type optimized for CPU to GPU uploads | ||
| 24 | Download, ///< Requires a host visible memory type optimized for GPU to CPU readbacks | ||
| 25 | }; | ||
| 26 | |||
| 27 | /// Ownership handle of a memory commitment. | ||
| 28 | /// Points to a subregion of a memory allocation. | ||
| 29 | class MemoryCommit { | ||
| 30 | public: | ||
| 31 | explicit MemoryCommit() noexcept = default; | ||
| 32 | explicit MemoryCommit(MemoryAllocation* allocation_, VkDeviceMemory memory_, u64 begin_, | ||
| 33 | u64 end_) noexcept; | ||
| 34 | ~MemoryCommit(); | ||
| 35 | |||
| 36 | MemoryCommit& operator=(MemoryCommit&&) noexcept; | ||
| 37 | MemoryCommit(MemoryCommit&&) noexcept; | ||
| 38 | |||
| 39 | MemoryCommit& operator=(const MemoryCommit&) = delete; | ||
| 40 | MemoryCommit(const MemoryCommit&) = delete; | ||
| 41 | |||
| 42 | /// Returns a host visible memory map. | ||
| 43 | /// It will map the backing allocation if it hasn't been mapped before. | ||
| 44 | std::span<u8> Map(); | ||
| 45 | |||
| 46 | /// Returns the Vulkan memory handler. | ||
| 47 | VkDeviceMemory Memory() const { | ||
| 48 | return memory; | ||
| 49 | } | ||
| 50 | |||
| 51 | /// Returns the start position of the commit relative to the allocation. | ||
| 52 | VkDeviceSize Offset() const { | ||
| 53 | return static_cast<VkDeviceSize>(begin); | ||
| 54 | } | ||
| 55 | |||
| 56 | private: | ||
| 57 | void Release(); | ||
| 58 | |||
| 59 | MemoryAllocation* allocation{}; ///< Pointer to the large memory allocation. | ||
| 60 | VkDeviceMemory memory{}; ///< Vulkan device memory handler. | ||
| 61 | u64 begin{}; ///< Beginning offset in bytes to where the commit exists. | ||
| 62 | u64 end{}; ///< Offset in bytes where the commit ends. | ||
| 63 | std::span<u8> span; ///< Host visible memory span. Empty if not queried before. | ||
| 64 | }; | ||
| 65 | |||
| 66 | /// Memory allocator container. | ||
| 67 | /// Allocates and releases memory allocations on demand. | ||
| 68 | class MemoryAllocator { | ||
| 69 | public: | ||
| 70 | explicit MemoryAllocator(const Device& device_); | ||
| 71 | ~MemoryAllocator(); | ||
| 72 | |||
| 73 | MemoryAllocator& operator=(const MemoryAllocator&) = delete; | ||
| 74 | MemoryAllocator(const MemoryAllocator&) = delete; | ||
| 75 | |||
| 76 | /** | ||
| 77 | * Commits a memory with the specified requeriments. | ||
| 78 | * | ||
| 79 | * @param requirements Requirements returned from a Vulkan call. | ||
| 80 | * @param host_visible Signals the allocator that it *must* use host visible and coherent | ||
| 81 | * memory. When passing false, it will try to allocate device local memory. | ||
| 82 | * | ||
| 83 | * @returns A memory commit. | ||
| 84 | */ | ||
| 85 | MemoryCommit Commit(const VkMemoryRequirements& requirements, MemoryUsage usage); | ||
| 86 | |||
| 87 | /// Commits memory required by the buffer and binds it. | ||
| 88 | MemoryCommit Commit(const vk::Buffer& buffer, MemoryUsage usage); | ||
| 89 | |||
| 90 | /// Commits memory required by the image and binds it. | ||
| 91 | MemoryCommit Commit(const vk::Image& image, MemoryUsage usage); | ||
| 92 | |||
| 93 | private: | ||
| 94 | /// Allocates a chunk of memory. | ||
| 95 | void AllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size); | ||
| 96 | |||
| 97 | /// Tries to allocate a memory commit. | ||
| 98 | std::optional<MemoryCommit> TryCommit(const VkMemoryRequirements& requirements, | ||
| 99 | VkMemoryPropertyFlags flags); | ||
| 100 | |||
| 101 | /// Returns the fastest compatible memory property flags from a wanted usage. | ||
| 102 | VkMemoryPropertyFlags MemoryPropertyFlags(u32 type_mask, MemoryUsage usage) const; | ||
| 103 | |||
| 104 | /// Returns the fastest compatible memory property flags from the wanted flags. | ||
| 105 | VkMemoryPropertyFlags MemoryPropertyFlags(u32 type_mask, VkMemoryPropertyFlags flags) const; | ||
| 106 | |||
| 107 | /// Returns index to the fastest memory type compatible with the passed requirements. | ||
| 108 | std::optional<u32> FindType(VkMemoryPropertyFlags flags, u32 type_mask) const; | ||
| 109 | |||
| 110 | const Device& device; ///< Device handle. | ||
| 111 | const VkPhysicalDeviceMemoryProperties properties; ///< Physical device properties. | ||
| 112 | std::vector<std::unique_ptr<MemoryAllocation>> allocations; ///< Current allocations. | ||
| 113 | }; | ||
| 114 | |||
| 115 | /// Returns true when a memory usage is guaranteed to be host visible. | ||
| 116 | bool IsHostVisible(MemoryUsage usage) noexcept; | ||
| 117 | |||
| 118 | } // namespace Vulkan | ||
diff --git a/src/video_core/vulkan_common/vulkan_surface.cpp b/src/video_core/vulkan_common/vulkan_surface.cpp new file mode 100644 index 000000000..3c3238f96 --- /dev/null +++ b/src/video_core/vulkan_common/vulkan_surface.cpp | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/logging/log.h" | ||
| 6 | #include "core/frontend/emu_window.h" | ||
| 7 | #include "video_core/vulkan_common/vulkan_surface.h" | ||
| 8 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 9 | |||
| 10 | // Include these late to avoid polluting previous headers | ||
| 11 | #ifdef _WIN32 | ||
| 12 | #include <windows.h> | ||
| 13 | // ensure include order | ||
| 14 | #include <vulkan/vulkan_win32.h> | ||
| 15 | #endif | ||
| 16 | |||
| 17 | #if !defined(_WIN32) && !defined(__APPLE__) | ||
| 18 | #include <X11/Xlib.h> | ||
| 19 | #include <vulkan/vulkan_wayland.h> | ||
| 20 | #include <vulkan/vulkan_xlib.h> | ||
| 21 | #endif | ||
| 22 | |||
| 23 | namespace Vulkan { | ||
| 24 | |||
| 25 | vk::SurfaceKHR CreateSurface(const vk::Instance& instance, | ||
| 26 | const Core::Frontend::EmuWindow& emu_window) { | ||
| 27 | [[maybe_unused]] const vk::InstanceDispatch& dld = instance.Dispatch(); | ||
| 28 | [[maybe_unused]] const auto& window_info = emu_window.GetWindowInfo(); | ||
| 29 | VkSurfaceKHR unsafe_surface = nullptr; | ||
| 30 | |||
| 31 | #ifdef _WIN32 | ||
| 32 | if (window_info.type == Core::Frontend::WindowSystemType::Windows) { | ||
| 33 | const HWND hWnd = static_cast<HWND>(window_info.render_surface); | ||
| 34 | const VkWin32SurfaceCreateInfoKHR win32_ci{VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, | ||
| 35 | nullptr, 0, nullptr, hWnd}; | ||
| 36 | const auto vkCreateWin32SurfaceKHR = reinterpret_cast<PFN_vkCreateWin32SurfaceKHR>( | ||
| 37 | dld.vkGetInstanceProcAddr(*instance, "vkCreateWin32SurfaceKHR")); | ||
| 38 | if (!vkCreateWin32SurfaceKHR || | ||
| 39 | vkCreateWin32SurfaceKHR(*instance, &win32_ci, nullptr, &unsafe_surface) != VK_SUCCESS) { | ||
| 40 | LOG_ERROR(Render_Vulkan, "Failed to initialize Win32 surface"); | ||
| 41 | throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); | ||
| 42 | } | ||
| 43 | } | ||
| 44 | #endif | ||
| 45 | #if !defined(_WIN32) && !defined(__APPLE__) | ||
| 46 | if (window_info.type == Core::Frontend::WindowSystemType::X11) { | ||
| 47 | const VkXlibSurfaceCreateInfoKHR xlib_ci{ | ||
| 48 | VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, nullptr, 0, | ||
| 49 | static_cast<Display*>(window_info.display_connection), | ||
| 50 | reinterpret_cast<Window>(window_info.render_surface)}; | ||
| 51 | const auto vkCreateXlibSurfaceKHR = reinterpret_cast<PFN_vkCreateXlibSurfaceKHR>( | ||
| 52 | dld.vkGetInstanceProcAddr(*instance, "vkCreateXlibSurfaceKHR")); | ||
| 53 | if (!vkCreateXlibSurfaceKHR || | ||
| 54 | vkCreateXlibSurfaceKHR(*instance, &xlib_ci, nullptr, &unsafe_surface) != VK_SUCCESS) { | ||
| 55 | LOG_ERROR(Render_Vulkan, "Failed to initialize Xlib surface"); | ||
| 56 | throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | if (window_info.type == Core::Frontend::WindowSystemType::Wayland) { | ||
| 60 | const VkWaylandSurfaceCreateInfoKHR wayland_ci{ | ||
| 61 | VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, nullptr, 0, | ||
| 62 | static_cast<wl_display*>(window_info.display_connection), | ||
| 63 | static_cast<wl_surface*>(window_info.render_surface)}; | ||
| 64 | const auto vkCreateWaylandSurfaceKHR = reinterpret_cast<PFN_vkCreateWaylandSurfaceKHR>( | ||
| 65 | dld.vkGetInstanceProcAddr(*instance, "vkCreateWaylandSurfaceKHR")); | ||
| 66 | if (!vkCreateWaylandSurfaceKHR || | ||
| 67 | vkCreateWaylandSurfaceKHR(*instance, &wayland_ci, nullptr, &unsafe_surface) != | ||
| 68 | VK_SUCCESS) { | ||
| 69 | LOG_ERROR(Render_Vulkan, "Failed to initialize Wayland surface"); | ||
| 70 | throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); | ||
| 71 | } | ||
| 72 | } | ||
| 73 | #endif | ||
| 74 | if (!unsafe_surface) { | ||
| 75 | LOG_ERROR(Render_Vulkan, "Presentation not supported on this platform"); | ||
| 76 | throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); | ||
| 77 | } | ||
| 78 | return vk::SurfaceKHR(unsafe_surface, *instance, dld); | ||
| 79 | } | ||
| 80 | |||
| 81 | } // namespace Vulkan | ||
diff --git a/src/video_core/vulkan_common/vulkan_surface.h b/src/video_core/vulkan_common/vulkan_surface.h new file mode 100644 index 000000000..05a169e32 --- /dev/null +++ b/src/video_core/vulkan_common/vulkan_surface.h | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 8 | |||
| 9 | namespace Core::Frontend { | ||
| 10 | class EmuWindow; | ||
| 11 | } | ||
| 12 | |||
| 13 | namespace Vulkan { | ||
| 14 | |||
| 15 | [[nodiscard]] vk::SurfaceKHR CreateSurface(const vk::Instance& instance, | ||
| 16 | const Core::Frontend::EmuWindow& emu_window); | ||
| 17 | |||
| 18 | } // namespace Vulkan | ||
diff --git a/src/video_core/renderer_vulkan/wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index 2a21e850d..5e15ad607 100644 --- a/src/video_core/renderer_vulkan/wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp | |||
| @@ -13,7 +13,7 @@ | |||
| 13 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 14 | #include "common/logging/log.h" | 14 | #include "common/logging/log.h" |
| 15 | 15 | ||
| 16 | #include "video_core/renderer_vulkan/wrapper.h" | 16 | #include "video_core/vulkan_common/vulkan_wrapper.h" |
| 17 | 17 | ||
| 18 | namespace Vulkan::vk { | 18 | namespace Vulkan::vk { |
| 19 | 19 | ||
| @@ -435,7 +435,7 @@ VkResult Free(VkDevice device, VkCommandPool handle, Span<VkCommandBuffer> buffe | |||
| 435 | } | 435 | } |
| 436 | 436 | ||
| 437 | Instance Instance::Create(u32 version, Span<const char*> layers, Span<const char*> extensions, | 437 | Instance Instance::Create(u32 version, Span<const char*> layers, Span<const char*> extensions, |
| 438 | InstanceDispatch& dispatch) noexcept { | 438 | InstanceDispatch& dispatch) { |
| 439 | const VkApplicationInfo application_info{ | 439 | const VkApplicationInfo application_info{ |
| 440 | .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, | 440 | .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, |
| 441 | .pNext = nullptr, | 441 | .pNext = nullptr, |
| @@ -455,55 +455,30 @@ Instance Instance::Create(u32 version, Span<const char*> layers, Span<const char | |||
| 455 | .enabledExtensionCount = extensions.size(), | 455 | .enabledExtensionCount = extensions.size(), |
| 456 | .ppEnabledExtensionNames = extensions.data(), | 456 | .ppEnabledExtensionNames = extensions.data(), |
| 457 | }; | 457 | }; |
| 458 | |||
| 459 | VkInstance instance; | 458 | VkInstance instance; |
| 460 | if (dispatch.vkCreateInstance(&ci, nullptr, &instance) != VK_SUCCESS) { | 459 | Check(dispatch.vkCreateInstance(&ci, nullptr, &instance)); |
| 461 | // Failed to create the instance. | ||
| 462 | return {}; | ||
| 463 | } | ||
| 464 | if (!Proc(dispatch.vkDestroyInstance, dispatch, "vkDestroyInstance", instance)) { | 460 | if (!Proc(dispatch.vkDestroyInstance, dispatch, "vkDestroyInstance", instance)) { |
| 465 | // We successfully created an instance but the destroy function couldn't be loaded. | 461 | // We successfully created an instance but the destroy function couldn't be loaded. |
| 466 | // This is a good moment to panic. | 462 | // This is a good moment to panic. |
| 467 | return {}; | 463 | throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); |
| 468 | } | 464 | } |
| 469 | |||
| 470 | return Instance(instance, dispatch); | 465 | return Instance(instance, dispatch); |
| 471 | } | 466 | } |
| 472 | 467 | ||
| 473 | std::optional<std::vector<VkPhysicalDevice>> Instance::EnumeratePhysicalDevices() { | 468 | std::vector<VkPhysicalDevice> Instance::EnumeratePhysicalDevices() const { |
| 474 | u32 num; | 469 | u32 num; |
| 475 | if (dld->vkEnumeratePhysicalDevices(handle, &num, nullptr) != VK_SUCCESS) { | 470 | Check(dld->vkEnumeratePhysicalDevices(handle, &num, nullptr)); |
| 476 | return std::nullopt; | ||
| 477 | } | ||
| 478 | std::vector<VkPhysicalDevice> physical_devices(num); | 471 | std::vector<VkPhysicalDevice> physical_devices(num); |
| 479 | if (dld->vkEnumeratePhysicalDevices(handle, &num, physical_devices.data()) != VK_SUCCESS) { | 472 | Check(dld->vkEnumeratePhysicalDevices(handle, &num, physical_devices.data())); |
| 480 | return std::nullopt; | ||
| 481 | } | ||
| 482 | SortPhysicalDevices(physical_devices, *dld); | 473 | SortPhysicalDevices(physical_devices, *dld); |
| 483 | return std::make_optional(std::move(physical_devices)); | 474 | return physical_devices; |
| 484 | } | 475 | } |
| 485 | 476 | ||
| 486 | DebugCallback Instance::TryCreateDebugCallback( | 477 | DebugUtilsMessenger Instance::CreateDebugUtilsMessenger( |
| 487 | PFN_vkDebugUtilsMessengerCallbackEXT callback) noexcept { | 478 | const VkDebugUtilsMessengerCreateInfoEXT& create_info) const { |
| 488 | const VkDebugUtilsMessengerCreateInfoEXT ci{ | 479 | VkDebugUtilsMessengerEXT object; |
| 489 | .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, | 480 | Check(dld->vkCreateDebugUtilsMessengerEXT(handle, &create_info, nullptr, &object)); |
| 490 | .pNext = nullptr, | 481 | return DebugUtilsMessenger(object, handle, *dld); |
| 491 | .flags = 0, | ||
| 492 | .messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | | ||
| 493 | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | | ||
| 494 | VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | | ||
| 495 | VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT, | ||
| 496 | .messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | | ||
| 497 | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT, | ||
| 498 | .pfnUserCallback = callback, | ||
| 499 | .pUserData = nullptr, | ||
| 500 | }; | ||
| 501 | |||
| 502 | VkDebugUtilsMessengerEXT messenger; | ||
| 503 | if (dld->vkCreateDebugUtilsMessengerEXT(handle, &ci, nullptr, &messenger) != VK_SUCCESS) { | ||
| 504 | return {}; | ||
| 505 | } | ||
| 506 | return DebugCallback(messenger, handle, *dld); | ||
| 507 | } | 482 | } |
| 508 | 483 | ||
| 509 | void Buffer::BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const { | 484 | void Buffer::BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const { |
| @@ -605,7 +580,7 @@ void Semaphore::SetObjectNameEXT(const char* name) const { | |||
| 605 | 580 | ||
| 606 | Device Device::Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci, | 581 | Device Device::Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci, |
| 607 | Span<const char*> enabled_extensions, const void* next, | 582 | Span<const char*> enabled_extensions, const void* next, |
| 608 | DeviceDispatch& dispatch) noexcept { | 583 | DeviceDispatch& dispatch) { |
| 609 | const VkDeviceCreateInfo ci{ | 584 | const VkDeviceCreateInfo ci{ |
| 610 | .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, | 585 | .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, |
| 611 | .pNext = next, | 586 | .pNext = next, |
| @@ -618,11 +593,8 @@ Device Device::Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreate | |||
| 618 | .ppEnabledExtensionNames = enabled_extensions.data(), | 593 | .ppEnabledExtensionNames = enabled_extensions.data(), |
| 619 | .pEnabledFeatures = nullptr, | 594 | .pEnabledFeatures = nullptr, |
| 620 | }; | 595 | }; |
| 621 | |||
| 622 | VkDevice device; | 596 | VkDevice device; |
| 623 | if (dispatch.vkCreateDevice(physical_device, &ci, nullptr, &device) != VK_SUCCESS) { | 597 | Check(dispatch.vkCreateDevice(physical_device, &ci, nullptr, &device)); |
| 624 | return {}; | ||
| 625 | } | ||
| 626 | Load(device, dispatch); | 598 | Load(device, dispatch); |
| 627 | return Device(device, dispatch); | 599 | return Device(device, dispatch); |
| 628 | } | 600 | } |
diff --git a/src/video_core/renderer_vulkan/wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index f9a184e00..9689de0cb 100644 --- a/src/video_core/renderer_vulkan/wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h | |||
| @@ -144,152 +144,152 @@ inline VkResult Filter(VkResult result) { | |||
| 144 | 144 | ||
| 145 | /// Table holding Vulkan instance function pointers. | 145 | /// Table holding Vulkan instance function pointers. |
| 146 | struct InstanceDispatch { | 146 | struct InstanceDispatch { |
| 147 | PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; | 147 | PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{}; |
| 148 | 148 | ||
| 149 | PFN_vkCreateInstance vkCreateInstance; | 149 | PFN_vkCreateInstance vkCreateInstance{}; |
| 150 | PFN_vkDestroyInstance vkDestroyInstance; | 150 | PFN_vkDestroyInstance vkDestroyInstance{}; |
| 151 | PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties; | 151 | PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties{}; |
| 152 | PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties; | 152 | PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties{}; |
| 153 | 153 | ||
| 154 | PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT; | 154 | PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT{}; |
| 155 | PFN_vkCreateDevice vkCreateDevice; | 155 | PFN_vkCreateDevice vkCreateDevice{}; |
| 156 | PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT; | 156 | PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT{}; |
| 157 | PFN_vkDestroyDevice vkDestroyDevice; | 157 | PFN_vkDestroyDevice vkDestroyDevice{}; |
| 158 | PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR; | 158 | PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR{}; |
| 159 | PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties; | 159 | PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties{}; |
| 160 | PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices; | 160 | PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices{}; |
| 161 | PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr; | 161 | PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr{}; |
| 162 | PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR; | 162 | PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR{}; |
| 163 | PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties; | 163 | PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties{}; |
| 164 | PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties; | 164 | PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties{}; |
| 165 | PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties; | 165 | PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties{}; |
| 166 | PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR; | 166 | PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR{}; |
| 167 | PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties; | 167 | PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties{}; |
| 168 | PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR; | 168 | PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR{}; |
| 169 | PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR; | 169 | PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR{}; |
| 170 | PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR; | 170 | PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR{}; |
| 171 | PFN_vkGetPhysicalDeviceSurfaceSupportKHR vkGetPhysicalDeviceSurfaceSupportKHR; | 171 | PFN_vkGetPhysicalDeviceSurfaceSupportKHR vkGetPhysicalDeviceSurfaceSupportKHR{}; |
| 172 | PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR; | 172 | PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR{}; |
| 173 | PFN_vkQueuePresentKHR vkQueuePresentKHR; | 173 | PFN_vkQueuePresentKHR vkQueuePresentKHR{}; |
| 174 | }; | 174 | }; |
| 175 | 175 | ||
| 176 | /// Table holding Vulkan device function pointers. | 176 | /// Table holding Vulkan device function pointers. |
| 177 | struct DeviceDispatch : public InstanceDispatch { | 177 | struct DeviceDispatch : public InstanceDispatch { |
| 178 | PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR; | 178 | PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR{}; |
| 179 | PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers; | 179 | PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers{}; |
| 180 | PFN_vkAllocateDescriptorSets vkAllocateDescriptorSets; | 180 | PFN_vkAllocateDescriptorSets vkAllocateDescriptorSets{}; |
| 181 | PFN_vkAllocateMemory vkAllocateMemory; | 181 | PFN_vkAllocateMemory vkAllocateMemory{}; |
| 182 | PFN_vkBeginCommandBuffer vkBeginCommandBuffer; | 182 | PFN_vkBeginCommandBuffer vkBeginCommandBuffer{}; |
| 183 | PFN_vkBindBufferMemory vkBindBufferMemory; | 183 | PFN_vkBindBufferMemory vkBindBufferMemory{}; |
| 184 | PFN_vkBindImageMemory vkBindImageMemory; | 184 | PFN_vkBindImageMemory vkBindImageMemory{}; |
| 185 | PFN_vkCmdBeginQuery vkCmdBeginQuery; | 185 | PFN_vkCmdBeginQuery vkCmdBeginQuery{}; |
| 186 | PFN_vkCmdBeginRenderPass vkCmdBeginRenderPass; | 186 | PFN_vkCmdBeginRenderPass vkCmdBeginRenderPass{}; |
| 187 | PFN_vkCmdBeginTransformFeedbackEXT vkCmdBeginTransformFeedbackEXT; | 187 | PFN_vkCmdBeginTransformFeedbackEXT vkCmdBeginTransformFeedbackEXT{}; |
| 188 | PFN_vkCmdBeginDebugUtilsLabelEXT vkCmdBeginDebugUtilsLabelEXT; | 188 | PFN_vkCmdBeginDebugUtilsLabelEXT vkCmdBeginDebugUtilsLabelEXT{}; |
| 189 | PFN_vkCmdBindDescriptorSets vkCmdBindDescriptorSets; | 189 | PFN_vkCmdBindDescriptorSets vkCmdBindDescriptorSets{}; |
| 190 | PFN_vkCmdBindIndexBuffer vkCmdBindIndexBuffer; | 190 | PFN_vkCmdBindIndexBuffer vkCmdBindIndexBuffer{}; |
| 191 | PFN_vkCmdBindPipeline vkCmdBindPipeline; | 191 | PFN_vkCmdBindPipeline vkCmdBindPipeline{}; |
| 192 | PFN_vkCmdBindTransformFeedbackBuffersEXT vkCmdBindTransformFeedbackBuffersEXT; | 192 | PFN_vkCmdBindTransformFeedbackBuffersEXT vkCmdBindTransformFeedbackBuffersEXT{}; |
| 193 | PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers; | 193 | PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers{}; |
| 194 | PFN_vkCmdBlitImage vkCmdBlitImage; | 194 | PFN_vkCmdBlitImage vkCmdBlitImage{}; |
| 195 | PFN_vkCmdClearAttachments vkCmdClearAttachments; | 195 | PFN_vkCmdClearAttachments vkCmdClearAttachments{}; |
| 196 | PFN_vkCmdCopyBuffer vkCmdCopyBuffer; | 196 | PFN_vkCmdCopyBuffer vkCmdCopyBuffer{}; |
| 197 | PFN_vkCmdCopyBufferToImage vkCmdCopyBufferToImage; | 197 | PFN_vkCmdCopyBufferToImage vkCmdCopyBufferToImage{}; |
| 198 | PFN_vkCmdCopyImage vkCmdCopyImage; | 198 | PFN_vkCmdCopyImage vkCmdCopyImage{}; |
| 199 | PFN_vkCmdCopyImageToBuffer vkCmdCopyImageToBuffer; | 199 | PFN_vkCmdCopyImageToBuffer vkCmdCopyImageToBuffer{}; |
| 200 | PFN_vkCmdDispatch vkCmdDispatch; | 200 | PFN_vkCmdDispatch vkCmdDispatch{}; |
| 201 | PFN_vkCmdDraw vkCmdDraw; | 201 | PFN_vkCmdDraw vkCmdDraw{}; |
| 202 | PFN_vkCmdDrawIndexed vkCmdDrawIndexed; | 202 | PFN_vkCmdDrawIndexed vkCmdDrawIndexed{}; |
| 203 | PFN_vkCmdEndQuery vkCmdEndQuery; | 203 | PFN_vkCmdEndQuery vkCmdEndQuery{}; |
| 204 | PFN_vkCmdEndRenderPass vkCmdEndRenderPass; | 204 | PFN_vkCmdEndRenderPass vkCmdEndRenderPass{}; |
| 205 | PFN_vkCmdEndTransformFeedbackEXT vkCmdEndTransformFeedbackEXT; | 205 | PFN_vkCmdEndTransformFeedbackEXT vkCmdEndTransformFeedbackEXT{}; |
| 206 | PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabelEXT; | 206 | PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabelEXT{}; |
| 207 | PFN_vkCmdFillBuffer vkCmdFillBuffer; | 207 | PFN_vkCmdFillBuffer vkCmdFillBuffer{}; |
| 208 | PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier; | 208 | PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier{}; |
| 209 | PFN_vkCmdPushConstants vkCmdPushConstants; | 209 | PFN_vkCmdPushConstants vkCmdPushConstants{}; |
| 210 | PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants; | 210 | PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants{}; |
| 211 | PFN_vkCmdSetDepthBias vkCmdSetDepthBias; | 211 | PFN_vkCmdSetDepthBias vkCmdSetDepthBias{}; |
| 212 | PFN_vkCmdSetDepthBounds vkCmdSetDepthBounds; | 212 | PFN_vkCmdSetDepthBounds vkCmdSetDepthBounds{}; |
| 213 | PFN_vkCmdSetEvent vkCmdSetEvent; | 213 | PFN_vkCmdSetEvent vkCmdSetEvent{}; |
| 214 | PFN_vkCmdSetScissor vkCmdSetScissor; | 214 | PFN_vkCmdSetScissor vkCmdSetScissor{}; |
| 215 | PFN_vkCmdSetStencilCompareMask vkCmdSetStencilCompareMask; | 215 | PFN_vkCmdSetStencilCompareMask vkCmdSetStencilCompareMask{}; |
| 216 | PFN_vkCmdSetStencilReference vkCmdSetStencilReference; | 216 | PFN_vkCmdSetStencilReference vkCmdSetStencilReference{}; |
| 217 | PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask; | 217 | PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask{}; |
| 218 | PFN_vkCmdSetViewport vkCmdSetViewport; | 218 | PFN_vkCmdSetViewport vkCmdSetViewport{}; |
| 219 | PFN_vkCmdWaitEvents vkCmdWaitEvents; | 219 | PFN_vkCmdWaitEvents vkCmdWaitEvents{}; |
| 220 | PFN_vkCmdBindVertexBuffers2EXT vkCmdBindVertexBuffers2EXT; | 220 | PFN_vkCmdBindVertexBuffers2EXT vkCmdBindVertexBuffers2EXT{}; |
| 221 | PFN_vkCmdSetCullModeEXT vkCmdSetCullModeEXT; | 221 | PFN_vkCmdSetCullModeEXT vkCmdSetCullModeEXT{}; |
| 222 | PFN_vkCmdSetDepthBoundsTestEnableEXT vkCmdSetDepthBoundsTestEnableEXT; | 222 | PFN_vkCmdSetDepthBoundsTestEnableEXT vkCmdSetDepthBoundsTestEnableEXT{}; |
| 223 | PFN_vkCmdSetDepthCompareOpEXT vkCmdSetDepthCompareOpEXT; | 223 | PFN_vkCmdSetDepthCompareOpEXT vkCmdSetDepthCompareOpEXT{}; |
| 224 | PFN_vkCmdSetDepthTestEnableEXT vkCmdSetDepthTestEnableEXT; | 224 | PFN_vkCmdSetDepthTestEnableEXT vkCmdSetDepthTestEnableEXT{}; |
| 225 | PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT; | 225 | PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT{}; |
| 226 | PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT; | 226 | PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT{}; |
| 227 | PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT; | 227 | PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT{}; |
| 228 | PFN_vkCmdSetStencilOpEXT vkCmdSetStencilOpEXT; | 228 | PFN_vkCmdSetStencilOpEXT vkCmdSetStencilOpEXT{}; |
| 229 | PFN_vkCmdSetStencilTestEnableEXT vkCmdSetStencilTestEnableEXT; | 229 | PFN_vkCmdSetStencilTestEnableEXT vkCmdSetStencilTestEnableEXT{}; |
| 230 | PFN_vkCmdResolveImage vkCmdResolveImage; | 230 | PFN_vkCmdResolveImage vkCmdResolveImage{}; |
| 231 | PFN_vkCreateBuffer vkCreateBuffer; | 231 | PFN_vkCreateBuffer vkCreateBuffer{}; |
| 232 | PFN_vkCreateBufferView vkCreateBufferView; | 232 | PFN_vkCreateBufferView vkCreateBufferView{}; |
| 233 | PFN_vkCreateCommandPool vkCreateCommandPool; | 233 | PFN_vkCreateCommandPool vkCreateCommandPool{}; |
| 234 | PFN_vkCreateComputePipelines vkCreateComputePipelines; | 234 | PFN_vkCreateComputePipelines vkCreateComputePipelines{}; |
| 235 | PFN_vkCreateDescriptorPool vkCreateDescriptorPool; | 235 | PFN_vkCreateDescriptorPool vkCreateDescriptorPool{}; |
| 236 | PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout; | 236 | PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout{}; |
| 237 | PFN_vkCreateDescriptorUpdateTemplateKHR vkCreateDescriptorUpdateTemplateKHR; | 237 | PFN_vkCreateDescriptorUpdateTemplateKHR vkCreateDescriptorUpdateTemplateKHR{}; |
| 238 | PFN_vkCreateEvent vkCreateEvent; | 238 | PFN_vkCreateEvent vkCreateEvent{}; |
| 239 | PFN_vkCreateFence vkCreateFence; | 239 | PFN_vkCreateFence vkCreateFence{}; |
| 240 | PFN_vkCreateFramebuffer vkCreateFramebuffer; | 240 | PFN_vkCreateFramebuffer vkCreateFramebuffer{}; |
| 241 | PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines; | 241 | PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines{}; |
| 242 | PFN_vkCreateImage vkCreateImage; | 242 | PFN_vkCreateImage vkCreateImage{}; |
| 243 | PFN_vkCreateImageView vkCreateImageView; | 243 | PFN_vkCreateImageView vkCreateImageView{}; |
| 244 | PFN_vkCreatePipelineLayout vkCreatePipelineLayout; | 244 | PFN_vkCreatePipelineLayout vkCreatePipelineLayout{}; |
| 245 | PFN_vkCreateQueryPool vkCreateQueryPool; | 245 | PFN_vkCreateQueryPool vkCreateQueryPool{}; |
| 246 | PFN_vkCreateRenderPass vkCreateRenderPass; | 246 | PFN_vkCreateRenderPass vkCreateRenderPass{}; |
| 247 | PFN_vkCreateSampler vkCreateSampler; | 247 | PFN_vkCreateSampler vkCreateSampler{}; |
| 248 | PFN_vkCreateSemaphore vkCreateSemaphore; | 248 | PFN_vkCreateSemaphore vkCreateSemaphore{}; |
| 249 | PFN_vkCreateShaderModule vkCreateShaderModule; | 249 | PFN_vkCreateShaderModule vkCreateShaderModule{}; |
| 250 | PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR; | 250 | PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR{}; |
| 251 | PFN_vkDestroyBuffer vkDestroyBuffer; | 251 | PFN_vkDestroyBuffer vkDestroyBuffer{}; |
| 252 | PFN_vkDestroyBufferView vkDestroyBufferView; | 252 | PFN_vkDestroyBufferView vkDestroyBufferView{}; |
| 253 | PFN_vkDestroyCommandPool vkDestroyCommandPool; | 253 | PFN_vkDestroyCommandPool vkDestroyCommandPool{}; |
| 254 | PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool; | 254 | PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool{}; |
| 255 | PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout; | 255 | PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout{}; |
| 256 | PFN_vkDestroyDescriptorUpdateTemplateKHR vkDestroyDescriptorUpdateTemplateKHR; | 256 | PFN_vkDestroyDescriptorUpdateTemplateKHR vkDestroyDescriptorUpdateTemplateKHR{}; |
| 257 | PFN_vkDestroyEvent vkDestroyEvent; | 257 | PFN_vkDestroyEvent vkDestroyEvent{}; |
| 258 | PFN_vkDestroyFence vkDestroyFence; | 258 | PFN_vkDestroyFence vkDestroyFence{}; |
| 259 | PFN_vkDestroyFramebuffer vkDestroyFramebuffer; | 259 | PFN_vkDestroyFramebuffer vkDestroyFramebuffer{}; |
| 260 | PFN_vkDestroyImage vkDestroyImage; | 260 | PFN_vkDestroyImage vkDestroyImage{}; |
| 261 | PFN_vkDestroyImageView vkDestroyImageView; | 261 | PFN_vkDestroyImageView vkDestroyImageView{}; |
| 262 | PFN_vkDestroyPipeline vkDestroyPipeline; | 262 | PFN_vkDestroyPipeline vkDestroyPipeline{}; |
| 263 | PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout; | 263 | PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout{}; |
| 264 | PFN_vkDestroyQueryPool vkDestroyQueryPool; | 264 | PFN_vkDestroyQueryPool vkDestroyQueryPool{}; |
| 265 | PFN_vkDestroyRenderPass vkDestroyRenderPass; | 265 | PFN_vkDestroyRenderPass vkDestroyRenderPass{}; |
| 266 | PFN_vkDestroySampler vkDestroySampler; | 266 | PFN_vkDestroySampler vkDestroySampler{}; |
| 267 | PFN_vkDestroySemaphore vkDestroySemaphore; | 267 | PFN_vkDestroySemaphore vkDestroySemaphore{}; |
| 268 | PFN_vkDestroyShaderModule vkDestroyShaderModule; | 268 | PFN_vkDestroyShaderModule vkDestroyShaderModule{}; |
| 269 | PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR; | 269 | PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR{}; |
| 270 | PFN_vkDeviceWaitIdle vkDeviceWaitIdle; | 270 | PFN_vkDeviceWaitIdle vkDeviceWaitIdle{}; |
| 271 | PFN_vkEndCommandBuffer vkEndCommandBuffer; | 271 | PFN_vkEndCommandBuffer vkEndCommandBuffer{}; |
| 272 | PFN_vkFreeCommandBuffers vkFreeCommandBuffers; | 272 | PFN_vkFreeCommandBuffers vkFreeCommandBuffers{}; |
| 273 | PFN_vkFreeDescriptorSets vkFreeDescriptorSets; | 273 | PFN_vkFreeDescriptorSets vkFreeDescriptorSets{}; |
| 274 | PFN_vkFreeMemory vkFreeMemory; | 274 | PFN_vkFreeMemory vkFreeMemory{}; |
| 275 | PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements; | 275 | PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements{}; |
| 276 | PFN_vkGetDeviceQueue vkGetDeviceQueue; | 276 | PFN_vkGetDeviceQueue vkGetDeviceQueue{}; |
| 277 | PFN_vkGetEventStatus vkGetEventStatus; | 277 | PFN_vkGetEventStatus vkGetEventStatus{}; |
| 278 | PFN_vkGetFenceStatus vkGetFenceStatus; | 278 | PFN_vkGetFenceStatus vkGetFenceStatus{}; |
| 279 | PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements; | 279 | PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements{}; |
| 280 | PFN_vkGetQueryPoolResults vkGetQueryPoolResults; | 280 | PFN_vkGetQueryPoolResults vkGetQueryPoolResults{}; |
| 281 | PFN_vkGetSemaphoreCounterValueKHR vkGetSemaphoreCounterValueKHR; | 281 | PFN_vkGetSemaphoreCounterValueKHR vkGetSemaphoreCounterValueKHR{}; |
| 282 | PFN_vkMapMemory vkMapMemory; | 282 | PFN_vkMapMemory vkMapMemory{}; |
| 283 | PFN_vkQueueSubmit vkQueueSubmit; | 283 | PFN_vkQueueSubmit vkQueueSubmit{}; |
| 284 | PFN_vkResetFences vkResetFences; | 284 | PFN_vkResetFences vkResetFences{}; |
| 285 | PFN_vkResetQueryPoolEXT vkResetQueryPoolEXT; | 285 | PFN_vkResetQueryPoolEXT vkResetQueryPoolEXT{}; |
| 286 | PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT; | 286 | PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT{}; |
| 287 | PFN_vkSetDebugUtilsObjectTagEXT vkSetDebugUtilsObjectTagEXT; | 287 | PFN_vkSetDebugUtilsObjectTagEXT vkSetDebugUtilsObjectTagEXT{}; |
| 288 | PFN_vkUnmapMemory vkUnmapMemory; | 288 | PFN_vkUnmapMemory vkUnmapMemory{}; |
| 289 | PFN_vkUpdateDescriptorSetWithTemplateKHR vkUpdateDescriptorSetWithTemplateKHR; | 289 | PFN_vkUpdateDescriptorSetWithTemplateKHR vkUpdateDescriptorSetWithTemplateKHR{}; |
| 290 | PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets; | 290 | PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets{}; |
| 291 | PFN_vkWaitForFences vkWaitForFences; | 291 | PFN_vkWaitForFences vkWaitForFences{}; |
| 292 | PFN_vkWaitSemaphoresKHR vkWaitSemaphoresKHR; | 292 | PFN_vkWaitSemaphoresKHR vkWaitSemaphoresKHR{}; |
| 293 | }; | 293 | }; |
| 294 | 294 | ||
| 295 | /// Loads instance agnostic function pointers. | 295 | /// Loads instance agnostic function pointers. |
| @@ -555,7 +555,7 @@ private: | |||
| 555 | const DeviceDispatch* dld = nullptr; | 555 | const DeviceDispatch* dld = nullptr; |
| 556 | }; | 556 | }; |
| 557 | 557 | ||
| 558 | using DebugCallback = Handle<VkDebugUtilsMessengerEXT, VkInstance, InstanceDispatch>; | 558 | using DebugUtilsMessenger = Handle<VkDebugUtilsMessengerEXT, VkInstance, InstanceDispatch>; |
| 559 | using DescriptorSetLayout = Handle<VkDescriptorSetLayout, VkDevice, DeviceDispatch>; | 559 | using DescriptorSetLayout = Handle<VkDescriptorSetLayout, VkDevice, DeviceDispatch>; |
| 560 | using DescriptorUpdateTemplateKHR = Handle<VkDescriptorUpdateTemplateKHR, VkDevice, DeviceDispatch>; | 560 | using DescriptorUpdateTemplateKHR = Handle<VkDescriptorUpdateTemplateKHR, VkDevice, DeviceDispatch>; |
| 561 | using Pipeline = Handle<VkPipeline, VkDevice, DeviceDispatch>; | 561 | using Pipeline = Handle<VkPipeline, VkDevice, DeviceDispatch>; |
| @@ -573,16 +573,25 @@ class Instance : public Handle<VkInstance, NoOwner, InstanceDispatch> { | |||
| 573 | using Handle<VkInstance, NoOwner, InstanceDispatch>::Handle; | 573 | using Handle<VkInstance, NoOwner, InstanceDispatch>::Handle; |
| 574 | 574 | ||
| 575 | public: | 575 | public: |
| 576 | /// Creates a Vulkan instance. Use "operator bool" for error handling. | 576 | /// Creates a Vulkan instance. |
| 577 | /// @throw Exception on initialization error. | ||
| 577 | static Instance Create(u32 version, Span<const char*> layers, Span<const char*> extensions, | 578 | static Instance Create(u32 version, Span<const char*> layers, Span<const char*> extensions, |
| 578 | InstanceDispatch& dispatch) noexcept; | 579 | InstanceDispatch& dispatch); |
| 579 | 580 | ||
| 580 | /// Enumerates physical devices. | 581 | /// Enumerates physical devices. |
| 581 | /// @return Physical devices and an empty handle on failure. | 582 | /// @return Physical devices and an empty handle on failure. |
| 582 | std::optional<std::vector<VkPhysicalDevice>> EnumeratePhysicalDevices(); | 583 | /// @throw Exception on Vulkan error. |
| 584 | std::vector<VkPhysicalDevice> EnumeratePhysicalDevices() const; | ||
| 583 | 585 | ||
| 584 | /// Tries to create a debug callback messenger. Returns an empty handle on failure. | 586 | /// Creates a debug callback messenger. |
| 585 | DebugCallback TryCreateDebugCallback(PFN_vkDebugUtilsMessengerCallbackEXT callback) noexcept; | 587 | /// @throw Exception on creation failure. |
| 588 | DebugUtilsMessenger CreateDebugUtilsMessenger( | ||
| 589 | const VkDebugUtilsMessengerCreateInfoEXT& create_info) const; | ||
| 590 | |||
| 591 | /// Returns dispatch table. | ||
| 592 | const InstanceDispatch& Dispatch() const noexcept { | ||
| 593 | return *dld; | ||
| 594 | } | ||
| 586 | }; | 595 | }; |
| 587 | 596 | ||
| 588 | class Queue { | 597 | class Queue { |
| @@ -787,7 +796,7 @@ class Device : public Handle<VkDevice, NoOwner, DeviceDispatch> { | |||
| 787 | public: | 796 | public: |
| 788 | static Device Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci, | 797 | static Device Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci, |
| 789 | Span<const char*> enabled_extensions, const void* next, | 798 | Span<const char*> enabled_extensions, const void* next, |
| 790 | DeviceDispatch& dispatch) noexcept; | 799 | DeviceDispatch& dispatch); |
| 791 | 800 | ||
| 792 | Queue GetQueue(u32 family_index) const noexcept; | 801 | Queue GetQueue(u32 family_index) const noexcept; |
| 793 | 802 | ||
diff --git a/src/yuzu/applets/controller.cpp b/src/yuzu/applets/controller.cpp index a15e8ca2a..c680fd2c2 100644 --- a/src/yuzu/applets/controller.cpp +++ b/src/yuzu/applets/controller.cpp | |||
| @@ -535,7 +535,7 @@ void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) | |||
| 535 | // This emulates a delay between disconnecting and reconnecting controllers as some games | 535 | // This emulates a delay between disconnecting and reconnecting controllers as some games |
| 536 | // do not respond to a change in controller type if it was instantaneous. | 536 | // do not respond to a change in controller type if it was instantaneous. |
| 537 | using namespace std::chrono_literals; | 537 | using namespace std::chrono_literals; |
| 538 | std::this_thread::sleep_for(20ms); | 538 | std::this_thread::sleep_for(60ms); |
| 539 | 539 | ||
| 540 | UpdateController(controller_type, player_index, player_connected); | 540 | UpdateController(controller_type, player_index, player_connected); |
| 541 | } | 541 | } |
diff --git a/src/yuzu/applets/error.cpp b/src/yuzu/applets/error.cpp index 53a993cf6..8ee03ddb3 100644 --- a/src/yuzu/applets/error.cpp +++ b/src/yuzu/applets/error.cpp | |||
| @@ -19,7 +19,7 @@ QtErrorDisplay::~QtErrorDisplay() = default; | |||
| 19 | void QtErrorDisplay::ShowError(ResultCode error, std::function<void()> finished) const { | 19 | void QtErrorDisplay::ShowError(ResultCode error, std::function<void()> finished) const { |
| 20 | callback = std::move(finished); | 20 | callback = std::move(finished); |
| 21 | emit MainWindowDisplayError( | 21 | emit MainWindowDisplayError( |
| 22 | tr("An error has occured.\nPlease try again or contact the developer of the " | 22 | tr("An error has occurred.\nPlease try again or contact the developer of the " |
| 23 | "software.\n\nError Code: %1-%2 (0x%3)") | 23 | "software.\n\nError Code: %1-%2 (0x%3)") |
| 24 | .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) | 24 | .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) |
| 25 | .arg(error.description, 4, 10, QChar::fromLatin1('0')) | 25 | .arg(error.description, 4, 10, QChar::fromLatin1('0')) |
| @@ -32,7 +32,7 @@ void QtErrorDisplay::ShowErrorWithTimestamp(ResultCode error, std::chrono::secon | |||
| 32 | 32 | ||
| 33 | const QDateTime date_time = QDateTime::fromSecsSinceEpoch(time.count()); | 33 | const QDateTime date_time = QDateTime::fromSecsSinceEpoch(time.count()); |
| 34 | emit MainWindowDisplayError( | 34 | emit MainWindowDisplayError( |
| 35 | tr("An error occured on %1 at %2.\nPlease try again or contact the " | 35 | tr("An error occurred on %1 at %2.\nPlease try again or contact the " |
| 36 | "developer of the software.\n\nError Code: %3-%4 (0x%5)") | 36 | "developer of the software.\n\nError Code: %3-%4 (0x%5)") |
| 37 | .arg(date_time.toString(QStringLiteral("dddd, MMMM d, yyyy"))) | 37 | .arg(date_time.toString(QStringLiteral("dddd, MMMM d, yyyy"))) |
| 38 | .arg(date_time.toString(QStringLiteral("h:mm:ss A"))) | 38 | .arg(date_time.toString(QStringLiteral("h:mm:ss A"))) |
| @@ -46,7 +46,7 @@ void QtErrorDisplay::ShowCustomErrorText(ResultCode error, std::string dialog_te | |||
| 46 | std::function<void()> finished) const { | 46 | std::function<void()> finished) const { |
| 47 | callback = std::move(finished); | 47 | callback = std::move(finished); |
| 48 | emit MainWindowDisplayError( | 48 | emit MainWindowDisplayError( |
| 49 | tr("An error has occured.\nError Code: %1-%2 (0x%3)\n\n%4\n\n%5") | 49 | tr("An error has occurred.\nError Code: %1-%2 (0x%3)\n\n%4\n\n%5") |
| 50 | .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) | 50 | .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) |
| 51 | .arg(error.description, 4, 10, QChar::fromLatin1('0')) | 51 | .arg(error.description, 4, 10, QChar::fromLatin1('0')) |
| 52 | .arg(error.raw, 8, 16, QChar::fromLatin1('0')) | 52 | .arg(error.raw, 8, 16, QChar::fromLatin1('0')) |
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index e124836b5..e6c8f18af 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -290,8 +290,8 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_, | |||
| 290 | QString::fromUtf8(Common::g_scm_branch), | 290 | QString::fromUtf8(Common::g_scm_branch), |
| 291 | QString::fromUtf8(Common::g_scm_desc))); | 291 | QString::fromUtf8(Common::g_scm_desc))); |
| 292 | setAttribute(Qt::WA_AcceptTouchEvents); | 292 | setAttribute(Qt::WA_AcceptTouchEvents); |
| 293 | auto layout = new QHBoxLayout(this); | 293 | auto* layout = new QHBoxLayout(this); |
| 294 | layout->setMargin(0); | 294 | layout->setContentsMargins(0, 0, 0, 0); |
| 295 | setLayout(layout); | 295 | setLayout(layout); |
| 296 | input_subsystem->Initialize(); | 296 | input_subsystem->Initialize(); |
| 297 | 297 | ||
| @@ -397,7 +397,7 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) { | |||
| 397 | this->TouchPressed(x, y); | 397 | this->TouchPressed(x, y); |
| 398 | } | 398 | } |
| 399 | 399 | ||
| 400 | QWidget::mousePressEvent(event); | 400 | emit MouseActivity(); |
| 401 | } | 401 | } |
| 402 | 402 | ||
| 403 | void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { | 403 | void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { |
| @@ -411,7 +411,7 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { | |||
| 411 | input_subsystem->GetMouse()->MouseMove(x, y); | 411 | input_subsystem->GetMouse()->MouseMove(x, y); |
| 412 | this->TouchMoved(x, y); | 412 | this->TouchMoved(x, y); |
| 413 | 413 | ||
| 414 | QWidget::mouseMoveEvent(event); | 414 | emit MouseActivity(); |
| 415 | } | 415 | } |
| 416 | 416 | ||
| 417 | void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { | 417 | void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { |
| @@ -688,3 +688,10 @@ void GRenderWindow::showEvent(QShowEvent* event) { | |||
| 688 | connect(windowHandle(), &QWindow::screenChanged, this, &GRenderWindow::OnFramebufferSizeChanged, | 688 | connect(windowHandle(), &QWindow::screenChanged, this, &GRenderWindow::OnFramebufferSizeChanged, |
| 689 | Qt::UniqueConnection); | 689 | Qt::UniqueConnection); |
| 690 | } | 690 | } |
| 691 | |||
| 692 | bool GRenderWindow::eventFilter(QObject* object, QEvent* event) { | ||
| 693 | if (event->type() == QEvent::HoverMove) { | ||
| 694 | emit MouseActivity(); | ||
| 695 | } | ||
| 696 | return false; | ||
| 697 | } | ||
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index ebe5cb965..339095509 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h | |||
| @@ -184,6 +184,7 @@ signals: | |||
| 184 | void Closed(); | 184 | void Closed(); |
| 185 | void FirstFrameDisplayed(); | 185 | void FirstFrameDisplayed(); |
| 186 | void ExecuteProgramSignal(std::size_t program_index); | 186 | void ExecuteProgramSignal(std::size_t program_index); |
| 187 | void MouseActivity(); | ||
| 187 | 188 | ||
| 188 | private: | 189 | private: |
| 189 | void TouchBeginEvent(const QTouchEvent* event); | 190 | void TouchBeginEvent(const QTouchEvent* event); |
| @@ -216,4 +217,5 @@ private: | |||
| 216 | 217 | ||
| 217 | protected: | 218 | protected: |
| 218 | void showEvent(QShowEvent* event) override; | 219 | void showEvent(QShowEvent* event) override; |
| 220 | bool eventFilter(QObject* object, QEvent* event) override; | ||
| 219 | }; | 221 | }; |
diff --git a/src/yuzu/compatdb.cpp b/src/yuzu/compatdb.cpp index 649912557..a470056ef 100644 --- a/src/yuzu/compatdb.cpp +++ b/src/yuzu/compatdb.cpp | |||
| @@ -72,7 +72,7 @@ void CompatDB::Submit() { | |||
| 72 | void CompatDB::OnTestcaseSubmitted() { | 72 | void CompatDB::OnTestcaseSubmitted() { |
| 73 | if (!testcase_watcher.result()) { | 73 | if (!testcase_watcher.result()) { |
| 74 | QMessageBox::critical(this, tr("Communication error"), | 74 | QMessageBox::critical(this, tr("Communication error"), |
| 75 | tr("An error occured while sending the Testcase")); | 75 | tr("An error occurred while sending the Testcase")); |
| 76 | button(NextButton)->setEnabled(true); | 76 | button(NextButton)->setEnabled(true); |
| 77 | button(NextButton)->setText(tr("Next")); | 77 | button(NextButton)->setText(tr("Next")); |
| 78 | button(CancelButton)->setVisible(true); | 78 | button(CancelButton)->setVisible(true); |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 34c2a5f8b..cda448718 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -514,7 +514,7 @@ void Config::ReadControlValues() { | |||
| 514 | Settings::values.emulate_analog_keyboard = | 514 | Settings::values.emulate_analog_keyboard = |
| 515 | ReadSetting(QStringLiteral("emulate_analog_keyboard"), false).toBool(); | 515 | ReadSetting(QStringLiteral("emulate_analog_keyboard"), false).toBool(); |
| 516 | 516 | ||
| 517 | ReadSettingGlobal(Settings::values.use_docked_mode, QStringLiteral("use_docked_mode"), false); | 517 | ReadSettingGlobal(Settings::values.use_docked_mode, QStringLiteral("use_docked_mode"), true); |
| 518 | ReadSettingGlobal(Settings::values.vibration_enabled, QStringLiteral("vibration_enabled"), | 518 | ReadSettingGlobal(Settings::values.vibration_enabled, QStringLiteral("vibration_enabled"), |
| 519 | true); | 519 | true); |
| 520 | ReadSettingGlobal(Settings::values.enable_accurate_vibrations, | 520 | ReadSettingGlobal(Settings::values.enable_accurate_vibrations, |
| @@ -764,6 +764,8 @@ void Config::ReadCpuValues() { | |||
| 764 | ReadSetting(QStringLiteral("cpuopt_unsafe_unfuse_fma"), true).toBool(); | 764 | ReadSetting(QStringLiteral("cpuopt_unsafe_unfuse_fma"), true).toBool(); |
| 765 | Settings::values.cpuopt_unsafe_reduce_fp_error = | 765 | Settings::values.cpuopt_unsafe_reduce_fp_error = |
| 766 | ReadSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"), true).toBool(); | 766 | ReadSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"), true).toBool(); |
| 767 | Settings::values.cpuopt_unsafe_inaccurate_nan = | ||
| 768 | ReadSetting(QStringLiteral("cpuopt_unsafe_inaccurate_nan"), true).toBool(); | ||
| 767 | } | 769 | } |
| 768 | 770 | ||
| 769 | qt_config->endGroup(); | 771 | qt_config->endGroup(); |
| @@ -1174,7 +1176,7 @@ void Config::SaveControlValues() { | |||
| 1174 | SaveTouchscreenValues(); | 1176 | SaveTouchscreenValues(); |
| 1175 | SaveMotionTouchValues(); | 1177 | SaveMotionTouchValues(); |
| 1176 | 1178 | ||
| 1177 | WriteSettingGlobal(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false); | 1179 | WriteSettingGlobal(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, true); |
| 1178 | WriteSettingGlobal(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled, | 1180 | WriteSettingGlobal(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled, |
| 1179 | true); | 1181 | true); |
| 1180 | WriteSettingGlobal(QStringLiteral("enable_accurate_vibrations"), | 1182 | WriteSettingGlobal(QStringLiteral("enable_accurate_vibrations"), |
| @@ -1327,6 +1329,8 @@ void Config::SaveCpuValues() { | |||
| 1327 | Settings::values.cpuopt_unsafe_unfuse_fma, true); | 1329 | Settings::values.cpuopt_unsafe_unfuse_fma, true); |
| 1328 | WriteSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"), | 1330 | WriteSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"), |
| 1329 | Settings::values.cpuopt_unsafe_reduce_fp_error, true); | 1331 | Settings::values.cpuopt_unsafe_reduce_fp_error, true); |
| 1332 | WriteSetting(QStringLiteral("cpuopt_unsafe_inaccurate_nan"), | ||
| 1333 | Settings::values.cpuopt_unsafe_inaccurate_nan, true); | ||
| 1330 | } | 1334 | } |
| 1331 | 1335 | ||
| 1332 | qt_config->endGroup(); | 1336 | qt_config->endGroup(); |
diff --git a/src/yuzu/configuration/configure_cpu.cpp b/src/yuzu/configuration/configure_cpu.cpp index 37fcd6adc..d055cbd60 100644 --- a/src/yuzu/configuration/configure_cpu.cpp +++ b/src/yuzu/configuration/configure_cpu.cpp | |||
| @@ -36,6 +36,8 @@ void ConfigureCpu::SetConfiguration() { | |||
| 36 | ui->cpuopt_unsafe_unfuse_fma->setChecked(Settings::values.cpuopt_unsafe_unfuse_fma); | 36 | ui->cpuopt_unsafe_unfuse_fma->setChecked(Settings::values.cpuopt_unsafe_unfuse_fma); |
| 37 | ui->cpuopt_unsafe_reduce_fp_error->setEnabled(runtime_lock); | 37 | ui->cpuopt_unsafe_reduce_fp_error->setEnabled(runtime_lock); |
| 38 | ui->cpuopt_unsafe_reduce_fp_error->setChecked(Settings::values.cpuopt_unsafe_reduce_fp_error); | 38 | ui->cpuopt_unsafe_reduce_fp_error->setChecked(Settings::values.cpuopt_unsafe_reduce_fp_error); |
| 39 | ui->cpuopt_unsafe_inaccurate_nan->setEnabled(runtime_lock); | ||
| 40 | ui->cpuopt_unsafe_inaccurate_nan->setChecked(Settings::values.cpuopt_unsafe_inaccurate_nan); | ||
| 39 | } | 41 | } |
| 40 | 42 | ||
| 41 | void ConfigureCpu::AccuracyUpdated(int index) { | 43 | void ConfigureCpu::AccuracyUpdated(int index) { |
| @@ -61,6 +63,7 @@ void ConfigureCpu::ApplyConfiguration() { | |||
| 61 | static_cast<Settings::CPUAccuracy>(ui->accuracy->currentIndex()); | 63 | static_cast<Settings::CPUAccuracy>(ui->accuracy->currentIndex()); |
| 62 | Settings::values.cpuopt_unsafe_unfuse_fma = ui->cpuopt_unsafe_unfuse_fma->isChecked(); | 64 | Settings::values.cpuopt_unsafe_unfuse_fma = ui->cpuopt_unsafe_unfuse_fma->isChecked(); |
| 63 | Settings::values.cpuopt_unsafe_reduce_fp_error = ui->cpuopt_unsafe_reduce_fp_error->isChecked(); | 65 | Settings::values.cpuopt_unsafe_reduce_fp_error = ui->cpuopt_unsafe_reduce_fp_error->isChecked(); |
| 66 | Settings::values.cpuopt_unsafe_inaccurate_nan = ui->cpuopt_unsafe_inaccurate_nan->isChecked(); | ||
| 64 | } | 67 | } |
| 65 | 68 | ||
| 66 | void ConfigureCpu::changeEvent(QEvent* event) { | 69 | void ConfigureCpu::changeEvent(QEvent* event) { |
diff --git a/src/yuzu/configuration/configure_cpu.ui b/src/yuzu/configuration/configure_cpu.ui index ebdd2e6e9..bcd0962e9 100644 --- a/src/yuzu/configuration/configure_cpu.ui +++ b/src/yuzu/configuration/configure_cpu.ui | |||
| @@ -109,6 +109,18 @@ | |||
| 109 | </property> | 109 | </property> |
| 110 | </widget> | 110 | </widget> |
| 111 | </item> | 111 | </item> |
| 112 | <item> | ||
| 113 | <widget class="QCheckBox" name="cpuopt_unsafe_inaccurate_nan"> | ||
| 114 | <property name="text"> | ||
| 115 | <string>Inaccurate NaN handling</string> | ||
| 116 | </property> | ||
| 117 | <property name="toolTip"> | ||
| 118 | <string> | ||
| 119 | <div>This option improves speed by removing NaN checking. Please note this also reduces accuracy of certain floating-point instructions.</div> | ||
| 120 | </string> | ||
| 121 | </property> | ||
| 122 | </widget> | ||
| 123 | </item> | ||
| 112 | </layout> | 124 | </layout> |
| 113 | </widget> | 125 | </widget> |
| 114 | </item> | 126 | </item> |
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index b33f8437a..d6b17a28d 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp | |||
| @@ -117,31 +117,13 @@ void ConfigureDialog::UpdateVisibleTabs() { | |||
| 117 | return; | 117 | return; |
| 118 | } | 118 | } |
| 119 | 119 | ||
| 120 | const std::map<QWidget*, QString> widgets = { | ||
| 121 | {ui->generalTab, tr("General")}, | ||
| 122 | {ui->systemTab, tr("System")}, | ||
| 123 | {ui->profileManagerTab, tr("Profiles")}, | ||
| 124 | {ui->inputTab, tr("Controls")}, | ||
| 125 | {ui->hotkeysTab, tr("Hotkeys")}, | ||
| 126 | {ui->cpuTab, tr("CPU")}, | ||
| 127 | {ui->cpuDebugTab, tr("Debug")}, | ||
| 128 | {ui->graphicsTab, tr("Graphics")}, | ||
| 129 | {ui->graphicsAdvancedTab, tr("Advanced")}, | ||
| 130 | {ui->audioTab, tr("Audio")}, | ||
| 131 | {ui->debugTab, tr("Debug")}, | ||
| 132 | {ui->webTab, tr("Web")}, | ||
| 133 | {ui->uiTab, tr("UI")}, | ||
| 134 | {ui->filesystemTab, tr("Filesystem")}, | ||
| 135 | {ui->serviceTab, tr("Services")}, | ||
| 136 | }; | ||
| 137 | |||
| 138 | [[maybe_unused]] const QSignalBlocker blocker(ui->tabWidget); | 120 | [[maybe_unused]] const QSignalBlocker blocker(ui->tabWidget); |
| 139 | 121 | ||
| 140 | ui->tabWidget->clear(); | 122 | ui->tabWidget->clear(); |
| 141 | 123 | ||
| 142 | const QList<QWidget*> tabs = qvariant_cast<QList<QWidget*>>(items[0]->data(Qt::UserRole)); | 124 | const auto tabs = qvariant_cast<QList<QWidget*>>(items[0]->data(Qt::UserRole)); |
| 143 | 125 | ||
| 144 | for (const auto tab : tabs) { | 126 | for (auto* const tab : tabs) { |
| 145 | ui->tabWidget->addTab(tab, tab->accessibleName()); | 127 | ui->tabWidget->addTab(tab, tab->accessibleName()); |
| 146 | } | 128 | } |
| 147 | } | 129 | } |
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index d9009091b..567a36d9b 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <memory> | 6 | #include <memory> |
| 7 | #include <thread> | ||
| 7 | 8 | ||
| 8 | #include <QSignalBlocker> | 9 | #include <QSignalBlocker> |
| 9 | #include <QTimer> | 10 | #include <QTimer> |
| @@ -181,8 +182,18 @@ QList<QWidget*> ConfigureInput::GetSubTabs() const { | |||
| 181 | } | 182 | } |
| 182 | 183 | ||
| 183 | void ConfigureInput::ApplyConfiguration() { | 184 | void ConfigureInput::ApplyConfiguration() { |
| 184 | for (auto controller : player_controllers) { | 185 | for (auto* controller : player_controllers) { |
| 185 | controller->ApplyConfiguration(); | 186 | controller->ApplyConfiguration(); |
| 187 | controller->TryDisconnectSelectedController(); | ||
| 188 | } | ||
| 189 | |||
| 190 | // This emulates a delay between disconnecting and reconnecting controllers as some games | ||
| 191 | // do not respond to a change in controller type if it was instantaneous. | ||
| 192 | using namespace std::chrono_literals; | ||
| 193 | std::this_thread::sleep_for(60ms); | ||
| 194 | |||
| 195 | for (auto* controller : player_controllers) { | ||
| 196 | controller->TryConnectSelectedController(); | ||
| 186 | } | 197 | } |
| 187 | 198 | ||
| 188 | advanced->ApplyConfiguration(); | 199 | advanced->ApplyConfiguration(); |
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 3c7500ee3..46ea026e4 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp | |||
| @@ -4,7 +4,6 @@ | |||
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <memory> | 6 | #include <memory> |
| 7 | #include <thread> | ||
| 8 | #include <utility> | 7 | #include <utility> |
| 9 | #include <QGridLayout> | 8 | #include <QGridLayout> |
| 10 | #include <QInputDialog> | 9 | #include <QInputDialog> |
| @@ -576,6 +575,10 @@ void ConfigureInputPlayer::ApplyConfiguration() { | |||
| 576 | 575 | ||
| 577 | std::transform(motions_param.begin(), motions_param.end(), motions.begin(), | 576 | std::transform(motions_param.begin(), motions_param.end(), motions.begin(), |
| 578 | [](const Common::ParamPackage& param) { return param.Serialize(); }); | 577 | [](const Common::ParamPackage& param) { return param.Serialize(); }); |
| 578 | } | ||
| 579 | |||
| 580 | void ConfigureInputPlayer::TryConnectSelectedController() { | ||
| 581 | auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 579 | 582 | ||
| 580 | const auto controller_type = | 583 | const auto controller_type = |
| 581 | GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); | 584 | GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); |
| @@ -588,15 +591,12 @@ void ConfigureInputPlayer::ApplyConfiguration() { | |||
| 588 | return; | 591 | return; |
| 589 | } | 592 | } |
| 590 | 593 | ||
| 591 | // Disconnect the controller first. | ||
| 592 | UpdateController(controller_type, player_index, false); | ||
| 593 | |||
| 594 | player.controller_type = controller_type; | 594 | player.controller_type = controller_type; |
| 595 | player.connected = player_connected; | 595 | player.connected = player_connected; |
| 596 | 596 | ||
| 597 | ConfigureVibration::SetVibrationDevices(player_index); | 597 | ConfigureVibration::SetVibrationDevices(player_index); |
| 598 | 598 | ||
| 599 | // Handheld | 599 | // Connect/Disconnect Handheld depending on Player 1's controller configuration. |
| 600 | if (player_index == 0) { | 600 | if (player_index == 0) { |
| 601 | auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; | 601 | auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; |
| 602 | if (controller_type == Settings::ControllerType::Handheld) { | 602 | if (controller_type == Settings::ControllerType::Handheld) { |
| @@ -611,14 +611,26 @@ void ConfigureInputPlayer::ApplyConfiguration() { | |||
| 611 | return; | 611 | return; |
| 612 | } | 612 | } |
| 613 | 613 | ||
| 614 | // This emulates a delay between disconnecting and reconnecting controllers as some games | ||
| 615 | // do not respond to a change in controller type if it was instantaneous. | ||
| 616 | using namespace std::chrono_literals; | ||
| 617 | std::this_thread::sleep_for(20ms); | ||
| 618 | |||
| 619 | UpdateController(controller_type, player_index, player_connected); | 614 | UpdateController(controller_type, player_index, player_connected); |
| 620 | } | 615 | } |
| 621 | 616 | ||
| 617 | void ConfigureInputPlayer::TryDisconnectSelectedController() { | ||
| 618 | const auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 619 | |||
| 620 | const auto controller_type = | ||
| 621 | GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); | ||
| 622 | const auto player_connected = ui->groupConnectedController->isChecked() && | ||
| 623 | controller_type != Settings::ControllerType::Handheld; | ||
| 624 | |||
| 625 | // Do not do anything if the controller configuration has not changed. | ||
| 626 | if (player.controller_type == controller_type && player.connected == player_connected) { | ||
| 627 | return; | ||
| 628 | } | ||
| 629 | |||
| 630 | // Disconnect the controller first. | ||
| 631 | UpdateController(controller_type, player_index, false); | ||
| 632 | } | ||
| 633 | |||
| 622 | void ConfigureInputPlayer::showEvent(QShowEvent* event) { | 634 | void ConfigureInputPlayer::showEvent(QShowEvent* event) { |
| 623 | if (bottom_row == nullptr) { | 635 | if (bottom_row == nullptr) { |
| 624 | return; | 636 | return; |
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index 9c30879a2..c4ae50de7 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h | |||
| @@ -54,6 +54,18 @@ public: | |||
| 54 | /// Save all button configurations to settings file. | 54 | /// Save all button configurations to settings file. |
| 55 | void ApplyConfiguration(); | 55 | void ApplyConfiguration(); |
| 56 | 56 | ||
| 57 | /** | ||
| 58 | * Attempts to connect the currently selected controller in the HID backend. | ||
| 59 | * This function will not do anything if it is not connected in the frontend. | ||
| 60 | */ | ||
| 61 | void TryConnectSelectedController(); | ||
| 62 | |||
| 63 | /** | ||
| 64 | * Attempts to disconnect the currently selected controller in the HID backend. | ||
| 65 | * This function will not do anything if the configuration has not changed. | ||
| 66 | */ | ||
| 67 | void TryDisconnectSelectedController(); | ||
| 68 | |||
| 57 | /// Set the connection state checkbox (used to sync state). | 69 | /// Set the connection state checkbox (used to sync state). |
| 58 | void ConnectPlayer(bool connected); | 70 | void ConnectPlayer(bool connected); |
| 59 | 71 | ||
diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp index c2a7113da..caaa85930 100644 --- a/src/yuzu/configuration/configure_motion_touch.cpp +++ b/src/yuzu/configuration/configure_motion_touch.cpp | |||
| @@ -4,12 +4,15 @@ | |||
| 4 | 4 | ||
| 5 | #include <array> | 5 | #include <array> |
| 6 | #include <sstream> | 6 | #include <sstream> |
| 7 | |||
| 7 | #include <QCloseEvent> | 8 | #include <QCloseEvent> |
| 8 | #include <QLabel> | 9 | #include <QLabel> |
| 9 | #include <QMessageBox> | 10 | #include <QMessageBox> |
| 10 | #include <QPushButton> | 11 | #include <QPushButton> |
| 12 | #include <QRegularExpression> | ||
| 11 | #include <QStringListModel> | 13 | #include <QStringListModel> |
| 12 | #include <QVBoxLayout> | 14 | #include <QVBoxLayout> |
| 15 | |||
| 13 | #include "common/logging/log.h" | 16 | #include "common/logging/log.h" |
| 14 | #include "core/settings.h" | 17 | #include "core/settings.h" |
| 15 | #include "input_common/main.h" | 18 | #include "input_common/main.h" |
| @@ -51,6 +54,8 @@ CalibrationConfigurationDialog::CalibrationConfigurationDialog(QWidget* parent, | |||
| 51 | case CalibrationConfigurationJob::Status::Completed: | 54 | case CalibrationConfigurationJob::Status::Completed: |
| 52 | text = tr("Configuration completed!"); | 55 | text = tr("Configuration completed!"); |
| 53 | break; | 56 | break; |
| 57 | default: | ||
| 58 | break; | ||
| 54 | } | 59 | } |
| 55 | QMetaObject::invokeMethod(this, "UpdateLabelText", Q_ARG(QString, text)); | 60 | QMetaObject::invokeMethod(this, "UpdateLabelText", Q_ARG(QString, text)); |
| 56 | if (status == CalibrationConfigurationJob::Status::Completed) { | 61 | if (status == CalibrationConfigurationJob::Status::Completed) { |
| @@ -107,7 +112,6 @@ ConfigureMotionTouch::~ConfigureMotionTouch() = default; | |||
| 107 | void ConfigureMotionTouch::SetConfiguration() { | 112 | void ConfigureMotionTouch::SetConfiguration() { |
| 108 | const Common::ParamPackage motion_param(Settings::values.motion_device); | 113 | const Common::ParamPackage motion_param(Settings::values.motion_device); |
| 109 | const Common::ParamPackage touch_param(Settings::values.touch_device); | 114 | const Common::ParamPackage touch_param(Settings::values.touch_device); |
| 110 | const std::string motion_engine = motion_param.Get("engine", "motion_emu"); | ||
| 111 | const std::string touch_engine = touch_param.Get("engine", "emu_window"); | 115 | const std::string touch_engine = touch_param.Get("engine", "emu_window"); |
| 112 | 116 | ||
| 113 | ui->touch_provider->setCurrentIndex( | 117 | ui->touch_provider->setCurrentIndex( |
| @@ -183,14 +187,15 @@ void ConfigureMotionTouch::ConnectEvents() { | |||
| 183 | } | 187 | } |
| 184 | 188 | ||
| 185 | void ConfigureMotionTouch::OnUDPAddServer() { | 189 | void ConfigureMotionTouch::OnUDPAddServer() { |
| 186 | QRegExp re(tr(R"re(^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4]" | 190 | // Validator for IP address |
| 187 | "[0-9]|[01]?[0-9][0-9]?)$)re")); // a valid ip address | 191 | const QRegularExpression re(QStringLiteral( |
| 192 | R"re(^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$)re")); | ||
| 188 | bool ok; | 193 | bool ok; |
| 189 | QString port_text = ui->udp_port->text(); | 194 | const QString port_text = ui->udp_port->text(); |
| 190 | QString server_text = ui->udp_server->text(); | 195 | const QString server_text = ui->udp_server->text(); |
| 191 | const QString server_string = tr("%1:%2").arg(server_text, port_text); | 196 | const QString server_string = tr("%1:%2").arg(server_text, port_text); |
| 192 | int port_number = port_text.toInt(&ok, 10); | 197 | const int port_number = port_text.toInt(&ok, 10); |
| 193 | int row = udp_server_list_model->rowCount(); | 198 | const int row = udp_server_list_model->rowCount(); |
| 194 | 199 | ||
| 195 | if (!ok) { | 200 | if (!ok) { |
| 196 | QMessageBox::warning(this, tr("yuzu"), tr("Port number has invalid characters")); | 201 | QMessageBox::warning(this, tr("yuzu"), tr("Port number has invalid characters")); |
| @@ -200,7 +205,7 @@ void ConfigureMotionTouch::OnUDPAddServer() { | |||
| 200 | QMessageBox::warning(this, tr("yuzu"), tr("Port has to be in range 0 and 65353")); | 205 | QMessageBox::warning(this, tr("yuzu"), tr("Port has to be in range 0 and 65353")); |
| 201 | return; | 206 | return; |
| 202 | } | 207 | } |
| 203 | if (!re.exactMatch(server_text)) { | 208 | if (!re.match(server_text).hasMatch()) { |
| 204 | QMessageBox::warning(this, tr("yuzu"), tr("IP address is not valid")); | 209 | QMessageBox::warning(this, tr("yuzu"), tr("IP address is not valid")); |
| 205 | return; | 210 | return; |
| 206 | } | 211 | } |
| @@ -325,14 +330,13 @@ void ConfigureMotionTouch::ApplyConfiguration() { | |||
| 325 | std::string touch_engine = ui->touch_provider->currentData().toString().toStdString(); | 330 | std::string touch_engine = ui->touch_provider->currentData().toString().toStdString(); |
| 326 | 331 | ||
| 327 | Common::ParamPackage touch_param{}; | 332 | Common::ParamPackage touch_param{}; |
| 328 | touch_param.Set("engine", std::move(touch_engine)); | ||
| 329 | |||
| 330 | if (touch_engine == "cemuhookudp") { | 333 | if (touch_engine == "cemuhookudp") { |
| 331 | touch_param.Set("min_x", min_x); | 334 | touch_param.Set("min_x", min_x); |
| 332 | touch_param.Set("min_y", min_y); | 335 | touch_param.Set("min_y", min_y); |
| 333 | touch_param.Set("max_x", max_x); | 336 | touch_param.Set("max_x", max_x); |
| 334 | touch_param.Set("max_y", max_y); | 337 | touch_param.Set("max_y", max_y); |
| 335 | } | 338 | } |
| 339 | touch_param.Set("engine", std::move(touch_engine)); | ||
| 336 | 340 | ||
| 337 | Settings::values.touch_device = touch_param.Serialize(); | 341 | Settings::values.touch_device = touch_param.Serialize(); |
| 338 | Settings::values.use_touch_from_button = ui->touch_from_button_checkbox->isChecked(); | 342 | Settings::values.use_touch_from_button = ui->touch_from_button_checkbox->isChecked(); |
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index 0925c10b4..a93b5d3c2 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp | |||
| @@ -14,10 +14,10 @@ | |||
| 14 | #include "core/core.h" | 14 | #include "core/core.h" |
| 15 | #include "core/hle/kernel/handle_table.h" | 15 | #include "core/hle/kernel/handle_table.h" |
| 16 | #include "core/hle/kernel/k_scheduler.h" | 16 | #include "core/hle/kernel/k_scheduler.h" |
| 17 | #include "core/hle/kernel/mutex.h" | 17 | #include "core/hle/kernel/k_synchronization_object.h" |
| 18 | #include "core/hle/kernel/process.h" | 18 | #include "core/hle/kernel/process.h" |
| 19 | #include "core/hle/kernel/readable_event.h" | 19 | #include "core/hle/kernel/readable_event.h" |
| 20 | #include "core/hle/kernel/synchronization_object.h" | 20 | #include "core/hle/kernel/svc_common.h" |
| 21 | #include "core/hle/kernel/thread.h" | 21 | #include "core/hle/kernel/thread.h" |
| 22 | #include "core/memory.h" | 22 | #include "core/memory.h" |
| 23 | 23 | ||
| @@ -116,7 +116,7 @@ QString WaitTreeText::GetText() const { | |||
| 116 | WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address, const Kernel::HandleTable& handle_table) | 116 | WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address, const Kernel::HandleTable& handle_table) |
| 117 | : mutex_address(mutex_address) { | 117 | : mutex_address(mutex_address) { |
| 118 | mutex_value = Core::System::GetInstance().Memory().Read32(mutex_address); | 118 | mutex_value = Core::System::GetInstance().Memory().Read32(mutex_address); |
| 119 | owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Mutex::MutexOwnerMask); | 119 | owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Svc::HandleWaitMask); |
| 120 | owner = handle_table.Get<Kernel::Thread>(owner_handle); | 120 | owner = handle_table.Get<Kernel::Thread>(owner_handle); |
| 121 | } | 121 | } |
| 122 | 122 | ||
| @@ -127,7 +127,7 @@ QString WaitTreeMutexInfo::GetText() const { | |||
| 127 | } | 127 | } |
| 128 | 128 | ||
| 129 | std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutexInfo::GetChildren() const { | 129 | std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutexInfo::GetChildren() const { |
| 130 | const bool has_waiters = (mutex_value & Kernel::Mutex::MutexHasWaitersFlag) != 0; | 130 | const bool has_waiters = (mutex_value & Kernel::Svc::HandleWaitMask) != 0; |
| 131 | 131 | ||
| 132 | std::vector<std::unique_ptr<WaitTreeItem>> list; | 132 | std::vector<std::unique_ptr<WaitTreeItem>> list; |
| 133 | list.push_back(std::make_unique<WaitTreeText>(tr("has waiters: %1").arg(has_waiters))); | 133 | list.push_back(std::make_unique<WaitTreeText>(tr("has waiters: %1").arg(has_waiters))); |
| @@ -169,7 +169,8 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() cons | |||
| 169 | return list; | 169 | return list; |
| 170 | } | 170 | } |
| 171 | 171 | ||
| 172 | WaitTreeSynchronizationObject::WaitTreeSynchronizationObject(const Kernel::SynchronizationObject& o) | 172 | WaitTreeSynchronizationObject::WaitTreeSynchronizationObject( |
| 173 | const Kernel::KSynchronizationObject& o) | ||
| 173 | : object(o) {} | 174 | : object(o) {} |
| 174 | WaitTreeSynchronizationObject::~WaitTreeSynchronizationObject() = default; | 175 | WaitTreeSynchronizationObject::~WaitTreeSynchronizationObject() = default; |
| 175 | 176 | ||
| @@ -188,7 +189,7 @@ QString WaitTreeSynchronizationObject::GetText() const { | |||
| 188 | } | 189 | } |
| 189 | 190 | ||
| 190 | std::unique_ptr<WaitTreeSynchronizationObject> WaitTreeSynchronizationObject::make( | 191 | std::unique_ptr<WaitTreeSynchronizationObject> WaitTreeSynchronizationObject::make( |
| 191 | const Kernel::SynchronizationObject& object) { | 192 | const Kernel::KSynchronizationObject& object) { |
| 192 | switch (object.GetHandleType()) { | 193 | switch (object.GetHandleType()) { |
| 193 | case Kernel::HandleType::ReadableEvent: | 194 | case Kernel::HandleType::ReadableEvent: |
| 194 | return std::make_unique<WaitTreeEvent>(static_cast<const Kernel::ReadableEvent&>(object)); | 195 | return std::make_unique<WaitTreeEvent>(static_cast<const Kernel::ReadableEvent&>(object)); |
| @@ -202,7 +203,7 @@ std::unique_ptr<WaitTreeSynchronizationObject> WaitTreeSynchronizationObject::ma | |||
| 202 | std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeSynchronizationObject::GetChildren() const { | 203 | std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeSynchronizationObject::GetChildren() const { |
| 203 | std::vector<std::unique_ptr<WaitTreeItem>> list; | 204 | std::vector<std::unique_ptr<WaitTreeItem>> list; |
| 204 | 205 | ||
| 205 | const auto& threads = object.GetWaitingThreads(); | 206 | const auto& threads = object.GetWaitingThreadsForDebugging(); |
| 206 | if (threads.empty()) { | 207 | if (threads.empty()) { |
| 207 | list.push_back(std::make_unique<WaitTreeText>(tr("waited by no thread"))); | 208 | list.push_back(std::make_unique<WaitTreeText>(tr("waited by no thread"))); |
| 208 | } else { | 209 | } else { |
| @@ -211,8 +212,8 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeSynchronizationObject::GetChi | |||
| 211 | return list; | 212 | return list; |
| 212 | } | 213 | } |
| 213 | 214 | ||
| 214 | WaitTreeObjectList::WaitTreeObjectList( | 215 | WaitTreeObjectList::WaitTreeObjectList(const std::vector<Kernel::KSynchronizationObject*>& list, |
| 215 | const std::vector<std::shared_ptr<Kernel::SynchronizationObject>>& list, bool w_all) | 216 | bool w_all) |
| 216 | : object_list(list), wait_all(w_all) {} | 217 | : object_list(list), wait_all(w_all) {} |
| 217 | 218 | ||
| 218 | WaitTreeObjectList::~WaitTreeObjectList() = default; | 219 | WaitTreeObjectList::~WaitTreeObjectList() = default; |
| @@ -237,8 +238,8 @@ WaitTreeThread::~WaitTreeThread() = default; | |||
| 237 | QString WaitTreeThread::GetText() const { | 238 | QString WaitTreeThread::GetText() const { |
| 238 | const auto& thread = static_cast<const Kernel::Thread&>(object); | 239 | const auto& thread = static_cast<const Kernel::Thread&>(object); |
| 239 | QString status; | 240 | QString status; |
| 240 | switch (thread.GetStatus()) { | 241 | switch (thread.GetState()) { |
| 241 | case Kernel::ThreadStatus::Ready: | 242 | case Kernel::ThreadState::Runnable: |
| 242 | if (!thread.IsPaused()) { | 243 | if (!thread.IsPaused()) { |
| 243 | if (thread.WasRunning()) { | 244 | if (thread.WasRunning()) { |
| 244 | status = tr("running"); | 245 | status = tr("running"); |
| @@ -249,35 +250,39 @@ QString WaitTreeThread::GetText() const { | |||
| 249 | status = tr("paused"); | 250 | status = tr("paused"); |
| 250 | } | 251 | } |
| 251 | break; | 252 | break; |
| 252 | case Kernel::ThreadStatus::Paused: | 253 | case Kernel::ThreadState::Waiting: |
| 253 | status = tr("paused"); | 254 | switch (thread.GetWaitReasonForDebugging()) { |
| 254 | break; | 255 | case Kernel::ThreadWaitReasonForDebugging::Sleep: |
| 255 | case Kernel::ThreadStatus::WaitHLEEvent: | 256 | status = tr("sleeping"); |
| 256 | status = tr("waiting for HLE return"); | 257 | break; |
| 257 | break; | 258 | case Kernel::ThreadWaitReasonForDebugging::IPC: |
| 258 | case Kernel::ThreadStatus::WaitSleep: | 259 | status = tr("waiting for IPC reply"); |
| 259 | status = tr("sleeping"); | 260 | break; |
| 260 | break; | 261 | case Kernel::ThreadWaitReasonForDebugging::Synchronization: |
| 261 | case Kernel::ThreadStatus::WaitIPC: | 262 | status = tr("waiting for objects"); |
| 262 | status = tr("waiting for IPC reply"); | 263 | break; |
| 263 | break; | 264 | case Kernel::ThreadWaitReasonForDebugging::ConditionVar: |
| 264 | case Kernel::ThreadStatus::WaitSynch: | 265 | status = tr("waiting for condition variable"); |
| 265 | status = tr("waiting for objects"); | 266 | break; |
| 266 | break; | 267 | case Kernel::ThreadWaitReasonForDebugging::Arbitration: |
| 267 | case Kernel::ThreadStatus::WaitMutex: | 268 | status = tr("waiting for address arbiter"); |
| 268 | status = tr("waiting for mutex"); | 269 | break; |
| 269 | break; | 270 | case Kernel::ThreadWaitReasonForDebugging::Suspended: |
| 270 | case Kernel::ThreadStatus::WaitCondVar: | 271 | status = tr("waiting for suspend resume"); |
| 271 | status = tr("waiting for condition variable"); | 272 | break; |
| 273 | default: | ||
| 274 | status = tr("waiting"); | ||
| 275 | break; | ||
| 276 | } | ||
| 272 | break; | 277 | break; |
| 273 | case Kernel::ThreadStatus::WaitArb: | 278 | case Kernel::ThreadState::Initialized: |
| 274 | status = tr("waiting for address arbiter"); | 279 | status = tr("initialized"); |
| 275 | break; | 280 | break; |
| 276 | case Kernel::ThreadStatus::Dormant: | 281 | case Kernel::ThreadState::Terminated: |
| 277 | status = tr("dormant"); | 282 | status = tr("terminated"); |
| 278 | break; | 283 | break; |
| 279 | case Kernel::ThreadStatus::Dead: | 284 | default: |
| 280 | status = tr("dead"); | 285 | status = tr("unknown"); |
| 281 | break; | 286 | break; |
| 282 | } | 287 | } |
| 283 | 288 | ||
| @@ -293,8 +298,8 @@ QColor WaitTreeThread::GetColor() const { | |||
| 293 | const std::size_t color_index = IsDarkTheme() ? 1 : 0; | 298 | const std::size_t color_index = IsDarkTheme() ? 1 : 0; |
| 294 | 299 | ||
| 295 | const auto& thread = static_cast<const Kernel::Thread&>(object); | 300 | const auto& thread = static_cast<const Kernel::Thread&>(object); |
| 296 | switch (thread.GetStatus()) { | 301 | switch (thread.GetState()) { |
| 297 | case Kernel::ThreadStatus::Ready: | 302 | case Kernel::ThreadState::Runnable: |
| 298 | if (!thread.IsPaused()) { | 303 | if (!thread.IsPaused()) { |
| 299 | if (thread.WasRunning()) { | 304 | if (thread.WasRunning()) { |
| 300 | return QColor(WaitTreeColors[0][color_index]); | 305 | return QColor(WaitTreeColors[0][color_index]); |
| @@ -304,21 +309,24 @@ QColor WaitTreeThread::GetColor() const { | |||
| 304 | } else { | 309 | } else { |
| 305 | return QColor(WaitTreeColors[2][color_index]); | 310 | return QColor(WaitTreeColors[2][color_index]); |
| 306 | } | 311 | } |
| 307 | case Kernel::ThreadStatus::Paused: | 312 | case Kernel::ThreadState::Waiting: |
| 308 | return QColor(WaitTreeColors[3][color_index]); | 313 | switch (thread.GetWaitReasonForDebugging()) { |
| 309 | case Kernel::ThreadStatus::WaitHLEEvent: | 314 | case Kernel::ThreadWaitReasonForDebugging::IPC: |
| 310 | case Kernel::ThreadStatus::WaitIPC: | 315 | return QColor(WaitTreeColors[4][color_index]); |
| 311 | return QColor(WaitTreeColors[4][color_index]); | 316 | case Kernel::ThreadWaitReasonForDebugging::Sleep: |
| 312 | case Kernel::ThreadStatus::WaitSleep: | 317 | return QColor(WaitTreeColors[5][color_index]); |
| 313 | return QColor(WaitTreeColors[5][color_index]); | 318 | case Kernel::ThreadWaitReasonForDebugging::Synchronization: |
| 314 | case Kernel::ThreadStatus::WaitSynch: | 319 | case Kernel::ThreadWaitReasonForDebugging::ConditionVar: |
| 315 | case Kernel::ThreadStatus::WaitMutex: | 320 | case Kernel::ThreadWaitReasonForDebugging::Arbitration: |
| 316 | case Kernel::ThreadStatus::WaitCondVar: | 321 | case Kernel::ThreadWaitReasonForDebugging::Suspended: |
| 317 | case Kernel::ThreadStatus::WaitArb: | 322 | return QColor(WaitTreeColors[6][color_index]); |
| 318 | return QColor(WaitTreeColors[6][color_index]); | 323 | break; |
| 319 | case Kernel::ThreadStatus::Dormant: | 324 | default: |
| 325 | return QColor(WaitTreeColors[3][color_index]); | ||
| 326 | } | ||
| 327 | case Kernel::ThreadState::Initialized: | ||
| 320 | return QColor(WaitTreeColors[7][color_index]); | 328 | return QColor(WaitTreeColors[7][color_index]); |
| 321 | case Kernel::ThreadStatus::Dead: | 329 | case Kernel::ThreadState::Terminated: |
| 322 | return QColor(WaitTreeColors[8][color_index]); | 330 | return QColor(WaitTreeColors[8][color_index]); |
| 323 | default: | 331 | default: |
| 324 | return WaitTreeItem::GetColor(); | 332 | return WaitTreeItem::GetColor(); |
| @@ -354,11 +362,11 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const { | |||
| 354 | list.push_back(std::make_unique<WaitTreeText>(tr("thread id = %1").arg(thread.GetThreadID()))); | 362 | list.push_back(std::make_unique<WaitTreeText>(tr("thread id = %1").arg(thread.GetThreadID()))); |
| 355 | list.push_back(std::make_unique<WaitTreeText>(tr("priority = %1(current) / %2(normal)") | 363 | list.push_back(std::make_unique<WaitTreeText>(tr("priority = %1(current) / %2(normal)") |
| 356 | .arg(thread.GetPriority()) | 364 | .arg(thread.GetPriority()) |
| 357 | .arg(thread.GetNominalPriority()))); | 365 | .arg(thread.GetBasePriority()))); |
| 358 | list.push_back(std::make_unique<WaitTreeText>( | 366 | list.push_back(std::make_unique<WaitTreeText>( |
| 359 | tr("last running ticks = %1").arg(thread.GetLastScheduledTick()))); | 367 | tr("last running ticks = %1").arg(thread.GetLastScheduledTick()))); |
| 360 | 368 | ||
| 361 | const VAddr mutex_wait_address = thread.GetMutexWaitAddress(); | 369 | const VAddr mutex_wait_address = thread.GetMutexWaitAddressForDebugging(); |
| 362 | if (mutex_wait_address != 0) { | 370 | if (mutex_wait_address != 0) { |
| 363 | const auto& handle_table = thread.GetOwnerProcess()->GetHandleTable(); | 371 | const auto& handle_table = thread.GetOwnerProcess()->GetHandleTable(); |
| 364 | list.push_back(std::make_unique<WaitTreeMutexInfo>(mutex_wait_address, handle_table)); | 372 | list.push_back(std::make_unique<WaitTreeMutexInfo>(mutex_wait_address, handle_table)); |
| @@ -366,9 +374,11 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const { | |||
| 366 | list.push_back(std::make_unique<WaitTreeText>(tr("not waiting for mutex"))); | 374 | list.push_back(std::make_unique<WaitTreeText>(tr("not waiting for mutex"))); |
| 367 | } | 375 | } |
| 368 | 376 | ||
| 369 | if (thread.GetStatus() == Kernel::ThreadStatus::WaitSynch) { | 377 | if (thread.GetState() == Kernel::ThreadState::Waiting && |
| 370 | list.push_back(std::make_unique<WaitTreeObjectList>(thread.GetSynchronizationObjects(), | 378 | thread.GetWaitReasonForDebugging() == |
| 371 | thread.IsWaitingSync())); | 379 | Kernel::ThreadWaitReasonForDebugging::Synchronization) { |
| 380 | list.push_back(std::make_unique<WaitTreeObjectList>(thread.GetWaitObjectsForDebugging(), | ||
| 381 | thread.IsCancellable())); | ||
| 372 | } | 382 | } |
| 373 | 383 | ||
| 374 | list.push_back(std::make_unique<WaitTreeCallstack>(thread)); | 384 | list.push_back(std::make_unique<WaitTreeCallstack>(thread)); |
| @@ -380,7 +390,7 @@ WaitTreeEvent::WaitTreeEvent(const Kernel::ReadableEvent& object) | |||
| 380 | : WaitTreeSynchronizationObject(object) {} | 390 | : WaitTreeSynchronizationObject(object) {} |
| 381 | WaitTreeEvent::~WaitTreeEvent() = default; | 391 | WaitTreeEvent::~WaitTreeEvent() = default; |
| 382 | 392 | ||
| 383 | WaitTreeThreadList::WaitTreeThreadList(const std::vector<std::shared_ptr<Kernel::Thread>>& list) | 393 | WaitTreeThreadList::WaitTreeThreadList(const std::vector<Kernel::Thread*>& list) |
| 384 | : thread_list(list) {} | 394 | : thread_list(list) {} |
| 385 | WaitTreeThreadList::~WaitTreeThreadList() = default; | 395 | WaitTreeThreadList::~WaitTreeThreadList() = default; |
| 386 | 396 | ||
diff --git a/src/yuzu/debugger/wait_tree.h b/src/yuzu/debugger/wait_tree.h index 8e3bc4b24..cf96911ea 100644 --- a/src/yuzu/debugger/wait_tree.h +++ b/src/yuzu/debugger/wait_tree.h | |||
| @@ -18,8 +18,8 @@ class EmuThread; | |||
| 18 | 18 | ||
| 19 | namespace Kernel { | 19 | namespace Kernel { |
| 20 | class HandleTable; | 20 | class HandleTable; |
| 21 | class KSynchronizationObject; | ||
| 21 | class ReadableEvent; | 22 | class ReadableEvent; |
| 22 | class SynchronizationObject; | ||
| 23 | class Thread; | 23 | class Thread; |
| 24 | } // namespace Kernel | 24 | } // namespace Kernel |
| 25 | 25 | ||
| @@ -102,30 +102,29 @@ private: | |||
| 102 | class WaitTreeSynchronizationObject : public WaitTreeExpandableItem { | 102 | class WaitTreeSynchronizationObject : public WaitTreeExpandableItem { |
| 103 | Q_OBJECT | 103 | Q_OBJECT |
| 104 | public: | 104 | public: |
| 105 | explicit WaitTreeSynchronizationObject(const Kernel::SynchronizationObject& object); | 105 | explicit WaitTreeSynchronizationObject(const Kernel::KSynchronizationObject& object); |
| 106 | ~WaitTreeSynchronizationObject() override; | 106 | ~WaitTreeSynchronizationObject() override; |
| 107 | 107 | ||
| 108 | static std::unique_ptr<WaitTreeSynchronizationObject> make( | 108 | static std::unique_ptr<WaitTreeSynchronizationObject> make( |
| 109 | const Kernel::SynchronizationObject& object); | 109 | const Kernel::KSynchronizationObject& object); |
| 110 | QString GetText() const override; | 110 | QString GetText() const override; |
| 111 | std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; | 111 | std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; |
| 112 | 112 | ||
| 113 | protected: | 113 | protected: |
| 114 | const Kernel::SynchronizationObject& object; | 114 | const Kernel::KSynchronizationObject& object; |
| 115 | }; | 115 | }; |
| 116 | 116 | ||
| 117 | class WaitTreeObjectList : public WaitTreeExpandableItem { | 117 | class WaitTreeObjectList : public WaitTreeExpandableItem { |
| 118 | Q_OBJECT | 118 | Q_OBJECT |
| 119 | public: | 119 | public: |
| 120 | WaitTreeObjectList(const std::vector<std::shared_ptr<Kernel::SynchronizationObject>>& list, | 120 | WaitTreeObjectList(const std::vector<Kernel::KSynchronizationObject*>& list, bool wait_all); |
| 121 | bool wait_all); | ||
| 122 | ~WaitTreeObjectList() override; | 121 | ~WaitTreeObjectList() override; |
| 123 | 122 | ||
| 124 | QString GetText() const override; | 123 | QString GetText() const override; |
| 125 | std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; | 124 | std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; |
| 126 | 125 | ||
| 127 | private: | 126 | private: |
| 128 | const std::vector<std::shared_ptr<Kernel::SynchronizationObject>>& object_list; | 127 | const std::vector<Kernel::KSynchronizationObject*>& object_list; |
| 129 | bool wait_all; | 128 | bool wait_all; |
| 130 | }; | 129 | }; |
| 131 | 130 | ||
| @@ -150,14 +149,14 @@ public: | |||
| 150 | class WaitTreeThreadList : public WaitTreeExpandableItem { | 149 | class WaitTreeThreadList : public WaitTreeExpandableItem { |
| 151 | Q_OBJECT | 150 | Q_OBJECT |
| 152 | public: | 151 | public: |
| 153 | explicit WaitTreeThreadList(const std::vector<std::shared_ptr<Kernel::Thread>>& list); | 152 | explicit WaitTreeThreadList(const std::vector<Kernel::Thread*>& list); |
| 154 | ~WaitTreeThreadList() override; | 153 | ~WaitTreeThreadList() override; |
| 155 | 154 | ||
| 156 | QString GetText() const override; | 155 | QString GetText() const override; |
| 157 | std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; | 156 | std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; |
| 158 | 157 | ||
| 159 | private: | 158 | private: |
| 160 | const std::vector<std::shared_ptr<Kernel::Thread>>& thread_list; | 159 | const std::vector<Kernel::Thread*>& thread_list; |
| 161 | }; | 160 | }; |
| 162 | 161 | ||
| 163 | class WaitTreeModel : public QAbstractItemModel { | 162 | class WaitTreeModel : public QAbstractItemModel { |
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 70d865112..37b0d1a0e 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp | |||
| @@ -119,7 +119,7 @@ void GameListSearchField::setFocus() { | |||
| 119 | GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} { | 119 | GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} { |
| 120 | auto* const key_release_eater = new KeyReleaseEater(parent, this); | 120 | auto* const key_release_eater = new KeyReleaseEater(parent, this); |
| 121 | layout_filter = new QHBoxLayout; | 121 | layout_filter = new QHBoxLayout; |
| 122 | layout_filter->setMargin(8); | 122 | layout_filter->setContentsMargins(8, 8, 8, 8); |
| 123 | label_filter = new QLabel; | 123 | label_filter = new QLabel; |
| 124 | label_filter->setText(tr("Filter:")); | 124 | label_filter->setText(tr("Filter:")); |
| 125 | edit_filter = new QLineEdit; | 125 | edit_filter = new QLineEdit; |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index ab66d7f93..2e74037d1 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -142,7 +142,7 @@ constexpr int default_mouse_timeout = 2500; | |||
| 142 | /** | 142 | /** |
| 143 | * "Callouts" are one-time instructional messages shown to the user. In the config settings, there | 143 | * "Callouts" are one-time instructional messages shown to the user. In the config settings, there |
| 144 | * is a bitfield "callout_flags" options, used to track if a message has already been shown to the | 144 | * is a bitfield "callout_flags" options, used to track if a message has already been shown to the |
| 145 | * user. This is 32-bits - if we have more than 32 callouts, we should retire and recyle old ones. | 145 | * user. This is 32-bits - if we have more than 32 callouts, we should retire and recycle old ones. |
| 146 | */ | 146 | */ |
| 147 | enum class CalloutFlag : uint32_t { | 147 | enum class CalloutFlag : uint32_t { |
| 148 | Telemetry = 0x1, | 148 | Telemetry = 0x1, |
| @@ -292,12 +292,48 @@ GMainWindow::GMainWindow() | |||
| 292 | connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor); | 292 | connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor); |
| 293 | connect(ui.menubar, &QMenuBar::hovered, this, &GMainWindow::ShowMouseCursor); | 293 | connect(ui.menubar, &QMenuBar::hovered, this, &GMainWindow::ShowMouseCursor); |
| 294 | 294 | ||
| 295 | MigrateConfigFiles(); | ||
| 296 | |||
| 297 | ui.action_Fullscreen->setChecked(false); | ||
| 298 | |||
| 295 | QStringList args = QApplication::arguments(); | 299 | QStringList args = QApplication::arguments(); |
| 296 | if (args.length() >= 2) { | 300 | |
| 297 | BootGame(args[1]); | 301 | if (args.size() < 2) { |
| 302 | return; | ||
| 298 | } | 303 | } |
| 299 | 304 | ||
| 300 | MigrateConfigFiles(); | 305 | QString game_path; |
| 306 | |||
| 307 | for (int i = 1; i < args.size(); ++i) { | ||
| 308 | // Preserves drag/drop functionality | ||
| 309 | if (args.size() == 2 && !args[1].startsWith(QChar::fromLatin1('-'))) { | ||
| 310 | game_path = args[1]; | ||
| 311 | break; | ||
| 312 | } | ||
| 313 | |||
| 314 | // Launch game in fullscreen mode | ||
| 315 | if (args[i] == QStringLiteral("-f")) { | ||
| 316 | ui.action_Fullscreen->setChecked(true); | ||
| 317 | continue; | ||
| 318 | } | ||
| 319 | |||
| 320 | // Launch game at path | ||
| 321 | if (args[i] == QStringLiteral("-g")) { | ||
| 322 | if (i >= args.size() - 1) { | ||
| 323 | continue; | ||
| 324 | } | ||
| 325 | |||
| 326 | if (args[i + 1].startsWith(QChar::fromLatin1('-'))) { | ||
| 327 | continue; | ||
| 328 | } | ||
| 329 | |||
| 330 | game_path = args[++i]; | ||
| 331 | } | ||
| 332 | } | ||
| 333 | |||
| 334 | if (!game_path.isEmpty()) { | ||
| 335 | BootGame(game_path); | ||
| 336 | } | ||
| 301 | } | 337 | } |
| 302 | 338 | ||
| 303 | GMainWindow::~GMainWindow() { | 339 | GMainWindow::~GMainWindow() { |
| @@ -1045,20 +1081,24 @@ bool GMainWindow::LoadROM(const QString& filename, std::size_t program_index) { | |||
| 1045 | break; | 1081 | break; |
| 1046 | 1082 | ||
| 1047 | default: | 1083 | default: |
| 1048 | if (static_cast<u32>(result) > | 1084 | if (result > Core::System::ResultStatus::ErrorLoader) { |
| 1049 | static_cast<u32>(Core::System::ResultStatus::ErrorLoader)) { | ||
| 1050 | const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader); | 1085 | const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader); |
| 1051 | const u16 error_id = static_cast<u16>(result) - loader_id; | 1086 | const u16 error_id = static_cast<u16>(result) - loader_id; |
| 1052 | const std::string error_code = fmt::format("({:04X}-{:04X})", loader_id, error_id); | 1087 | const std::string error_code = fmt::format("({:04X}-{:04X})", loader_id, error_id); |
| 1053 | LOG_CRITICAL(Frontend, "Failed to load ROM! {}", error_code); | 1088 | LOG_CRITICAL(Frontend, "Failed to load ROM! {}", error_code); |
| 1054 | QMessageBox::critical( | 1089 | |
| 1055 | this, | 1090 | const auto title = |
| 1056 | tr("Error while loading ROM! ").append(QString::fromStdString(error_code)), | 1091 | tr("Error while loading ROM! %1", "%1 signifies a numeric error code.") |
| 1057 | QString::fromStdString(fmt::format( | 1092 | .arg(QString::fromStdString(error_code)); |
| 1058 | "{}<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the " | 1093 | const auto description = |
| 1059 | "yuzu quickstart guide</a> to redump your files.<br>You can refer " | 1094 | tr("%1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the " |
| 1060 | "to the yuzu wiki</a> or the yuzu Discord</a> for help.", | 1095 | "yuzu quickstart guide</a> to redump your files.<br>You can refer " |
| 1061 | static_cast<Loader::ResultStatus>(error_id)))); | 1096 | "to the yuzu wiki</a> or the yuzu Discord</a> for help.", |
| 1097 | "%1 signifies an error string.") | ||
| 1098 | .arg(QString::fromStdString( | ||
| 1099 | GetResultStatusString(static_cast<Loader::ResultStatus>(error_id)))); | ||
| 1100 | |||
| 1101 | QMessageBox::critical(this, title, description); | ||
| 1062 | } else { | 1102 | } else { |
| 1063 | QMessageBox::critical( | 1103 | QMessageBox::critical( |
| 1064 | this, tr("Error while loading ROM!"), | 1104 | this, tr("Error while loading ROM!"), |
| @@ -1130,6 +1170,7 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index) { | |||
| 1130 | [this](std::size_t program_index) { render_window->ExecuteProgram(program_index); }); | 1170 | [this](std::size_t program_index) { render_window->ExecuteProgram(program_index); }); |
| 1131 | 1171 | ||
| 1132 | connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); | 1172 | connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); |
| 1173 | connect(render_window, &GRenderWindow::MouseActivity, this, &GMainWindow::OnMouseActivity); | ||
| 1133 | // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views | 1174 | // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views |
| 1134 | // before the CPU continues | 1175 | // before the CPU continues |
| 1135 | connect(emu_thread.get(), &EmuThread::DebugModeEntered, waitTreeWidget, | 1176 | connect(emu_thread.get(), &EmuThread::DebugModeEntered, waitTreeWidget, |
| @@ -1153,8 +1194,8 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index) { | |||
| 1153 | 1194 | ||
| 1154 | if (UISettings::values.hide_mouse) { | 1195 | if (UISettings::values.hide_mouse) { |
| 1155 | mouse_hide_timer.start(); | 1196 | mouse_hide_timer.start(); |
| 1156 | setMouseTracking(true); | 1197 | render_window->installEventFilter(render_window); |
| 1157 | ui.centralwidget->setMouseTracking(true); | 1198 | render_window->setAttribute(Qt::WA_Hover, true); |
| 1158 | } | 1199 | } |
| 1159 | 1200 | ||
| 1160 | std::string title_name; | 1201 | std::string title_name; |
| @@ -1231,8 +1272,8 @@ void GMainWindow::ShutdownGame() { | |||
| 1231 | } | 1272 | } |
| 1232 | game_list->SetFilterFocus(); | 1273 | game_list->SetFilterFocus(); |
| 1233 | 1274 | ||
| 1234 | setMouseTracking(false); | 1275 | render_window->removeEventFilter(render_window); |
| 1235 | ui.centralwidget->setMouseTracking(false); | 1276 | render_window->setAttribute(Qt::WA_Hover, false); |
| 1236 | 1277 | ||
| 1237 | UpdateWindowTitle(); | 1278 | UpdateWindowTitle(); |
| 1238 | 1279 | ||
| @@ -2313,12 +2354,12 @@ void GMainWindow::OnConfigure() { | |||
| 2313 | config->Save(); | 2354 | config->Save(); |
| 2314 | 2355 | ||
| 2315 | if (UISettings::values.hide_mouse && emulation_running) { | 2356 | if (UISettings::values.hide_mouse && emulation_running) { |
| 2316 | setMouseTracking(true); | 2357 | render_window->installEventFilter(render_window); |
| 2317 | ui.centralwidget->setMouseTracking(true); | 2358 | render_window->setAttribute(Qt::WA_Hover, true); |
| 2318 | mouse_hide_timer.start(); | 2359 | mouse_hide_timer.start(); |
| 2319 | } else { | 2360 | } else { |
| 2320 | setMouseTracking(false); | 2361 | render_window->removeEventFilter(render_window); |
| 2321 | ui.centralwidget->setMouseTracking(false); | 2362 | render_window->setAttribute(Qt::WA_Hover, false); |
| 2322 | } | 2363 | } |
| 2323 | 2364 | ||
| 2324 | UpdateStatusButtons(); | 2365 | UpdateStatusButtons(); |
| @@ -2558,21 +2599,17 @@ void GMainWindow::HideMouseCursor() { | |||
| 2558 | ShowMouseCursor(); | 2599 | ShowMouseCursor(); |
| 2559 | return; | 2600 | return; |
| 2560 | } | 2601 | } |
| 2561 | setCursor(QCursor(Qt::BlankCursor)); | 2602 | render_window->setCursor(QCursor(Qt::BlankCursor)); |
| 2562 | } | 2603 | } |
| 2563 | 2604 | ||
| 2564 | void GMainWindow::ShowMouseCursor() { | 2605 | void GMainWindow::ShowMouseCursor() { |
| 2565 | unsetCursor(); | 2606 | render_window->unsetCursor(); |
| 2566 | if (emu_thread != nullptr && UISettings::values.hide_mouse) { | 2607 | if (emu_thread != nullptr && UISettings::values.hide_mouse) { |
| 2567 | mouse_hide_timer.start(); | 2608 | mouse_hide_timer.start(); |
| 2568 | } | 2609 | } |
| 2569 | } | 2610 | } |
| 2570 | 2611 | ||
| 2571 | void GMainWindow::mouseMoveEvent(QMouseEvent* event) { | 2612 | void GMainWindow::OnMouseActivity() { |
| 2572 | ShowMouseCursor(); | ||
| 2573 | } | ||
| 2574 | |||
| 2575 | void GMainWindow::mousePressEvent(QMouseEvent* event) { | ||
| 2576 | ShowMouseCursor(); | 2613 | ShowMouseCursor(); |
| 2577 | } | 2614 | } |
| 2578 | 2615 | ||
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index ea6d2c30d..31788ea62 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -248,6 +248,7 @@ private slots: | |||
| 248 | void OnCoreError(Core::System::ResultStatus, std::string); | 248 | void OnCoreError(Core::System::ResultStatus, std::string); |
| 249 | void OnReinitializeKeys(ReinitializeKeyBehavior behavior); | 249 | void OnReinitializeKeys(ReinitializeKeyBehavior behavior); |
| 250 | void OnLanguageChanged(const QString& locale); | 250 | void OnLanguageChanged(const QString& locale); |
| 251 | void OnMouseActivity(); | ||
| 251 | 252 | ||
| 252 | private: | 253 | private: |
| 253 | void RemoveBaseContent(u64 program_id, const QString& entry_type); | 254 | void RemoveBaseContent(u64 program_id, const QString& entry_type); |
| @@ -335,6 +336,4 @@ protected: | |||
| 335 | void dropEvent(QDropEvent* event) override; | 336 | void dropEvent(QDropEvent* event) override; |
| 336 | void dragEnterEvent(QDragEnterEvent* event) override; | 337 | void dragEnterEvent(QDragEnterEvent* event) override; |
| 337 | void dragMoveEvent(QDragMoveEvent* event) override; | 338 | void dragMoveEvent(QDragMoveEvent* event) override; |
| 338 | void mouseMoveEvent(QMouseEvent* event) override; | ||
| 339 | void mousePressEvent(QMouseEvent* event) override; | ||
| 340 | }; | 339 | }; |
diff --git a/src/yuzu/util/url_request_interceptor.cpp b/src/yuzu/util/url_request_interceptor.cpp index 2d491d8c0..b637e771e 100644 --- a/src/yuzu/util/url_request_interceptor.cpp +++ b/src/yuzu/util/url_request_interceptor.cpp | |||
| @@ -22,6 +22,8 @@ void UrlRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) { | |||
| 22 | case QWebEngineUrlRequestInfo::ResourceTypeXhr: | 22 | case QWebEngineUrlRequestInfo::ResourceTypeXhr: |
| 23 | emit FrameChanged(); | 23 | emit FrameChanged(); |
| 24 | break; | 24 | break; |
| 25 | default: | ||
| 26 | break; | ||
| 25 | } | 27 | } |
| 26 | } | 28 | } |
| 27 | 29 | ||
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 38075c345..41ef6f6b8 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp | |||
| @@ -344,7 +344,7 @@ void Config::ReadValues() { | |||
| 344 | 344 | ||
| 345 | // System | 345 | // System |
| 346 | Settings::values.use_docked_mode.SetValue( | 346 | Settings::values.use_docked_mode.SetValue( |
| 347 | sdl2_config->GetBoolean("System", "use_docked_mode", false)); | 347 | sdl2_config->GetBoolean("System", "use_docked_mode", true)); |
| 348 | 348 | ||
| 349 | Settings::values.current_user = std::clamp<int>( | 349 | Settings::values.current_user = std::clamp<int>( |
| 350 | sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1); | 350 | sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1); |
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 2d4b98d9a..3ee0e037d 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h | |||
| @@ -274,7 +274,7 @@ gamecard_path = | |||
| 274 | 274 | ||
| 275 | [System] | 275 | [System] |
| 276 | # Whether the system is docked | 276 | # Whether the system is docked |
| 277 | # 1: Yes, 0 (default): No | 277 | # 1 (default): Yes, 0: No |
| 278 | use_docked_mode = | 278 | use_docked_mode = |
| 279 | 279 | ||
| 280 | # Allow the use of NFC in games | 280 | # Allow the use of NFC in games |
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 2497c71ae..4faf62ede 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -95,8 +95,6 @@ int main(int argc, char** argv) { | |||
| 95 | int option_index = 0; | 95 | int option_index = 0; |
| 96 | 96 | ||
| 97 | InitializeLogging(); | 97 | InitializeLogging(); |
| 98 | |||
| 99 | char* endarg; | ||
| 100 | #ifdef _WIN32 | 98 | #ifdef _WIN32 |
| 101 | int argc_w; | 99 | int argc_w; |
| 102 | auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w); | 100 | auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w); |
| @@ -202,7 +200,7 @@ int main(int argc, char** argv) { | |||
| 202 | const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader); | 200 | const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader); |
| 203 | const u16 error_id = static_cast<u16>(load_result) - loader_id; | 201 | const u16 error_id = static_cast<u16>(load_result) - loader_id; |
| 204 | LOG_CRITICAL(Frontend, | 202 | LOG_CRITICAL(Frontend, |
| 205 | "While attempting to load the ROM requested, an error occured. Please " | 203 | "While attempting to load the ROM requested, an error occurred. Please " |
| 206 | "refer to the yuzu wiki for more information or the yuzu discord for " | 204 | "refer to the yuzu wiki for more information or the yuzu discord for " |
| 207 | "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}", | 205 | "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}", |
| 208 | loader_id, error_id, static_cast<Loader::ResultStatus>(error_id)); | 206 | loader_id, error_id, static_cast<Loader::ResultStatus>(error_id)); |
diff --git a/src/yuzu_tester/CMakeLists.txt b/src/yuzu_tester/CMakeLists.txt deleted file mode 100644 index d8a2a1511..000000000 --- a/src/yuzu_tester/CMakeLists.txt +++ /dev/null | |||
| @@ -1,32 +0,0 @@ | |||
| 1 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules) | ||
| 2 | |||
| 3 | add_executable(yuzu-tester | ||
| 4 | config.cpp | ||
| 5 | config.h | ||
| 6 | default_ini.h | ||
| 7 | emu_window/emu_window_sdl2_hide.cpp | ||
| 8 | emu_window/emu_window_sdl2_hide.h | ||
| 9 | resource.h | ||
| 10 | service/yuzutest.cpp | ||
| 11 | service/yuzutest.h | ||
| 12 | yuzu.cpp | ||
| 13 | yuzu.rc | ||
| 14 | ) | ||
| 15 | |||
| 16 | create_target_directory_groups(yuzu-tester) | ||
| 17 | |||
| 18 | target_link_libraries(yuzu-tester PRIVATE common core input_common) | ||
| 19 | target_link_libraries(yuzu-tester PRIVATE inih glad) | ||
| 20 | if (MSVC) | ||
| 21 | target_link_libraries(yuzu-tester PRIVATE getopt) | ||
| 22 | endif() | ||
| 23 | target_link_libraries(yuzu-tester PRIVATE ${PLATFORM_LIBRARIES} SDL2 Threads::Threads) | ||
| 24 | |||
| 25 | if(UNIX AND NOT APPLE) | ||
| 26 | install(TARGETS yuzu-tester RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") | ||
| 27 | endif() | ||
| 28 | |||
| 29 | if (MSVC) | ||
| 30 | include(CopyYuzuSDLDeps) | ||
| 31 | copy_yuzu_SDL_deps(yuzu-tester) | ||
| 32 | endif() | ||
diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp deleted file mode 100644 index 91684e96e..000000000 --- a/src/yuzu_tester/config.cpp +++ /dev/null | |||
| @@ -1,194 +0,0 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <memory> | ||
| 6 | #include <sstream> | ||
| 7 | #include <SDL.h> | ||
| 8 | #include <inih/cpp/INIReader.h> | ||
| 9 | #include "common/file_util.h" | ||
| 10 | #include "common/logging/log.h" | ||
| 11 | #include "common/param_package.h" | ||
| 12 | #include "core/hle/service/acc/profile_manager.h" | ||
| 13 | #include "core/settings.h" | ||
| 14 | #include "input_common/main.h" | ||
| 15 | #include "yuzu_tester/config.h" | ||
| 16 | #include "yuzu_tester/default_ini.h" | ||
| 17 | |||
| 18 | namespace FS = Common::FS; | ||
| 19 | |||
| 20 | Config::Config() { | ||
| 21 | // TODO: Don't hardcode the path; let the frontend decide where to put the config files. | ||
| 22 | sdl2_config_loc = FS::GetUserPath(FS::UserPath::ConfigDir) + "sdl2-tester-config.ini"; | ||
| 23 | sdl2_config = std::make_unique<INIReader>(sdl2_config_loc); | ||
| 24 | |||
| 25 | Reload(); | ||
| 26 | } | ||
| 27 | |||
| 28 | Config::~Config() = default; | ||
| 29 | |||
| 30 | bool Config::LoadINI(const std::string& default_contents, bool retry) { | ||
| 31 | const char* location = this->sdl2_config_loc.c_str(); | ||
| 32 | if (sdl2_config->ParseError() < 0) { | ||
| 33 | if (retry) { | ||
| 34 | LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", location); | ||
| 35 | FS::CreateFullPath(location); | ||
| 36 | FS::WriteStringToFile(true, default_contents, location); | ||
| 37 | sdl2_config = std::make_unique<INIReader>(location); // Reopen file | ||
| 38 | |||
| 39 | return LoadINI(default_contents, false); | ||
| 40 | } | ||
| 41 | LOG_ERROR(Config, "Failed."); | ||
| 42 | return false; | ||
| 43 | } | ||
| 44 | LOG_INFO(Config, "Successfully loaded {}", location); | ||
| 45 | return true; | ||
| 46 | } | ||
| 47 | |||
| 48 | void Config::ReadValues() { | ||
| 49 | // Controls | ||
| 50 | for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { | ||
| 51 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | ||
| 52 | Settings::values.players.GetValue()[p].buttons[i] = ""; | ||
| 53 | } | ||
| 54 | |||
| 55 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | ||
| 56 | Settings::values.players.GetValue()[p].analogs[i] = ""; | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | Settings::values.mouse_enabled = false; | ||
| 61 | for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { | ||
| 62 | Settings::values.mouse_buttons[i] = ""; | ||
| 63 | } | ||
| 64 | |||
| 65 | Settings::values.motion_device = ""; | ||
| 66 | |||
| 67 | Settings::values.keyboard_enabled = false; | ||
| 68 | |||
| 69 | Settings::values.debug_pad_enabled = false; | ||
| 70 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | ||
| 71 | Settings::values.debug_pad_buttons[i] = ""; | ||
| 72 | } | ||
| 73 | |||
| 74 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | ||
| 75 | Settings::values.debug_pad_analogs[i] = ""; | ||
| 76 | } | ||
| 77 | |||
| 78 | Settings::values.vibration_enabled.SetValue(true); | ||
| 79 | Settings::values.enable_accurate_vibrations.SetValue(false); | ||
| 80 | Settings::values.motion_enabled.SetValue(true); | ||
| 81 | Settings::values.touchscreen.enabled = ""; | ||
| 82 | Settings::values.touchscreen.device = ""; | ||
| 83 | Settings::values.touchscreen.finger = 0; | ||
| 84 | Settings::values.touchscreen.rotation_angle = 0; | ||
| 85 | Settings::values.touchscreen.diameter_x = 15; | ||
| 86 | Settings::values.touchscreen.diameter_y = 15; | ||
| 87 | |||
| 88 | Settings::values.use_docked_mode.SetValue( | ||
| 89 | sdl2_config->GetBoolean("Controls", "use_docked_mode", false)); | ||
| 90 | |||
| 91 | // Data Storage | ||
| 92 | Settings::values.use_virtual_sd = | ||
| 93 | sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true); | ||
| 94 | FS::GetUserPath(Common::FS::UserPath::NANDDir, | ||
| 95 | sdl2_config->Get("Data Storage", "nand_directory", | ||
| 96 | Common::FS::GetUserPath(Common::FS::UserPath::NANDDir))); | ||
| 97 | FS::GetUserPath(Common::FS::UserPath::SDMCDir, | ||
| 98 | sdl2_config->Get("Data Storage", "sdmc_directory", | ||
| 99 | Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir))); | ||
| 100 | |||
| 101 | // System | ||
| 102 | Settings::values.current_user = std::clamp<int>( | ||
| 103 | sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1); | ||
| 104 | |||
| 105 | const auto rng_seed_enabled = sdl2_config->GetBoolean("System", "rng_seed_enabled", false); | ||
| 106 | if (rng_seed_enabled) { | ||
| 107 | Settings::values.rng_seed.SetValue(sdl2_config->GetInteger("System", "rng_seed", 0)); | ||
| 108 | } else { | ||
| 109 | Settings::values.rng_seed.SetValue(std::nullopt); | ||
| 110 | } | ||
| 111 | |||
| 112 | const auto custom_rtc_enabled = sdl2_config->GetBoolean("System", "custom_rtc_enabled", false); | ||
| 113 | if (custom_rtc_enabled) { | ||
| 114 | Settings::values.custom_rtc.SetValue( | ||
| 115 | std::chrono::seconds(sdl2_config->GetInteger("System", "custom_rtc", 0))); | ||
| 116 | } else { | ||
| 117 | Settings::values.custom_rtc.SetValue(std::nullopt); | ||
| 118 | } | ||
| 119 | |||
| 120 | // Core | ||
| 121 | Settings::values.use_multi_core.SetValue( | ||
| 122 | sdl2_config->GetBoolean("Core", "use_multi_core", false)); | ||
| 123 | |||
| 124 | // Renderer | ||
| 125 | Settings::values.aspect_ratio.SetValue( | ||
| 126 | static_cast<int>(sdl2_config->GetInteger("Renderer", "aspect_ratio", 0))); | ||
| 127 | Settings::values.max_anisotropy.SetValue( | ||
| 128 | static_cast<int>(sdl2_config->GetInteger("Renderer", "max_anisotropy", 0))); | ||
| 129 | Settings::values.use_frame_limit.SetValue(false); | ||
| 130 | Settings::values.frame_limit.SetValue(100); | ||
| 131 | Settings::values.use_disk_shader_cache.SetValue( | ||
| 132 | sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false)); | ||
| 133 | const int gpu_accuracy_level = sdl2_config->GetInteger("Renderer", "gpu_accuracy", 0); | ||
| 134 | Settings::values.gpu_accuracy.SetValue(static_cast<Settings::GPUAccuracy>(gpu_accuracy_level)); | ||
| 135 | Settings::values.use_asynchronous_gpu_emulation.SetValue( | ||
| 136 | sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false)); | ||
| 137 | Settings::values.use_fast_gpu_time.SetValue( | ||
| 138 | sdl2_config->GetBoolean("Renderer", "use_fast_gpu_time", true)); | ||
| 139 | |||
| 140 | Settings::values.bg_red.SetValue( | ||
| 141 | static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0))); | ||
| 142 | Settings::values.bg_green.SetValue( | ||
| 143 | static_cast<float>(sdl2_config->GetReal("Renderer", "bg_green", 0.0))); | ||
| 144 | Settings::values.bg_blue.SetValue( | ||
| 145 | static_cast<float>(sdl2_config->GetReal("Renderer", "bg_blue", 0.0))); | ||
| 146 | |||
| 147 | // Audio | ||
| 148 | Settings::values.sink_id = "null"; | ||
| 149 | Settings::values.enable_audio_stretching.SetValue(false); | ||
| 150 | Settings::values.audio_device_id = "auto"; | ||
| 151 | Settings::values.volume.SetValue(0); | ||
| 152 | |||
| 153 | Settings::values.language_index.SetValue( | ||
| 154 | sdl2_config->GetInteger("System", "language_index", 1)); | ||
| 155 | |||
| 156 | // Miscellaneous | ||
| 157 | Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); | ||
| 158 | Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false); | ||
| 159 | |||
| 160 | // Debugging | ||
| 161 | Settings::values.program_args = ""; | ||
| 162 | Settings::values.dump_exefs = sdl2_config->GetBoolean("Debugging", "dump_exefs", false); | ||
| 163 | Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false); | ||
| 164 | |||
| 165 | const auto title_list = sdl2_config->Get("AddOns", "title_ids", ""); | ||
| 166 | std::stringstream ss(title_list); | ||
| 167 | std::string line; | ||
| 168 | while (std::getline(ss, line, '|')) { | ||
| 169 | const auto title_id = std::stoul(line, nullptr, 16); | ||
| 170 | const auto disabled_list = sdl2_config->Get("AddOns", "disabled_" + line, ""); | ||
| 171 | |||
| 172 | std::stringstream inner_ss(disabled_list); | ||
| 173 | std::string inner_line; | ||
| 174 | std::vector<std::string> out; | ||
| 175 | while (std::getline(inner_ss, inner_line, '|')) { | ||
| 176 | out.push_back(inner_line); | ||
| 177 | } | ||
| 178 | |||
| 179 | Settings::values.disabled_addons.insert_or_assign(title_id, out); | ||
| 180 | } | ||
| 181 | |||
| 182 | // Web Service | ||
| 183 | Settings::values.enable_telemetry = | ||
| 184 | sdl2_config->GetBoolean("WebService", "enable_telemetry", true); | ||
| 185 | Settings::values.web_api_url = | ||
| 186 | sdl2_config->Get("WebService", "web_api_url", "https://api.yuzu-emu.org"); | ||
| 187 | Settings::values.yuzu_username = sdl2_config->Get("WebService", "yuzu_username", ""); | ||
| 188 | Settings::values.yuzu_token = sdl2_config->Get("WebService", "yuzu_token", ""); | ||
| 189 | } | ||
| 190 | |||
| 191 | void Config::Reload() { | ||
| 192 | LoadINI(DefaultINI::sdl2_config_file); | ||
| 193 | ReadValues(); | ||
| 194 | } | ||
diff --git a/src/yuzu_tester/config.h b/src/yuzu_tester/config.h deleted file mode 100644 index 3b68e5bc9..000000000 --- a/src/yuzu_tester/config.h +++ /dev/null | |||
| @@ -1,24 +0,0 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <string> | ||
| 9 | |||
| 10 | class INIReader; | ||
| 11 | |||
| 12 | class Config { | ||
| 13 | std::unique_ptr<INIReader> sdl2_config; | ||
| 14 | std::string sdl2_config_loc; | ||
| 15 | |||
| 16 | bool LoadINI(const std::string& default_contents = "", bool retry = true); | ||
| 17 | void ReadValues(); | ||
| 18 | |||
| 19 | public: | ||
| 20 | Config(); | ||
| 21 | ~Config(); | ||
| 22 | |||
| 23 | void Reload(); | ||
| 24 | }; | ||
diff --git a/src/yuzu_tester/default_ini.h b/src/yuzu_tester/default_ini.h deleted file mode 100644 index 3eb64e9d7..000000000 --- a/src/yuzu_tester/default_ini.h +++ /dev/null | |||
| @@ -1,182 +0,0 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | namespace DefaultINI { | ||
| 8 | |||
| 9 | const char* sdl2_config_file = R"( | ||
| 10 | [Core] | ||
| 11 | # Whether to use multi-core for CPU emulation | ||
| 12 | # 0 (default): Disabled, 1: Enabled | ||
| 13 | use_multi_core= | ||
| 14 | |||
| 15 | [Cpu] | ||
| 16 | # Enable inline page tables optimization (faster guest memory access) | ||
| 17 | # 0: Disabled, 1 (default): Enabled | ||
| 18 | cpuopt_page_tables = | ||
| 19 | |||
| 20 | # Enable block linking CPU optimization (reduce block dispatcher use during predictable jumps) | ||
| 21 | # 0: Disabled, 1 (default): Enabled | ||
| 22 | cpuopt_block_linking = | ||
| 23 | |||
| 24 | # Enable return stack buffer CPU optimization (reduce block dispatcher use during predictable returns) | ||
| 25 | # 0: Disabled, 1 (default): Enabled | ||
| 26 | cpuopt_return_stack_buffer = | ||
| 27 | |||
| 28 | # Enable fast dispatcher CPU optimization (use a two-tiered dispatcher architecture) | ||
| 29 | # 0: Disabled, 1 (default): Enabled | ||
| 30 | cpuopt_fast_dispatcher = | ||
| 31 | |||
| 32 | # Enable context elimination CPU Optimization (reduce host memory use for guest context) | ||
| 33 | # 0: Disabled, 1 (default): Enabled | ||
| 34 | cpuopt_context_elimination = | ||
| 35 | |||
| 36 | # Enable constant propagation CPU optimization (basic IR optimization) | ||
| 37 | # 0: Disabled, 1 (default): Enabled | ||
| 38 | cpuopt_const_prop = | ||
| 39 | |||
| 40 | # Enable miscellaneous CPU optimizations (basic IR optimization) | ||
| 41 | # 0: Disabled, 1 (default): Enabled | ||
| 42 | cpuopt_misc_ir = | ||
| 43 | |||
| 44 | # Enable reduction of memory misalignment checks (reduce memory fallbacks for misaligned access) | ||
| 45 | # 0: Disabled, 1 (default): Enabled | ||
| 46 | cpuopt_reduce_misalign_checks = | ||
| 47 | |||
| 48 | [Renderer] | ||
| 49 | # Whether to use software or hardware rendering. | ||
| 50 | # 0: Software, 1 (default): Hardware | ||
| 51 | use_hw_renderer = | ||
| 52 | |||
| 53 | # Whether to use the Just-In-Time (JIT) compiler for shader emulation | ||
| 54 | # 0: Interpreter (slow), 1 (default): JIT (fast) | ||
| 55 | use_shader_jit = | ||
| 56 | |||
| 57 | # Aspect ratio | ||
| 58 | # 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Stretch to Window | ||
| 59 | aspect_ratio = | ||
| 60 | |||
| 61 | # Anisotropic filtering | ||
| 62 | # 0: Default, 1: 2x, 2: 4x, 3: 8x, 4: 16x | ||
| 63 | max_anisotropy = | ||
| 64 | |||
| 65 | # Whether to enable V-Sync (caps the framerate at 60FPS) or not. | ||
| 66 | # 0 (default): Off, 1: On | ||
| 67 | use_vsync = | ||
| 68 | |||
| 69 | # Whether to use disk based shader cache | ||
| 70 | # 0 (default): Off, 1 : On | ||
| 71 | use_disk_shader_cache = | ||
| 72 | |||
| 73 | # Whether to use accurate GPU emulation | ||
| 74 | # 0 (default): Off (fast), 1 : On (slow) | ||
| 75 | use_accurate_gpu_emulation = | ||
| 76 | |||
| 77 | # Whether to use asynchronous GPU emulation | ||
| 78 | # 0 : Off (slow), 1 (default): On (fast) | ||
| 79 | use_asynchronous_gpu_emulation = | ||
| 80 | |||
| 81 | # The clear color for the renderer. What shows up on the sides of the bottom screen. | ||
| 82 | # Must be in range of 0.0-1.0. Defaults to 1.0 for all. | ||
| 83 | bg_red = | ||
| 84 | bg_blue = | ||
| 85 | bg_green = | ||
| 86 | |||
| 87 | [Layout] | ||
| 88 | # Layout for the screen inside the render window. | ||
| 89 | # 0 (default): Default Top Bottom Screen, 1: Single Screen Only, 2: Large Screen Small Screen | ||
| 90 | layout_option = | ||
| 91 | |||
| 92 | # Toggle custom layout (using the settings below) on or off. | ||
| 93 | # 0 (default): Off, 1: On | ||
| 94 | custom_layout = | ||
| 95 | |||
| 96 | # Screen placement when using Custom layout option | ||
| 97 | # 0x, 0y is the top left corner of the render window. | ||
| 98 | custom_top_left = | ||
| 99 | custom_top_top = | ||
| 100 | custom_top_right = | ||
| 101 | custom_top_bottom = | ||
| 102 | custom_bottom_left = | ||
| 103 | custom_bottom_top = | ||
| 104 | custom_bottom_right = | ||
| 105 | custom_bottom_bottom = | ||
| 106 | |||
| 107 | # Swaps the prominent screen with the other screen. | ||
| 108 | # For example, if Single Screen is chosen, setting this to 1 will display the bottom screen instead of the top screen. | ||
| 109 | # 0 (default): Top Screen is prominent, 1: Bottom Screen is prominent | ||
| 110 | swap_screen = | ||
| 111 | |||
| 112 | [Data Storage] | ||
| 113 | # Whether to create a virtual SD card. | ||
| 114 | # 1 (default): Yes, 0: No | ||
| 115 | use_virtual_sd = | ||
| 116 | |||
| 117 | [System] | ||
| 118 | # Whether the system is docked | ||
| 119 | # 1: Yes, 0 (default): No | ||
| 120 | use_docked_mode = | ||
| 121 | |||
| 122 | # Allow the use of NFC in games | ||
| 123 | # 1 (default): Yes, 0 : No | ||
| 124 | enable_nfc = | ||
| 125 | |||
| 126 | # Sets the seed for the RNG generator built into the switch | ||
| 127 | # rng_seed will be ignored and randomly generated if rng_seed_enabled is false | ||
| 128 | rng_seed_enabled = | ||
| 129 | rng_seed = | ||
| 130 | |||
| 131 | # Sets the current time (in seconds since 12:00 AM Jan 1, 1970) that will be used by the time service | ||
| 132 | # This will auto-increment, with the time set being the time the game is started | ||
| 133 | # This override will only occur if custom_rtc_enabled is true, otherwise the current time is used | ||
| 134 | custom_rtc_enabled = | ||
| 135 | custom_rtc = | ||
| 136 | |||
| 137 | # Sets the account username, max length is 32 characters | ||
| 138 | # yuzu (default) | ||
| 139 | username = yuzu | ||
| 140 | |||
| 141 | # Sets the systems language index | ||
| 142 | # 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese, | ||
| 143 | # 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French, | ||
| 144 | # 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese | ||
| 145 | language_index = | ||
| 146 | |||
| 147 | # The system region that yuzu will use during emulation | ||
| 148 | # -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan | ||
| 149 | region_value = | ||
| 150 | |||
| 151 | [Miscellaneous] | ||
| 152 | # A filter which removes logs below a certain logging level. | ||
| 153 | # Examples: *:Debug Kernel.SVC:Trace Service.*:Critical | ||
| 154 | log_filter = *:Trace | ||
| 155 | |||
| 156 | [Debugging] | ||
| 157 | # Arguments to be passed to argv/argc in the emulated program. It is preferable to use the testing service datastring | ||
| 158 | program_args= | ||
| 159 | # Determines whether or not yuzu will dump the ExeFS of all games it attempts to load while loading them | ||
| 160 | dump_exefs=false | ||
| 161 | # Determines whether or not yuzu will dump all NSOs it attempts to load while loading them | ||
| 162 | dump_nso=false | ||
| 163 | |||
| 164 | [WebService] | ||
| 165 | # Whether or not to enable telemetry | ||
| 166 | # 0: No, 1 (default): Yes | ||
| 167 | enable_telemetry = | ||
| 168 | # URL for Web API | ||
| 169 | web_api_url = https://api.yuzu-emu.org | ||
| 170 | # Username and token for yuzu Web Service | ||
| 171 | # See https://profile.yuzu-emu.org/ for more info | ||
| 172 | yuzu_username = | ||
| 173 | yuzu_token = | ||
| 174 | |||
| 175 | [AddOns] | ||
| 176 | # Used to disable add-ons | ||
| 177 | # List of title IDs of games that will have add-ons disabled (separated by '|'): | ||
| 178 | title_ids = | ||
| 179 | # For each title ID, have a key/value pair called `disabled_<title_id>` equal to the names of the add-ons to disable (sep. by '|') | ||
| 180 | # e.x. disabled_0100000000010000 = Update|DLC <- disables Updates and DLC on Super Mario Odyssey | ||
| 181 | )"; | ||
| 182 | } | ||
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp deleted file mode 100644 index 358e03870..000000000 --- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp +++ /dev/null | |||
| @@ -1,146 +0,0 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <cstdlib> | ||
| 7 | #include <string> | ||
| 8 | |||
| 9 | #include <fmt/format.h> | ||
| 10 | |||
| 11 | #define SDL_MAIN_HANDLED | ||
| 12 | #include <SDL.h> | ||
| 13 | |||
| 14 | #include <glad/glad.h> | ||
| 15 | |||
| 16 | #include "common/logging/log.h" | ||
| 17 | #include "common/scm_rev.h" | ||
| 18 | #include "core/settings.h" | ||
| 19 | #include "input_common/main.h" | ||
| 20 | #include "yuzu_tester/emu_window/emu_window_sdl2_hide.h" | ||
| 21 | |||
| 22 | bool EmuWindow_SDL2_Hide::SupportsRequiredGLExtensions() { | ||
| 23 | std::vector<std::string> unsupported_ext; | ||
| 24 | |||
| 25 | if (!GLAD_GL_ARB_direct_state_access) | ||
| 26 | unsupported_ext.push_back("ARB_direct_state_access"); | ||
| 27 | if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) | ||
| 28 | unsupported_ext.push_back("ARB_vertex_type_10f_11f_11f_rev"); | ||
| 29 | if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) | ||
| 30 | unsupported_ext.push_back("ARB_texture_mirror_clamp_to_edge"); | ||
| 31 | if (!GLAD_GL_ARB_multi_bind) | ||
| 32 | unsupported_ext.push_back("ARB_multi_bind"); | ||
| 33 | |||
| 34 | // Extensions required to support some texture formats. | ||
| 35 | if (!GLAD_GL_EXT_texture_compression_s3tc) | ||
| 36 | unsupported_ext.push_back("EXT_texture_compression_s3tc"); | ||
| 37 | if (!GLAD_GL_ARB_texture_compression_rgtc) | ||
| 38 | unsupported_ext.push_back("ARB_texture_compression_rgtc"); | ||
| 39 | if (!GLAD_GL_ARB_depth_buffer_float) | ||
| 40 | unsupported_ext.push_back("ARB_depth_buffer_float"); | ||
| 41 | |||
| 42 | for (const std::string& ext : unsupported_ext) | ||
| 43 | LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext); | ||
| 44 | |||
| 45 | return unsupported_ext.empty(); | ||
| 46 | } | ||
| 47 | |||
| 48 | EmuWindow_SDL2_Hide::EmuWindow_SDL2_Hide() { | ||
| 49 | // Initialize the window | ||
| 50 | if (SDL_Init(SDL_INIT_VIDEO) < 0) { | ||
| 51 | LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); | ||
| 52 | exit(1); | ||
| 53 | } | ||
| 54 | |||
| 55 | input_subsystem->Initialize(); | ||
| 56 | |||
| 57 | SDL_SetMainReady(); | ||
| 58 | |||
| 59 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); | ||
| 60 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); | ||
| 61 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); | ||
| 62 | SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); | ||
| 63 | SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); | ||
| 64 | SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); | ||
| 65 | SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); | ||
| 66 | SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); | ||
| 67 | |||
| 68 | std::string window_title = fmt::format("yuzu-tester {} | {}-{}", Common::g_build_fullname, | ||
| 69 | Common::g_scm_branch, Common::g_scm_desc); | ||
| 70 | render_window = SDL_CreateWindow(window_title.c_str(), | ||
| 71 | SDL_WINDOWPOS_UNDEFINED, // x position | ||
| 72 | SDL_WINDOWPOS_UNDEFINED, // y position | ||
| 73 | Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, | ||
| 74 | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | | ||
| 75 | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN); | ||
| 76 | |||
| 77 | if (render_window == nullptr) { | ||
| 78 | LOG_CRITICAL(Frontend, "Failed to create SDL2 window! {}", SDL_GetError()); | ||
| 79 | exit(1); | ||
| 80 | } | ||
| 81 | |||
| 82 | gl_context = SDL_GL_CreateContext(render_window); | ||
| 83 | |||
| 84 | if (gl_context == nullptr) { | ||
| 85 | LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! {}", SDL_GetError()); | ||
| 86 | exit(1); | ||
| 87 | } | ||
| 88 | |||
| 89 | if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) { | ||
| 90 | LOG_CRITICAL(Frontend, "Failed to initialize GL functions! {}", SDL_GetError()); | ||
| 91 | exit(1); | ||
| 92 | } | ||
| 93 | |||
| 94 | if (!SupportsRequiredGLExtensions()) { | ||
| 95 | LOG_CRITICAL(Frontend, "GPU does not support all required OpenGL extensions! Exiting..."); | ||
| 96 | exit(1); | ||
| 97 | } | ||
| 98 | |||
| 99 | SDL_PumpEvents(); | ||
| 100 | SDL_GL_SetSwapInterval(false); | ||
| 101 | LOG_INFO(Frontend, "yuzu-tester Version: {} | {}-{}", Common::g_build_fullname, | ||
| 102 | Common::g_scm_branch, Common::g_scm_desc); | ||
| 103 | Settings::LogSettings(); | ||
| 104 | } | ||
| 105 | |||
| 106 | EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() { | ||
| 107 | input_subsystem->Shutdown(); | ||
| 108 | SDL_GL_DeleteContext(gl_context); | ||
| 109 | SDL_Quit(); | ||
| 110 | } | ||
| 111 | |||
| 112 | bool EmuWindow_SDL2_Hide::IsShown() const { | ||
| 113 | return false; | ||
| 114 | } | ||
| 115 | |||
| 116 | class SDLGLContext : public Core::Frontend::GraphicsContext { | ||
| 117 | public: | ||
| 118 | explicit SDLGLContext() { | ||
| 119 | // create a hidden window to make the shared context against | ||
| 120 | window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0, | ||
| 121 | SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL); | ||
| 122 | context = SDL_GL_CreateContext(window); | ||
| 123 | } | ||
| 124 | |||
| 125 | ~SDLGLContext() { | ||
| 126 | DoneCurrent(); | ||
| 127 | SDL_GL_DeleteContext(context); | ||
| 128 | SDL_DestroyWindow(window); | ||
| 129 | } | ||
| 130 | |||
| 131 | void MakeCurrent() override { | ||
| 132 | SDL_GL_MakeCurrent(window, context); | ||
| 133 | } | ||
| 134 | |||
| 135 | void DoneCurrent() override { | ||
| 136 | SDL_GL_MakeCurrent(window, nullptr); | ||
| 137 | } | ||
| 138 | |||
| 139 | private: | ||
| 140 | SDL_Window* window; | ||
| 141 | SDL_GLContext context; | ||
| 142 | }; | ||
| 143 | |||
| 144 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_Hide::CreateSharedContext() const { | ||
| 145 | return std::make_unique<SDLGLContext>(); | ||
| 146 | } | ||
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h deleted file mode 100644 index adccdf35e..000000000 --- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h +++ /dev/null | |||
| @@ -1,37 +0,0 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/frontend/emu_window.h" | ||
| 8 | |||
| 9 | struct SDL_Window; | ||
| 10 | |||
| 11 | namespace InputCommon { | ||
| 12 | class InputSubsystem; | ||
| 13 | } | ||
| 14 | |||
| 15 | class EmuWindow_SDL2_Hide : public Core::Frontend::EmuWindow { | ||
| 16 | public: | ||
| 17 | explicit EmuWindow_SDL2_Hide(); | ||
| 18 | ~EmuWindow_SDL2_Hide(); | ||
| 19 | |||
| 20 | /// Whether the screen is being shown or not. | ||
| 21 | bool IsShown() const override; | ||
| 22 | |||
| 23 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; | ||
| 24 | |||
| 25 | private: | ||
| 26 | /// Whether the GPU and driver supports the OpenGL extension required | ||
| 27 | bool SupportsRequiredGLExtensions(); | ||
| 28 | |||
| 29 | std::unique_ptr<InputCommon::InputSubsystem> input_subsystem; | ||
| 30 | |||
| 31 | /// Internal SDL2 render window | ||
| 32 | SDL_Window* render_window; | ||
| 33 | |||
| 34 | using SDL_GLContext = void*; | ||
| 35 | /// The OpenGL context associated with the window | ||
| 36 | SDL_GLContext gl_context; | ||
| 37 | }; | ||
diff --git a/src/yuzu_tester/resource.h b/src/yuzu_tester/resource.h deleted file mode 100644 index df8e459e4..000000000 --- a/src/yuzu_tester/resource.h +++ /dev/null | |||
| @@ -1,16 +0,0 @@ | |||
| 1 | //{{NO_DEPENDENCIES}} | ||
| 2 | // Microsoft Visual C++ generated include file. | ||
| 3 | // Used by pcafe.rc | ||
| 4 | // | ||
| 5 | #define IDI_ICON3 103 | ||
| 6 | |||
| 7 | // Next default values for new objects | ||
| 8 | // | ||
| 9 | #ifdef APSTUDIO_INVOKED | ||
| 10 | #ifndef APSTUDIO_READONLY_SYMBOLS | ||
| 11 | #define _APS_NEXT_RESOURCE_VALUE 105 | ||
| 12 | #define _APS_NEXT_COMMAND_VALUE 40001 | ||
| 13 | #define _APS_NEXT_CONTROL_VALUE 1001 | ||
| 14 | #define _APS_NEXT_SYMED_VALUE 101 | ||
| 15 | #endif | ||
| 16 | #endif | ||
diff --git a/src/yuzu_tester/service/yuzutest.cpp b/src/yuzu_tester/service/yuzutest.cpp deleted file mode 100644 index e257fae25..000000000 --- a/src/yuzu_tester/service/yuzutest.cpp +++ /dev/null | |||
| @@ -1,115 +0,0 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <memory> | ||
| 6 | #include "common/string_util.h" | ||
| 7 | #include "core/core.h" | ||
| 8 | #include "core/hle/ipc_helpers.h" | ||
| 9 | #include "core/hle/service/service.h" | ||
| 10 | #include "core/hle/service/sm/sm.h" | ||
| 11 | #include "yuzu_tester/service/yuzutest.h" | ||
| 12 | |||
| 13 | namespace Service::Yuzu { | ||
| 14 | |||
| 15 | constexpr u64 SERVICE_VERSION = 0x00000002; | ||
| 16 | |||
| 17 | class YuzuTest final : public ServiceFramework<YuzuTest> { | ||
| 18 | public: | ||
| 19 | explicit YuzuTest(Core::System& system_, std::string data_, | ||
| 20 | std::function<void(std::vector<TestResult>)> finish_callback_) | ||
| 21 | : ServiceFramework{system_, "yuzutest"}, data{std::move(data_)}, finish_callback{std::move( | ||
| 22 | finish_callback_)} { | ||
| 23 | static const FunctionInfo functions[] = { | ||
| 24 | {0, &YuzuTest::Initialize, "Initialize"}, | ||
| 25 | {1, &YuzuTest::GetServiceVersion, "GetServiceVersion"}, | ||
| 26 | {2, &YuzuTest::GetData, "GetData"}, | ||
| 27 | {10, &YuzuTest::StartIndividual, "StartIndividual"}, | ||
| 28 | {20, &YuzuTest::FinishIndividual, "FinishIndividual"}, | ||
| 29 | {100, &YuzuTest::ExitProgram, "ExitProgram"}, | ||
| 30 | }; | ||
| 31 | |||
| 32 | RegisterHandlers(functions); | ||
| 33 | } | ||
| 34 | |||
| 35 | private: | ||
| 36 | void Initialize(Kernel::HLERequestContext& ctx) { | ||
| 37 | LOG_DEBUG(Frontend, "called"); | ||
| 38 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 39 | rb.Push(RESULT_SUCCESS); | ||
| 40 | } | ||
| 41 | |||
| 42 | void GetServiceVersion(Kernel::HLERequestContext& ctx) { | ||
| 43 | LOG_DEBUG(Frontend, "called"); | ||
| 44 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 45 | rb.Push(RESULT_SUCCESS); | ||
| 46 | rb.Push(SERVICE_VERSION); | ||
| 47 | } | ||
| 48 | |||
| 49 | void GetData(Kernel::HLERequestContext& ctx) { | ||
| 50 | LOG_DEBUG(Frontend, "called"); | ||
| 51 | const auto size = ctx.GetWriteBufferSize(); | ||
| 52 | const auto write_size = std::min(size, data.size()); | ||
| 53 | ctx.WriteBuffer(data.data(), write_size); | ||
| 54 | |||
| 55 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 56 | rb.Push(RESULT_SUCCESS); | ||
| 57 | rb.Push<u32>(static_cast<u32>(write_size)); | ||
| 58 | } | ||
| 59 | |||
| 60 | void StartIndividual(Kernel::HLERequestContext& ctx) { | ||
| 61 | const auto name_raw = ctx.ReadBuffer(); | ||
| 62 | |||
| 63 | const auto name = Common::StringFromFixedZeroTerminatedBuffer( | ||
| 64 | reinterpret_cast<const char*>(name_raw.data()), name_raw.size()); | ||
| 65 | |||
| 66 | LOG_DEBUG(Frontend, "called, name={}", name); | ||
| 67 | |||
| 68 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 69 | rb.Push(RESULT_SUCCESS); | ||
| 70 | } | ||
| 71 | |||
| 72 | void FinishIndividual(Kernel::HLERequestContext& ctx) { | ||
| 73 | IPC::RequestParser rp{ctx}; | ||
| 74 | |||
| 75 | const auto code = rp.PopRaw<u32>(); | ||
| 76 | |||
| 77 | const auto result_data_raw = ctx.ReadBuffer(); | ||
| 78 | const auto test_name_raw = ctx.ReadBuffer(1); | ||
| 79 | |||
| 80 | const auto data = Common::StringFromFixedZeroTerminatedBuffer( | ||
| 81 | reinterpret_cast<const char*>(result_data_raw.data()), result_data_raw.size()); | ||
| 82 | const auto test_name = Common::StringFromFixedZeroTerminatedBuffer( | ||
| 83 | reinterpret_cast<const char*>(test_name_raw.data()), test_name_raw.size()); | ||
| 84 | |||
| 85 | LOG_INFO(Frontend, "called, result_code={:08X}, data={}, name={}", code, data, test_name); | ||
| 86 | |||
| 87 | results.push_back({code, data, test_name}); | ||
| 88 | |||
| 89 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 90 | rb.Push(RESULT_SUCCESS); | ||
| 91 | } | ||
| 92 | |||
| 93 | void ExitProgram(Kernel::HLERequestContext& ctx) { | ||
| 94 | LOG_DEBUG(Frontend, "called"); | ||
| 95 | |||
| 96 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 97 | rb.Push(RESULT_SUCCESS); | ||
| 98 | |||
| 99 | finish_callback(std::move(results)); | ||
| 100 | } | ||
| 101 | |||
| 102 | std::string data; | ||
| 103 | |||
| 104 | std::vector<TestResult> results; | ||
| 105 | std::function<void(std::vector<TestResult>)> finish_callback; | ||
| 106 | }; | ||
| 107 | |||
| 108 | void InstallInterfaces(Core::System& system, std::string data, | ||
| 109 | std::function<void(std::vector<TestResult>)> finish_callback) { | ||
| 110 | auto& sm = system.ServiceManager(); | ||
| 111 | std::make_shared<YuzuTest>(system, std::move(data), std::move(finish_callback)) | ||
| 112 | ->InstallAsService(sm); | ||
| 113 | } | ||
| 114 | |||
| 115 | } // namespace Service::Yuzu | ||
diff --git a/src/yuzu_tester/service/yuzutest.h b/src/yuzu_tester/service/yuzutest.h deleted file mode 100644 index 7794814fa..000000000 --- a/src/yuzu_tester/service/yuzutest.h +++ /dev/null | |||
| @@ -1,25 +0,0 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <functional> | ||
| 8 | #include <string> | ||
| 9 | |||
| 10 | namespace Core { | ||
| 11 | class System; | ||
| 12 | } | ||
| 13 | |||
| 14 | namespace Service::Yuzu { | ||
| 15 | |||
| 16 | struct TestResult { | ||
| 17 | u32 code; | ||
| 18 | std::string data; | ||
| 19 | std::string name; | ||
| 20 | }; | ||
| 21 | |||
| 22 | void InstallInterfaces(Core::System& system, std::string data, | ||
| 23 | std::function<void(std::vector<TestResult>)> finish_callback); | ||
| 24 | |||
| 25 | } // namespace Service::Yuzu | ||
diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp deleted file mode 100644 index 6435ffabb..000000000 --- a/src/yuzu_tester/yuzu.cpp +++ /dev/null | |||
| @@ -1,268 +0,0 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <chrono> | ||
| 6 | #include <iostream> | ||
| 7 | #include <memory> | ||
| 8 | #include <string> | ||
| 9 | #include <thread> | ||
| 10 | |||
| 11 | #include <fmt/ostream.h> | ||
| 12 | |||
| 13 | #include "common/common_paths.h" | ||
| 14 | #include "common/detached_tasks.h" | ||
| 15 | #include "common/file_util.h" | ||
| 16 | #include "common/logging/backend.h" | ||
| 17 | #include "common/logging/filter.h" | ||
| 18 | #include "common/logging/log.h" | ||
| 19 | #include "common/microprofile.h" | ||
| 20 | #include "common/scm_rev.h" | ||
| 21 | #include "common/scope_exit.h" | ||
| 22 | #include "common/string_util.h" | ||
| 23 | #include "common/telemetry.h" | ||
| 24 | #include "core/core.h" | ||
| 25 | #include "core/crypto/key_manager.h" | ||
| 26 | #include "core/file_sys/registered_cache.h" | ||
| 27 | #include "core/file_sys/vfs_real.h" | ||
| 28 | #include "core/hle/service/filesystem/filesystem.h" | ||
| 29 | #include "core/loader/loader.h" | ||
| 30 | #include "core/settings.h" | ||
| 31 | #include "core/telemetry_session.h" | ||
| 32 | #include "video_core/renderer_base.h" | ||
| 33 | #include "yuzu_tester/config.h" | ||
| 34 | #include "yuzu_tester/emu_window/emu_window_sdl2_hide.h" | ||
| 35 | #include "yuzu_tester/service/yuzutest.h" | ||
| 36 | |||
| 37 | #ifdef _WIN32 | ||
| 38 | // windows.h needs to be included before shellapi.h | ||
| 39 | #include <windows.h> | ||
| 40 | |||
| 41 | #include <shellapi.h> | ||
| 42 | #endif | ||
| 43 | |||
| 44 | #undef _UNICODE | ||
| 45 | #include <getopt.h> | ||
| 46 | #ifndef _MSC_VER | ||
| 47 | #include <unistd.h> | ||
| 48 | #endif | ||
| 49 | |||
| 50 | #ifdef _WIN32 | ||
| 51 | extern "C" { | ||
| 52 | // tells Nvidia and AMD drivers to use the dedicated GPU by default on laptops with switchable | ||
| 53 | // graphics | ||
| 54 | __declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001; | ||
| 55 | __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; | ||
| 56 | } | ||
| 57 | #endif | ||
| 58 | |||
| 59 | static void PrintHelp(const char* argv0) { | ||
| 60 | std::cout << "Usage: " << argv0 | ||
| 61 | << " [options] <filename>\n" | ||
| 62 | "-h, --help Display this help and exit\n" | ||
| 63 | "-v, --version Output version information and exit\n" | ||
| 64 | "-d, --datastring Pass following string as data to test service command #2\n" | ||
| 65 | "-l, --log Log to console in addition to file (will log to file only " | ||
| 66 | "by default)\n"; | ||
| 67 | } | ||
| 68 | |||
| 69 | static void PrintVersion() { | ||
| 70 | std::cout << "yuzu [Test Utility] " << Common::g_scm_branch << " " << Common::g_scm_desc | ||
| 71 | << std::endl; | ||
| 72 | } | ||
| 73 | |||
| 74 | static void InitializeLogging(bool console) { | ||
| 75 | Log::Filter log_filter(Log::Level::Debug); | ||
| 76 | log_filter.ParseFilterString(Settings::values.log_filter); | ||
| 77 | Log::SetGlobalFilter(log_filter); | ||
| 78 | |||
| 79 | if (console) | ||
| 80 | Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); | ||
| 81 | |||
| 82 | const std::string& log_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir); | ||
| 83 | Common::FS::CreateFullPath(log_dir); | ||
| 84 | Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE)); | ||
| 85 | #ifdef _WIN32 | ||
| 86 | Log::AddBackend(std::make_unique<Log::DebuggerBackend>()); | ||
| 87 | #endif | ||
| 88 | } | ||
| 89 | |||
| 90 | /// Application entry point | ||
| 91 | int main(int argc, char** argv) { | ||
| 92 | Common::DetachedTasks detached_tasks; | ||
| 93 | Config config; | ||
| 94 | |||
| 95 | int option_index = 0; | ||
| 96 | |||
| 97 | #ifdef _WIN32 | ||
| 98 | int argc_w; | ||
| 99 | auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w); | ||
| 100 | |||
| 101 | if (argv_w == nullptr) { | ||
| 102 | std::cout << "Failed to get command line arguments" << std::endl; | ||
| 103 | return -1; | ||
| 104 | } | ||
| 105 | #endif | ||
| 106 | std::string filepath; | ||
| 107 | |||
| 108 | static struct option long_options[] = { | ||
| 109 | {"help", no_argument, 0, 'h'}, | ||
| 110 | {"version", no_argument, 0, 'v'}, | ||
| 111 | {"datastring", optional_argument, 0, 'd'}, | ||
| 112 | {"log", no_argument, 0, 'l'}, | ||
| 113 | {0, 0, 0, 0}, | ||
| 114 | }; | ||
| 115 | |||
| 116 | bool console_log = false; | ||
| 117 | std::string datastring; | ||
| 118 | |||
| 119 | while (optind < argc) { | ||
| 120 | int arg = getopt_long(argc, argv, "hvdl::", long_options, &option_index); | ||
| 121 | if (arg != -1) { | ||
| 122 | switch (static_cast<char>(arg)) { | ||
| 123 | case 'h': | ||
| 124 | PrintHelp(argv[0]); | ||
| 125 | return 0; | ||
| 126 | case 'v': | ||
| 127 | PrintVersion(); | ||
| 128 | return 0; | ||
| 129 | case 'd': | ||
| 130 | datastring = argv[optind]; | ||
| 131 | ++optind; | ||
| 132 | break; | ||
| 133 | case 'l': | ||
| 134 | console_log = true; | ||
| 135 | break; | ||
| 136 | } | ||
| 137 | } else { | ||
| 138 | #ifdef _WIN32 | ||
| 139 | filepath = Common::UTF16ToUTF8(argv_w[optind]); | ||
| 140 | #else | ||
| 141 | filepath = argv[optind]; | ||
| 142 | #endif | ||
| 143 | optind++; | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | InitializeLogging(console_log); | ||
| 148 | |||
| 149 | #ifdef _WIN32 | ||
| 150 | LocalFree(argv_w); | ||
| 151 | #endif | ||
| 152 | |||
| 153 | MicroProfileOnThreadCreate("EmuThread"); | ||
| 154 | SCOPE_EXIT({ MicroProfileShutdown(); }); | ||
| 155 | |||
| 156 | if (filepath.empty()) { | ||
| 157 | LOG_CRITICAL(Frontend, "Failed to load application: No application specified"); | ||
| 158 | std::cout << "Failed to load application: No application specified" << std::endl; | ||
| 159 | PrintHelp(argv[0]); | ||
| 160 | return -1; | ||
| 161 | } | ||
| 162 | |||
| 163 | Core::System& system{Core::System::GetInstance()}; | ||
| 164 | |||
| 165 | Settings::Apply(system); | ||
| 166 | |||
| 167 | const auto emu_window{std::make_unique<EmuWindow_SDL2_Hide>()}; | ||
| 168 | |||
| 169 | bool finished = false; | ||
| 170 | int return_value = 0; | ||
| 171 | const auto callback = [&finished, | ||
| 172 | &return_value](std::vector<Service::Yuzu::TestResult> results) { | ||
| 173 | finished = true; | ||
| 174 | return_value = 0; | ||
| 175 | |||
| 176 | // Find the minimum length needed to fully enclose all test names (and the header field) in | ||
| 177 | // the fmt::format column by first finding the maximum size of any test name and comparing | ||
| 178 | // that to 9, the string length of 'Test Name' | ||
| 179 | const auto needed_length_name = | ||
| 180 | std::max<u64>(std::max_element(results.begin(), results.end(), | ||
| 181 | [](const auto& lhs, const auto& rhs) { | ||
| 182 | return lhs.name.size() < rhs.name.size(); | ||
| 183 | }) | ||
| 184 | ->name.size(), | ||
| 185 | 9ull); | ||
| 186 | |||
| 187 | std::size_t passed = 0; | ||
| 188 | std::size_t failed = 0; | ||
| 189 | |||
| 190 | std::cout << fmt::format("Result [Res Code] | {:<{}} | Extra Data", "Test Name", | ||
| 191 | needed_length_name) | ||
| 192 | << std::endl; | ||
| 193 | |||
| 194 | for (const auto& res : results) { | ||
| 195 | const auto main_res = res.code == 0 ? "PASSED" : "FAILED"; | ||
| 196 | if (res.code == 0) | ||
| 197 | ++passed; | ||
| 198 | else | ||
| 199 | ++failed; | ||
| 200 | std::cout << fmt::format("{} [{:08X}] | {:<{}} | {}", main_res, res.code, res.name, | ||
| 201 | needed_length_name, res.data) | ||
| 202 | << std::endl; | ||
| 203 | } | ||
| 204 | |||
| 205 | std::cout << std::endl | ||
| 206 | << fmt::format("{:4d} Passed | {:4d} Failed | {:4d} Total | {:2.2f} Passed Ratio", | ||
| 207 | passed, failed, passed + failed, | ||
| 208 | static_cast<float>(passed) / (passed + failed)) | ||
| 209 | << std::endl | ||
| 210 | << (failed == 0 ? "PASSED" : "FAILED") << std::endl; | ||
| 211 | |||
| 212 | if (failed > 0) | ||
| 213 | return_value = -1; | ||
| 214 | }; | ||
| 215 | |||
| 216 | system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); | ||
| 217 | system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); | ||
| 218 | system.GetFileSystemController().CreateFactories(*system.GetFilesystem()); | ||
| 219 | |||
| 220 | SCOPE_EXIT({ system.Shutdown(); }); | ||
| 221 | |||
| 222 | const Core::System::ResultStatus load_result{system.Load(*emu_window, filepath)}; | ||
| 223 | |||
| 224 | switch (load_result) { | ||
| 225 | case Core::System::ResultStatus::ErrorGetLoader: | ||
| 226 | LOG_CRITICAL(Frontend, "Failed to obtain loader for {}!", filepath); | ||
| 227 | return -1; | ||
| 228 | case Core::System::ResultStatus::ErrorLoader: | ||
| 229 | LOG_CRITICAL(Frontend, "Failed to load ROM!"); | ||
| 230 | return -1; | ||
| 231 | case Core::System::ResultStatus::ErrorNotInitialized: | ||
| 232 | LOG_CRITICAL(Frontend, "CPUCore not initialized"); | ||
| 233 | return -1; | ||
| 234 | case Core::System::ResultStatus::ErrorVideoCore: | ||
| 235 | LOG_CRITICAL(Frontend, "Failed to initialize VideoCore!"); | ||
| 236 | return -1; | ||
| 237 | case Core::System::ResultStatus::Success: | ||
| 238 | break; // Expected case | ||
| 239 | default: | ||
| 240 | if (static_cast<u32>(load_result) > | ||
| 241 | static_cast<u32>(Core::System::ResultStatus::ErrorLoader)) { | ||
| 242 | const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader); | ||
| 243 | const u16 error_id = static_cast<u16>(load_result) - loader_id; | ||
| 244 | LOG_CRITICAL(Frontend, | ||
| 245 | "While attempting to load the ROM requested, an error occured. Please " | ||
| 246 | "refer to the yuzu wiki for more information or the yuzu discord for " | ||
| 247 | "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}", | ||
| 248 | loader_id, error_id, static_cast<Loader::ResultStatus>(error_id)); | ||
| 249 | } | ||
| 250 | break; | ||
| 251 | } | ||
| 252 | |||
| 253 | Service::Yuzu::InstallInterfaces(system, datastring, callback); | ||
| 254 | |||
| 255 | system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", | ||
| 256 | "SDLHideTester"); | ||
| 257 | |||
| 258 | system.GPU().Start(); | ||
| 259 | |||
| 260 | void(system.Run()); | ||
| 261 | while (!finished) { | ||
| 262 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); | ||
| 263 | } | ||
| 264 | void(system.Pause()); | ||
| 265 | |||
| 266 | detached_tasks.WaitForAllTasks(); | ||
| 267 | return return_value; | ||
| 268 | } | ||
diff --git a/src/yuzu_tester/yuzu.rc b/src/yuzu_tester/yuzu.rc deleted file mode 100644 index 0cde75e2f..000000000 --- a/src/yuzu_tester/yuzu.rc +++ /dev/null | |||
| @@ -1,17 +0,0 @@ | |||
| 1 | #include "winresrc.h" | ||
| 2 | ///////////////////////////////////////////////////////////////////////////// | ||
| 3 | // | ||
| 4 | // Icon | ||
| 5 | // | ||
| 6 | |||
| 7 | // Icon with lowest ID value placed first to ensure application icon | ||
| 8 | // remains consistent on all systems. | ||
| 9 | YUZU_ICON ICON "../../dist/yuzu.ico" | ||
| 10 | |||
| 11 | |||
| 12 | ///////////////////////////////////////////////////////////////////////////// | ||
| 13 | // | ||
| 14 | // RT_MANIFEST | ||
| 15 | // | ||
| 16 | |||
| 17 | 0 RT_MANIFEST "../../dist/yuzu.manifest" | ||