summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.ci/scripts/linux/docker.sh5
-rw-r--r--.ci/scripts/linux/upload.sh24
-rw-r--r--src/CMakeLists.txt4
-rw-r--r--src/audio_core/sink_context.h6
-rw-r--r--src/common/CMakeLists.txt4
-rw-r--r--src/common/alignment.h29
-rw-r--r--src/common/atomic_ops.cpp75
-rw-r--r--src/common/atomic_ops.h71
-rw-r--r--src/common/bit_util.h76
-rw-r--r--src/common/color.h271
-rw-r--r--src/common/common_funcs.h8
-rw-r--r--src/common/intrusive_red_black_tree.h99
-rw-r--r--src/common/logging/backend.cpp16
-rw-r--r--src/common/timer.cpp159
-rw-r--r--src/common/timer.h41
-rw-r--r--src/common/tree.h1410
-rw-r--r--src/common/x64/native_clock.cpp110
-rw-r--r--src/common/x64/native_clock.h21
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/file_sys/content_archive.cpp24
-rw-r--r--src/core/frontend/input_interpreter.cpp4
-rw-r--r--src/core/frontend/input_interpreter.h21
-rw-r--r--src/core/hle/ipc.h4
-rw-r--r--src/core/hle/kernel/k_priority_queue.h4
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp8
-rw-r--r--src/core/hle/kernel/memory/page_heap.h4
-rw-r--r--src/core/hle/kernel/process_capability.cpp4
-rw-r--r--src/core/hle/service/am/applets/error.cpp10
-rw-r--r--src/tests/CMakeLists.txt1
-rw-r--r--src/tests/common/bit_utils.cpp23
-rw-r--r--src/video_core/CMakeLists.txt6
-rw-r--r--src/video_core/cdma_pusher.cpp4
-rw-r--r--src/video_core/cdma_pusher.h2
-rw-r--r--src/video_core/command_classes/codecs/h264.cpp4
-rw-r--r--src/video_core/engines/fermi_2d.h30
-rw-r--r--src/video_core/engines/kepler_compute.h16
-rw-r--r--src/video_core/engines/kepler_memory.h4
-rw-r--r--src/video_core/engines/maxwell_3d.h150
-rw-r--r--src/video_core/engines/shader_header.h38
-rw-r--r--src/video_core/gpu.h8
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp10
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp22
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.h10
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp44
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h22
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.cpp32
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.h15
-rw-r--r--src/video_core/renderer_vulkan/vk_fence_manager.cpp54
-rw-r--r--src/video_core/renderer_vulkan/vk_fence_manager.h15
-rw-r--r--src/video_core/renderer_vulkan/vk_memory_manager.cpp230
-rw-r--r--src/video_core/renderer_vulkan/vk_memory_manager.h132
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp17
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h10
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp126
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.h62
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp22
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h22
-rw-r--r--src/video_core/texture_cache/accelerated_swizzle.cpp4
-rw-r--r--src/video_core/texture_cache/util.cpp10
-rw-r--r--src/video_core/textures/astc.cpp41
-rw-r--r--src/video_core/textures/decoders.cpp8
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp11
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h35
-rw-r--r--src/video_core/vulkan_common/vulkan_memory_allocator.cpp268
-rw-r--r--src/video_core/vulkan_common/vulkan_memory_allocator.h118
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.h284
-rw-r--r--src/yuzu/bootmanager.cpp4
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp22
-rw-r--r--src/yuzu/configuration/configure_motion_touch.cpp22
-rw-r--r--src/yuzu/game_list.cpp2
-rw-r--r--src/yuzu_tester/CMakeLists.txt32
-rw-r--r--src/yuzu_tester/config.cpp194
-rw-r--r--src/yuzu_tester/config.h24
-rw-r--r--src/yuzu_tester/default_ini.h182
-rw-r--r--src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp146
-rw-r--r--src/yuzu_tester/emu_window/emu_window_sdl2_hide.h37
-rw-r--r--src/yuzu_tester/resource.h16
-rw-r--r--src/yuzu_tester/service/yuzutest.cpp115
-rw-r--r--src/yuzu_tester/service/yuzutest.h25
-rw-r--r--src/yuzu_tester/yuzu.cpp268
-rw-r--r--src/yuzu_tester/yuzu.rc17
82 files changed, 1924 insertions, 3610 deletions
diff --git a/.ci/scripts/linux/docker.sh b/.ci/scripts/linux/docker.sh
index 30391f6ad..39b1f77d7 100755
--- a/.ci/scripts/linux/docker.sh
+++ b/.ci/scripts/linux/docker.sh
@@ -22,12 +22,10 @@ rm -vf AppDir/usr/bin/yuzu-cmd AppDir/usr/bin/yuzu-tester
22# Download tools needed to build an AppImage 22# Download tools needed to build an AppImage
23wget -nc https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage 23wget -nc https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
24wget -nc https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage 24wget -nc https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage
25wget -nc https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
26wget -nc https://github.com/darealshinji/AppImageKit-checkrt/releases/download/continuous/AppRun-patched-x86_64 25wget -nc https://github.com/darealshinji/AppImageKit-checkrt/releases/download/continuous/AppRun-patched-x86_64
27wget -nc https://github.com/darealshinji/AppImageKit-checkrt/releases/download/continuous/exec-x86_64.so 26wget -nc https://github.com/darealshinji/AppImageKit-checkrt/releases/download/continuous/exec-x86_64.so
28# Set executable bit 27# Set executable bit
29chmod 755 \ 28chmod 755 \
30 appimagetool-x86_64.AppImage \
31 AppRun-patched-x86_64 \ 29 AppRun-patched-x86_64 \
32 exec-x86_64.so \ 30 exec-x86_64.so \
33 linuxdeploy-x86_64.AppImage \ 31 linuxdeploy-x86_64.AppImage \
@@ -49,6 +47,3 @@ cp exec-x86_64.so AppDir/usr/optional/exec.so
49cp AppRun-patched-x86_64 AppDir/AppRun 47cp AppRun-patched-x86_64 AppDir/AppRun
50cp --dereference /usr/lib/x86_64-linux-gnu/libstdc++.so.6 AppDir/usr/optional/libstdc++/libstdc++.so.6 48cp --dereference /usr/lib/x86_64-linux-gnu/libstdc++.so.6 AppDir/usr/optional/libstdc++/libstdc++.so.6
51cp --dereference /lib/x86_64-linux-gnu/libgcc_s.so.1 AppDir/usr/optional/libgcc_s/libgcc_s.so.1 49cp --dereference /lib/x86_64-linux-gnu/libgcc_s.so.1 AppDir/usr/optional/libgcc_s/libgcc_s.so.1
52
53# Build the AppImage
54./appimagetool-x86_64.AppImage AppDir
diff --git a/.ci/scripts/linux/upload.sh b/.ci/scripts/linux/upload.sh
index 7175e4cb5..b2ea07388 100644
--- a/.ci/scripts/linux/upload.sh
+++ b/.ci/scripts/linux/upload.sh
@@ -2,8 +2,7 @@
2 2
3. .ci/scripts/common/pre-upload.sh 3. .ci/scripts/common/pre-upload.sh
4 4
5APPIMAGE_NAME="yuzu-x86_64.AppImage" 5APPIMAGE_NAME="yuzu-${GITDATE}-${GITREV}.AppImage"
6NEW_APPIMAGE_NAME="yuzu-${GITDATE}-${GITREV}-x86_64.AppImage"
7REV_NAME="yuzu-linux-${GITDATE}-${GITREV}" 6REV_NAME="yuzu-linux-${GITDATE}-${GITREV}"
8ARCHIVE_NAME="${REV_NAME}.tar.xz" 7ARCHIVE_NAME="${REV_NAME}.tar.xz"
9COMPRESSION_FLAGS="-cJvf" 8COMPRESSION_FLAGS="-cJvf"
@@ -19,7 +18,24 @@ mkdir "$DIR_NAME"
19cp build/bin/yuzu-cmd "$DIR_NAME" 18cp build/bin/yuzu-cmd "$DIR_NAME"
20cp build/bin/yuzu "$DIR_NAME" 19cp build/bin/yuzu "$DIR_NAME"
21 20
22# Copy the AppImage to the artifacts directory and avoid compressing it 21# Build an AppImage
23cp "build/${APPIMAGE_NAME}" "${ARTIFACTS_DIR}/${NEW_APPIMAGE_NAME}" 22cd build
23
24wget -nc https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
25chmod 755 appimagetool-x86_64.AppImage
26
27if [ "${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}"
30else
31 ./appimagetool-x86_64.AppImage AppDir "${APPIMAGE_NAME}"
32fi
33cd ..
34
35# Copy the AppImage and update info to the artifacts directory and avoid compressing it
36cp "build/${APPIMAGE_NAME}" "${ARTIFACTS_DIR}/"
37if [ -f "build/${APPIMAGE_NAME}.zsync" ]; then
38 cp "build/${APPIMAGE_NAME}.zsync" "${ARTIFACTS_DIR}/"
39fi
24 40
25. .ci/scripts/common/post-upload.sh 41. .ci/scripts/common/post-upload.sh
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 61adbef28..478246b6f 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -67,8 +67,11 @@ else()
67 -Werror=implicit-fallthrough 67 -Werror=implicit-fallthrough
68 -Werror=missing-declarations 68 -Werror=missing-declarations
69 -Werror=reorder 69 -Werror=reorder
70 -Werror=switch
70 -Werror=uninitialized 71 -Werror=uninitialized
72 -Werror=unused-function
71 -Werror=unused-result 73 -Werror=unused-result
74 -Werror=unused-variable
72 -Wextra 75 -Wextra
73 -Wmissing-declarations 76 -Wmissing-declarations
74 -Wno-attributes 77 -Wno-attributes
@@ -127,7 +130,6 @@ add_subdirectory(tests)
127 130
128if (ENABLE_SDL2) 131if (ENABLE_SDL2)
129 add_subdirectory(yuzu_cmd) 132 add_subdirectory(yuzu_cmd)
130 add_subdirectory(yuzu_tester)
131endif() 133endif()
132 134
133if (ENABLE_QT) 135if (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 5d781cd77..f77575a00 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -98,7 +98,6 @@ add_library(common STATIC
98 algorithm.h 98 algorithm.h
99 alignment.h 99 alignment.h
100 assert.h 100 assert.h
101 atomic_ops.cpp
102 atomic_ops.h 101 atomic_ops.h
103 detached_tasks.cpp 102 detached_tasks.cpp
104 detached_tasks.h 103 detached_tasks.h
@@ -108,7 +107,6 @@ add_library(common STATIC
108 bit_util.h 107 bit_util.h
109 cityhash.cpp 108 cityhash.cpp
110 cityhash.h 109 cityhash.h
111 color.h
112 common_funcs.h 110 common_funcs.h
113 common_paths.h 111 common_paths.h
114 common_types.h 112 common_types.h
@@ -167,8 +165,6 @@ add_library(common STATIC
167 threadsafe_queue.h 165 threadsafe_queue.h
168 time_zone.cpp 166 time_zone.cpp
169 time_zone.h 167 time_zone.h
170 timer.cpp
171 timer.h
172 tree.h 168 tree.h
173 uint128.cpp 169 uint128.cpp
174 uint128.h 170 uint128.h
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 @@
9namespace Common { 9namespace Common {
10 10
11template <typename T> 11template <typename T>
12[[nodiscard]] constexpr T AlignUp(T value, std::size_t size) { 12requires 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
19template <typename T> 18template <typename T>
20[[nodiscard]] constexpr T AlignDown(T value, std::size_t size) { 19requires 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
25template <typename T> 23template <typename T>
26[[nodiscard]] constexpr T AlignBits(T value, std::size_t align) { 24requires 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
31template <typename T> 28template <typename T>
32[[nodiscard]] constexpr bool Is4KBAligned(T value) { 29requires 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
37template <typename T> 33template <typename T>
38[[nodiscard]] constexpr bool IsWordAligned(T value) { 34requires 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
43template <typename T> 38template <typename T>
44[[nodiscard]] constexpr bool IsAligned(T value, std::size_t alignment) { 39requires 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
50template <typename T, std::size_t Align = 16> 45template <typename T, size_t Align = 16>
51class AlignmentAllocator { 46class AlignmentAllocator {
52public: 47public:
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/atomic_ops.cpp b/src/common/atomic_ops.cpp
deleted file mode 100644
index 1612d0e67..000000000
--- a/src/common/atomic_ops.cpp
+++ /dev/null
@@ -1,75 +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 <cstring>
6
7#include "common/atomic_ops.h"
8
9#if _MSC_VER
10#include <intrin.h>
11#endif
12
13namespace Common {
14
15#if _MSC_VER
16
17bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
18 const u8 result =
19 _InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected);
20 return result == expected;
21}
22
23bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) {
24 const u16 result =
25 _InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected);
26 return result == expected;
27}
28
29bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) {
30 const u32 result =
31 _InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected);
32 return result == expected;
33}
34
35bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) {
36 const u64 result = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer),
37 value, expected);
38 return result == expected;
39}
40
41bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
42 return _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1],
43 value[0],
44 reinterpret_cast<__int64*>(expected.data())) != 0;
45}
46
47#else
48
49bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
50 return __sync_bool_compare_and_swap(pointer, expected, value);
51}
52
53bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) {
54 return __sync_bool_compare_and_swap(pointer, expected, value);
55}
56
57bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) {
58 return __sync_bool_compare_and_swap(pointer, expected, value);
59}
60
61bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) {
62 return __sync_bool_compare_and_swap(pointer, expected, value);
63}
64
65bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
66 unsigned __int128 value_a;
67 unsigned __int128 expected_a;
68 std::memcpy(&value_a, value.data(), sizeof(u128));
69 std::memcpy(&expected_a, expected.data(), sizeof(u128));
70 return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
71}
72
73#endif
74
75} // namespace Common
diff --git a/src/common/atomic_ops.h b/src/common/atomic_ops.h
index b46888589..2b1f515e8 100644
--- a/src/common/atomic_ops.h
+++ b/src/common/atomic_ops.h
@@ -4,14 +4,75 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <cstring>
8#include <memory>
9
7#include "common/common_types.h" 10#include "common/common_types.h"
8 11
12#if _MSC_VER
13#include <intrin.h>
14#endif
15
9namespace Common { 16namespace Common {
10 17
11[[nodiscard]] bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected); 18#if _MSC_VER
12[[nodiscard]] bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected); 19
13[[nodiscard]] bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected); 20[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
14[[nodiscard]] bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected); 21 const u8 result =
15[[nodiscard]] bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected); 22 _InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected);
23 return result == expected;
24}
25
26[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) {
27 const u16 result =
28 _InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected);
29 return result == expected;
30}
31
32[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) {
33 const u32 result =
34 _InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected);
35 return result == expected;
36}
37
38[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) {
39 const u64 result = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer),
40 value, expected);
41 return result == expected;
42}
43
44[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
45 return _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1],
46 value[0],
47 reinterpret_cast<__int64*>(expected.data())) != 0;
48}
49
50#else
51
52[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
53 return __sync_bool_compare_and_swap(pointer, expected, value);
54}
55
56[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) {
57 return __sync_bool_compare_and_swap(pointer, expected, value);
58}
59
60[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) {
61 return __sync_bool_compare_and_swap(pointer, expected, value);
62}
63
64[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) {
65 return __sync_bool_compare_and_swap(pointer, expected, value);
66}
67
68[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
69 unsigned __int128 value_a;
70 unsigned __int128 expected_a;
71 std::memcpy(&value_a, value.data(), sizeof(u128));
72 std::memcpy(&expected_a, expected.data(), sizeof(u128));
73 return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
74}
75
76#endif
16 77
17} // namespace Common 78} // namespace Common
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
13namespace 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 */
152inline 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 */
164inline 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 */
175inline 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 */
184inline 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 */
196inline 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 */
208inline 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 */
220inline 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 */
230inline 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 */
242inline 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 */
255inline 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 */
267inline 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 c90978f9c..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
diff --git a/src/common/intrusive_red_black_tree.h b/src/common/intrusive_red_black_tree.h
index fb55de94e..c0bbcd457 100644
--- a/src/common/intrusive_red_black_tree.h
+++ b/src/common/intrusive_red_black_tree.h
@@ -16,17 +16,30 @@ class IntrusiveRedBlackTreeImpl;
16} 16}
17 17
18struct IntrusiveRedBlackTreeNode { 18struct IntrusiveRedBlackTreeNode {
19public:
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 }
19 35
20private: 36private:
21 RB_ENTRY(IntrusiveRedBlackTreeNode) entry{}; 37 EntryType entry{};
22 38
23 friend class impl::IntrusiveRedBlackTreeImpl; 39 friend class impl::IntrusiveRedBlackTreeImpl;
24 40
25 template <class, class, class> 41 template <class, class, class>
26 friend class IntrusiveRedBlackTree; 42 friend class IntrusiveRedBlackTree;
27
28public:
29 constexpr IntrusiveRedBlackTreeNode() = default;
30}; 43};
31 44
32template <class T, class Traits, class Comparator> 45template <class T, class Traits, class Comparator>
@@ -35,17 +48,12 @@ class IntrusiveRedBlackTree;
35namespace impl { 48namespace impl {
36 49
37class IntrusiveRedBlackTreeImpl { 50class IntrusiveRedBlackTreeImpl {
38
39private: 51private:
40 template <class, class, class> 52 template <class, class, class>
41 friend class ::Common::IntrusiveRedBlackTree; 53 friend class ::Common::IntrusiveRedBlackTree;
42 54
43private: 55 using RootType = RBHead<IntrusiveRedBlackTreeNode>;
44 RB_HEAD(IntrusiveRedBlackTreeRoot, IntrusiveRedBlackTreeNode); 56 RootType root;
45 using RootType = IntrusiveRedBlackTreeRoot;
46
47private:
48 IntrusiveRedBlackTreeRoot root;
49 57
50public: 58public:
51 template <bool Const> 59 template <bool Const>
@@ -121,57 +129,45 @@ public:
121 } 129 }
122 }; 130 };
123 131
124protected:
125 // Generate static implementations for non-comparison operations for IntrusiveRedBlackTreeRoot.
126 RB_GENERATE_WITHOUT_COMPARE_STATIC(IntrusiveRedBlackTreeRoot, IntrusiveRedBlackTreeNode, entry);
127
128private: 132private:
129 // Define accessors using RB_* functions. 133 // Define accessors using RB_* functions.
130 constexpr void InitializeImpl() {
131 RB_INIT(&this->root);
132 }
133
134 bool EmptyImpl() const { 134 bool EmptyImpl() const {
135 return RB_EMPTY(&this->root); 135 return root.IsEmpty();
136 } 136 }
137 137
138 IntrusiveRedBlackTreeNode* GetMinImpl() const { 138 IntrusiveRedBlackTreeNode* GetMinImpl() const {
139 return RB_MIN(IntrusiveRedBlackTreeRoot, 139 return RB_MIN(const_cast<RootType*>(&root));
140 const_cast<IntrusiveRedBlackTreeRoot*>(&this->root));
141 } 140 }
142 141
143 IntrusiveRedBlackTreeNode* GetMaxImpl() const { 142 IntrusiveRedBlackTreeNode* GetMaxImpl() const {
144 return RB_MAX(IntrusiveRedBlackTreeRoot, 143 return RB_MAX(const_cast<RootType*>(&root));
145 const_cast<IntrusiveRedBlackTreeRoot*>(&this->root));
146 } 144 }
147 145
148 IntrusiveRedBlackTreeNode* RemoveImpl(IntrusiveRedBlackTreeNode* node) { 146 IntrusiveRedBlackTreeNode* RemoveImpl(IntrusiveRedBlackTreeNode* node) {
149 return RB_REMOVE(IntrusiveRedBlackTreeRoot, &this->root, node); 147 return RB_REMOVE(&root, node);
150 } 148 }
151 149
152public: 150public:
153 static IntrusiveRedBlackTreeNode* GetNext(IntrusiveRedBlackTreeNode* node) { 151 static IntrusiveRedBlackTreeNode* GetNext(IntrusiveRedBlackTreeNode* node) {
154 return RB_NEXT(IntrusiveRedBlackTreeRoot, nullptr, node); 152 return RB_NEXT(node);
155 } 153 }
156 154
157 static IntrusiveRedBlackTreeNode* GetPrev(IntrusiveRedBlackTreeNode* node) { 155 static IntrusiveRedBlackTreeNode* GetPrev(IntrusiveRedBlackTreeNode* node) {
158 return RB_PREV(IntrusiveRedBlackTreeRoot, nullptr, node); 156 return RB_PREV(node);
159 } 157 }
160 158
161 static IntrusiveRedBlackTreeNode const* GetNext(const IntrusiveRedBlackTreeNode* node) { 159 static const IntrusiveRedBlackTreeNode* GetNext(const IntrusiveRedBlackTreeNode* node) {
162 return static_cast<const IntrusiveRedBlackTreeNode*>( 160 return static_cast<const IntrusiveRedBlackTreeNode*>(
163 GetNext(const_cast<IntrusiveRedBlackTreeNode*>(node))); 161 GetNext(const_cast<IntrusiveRedBlackTreeNode*>(node)));
164 } 162 }
165 163
166 static IntrusiveRedBlackTreeNode const* GetPrev(const IntrusiveRedBlackTreeNode* node) { 164 static const IntrusiveRedBlackTreeNode* GetPrev(const IntrusiveRedBlackTreeNode* node) {
167 return static_cast<const IntrusiveRedBlackTreeNode*>( 165 return static_cast<const IntrusiveRedBlackTreeNode*>(
168 GetPrev(const_cast<IntrusiveRedBlackTreeNode*>(node))); 166 GetPrev(const_cast<IntrusiveRedBlackTreeNode*>(node)));
169 } 167 }
170 168
171public: 169public:
172 constexpr IntrusiveRedBlackTreeImpl() : root() { 170 constexpr IntrusiveRedBlackTreeImpl() {}
173 this->InitializeImpl();
174 }
175 171
176 // Iterator accessors. 172 // Iterator accessors.
177 iterator begin() { 173 iterator begin() {
@@ -269,8 +265,6 @@ private:
269 ImplType impl{}; 265 ImplType impl{};
270 266
271public: 267public:
272 struct IntrusiveRedBlackTreeRootWithCompare : ImplType::IntrusiveRedBlackTreeRoot {};
273
274 template <bool Const> 268 template <bool Const>
275 class Iterator; 269 class Iterator;
276 270
@@ -363,11 +357,6 @@ public:
363 }; 357 };
364 358
365private: 359private:
366 // Generate static implementations for comparison operations for IntrusiveRedBlackTreeRoot.
367 RB_GENERATE_WITH_COMPARE_STATIC(IntrusiveRedBlackTreeRootWithCompare, IntrusiveRedBlackTreeNode,
368 entry, CompareImpl, LightCompareImpl);
369
370private:
371 static int CompareImpl(const IntrusiveRedBlackTreeNode* lhs, 360 static int CompareImpl(const IntrusiveRedBlackTreeNode* lhs,
372 const IntrusiveRedBlackTreeNode* rhs) { 361 const IntrusiveRedBlackTreeNode* rhs) {
373 return Comparator::Compare(*Traits::GetParent(lhs), *Traits::GetParent(rhs)); 362 return Comparator::Compare(*Traits::GetParent(lhs), *Traits::GetParent(rhs));
@@ -379,41 +368,27 @@ private:
379 368
380 // Define accessors using RB_* functions. 369 // Define accessors using RB_* functions.
381 IntrusiveRedBlackTreeNode* InsertImpl(IntrusiveRedBlackTreeNode* node) { 370 IntrusiveRedBlackTreeNode* InsertImpl(IntrusiveRedBlackTreeNode* node) {
382 return RB_INSERT(IntrusiveRedBlackTreeRootWithCompare, 371 return RB_INSERT(&impl.root, node, CompareImpl);
383 static_cast<IntrusiveRedBlackTreeRootWithCompare*>(&this->impl.root),
384 node);
385 } 372 }
386 373
387 IntrusiveRedBlackTreeNode* FindImpl(const IntrusiveRedBlackTreeNode* node) const { 374 IntrusiveRedBlackTreeNode* FindImpl(const IntrusiveRedBlackTreeNode* node) const {
388 return RB_FIND( 375 return RB_FIND(const_cast<ImplType::RootType*>(&impl.root),
389 IntrusiveRedBlackTreeRootWithCompare, 376 const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl);
390 const_cast<IntrusiveRedBlackTreeRootWithCompare*>(
391 static_cast<const IntrusiveRedBlackTreeRootWithCompare*>(&this->impl.root)),
392 const_cast<IntrusiveRedBlackTreeNode*>(node));
393 } 377 }
394 378
395 IntrusiveRedBlackTreeNode* NFindImpl(const IntrusiveRedBlackTreeNode* node) const { 379 IntrusiveRedBlackTreeNode* NFindImpl(const IntrusiveRedBlackTreeNode* node) const {
396 return RB_NFIND( 380 return RB_NFIND(const_cast<ImplType::RootType*>(&impl.root),
397 IntrusiveRedBlackTreeRootWithCompare, 381 const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl);
398 const_cast<IntrusiveRedBlackTreeRootWithCompare*>(
399 static_cast<const IntrusiveRedBlackTreeRootWithCompare*>(&this->impl.root)),
400 const_cast<IntrusiveRedBlackTreeNode*>(node));
401 } 382 }
402 383
403 IntrusiveRedBlackTreeNode* FindLightImpl(const_light_pointer lelm) const { 384 IntrusiveRedBlackTreeNode* FindLightImpl(const_light_pointer lelm) const {
404 return RB_FIND_LIGHT( 385 return RB_FIND_LIGHT(const_cast<ImplType::RootType*>(&impl.root),
405 IntrusiveRedBlackTreeRootWithCompare, 386 static_cast<const void*>(lelm), LightCompareImpl);
406 const_cast<IntrusiveRedBlackTreeRootWithCompare*>(
407 static_cast<const IntrusiveRedBlackTreeRootWithCompare*>(&this->impl.root)),
408 static_cast<const void*>(lelm));
409 } 387 }
410 388
411 IntrusiveRedBlackTreeNode* NFindLightImpl(const_light_pointer lelm) const { 389 IntrusiveRedBlackTreeNode* NFindLightImpl(const_light_pointer lelm) const {
412 return RB_NFIND_LIGHT( 390 return RB_NFIND_LIGHT(const_cast<ImplType::RootType*>(&impl.root),
413 IntrusiveRedBlackTreeRootWithCompare, 391 static_cast<const void*>(lelm), LightCompareImpl);
414 const_cast<IntrusiveRedBlackTreeRootWithCompare*>(
415 static_cast<const IntrusiveRedBlackTreeRootWithCompare*>(&this->impl.root)),
416 static_cast<const void*>(lelm));
417 } 392 }
418 393
419public: 394public:
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. 148FileBackend::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")) {
150FileBackend::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
153void FileBackend::Write(const Entry& entry) { 161void 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/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
11namespace Common {
12
13std::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
23Timer::Timer() : m_LastTime(0), m_StartTime(0), m_Running(false) {
24 Update();
25}
26
27// Write the starting time
28void Timer::Start() {
29 m_StartTime = GetTimeMs();
30 m_Running = true;
31}
32
33// Stop the timer
34void Timer::Stop() {
35 // Write the final time
36 m_LastTime = GetTimeMs();
37 m_Running = false;
38}
39
40// Update the last time variable
41void 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()
51std::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.
57void Timer::AddTimeDifference() {
58 m_StartTime += GetTimeDifference();
59}
60
61// Get the time elapsed since the Start()
62std::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()
76std::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
101std::chrono::seconds Timer::GetTimeSinceJan1970() {
102 return std::chrono::duration_cast<std::chrono::seconds>(GetTimeMs());
103}
104
105std::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.
127std::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// ----------------
143double 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
11namespace Common {
12class Timer {
13public:
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
35private:
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
index a6b636646..3da49e422 100644
--- a/src/common/tree.h
+++ b/src/common/tree.h
@@ -27,33 +27,10 @@
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */ 28 */
29 29
30#ifndef _SYS_TREE_H_ 30#pragma once
31#define _SYS_TREE_H_
32
33/* FreeBSD <sys/cdefs.h> has a lot of defines we don't really want. */
34/* tree.h only actually uses __inline and __unused, so we'll just define those. */
35
36/* #include <sys/cdefs.h> */
37
38#ifndef __inline
39#define __inline inline
40#endif
41 31
42/* 32/*
43 * This file defines data structures for different types of trees: 33 * This file defines data structures for red-black trees.
44 * splay trees and red-black trees.
45 *
46 * A splay tree is a self-organizing data structure. Every operation
47 * on the tree causes a splay to happen. The splay moves the requested
48 * node to the root of the tree and partly rebalances it.
49 *
50 * This has the benefit that request locality causes faster lookups as
51 * the requested nodes move to the top of the tree. On the other hand,
52 * every lookup causes memory writes.
53 *
54 * The Balance Theorem bounds the total access time for m operations
55 * and n inserts on an initially empty tree as O((m + n)lg n). The
56 * amortized cost for a sequence of m accesses to a splay tree is O(lg n);
57 * 34 *
58 * A red-black tree is a binary search tree with the node color as an 35 * A red-black tree is a binary search tree with the node color as an
59 * extra attribute. It fulfills a set of conditions: 36 * extra attribute. It fulfills a set of conditions:
@@ -66,757 +43,632 @@
66 * The maximum height of a red-black tree is 2lg (n+1). 43 * The maximum height of a red-black tree is 2lg (n+1).
67 */ 44 */
68 45
69#define SPLAY_HEAD(name, type) \ 46namespace Common {
70 struct name { \ 47template <typename T>
71 struct type* sph_root; /* root of the tree */ \ 48class RBHead {
72 } 49public:
73 50 [[nodiscard]] T* Root() {
74#define SPLAY_INITIALIZER(root) \ 51 return rbh_root;
75 { NULL } 52 }
76 53
77#define SPLAY_INIT(root) \ 54 [[nodiscard]] const T* Root() const {
78 do { \ 55 return rbh_root;
79 (root)->sph_root = NULL; \ 56 }
80 } while (/*CONSTCOND*/ 0) 57
81 58 void SetRoot(T* root) {
82#define SPLAY_ENTRY(type) \ 59 rbh_root = root;
83 struct { \ 60 }
84 struct type* spe_left; /* left element */ \ 61
85 struct type* spe_right; /* right element */ \ 62 [[nodiscard]] bool IsEmpty() const {
86 } 63 return Root() == nullptr;
87 64 }
88#define SPLAY_LEFT(elm, field) (elm)->field.spe_left 65
89#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right 66private:
90#define SPLAY_ROOT(head) (head)->sph_root 67 T* rbh_root = nullptr;
91#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) 68};
92 69
93/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ 70enum class EntryColor {
94#define SPLAY_ROTATE_RIGHT(head, tmp, field) \ 71 Black,
95 do { \ 72 Red,
96 SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ 73};
97 SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ 74
98 (head)->sph_root = tmp; \ 75template <typename T>
99 } while (/*CONSTCOND*/ 0) 76class RBEntry {
100 77public:
101#define SPLAY_ROTATE_LEFT(head, tmp, field) \ 78 [[nodiscard]] T* Left() {
102 do { \ 79 return rbe_left;
103 SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ 80 }
104 SPLAY_LEFT(tmp, field) = (head)->sph_root; \ 81
105 (head)->sph_root = tmp; \ 82 [[nodiscard]] const T* Left() const {
106 } while (/*CONSTCOND*/ 0) 83 return rbe_left;
107 84 }
108#define SPLAY_LINKLEFT(head, tmp, field) \ 85
109 do { \ 86 void SetLeft(T* left) {
110 SPLAY_LEFT(tmp, field) = (head)->sph_root; \ 87 rbe_left = left;
111 tmp = (head)->sph_root; \ 88 }
112 (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ 89
113 } while (/*CONSTCOND*/ 0) 90 [[nodiscard]] T* Right() {
114 91 return rbe_right;
115#define SPLAY_LINKRIGHT(head, tmp, field) \ 92 }
116 do { \ 93
117 SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ 94 [[nodiscard]] const T* Right() const {
118 tmp = (head)->sph_root; \ 95 return rbe_right;
119 (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ 96 }
120 } while (/*CONSTCOND*/ 0) 97
121 98 void SetRight(T* right) {
122#define SPLAY_ASSEMBLE(head, node, left, right, field) \ 99 rbe_right = right;
123 do { \ 100 }
124 SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ 101
125 SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field); \ 102 [[nodiscard]] T* Parent() {
126 SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ 103 return rbe_parent;
127 SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ 104 }
128 } while (/*CONSTCOND*/ 0) 105
129 106 [[nodiscard]] const T* Parent() const {
130/* Generates prototypes and inline functions */ 107 return rbe_parent;
131 108 }
132#define SPLAY_PROTOTYPE(name, type, field, cmp) \ 109
133 void name##_SPLAY(struct name*, struct type*); \ 110 void SetParent(T* parent) {
134 void name##_SPLAY_MINMAX(struct name*, int); \ 111 rbe_parent = parent;
135 struct type* name##_SPLAY_INSERT(struct name*, struct type*); \ 112 }
136 struct type* name##_SPLAY_REMOVE(struct name*, struct type*); \ 113
137 \ 114 [[nodiscard]] bool IsBlack() const {
138 /* Finds the node with the same key as elm */ \ 115 return rbe_color == EntryColor::Black;
139 static __inline struct type* name##_SPLAY_FIND(struct name* head, struct type* elm) { \ 116 }
140 if (SPLAY_EMPTY(head)) \ 117
141 return (NULL); \ 118 [[nodiscard]] bool IsRed() const {
142 name##_SPLAY(head, elm); \ 119 return rbe_color == EntryColor::Red;
143 if ((cmp)(elm, (head)->sph_root) == 0) \ 120 }
144 return (head->sph_root); \ 121
145 return (NULL); \ 122 [[nodiscard]] EntryColor Color() const {
146 } \ 123 return rbe_color;
147 \ 124 }
148 static __inline struct type* name##_SPLAY_NEXT(struct name* head, struct type* elm) { \ 125
149 name##_SPLAY(head, elm); \ 126 void SetColor(EntryColor color) {
150 if (SPLAY_RIGHT(elm, field) != NULL) { \ 127 rbe_color = color;
151 elm = SPLAY_RIGHT(elm, field); \ 128 }
152 while (SPLAY_LEFT(elm, field) != NULL) { \ 129
153 elm = SPLAY_LEFT(elm, field); \ 130private:
154 } \ 131 T* rbe_left = nullptr;
155 } else \ 132 T* rbe_right = nullptr;
156 elm = NULL; \ 133 T* rbe_parent = nullptr;
157 return (elm); \ 134 EntryColor rbe_color{};
158 } \ 135};
159 \ 136
160 static __inline struct type* name##_SPLAY_MIN_MAX(struct name* head, int val) { \ 137template <typename Node>
161 name##_SPLAY_MINMAX(head, val); \ 138[[nodiscard]] RBEntry<Node>& RB_ENTRY(Node* node) {
162 return (SPLAY_ROOT(head)); \ 139 return node->GetEntry();
163 } 140}
164 141
165/* Main splay operation. 142template <typename Node>
166 * Moves node close to the key of elm to top 143[[nodiscard]] const RBEntry<Node>& RB_ENTRY(const Node* node) {
167 */ 144 return node->GetEntry();
168#define SPLAY_GENERATE(name, type, field, cmp) \ 145}
169 struct type* name##_SPLAY_INSERT(struct name* head, struct type* elm) { \ 146
170 if (SPLAY_EMPTY(head)) { \ 147template <typename Node>
171 SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ 148[[nodiscard]] Node* RB_PARENT(Node* node) {
172 } else { \ 149 return RB_ENTRY(node).Parent();
173 int __comp; \ 150}
174 name##_SPLAY(head, elm); \ 151
175 __comp = (cmp)(elm, (head)->sph_root); \ 152template <typename Node>
176 if (__comp < 0) { \ 153[[nodiscard]] const Node* RB_PARENT(const Node* node) {
177 SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field); \ 154 return RB_ENTRY(node).Parent();
178 SPLAY_RIGHT(elm, field) = (head)->sph_root; \ 155}
179 SPLAY_LEFT((head)->sph_root, field) = NULL; \ 156
180 } else if (__comp > 0) { \ 157template <typename Node>
181 SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field); \ 158void RB_SET_PARENT(Node* node, Node* parent) {
182 SPLAY_LEFT(elm, field) = (head)->sph_root; \ 159 return RB_ENTRY(node).SetParent(parent);
183 SPLAY_RIGHT((head)->sph_root, field) = NULL; \ 160}
184 } else \ 161
185 return ((head)->sph_root); \ 162template <typename Node>
186 } \ 163[[nodiscard]] Node* RB_LEFT(Node* node) {
187 (head)->sph_root = (elm); \ 164 return RB_ENTRY(node).Left();
188 return (NULL); \ 165}
189 } \ 166
190 \ 167template <typename Node>
191 struct type* name##_SPLAY_REMOVE(struct name* head, struct type* elm) { \ 168[[nodiscard]] const Node* RB_LEFT(const Node* node) {
192 struct type* __tmp; \ 169 return RB_ENTRY(node).Left();
193 if (SPLAY_EMPTY(head)) \ 170}
194 return (NULL); \ 171
195 name##_SPLAY(head, elm); \ 172template <typename Node>
196 if ((cmp)(elm, (head)->sph_root) == 0) { \ 173void RB_SET_LEFT(Node* node, Node* left) {
197 if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ 174 return RB_ENTRY(node).SetLeft(left);
198 (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ 175}
199 } else { \ 176
200 __tmp = SPLAY_RIGHT((head)->sph_root, field); \ 177template <typename Node>
201 (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ 178[[nodiscard]] Node* RB_RIGHT(Node* node) {
202 name##_SPLAY(head, elm); \ 179 return RB_ENTRY(node).Right();
203 SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ 180}
204 } \ 181
205 return (elm); \ 182template <typename Node>
206 } \ 183[[nodiscard]] const Node* RB_RIGHT(const Node* node) {
207 return (NULL); \ 184 return RB_ENTRY(node).Right();
208 } \ 185}
209 \ 186
210 void name##_SPLAY(struct name* head, struct type* elm) { \ 187template <typename Node>
211 struct type __node, *__left, *__right, *__tmp; \ 188void RB_SET_RIGHT(Node* node, Node* right) {
212 int __comp; \ 189 return RB_ENTRY(node).SetRight(right);
213 \ 190}
214 SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL; \ 191
215 __left = __right = &__node; \ 192template <typename Node>
216 \ 193[[nodiscard]] bool RB_IS_BLACK(const Node* node) {
217 while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \ 194 return RB_ENTRY(node).IsBlack();
218 if (__comp < 0) { \ 195}
219 __tmp = SPLAY_LEFT((head)->sph_root, field); \ 196
220 if (__tmp == NULL) \ 197template <typename Node>
221 break; \ 198[[nodiscard]] bool RB_IS_RED(const Node* node) {
222 if ((cmp)(elm, __tmp) < 0) { \ 199 return RB_ENTRY(node).IsRed();
223 SPLAY_ROTATE_RIGHT(head, __tmp, field); \ 200}
224 if (SPLAY_LEFT((head)->sph_root, field) == NULL) \ 201
225 break; \ 202template <typename Node>
226 } \ 203[[nodiscard]] EntryColor RB_COLOR(const Node* node) {
227 SPLAY_LINKLEFT(head, __right, field); \ 204 return RB_ENTRY(node).Color();
228 } else if (__comp > 0) { \ 205}
229 __tmp = SPLAY_RIGHT((head)->sph_root, field); \ 206
230 if (__tmp == NULL) \ 207template <typename Node>
231 break; \ 208void RB_SET_COLOR(Node* node, EntryColor color) {
232 if ((cmp)(elm, __tmp) > 0) { \ 209 return RB_ENTRY(node).SetColor(color);
233 SPLAY_ROTATE_LEFT(head, __tmp, field); \ 210}
234 if (SPLAY_RIGHT((head)->sph_root, field) == NULL) \ 211
235 break; \ 212template <typename Node>
236 } \ 213void RB_SET(Node* node, Node* parent) {
237 SPLAY_LINKRIGHT(head, __left, field); \ 214 auto& entry = RB_ENTRY(node);
238 } \ 215 entry.SetParent(parent);
239 } \ 216 entry.SetLeft(nullptr);
240 SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ 217 entry.SetRight(nullptr);
241 } \ 218 entry.SetColor(EntryColor::Red);
242 \ 219}
243 /* Splay with either the minimum or the maximum element \ 220
244 * Used to find minimum or maximum element in tree. \ 221template <typename Node>
245 */ \ 222void RB_SET_BLACKRED(Node* black, Node* red) {
246 void name##_SPLAY_MINMAX(struct name* head, int __comp) { \ 223 RB_SET_COLOR(black, EntryColor::Black);
247 struct type __node, *__left, *__right, *__tmp; \ 224 RB_SET_COLOR(red, EntryColor::Red);
248 \ 225}
249 SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL; \ 226
250 __left = __right = &__node; \ 227template <typename Node>
251 \ 228void RB_ROTATE_LEFT(RBHead<Node>* head, Node* elm, Node*& tmp) {
252 while (1) { \ 229 tmp = RB_RIGHT(elm);
253 if (__comp < 0) { \ 230 RB_SET_RIGHT(elm, RB_LEFT(tmp));
254 __tmp = SPLAY_LEFT((head)->sph_root, field); \ 231 if (RB_RIGHT(elm) != nullptr) {
255 if (__tmp == NULL) \ 232 RB_SET_PARENT(RB_LEFT(tmp), elm);
256 break; \ 233 }
257 if (__comp < 0) { \ 234
258 SPLAY_ROTATE_RIGHT(head, __tmp, field); \ 235 RB_SET_PARENT(tmp, RB_PARENT(elm));
259 if (SPLAY_LEFT((head)->sph_root, field) == NULL) \ 236 if (RB_PARENT(tmp) != nullptr) {
260 break; \ 237 if (elm == RB_LEFT(RB_PARENT(elm))) {
261 } \ 238 RB_SET_LEFT(RB_PARENT(elm), tmp);
262 SPLAY_LINKLEFT(head, __right, field); \ 239 } else {
263 } else if (__comp > 0) { \ 240 RB_SET_RIGHT(RB_PARENT(elm), tmp);
264 __tmp = SPLAY_RIGHT((head)->sph_root, field); \ 241 }
265 if (__tmp == NULL) \ 242 } else {
266 break; \ 243 head->SetRoot(tmp);
267 if (__comp > 0) { \ 244 }
268 SPLAY_ROTATE_LEFT(head, __tmp, field); \ 245
269 if (SPLAY_RIGHT((head)->sph_root, field) == NULL) \ 246 RB_SET_LEFT(tmp, elm);
270 break; \ 247 RB_SET_PARENT(elm, tmp);
271 } \ 248}
272 SPLAY_LINKRIGHT(head, __left, field); \ 249
273 } \ 250template <typename Node>
274 } \ 251void RB_ROTATE_RIGHT(RBHead<Node>* head, Node* elm, Node*& tmp) {
275 SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ 252 tmp = RB_LEFT(elm);
276 } 253 RB_SET_LEFT(elm, RB_RIGHT(tmp));
277 254 if (RB_LEFT(elm) != nullptr) {
278#define SPLAY_NEGINF -1 255 RB_SET_PARENT(RB_RIGHT(tmp), elm);
279#define SPLAY_INF 1 256 }
280 257
281#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) 258 RB_SET_PARENT(tmp, RB_PARENT(elm));
282#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) 259 if (RB_PARENT(tmp) != nullptr) {
283#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) 260 if (elm == RB_LEFT(RB_PARENT(elm))) {
284#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) 261 RB_SET_LEFT(RB_PARENT(elm), tmp);
285#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) 262 } else {
286#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) 263 RB_SET_RIGHT(RB_PARENT(elm), tmp);
287 264 }
288#define SPLAY_FOREACH(x, name, head) \ 265 } else {
289 for ((x) = SPLAY_MIN(name, head); (x) != NULL; (x) = SPLAY_NEXT(name, head, x)) 266 head->SetRoot(tmp);
290 267 }
291/* Macros that define a red-black tree */ 268
292#define RB_HEAD(name, type) \ 269 RB_SET_RIGHT(tmp, elm);
293 struct name { \ 270 RB_SET_PARENT(elm, tmp);
294 struct type* rbh_root; /* root of the tree */ \ 271}
295 } 272
296 273template <typename Node>
297#define RB_INITIALIZER(root) \ 274void RB_INSERT_COLOR(RBHead<Node>* head, Node* elm) {
298 { NULL } 275 Node* parent = nullptr;
299 276 Node* tmp = nullptr;
300#define RB_INIT(root) \ 277
301 do { \ 278 while ((parent = RB_PARENT(elm)) != nullptr && RB_IS_RED(parent)) {
302 (root)->rbh_root = NULL; \ 279 Node* gparent = RB_PARENT(parent);
303 } while (/*CONSTCOND*/ 0) 280 if (parent == RB_LEFT(gparent)) {
304 281 tmp = RB_RIGHT(gparent);
305#define RB_BLACK 0 282 if (tmp && RB_IS_RED(tmp)) {
306#define RB_RED 1 283 RB_SET_COLOR(tmp, EntryColor::Black);
307#define RB_ENTRY(type) \ 284 RB_SET_BLACKRED(parent, gparent);
308 struct { \ 285 elm = gparent;
309 struct type* rbe_left; /* left element */ \ 286 continue;
310 struct type* rbe_right; /* right element */ \ 287 }
311 struct type* rbe_parent; /* parent element */ \ 288
312 int rbe_color; /* node color */ \ 289 if (RB_RIGHT(parent) == elm) {
313 } 290 RB_ROTATE_LEFT(head, parent, tmp);
314 291 tmp = parent;
315#define RB_LEFT(elm, field) (elm)->field.rbe_left 292 parent = elm;
316#define RB_RIGHT(elm, field) (elm)->field.rbe_right 293 elm = tmp;
317#define RB_PARENT(elm, field) (elm)->field.rbe_parent 294 }
318#define RB_COLOR(elm, field) (elm)->field.rbe_color 295
319#define RB_ROOT(head) (head)->rbh_root 296 RB_SET_BLACKRED(parent, gparent);
320#define RB_EMPTY(head) (RB_ROOT(head) == NULL) 297 RB_ROTATE_RIGHT(head, gparent, tmp);
321 298 } else {
322#define RB_SET(elm, parent, field) \ 299 tmp = RB_LEFT(gparent);
323 do { \ 300 if (tmp && RB_IS_RED(tmp)) {
324 RB_PARENT(elm, field) = parent; \ 301 RB_SET_COLOR(tmp, EntryColor::Black);
325 RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ 302 RB_SET_BLACKRED(parent, gparent);
326 RB_COLOR(elm, field) = RB_RED; \ 303 elm = gparent;
327 } while (/*CONSTCOND*/ 0) 304 continue;
328 305 }
329#define RB_SET_BLACKRED(black, red, field) \ 306
330 do { \ 307 if (RB_LEFT(parent) == elm) {
331 RB_COLOR(black, field) = RB_BLACK; \ 308 RB_ROTATE_RIGHT(head, parent, tmp);
332 RB_COLOR(red, field) = RB_RED; \ 309 tmp = parent;
333 } while (/*CONSTCOND*/ 0) 310 parent = elm;
334 311 elm = tmp;
335#ifndef RB_AUGMENT 312 }
336#define RB_AUGMENT(x) \ 313
337 do { \ 314 RB_SET_BLACKRED(parent, gparent);
338 } while (0) 315 RB_ROTATE_LEFT(head, gparent, tmp);
339#endif 316 }
340 317 }
341#define RB_ROTATE_LEFT(head, elm, tmp, field) \ 318
342 do { \ 319 RB_SET_COLOR(head->Root(), EntryColor::Black);
343 (tmp) = RB_RIGHT(elm, field); \ 320}
344 if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \ 321
345 RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ 322template <typename Node>
346 } \ 323void RB_REMOVE_COLOR(RBHead<Node>* head, Node* parent, Node* elm) {
347 RB_AUGMENT(elm); \ 324 Node* tmp;
348 if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ 325 while ((elm == nullptr || RB_IS_BLACK(elm)) && elm != head->Root()) {
349 if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ 326 if (RB_LEFT(parent) == elm) {
350 RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ 327 tmp = RB_RIGHT(parent);
351 else \ 328 if (RB_IS_RED(tmp)) {
352 RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ 329 RB_SET_BLACKRED(tmp, parent);
353 } else \ 330 RB_ROTATE_LEFT(head, parent, tmp);
354 (head)->rbh_root = (tmp); \ 331 tmp = RB_RIGHT(parent);
355 RB_LEFT(tmp, field) = (elm); \ 332 }
356 RB_PARENT(elm, field) = (tmp); \ 333
357 RB_AUGMENT(tmp); \ 334 if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) &&
358 if ((RB_PARENT(tmp, field))) \ 335 (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) {
359 RB_AUGMENT(RB_PARENT(tmp, field)); \ 336 RB_SET_COLOR(tmp, EntryColor::Red);
360 } while (/*CONSTCOND*/ 0) 337 elm = parent;
361 338 parent = RB_PARENT(elm);
362#define RB_ROTATE_RIGHT(head, elm, tmp, field) \ 339 } else {
363 do { \ 340 if (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp))) {
364 (tmp) = RB_LEFT(elm, field); \ 341 Node* oleft;
365 if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \ 342 if ((oleft = RB_LEFT(tmp)) != nullptr) {
366 RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ 343 RB_SET_COLOR(oleft, EntryColor::Black);
367 } \ 344 }
368 RB_AUGMENT(elm); \ 345
369 if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ 346 RB_SET_COLOR(tmp, EntryColor::Red);
370 if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ 347 RB_ROTATE_RIGHT(head, tmp, oleft);
371 RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ 348 tmp = RB_RIGHT(parent);
372 else \ 349 }
373 RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ 350
374 } else \ 351 RB_SET_COLOR(tmp, RB_COLOR(parent));
375 (head)->rbh_root = (tmp); \ 352 RB_SET_COLOR(parent, EntryColor::Black);
376 RB_RIGHT(tmp, field) = (elm); \ 353 if (RB_RIGHT(tmp)) {
377 RB_PARENT(elm, field) = (tmp); \ 354 RB_SET_COLOR(RB_RIGHT(tmp), EntryColor::Black);
378 RB_AUGMENT(tmp); \ 355 }
379 if ((RB_PARENT(tmp, field))) \ 356
380 RB_AUGMENT(RB_PARENT(tmp, field)); \ 357 RB_ROTATE_LEFT(head, parent, tmp);
381 } while (/*CONSTCOND*/ 0) 358 elm = head->Root();
382 359 break;
383/* Generates prototypes and inline functions */ 360 }
384#define RB_PROTOTYPE(name, type, field, cmp) RB_PROTOTYPE_INTERNAL(name, type, field, cmp, ) 361 } else {
385#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ 362 tmp = RB_LEFT(parent);
386 RB_PROTOTYPE_INTERNAL(name, type, field, cmp, static) 363 if (RB_IS_RED(tmp)) {
387#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ 364 RB_SET_BLACKRED(tmp, parent);
388 RB_PROTOTYPE_INSERT_COLOR(name, type, attr); \ 365 RB_ROTATE_RIGHT(head, parent, tmp);
389 RB_PROTOTYPE_REMOVE_COLOR(name, type, attr); \ 366 tmp = RB_LEFT(parent);
390 RB_PROTOTYPE_INSERT(name, type, attr); \ 367 }
391 RB_PROTOTYPE_REMOVE(name, type, attr); \ 368
392 RB_PROTOTYPE_FIND(name, type, attr); \ 369 if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) &&
393 RB_PROTOTYPE_NFIND(name, type, attr); \ 370 (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) {
394 RB_PROTOTYPE_FIND_LIGHT(name, type, attr); \ 371 RB_SET_COLOR(tmp, EntryColor::Red);
395 RB_PROTOTYPE_NFIND_LIGHT(name, type, attr); \ 372 elm = parent;
396 RB_PROTOTYPE_NEXT(name, type, attr); \ 373 parent = RB_PARENT(elm);
397 RB_PROTOTYPE_PREV(name, type, attr); \ 374 } else {
398 RB_PROTOTYPE_MINMAX(name, type, attr); 375 if (RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) {
399#define RB_PROTOTYPE_INSERT_COLOR(name, type, attr) \ 376 Node* oright;
400 attr void name##_RB_INSERT_COLOR(struct name*, struct type*) 377 if ((oright = RB_RIGHT(tmp)) != nullptr) {
401#define RB_PROTOTYPE_REMOVE_COLOR(name, type, attr) \ 378 RB_SET_COLOR(oright, EntryColor::Black);
402 attr void name##_RB_REMOVE_COLOR(struct name*, struct type*, struct type*) 379 }
403#define RB_PROTOTYPE_REMOVE(name, type, attr) \ 380
404 attr struct type* name##_RB_REMOVE(struct name*, struct type*) 381 RB_SET_COLOR(tmp, EntryColor::Red);
405#define RB_PROTOTYPE_INSERT(name, type, attr) \ 382 RB_ROTATE_LEFT(head, tmp, oright);
406 attr struct type* name##_RB_INSERT(struct name*, struct type*) 383 tmp = RB_LEFT(parent);
407#define RB_PROTOTYPE_FIND(name, type, attr) \ 384 }
408 attr struct type* name##_RB_FIND(struct name*, struct type*) 385
409#define RB_PROTOTYPE_NFIND(name, type, attr) \ 386 RB_SET_COLOR(tmp, RB_COLOR(parent));
410 attr struct type* name##_RB_NFIND(struct name*, struct type*) 387 RB_SET_COLOR(parent, EntryColor::Black);
411#define RB_PROTOTYPE_FIND_LIGHT(name, type, attr) \ 388
412 attr struct type* name##_RB_FIND_LIGHT(struct name*, const void*) 389 if (RB_LEFT(tmp)) {
413#define RB_PROTOTYPE_NFIND_LIGHT(name, type, attr) \ 390 RB_SET_COLOR(RB_LEFT(tmp), EntryColor::Black);
414 attr struct type* name##_RB_NFIND_LIGHT(struct name*, const void*) 391 }
415#define RB_PROTOTYPE_NEXT(name, type, attr) attr struct type* name##_RB_NEXT(struct type*) 392
416#define RB_PROTOTYPE_PREV(name, type, attr) attr struct type* name##_RB_PREV(struct type*) 393 RB_ROTATE_RIGHT(head, parent, tmp);
417#define RB_PROTOTYPE_MINMAX(name, type, attr) attr struct type* name##_RB_MINMAX(struct name*, int) 394 elm = head->Root();
418 395 break;
419/* Main rb operation. 396 }
420 * Moves node close to the key of elm to top 397 }
421 */ 398 }
422#define RB_GENERATE_WITHOUT_COMPARE(name, type, field) \ 399
423 RB_GENERATE_WITHOUT_COMPARE_INTERNAL(name, type, field, ) 400 if (elm) {
424#define RB_GENERATE_WITHOUT_COMPARE_STATIC(name, type, field) \ 401 RB_SET_COLOR(elm, EntryColor::Black);
425 RB_GENERATE_WITHOUT_COMPARE_INTERNAL(name, type, field, static) 402 }
426#define RB_GENERATE_WITHOUT_COMPARE_INTERNAL(name, type, field, attr) \ 403}
427 RB_GENERATE_REMOVE_COLOR(name, type, field, attr) \ 404
428 RB_GENERATE_REMOVE(name, type, field, attr) \ 405template <typename Node>
429 RB_GENERATE_NEXT(name, type, field, attr) \ 406Node* RB_REMOVE(RBHead<Node>* head, Node* elm) {
430 RB_GENERATE_PREV(name, type, field, attr) \ 407 Node* child = nullptr;
431 RB_GENERATE_MINMAX(name, type, field, attr) 408 Node* parent = nullptr;
432 409 Node* old = elm;
433#define RB_GENERATE_WITH_COMPARE(name, type, field, cmp, lcmp) \ 410 EntryColor color{};
434 RB_GENERATE_WITH_COMPARE_INTERNAL(name, type, field, cmp, lcmp, ) 411
435#define RB_GENERATE_WITH_COMPARE_STATIC(name, type, field, cmp, lcmp) \ 412 const auto finalize = [&] {
436 RB_GENERATE_WITH_COMPARE_INTERNAL(name, type, field, cmp, lcmp, static) 413 if (color == EntryColor::Black) {
437#define RB_GENERATE_WITH_COMPARE_INTERNAL(name, type, field, cmp, lcmp, attr) \ 414 RB_REMOVE_COLOR(head, parent, child);
438 RB_GENERATE_INSERT_COLOR(name, type, field, attr) \ 415 }
439 RB_GENERATE_INSERT(name, type, field, cmp, attr) \ 416
440 RB_GENERATE_FIND(name, type, field, cmp, attr) \ 417 return old;
441 RB_GENERATE_NFIND(name, type, field, cmp, attr) \ 418 };
442 RB_GENERATE_FIND_LIGHT(name, type, field, lcmp, attr) \ 419
443 RB_GENERATE_NFIND_LIGHT(name, type, field, lcmp, attr) 420 if (RB_LEFT(elm) == nullptr) {
444 421 child = RB_RIGHT(elm);
445#define RB_GENERATE_ALL(name, type, field, cmp) RB_GENERATE_ALL_INTERNAL(name, type, field, cmp, ) 422 } else if (RB_RIGHT(elm) == nullptr) {
446#define RB_GENERATE_ALL_STATIC(name, type, field, cmp) \ 423 child = RB_LEFT(elm);
447 RB_GENERATE_ALL_INTERNAL(name, type, field, cmp, static) 424 } else {
448#define RB_GENERATE_ALL_INTERNAL(name, type, field, cmp, attr) \ 425 Node* left;
449 RB_GENERATE_WITHOUT_COMPARE_INTERNAL(name, type, field, attr) \ 426 elm = RB_RIGHT(elm);
450 RB_GENERATE_WITH_COMPARE_INTERNAL(name, type, field, cmp, attr) 427 while ((left = RB_LEFT(elm)) != nullptr) {
451 428 elm = left;
452#define RB_GENERATE_INSERT_COLOR(name, type, field, attr) \ 429 }
453 attr void name##_RB_INSERT_COLOR(struct name* head, struct type* elm) { \ 430
454 struct type *parent, *gparent, *tmp; \ 431 child = RB_RIGHT(elm);
455 while ((parent = RB_PARENT(elm, field)) != NULL && RB_COLOR(parent, field) == RB_RED) { \ 432 parent = RB_PARENT(elm);
456 gparent = RB_PARENT(parent, field); \ 433 color = RB_COLOR(elm);
457 if (parent == RB_LEFT(gparent, field)) { \ 434
458 tmp = RB_RIGHT(gparent, field); \ 435 if (child) {
459 if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ 436 RB_SET_PARENT(child, parent);
460 RB_COLOR(tmp, field) = RB_BLACK; \ 437 }
461 RB_SET_BLACKRED(parent, gparent, field); \ 438 if (parent) {
462 elm = gparent; \ 439 if (RB_LEFT(parent) == elm) {
463 continue; \ 440 RB_SET_LEFT(parent, child);
464 } \ 441 } else {
465 if (RB_RIGHT(parent, field) == elm) { \ 442 RB_SET_RIGHT(parent, child);
466 RB_ROTATE_LEFT(head, parent, tmp, field); \ 443 }
467 tmp = parent; \ 444 } else {
468 parent = elm; \ 445 head->SetRoot(child);
469 elm = tmp; \ 446 }
470 } \ 447
471 RB_SET_BLACKRED(parent, gparent, field); \ 448 if (RB_PARENT(elm) == old) {
472 RB_ROTATE_RIGHT(head, gparent, tmp, field); \ 449 parent = elm;
473 } else { \ 450 }
474 tmp = RB_LEFT(gparent, field); \ 451
475 if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ 452 elm->SetEntry(old->GetEntry());
476 RB_COLOR(tmp, field) = RB_BLACK; \ 453
477 RB_SET_BLACKRED(parent, gparent, field); \ 454 if (RB_PARENT(old)) {
478 elm = gparent; \ 455 if (RB_LEFT(RB_PARENT(old)) == old) {
479 continue; \ 456 RB_SET_LEFT(RB_PARENT(old), elm);
480 } \ 457 } else {
481 if (RB_LEFT(parent, field) == elm) { \ 458 RB_SET_RIGHT(RB_PARENT(old), elm);
482 RB_ROTATE_RIGHT(head, parent, tmp, field); \ 459 }
483 tmp = parent; \ 460 } else {
484 parent = elm; \ 461 head->SetRoot(elm);
485 elm = tmp; \ 462 }
486 } \ 463 RB_SET_PARENT(RB_LEFT(old), elm);
487 RB_SET_BLACKRED(parent, gparent, field); \ 464 if (RB_RIGHT(old)) {
488 RB_ROTATE_LEFT(head, gparent, tmp, field); \ 465 RB_SET_PARENT(RB_RIGHT(old), elm);
489 } \ 466 }
490 } \ 467 if (parent) {
491 RB_COLOR(head->rbh_root, field) = RB_BLACK; \ 468 left = parent;
492 } 469 }
493 470
494#define RB_GENERATE_REMOVE_COLOR(name, type, field, attr) \ 471 return finalize();
495 attr void name##_RB_REMOVE_COLOR(struct name* head, struct type* parent, struct type* elm) { \ 472 }
496 struct type* tmp; \ 473
497 while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && elm != RB_ROOT(head)) { \ 474 parent = RB_PARENT(elm);
498 if (RB_LEFT(parent, field) == elm) { \ 475 color = RB_COLOR(elm);
499 tmp = RB_RIGHT(parent, field); \ 476
500 if (RB_COLOR(tmp, field) == RB_RED) { \ 477 if (child) {
501 RB_SET_BLACKRED(tmp, parent, field); \ 478 RB_SET_PARENT(child, parent);
502 RB_ROTATE_LEFT(head, parent, tmp, field); \ 479 }
503 tmp = RB_RIGHT(parent, field); \ 480 if (parent) {
504 } \ 481 if (RB_LEFT(parent) == elm) {
505 if ((RB_LEFT(tmp, field) == NULL || \ 482 RB_SET_LEFT(parent, child);
506 RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) && \ 483 } else {
507 (RB_RIGHT(tmp, field) == NULL || \ 484 RB_SET_RIGHT(parent, child);
508 RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) { \ 485 }
509 RB_COLOR(tmp, field) = RB_RED; \ 486 } else {
510 elm = parent; \ 487 head->SetRoot(child);
511 parent = RB_PARENT(elm, field); \ 488 }
512 } else { \ 489
513 if (RB_RIGHT(tmp, field) == NULL || \ 490 return finalize();
514 RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) { \ 491}
515 struct type* oleft; \ 492
516 if ((oleft = RB_LEFT(tmp, field)) != NULL) \ 493// Inserts a node into the RB tree
517 RB_COLOR(oleft, field) = RB_BLACK; \ 494template <typename Node, typename CompareFunction>
518 RB_COLOR(tmp, field) = RB_RED; \ 495Node* RB_INSERT(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
519 RB_ROTATE_RIGHT(head, tmp, oleft, field); \ 496 Node* parent = nullptr;
520 tmp = RB_RIGHT(parent, field); \ 497 Node* tmp = head->Root();
521 } \ 498 int comp = 0;
522 RB_COLOR(tmp, field) = RB_COLOR(parent, field); \ 499
523 RB_COLOR(parent, field) = RB_BLACK; \ 500 while (tmp) {
524 if (RB_RIGHT(tmp, field)) \ 501 parent = tmp;
525 RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK; \ 502 comp = cmp(elm, parent);
526 RB_ROTATE_LEFT(head, parent, tmp, field); \ 503 if (comp < 0) {
527 elm = RB_ROOT(head); \ 504 tmp = RB_LEFT(tmp);
528 break; \ 505 } else if (comp > 0) {
529 } \ 506 tmp = RB_RIGHT(tmp);
530 } else { \ 507 } else {
531 tmp = RB_LEFT(parent, field); \ 508 return tmp;
532 if (RB_COLOR(tmp, field) == RB_RED) { \ 509 }
533 RB_SET_BLACKRED(tmp, parent, field); \ 510 }
534 RB_ROTATE_RIGHT(head, parent, tmp, field); \ 511
535 tmp = RB_LEFT(parent, field); \ 512 RB_SET(elm, parent);
536 } \ 513
537 if ((RB_LEFT(tmp, field) == NULL || \ 514 if (parent != nullptr) {
538 RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) && \ 515 if (comp < 0) {
539 (RB_RIGHT(tmp, field) == NULL || \ 516 RB_SET_LEFT(parent, elm);
540 RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) { \ 517 } else {
541 RB_COLOR(tmp, field) = RB_RED; \ 518 RB_SET_RIGHT(parent, elm);
542 elm = parent; \ 519 }
543 parent = RB_PARENT(elm, field); \ 520 } else {
544 } else { \ 521 head->SetRoot(elm);
545 if (RB_LEFT(tmp, field) == NULL || \ 522 }
546 RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) { \ 523
547 struct type* oright; \ 524 RB_INSERT_COLOR(head, elm);
548 if ((oright = RB_RIGHT(tmp, field)) != NULL) \ 525 return nullptr;
549 RB_COLOR(oright, field) = RB_BLACK; \ 526}
550 RB_COLOR(tmp, field) = RB_RED; \ 527
551 RB_ROTATE_LEFT(head, tmp, oright, field); \ 528// Finds the node with the same key as elm
552 tmp = RB_LEFT(parent, field); \ 529template <typename Node, typename CompareFunction>
553 } \ 530Node* RB_FIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
554 RB_COLOR(tmp, field) = RB_COLOR(parent, field); \ 531 Node* tmp = head->Root();
555 RB_COLOR(parent, field) = RB_BLACK; \ 532
556 if (RB_LEFT(tmp, field)) \ 533 while (tmp) {
557 RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK; \ 534 const int comp = cmp(elm, tmp);
558 RB_ROTATE_RIGHT(head, parent, tmp, field); \ 535 if (comp < 0) {
559 elm = RB_ROOT(head); \ 536 tmp = RB_LEFT(tmp);
560 break; \ 537 } else if (comp > 0) {
561 } \ 538 tmp = RB_RIGHT(tmp);
562 } \ 539 } else {
563 } \ 540 return tmp;
564 if (elm) \ 541 }
565 RB_COLOR(elm, field) = RB_BLACK; \ 542 }
566 } 543
567 544 return nullptr;
568#define RB_GENERATE_REMOVE(name, type, field, attr) \ 545}
569 attr struct type* name##_RB_REMOVE(struct name* head, struct type* elm) { \ 546
570 struct type *child, *parent, *old = elm; \ 547// Finds the first node greater than or equal to the search key
571 int color; \ 548template <typename Node, typename CompareFunction>
572 if (RB_LEFT(elm, field) == NULL) \ 549Node* RB_NFIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
573 child = RB_RIGHT(elm, field); \ 550 Node* tmp = head->Root();
574 else if (RB_RIGHT(elm, field) == NULL) \ 551 Node* res = nullptr;
575 child = RB_LEFT(elm, field); \ 552
576 else { \ 553 while (tmp) {
577 struct type* left; \ 554 const int comp = cmp(elm, tmp);
578 elm = RB_RIGHT(elm, field); \ 555 if (comp < 0) {
579 while ((left = RB_LEFT(elm, field)) != NULL) \ 556 res = tmp;
580 elm = left; \ 557 tmp = RB_LEFT(tmp);
581 child = RB_RIGHT(elm, field); \ 558 } else if (comp > 0) {
582 parent = RB_PARENT(elm, field); \ 559 tmp = RB_RIGHT(tmp);
583 color = RB_COLOR(elm, field); \ 560 } else {
584 if (child) \ 561 return tmp;
585 RB_PARENT(child, field) = parent; \ 562 }
586 if (parent) { \ 563 }
587 if (RB_LEFT(parent, field) == elm) \ 564
588 RB_LEFT(parent, field) = child; \ 565 return res;
589 else \ 566}
590 RB_RIGHT(parent, field) = child; \ 567
591 RB_AUGMENT(parent); \ 568// Finds the node with the same key as lelm
592 } else \ 569template <typename Node, typename CompareFunction>
593 RB_ROOT(head) = child; \ 570Node* RB_FIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) {
594 if (RB_PARENT(elm, field) == old) \ 571 Node* tmp = head->Root();
595 parent = elm; \ 572
596 (elm)->field = (old)->field; \ 573 while (tmp) {
597 if (RB_PARENT(old, field)) { \ 574 const int comp = lcmp(lelm, tmp);
598 if (RB_LEFT(RB_PARENT(old, field), field) == old) \ 575 if (comp < 0) {
599 RB_LEFT(RB_PARENT(old, field), field) = elm; \ 576 tmp = RB_LEFT(tmp);
600 else \ 577 } else if (comp > 0) {
601 RB_RIGHT(RB_PARENT(old, field), field) = elm; \ 578 tmp = RB_RIGHT(tmp);
602 RB_AUGMENT(RB_PARENT(old, field)); \ 579 } else {
603 } else \ 580 return tmp;
604 RB_ROOT(head) = elm; \ 581 }
605 RB_PARENT(RB_LEFT(old, field), field) = elm; \ 582 }
606 if (RB_RIGHT(old, field)) \ 583
607 RB_PARENT(RB_RIGHT(old, field), field) = elm; \ 584 return nullptr;
608 if (parent) { \ 585}
609 left = parent; \ 586
610 do { \ 587// Finds the first node greater than or equal to the search key
611 RB_AUGMENT(left); \ 588template <typename Node, typename CompareFunction>
612 } while ((left = RB_PARENT(left, field)) != NULL); \ 589Node* RB_NFIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) {
613 } \ 590 Node* tmp = head->Root();
614 goto color; \ 591 Node* res = nullptr;
615 } \ 592
616 parent = RB_PARENT(elm, field); \ 593 while (tmp) {
617 color = RB_COLOR(elm, field); \ 594 const int comp = lcmp(lelm, tmp);
618 if (child) \ 595 if (comp < 0) {
619 RB_PARENT(child, field) = parent; \ 596 res = tmp;
620 if (parent) { \ 597 tmp = RB_LEFT(tmp);
621 if (RB_LEFT(parent, field) == elm) \ 598 } else if (comp > 0) {
622 RB_LEFT(parent, field) = child; \ 599 tmp = RB_RIGHT(tmp);
623 else \ 600 } else {
624 RB_RIGHT(parent, field) = child; \ 601 return tmp;
625 RB_AUGMENT(parent); \ 602 }
626 } else \ 603 }
627 RB_ROOT(head) = child; \ 604
628 color: \ 605 return res;
629 if (color == RB_BLACK) \ 606}
630 name##_RB_REMOVE_COLOR(head, parent, child); \ 607
631 return (old); \ 608template <typename Node>
632 } 609Node* RB_NEXT(Node* elm) {
633 610 if (RB_RIGHT(elm)) {
634#define RB_GENERATE_INSERT(name, type, field, cmp, attr) \ 611 elm = RB_RIGHT(elm);
635 /* Inserts a node into the RB tree */ \ 612 while (RB_LEFT(elm)) {
636 attr struct type* name##_RB_INSERT(struct name* head, struct type* elm) { \ 613 elm = RB_LEFT(elm);
637 struct type* tmp; \ 614 }
638 struct type* parent = NULL; \ 615 } else {
639 int comp = 0; \ 616 if (RB_PARENT(elm) && (elm == RB_LEFT(RB_PARENT(elm)))) {
640 tmp = RB_ROOT(head); \ 617 elm = RB_PARENT(elm);
641 while (tmp) { \ 618 } else {
642 parent = tmp; \ 619 while (RB_PARENT(elm) && (elm == RB_RIGHT(RB_PARENT(elm)))) {
643 comp = (cmp)(elm, parent); \ 620 elm = RB_PARENT(elm);
644 if (comp < 0) \ 621 }
645 tmp = RB_LEFT(tmp, field); \ 622 elm = RB_PARENT(elm);
646 else if (comp > 0) \ 623 }
647 tmp = RB_RIGHT(tmp, field); \ 624 }
648 else \ 625 return elm;
649 return (tmp); \ 626}
650 } \ 627
651 RB_SET(elm, parent, field); \ 628template <typename Node>
652 if (parent != NULL) { \ 629Node* RB_PREV(Node* elm) {
653 if (comp < 0) \ 630 if (RB_LEFT(elm)) {
654 RB_LEFT(parent, field) = elm; \ 631 elm = RB_LEFT(elm);
655 else \ 632 while (RB_RIGHT(elm)) {
656 RB_RIGHT(parent, field) = elm; \ 633 elm = RB_RIGHT(elm);
657 RB_AUGMENT(parent); \ 634 }
658 } else \ 635 } else {
659 RB_ROOT(head) = elm; \ 636 if (RB_PARENT(elm) && (elm == RB_RIGHT(RB_PARENT(elm)))) {
660 name##_RB_INSERT_COLOR(head, elm); \ 637 elm = RB_PARENT(elm);
661 return (NULL); \ 638 } else {
662 } 639 while (RB_PARENT(elm) && (elm == RB_LEFT(RB_PARENT(elm)))) {
663 640 elm = RB_PARENT(elm);
664#define RB_GENERATE_FIND(name, type, field, cmp, attr) \ 641 }
665 /* Finds the node with the same key as elm */ \ 642 elm = RB_PARENT(elm);
666 attr struct type* name##_RB_FIND(struct name* head, struct type* elm) { \ 643 }
667 struct type* tmp = RB_ROOT(head); \ 644 }
668 int comp; \ 645 return elm;
669 while (tmp) { \ 646}
670 comp = cmp(elm, tmp); \ 647
671 if (comp < 0) \ 648template <typename Node>
672 tmp = RB_LEFT(tmp, field); \ 649Node* RB_MINMAX(RBHead<Node>* head, bool is_min) {
673 else if (comp > 0) \ 650 Node* tmp = head->Root();
674 tmp = RB_RIGHT(tmp, field); \ 651 Node* parent = nullptr;
675 else \ 652
676 return (tmp); \ 653 while (tmp) {
677 } \ 654 parent = tmp;
678 return (NULL); \ 655 if (is_min) {
679 } 656 tmp = RB_LEFT(tmp);
680 657 } else {
681#define RB_GENERATE_NFIND(name, type, field, cmp, attr) \ 658 tmp = RB_RIGHT(tmp);
682 /* Finds the first node greater than or equal to the search key */ \ 659 }
683 attr struct type* name##_RB_NFIND(struct name* head, struct type* elm) { \ 660 }
684 struct type* tmp = RB_ROOT(head); \ 661
685 struct type* res = NULL; \ 662 return parent;
686 int comp; \ 663}
687 while (tmp) { \ 664
688 comp = cmp(elm, tmp); \ 665template <typename Node>
689 if (comp < 0) { \ 666Node* RB_MIN(RBHead<Node>* head) {
690 res = tmp; \ 667 return RB_MINMAX(head, true);
691 tmp = RB_LEFT(tmp, field); \ 668}
692 } else if (comp > 0) \ 669
693 tmp = RB_RIGHT(tmp, field); \ 670template <typename Node>
694 else \ 671Node* RB_MAX(RBHead<Node>* head) {
695 return (tmp); \ 672 return RB_MINMAX(head, false);
696 } \ 673}
697 return (res); \ 674} // namespace Common
698 }
699
700#define RB_GENERATE_FIND_LIGHT(name, type, field, lcmp, attr) \
701 /* Finds the node with the same key as elm */ \
702 attr struct type* name##_RB_FIND_LIGHT(struct name* head, const void* lelm) { \
703 struct type* tmp = RB_ROOT(head); \
704 int comp; \
705 while (tmp) { \
706 comp = lcmp(lelm, tmp); \
707 if (comp < 0) \
708 tmp = RB_LEFT(tmp, field); \
709 else if (comp > 0) \
710 tmp = RB_RIGHT(tmp, field); \
711 else \
712 return (tmp); \
713 } \
714 return (NULL); \
715 }
716
717#define RB_GENERATE_NFIND_LIGHT(name, type, field, lcmp, attr) \
718 /* Finds the first node greater than or equal to the search key */ \
719 attr struct type* name##_RB_NFIND_LIGHT(struct name* head, const void* lelm) { \
720 struct type* tmp = RB_ROOT(head); \
721 struct type* res = NULL; \
722 int comp; \
723 while (tmp) { \
724 comp = lcmp(lelm, tmp); \
725 if (comp < 0) { \
726 res = tmp; \
727 tmp = RB_LEFT(tmp, field); \
728 } else if (comp > 0) \
729 tmp = RB_RIGHT(tmp, field); \
730 else \
731 return (tmp); \
732 } \
733 return (res); \
734 }
735
736#define RB_GENERATE_NEXT(name, type, field, attr) \
737 /* ARGSUSED */ \
738 attr struct type* name##_RB_NEXT(struct type* elm) { \
739 if (RB_RIGHT(elm, field)) { \
740 elm = RB_RIGHT(elm, field); \
741 while (RB_LEFT(elm, field)) \
742 elm = RB_LEFT(elm, field); \
743 } else { \
744 if (RB_PARENT(elm, field) && (elm == RB_LEFT(RB_PARENT(elm, field), field))) \
745 elm = RB_PARENT(elm, field); \
746 else { \
747 while (RB_PARENT(elm, field) && (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \
748 elm = RB_PARENT(elm, field); \
749 elm = RB_PARENT(elm, field); \
750 } \
751 } \
752 return (elm); \
753 }
754
755#define RB_GENERATE_PREV(name, type, field, attr) \
756 /* ARGSUSED */ \
757 attr struct type* name##_RB_PREV(struct type* elm) { \
758 if (RB_LEFT(elm, field)) { \
759 elm = RB_LEFT(elm, field); \
760 while (RB_RIGHT(elm, field)) \
761 elm = RB_RIGHT(elm, field); \
762 } else { \
763 if (RB_PARENT(elm, field) && (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \
764 elm = RB_PARENT(elm, field); \
765 else { \
766 while (RB_PARENT(elm, field) && (elm == RB_LEFT(RB_PARENT(elm, field), field))) \
767 elm = RB_PARENT(elm, field); \
768 elm = RB_PARENT(elm, field); \
769 } \
770 } \
771 return (elm); \
772 }
773
774#define RB_GENERATE_MINMAX(name, type, field, attr) \
775 attr struct type* name##_RB_MINMAX(struct name* head, int val) { \
776 struct type* tmp = RB_ROOT(head); \
777 struct type* parent = NULL; \
778 while (tmp) { \
779 parent = tmp; \
780 if (val < 0) \
781 tmp = RB_LEFT(tmp, field); \
782 else \
783 tmp = RB_RIGHT(tmp, field); \
784 } \
785 return (parent); \
786 }
787
788#define RB_NEGINF -1
789#define RB_INF 1
790
791#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y)
792#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y)
793#define RB_FIND(name, x, y) name##_RB_FIND(x, y)
794#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y)
795#define RB_FIND_LIGHT(name, x, y) name##_RB_FIND_LIGHT(x, y)
796#define RB_NFIND_LIGHT(name, x, y) name##_RB_NFIND_LIGHT(x, y)
797#define RB_NEXT(name, x, y) name##_RB_NEXT(y)
798#define RB_PREV(name, x, y) name##_RB_PREV(y)
799#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF)
800#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)
801
802#define RB_FOREACH(x, name, head) \
803 for ((x) = RB_MIN(name, head); (x) != NULL; (x) = name##_RB_NEXT(x))
804
805#define RB_FOREACH_FROM(x, name, y) \
806 for ((x) = (y); ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); (x) = (y))
807
808#define RB_FOREACH_SAFE(x, name, head, y) \
809 for ((x) = RB_MIN(name, head); ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
810 (x) = (y))
811
812#define RB_FOREACH_REVERSE(x, name, head) \
813 for ((x) = RB_MAX(name, head); (x) != NULL; (x) = name##_RB_PREV(x))
814
815#define RB_FOREACH_REVERSE_FROM(x, name, y) \
816 for ((x) = (y); ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); (x) = (y))
817
818#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \
819 for ((x) = RB_MAX(name, head); ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \
820 (x) = (y))
821
822#endif /* _SYS_TREE_H_ */
diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp
index eb8a7782f..a65f6b832 100644
--- a/src/common/x64/native_clock.cpp
+++ b/src/common/x64/native_clock.cpp
@@ -2,19 +2,74 @@
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 <array>
5#include <chrono> 6#include <chrono>
7#include <limits>
6#include <mutex> 8#include <mutex>
7#include <thread> 9#include <thread>
8 10
9#ifdef _MSC_VER 11#ifdef _MSC_VER
10#include <intrin.h> 12#include <intrin.h>
13
14#pragma intrinsic(__umulh)
15#pragma intrinsic(_udiv128)
11#else 16#else
12#include <x86intrin.h> 17#include <x86intrin.h>
13#endif 18#endif
14 19
20#include "common/atomic_ops.h"
15#include "common/uint128.h" 21#include "common/uint128.h"
16#include "common/x64/native_clock.h" 22#include "common/x64/native_clock.h"
17 23
24namespace {
25
26[[nodiscard]] u64 GetFixedPoint64Factor(u64 numerator, u64 divisor) {
27#ifdef __SIZEOF_INT128__
28 const auto base = static_cast<unsigned __int128>(numerator) << 64ULL;
29 return static_cast<u64>(base / divisor);
30#elif defined(_M_X64) || defined(_M_ARM64)
31 std::array<u64, 2> r = {0, numerator};
32 u64 remainder;
33#if _MSC_VER < 1923
34 return udiv128(r[1], r[0], divisor, &remainder);
35#else
36 return _udiv128(r[1], r[0], divisor, &remainder);
37#endif
38#else
39 // This one is bit more inaccurate.
40 return MultiplyAndDivide64(std::numeric_limits<u64>::max(), numerator, divisor);
41#endif
42}
43
44[[nodiscard]] u64 MultiplyHigh(u64 a, u64 b) {
45#ifdef __SIZEOF_INT128__
46 return (static_cast<unsigned __int128>(a) * static_cast<unsigned __int128>(b)) >> 64;
47#elif defined(_M_X64) || defined(_M_ARM64)
48 return __umulh(a, b); // MSVC
49#else
50 // Generic fallback
51 const u64 a_lo = u32(a);
52 const u64 a_hi = a >> 32;
53 const u64 b_lo = u32(b);
54 const u64 b_hi = b >> 32;
55
56 const u64 a_x_b_hi = a_hi * b_hi;
57 const u64 a_x_b_mid = a_hi * b_lo;
58 const u64 b_x_a_mid = b_hi * a_lo;
59 const u64 a_x_b_lo = a_lo * b_lo;
60
61 const u64 carry_bit = (static_cast<u64>(static_cast<u32>(a_x_b_mid)) +
62 static_cast<u64>(static_cast<u32>(b_x_a_mid)) + (a_x_b_lo >> 32)) >>
63 32;
64
65 const u64 multhi = a_x_b_hi + (a_x_b_mid >> 32) + (b_x_a_mid >> 32) + carry_bit;
66
67 return multhi;
68#endif
69}
70
71} // namespace
72
18namespace Common { 73namespace Common {
19 74
20u64 EstimateRDTSCFrequency() { 75u64 EstimateRDTSCFrequency() {
@@ -48,54 +103,71 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen
48 : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{ 103 : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{
49 rtsc_frequency_} { 104 rtsc_frequency_} {
50 _mm_mfence(); 105 _mm_mfence();
51 last_measure = __rdtsc(); 106 time_point.inner.last_measure = __rdtsc();
52 accumulated_ticks = 0U; 107 time_point.inner.accumulated_ticks = 0U;
108 ns_rtsc_factor = GetFixedPoint64Factor(1000000000, rtsc_frequency);
109 us_rtsc_factor = GetFixedPoint64Factor(1000000, rtsc_frequency);
110 ms_rtsc_factor = GetFixedPoint64Factor(1000, rtsc_frequency);
111 clock_rtsc_factor = GetFixedPoint64Factor(emulated_clock_frequency, rtsc_frequency);
112 cpu_rtsc_factor = GetFixedPoint64Factor(emulated_cpu_frequency, rtsc_frequency);
53} 113}
54 114
55u64 NativeClock::GetRTSC() { 115u64 NativeClock::GetRTSC() {
56 std::scoped_lock scope{rtsc_serialize}; 116 TimePoint new_time_point{};
57 _mm_mfence(); 117 TimePoint current_time_point{};
58 const u64 current_measure = __rdtsc(); 118 do {
59 u64 diff = current_measure - last_measure; 119 current_time_point.pack = time_point.pack;
60 diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0) 120 _mm_mfence();
61 if (current_measure > last_measure) { 121 const u64 current_measure = __rdtsc();
62 last_measure = current_measure; 122 u64 diff = current_measure - current_time_point.inner.last_measure;
63 } 123 diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0)
64 accumulated_ticks += diff; 124 new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure
125 ? current_measure
126 : current_time_point.inner.last_measure;
127 new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff;
128 } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
129 current_time_point.pack));
65 /// The clock cannot be more precise than the guest timer, remove the lower bits 130 /// The clock cannot be more precise than the guest timer, remove the lower bits
66 return accumulated_ticks & inaccuracy_mask; 131 return new_time_point.inner.accumulated_ticks & inaccuracy_mask;
67} 132}
68 133
69void NativeClock::Pause(bool is_paused) { 134void NativeClock::Pause(bool is_paused) {
70 if (!is_paused) { 135 if (!is_paused) {
71 _mm_mfence(); 136 TimePoint current_time_point{};
72 last_measure = __rdtsc(); 137 TimePoint new_time_point{};
138 do {
139 current_time_point.pack = time_point.pack;
140 new_time_point.pack = current_time_point.pack;
141 _mm_mfence();
142 new_time_point.inner.last_measure = __rdtsc();
143 } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
144 current_time_point.pack));
73 } 145 }
74} 146}
75 147
76std::chrono::nanoseconds NativeClock::GetTimeNS() { 148std::chrono::nanoseconds NativeClock::GetTimeNS() {
77 const u64 rtsc_value = GetRTSC(); 149 const u64 rtsc_value = GetRTSC();
78 return std::chrono::nanoseconds{MultiplyAndDivide64(rtsc_value, 1000000000, rtsc_frequency)}; 150 return std::chrono::nanoseconds{MultiplyHigh(rtsc_value, ns_rtsc_factor)};
79} 151}
80 152
81std::chrono::microseconds NativeClock::GetTimeUS() { 153std::chrono::microseconds NativeClock::GetTimeUS() {
82 const u64 rtsc_value = GetRTSC(); 154 const u64 rtsc_value = GetRTSC();
83 return std::chrono::microseconds{MultiplyAndDivide64(rtsc_value, 1000000, rtsc_frequency)}; 155 return std::chrono::microseconds{MultiplyHigh(rtsc_value, us_rtsc_factor)};
84} 156}
85 157
86std::chrono::milliseconds NativeClock::GetTimeMS() { 158std::chrono::milliseconds NativeClock::GetTimeMS() {
87 const u64 rtsc_value = GetRTSC(); 159 const u64 rtsc_value = GetRTSC();
88 return std::chrono::milliseconds{MultiplyAndDivide64(rtsc_value, 1000, rtsc_frequency)}; 160 return std::chrono::milliseconds{MultiplyHigh(rtsc_value, ms_rtsc_factor)};
89} 161}
90 162
91u64 NativeClock::GetClockCycles() { 163u64 NativeClock::GetClockCycles() {
92 const u64 rtsc_value = GetRTSC(); 164 const u64 rtsc_value = GetRTSC();
93 return MultiplyAndDivide64(rtsc_value, emulated_clock_frequency, rtsc_frequency); 165 return MultiplyHigh(rtsc_value, clock_rtsc_factor);
94} 166}
95 167
96u64 NativeClock::GetCPUCycles() { 168u64 NativeClock::GetCPUCycles() {
97 const u64 rtsc_value = GetRTSC(); 169 const u64 rtsc_value = GetRTSC();
98 return MultiplyAndDivide64(rtsc_value, emulated_cpu_frequency, rtsc_frequency); 170 return MultiplyHigh(rtsc_value, cpu_rtsc_factor);
99} 171}
100 172
101} // namespace X64 173} // namespace X64
diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h
index 6d1e32ac8..7cbd400d2 100644
--- a/src/common/x64/native_clock.h
+++ b/src/common/x64/native_clock.h
@@ -6,7 +6,6 @@
6 6
7#include <optional> 7#include <optional>
8 8
9#include "common/spin_lock.h"
10#include "common/wall_clock.h" 9#include "common/wall_clock.h"
11 10
12namespace Common { 11namespace Common {
@@ -32,14 +31,28 @@ public:
32private: 31private:
33 u64 GetRTSC(); 32 u64 GetRTSC();
34 33
34 union alignas(16) TimePoint {
35 TimePoint() : pack{} {}
36 u128 pack{};
37 struct Inner {
38 u64 last_measure{};
39 u64 accumulated_ticks{};
40 } inner;
41 };
42
35 /// value used to reduce the native clocks accuracy as some apss rely on 43 /// value used to reduce the native clocks accuracy as some apss rely on
36 /// undefined behavior where the level of accuracy in the clock shouldn't 44 /// undefined behavior where the level of accuracy in the clock shouldn't
37 /// be higher. 45 /// be higher.
38 static constexpr u64 inaccuracy_mask = ~(UINT64_C(0x400) - 1); 46 static constexpr u64 inaccuracy_mask = ~(UINT64_C(0x400) - 1);
39 47
40 SpinLock rtsc_serialize{}; 48 TimePoint time_point;
41 u64 last_measure{}; 49 // factors
42 u64 accumulated_ticks{}; 50 u64 clock_rtsc_factor{};
51 u64 cpu_rtsc_factor{};
52 u64 ns_rtsc_factor{};
53 u64 us_rtsc_factor{};
54 u64 ms_rtsc_factor{};
55
43 u64 rtsc_frequency; 56 u64 rtsc_frequency;
44}; 57};
45} // namespace X64 58} // namespace X64
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 1b8ad476e..99310dc50 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -643,9 +643,7 @@ else()
643 -Werror=conversion 643 -Werror=conversion
644 -Werror=ignored-qualifiers 644 -Werror=ignored-qualifiers
645 -Werror=implicit-fallthrough 645 -Werror=implicit-fallthrough
646 -Werror=reorder
647 -Werror=sign-compare 646 -Werror=sign-compare
648 -Werror=unused-variable
649 647
650 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter> 648 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
651 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable> 649 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
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.");
43struct IVFCHeader { 43struct 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};
50static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size."); 50static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size.");
51 51
52struct NCASectionHeaderBlock { 52struct 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};
58static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size."); 58static_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};
66static_assert(sizeof(NCASectionRaw) == 0x200, "NCASectionRaw has incorrect size."); 66static_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};
79static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size."); 79static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size.");
80 80
81struct RomFSSuperblock { 81struct 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};
86static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size."); 86static_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};
96static_assert(sizeof(BKTRHeader) == 0x20, "BKTRHeader has incorrect size."); 96static_assert(sizeof(BKTRHeader) == 0x20, "BKTRHeader has incorrect size.");
97 97
98struct BKTRSuperblock { 98struct 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};
106static_assert(sizeof(BKTRSuperblock) == 0x200, "BKTRSuperblock has incorrect size."); 106static_assert(sizeof(BKTRSuperblock) == 0x200, "BKTRSuperblock has incorrect size.");
107 107
diff --git a/src/core/frontend/input_interpreter.cpp b/src/core/frontend/input_interpreter.cpp
index 66ae506cd..ec5fe660e 100644
--- a/src/core/frontend/input_interpreter.cpp
+++ b/src/core/frontend/input_interpreter.cpp
@@ -25,6 +25,10 @@ void InputInterpreter::PollInput() {
25 button_states[current_index] = button_state; 25 button_states[current_index] = button_state;
26} 26}
27 27
28bool InputInterpreter::IsButtonPressed(HIDButton button) const {
29 return (button_states[current_index] & (1U << static_cast<u8>(button))) != 0;
30}
31
28bool InputInterpreter::IsButtonPressedOnce(HIDButton button) const { 32bool InputInterpreter::IsButtonPressedOnce(HIDButton button) const {
29 const bool current_press = 33 const bool current_press =
30 (button_states[current_index] & (1U << static_cast<u8>(button))) != 0; 34 (button_states[current_index] & (1U << static_cast<u8>(button))) != 0;
diff --git a/src/core/frontend/input_interpreter.h b/src/core/frontend/input_interpreter.h
index fea9aebe6..36a92a6b6 100644
--- a/src/core/frontend/input_interpreter.h
+++ b/src/core/frontend/input_interpreter.h
@@ -67,6 +67,27 @@ public:
67 void PollInput(); 67 void PollInput();
68 68
69 /** 69 /**
70 * Checks whether the button is pressed.
71 *
72 * @param button The button to check.
73 *
74 * @returns True when the button is pressed.
75 */
76 [[nodiscard]] bool IsButtonPressed(HIDButton button) const;
77
78 /**
79 * Checks whether any of the buttons in the parameter list is pressed.
80 *
81 * @tparam HIDButton The buttons to check.
82 *
83 * @returns True when at least one of the buttons is pressed.
84 */
85 template <HIDButton... T>
86 [[nodiscard]] bool IsAnyButtonPressed() {
87 return (IsButtonPressed(T) || ...);
88 }
89
90 /**
70 * The specified button is considered to be pressed once 91 * The specified button is considered to be pressed once
71 * if it is currently pressed and not pressed previously. 92 * if it is currently pressed and not pressed previously.
72 * 93 *
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/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 42f0ea483..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
32void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule, 34void 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;
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/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
61u32 GetFlagBitOffset(CapabilityType type) { 63u32 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/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 {
20struct ShowError { 20struct 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.");
32struct ShowErrorRecord { 32struct 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
41struct SystemErrorArg { 41struct 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
52struct ApplicationErrorArg { 52struct 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/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 5b637f3c5..6a5c18945 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -1,6 +1,5 @@
1add_executable(tests 1add_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
9namespace Common {
10
11TEST_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/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index c3d0f4c31..e01ea55ab 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -136,8 +136,6 @@ add_library(video_core STATIC
136 renderer_vulkan/vk_graphics_pipeline.h 136 renderer_vulkan/vk_graphics_pipeline.h
137 renderer_vulkan/vk_master_semaphore.cpp 137 renderer_vulkan/vk_master_semaphore.cpp
138 renderer_vulkan/vk_master_semaphore.h 138 renderer_vulkan/vk_master_semaphore.h
139 renderer_vulkan/vk_memory_manager.cpp
140 renderer_vulkan/vk_memory_manager.h
141 renderer_vulkan/vk_pipeline_cache.cpp 139 renderer_vulkan/vk_pipeline_cache.cpp
142 renderer_vulkan/vk_pipeline_cache.h 140 renderer_vulkan/vk_pipeline_cache.h
143 renderer_vulkan/vk_query_cache.cpp 141 renderer_vulkan/vk_query_cache.cpp
@@ -260,6 +258,8 @@ add_library(video_core STATIC
260 vulkan_common/vulkan_instance.h 258 vulkan_common/vulkan_instance.h
261 vulkan_common/vulkan_library.cpp 259 vulkan_common/vulkan_library.cpp
262 vulkan_common/vulkan_library.h 260 vulkan_common/vulkan_library.h
261 vulkan_common/vulkan_memory_allocator.cpp
262 vulkan_common/vulkan_memory_allocator.h
263 vulkan_common/vulkan_surface.cpp 263 vulkan_common/vulkan_surface.cpp
264 vulkan_common/vulkan_surface.h 264 vulkan_common/vulkan_surface.h
265 vulkan_common/vulkan_wrapper.cpp 265 vulkan_common/vulkan_wrapper.cpp
@@ -313,9 +313,7 @@ else()
313 -Werror=pessimizing-move 313 -Werror=pessimizing-move
314 -Werror=redundant-move 314 -Werror=redundant-move
315 -Werror=shadow 315 -Werror=shadow
316 -Werror=switch
317 -Werror=type-limits 316 -Werror=type-limits
318 -Werror=unused-variable
319 317
320 $<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess> 318 $<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess>
321 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter> 319 $<$<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 94679d5d1..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"
@@ -56,7 +56,7 @@ void CDmaPusher::Step() {
56 56
57 for (const u32 value : values) { 57 for (const u32 value : values) {
58 if (mask != 0) { 58 if (mask != 0) {
59 const u32 lbs = Common::CountTrailingZeroes32(mask); 59 const auto lbs = static_cast<u32>(std::countr_zero(mask));
60 mask &= ~(1U << lbs); 60 mask &= ~(1U << lbs);
61 ExecuteCommand(static_cast<u32>(offset + lbs), value); 61 ExecuteCommand(static_cast<u32>(offset + lbs), value);
62 continue; 62 continue;
diff --git a/src/video_core/cdma_pusher.h b/src/video_core/cdma_pusher.h
index 8ca70b6dd..e5f212c1a 100644
--- a/src/video_core/cdma_pusher.h
+++ b/src/video_core/cdma_pusher.h
@@ -126,7 +126,7 @@ private:
126 126
127 s32 count{}; 127 s32 count{};
128 s32 offset{}; 128 s32 offset{};
129 s32 mask{}; 129 u32 mask{};
130 bool incrementing{}; 130 bool incrementing{};
131 131
132 // 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
268void H264BitWriter::WriteExpGolombCodedUInt(u32 value) { 268void 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/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_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index d7437e185..61796e33a 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -23,7 +23,6 @@
23#include "video_core/renderer_vulkan/renderer_vulkan.h" 23#include "video_core/renderer_vulkan/renderer_vulkan.h"
24#include "video_core/renderer_vulkan/vk_blit_screen.h" 24#include "video_core/renderer_vulkan/vk_blit_screen.h"
25#include "video_core/renderer_vulkan/vk_master_semaphore.h" 25#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_rasterizer.h" 26#include "video_core/renderer_vulkan/vk_rasterizer.h"
28#include "video_core/renderer_vulkan/vk_scheduler.h" 27#include "video_core/renderer_vulkan/vk_scheduler.h"
29#include "video_core/renderer_vulkan/vk_state_tracker.h" 28#include "video_core/renderer_vulkan/vk_state_tracker.h"
@@ -32,6 +31,7 @@
32#include "video_core/vulkan_common/vulkan_device.h" 31#include "video_core/vulkan_common/vulkan_device.h"
33#include "video_core/vulkan_common/vulkan_instance.h" 32#include "video_core/vulkan_common/vulkan_instance.h"
34#include "video_core/vulkan_common/vulkan_library.h" 33#include "video_core/vulkan_common/vulkan_library.h"
34#include "video_core/vulkan_common/vulkan_memory_allocator.h"
35#include "video_core/vulkan_common/vulkan_surface.h" 35#include "video_core/vulkan_common/vulkan_surface.h"
36#include "video_core/vulkan_common/vulkan_wrapper.h" 36#include "video_core/vulkan_common/vulkan_wrapper.h"
37 37
@@ -137,7 +137,7 @@ bool RendererVulkan::Init() try {
137 InitializeDevice(); 137 InitializeDevice();
138 Report(); 138 Report();
139 139
140 memory_manager = std::make_unique<VKMemoryManager>(*device); 140 memory_allocator = std::make_unique<MemoryAllocator>(*device);
141 141
142 state_tracker = std::make_unique<StateTracker>(gpu); 142 state_tracker = std::make_unique<StateTracker>(gpu);
143 143
@@ -149,11 +149,11 @@ bool RendererVulkan::Init() try {
149 149
150 rasterizer = std::make_unique<RasterizerVulkan>(render_window, gpu, gpu.MemoryManager(), 150 rasterizer = std::make_unique<RasterizerVulkan>(render_window, gpu, gpu.MemoryManager(),
151 cpu_memory, screen_info, *device, 151 cpu_memory, screen_info, *device,
152 *memory_manager, *state_tracker, *scheduler); 152 *memory_allocator, *state_tracker, *scheduler);
153 153
154 blit_screen = 154 blit_screen =
155 std::make_unique<VKBlitScreen>(cpu_memory, render_window, *rasterizer, *device, 155 std::make_unique<VKBlitScreen>(cpu_memory, render_window, *rasterizer, *device,
156 *memory_manager, *swapchain, *scheduler, screen_info); 156 *memory_allocator, *swapchain, *scheduler, screen_info);
157 return true; 157 return true;
158 158
159} catch (const vk::Exception& exception) { 159} catch (const vk::Exception& exception) {
@@ -172,7 +172,7 @@ void RendererVulkan::ShutDown() {
172 blit_screen.reset(); 172 blit_screen.reset();
173 scheduler.reset(); 173 scheduler.reset();
174 swapchain.reset(); 174 swapchain.reset();
175 memory_manager.reset(); 175 memory_allocator.reset();
176 device.reset(); 176 device.reset();
177} 177}
178 178
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index 5575ffc54..daf55b9b4 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -29,8 +29,8 @@ namespace Vulkan {
29 29
30class Device; 30class Device;
31class StateTracker; 31class StateTracker;
32class MemoryAllocator;
32class VKBlitScreen; 33class VKBlitScreen;
33class VKMemoryManager;
34class VKSwapchain; 34class VKSwapchain;
35class VKScheduler; 35class VKScheduler;
36 36
@@ -75,7 +75,7 @@ private:
75 75
76 vk::DebugUtilsMessenger debug_callback; 76 vk::DebugUtilsMessenger debug_callback;
77 std::unique_ptr<Device> device; 77 std::unique_ptr<Device> device;
78 std::unique_ptr<VKMemoryManager> memory_manager; 78 std::unique_ptr<MemoryAllocator> memory_allocator;
79 std::unique_ptr<StateTracker> state_tracker; 79 std::unique_ptr<StateTracker> state_tracker;
80 std::unique_ptr<VKScheduler> scheduler; 80 std::unique_ptr<VKScheduler> scheduler;
81 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 5e184eb42..3e3b895e0 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -22,13 +22,13 @@
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_master_semaphore.h" 24#include "video_core/renderer_vulkan/vk_master_semaphore.h"
25#include "video_core/renderer_vulkan/vk_memory_manager.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_shader_util.h" 26#include "video_core/renderer_vulkan/vk_shader_util.h"
28#include "video_core/renderer_vulkan/vk_swapchain.h" 27#include "video_core/renderer_vulkan/vk_swapchain.h"
29#include "video_core/surface.h" 28#include "video_core/surface.h"
30#include "video_core/textures/decoders.h" 29#include "video_core/textures/decoders.h"
31#include "video_core/vulkan_common/vulkan_device.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" 32#include "video_core/vulkan_common/vulkan_wrapper.h"
33 33
34namespace Vulkan { 34namespace Vulkan {
@@ -115,10 +115,10 @@ struct VKBlitScreen::BufferData {
115VKBlitScreen::VKBlitScreen(Core::Memory::Memory& cpu_memory_, 115VKBlitScreen::VKBlitScreen(Core::Memory::Memory& cpu_memory_,
116 Core::Frontend::EmuWindow& render_window_, 116 Core::Frontend::EmuWindow& render_window_,
117 VideoCore::RasterizerInterface& rasterizer_, const Device& 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
648void VKBlitScreen::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer) { 646void 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
665void VKBlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) { 663void 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 69ed61770..b52576957 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.h
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.h
@@ -6,7 +6,7 @@
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/vulkan_common/vulkan_wrapper.h" 10#include "video_core/vulkan_common/vulkan_wrapper.h"
11 11
12namespace Core { 12namespace Core {
@@ -43,7 +43,7 @@ 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 Device& 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
@@ -86,7 +86,7 @@ private:
86 Core::Frontend::EmuWindow& render_window; 86 Core::Frontend::EmuWindow& render_window;
87 VideoCore::RasterizerInterface& rasterizer; 87 VideoCore::RasterizerInterface& rasterizer;
88 const Device& 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 4d517c547..d8ad40a0f 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -34,17 +34,13 @@ constexpr VkAccessFlags UPLOAD_ACCESS_BARRIERS =
34constexpr VkAccessFlags TRANSFORM_FEEDBACK_WRITE_ACCESS = 34constexpr 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
37std::unique_ptr<VKStreamBuffer> CreateStreamBuffer(const Device& device, VKScheduler& scheduler) {
38 return std::make_unique<VKStreamBuffer>(device, scheduler);
39}
40
41} // Anonymous namespace 37} // Anonymous namespace
42 38
43Buffer::Buffer(const Device& device_, VKMemoryManager& memory_manager, VKScheduler& scheduler_, 39Buffer::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 Device& device_, VKMemoryManager& memory_manager, VKSchedul
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
62Buffer::~Buffer() = default; 56Buffer::~Buffer() = default;
63 57
64void Buffer::Upload(std::size_t offset, std::size_t data_size, const u8* data) { 58void 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
106void Buffer::Download(std::size_t offset, std::size_t data_size, u8* data) { 100void 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
136void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, 130void 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
169VKBufferCache::VKBufferCache(VideoCore::RasterizerInterface& rasterizer_, 163VKBufferCache::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 Device& 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
179VKBufferCache::~VKBufferCache() = default; 173VKBufferCache::~VKBufferCache() = default;
180 174
181std::shared_ptr<Buffer> VKBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) { 175std::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
186VKBufferCache::BufferInfo VKBufferCache::GetEmptyBuffer(std::size_t size) { 180VKBufferCache::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 1c39aed34..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"
13#include "video_core/vulkan_common/vulkan_memory_allocator.h"
14#include "video_core/vulkan_common/vulkan_wrapper.h" 14#include "video_core/vulkan_common/vulkan_wrapper.h"
15 15
16namespace Vulkan { 16namespace Vulkan {
17 17
18class Device; 18class Device;
19class VKMemoryManager;
20class VKScheduler; 19class VKScheduler;
21 20
22class Buffer final : public VideoCommon::BufferBlock { 21class Buffer final : public VideoCommon::BufferBlock {
23public: 22public:
24 explicit Buffer(const Device& 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 {
@@ -43,18 +42,19 @@ public:
43private: 42private:
44 const Device& 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
51class VKBufferCache final : public VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer> { 51class VKBufferCache final : public VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer> {
52public: 52public:
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 Device& 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;
@@ -64,9 +64,9 @@ protected:
64 64
65private: 65private:
66 const Device& 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_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
index 02a6d54b7..5eb6a54be 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
@@ -164,7 +164,7 @@ VkDescriptorSet VKComputePass::CommitDescriptorSet(
164 164
165QuadArrayPass::QuadArrayPass(const Device& device_, VKScheduler& scheduler_, 165QuadArrayPass::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;
177std::pair<VkBuffer, VkDeviceSize> QuadArrayPass::Assemble(u32 num_vertices, u32 first) { 177std::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
214Uint8Pass::Uint8Pass(const Device& device, VKScheduler& scheduler_, 214Uint8Pass::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;
224std::pair<VkBuffer, u64> Uint8Pass::Assemble(u32 num_vertices, VkBuffer src_buffer, 224std::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
258QuadIndexedPass::QuadIndexedPass(const Device& device_, VKScheduler& scheduler_, 258QuadIndexedPass::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 7ddb09afb..f5c6f5f17 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.h
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.h
@@ -16,8 +16,8 @@
16namespace Vulkan { 16namespace Vulkan {
17 17
18class Device; 18class Device;
19class StagingBufferPool;
19class VKScheduler; 20class VKScheduler;
20class VKStagingBufferPool;
21class VKUpdateDescriptorQueue; 21class VKUpdateDescriptorQueue;
22 22
23class VKComputePass { 23class VKComputePass {
@@ -45,7 +45,7 @@ class QuadArrayPass final : public VKComputePass {
45public: 45public:
46 explicit QuadArrayPass(const Device& 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
54private: 54private:
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
60class Uint8Pass final : public VKComputePass { 60class Uint8Pass final : public VKComputePass {
61public: 61public:
62 explicit Uint8Pass(const Device& 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,7 +68,7 @@ public:
69 68
70private: 69private:
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
@@ -77,7 +76,7 @@ class QuadIndexedPass final : public VKComputePass {
77public: 76public:
78 explicit QuadIndexedPass(const Device& 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
88private: 87private:
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_fence_manager.cpp b/src/video_core/renderer_vulkan/vk_fence_manager.cpp
index 4c5bc0aa1..6cd00884d 100644
--- a/src/video_core/renderer_vulkan/vk_fence_manager.cpp
+++ b/src/video_core/renderer_vulkan/vk_fence_manager.cpp
@@ -3,7 +3,6 @@
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_fence_manager.h" 8#include "video_core/renderer_vulkan/vk_fence_manager.h"
@@ -14,13 +13,11 @@
14 13
15namespace Vulkan { 14namespace Vulkan {
16 15
17InnerFence::InnerFence(const Device& device_, VKScheduler& scheduler_, u32 payload_, 16InnerFence::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
21InnerFence::InnerFence(const Device& device_, VKScheduler& scheduler_, GPUVAddr address_, 19InnerFence::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
25InnerFence::~InnerFence() = default; 22InnerFence::~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
42bool InnerFence::IsSignaled() const { 33bool 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
50void InnerFence::Wait() { 40void 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
64bool 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
75VKFenceManager::VKFenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::GPU& gpu_, 47VKFenceManager::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 Device& 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
82Fence VKFenceManager::CreateFence(u32 value, bool is_stubbed) { 54Fence 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
86Fence VKFenceManager::CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) { 58Fence 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
90void VKFenceManager::QueueFence(Fence& fence) { 62void 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 6b51e4587..9c5e5aa8f 100644
--- a/src/video_core/renderer_vulkan/vk_fence_manager.h
+++ b/src/video_core/renderer_vulkan/vk_fence_manager.h
@@ -28,10 +28,8 @@ class VKScheduler;
28 28
29class InnerFence : public VideoCommon::FenceBase { 29class InnerFence : public VideoCommon::FenceBase {
30public: 30public:
31 explicit InnerFence(const Device& 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 Device& 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
43private: 41private:
44 bool IsEventSignalled() const;
45
46 const Device& device;
47 VKScheduler& scheduler; 42 VKScheduler& scheduler;
48 vk::Event event; 43 u64 wait_tick = 0;
49 u64 ticks = 0;
50}; 44};
51using Fence = std::shared_ptr<InnerFence>; 45using 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 Device& device_, VKScheduler& scheduler_); 55 VKScheduler& scheduler_);
62 56
63protected: 57protected:
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
70private: 64private:
71 const Device& device;
72 VKScheduler& scheduler; 65 VKScheduler& scheduler;
73}; 66};
74 67
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 a6abd0eee..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_memory_manager.h"
15#include "video_core/vulkan_common/vulkan_device.h"
16#include "video_core/vulkan_common/vulkan_wrapper.h"
17
18namespace Vulkan {
19
20namespace {
21
22u64 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
30class VKMemoryAllocation final {
31public:
32 explicit VKMemoryAllocation(const Device& 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
74private:
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 Device& 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
120VKMemoryManager::VKMemoryManager(const Device& device_)
121 : device{device_}, properties{device_.GetPhysical().GetMemoryProperties()} {}
122
123VKMemoryManager::~VKMemoryManager() = default;
124
125VKMemoryCommit 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
154VKMemoryCommit 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
160VKMemoryCommit 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
166bool 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
197VKMemoryCommit 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
210VKMemoryCommitImpl::VKMemoryCommitImpl(const Device& device_, VKMemoryAllocation* allocation_,
211 const vk::DeviceMemory& memory_, u64 begin_, u64 end_)
212 : device{device_}, memory{memory_}, interval{begin_, end_}, allocation{allocation_} {}
213
214VKMemoryCommitImpl::~VKMemoryCommitImpl() {
215 allocation->Free(this);
216}
217
218MemoryMap VKMemoryCommitImpl::Map(u64 size, u64 offset_) const {
219 return MemoryMap(this, std::span<u8>(memory.Map(interval.first + offset_, size), size));
220}
221
222void VKMemoryCommitImpl::Unmap() const {
223 memory.Unmap();
224}
225
226MemoryMap 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 2452bca4e..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/vulkan_common/vulkan_wrapper.h"
13
14namespace Vulkan {
15
16class Device;
17class MemoryMap;
18class VKMemoryAllocation;
19class VKMemoryCommitImpl;
20
21using VKMemoryCommit = std::unique_ptr<VKMemoryCommitImpl>;
22
23class VKMemoryManager final {
24public:
25 explicit VKMemoryManager(const Device& 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
44private:
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 Device& device; ///< Device handler.
53 const VkPhysicalDeviceMemoryProperties properties; ///< Physical device properties.
54 std::vector<std::unique_ptr<VKMemoryAllocation>> allocations; ///< Current allocations.
55};
56
57class VKMemoryCommitImpl final {
58 friend VKMemoryAllocation;
59 friend MemoryMap;
60
61public:
62 explicit VKMemoryCommitImpl(const Device& 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
84private:
85 /// Unmaps memory.
86 void Unmap() const;
87
88 const Device& 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.
95class MemoryMap final {
96public:
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
127private:
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_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 93fbea510..f0a111829 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -409,27 +409,26 @@ void RasterizerVulkan::DrawParameters::Draw(vk::CommandBuffer cmdbuf) const {
409RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, 409RasterizerVulkan::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 Device& 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()) {
@@ -1446,7 +1445,7 @@ VkBuffer RasterizerVulkan::DefaultBuffer() {
1446 .queueFamilyIndexCount = 0, 1445 .queueFamilyIndexCount = 0,
1447 .pQueueFamilyIndices = nullptr, 1446 .pQueueFamilyIndices = nullptr,
1448 }); 1447 });
1449 default_buffer_commit = memory_manager.Commit(default_buffer, false); 1448 default_buffer_commit = memory_allocator.Commit(default_buffer, MemoryUsage::DeviceLocal);
1450 1449
1451 scheduler.RequestOutsideRenderPassOperationContext(); 1450 scheduler.RequestOutsideRenderPassOperationContext();
1452 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 4695718e9..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"
@@ -30,6 +29,7 @@
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/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" 33#include "video_core/vulkan_common/vulkan_wrapper.h"
34 34
35namespace Core { 35namespace Core {
@@ -56,7 +56,7 @@ 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 Device& 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
@@ -213,12 +213,12 @@ private:
213 213
214 VKScreenInfo& screen_info; 214 VKScreenInfo& screen_info;
215 const Device& 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_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
index 1e0b8b922..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,10 +3,12 @@
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_scheduler.h" 14#include "video_core/renderer_vulkan/vk_scheduler.h"
@@ -16,45 +18,51 @@
16 18
17namespace Vulkan { 19namespace Vulkan {
18 20
19VKStagingBufferPool::StagingBuffer::StagingBuffer(std::unique_ptr<VKBuffer> buffer_) 21StagingBufferPool::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_} {}
22VKStagingBufferPool::VKStagingBufferPool(const Device& device_, VKMemoryManager& memory_manager_,
23 VKScheduler& scheduler_)
24 : device{device_}, memory_manager{memory_manager_}, scheduler{scheduler_} {}
25 24
26VKStagingBufferPool::~VKStagingBufferPool() = default; 25StagingBufferPool::~StagingBufferPool() = default;
27 26
28VKBuffer& VKStagingBufferPool::GetUnusedBuffer(std::size_t size, bool host_visible) { 27StagingBufferRef 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
35void VKStagingBufferPool::TickFrame() { 34void 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
42VKBuffer* VKStagingBufferPool::TryGetReservedBuffer(std::size_t size, bool host_visible) { 42std::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
53VKBuffer& VKStagingBufferPool::CreateStagingBuffer(std::size_t size, bool host_visible) { 63StagingBufferRef 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{
77VKStagingBufferPool::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
81void VKStagingBufferPool::ReleaseCache(bool host_visible) { 93StagingBufferPool::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
89u64 VKStagingBufferPool::ReleaseLevel(StagingBuffersCache& cache, std::size_t log2) { 107void StagingBufferPool::ReleaseCache(MemoryUsage usage) {
90 static constexpr std::size_t deletions_per_tick = 16; 108 ReleaseLevel(GetCache(usage), current_delete_level);
109}
91 110
111void 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 90dadcbbe..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,7 +9,7 @@
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/vulkan_common/vulkan_wrapper.h" 13#include "video_core/vulkan_common/vulkan_wrapper.h"
14 14
15namespace Vulkan { 15namespace Vulkan {
@@ -17,55 +17,65 @@ namespace Vulkan {
17class Device; 17class Device;
18class VKScheduler; 18class VKScheduler;
19 19
20struct VKBuffer final { 20struct StagingBufferRef {
21 vk::Buffer handle; 21 VkBuffer buffer;
22 VKMemoryCommit commit; 22 std::span<u8> mapped_span;
23}; 23};
24 24
25class VKStagingBufferPool final { 25class StagingBufferPool {
26public: 26public:
27 explicit VKStagingBufferPool(const Device& 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
35private: 35private:
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 Device& 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_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index bd11de012..ab14922d7 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -10,12 +10,12 @@
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_memory_manager.h"
14#include "video_core/renderer_vulkan/vk_rasterizer.h" 13#include "video_core/renderer_vulkan/vk_rasterizer.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_staging_buffer_pool.h" 15#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
17#include "video_core/renderer_vulkan/vk_texture_cache.h" 16#include "video_core/renderer_vulkan/vk_texture_cache.h"
18#include "video_core/vulkan_common/vulkan_device.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" 19#include "video_core/vulkan_common/vulkan_wrapper.h"
20 20
21namespace Vulkan { 21namespace Vulkan {
@@ -554,10 +554,18 @@ void TextureCacheRuntime::Finish() {
554} 554}
555 555
556ImageBufferMap TextureCacheRuntime::MapUploadBuffer(size_t size) { 556ImageBufferMap TextureCacheRuntime::MapUploadBuffer(size_t size) {
557 const auto& buffer = staging_buffer_pool.GetUnusedBuffer(size, true); 557 const auto staging_ref = staging_buffer_pool.Request(size, MemoryUsage::Upload);
558 return ImageBufferMap{ 558 return {
559 .handle = *buffer.handle, 559 .handle = staging_ref.buffer,
560 .map = buffer.commit->Map(size), 560 .span = staging_ref.mapped_span,
561 };
562}
563
564ImageBufferMap 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,
561 }; 569 };
562} 570}
563 571
@@ -788,9 +796,9 @@ Image::Image(TextureCacheRuntime& runtime, const ImageInfo& info_, GPUVAddr gpu_
788 image(MakeImage(runtime.device, info)), buffer(MakeBuffer(runtime.device, info)), 796 image(MakeImage(runtime.device, info)), buffer(MakeBuffer(runtime.device, info)),
789 aspect_mask(ImageAspectMask(info.format)) { 797 aspect_mask(ImageAspectMask(info.format)) {
790 if (image) { 798 if (image) {
791 commit = runtime.memory_manager.Commit(image, false); 799 commit = runtime.memory_allocator.Commit(image, MemoryUsage::DeviceLocal);
792 } else { 800 } else {
793 commit = runtime.memory_manager.Commit(buffer, false); 801 commit = runtime.memory_allocator.Commit(buffer, MemoryUsage::DeviceLocal);
794 } 802 }
795 if (IsPixelFormatASTC(info.format) && !runtime.device.IsOptimalAstcSupported()) { 803 if (IsPixelFormatASTC(info.format) && !runtime.device.IsOptimalAstcSupported()) {
796 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 92a7aad8b..a55d405d1 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -7,8 +7,8 @@
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/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" 12#include "video_core/vulkan_common/vulkan_wrapper.h"
13 13
14namespace Vulkan { 14namespace Vulkan {
@@ -19,14 +19,13 @@ using VideoCommon::Offset2D;
19using VideoCommon::RenderTargets; 19using VideoCommon::RenderTargets;
20using VideoCore::Surface::PixelFormat; 20using VideoCore::Surface::PixelFormat;
21 21
22class VKScheduler;
23class VKStagingBufferPool;
24
25class BlitImageHelper; 22class BlitImageHelper;
26class Device; 23class Device;
27class Image; 24class Image;
28class ImageView; 25class ImageView;
29class Framebuffer; 26class Framebuffer;
27class StagingBufferPool;
28class VKScheduler;
30 29
31struct RenderPassKey { 30struct 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
70struct TextureCacheRuntime { 69struct TextureCacheRuntime {
71 const Device& 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,
@@ -141,7 +137,7 @@ private:
141 VKScheduler* scheduler; 137 VKScheduler* scheduler;
142 vk::Image image; 138 vk::Image image;
143 vk::Buffer buffer; 139 vk::Buffer buffer;
144 VKMemoryCommit commit; 140 MemoryCommit commit;
145 VkImageAspectFlags aspect_mask = 0; 141 VkImageAspectFlags aspect_mask = 0;
146 bool initialized = false; 142 bool initialized = false;
147}; 143};
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/util.cpp b/src/video_core/texture_cache/util.cpp
index 279932778..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
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
43class InputBitStream { 43class InputBitStream {
44public: 44public:
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
80private: 83private:
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
86class OutputBitStream { 90class OutputBitStream {
@@ -193,15 +197,15 @@ struct IntegerEncodedValue {
193 }; 197 };
194}; 198};
195using IntegerEncodedVector = boost::container::static_vector< 199using 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
201static void DecodeTritBlock(InputBitStream& bits, IntegerEncodedVector& result, u32 nBitsPerValue) { 205static 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
869static void DecodeColorValues(u32* out, u8* data, const u32* modes, const u32 nPartitions, 873static 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
1442static void DecompressBlock(std::span<const u8, 16> inBuf, const u32 blockWidth, 1446static 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
217std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, 217std::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/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 75173324e..37d7b45a3 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -99,8 +99,7 @@ VkFormatFeatureFlags GetFormatFeatures(VkFormatProperties properties, FormatType
99 }); 99 });
100} 100}
101 101
102std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties( 102std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::PhysicalDevice physical) {
103 vk::PhysicalDevice physical, const vk::InstanceDispatch& dld) {
104 static constexpr std::array formats{ 103 static constexpr std::array formats{
105 VK_FORMAT_A8B8G8R8_UNORM_PACK32, 104 VK_FORMAT_A8B8G8R8_UNORM_PACK32,
106 VK_FORMAT_A8B8G8R8_UINT_PACK32, 105 VK_FORMAT_A8B8G8R8_UINT_PACK32,
@@ -210,7 +209,7 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(
210Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR surface, 209Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR surface,
211 const vk::InstanceDispatch& dld_) 210 const vk::InstanceDispatch& dld_)
212 : instance{instance_}, dld{dld_}, physical{physical_}, properties{physical.GetProperties()}, 211 : instance{instance_}, dld{dld_}, physical{physical_}, properties{physical.GetProperties()},
213 format_properties{GetFormatProperties(physical, dld)} { 212 format_properties{GetFormatProperties(physical)} {
214 CheckSuitability(); 213 CheckSuitability();
215 SetupFamilies(surface); 214 SetupFamilies(surface);
216 SetupFeatures(); 215 SetupFeatures();
@@ -221,6 +220,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
221 VkPhysicalDeviceFeatures2 features2{ 220 VkPhysicalDeviceFeatures2 features2{
222 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, 221 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
223 .pNext = nullptr, 222 .pNext = nullptr,
223 .features{},
224 }; 224 };
225 const void* first_next = &features2; 225 const void* first_next = &features2;
226 void** next = &features2.pNext; 226 void** next = &features2.pNext;
@@ -256,7 +256,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
256 .shaderTessellationAndGeometryPointSize = false, 256 .shaderTessellationAndGeometryPointSize = false,
257 .shaderImageGatherExtended = true, 257 .shaderImageGatherExtended = true,
258 .shaderStorageImageExtendedFormats = false, 258 .shaderStorageImageExtendedFormats = false,
259 .shaderStorageImageMultisample = true, 259 .shaderStorageImageMultisample = is_shader_storage_image_multisample,
260 .shaderStorageImageReadWithoutFormat = is_formatless_image_load_supported, 260 .shaderStorageImageReadWithoutFormat = is_formatless_image_load_supported,
261 .shaderStorageImageWriteWithoutFormat = true, 261 .shaderStorageImageWriteWithoutFormat = true,
262 .shaderUniformBufferArrayDynamicIndexing = false, 262 .shaderUniformBufferArrayDynamicIndexing = false,
@@ -310,6 +310,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
310 310
311 VkPhysicalDeviceHostQueryResetFeaturesEXT host_query_reset{ 311 VkPhysicalDeviceHostQueryResetFeaturesEXT host_query_reset{
312 .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,
313 .hostQueryReset = true, 314 .hostQueryReset = true,
314 }; 315 };
315 SetNext(next, host_query_reset); 316 SetNext(next, host_query_reset);
@@ -604,7 +605,6 @@ void Device::CheckSuitability() const {
604 std::make_pair(features.occlusionQueryPrecise, "occlusionQueryPrecise"), 605 std::make_pair(features.occlusionQueryPrecise, "occlusionQueryPrecise"),
605 std::make_pair(features.fragmentStoresAndAtomics, "fragmentStoresAndAtomics"), 606 std::make_pair(features.fragmentStoresAndAtomics, "fragmentStoresAndAtomics"),
606 std::make_pair(features.shaderImageGatherExtended, "shaderImageGatherExtended"), 607 std::make_pair(features.shaderImageGatherExtended, "shaderImageGatherExtended"),
607 std::make_pair(features.shaderStorageImageMultisample, "shaderStorageImageMultisample"),
608 std::make_pair(features.shaderStorageImageWriteWithoutFormat, 608 std::make_pair(features.shaderStorageImageWriteWithoutFormat,
609 "shaderStorageImageWriteWithoutFormat"), 609 "shaderStorageImageWriteWithoutFormat"),
610 }; 610 };
@@ -804,6 +804,7 @@ void Device::SetupFamilies(VkSurfaceKHR surface) {
804void Device::SetupFeatures() { 804void Device::SetupFeatures() {
805 const auto supported_features{physical.GetFeatures()}; 805 const auto supported_features{physical.GetFeatures()};
806 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;
807 is_blit_depth_stencil_supported = TestDepthStencilBlits(); 808 is_blit_depth_stencil_supported = TestDepthStencilBlits();
808 is_optimal_astc_supported = IsOptimalAstcSupported(supported_features); 809 is_optimal_astc_supported = IsOptimalAstcSupported(supported_features);
809} 810}
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index a973c3ce4..4b66dba7a 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -272,23 +272,24 @@ private:
272 bool is_optimal_astc_supported{}; ///< Support for native ASTC. 272 bool is_optimal_astc_supported{}; ///< Support for native ASTC.
273 bool is_float16_supported{}; ///< Support for float16 arithmetics. 273 bool is_float16_supported{}; ///< Support for float16 arithmetics.
274 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.
275 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.
276 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.
277 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.
278 bool khr_uniform_buffer_standard_layout{}; ///< Support for std430 on UBOs. 278 bool nv_viewport_swizzle{}; ///< Support for VK_NV_viewport_swizzle.
279 bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8. 279 bool khr_uniform_buffer_standard_layout{}; ///< Support for std430 on UBOs.
280 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.
281 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.
282 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.
283 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.
284 bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback. 284 bool ext_tooling_info{}; ///< Support for VK_EXT_tooling_info.
285 bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color. 285 bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback.
286 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.
287 bool ext_robustness2{}; ///< Support for VK_EXT_robustness2. 287 bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state.
288 bool ext_shader_stencil_export{}; ///< Support for VK_EXT_shader_stencil_export. 288 bool ext_robustness2{}; ///< Support for VK_EXT_robustness2.
289 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.
290 bool has_renderdoc{}; ///< Has RenderDoc attached 290 bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config.
291 bool has_nsight_graphics{}; ///< Has Nsight Graphics attached 291 bool has_renderdoc{}; ///< Has RenderDoc attached
292 bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
292 293
293 // Asynchronous Graphics Pipeline setting 294 // Asynchronous Graphics Pipeline setting
294 bool use_asynchronous_shaders{}; ///< Setting to use asynchronous shaders/graphics pipeline 295 bool use_asynchronous_shaders{}; ///< Setting to use asynchronous shaders/graphics pipeline
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
18namespace Vulkan {
19namespace {
20struct 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
56class MemoryAllocation {
57public:
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
96private:
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
130MemoryCommit::MemoryCommit(MemoryAllocation* allocation_, VkDeviceMemory memory_, u64 begin_,
131 u64 end_) noexcept
132 : allocation{allocation_}, memory{memory_}, begin{begin_}, end{end_} {}
133
134MemoryCommit::~MemoryCommit() {
135 Release();
136}
137
138MemoryCommit& 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
148MemoryCommit::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
152std::span<u8> MemoryCommit::Map() {
153 if (span.empty()) {
154 span = allocation->Map().subspan(begin, end - begin);
155 }
156 return span;
157}
158
159void MemoryCommit::Release() {
160 if (allocation) {
161 allocation->Free(begin);
162 }
163}
164
165MemoryAllocator::MemoryAllocator(const Device& device_)
166 : device{device_}, properties{device_.GetPhysical().GetMemoryProperties()} {}
167
168MemoryAllocator::~MemoryAllocator() = default;
169
170MemoryCommit 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
185MemoryCommit 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
191MemoryCommit 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
197void 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
209std::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
222VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask, MemoryUsage usage) const {
223 return MemoryPropertyFlags(type_mask, MemoryUsagePropertyFlags(usage));
224}
225
226VkMemoryPropertyFlags 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
244std::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
256bool 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
14namespace Vulkan {
15
16class Device;
17class MemoryMap;
18class MemoryAllocation;
19
20/// Hints and requirements for the backing memory type of a commit
21enum 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.
29class MemoryCommit {
30public:
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
56private:
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.
68class MemoryAllocator {
69public:
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
93private:
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.
116bool IsHostVisible(MemoryUsage usage) noexcept;
117
118} // namespace Vulkan
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h
index 912cab46c..9689de0cb 100644
--- a/src/video_core/vulkan_common/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.
146struct InstanceDispatch { 146struct 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.
177struct DeviceDispatch : public InstanceDispatch { 177struct 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.
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 85ee2577d..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
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_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp
index eb8eacbf9..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"
@@ -109,7 +112,6 @@ ConfigureMotionTouch::~ConfigureMotionTouch() = default;
109void ConfigureMotionTouch::SetConfiguration() { 112void ConfigureMotionTouch::SetConfiguration() {
110 const Common::ParamPackage motion_param(Settings::values.motion_device); 113 const Common::ParamPackage motion_param(Settings::values.motion_device);
111 const Common::ParamPackage touch_param(Settings::values.touch_device); 114 const Common::ParamPackage touch_param(Settings::values.touch_device);
112 const std::string motion_engine = motion_param.Get("engine", "motion_emu");
113 const std::string touch_engine = touch_param.Get("engine", "emu_window"); 115 const std::string touch_engine = touch_param.Get("engine", "emu_window");
114 116
115 ui->touch_provider->setCurrentIndex( 117 ui->touch_provider->setCurrentIndex(
@@ -185,14 +187,15 @@ void ConfigureMotionTouch::ConnectEvents() {
185} 187}
186 188
187void ConfigureMotionTouch::OnUDPAddServer() { 189void ConfigureMotionTouch::OnUDPAddServer() {
188 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
189 "[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"));
190 bool ok; 193 bool ok;
191 QString port_text = ui->udp_port->text(); 194 const QString port_text = ui->udp_port->text();
192 QString server_text = ui->udp_server->text(); 195 const QString server_text = ui->udp_server->text();
193 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);
194 int port_number = port_text.toInt(&ok, 10); 197 const int port_number = port_text.toInt(&ok, 10);
195 int row = udp_server_list_model->rowCount(); 198 const int row = udp_server_list_model->rowCount();
196 199
197 if (!ok) { 200 if (!ok) {
198 QMessageBox::warning(this, tr("yuzu"), tr("Port number has invalid characters")); 201 QMessageBox::warning(this, tr("yuzu"), tr("Port number has invalid characters"));
@@ -202,7 +205,7 @@ void ConfigureMotionTouch::OnUDPAddServer() {
202 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"));
203 return; 206 return;
204 } 207 }
205 if (!re.exactMatch(server_text)) { 208 if (!re.match(server_text).hasMatch()) {
206 QMessageBox::warning(this, tr("yuzu"), tr("IP address is not valid")); 209 QMessageBox::warning(this, tr("yuzu"), tr("IP address is not valid"));
207 return; 210 return;
208 } 211 }
@@ -327,14 +330,13 @@ void ConfigureMotionTouch::ApplyConfiguration() {
327 std::string touch_engine = ui->touch_provider->currentData().toString().toStdString(); 330 std::string touch_engine = ui->touch_provider->currentData().toString().toStdString();
328 331
329 Common::ParamPackage touch_param{}; 332 Common::ParamPackage touch_param{};
330 touch_param.Set("engine", std::move(touch_engine));
331
332 if (touch_engine == "cemuhookudp") { 333 if (touch_engine == "cemuhookudp") {
333 touch_param.Set("min_x", min_x); 334 touch_param.Set("min_x", min_x);
334 touch_param.Set("min_y", min_y); 335 touch_param.Set("min_y", min_y);
335 touch_param.Set("max_x", max_x); 336 touch_param.Set("max_x", max_x);
336 touch_param.Set("max_y", max_y); 337 touch_param.Set("max_y", max_y);
337 } 338 }
339 touch_param.Set("engine", std::move(touch_engine));
338 340
339 Settings::values.touch_device = touch_param.Serialize(); 341 Settings::values.touch_device = touch_param.Serialize();
340 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/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() {
119GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} { 119GameListSearchField::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_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 @@
1set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules)
2
3add_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
16create_target_directory_groups(yuzu-tester)
17
18target_link_libraries(yuzu-tester PRIVATE common core input_common)
19target_link_libraries(yuzu-tester PRIVATE inih glad)
20if (MSVC)
21 target_link_libraries(yuzu-tester PRIVATE getopt)
22endif()
23target_link_libraries(yuzu-tester PRIVATE ${PLATFORM_LIBRARIES} SDL2 Threads::Threads)
24
25if(UNIX AND NOT APPLE)
26 install(TARGETS yuzu-tester RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
27endif()
28
29if (MSVC)
30 include(CopyYuzuSDLDeps)
31 copy_yuzu_SDL_deps(yuzu-tester)
32endif()
diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp
deleted file mode 100644
index 0aa143e1f..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
18namespace FS = Common::FS;
19
20Config::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
28Config::~Config() = default;
29
30bool 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
48void 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", true));
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
191void 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
10class INIReader;
11
12class 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
19public:
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 779c3791b..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
7namespace DefaultINI {
8
9const char* sdl2_config_file = R"(
10[Core]
11# Whether to use multi-core for CPU emulation
12# 0 (default): Disabled, 1: Enabled
13use_multi_core=
14
15[Cpu]
16# Enable inline page tables optimization (faster guest memory access)
17# 0: Disabled, 1 (default): Enabled
18cpuopt_page_tables =
19
20# Enable block linking CPU optimization (reduce block dispatcher use during predictable jumps)
21# 0: Disabled, 1 (default): Enabled
22cpuopt_block_linking =
23
24# Enable return stack buffer CPU optimization (reduce block dispatcher use during predictable returns)
25# 0: Disabled, 1 (default): Enabled
26cpuopt_return_stack_buffer =
27
28# Enable fast dispatcher CPU optimization (use a two-tiered dispatcher architecture)
29# 0: Disabled, 1 (default): Enabled
30cpuopt_fast_dispatcher =
31
32# Enable context elimination CPU Optimization (reduce host memory use for guest context)
33# 0: Disabled, 1 (default): Enabled
34cpuopt_context_elimination =
35
36# Enable constant propagation CPU optimization (basic IR optimization)
37# 0: Disabled, 1 (default): Enabled
38cpuopt_const_prop =
39
40# Enable miscellaneous CPU optimizations (basic IR optimization)
41# 0: Disabled, 1 (default): Enabled
42cpuopt_misc_ir =
43
44# Enable reduction of memory misalignment checks (reduce memory fallbacks for misaligned access)
45# 0: Disabled, 1 (default): Enabled
46cpuopt_reduce_misalign_checks =
47
48[Renderer]
49# Whether to use software or hardware rendering.
50# 0: Software, 1 (default): Hardware
51use_hw_renderer =
52
53# Whether to use the Just-In-Time (JIT) compiler for shader emulation
54# 0: Interpreter (slow), 1 (default): JIT (fast)
55use_shader_jit =
56
57# Aspect ratio
58# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Stretch to Window
59aspect_ratio =
60
61# Anisotropic filtering
62# 0: Default, 1: 2x, 2: 4x, 3: 8x, 4: 16x
63max_anisotropy =
64
65# Whether to enable V-Sync (caps the framerate at 60FPS) or not.
66# 0 (default): Off, 1: On
67use_vsync =
68
69# Whether to use disk based shader cache
70# 0 (default): Off, 1 : On
71use_disk_shader_cache =
72
73# Whether to use accurate GPU emulation
74# 0 (default): Off (fast), 1 : On (slow)
75use_accurate_gpu_emulation =
76
77# Whether to use asynchronous GPU emulation
78# 0 : Off (slow), 1 (default): On (fast)
79use_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.
83bg_red =
84bg_blue =
85bg_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
90layout_option =
91
92# Toggle custom layout (using the settings below) on or off.
93# 0 (default): Off, 1: On
94custom_layout =
95
96# Screen placement when using Custom layout option
97# 0x, 0y is the top left corner of the render window.
98custom_top_left =
99custom_top_top =
100custom_top_right =
101custom_top_bottom =
102custom_bottom_left =
103custom_bottom_top =
104custom_bottom_right =
105custom_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
110swap_screen =
111
112[Data Storage]
113# Whether to create a virtual SD card.
114# 1 (default): Yes, 0: No
115use_virtual_sd =
116
117[System]
118# Whether the system is docked
119# 1 (default): Yes, 0: No
120use_docked_mode =
121
122# Allow the use of NFC in games
123# 1 (default): Yes, 0 : No
124enable_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
128rng_seed_enabled =
129rng_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
134custom_rtc_enabled =
135custom_rtc =
136
137# Sets the account username, max length is 32 characters
138# yuzu (default)
139username = 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
145language_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
149region_value =
150
151[Miscellaneous]
152# A filter which removes logs below a certain logging level.
153# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical
154log_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
158program_args=
159# Determines whether or not yuzu will dump the ExeFS of all games it attempts to load while loading them
160dump_exefs=false
161# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them
162dump_nso=false
163
164[WebService]
165# Whether or not to enable telemetry
166# 0: No, 1 (default): Yes
167enable_telemetry =
168# URL for Web API
169web_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
172yuzu_username =
173yuzu_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 '|'):
178title_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
22bool 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
48EmuWindow_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
106EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() {
107 input_subsystem->Shutdown();
108 SDL_GL_DeleteContext(gl_context);
109 SDL_Quit();
110}
111
112bool EmuWindow_SDL2_Hide::IsShown() const {
113 return false;
114}
115
116class SDLGLContext : public Core::Frontend::GraphicsContext {
117public:
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
139private:
140 SDL_Window* window;
141 SDL_GLContext context;
142};
143
144std::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
9struct SDL_Window;
10
11namespace InputCommon {
12class InputSubsystem;
13}
14
15class EmuWindow_SDL2_Hide : public Core::Frontend::EmuWindow {
16public:
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
25private:
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
13namespace Service::Yuzu {
14
15constexpr u64 SERVICE_VERSION = 0x00000002;
16
17class YuzuTest final : public ServiceFramework<YuzuTest> {
18public:
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
35private:
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
108void 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
10namespace Core {
11class System;
12}
13
14namespace Service::Yuzu {
15
16struct TestResult {
17 u32 code;
18 std::string data;
19 std::string name;
20};
21
22void 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 09cf2ad77..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
51extern "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
59static 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
69static void PrintVersion() {
70 std::cout << "yuzu [Test Utility] " << Common::g_scm_branch << " " << Common::g_scm_desc
71 << std::endl;
72}
73
74static 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
91int 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 occurred. 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.
9YUZU_ICON ICON "../../dist/yuzu.ico"
10
11
12/////////////////////////////////////////////////////////////////////////////
13//
14// RT_MANIFEST
15//
16
170 RT_MANIFEST "../../dist/yuzu.manifest"