diff options
93 files changed, 3520 insertions, 2128 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index aaf3a90cf..27aa56780 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -261,7 +261,7 @@ if(ENABLE_SDL2) find_package(SDL2) if (NOT SDL2_FOUND) # otherwise add this to the list of libraries to install - list(APPEND CONAN_REQUIRED_LIBS "sdl2/2.0.12@bincrafters/stable") + list(APPEND CONAN_REQUIRED_LIBS "sdl2/2.0.14@bincrafters/stable") endif() endif() diff --git a/src/audio_core/voice_context.h b/src/audio_core/voice_context.h index 863248761..70359cadb 100644 --- a/src/audio_core/voice_context.h +++ b/src/audio_core/voice_context.h @@ -86,28 +86,28 @@ struct BehaviorFlags { static_assert(sizeof(BehaviorFlags) == 0x4, "BehaviorFlags is an invalid size"); struct ADPCMContext { - u16 header{}; - s16 yn1{}; - s16 yn2{}; + u16 header; + s16 yn1; + s16 yn2; }; static_assert(sizeof(ADPCMContext) == 0x6, "ADPCMContext is an invalid size"); struct VoiceState { - s64 played_sample_count{}; - s32 offset{}; - s32 wave_buffer_index{}; - std::array<bool, AudioCommon::MAX_WAVE_BUFFERS> is_wave_buffer_valid{}; - s32 wave_buffer_consumed{}; - std::array<s32, AudioCommon::MAX_SAMPLE_HISTORY> sample_history{}; - s32 fraction{}; - VAddr context_address{}; - Codec::ADPCM_Coeff coeff{}; - ADPCMContext context{}; - std::array<s64, 2> biquad_filter_state{}; - std::array<s32, AudioCommon::MAX_MIX_BUFFERS> previous_samples{}; - u32 external_context_size{}; - bool is_external_context_used{}; - bool voice_dropped{}; + s64 played_sample_count; + s32 offset; + s32 wave_buffer_index; + std::array<bool, AudioCommon::MAX_WAVE_BUFFERS> is_wave_buffer_valid; + s32 wave_buffer_consumed; + std::array<s32, AudioCommon::MAX_SAMPLE_HISTORY> sample_history; + s32 fraction; + VAddr context_address; + Codec::ADPCM_Coeff coeff; + ADPCMContext context; + std::array<s64, 2> biquad_filter_state; + std::array<s32, AudioCommon::MAX_MIX_BUFFERS> previous_samples; + u32 external_context_size; + bool is_external_context_used; + bool voice_dropped; }; class VoiceChannelResource { diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 9824c5564..f77575a00 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -98,7 +98,6 @@ add_library(common STATIC algorithm.h alignment.h assert.h - atomic_ops.cpp atomic_ops.h detached_tasks.cpp detached_tasks.h 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 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <cstring> - -#include "common/atomic_ops.h" - -#if _MSC_VER -#include <intrin.h> -#endif - -namespace Common { - -#if _MSC_VER - -bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) { - const u8 result = - _InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected); - return result == expected; -} - -bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) { - const u16 result = - _InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected); - return result == expected; -} - -bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) { - const u32 result = - _InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected); - return result == expected; -} - -bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) { - const u64 result = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer), - value, expected); - return result == expected; -} - -bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) { - return _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1], - value[0], - reinterpret_cast<__int64*>(expected.data())) != 0; -} - -#else - -bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) { - return __sync_bool_compare_and_swap(pointer, expected, value); -} - -bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) { - return __sync_bool_compare_and_swap(pointer, expected, value); -} - -bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) { - return __sync_bool_compare_and_swap(pointer, expected, value); -} - -bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) { - return __sync_bool_compare_and_swap(pointer, expected, value); -} - -bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) { - unsigned __int128 value_a; - unsigned __int128 expected_a; - std::memcpy(&value_a, value.data(), sizeof(u128)); - std::memcpy(&expected_a, expected.data(), sizeof(u128)); - return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a); -} - -#endif - -} // 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 @@ #pragma once +#include <cstring> +#include <memory> + #include "common/common_types.h" +#if _MSC_VER +#include <intrin.h> +#endif + namespace Common { -[[nodiscard]] bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected); -[[nodiscard]] bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected); -[[nodiscard]] bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected); -[[nodiscard]] bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected); -[[nodiscard]] bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected); +#if _MSC_VER + +[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) { + const u8 result = + _InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected); + return result == expected; +} + +[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) { + const u16 result = + _InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected); + return result == expected; +} + +[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) { + const u32 result = + _InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected); + return result == expected; +} + +[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) { + const u64 result = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer), + value, expected); + return result == expected; +} + +[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) { + return _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1], + value[0], + reinterpret_cast<__int64*>(expected.data())) != 0; +} + +#else + +[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) { + return __sync_bool_compare_and_swap(pointer, expected, value); +} + +[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) { + return __sync_bool_compare_and_swap(pointer, expected, value); +} + +[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) { + return __sync_bool_compare_and_swap(pointer, expected, value); +} + +[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) { + return __sync_bool_compare_and_swap(pointer, expected, value); +} + +[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) { + unsigned __int128 value_a; + unsigned __int128 expected_a; + std::memcpy(&value_a, value.data(), sizeof(u128)); + std::memcpy(&expected_a, expected.data(), sizeof(u128)); + return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a); +} + +#endif } // namespace Common diff --git a/src/common/bit_util.h b/src/common/bit_util.h index 685e7fc9b..64520ca4e 100644 --- a/src/common/bit_util.h +++ b/src/common/bit_util.h @@ -4,13 +4,10 @@ #pragma once +#include <bit> #include <climits> #include <cstddef> -#ifdef _MSC_VER -#include <intrin.h> -#endif - #include "common/common_types.h" namespace Common { @@ -21,48 +18,30 @@ template <typename T> return sizeof(T) * CHAR_BIT; } -#ifdef _MSC_VER - -[[nodiscard]] inline u32 MostSignificantBit32(const u32 value) { - unsigned long result; - _BitScanReverse(&result, value); - return static_cast<u32>(result); -} - -[[nodiscard]] inline u32 MostSignificantBit64(const u64 value) { - unsigned long result; - _BitScanReverse64(&result, value); - return static_cast<u32>(result); -} - -#else - -[[nodiscard]] inline u32 MostSignificantBit32(const u32 value) { - return 31U - static_cast<u32>(__builtin_clz(value)); +[[nodiscard]] constexpr u32 MostSignificantBit32(const u32 value) { + return 31U - static_cast<u32>(std::countl_zero(value)); } -[[nodiscard]] inline u32 MostSignificantBit64(const u64 value) { - return 63U - static_cast<u32>(__builtin_clzll(value)); +[[nodiscard]] constexpr u32 MostSignificantBit64(const u64 value) { + return 63U - static_cast<u32>(std::countl_zero(value)); } -#endif - -[[nodiscard]] inline u32 Log2Floor32(const u32 value) { +[[nodiscard]] constexpr u32 Log2Floor32(const u32 value) { return MostSignificantBit32(value); } -[[nodiscard]] inline u32 Log2Ceil32(const u32 value) { - const u32 log2_f = Log2Floor32(value); - return log2_f + ((value ^ (1U << log2_f)) != 0U); +[[nodiscard]] constexpr u32 Log2Floor64(const u64 value) { + return MostSignificantBit64(value); } -[[nodiscard]] inline u32 Log2Floor64(const u64 value) { - return MostSignificantBit64(value); +[[nodiscard]] constexpr u32 Log2Ceil32(const u32 value) { + const u32 log2_f = Log2Floor32(value); + return log2_f + static_cast<u32>((value ^ (1U << log2_f)) != 0U); } -[[nodiscard]] inline u32 Log2Ceil64(const u64 value) { - const u64 log2_f = static_cast<u64>(Log2Floor64(value)); - return static_cast<u32>(log2_f + ((value ^ (1ULL << log2_f)) != 0ULL)); +[[nodiscard]] constexpr u32 Log2Ceil64(const u64 value) { + const u64 log2_f = Log2Floor64(value); + return static_cast<u32>(log2_f + static_cast<u64>((value ^ (1ULL << log2_f)) != 0ULL)); } } // namespace Common 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; } struct IntrusiveRedBlackTreeNode { +public: + using EntryType = RBEntry<IntrusiveRedBlackTreeNode>; + + constexpr IntrusiveRedBlackTreeNode() = default; + + void SetEntry(const EntryType& new_entry) { + entry = new_entry; + } + + [[nodiscard]] EntryType& GetEntry() { + return entry; + } + + [[nodiscard]] const EntryType& GetEntry() const { + return entry; + } private: - RB_ENTRY(IntrusiveRedBlackTreeNode) entry{}; + EntryType entry{}; friend class impl::IntrusiveRedBlackTreeImpl; template <class, class, class> friend class IntrusiveRedBlackTree; - -public: - constexpr IntrusiveRedBlackTreeNode() = default; }; template <class T, class Traits, class Comparator> @@ -35,17 +48,12 @@ class IntrusiveRedBlackTree; namespace impl { class IntrusiveRedBlackTreeImpl { - private: template <class, class, class> friend class ::Common::IntrusiveRedBlackTree; -private: - RB_HEAD(IntrusiveRedBlackTreeRoot, IntrusiveRedBlackTreeNode); - using RootType = IntrusiveRedBlackTreeRoot; - -private: - IntrusiveRedBlackTreeRoot root; + using RootType = RBHead<IntrusiveRedBlackTreeNode>; + RootType root; public: template <bool Const> @@ -121,57 +129,45 @@ public: } }; -protected: - // Generate static implementations for non-comparison operations for IntrusiveRedBlackTreeRoot. - RB_GENERATE_WITHOUT_COMPARE_STATIC(IntrusiveRedBlackTreeRoot, IntrusiveRedBlackTreeNode, entry); - private: // Define accessors using RB_* functions. - constexpr void InitializeImpl() { - RB_INIT(&this->root); - } - bool EmptyImpl() const { - return RB_EMPTY(&this->root); + return root.IsEmpty(); } IntrusiveRedBlackTreeNode* GetMinImpl() const { - return RB_MIN(IntrusiveRedBlackTreeRoot, - const_cast<IntrusiveRedBlackTreeRoot*>(&this->root)); + return RB_MIN(const_cast<RootType*>(&root)); } IntrusiveRedBlackTreeNode* GetMaxImpl() const { - return RB_MAX(IntrusiveRedBlackTreeRoot, - const_cast<IntrusiveRedBlackTreeRoot*>(&this->root)); + return RB_MAX(const_cast<RootType*>(&root)); } IntrusiveRedBlackTreeNode* RemoveImpl(IntrusiveRedBlackTreeNode* node) { - return RB_REMOVE(IntrusiveRedBlackTreeRoot, &this->root, node); + return RB_REMOVE(&root, node); } public: static IntrusiveRedBlackTreeNode* GetNext(IntrusiveRedBlackTreeNode* node) { - return RB_NEXT(IntrusiveRedBlackTreeRoot, nullptr, node); + return RB_NEXT(node); } static IntrusiveRedBlackTreeNode* GetPrev(IntrusiveRedBlackTreeNode* node) { - return RB_PREV(IntrusiveRedBlackTreeRoot, nullptr, node); + return RB_PREV(node); } - static IntrusiveRedBlackTreeNode const* GetNext(const IntrusiveRedBlackTreeNode* node) { + static const IntrusiveRedBlackTreeNode* GetNext(const IntrusiveRedBlackTreeNode* node) { return static_cast<const IntrusiveRedBlackTreeNode*>( GetNext(const_cast<IntrusiveRedBlackTreeNode*>(node))); } - static IntrusiveRedBlackTreeNode const* GetPrev(const IntrusiveRedBlackTreeNode* node) { + static const IntrusiveRedBlackTreeNode* GetPrev(const IntrusiveRedBlackTreeNode* node) { return static_cast<const IntrusiveRedBlackTreeNode*>( GetPrev(const_cast<IntrusiveRedBlackTreeNode*>(node))); } public: - constexpr IntrusiveRedBlackTreeImpl() : root() { - this->InitializeImpl(); - } + constexpr IntrusiveRedBlackTreeImpl() {} // Iterator accessors. iterator begin() { @@ -269,8 +265,6 @@ private: ImplType impl{}; public: - struct IntrusiveRedBlackTreeRootWithCompare : ImplType::IntrusiveRedBlackTreeRoot {}; - template <bool Const> class Iterator; @@ -363,11 +357,6 @@ public: }; private: - // Generate static implementations for comparison operations for IntrusiveRedBlackTreeRoot. - RB_GENERATE_WITH_COMPARE_STATIC(IntrusiveRedBlackTreeRootWithCompare, IntrusiveRedBlackTreeNode, - entry, CompareImpl, LightCompareImpl); - -private: static int CompareImpl(const IntrusiveRedBlackTreeNode* lhs, const IntrusiveRedBlackTreeNode* rhs) { return Comparator::Compare(*Traits::GetParent(lhs), *Traits::GetParent(rhs)); @@ -379,41 +368,27 @@ private: // Define accessors using RB_* functions. IntrusiveRedBlackTreeNode* InsertImpl(IntrusiveRedBlackTreeNode* node) { - return RB_INSERT(IntrusiveRedBlackTreeRootWithCompare, - static_cast<IntrusiveRedBlackTreeRootWithCompare*>(&this->impl.root), - node); + return RB_INSERT(&impl.root, node, CompareImpl); } IntrusiveRedBlackTreeNode* FindImpl(const IntrusiveRedBlackTreeNode* node) const { - return RB_FIND( - IntrusiveRedBlackTreeRootWithCompare, - const_cast<IntrusiveRedBlackTreeRootWithCompare*>( - static_cast<const IntrusiveRedBlackTreeRootWithCompare*>(&this->impl.root)), - const_cast<IntrusiveRedBlackTreeNode*>(node)); + return RB_FIND(const_cast<ImplType::RootType*>(&impl.root), + const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl); } IntrusiveRedBlackTreeNode* NFindImpl(const IntrusiveRedBlackTreeNode* node) const { - return RB_NFIND( - IntrusiveRedBlackTreeRootWithCompare, - const_cast<IntrusiveRedBlackTreeRootWithCompare*>( - static_cast<const IntrusiveRedBlackTreeRootWithCompare*>(&this->impl.root)), - const_cast<IntrusiveRedBlackTreeNode*>(node)); + return RB_NFIND(const_cast<ImplType::RootType*>(&impl.root), + const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl); } IntrusiveRedBlackTreeNode* FindLightImpl(const_light_pointer lelm) const { - return RB_FIND_LIGHT( - IntrusiveRedBlackTreeRootWithCompare, - const_cast<IntrusiveRedBlackTreeRootWithCompare*>( - static_cast<const IntrusiveRedBlackTreeRootWithCompare*>(&this->impl.root)), - static_cast<const void*>(lelm)); + return RB_FIND_LIGHT(const_cast<ImplType::RootType*>(&impl.root), + static_cast<const void*>(lelm), LightCompareImpl); } IntrusiveRedBlackTreeNode* NFindLightImpl(const_light_pointer lelm) const { - return RB_NFIND_LIGHT( - IntrusiveRedBlackTreeRootWithCompare, - const_cast<IntrusiveRedBlackTreeRootWithCompare*>( - static_cast<const IntrusiveRedBlackTreeRootWithCompare*>(&this->impl.root)), - static_cast<const void*>(lelm)); + return RB_NFIND_LIGHT(const_cast<ImplType::RootType*>(&impl.root), + static_cast<const void*>(lelm), LightCompareImpl); } public: 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 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef _SYS_TREE_H_ -#define _SYS_TREE_H_ - -/* FreeBSD <sys/cdefs.h> has a lot of defines we don't really want. */ -/* tree.h only actually uses __inline and __unused, so we'll just define those. */ - -/* #include <sys/cdefs.h> */ - -#ifndef __inline -#define __inline inline -#endif +#pragma once /* - * This file defines data structures for different types of trees: - * splay trees and red-black trees. - * - * A splay tree is a self-organizing data structure. Every operation - * on the tree causes a splay to happen. The splay moves the requested - * node to the root of the tree and partly rebalances it. - * - * This has the benefit that request locality causes faster lookups as - * the requested nodes move to the top of the tree. On the other hand, - * every lookup causes memory writes. - * - * The Balance Theorem bounds the total access time for m operations - * and n inserts on an initially empty tree as O((m + n)lg n). The - * amortized cost for a sequence of m accesses to a splay tree is O(lg n); + * This file defines data structures for red-black trees. * * A red-black tree is a binary search tree with the node color as an * extra attribute. It fulfills a set of conditions: @@ -66,757 +43,632 @@ * The maximum height of a red-black tree is 2lg (n+1). */ -#define SPLAY_HEAD(name, type) \ - struct name { \ - struct type* sph_root; /* root of the tree */ \ - } - -#define SPLAY_INITIALIZER(root) \ - { NULL } - -#define SPLAY_INIT(root) \ - do { \ - (root)->sph_root = NULL; \ - } while (/*CONSTCOND*/ 0) - -#define SPLAY_ENTRY(type) \ - struct { \ - struct type* spe_left; /* left element */ \ - struct type* spe_right; /* right element */ \ - } - -#define SPLAY_LEFT(elm, field) (elm)->field.spe_left -#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right -#define SPLAY_ROOT(head) (head)->sph_root -#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) - -/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ -#define SPLAY_ROTATE_RIGHT(head, tmp, field) \ - do { \ - SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ - SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ - (head)->sph_root = tmp; \ - } while (/*CONSTCOND*/ 0) - -#define SPLAY_ROTATE_LEFT(head, tmp, field) \ - do { \ - SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ - SPLAY_LEFT(tmp, field) = (head)->sph_root; \ - (head)->sph_root = tmp; \ - } while (/*CONSTCOND*/ 0) - -#define SPLAY_LINKLEFT(head, tmp, field) \ - do { \ - SPLAY_LEFT(tmp, field) = (head)->sph_root; \ - tmp = (head)->sph_root; \ - (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ - } while (/*CONSTCOND*/ 0) - -#define SPLAY_LINKRIGHT(head, tmp, field) \ - do { \ - SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ - tmp = (head)->sph_root; \ - (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ - } while (/*CONSTCOND*/ 0) - -#define SPLAY_ASSEMBLE(head, node, left, right, field) \ - do { \ - SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ - SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field); \ - SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ - SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ - } while (/*CONSTCOND*/ 0) - -/* Generates prototypes and inline functions */ - -#define SPLAY_PROTOTYPE(name, type, field, cmp) \ - void name##_SPLAY(struct name*, struct type*); \ - void name##_SPLAY_MINMAX(struct name*, int); \ - struct type* name##_SPLAY_INSERT(struct name*, struct type*); \ - struct type* name##_SPLAY_REMOVE(struct name*, struct type*); \ - \ - /* Finds the node with the same key as elm */ \ - static __inline struct type* name##_SPLAY_FIND(struct name* head, struct type* elm) { \ - if (SPLAY_EMPTY(head)) \ - return (NULL); \ - name##_SPLAY(head, elm); \ - if ((cmp)(elm, (head)->sph_root) == 0) \ - return (head->sph_root); \ - return (NULL); \ - } \ - \ - static __inline struct type* name##_SPLAY_NEXT(struct name* head, struct type* elm) { \ - name##_SPLAY(head, elm); \ - if (SPLAY_RIGHT(elm, field) != NULL) { \ - elm = SPLAY_RIGHT(elm, field); \ - while (SPLAY_LEFT(elm, field) != NULL) { \ - elm = SPLAY_LEFT(elm, field); \ - } \ - } else \ - elm = NULL; \ - return (elm); \ - } \ - \ - static __inline struct type* name##_SPLAY_MIN_MAX(struct name* head, int val) { \ - name##_SPLAY_MINMAX(head, val); \ - return (SPLAY_ROOT(head)); \ - } - -/* Main splay operation. - * Moves node close to the key of elm to top - */ -#define SPLAY_GENERATE(name, type, field, cmp) \ - struct type* name##_SPLAY_INSERT(struct name* head, struct type* elm) { \ - if (SPLAY_EMPTY(head)) { \ - SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ - } else { \ - int __comp; \ - name##_SPLAY(head, elm); \ - __comp = (cmp)(elm, (head)->sph_root); \ - if (__comp < 0) { \ - SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field); \ - SPLAY_RIGHT(elm, field) = (head)->sph_root; \ - SPLAY_LEFT((head)->sph_root, field) = NULL; \ - } else if (__comp > 0) { \ - SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field); \ - SPLAY_LEFT(elm, field) = (head)->sph_root; \ - SPLAY_RIGHT((head)->sph_root, field) = NULL; \ - } else \ - return ((head)->sph_root); \ - } \ - (head)->sph_root = (elm); \ - return (NULL); \ - } \ - \ - struct type* name##_SPLAY_REMOVE(struct name* head, struct type* elm) { \ - struct type* __tmp; \ - if (SPLAY_EMPTY(head)) \ - return (NULL); \ - name##_SPLAY(head, elm); \ - if ((cmp)(elm, (head)->sph_root) == 0) { \ - if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ - (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ - } else { \ - __tmp = SPLAY_RIGHT((head)->sph_root, field); \ - (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ - name##_SPLAY(head, elm); \ - SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ - } \ - return (elm); \ - } \ - return (NULL); \ - } \ - \ - void name##_SPLAY(struct name* head, struct type* elm) { \ - struct type __node, *__left, *__right, *__tmp; \ - int __comp; \ - \ - SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL; \ - __left = __right = &__node; \ - \ - while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \ - if (__comp < 0) { \ - __tmp = SPLAY_LEFT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if ((cmp)(elm, __tmp) < 0) { \ - SPLAY_ROTATE_RIGHT(head, __tmp, field); \ - if (SPLAY_LEFT((head)->sph_root, field) == NULL) \ - break; \ - } \ - SPLAY_LINKLEFT(head, __right, field); \ - } else if (__comp > 0) { \ - __tmp = SPLAY_RIGHT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if ((cmp)(elm, __tmp) > 0) { \ - SPLAY_ROTATE_LEFT(head, __tmp, field); \ - if (SPLAY_RIGHT((head)->sph_root, field) == NULL) \ - break; \ - } \ - SPLAY_LINKRIGHT(head, __left, field); \ - } \ - } \ - SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ - } \ - \ - /* Splay with either the minimum or the maximum element \ - * Used to find minimum or maximum element in tree. \ - */ \ - void name##_SPLAY_MINMAX(struct name* head, int __comp) { \ - struct type __node, *__left, *__right, *__tmp; \ - \ - SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL; \ - __left = __right = &__node; \ - \ - while (1) { \ - if (__comp < 0) { \ - __tmp = SPLAY_LEFT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if (__comp < 0) { \ - SPLAY_ROTATE_RIGHT(head, __tmp, field); \ - if (SPLAY_LEFT((head)->sph_root, field) == NULL) \ - break; \ - } \ - SPLAY_LINKLEFT(head, __right, field); \ - } else if (__comp > 0) { \ - __tmp = SPLAY_RIGHT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if (__comp > 0) { \ - SPLAY_ROTATE_LEFT(head, __tmp, field); \ - if (SPLAY_RIGHT((head)->sph_root, field) == NULL) \ - break; \ - } \ - SPLAY_LINKRIGHT(head, __left, field); \ - } \ - } \ - SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ - } - -#define SPLAY_NEGINF -1 -#define SPLAY_INF 1 - -#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) -#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) -#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) -#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) -#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) -#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) - -#define SPLAY_FOREACH(x, name, head) \ - for ((x) = SPLAY_MIN(name, head); (x) != NULL; (x) = SPLAY_NEXT(name, head, x)) - -/* Macros that define a red-black tree */ -#define RB_HEAD(name, type) \ - struct name { \ - struct type* rbh_root; /* root of the tree */ \ - } - -#define RB_INITIALIZER(root) \ - { NULL } - -#define RB_INIT(root) \ - do { \ - (root)->rbh_root = NULL; \ - } while (/*CONSTCOND*/ 0) - -#define RB_BLACK 0 -#define RB_RED 1 -#define RB_ENTRY(type) \ - struct { \ - struct type* rbe_left; /* left element */ \ - struct type* rbe_right; /* right element */ \ - struct type* rbe_parent; /* parent element */ \ - int rbe_color; /* node color */ \ - } - -#define RB_LEFT(elm, field) (elm)->field.rbe_left -#define RB_RIGHT(elm, field) (elm)->field.rbe_right -#define RB_PARENT(elm, field) (elm)->field.rbe_parent -#define RB_COLOR(elm, field) (elm)->field.rbe_color -#define RB_ROOT(head) (head)->rbh_root -#define RB_EMPTY(head) (RB_ROOT(head) == NULL) - -#define RB_SET(elm, parent, field) \ - do { \ - RB_PARENT(elm, field) = parent; \ - RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ - RB_COLOR(elm, field) = RB_RED; \ - } while (/*CONSTCOND*/ 0) - -#define RB_SET_BLACKRED(black, red, field) \ - do { \ - RB_COLOR(black, field) = RB_BLACK; \ - RB_COLOR(red, field) = RB_RED; \ - } while (/*CONSTCOND*/ 0) - -#ifndef RB_AUGMENT -#define RB_AUGMENT(x) \ - do { \ - } while (0) -#endif - -#define RB_ROTATE_LEFT(head, elm, tmp, field) \ - do { \ - (tmp) = RB_RIGHT(elm, field); \ - if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \ - RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ - } \ - RB_AUGMENT(elm); \ - if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ - if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ - RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ - else \ - RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ - } else \ - (head)->rbh_root = (tmp); \ - RB_LEFT(tmp, field) = (elm); \ - RB_PARENT(elm, field) = (tmp); \ - RB_AUGMENT(tmp); \ - if ((RB_PARENT(tmp, field))) \ - RB_AUGMENT(RB_PARENT(tmp, field)); \ - } while (/*CONSTCOND*/ 0) - -#define RB_ROTATE_RIGHT(head, elm, tmp, field) \ - do { \ - (tmp) = RB_LEFT(elm, field); \ - if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \ - RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ - } \ - RB_AUGMENT(elm); \ - if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ - if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ - RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ - else \ - RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ - } else \ - (head)->rbh_root = (tmp); \ - RB_RIGHT(tmp, field) = (elm); \ - RB_PARENT(elm, field) = (tmp); \ - RB_AUGMENT(tmp); \ - if ((RB_PARENT(tmp, field))) \ - RB_AUGMENT(RB_PARENT(tmp, field)); \ - } while (/*CONSTCOND*/ 0) - -/* Generates prototypes and inline functions */ -#define RB_PROTOTYPE(name, type, field, cmp) RB_PROTOTYPE_INTERNAL(name, type, field, cmp, ) -#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ - RB_PROTOTYPE_INTERNAL(name, type, field, cmp, static) -#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ - RB_PROTOTYPE_INSERT_COLOR(name, type, attr); \ - RB_PROTOTYPE_REMOVE_COLOR(name, type, attr); \ - RB_PROTOTYPE_INSERT(name, type, attr); \ - RB_PROTOTYPE_REMOVE(name, type, attr); \ - RB_PROTOTYPE_FIND(name, type, attr); \ - RB_PROTOTYPE_NFIND(name, type, attr); \ - RB_PROTOTYPE_FIND_LIGHT(name, type, attr); \ - RB_PROTOTYPE_NFIND_LIGHT(name, type, attr); \ - RB_PROTOTYPE_NEXT(name, type, attr); \ - RB_PROTOTYPE_PREV(name, type, attr); \ - RB_PROTOTYPE_MINMAX(name, type, attr); -#define RB_PROTOTYPE_INSERT_COLOR(name, type, attr) \ - attr void name##_RB_INSERT_COLOR(struct name*, struct type*) -#define RB_PROTOTYPE_REMOVE_COLOR(name, type, attr) \ - attr void name##_RB_REMOVE_COLOR(struct name*, struct type*, struct type*) -#define RB_PROTOTYPE_REMOVE(name, type, attr) \ - attr struct type* name##_RB_REMOVE(struct name*, struct type*) -#define RB_PROTOTYPE_INSERT(name, type, attr) \ - attr struct type* name##_RB_INSERT(struct name*, struct type*) -#define RB_PROTOTYPE_FIND(name, type, attr) \ - attr struct type* name##_RB_FIND(struct name*, struct type*) -#define RB_PROTOTYPE_NFIND(name, type, attr) \ - attr struct type* name##_RB_NFIND(struct name*, struct type*) -#define RB_PROTOTYPE_FIND_LIGHT(name, type, attr) \ - attr struct type* name##_RB_FIND_LIGHT(struct name*, const void*) -#define RB_PROTOTYPE_NFIND_LIGHT(name, type, attr) \ - attr struct type* name##_RB_NFIND_LIGHT(struct name*, const void*) -#define RB_PROTOTYPE_NEXT(name, type, attr) attr struct type* name##_RB_NEXT(struct type*) -#define RB_PROTOTYPE_PREV(name, type, attr) attr struct type* name##_RB_PREV(struct type*) -#define RB_PROTOTYPE_MINMAX(name, type, attr) attr struct type* name##_RB_MINMAX(struct name*, int) - -/* Main rb operation. - * Moves node close to the key of elm to top - */ -#define RB_GENERATE_WITHOUT_COMPARE(name, type, field) \ - RB_GENERATE_WITHOUT_COMPARE_INTERNAL(name, type, field, ) -#define RB_GENERATE_WITHOUT_COMPARE_STATIC(name, type, field) \ - RB_GENERATE_WITHOUT_COMPARE_INTERNAL(name, type, field, static) -#define RB_GENERATE_WITHOUT_COMPARE_INTERNAL(name, type, field, attr) \ - RB_GENERATE_REMOVE_COLOR(name, type, field, attr) \ - RB_GENERATE_REMOVE(name, type, field, attr) \ - RB_GENERATE_NEXT(name, type, field, attr) \ - RB_GENERATE_PREV(name, type, field, attr) \ - RB_GENERATE_MINMAX(name, type, field, attr) - -#define RB_GENERATE_WITH_COMPARE(name, type, field, cmp, lcmp) \ - RB_GENERATE_WITH_COMPARE_INTERNAL(name, type, field, cmp, lcmp, ) -#define RB_GENERATE_WITH_COMPARE_STATIC(name, type, field, cmp, lcmp) \ - RB_GENERATE_WITH_COMPARE_INTERNAL(name, type, field, cmp, lcmp, static) -#define RB_GENERATE_WITH_COMPARE_INTERNAL(name, type, field, cmp, lcmp, attr) \ - RB_GENERATE_INSERT_COLOR(name, type, field, attr) \ - RB_GENERATE_INSERT(name, type, field, cmp, attr) \ - RB_GENERATE_FIND(name, type, field, cmp, attr) \ - RB_GENERATE_NFIND(name, type, field, cmp, attr) \ - RB_GENERATE_FIND_LIGHT(name, type, field, lcmp, attr) \ - RB_GENERATE_NFIND_LIGHT(name, type, field, lcmp, attr) - -#define RB_GENERATE_ALL(name, type, field, cmp) RB_GENERATE_ALL_INTERNAL(name, type, field, cmp, ) -#define RB_GENERATE_ALL_STATIC(name, type, field, cmp) \ - RB_GENERATE_ALL_INTERNAL(name, type, field, cmp, static) -#define RB_GENERATE_ALL_INTERNAL(name, type, field, cmp, attr) \ - RB_GENERATE_WITHOUT_COMPARE_INTERNAL(name, type, field, attr) \ - RB_GENERATE_WITH_COMPARE_INTERNAL(name, type, field, cmp, attr) - -#define RB_GENERATE_INSERT_COLOR(name, type, field, attr) \ - attr void name##_RB_INSERT_COLOR(struct name* head, struct type* elm) { \ - struct type *parent, *gparent, *tmp; \ - while ((parent = RB_PARENT(elm, field)) != NULL && RB_COLOR(parent, field) == RB_RED) { \ - gparent = RB_PARENT(parent, field); \ - if (parent == RB_LEFT(gparent, field)) { \ - tmp = RB_RIGHT(gparent, field); \ - if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ - RB_COLOR(tmp, field) = RB_BLACK; \ - RB_SET_BLACKRED(parent, gparent, field); \ - elm = gparent; \ - continue; \ - } \ - if (RB_RIGHT(parent, field) == elm) { \ - RB_ROTATE_LEFT(head, parent, tmp, field); \ - tmp = parent; \ - parent = elm; \ - elm = tmp; \ - } \ - RB_SET_BLACKRED(parent, gparent, field); \ - RB_ROTATE_RIGHT(head, gparent, tmp, field); \ - } else { \ - tmp = RB_LEFT(gparent, field); \ - if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ - RB_COLOR(tmp, field) = RB_BLACK; \ - RB_SET_BLACKRED(parent, gparent, field); \ - elm = gparent; \ - continue; \ - } \ - if (RB_LEFT(parent, field) == elm) { \ - RB_ROTATE_RIGHT(head, parent, tmp, field); \ - tmp = parent; \ - parent = elm; \ - elm = tmp; \ - } \ - RB_SET_BLACKRED(parent, gparent, field); \ - RB_ROTATE_LEFT(head, gparent, tmp, field); \ - } \ - } \ - RB_COLOR(head->rbh_root, field) = RB_BLACK; \ - } - -#define RB_GENERATE_REMOVE_COLOR(name, type, field, attr) \ - attr void name##_RB_REMOVE_COLOR(struct name* head, struct type* parent, struct type* elm) { \ - struct type* tmp; \ - while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && elm != RB_ROOT(head)) { \ - if (RB_LEFT(parent, field) == elm) { \ - tmp = RB_RIGHT(parent, field); \ - if (RB_COLOR(tmp, field) == RB_RED) { \ - RB_SET_BLACKRED(tmp, parent, field); \ - RB_ROTATE_LEFT(head, parent, tmp, field); \ - tmp = RB_RIGHT(parent, field); \ - } \ - if ((RB_LEFT(tmp, field) == NULL || \ - RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) && \ - (RB_RIGHT(tmp, field) == NULL || \ - RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) { \ - RB_COLOR(tmp, field) = RB_RED; \ - elm = parent; \ - parent = RB_PARENT(elm, field); \ - } else { \ - if (RB_RIGHT(tmp, field) == NULL || \ - RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) { \ - struct type* oleft; \ - if ((oleft = RB_LEFT(tmp, field)) != NULL) \ - RB_COLOR(oleft, field) = RB_BLACK; \ - RB_COLOR(tmp, field) = RB_RED; \ - RB_ROTATE_RIGHT(head, tmp, oleft, field); \ - tmp = RB_RIGHT(parent, field); \ - } \ - RB_COLOR(tmp, field) = RB_COLOR(parent, field); \ - RB_COLOR(parent, field) = RB_BLACK; \ - if (RB_RIGHT(tmp, field)) \ - RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK; \ - RB_ROTATE_LEFT(head, parent, tmp, field); \ - elm = RB_ROOT(head); \ - break; \ - } \ - } else { \ - tmp = RB_LEFT(parent, field); \ - if (RB_COLOR(tmp, field) == RB_RED) { \ - RB_SET_BLACKRED(tmp, parent, field); \ - RB_ROTATE_RIGHT(head, parent, tmp, field); \ - tmp = RB_LEFT(parent, field); \ - } \ - if ((RB_LEFT(tmp, field) == NULL || \ - RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) && \ - (RB_RIGHT(tmp, field) == NULL || \ - RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) { \ - RB_COLOR(tmp, field) = RB_RED; \ - elm = parent; \ - parent = RB_PARENT(elm, field); \ - } else { \ - if (RB_LEFT(tmp, field) == NULL || \ - RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) { \ - struct type* oright; \ - if ((oright = RB_RIGHT(tmp, field)) != NULL) \ - RB_COLOR(oright, field) = RB_BLACK; \ - RB_COLOR(tmp, field) = RB_RED; \ - RB_ROTATE_LEFT(head, tmp, oright, field); \ - tmp = RB_LEFT(parent, field); \ - } \ - RB_COLOR(tmp, field) = RB_COLOR(parent, field); \ - RB_COLOR(parent, field) = RB_BLACK; \ - if (RB_LEFT(tmp, field)) \ - RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK; \ - RB_ROTATE_RIGHT(head, parent, tmp, field); \ - elm = RB_ROOT(head); \ - break; \ - } \ - } \ - } \ - if (elm) \ - RB_COLOR(elm, field) = RB_BLACK; \ - } - -#define RB_GENERATE_REMOVE(name, type, field, attr) \ - attr struct type* name##_RB_REMOVE(struct name* head, struct type* elm) { \ - struct type *child, *parent, *old = elm; \ - int color; \ - if (RB_LEFT(elm, field) == NULL) \ - child = RB_RIGHT(elm, field); \ - else if (RB_RIGHT(elm, field) == NULL) \ - child = RB_LEFT(elm, field); \ - else { \ - struct type* left; \ - elm = RB_RIGHT(elm, field); \ - while ((left = RB_LEFT(elm, field)) != NULL) \ - elm = left; \ - child = RB_RIGHT(elm, field); \ - parent = RB_PARENT(elm, field); \ - color = RB_COLOR(elm, field); \ - if (child) \ - RB_PARENT(child, field) = parent; \ - if (parent) { \ - if (RB_LEFT(parent, field) == elm) \ - RB_LEFT(parent, field) = child; \ - else \ - RB_RIGHT(parent, field) = child; \ - RB_AUGMENT(parent); \ - } else \ - RB_ROOT(head) = child; \ - if (RB_PARENT(elm, field) == old) \ - parent = elm; \ - (elm)->field = (old)->field; \ - if (RB_PARENT(old, field)) { \ - if (RB_LEFT(RB_PARENT(old, field), field) == old) \ - RB_LEFT(RB_PARENT(old, field), field) = elm; \ - else \ - RB_RIGHT(RB_PARENT(old, field), field) = elm; \ - RB_AUGMENT(RB_PARENT(old, field)); \ - } else \ - RB_ROOT(head) = elm; \ - RB_PARENT(RB_LEFT(old, field), field) = elm; \ - if (RB_RIGHT(old, field)) \ - RB_PARENT(RB_RIGHT(old, field), field) = elm; \ - if (parent) { \ - left = parent; \ - do { \ - RB_AUGMENT(left); \ - } while ((left = RB_PARENT(left, field)) != NULL); \ - } \ - goto color; \ - } \ - parent = RB_PARENT(elm, field); \ - color = RB_COLOR(elm, field); \ - if (child) \ - RB_PARENT(child, field) = parent; \ - if (parent) { \ - if (RB_LEFT(parent, field) == elm) \ - RB_LEFT(parent, field) = child; \ - else \ - RB_RIGHT(parent, field) = child; \ - RB_AUGMENT(parent); \ - } else \ - RB_ROOT(head) = child; \ - color: \ - if (color == RB_BLACK) \ - name##_RB_REMOVE_COLOR(head, parent, child); \ - return (old); \ - } - -#define RB_GENERATE_INSERT(name, type, field, cmp, attr) \ - /* Inserts a node into the RB tree */ \ - attr struct type* name##_RB_INSERT(struct name* head, struct type* elm) { \ - struct type* tmp; \ - struct type* parent = NULL; \ - int comp = 0; \ - tmp = RB_ROOT(head); \ - while (tmp) { \ - parent = tmp; \ - comp = (cmp)(elm, parent); \ - if (comp < 0) \ - tmp = RB_LEFT(tmp, field); \ - else if (comp > 0) \ - tmp = RB_RIGHT(tmp, field); \ - else \ - return (tmp); \ - } \ - RB_SET(elm, parent, field); \ - if (parent != NULL) { \ - if (comp < 0) \ - RB_LEFT(parent, field) = elm; \ - else \ - RB_RIGHT(parent, field) = elm; \ - RB_AUGMENT(parent); \ - } else \ - RB_ROOT(head) = elm; \ - name##_RB_INSERT_COLOR(head, elm); \ - return (NULL); \ - } - -#define RB_GENERATE_FIND(name, type, field, cmp, attr) \ - /* Finds the node with the same key as elm */ \ - attr struct type* name##_RB_FIND(struct name* head, struct type* elm) { \ - struct type* tmp = RB_ROOT(head); \ - int comp; \ - while (tmp) { \ - comp = cmp(elm, tmp); \ - if (comp < 0) \ - tmp = RB_LEFT(tmp, field); \ - else if (comp > 0) \ - tmp = RB_RIGHT(tmp, field); \ - else \ - return (tmp); \ - } \ - return (NULL); \ - } - -#define RB_GENERATE_NFIND(name, type, field, cmp, attr) \ - /* Finds the first node greater than or equal to the search key */ \ - attr struct type* name##_RB_NFIND(struct name* head, struct type* elm) { \ - struct type* tmp = RB_ROOT(head); \ - struct type* res = NULL; \ - int comp; \ - while (tmp) { \ - comp = cmp(elm, tmp); \ - if (comp < 0) { \ - res = tmp; \ - tmp = RB_LEFT(tmp, field); \ - } else if (comp > 0) \ - tmp = RB_RIGHT(tmp, field); \ - else \ - return (tmp); \ - } \ - return (res); \ - } - -#define RB_GENERATE_FIND_LIGHT(name, type, field, lcmp, attr) \ - /* Finds the node with the same key as elm */ \ - attr struct type* name##_RB_FIND_LIGHT(struct name* head, const void* lelm) { \ - struct type* tmp = RB_ROOT(head); \ - int comp; \ - while (tmp) { \ - comp = lcmp(lelm, tmp); \ - if (comp < 0) \ - tmp = RB_LEFT(tmp, field); \ - else if (comp > 0) \ - tmp = RB_RIGHT(tmp, field); \ - else \ - return (tmp); \ - } \ - return (NULL); \ - } - -#define RB_GENERATE_NFIND_LIGHT(name, type, field, lcmp, attr) \ - /* Finds the first node greater than or equal to the search key */ \ - attr struct type* name##_RB_NFIND_LIGHT(struct name* head, const void* lelm) { \ - struct type* tmp = RB_ROOT(head); \ - struct type* res = NULL; \ - int comp; \ - while (tmp) { \ - comp = lcmp(lelm, tmp); \ - if (comp < 0) { \ - res = tmp; \ - tmp = RB_LEFT(tmp, field); \ - } else if (comp > 0) \ - tmp = RB_RIGHT(tmp, field); \ - else \ - return (tmp); \ - } \ - return (res); \ - } - -#define RB_GENERATE_NEXT(name, type, field, attr) \ - /* ARGSUSED */ \ - attr struct type* name##_RB_NEXT(struct type* elm) { \ - if (RB_RIGHT(elm, field)) { \ - elm = RB_RIGHT(elm, field); \ - while (RB_LEFT(elm, field)) \ - elm = RB_LEFT(elm, field); \ - } else { \ - if (RB_PARENT(elm, field) && (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ - elm = RB_PARENT(elm, field); \ - else { \ - while (RB_PARENT(elm, field) && (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ - elm = RB_PARENT(elm, field); \ - elm = RB_PARENT(elm, field); \ - } \ - } \ - return (elm); \ - } - -#define RB_GENERATE_PREV(name, type, field, attr) \ - /* ARGSUSED */ \ - attr struct type* name##_RB_PREV(struct type* elm) { \ - if (RB_LEFT(elm, field)) { \ - elm = RB_LEFT(elm, field); \ - while (RB_RIGHT(elm, field)) \ - elm = RB_RIGHT(elm, field); \ - } else { \ - if (RB_PARENT(elm, field) && (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ - elm = RB_PARENT(elm, field); \ - else { \ - while (RB_PARENT(elm, field) && (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ - elm = RB_PARENT(elm, field); \ - elm = RB_PARENT(elm, field); \ - } \ - } \ - return (elm); \ - } - -#define RB_GENERATE_MINMAX(name, type, field, attr) \ - attr struct type* name##_RB_MINMAX(struct name* head, int val) { \ - struct type* tmp = RB_ROOT(head); \ - struct type* parent = NULL; \ - while (tmp) { \ - parent = tmp; \ - if (val < 0) \ - tmp = RB_LEFT(tmp, field); \ - else \ - tmp = RB_RIGHT(tmp, field); \ - } \ - return (parent); \ - } - -#define RB_NEGINF -1 -#define RB_INF 1 - -#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) -#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) -#define RB_FIND(name, x, y) name##_RB_FIND(x, y) -#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) -#define RB_FIND_LIGHT(name, x, y) name##_RB_FIND_LIGHT(x, y) -#define RB_NFIND_LIGHT(name, x, y) name##_RB_NFIND_LIGHT(x, y) -#define RB_NEXT(name, x, y) name##_RB_NEXT(y) -#define RB_PREV(name, x, y) name##_RB_PREV(y) -#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) -#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) - -#define RB_FOREACH(x, name, head) \ - for ((x) = RB_MIN(name, head); (x) != NULL; (x) = name##_RB_NEXT(x)) - -#define RB_FOREACH_FROM(x, name, y) \ - for ((x) = (y); ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); (x) = (y)) - -#define RB_FOREACH_SAFE(x, name, head, y) \ - for ((x) = RB_MIN(name, head); ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ - (x) = (y)) - -#define RB_FOREACH_REVERSE(x, name, head) \ - for ((x) = RB_MAX(name, head); (x) != NULL; (x) = name##_RB_PREV(x)) - -#define RB_FOREACH_REVERSE_FROM(x, name, y) \ - for ((x) = (y); ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); (x) = (y)) - -#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ - for ((x) = RB_MAX(name, head); ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ - (x) = (y)) - -#endif /* _SYS_TREE_H_ */ +namespace Common { +template <typename T> +class RBHead { +public: + [[nodiscard]] T* Root() { + return rbh_root; + } + + [[nodiscard]] const T* Root() const { + return rbh_root; + } + + void SetRoot(T* root) { + rbh_root = root; + } + + [[nodiscard]] bool IsEmpty() const { + return Root() == nullptr; + } + +private: + T* rbh_root = nullptr; +}; + +enum class EntryColor { + Black, + Red, +}; + +template <typename T> +class RBEntry { +public: + [[nodiscard]] T* Left() { + return rbe_left; + } + + [[nodiscard]] const T* Left() const { + return rbe_left; + } + + void SetLeft(T* left) { + rbe_left = left; + } + + [[nodiscard]] T* Right() { + return rbe_right; + } + + [[nodiscard]] const T* Right() const { + return rbe_right; + } + + void SetRight(T* right) { + rbe_right = right; + } + + [[nodiscard]] T* Parent() { + return rbe_parent; + } + + [[nodiscard]] const T* Parent() const { + return rbe_parent; + } + + void SetParent(T* parent) { + rbe_parent = parent; + } + + [[nodiscard]] bool IsBlack() const { + return rbe_color == EntryColor::Black; + } + + [[nodiscard]] bool IsRed() const { + return rbe_color == EntryColor::Red; + } + + [[nodiscard]] EntryColor Color() const { + return rbe_color; + } + + void SetColor(EntryColor color) { + rbe_color = color; + } + +private: + T* rbe_left = nullptr; + T* rbe_right = nullptr; + T* rbe_parent = nullptr; + EntryColor rbe_color{}; +}; + +template <typename Node> +[[nodiscard]] RBEntry<Node>& RB_ENTRY(Node* node) { + return node->GetEntry(); +} + +template <typename Node> +[[nodiscard]] const RBEntry<Node>& RB_ENTRY(const Node* node) { + return node->GetEntry(); +} + +template <typename Node> +[[nodiscard]] Node* RB_PARENT(Node* node) { + return RB_ENTRY(node).Parent(); +} + +template <typename Node> +[[nodiscard]] const Node* RB_PARENT(const Node* node) { + return RB_ENTRY(node).Parent(); +} + +template <typename Node> +void RB_SET_PARENT(Node* node, Node* parent) { + return RB_ENTRY(node).SetParent(parent); +} + +template <typename Node> +[[nodiscard]] Node* RB_LEFT(Node* node) { + return RB_ENTRY(node).Left(); +} + +template <typename Node> +[[nodiscard]] const Node* RB_LEFT(const Node* node) { + return RB_ENTRY(node).Left(); +} + +template <typename Node> +void RB_SET_LEFT(Node* node, Node* left) { + return RB_ENTRY(node).SetLeft(left); +} + +template <typename Node> +[[nodiscard]] Node* RB_RIGHT(Node* node) { + return RB_ENTRY(node).Right(); +} + +template <typename Node> +[[nodiscard]] const Node* RB_RIGHT(const Node* node) { + return RB_ENTRY(node).Right(); +} + +template <typename Node> +void RB_SET_RIGHT(Node* node, Node* right) { + return RB_ENTRY(node).SetRight(right); +} + +template <typename Node> +[[nodiscard]] bool RB_IS_BLACK(const Node* node) { + return RB_ENTRY(node).IsBlack(); +} + +template <typename Node> +[[nodiscard]] bool RB_IS_RED(const Node* node) { + return RB_ENTRY(node).IsRed(); +} + +template <typename Node> +[[nodiscard]] EntryColor RB_COLOR(const Node* node) { + return RB_ENTRY(node).Color(); +} + +template <typename Node> +void RB_SET_COLOR(Node* node, EntryColor color) { + return RB_ENTRY(node).SetColor(color); +} + +template <typename Node> +void RB_SET(Node* node, Node* parent) { + auto& entry = RB_ENTRY(node); + entry.SetParent(parent); + entry.SetLeft(nullptr); + entry.SetRight(nullptr); + entry.SetColor(EntryColor::Red); +} + +template <typename Node> +void RB_SET_BLACKRED(Node* black, Node* red) { + RB_SET_COLOR(black, EntryColor::Black); + RB_SET_COLOR(red, EntryColor::Red); +} + +template <typename Node> +void RB_ROTATE_LEFT(RBHead<Node>* head, Node* elm, Node*& tmp) { + tmp = RB_RIGHT(elm); + RB_SET_RIGHT(elm, RB_LEFT(tmp)); + if (RB_RIGHT(elm) != nullptr) { + RB_SET_PARENT(RB_LEFT(tmp), elm); + } + + RB_SET_PARENT(tmp, RB_PARENT(elm)); + if (RB_PARENT(tmp) != nullptr) { + if (elm == RB_LEFT(RB_PARENT(elm))) { + RB_SET_LEFT(RB_PARENT(elm), tmp); + } else { + RB_SET_RIGHT(RB_PARENT(elm), tmp); + } + } else { + head->SetRoot(tmp); + } + + RB_SET_LEFT(tmp, elm); + RB_SET_PARENT(elm, tmp); +} + +template <typename Node> +void RB_ROTATE_RIGHT(RBHead<Node>* head, Node* elm, Node*& tmp) { + tmp = RB_LEFT(elm); + RB_SET_LEFT(elm, RB_RIGHT(tmp)); + if (RB_LEFT(elm) != nullptr) { + RB_SET_PARENT(RB_RIGHT(tmp), elm); + } + + RB_SET_PARENT(tmp, RB_PARENT(elm)); + if (RB_PARENT(tmp) != nullptr) { + if (elm == RB_LEFT(RB_PARENT(elm))) { + RB_SET_LEFT(RB_PARENT(elm), tmp); + } else { + RB_SET_RIGHT(RB_PARENT(elm), tmp); + } + } else { + head->SetRoot(tmp); + } + + RB_SET_RIGHT(tmp, elm); + RB_SET_PARENT(elm, tmp); +} + +template <typename Node> +void RB_INSERT_COLOR(RBHead<Node>* head, Node* elm) { + Node* parent = nullptr; + Node* tmp = nullptr; + + while ((parent = RB_PARENT(elm)) != nullptr && RB_IS_RED(parent)) { + Node* gparent = RB_PARENT(parent); + if (parent == RB_LEFT(gparent)) { + tmp = RB_RIGHT(gparent); + if (tmp && RB_IS_RED(tmp)) { + RB_SET_COLOR(tmp, EntryColor::Black); + RB_SET_BLACKRED(parent, gparent); + elm = gparent; + continue; + } + + if (RB_RIGHT(parent) == elm) { + RB_ROTATE_LEFT(head, parent, tmp); + tmp = parent; + parent = elm; + elm = tmp; + } + + RB_SET_BLACKRED(parent, gparent); + RB_ROTATE_RIGHT(head, gparent, tmp); + } else { + tmp = RB_LEFT(gparent); + if (tmp && RB_IS_RED(tmp)) { + RB_SET_COLOR(tmp, EntryColor::Black); + RB_SET_BLACKRED(parent, gparent); + elm = gparent; + continue; + } + + if (RB_LEFT(parent) == elm) { + RB_ROTATE_RIGHT(head, parent, tmp); + tmp = parent; + parent = elm; + elm = tmp; + } + + RB_SET_BLACKRED(parent, gparent); + RB_ROTATE_LEFT(head, gparent, tmp); + } + } + + RB_SET_COLOR(head->Root(), EntryColor::Black); +} + +template <typename Node> +void RB_REMOVE_COLOR(RBHead<Node>* head, Node* parent, Node* elm) { + Node* tmp; + while ((elm == nullptr || RB_IS_BLACK(elm)) && elm != head->Root()) { + if (RB_LEFT(parent) == elm) { + tmp = RB_RIGHT(parent); + if (RB_IS_RED(tmp)) { + RB_SET_BLACKRED(tmp, parent); + RB_ROTATE_LEFT(head, parent, tmp); + tmp = RB_RIGHT(parent); + } + + if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) && + (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) { + RB_SET_COLOR(tmp, EntryColor::Red); + elm = parent; + parent = RB_PARENT(elm); + } else { + if (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp))) { + Node* oleft; + if ((oleft = RB_LEFT(tmp)) != nullptr) { + RB_SET_COLOR(oleft, EntryColor::Black); + } + + RB_SET_COLOR(tmp, EntryColor::Red); + RB_ROTATE_RIGHT(head, tmp, oleft); + tmp = RB_RIGHT(parent); + } + + RB_SET_COLOR(tmp, RB_COLOR(parent)); + RB_SET_COLOR(parent, EntryColor::Black); + if (RB_RIGHT(tmp)) { + RB_SET_COLOR(RB_RIGHT(tmp), EntryColor::Black); + } + + RB_ROTATE_LEFT(head, parent, tmp); + elm = head->Root(); + break; + } + } else { + tmp = RB_LEFT(parent); + if (RB_IS_RED(tmp)) { + RB_SET_BLACKRED(tmp, parent); + RB_ROTATE_RIGHT(head, parent, tmp); + tmp = RB_LEFT(parent); + } + + if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) && + (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) { + RB_SET_COLOR(tmp, EntryColor::Red); + elm = parent; + parent = RB_PARENT(elm); + } else { + if (RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) { + Node* oright; + if ((oright = RB_RIGHT(tmp)) != nullptr) { + RB_SET_COLOR(oright, EntryColor::Black); + } + + RB_SET_COLOR(tmp, EntryColor::Red); + RB_ROTATE_LEFT(head, tmp, oright); + tmp = RB_LEFT(parent); + } + + RB_SET_COLOR(tmp, RB_COLOR(parent)); + RB_SET_COLOR(parent, EntryColor::Black); + + if (RB_LEFT(tmp)) { + RB_SET_COLOR(RB_LEFT(tmp), EntryColor::Black); + } + + RB_ROTATE_RIGHT(head, parent, tmp); + elm = head->Root(); + break; + } + } + } + + if (elm) { + RB_SET_COLOR(elm, EntryColor::Black); + } +} + +template <typename Node> +Node* RB_REMOVE(RBHead<Node>* head, Node* elm) { + Node* child = nullptr; + Node* parent = nullptr; + Node* old = elm; + EntryColor color{}; + + const auto finalize = [&] { + if (color == EntryColor::Black) { + RB_REMOVE_COLOR(head, parent, child); + } + + return old; + }; + + if (RB_LEFT(elm) == nullptr) { + child = RB_RIGHT(elm); + } else if (RB_RIGHT(elm) == nullptr) { + child = RB_LEFT(elm); + } else { + Node* left; + elm = RB_RIGHT(elm); + while ((left = RB_LEFT(elm)) != nullptr) { + elm = left; + } + + child = RB_RIGHT(elm); + parent = RB_PARENT(elm); + color = RB_COLOR(elm); + + if (child) { + RB_SET_PARENT(child, parent); + } + if (parent) { + if (RB_LEFT(parent) == elm) { + RB_SET_LEFT(parent, child); + } else { + RB_SET_RIGHT(parent, child); + } + } else { + head->SetRoot(child); + } + + if (RB_PARENT(elm) == old) { + parent = elm; + } + + elm->SetEntry(old->GetEntry()); + + if (RB_PARENT(old)) { + if (RB_LEFT(RB_PARENT(old)) == old) { + RB_SET_LEFT(RB_PARENT(old), elm); + } else { + RB_SET_RIGHT(RB_PARENT(old), elm); + } + } else { + head->SetRoot(elm); + } + RB_SET_PARENT(RB_LEFT(old), elm); + if (RB_RIGHT(old)) { + RB_SET_PARENT(RB_RIGHT(old), elm); + } + if (parent) { + left = parent; + } + + return finalize(); + } + + parent = RB_PARENT(elm); + color = RB_COLOR(elm); + + if (child) { + RB_SET_PARENT(child, parent); + } + if (parent) { + if (RB_LEFT(parent) == elm) { + RB_SET_LEFT(parent, child); + } else { + RB_SET_RIGHT(parent, child); + } + } else { + head->SetRoot(child); + } + + return finalize(); +} + +// Inserts a node into the RB tree +template <typename Node, typename CompareFunction> +Node* RB_INSERT(RBHead<Node>* head, Node* elm, CompareFunction cmp) { + Node* parent = nullptr; + Node* tmp = head->Root(); + int comp = 0; + + while (tmp) { + parent = tmp; + comp = cmp(elm, parent); + if (comp < 0) { + tmp = RB_LEFT(tmp); + } else if (comp > 0) { + tmp = RB_RIGHT(tmp); + } else { + return tmp; + } + } + + RB_SET(elm, parent); + + if (parent != nullptr) { + if (comp < 0) { + RB_SET_LEFT(parent, elm); + } else { + RB_SET_RIGHT(parent, elm); + } + } else { + head->SetRoot(elm); + } + + RB_INSERT_COLOR(head, elm); + return nullptr; +} + +// Finds the node with the same key as elm +template <typename Node, typename CompareFunction> +Node* RB_FIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) { + Node* tmp = head->Root(); + + while (tmp) { + const int comp = cmp(elm, tmp); + if (comp < 0) { + tmp = RB_LEFT(tmp); + } else if (comp > 0) { + tmp = RB_RIGHT(tmp); + } else { + return tmp; + } + } + + return nullptr; +} + +// Finds the first node greater than or equal to the search key +template <typename Node, typename CompareFunction> +Node* RB_NFIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) { + Node* tmp = head->Root(); + Node* res = nullptr; + + while (tmp) { + const int comp = cmp(elm, tmp); + if (comp < 0) { + res = tmp; + tmp = RB_LEFT(tmp); + } else if (comp > 0) { + tmp = RB_RIGHT(tmp); + } else { + return tmp; + } + } + + return res; +} + +// Finds the node with the same key as lelm +template <typename Node, typename CompareFunction> +Node* RB_FIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) { + Node* tmp = head->Root(); + + while (tmp) { + const int comp = lcmp(lelm, tmp); + if (comp < 0) { + tmp = RB_LEFT(tmp); + } else if (comp > 0) { + tmp = RB_RIGHT(tmp); + } else { + return tmp; + } + } + + return nullptr; +} + +// Finds the first node greater than or equal to the search key +template <typename Node, typename CompareFunction> +Node* RB_NFIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) { + Node* tmp = head->Root(); + Node* res = nullptr; + + while (tmp) { + const int comp = lcmp(lelm, tmp); + if (comp < 0) { + res = tmp; + tmp = RB_LEFT(tmp); + } else if (comp > 0) { + tmp = RB_RIGHT(tmp); + } else { + return tmp; + } + } + + return res; +} + +template <typename Node> +Node* RB_NEXT(Node* elm) { + if (RB_RIGHT(elm)) { + elm = RB_RIGHT(elm); + while (RB_LEFT(elm)) { + elm = RB_LEFT(elm); + } + } else { + if (RB_PARENT(elm) && (elm == RB_LEFT(RB_PARENT(elm)))) { + elm = RB_PARENT(elm); + } else { + while (RB_PARENT(elm) && (elm == RB_RIGHT(RB_PARENT(elm)))) { + elm = RB_PARENT(elm); + } + elm = RB_PARENT(elm); + } + } + return elm; +} + +template <typename Node> +Node* RB_PREV(Node* elm) { + if (RB_LEFT(elm)) { + elm = RB_LEFT(elm); + while (RB_RIGHT(elm)) { + elm = RB_RIGHT(elm); + } + } else { + if (RB_PARENT(elm) && (elm == RB_RIGHT(RB_PARENT(elm)))) { + elm = RB_PARENT(elm); + } else { + while (RB_PARENT(elm) && (elm == RB_LEFT(RB_PARENT(elm)))) { + elm = RB_PARENT(elm); + } + elm = RB_PARENT(elm); + } + } + return elm; +} + +template <typename Node> +Node* RB_MINMAX(RBHead<Node>* head, bool is_min) { + Node* tmp = head->Root(); + Node* parent = nullptr; + + while (tmp) { + parent = tmp; + if (is_min) { + tmp = RB_LEFT(tmp); + } else { + tmp = RB_RIGHT(tmp); + } + } + + return parent; +} + +template <typename Node> +Node* RB_MIN(RBHead<Node>* head) { + return RB_MINMAX(head, true); +} + +template <typename Node> +Node* RB_MAX(RBHead<Node>* head) { + return RB_MINMAX(head, false); +} +} // namespace Common diff --git a/src/common/uuid.h b/src/common/uuid.h index 4ab9a25f0..2e7a18405 100644 --- a/src/common/uuid.h +++ b/src/common/uuid.h @@ -14,8 +14,8 @@ constexpr u128 INVALID_UUID{{0, 0}}; struct UUID { // UUIDs which are 0 are considered invalid! - u128 uuid = INVALID_UUID; - constexpr UUID() = default; + u128 uuid; + UUID() = default; constexpr explicit UUID(const u128& id) : uuid{id} {} constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {} 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 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <array> #include <chrono> +#include <limits> #include <mutex> #include <thread> #ifdef _MSC_VER #include <intrin.h> + +#pragma intrinsic(__umulh) +#pragma intrinsic(_udiv128) #else #include <x86intrin.h> #endif +#include "common/atomic_ops.h" #include "common/uint128.h" #include "common/x64/native_clock.h" +namespace { + +[[nodiscard]] u64 GetFixedPoint64Factor(u64 numerator, u64 divisor) { +#ifdef __SIZEOF_INT128__ + const auto base = static_cast<unsigned __int128>(numerator) << 64ULL; + return static_cast<u64>(base / divisor); +#elif defined(_M_X64) || defined(_M_ARM64) + std::array<u64, 2> r = {0, numerator}; + u64 remainder; +#if _MSC_VER < 1923 + return udiv128(r[1], r[0], divisor, &remainder); +#else + return _udiv128(r[1], r[0], divisor, &remainder); +#endif +#else + // This one is bit more inaccurate. + return MultiplyAndDivide64(std::numeric_limits<u64>::max(), numerator, divisor); +#endif +} + +[[nodiscard]] u64 MultiplyHigh(u64 a, u64 b) { +#ifdef __SIZEOF_INT128__ + return (static_cast<unsigned __int128>(a) * static_cast<unsigned __int128>(b)) >> 64; +#elif defined(_M_X64) || defined(_M_ARM64) + return __umulh(a, b); // MSVC +#else + // Generic fallback + const u64 a_lo = u32(a); + const u64 a_hi = a >> 32; + const u64 b_lo = u32(b); + const u64 b_hi = b >> 32; + + const u64 a_x_b_hi = a_hi * b_hi; + const u64 a_x_b_mid = a_hi * b_lo; + const u64 b_x_a_mid = b_hi * a_lo; + const u64 a_x_b_lo = a_lo * b_lo; + + const u64 carry_bit = (static_cast<u64>(static_cast<u32>(a_x_b_mid)) + + static_cast<u64>(static_cast<u32>(b_x_a_mid)) + (a_x_b_lo >> 32)) >> + 32; + + const u64 multhi = a_x_b_hi + (a_x_b_mid >> 32) + (b_x_a_mid >> 32) + carry_bit; + + return multhi; +#endif +} + +} // namespace + namespace Common { u64 EstimateRDTSCFrequency() { @@ -48,54 +103,71 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{ rtsc_frequency_} { _mm_mfence(); - last_measure = __rdtsc(); - accumulated_ticks = 0U; + time_point.inner.last_measure = __rdtsc(); + time_point.inner.accumulated_ticks = 0U; + ns_rtsc_factor = GetFixedPoint64Factor(1000000000, rtsc_frequency); + us_rtsc_factor = GetFixedPoint64Factor(1000000, rtsc_frequency); + ms_rtsc_factor = GetFixedPoint64Factor(1000, rtsc_frequency); + clock_rtsc_factor = GetFixedPoint64Factor(emulated_clock_frequency, rtsc_frequency); + cpu_rtsc_factor = GetFixedPoint64Factor(emulated_cpu_frequency, rtsc_frequency); } u64 NativeClock::GetRTSC() { - std::scoped_lock scope{rtsc_serialize}; - _mm_mfence(); - const u64 current_measure = __rdtsc(); - u64 diff = current_measure - last_measure; - diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0) - if (current_measure > last_measure) { - last_measure = current_measure; - } - accumulated_ticks += diff; + TimePoint new_time_point{}; + TimePoint current_time_point{}; + do { + current_time_point.pack = time_point.pack; + _mm_mfence(); + const u64 current_measure = __rdtsc(); + u64 diff = current_measure - current_time_point.inner.last_measure; + diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0) + new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure + ? current_measure + : current_time_point.inner.last_measure; + new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff; + } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, + current_time_point.pack)); /// The clock cannot be more precise than the guest timer, remove the lower bits - return accumulated_ticks & inaccuracy_mask; + return new_time_point.inner.accumulated_ticks & inaccuracy_mask; } void NativeClock::Pause(bool is_paused) { if (!is_paused) { - _mm_mfence(); - last_measure = __rdtsc(); + TimePoint current_time_point{}; + TimePoint new_time_point{}; + do { + current_time_point.pack = time_point.pack; + new_time_point.pack = current_time_point.pack; + _mm_mfence(); + new_time_point.inner.last_measure = __rdtsc(); + } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, + current_time_point.pack)); } } std::chrono::nanoseconds NativeClock::GetTimeNS() { const u64 rtsc_value = GetRTSC(); - return std::chrono::nanoseconds{MultiplyAndDivide64(rtsc_value, 1000000000, rtsc_frequency)}; + return std::chrono::nanoseconds{MultiplyHigh(rtsc_value, ns_rtsc_factor)}; } std::chrono::microseconds NativeClock::GetTimeUS() { const u64 rtsc_value = GetRTSC(); - return std::chrono::microseconds{MultiplyAndDivide64(rtsc_value, 1000000, rtsc_frequency)}; + return std::chrono::microseconds{MultiplyHigh(rtsc_value, us_rtsc_factor)}; } std::chrono::milliseconds NativeClock::GetTimeMS() { const u64 rtsc_value = GetRTSC(); - return std::chrono::milliseconds{MultiplyAndDivide64(rtsc_value, 1000, rtsc_frequency)}; + return std::chrono::milliseconds{MultiplyHigh(rtsc_value, ms_rtsc_factor)}; } u64 NativeClock::GetClockCycles() { const u64 rtsc_value = GetRTSC(); - return MultiplyAndDivide64(rtsc_value, emulated_clock_frequency, rtsc_frequency); + return MultiplyHigh(rtsc_value, clock_rtsc_factor); } u64 NativeClock::GetCPUCycles() { const u64 rtsc_value = GetRTSC(); - return MultiplyAndDivide64(rtsc_value, emulated_cpu_frequency, rtsc_frequency); + return MultiplyHigh(rtsc_value, cpu_rtsc_factor); } } // 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 @@ #include <optional> -#include "common/spin_lock.h" #include "common/wall_clock.h" namespace Common { @@ -32,14 +31,28 @@ public: private: u64 GetRTSC(); + union alignas(16) TimePoint { + TimePoint() : pack{} {} + u128 pack{}; + struct Inner { + u64 last_measure{}; + u64 accumulated_ticks{}; + } inner; + }; + /// value used to reduce the native clocks accuracy as some apss rely on /// undefined behavior where the level of accuracy in the clock shouldn't /// be higher. static constexpr u64 inaccuracy_mask = ~(UINT64_C(0x400) - 1); - SpinLock rtsc_serialize{}; - u64 last_measure{}; - u64 accumulated_ticks{}; + TimePoint time_point; + // factors + u64 clock_rtsc_factor{}; + u64 cpu_rtsc_factor{}; + u64 ns_rtsc_factor{}; + u64 us_rtsc_factor{}; + u64 ms_rtsc_factor{}; + u64 rtsc_frequency; }; } // namespace X64 diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 99310dc50..2f6b22747 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -645,6 +645,7 @@ else() -Werror=implicit-fallthrough -Werror=sign-compare + $<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess> $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter> $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable> diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h index 17f774baa..86c9f5350 100644 --- a/src/core/file_sys/savedata_factory.h +++ b/src/core/file_sys/savedata_factory.h @@ -58,7 +58,7 @@ struct SaveDataAttribute { SaveDataType type; SaveDataRank rank; u16 index; - INSERT_PADDING_BYTES(4); + INSERT_PADDING_BYTES_NOINIT(4); u64 zero_1; u64 zero_2; u64 zero_3; @@ -72,7 +72,7 @@ struct SaveDataExtraData { u64 owner_id; s64 timestamp; SaveDataFlags flags; - INSERT_PADDING_BYTES(4); + INSERT_PADDING_BYTES_NOINIT(4); s64 available_size; s64 journal_size; s64 commit_id; diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp index a287eebe3..a44ce6288 100644 --- a/src/core/file_sys/vfs_real.cpp +++ b/src/core/file_sys/vfs_real.cpp @@ -133,8 +133,11 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_ } cache.erase(old_path); - file->Open(new_path, "r+b"); - cache.insert_or_assign(new_path, std::move(file)); + if (file->Open(new_path, "r+b")) { + cache.insert_or_assign(new_path, std::move(file)); + } else { + LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", new_path); + } } else { UNREACHABLE(); return nullptr; @@ -214,9 +217,12 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_, } auto file = cached.lock(); - file->Open(file_new_path, "r+b"); cache.erase(file_old_path); - cache.insert_or_assign(std::move(file_new_path), std::move(file)); + if (file->Open(file_new_path, "r+b")) { + cache.insert_or_assign(std::move(file_new_path), std::move(file)); + } else { + LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", file_new_path); + } } return OpenDirectory(new_path, Mode::ReadWrite); diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index 8c1193894..ee7a58b1c 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp @@ -21,21 +21,18 @@ public: std::mutex mutex; - bool touch_pressed = false; ///< True if touchpad area is currently pressed, otherwise false - - float touch_x = 0.0f; ///< Touchpad X-position - float touch_y = 0.0f; ///< Touchpad Y-position + Input::TouchStatus status; private: class Device : public Input::TouchDevice { public: explicit Device(std::weak_ptr<TouchState>&& touch_state) : touch_state(touch_state) {} - std::tuple<float, float, bool> GetStatus() const override { + Input::TouchStatus GetStatus() const override { if (auto state = touch_state.lock()) { std::lock_guard guard{state->mutex}; - return std::make_tuple(state->touch_x, state->touch_y, state->touch_pressed); + return state->status; } - return std::make_tuple(0.0f, 0.0f, false); + return {}; } private: @@ -79,36 +76,44 @@ std::tuple<unsigned, unsigned> EmuWindow::ClipToTouchScreen(unsigned new_x, unsi return std::make_tuple(new_x, new_y); } -void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) { - if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) +void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id) { + if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) { return; + } + if (id >= touch_state->status.size()) { + return; + } std::lock_guard guard{touch_state->mutex}; - touch_state->touch_x = + const float x = static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) / static_cast<float>(framebuffer_layout.screen.right - framebuffer_layout.screen.left); - touch_state->touch_y = + const float y = static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) / static_cast<float>(framebuffer_layout.screen.bottom - framebuffer_layout.screen.top); - touch_state->touch_pressed = true; + touch_state->status[id] = std::make_tuple(x, y, true); } -void EmuWindow::TouchReleased() { +void EmuWindow::TouchReleased(std::size_t id) { + if (id >= touch_state->status.size()) { + return; + } std::lock_guard guard{touch_state->mutex}; - touch_state->touch_pressed = false; - touch_state->touch_x = 0; - touch_state->touch_y = 0; + touch_state->status[id] = std::make_tuple(0.0f, 0.0f, false); } -void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) { - if (!touch_state->touch_pressed) +void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id) { + if (id >= touch_state->status.size()) { + return; + } + if (!std::get<2>(touch_state->status[id])) return; if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y); - TouchPressed(framebuffer_x, framebuffer_y); + TouchPressed(framebuffer_x, framebuffer_y, id); } void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) { diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 276d2b906..2436c6580 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -117,18 +117,23 @@ public: * Signal that a touch pressed event has occurred (e.g. mouse click pressed) * @param framebuffer_x Framebuffer x-coordinate that was pressed * @param framebuffer_y Framebuffer y-coordinate that was pressed + * @param id Touch event ID */ - void TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y); + void TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id); - /// Signal that a touch released event has occurred (e.g. mouse click released) - void TouchReleased(); + /** + * Signal that a touch released event has occurred (e.g. mouse click released) + * @param id Touch event ID + */ + void TouchReleased(std::size_t id); /** * Signal that a touch movement event has occurred (e.g. mouse was moved over the emu window) * @param framebuffer_x Framebuffer x-coordinate * @param framebuffer_y Framebuffer y-coordinate + * @param id Touch event ID */ - void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y); + void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id); /** * Returns currently active configuration. diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h index de51a754e..f014dfea3 100644 --- a/src/core/frontend/input.h +++ b/src/core/frontend/input.h @@ -163,10 +163,11 @@ using MotionStatus = std::tuple<Common::Vec3<float>, Common::Vec3<float>, Common using MotionDevice = InputDevice<MotionStatus>; /** - * A touch status is an object that returns a tuple of two floats and a bool. The floats are - * x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is pressed. + * A touch status is an object that returns an array of 16 tuple elements of two floats and a bool. + * The floats are x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is + * pressed. */ -using TouchStatus = std::tuple<float, float, bool>; +using TouchStatus = std::array<std::tuple<float, float, bool>, 16>; /** * A touch device is an input device that returns a touch status object 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() { button_states[current_index] = button_state; } +bool InputInterpreter::IsButtonPressed(HIDButton button) const { + return (button_states[current_index] & (1U << static_cast<u8>(button))) != 0; +} + bool InputInterpreter::IsButtonPressedOnce(HIDButton button) const { const bool current_press = (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..73fc47ffb 100644 --- a/src/core/frontend/input_interpreter.h +++ b/src/core/frontend/input_interpreter.h @@ -67,6 +67,27 @@ public: void PollInput(); /** + * Checks whether the button is pressed. + * + * @param button The button to check. + * + * @returns True when the button is pressed. + */ + [[nodiscard]] bool IsButtonPressed(HIDButton button) const; + + /** + * Checks whether any of the buttons in the parameter list is pressed. + * + * @tparam HIDButton The buttons to check. + * + * @returns True when at least one of the buttons is pressed. + */ + template <HIDButton... T> + [[nodiscard]] bool IsAnyButtonPressed() { + return (IsButtonPressed(T) || ...); + } + + /** * The specified button is considered to be pressed once * if it is currently pressed and not pressed previously. * @@ -79,12 +100,12 @@ public: /** * Checks whether any of the buttons in the parameter list is pressed once. * - * @tparam HIDButton The buttons to check. + * @tparam T The buttons to check. * * @returns True when at least one of the buttons is pressed once. */ template <HIDButton... T> - [[nodiscard]] bool IsAnyButtonPressedOnce() { + [[nodiscard]] bool IsAnyButtonPressedOnce() const { return (IsButtonPressedOnce(T) || ...); } @@ -100,12 +121,12 @@ public: /** * Checks whether any of the buttons in the parameter list is held down. * - * @tparam HIDButton The buttons to check. + * @tparam T The buttons to check. * * @returns True when at least one of the buttons is held down. */ template <HIDButton... T> - [[nodiscard]] bool IsAnyButtonHeld() { + [[nodiscard]] bool IsAnyButtonHeld() const { return (IsButtonHeld(T) || ...); } diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h index 79bcf5762..55b1716e4 100644 --- a/src/core/hle/ipc.h +++ b/src/core/hle/ipc.h @@ -146,7 +146,7 @@ static_assert(sizeof(BufferDescriptorC) == 8, "BufferDescriptorC size is incorre struct DataPayloadHeader { u32_le magic; - INSERT_PADDING_WORDS(1); + INSERT_PADDING_WORDS_NOINIT(1); }; static_assert(sizeof(DataPayloadHeader) == 8, "DataPayloadHeader size is incorrect"); @@ -174,7 +174,7 @@ struct DomainMessageHeader { INSERT_PADDING_WORDS_NOINIT(2); }; - std::array<u32, 4> raw{}; + std::array<u32, 4> raw; }; }; static_assert(sizeof(DomainMessageHeader) == 16, "DomainMessageHeader size is incorrect"); diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 6981f8ee7..3ec0e1eca 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -32,9 +32,15 @@ namespace Service::Account { -constexpr ResultCode ERR_INVALID_BUFFER_SIZE{ErrorModule::Account, 30}; +constexpr ResultCode ERR_INVALID_USER_ID{ErrorModule::Account, 20}; +constexpr ResultCode ERR_INVALID_APPLICATION_ID{ErrorModule::Account, 22}; +constexpr ResultCode ERR_INVALID_BUFFER{ErrorModule::Account, 30}; +constexpr ResultCode ERR_INVALID_BUFFER_SIZE{ErrorModule::Account, 31}; constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100}; +// Thumbnails are hard coded to be at least this size +constexpr std::size_t THUMBNAIL_SIZE = 0x24000; + static std::string GetImagePath(Common::UUID uuid) { return Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) + "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; @@ -369,7 +375,7 @@ protected: if (user_data.size() < sizeof(ProfileData)) { LOG_ERROR(Service_ACC, "ProfileData buffer too small!"); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_INVALID_BUFFER_SIZE); + rb.Push(ERR_INVALID_BUFFER); return; } @@ -402,7 +408,7 @@ protected: if (user_data.size() < sizeof(ProfileData)) { LOG_ERROR(Service_ACC, "ProfileData buffer too small!"); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_INVALID_BUFFER_SIZE); + rb.Push(ERR_INVALID_BUFFER); return; } @@ -534,7 +540,7 @@ private: rb.Push(RESULT_SUCCESS); } - Common::UUID user_id; + Common::UUID user_id{Common::INVALID_UUID}; }; // 6.0.0+ @@ -811,6 +817,55 @@ void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ct rb.Push(RESULT_SUCCESS); } +void Module::Interface::StoreSaveDataThumbnailApplication(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto uuid = rp.PopRaw<Common::UUID>(); + + LOG_WARNING(Service_ACC, "(STUBBED) called, uuid={}", uuid.Format()); + + // TODO(ogniK): Check if application ID is zero on acc initialize. As we don't have a reliable + // way of confirming things like the TID, we're going to assume a non zero value for the time + // being. + constexpr u64 tid{1}; + StoreSaveDataThumbnail(ctx, uuid, tid); +} + +void Module::Interface::StoreSaveDataThumbnailSystem(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto uuid = rp.PopRaw<Common::UUID>(); + const auto tid = rp.Pop<u64_le>(); + + LOG_WARNING(Service_ACC, "(STUBBED) called, uuid={}, tid={:016X}", uuid.Format(), tid); + StoreSaveDataThumbnail(ctx, uuid, tid); +} + +void Module::Interface::StoreSaveDataThumbnail(Kernel::HLERequestContext& ctx, + const Common::UUID& uuid, const u64 tid) { + IPC::ResponseBuilder rb{ctx, 2}; + + if (tid == 0) { + LOG_ERROR(Service_ACC, "TitleID is not valid!"); + rb.Push(ERR_INVALID_APPLICATION_ID); + return; + } + + if (!uuid) { + LOG_ERROR(Service_ACC, "User ID is not valid!"); + rb.Push(ERR_INVALID_USER_ID); + return; + } + const auto thumbnail_size = ctx.GetReadBufferSize(); + if (thumbnail_size != THUMBNAIL_SIZE) { + LOG_ERROR(Service_ACC, "Buffer size is empty! size={:X} expecting {:X}", thumbnail_size, + THUMBNAIL_SIZE); + rb.Push(ERR_INVALID_BUFFER_SIZE); + return; + } + + // TODO(ogniK): Construct save data thumbnail + rb.Push(RESULT_SUCCESS); +} + void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called"); // A u8 is passed into this function which we can safely ignore. It's to determine if we have diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h index ab8edc049..0e3ad8ec6 100644 --- a/src/core/hle/service/acc/acc.h +++ b/src/core/hle/service/acc/acc.h @@ -4,6 +4,7 @@ #pragma once +#include "common/uuid.h" #include "core/hle/service/glue/manager.h" #include "core/hle/service/service.h" @@ -36,9 +37,13 @@ public: void ListQualifiedUsers(Kernel::HLERequestContext& ctx); void LoadOpenContext(Kernel::HLERequestContext& ctx); void ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx); + void StoreSaveDataThumbnailApplication(Kernel::HLERequestContext& ctx); + void StoreSaveDataThumbnailSystem(Kernel::HLERequestContext& ctx); private: ResultCode InitializeApplicationInfoBase(); + void StoreSaveDataThumbnail(Kernel::HLERequestContext& ctx, const Common::UUID& uuid, + const u64 tid); enum class ApplicationType : u32_le { GameCard = 0, diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp index d2bb8c2c8..49b22583e 100644 --- a/src/core/hle/service/acc/acc_su.cpp +++ b/src/core/hle/service/acc/acc_su.cpp @@ -29,7 +29,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p {104, nullptr, "GetProfileUpdateNotifier"}, {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+ {106, nullptr, "GetProfileSyncNotifier"}, // 9.0.0+ - {110, nullptr, "StoreSaveDataThumbnail"}, + {110, &ACC_SU::StoreSaveDataThumbnailSystem, "StoreSaveDataThumbnail"}, {111, nullptr, "ClearSaveDataThumbnail"}, {112, nullptr, "LoadSaveDataThumbnail"}, {113, nullptr, "GetSaveDataThumbnailExistence"}, // 5.0.0+ diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp index 75a24f8f5..8d66d180d 100644 --- a/src/core/hle/service/acc/acc_u0.cpp +++ b/src/core/hle/service/acc/acc_u0.cpp @@ -26,7 +26,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"}, {102, nullptr, "AuthenticateApplicationAsync"}, {103, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+ - {110, nullptr, "StoreSaveDataThumbnail"}, + {110, &ACC_U0::StoreSaveDataThumbnailApplication, "StoreSaveDataThumbnail"}, {111, nullptr, "ClearSaveDataThumbnail"}, {120, nullptr, "CreateGuestLoginRequest"}, {130, &ACC_U0::LoadOpenContext, "LoadOpenContext"}, // 5.0.0+ diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp index a4aa5316a..951081cd0 100644 --- a/src/core/hle/service/acc/acc_u1.cpp +++ b/src/core/hle/service/acc/acc_u1.cpp @@ -29,7 +29,7 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p {104, nullptr, "GetProfileUpdateNotifier"}, {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+ {106, nullptr, "GetProfileSyncNotifier"}, // 9.0.0+ - {110, nullptr, "StoreSaveDataThumbnail"}, + {110, &ACC_U1::StoreSaveDataThumbnailApplication, "StoreSaveDataThumbnail"}, {111, nullptr, "ClearSaveDataThumbnail"}, {112, nullptr, "LoadSaveDataThumbnail"}, {113, nullptr, "GetSaveDataThumbnailExistence"}, // 5.0.0+ diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index 9b829e957..d9865d56f 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp @@ -227,17 +227,17 @@ void ProfileManager::CloseUser(UUID uuid) { /// Gets all valid user ids on the system UserIDArray ProfileManager::GetAllUsers() const { - UserIDArray output; - std::transform(profiles.begin(), profiles.end(), output.begin(), - [](const ProfileInfo& p) { return p.user_uuid; }); + UserIDArray output{}; + std::ranges::transform(profiles, output.begin(), + [](const ProfileInfo& p) { return p.user_uuid; }); return output; } /// Get all the open users on the system and zero out the rest of the data. This is specifically /// needed for GetOpenUsers and we need to ensure the rest of the output buffer is zero'd out UserIDArray ProfileManager::GetOpenUsers() const { - UserIDArray output; - std::transform(profiles.begin(), profiles.end(), output.begin(), [](const ProfileInfo& p) { + UserIDArray output{}; + std::ranges::transform(profiles, output.begin(), [](const ProfileInfo& p) { if (p.is_open) return p.user_uuid; return UUID{Common::INVALID_UUID}; diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h index 5310637a6..71b9d5518 100644 --- a/src/core/hle/service/acc/profile_manager.h +++ b/src/core/hle/service/acc/profile_manager.h @@ -23,12 +23,12 @@ using UserIDArray = std::array<Common::UUID, MAX_USERS>; /// Contains extra data related to a user. /// TODO: RE this structure struct ProfileData { - INSERT_PADDING_WORDS(1); - u32 icon_id{}; - u8 bg_color_id{}; - INSERT_PADDING_BYTES(0x7); - INSERT_PADDING_BYTES(0x10); - INSERT_PADDING_BYTES(0x60); + INSERT_PADDING_WORDS_NOINIT(1); + u32 icon_id; + u8 bg_color_id; + INSERT_PADDING_BYTES_NOINIT(0x7); + INSERT_PADDING_BYTES_NOINIT(0x10); + INSERT_PADDING_BYTES_NOINIT(0x60); }; static_assert(sizeof(ProfileData) == 0x80, "ProfileData structure has incorrect size"); @@ -43,9 +43,9 @@ struct ProfileInfo { }; struct ProfileBase { - Common::UUID user_uuid{Common::INVALID_UUID}; - u64_le timestamp{}; - ProfileUsername username{}; + Common::UUID user_uuid; + u64_le timestamp; + ProfileUsername username; // Zero out all the fields to make the profile slot considered "Empty" void Invalidate() { diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index c9808060a..1743bcb2b 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -856,7 +856,7 @@ public: {25, nullptr, "Terminate"}, {30, &ILibraryAppletAccessor::GetResult, "GetResult"}, {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"}, - {60, nullptr, "PresetLibraryAppletGpuTimeSliceZero"}, + {60, &ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero, "PresetLibraryAppletGpuTimeSliceZero"}, {100, &ILibraryAppletAccessor::PushInData, "PushInData"}, {101, &ILibraryAppletAccessor::PopOutData, "PopOutData"}, {102, nullptr, "PushExtraStorage"}, @@ -900,6 +900,13 @@ private: rb.Push(applet->GetStatus()); } + void PresetLibraryAppletGpuTimeSliceZero(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + void Start(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "called"); diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 0cd797109..02ca711fb 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -29,7 +29,7 @@ constexpr int DefaultSampleRate{48000}; struct AudoutParams { s32_le sample_rate; u16_le channel_count; - INSERT_PADDING_BYTES(2); + INSERT_PADDING_BYTES_NOINIT(2); }; static_assert(sizeof(AudoutParams) == 0x8, "AudoutParams is an invalid size"); diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index d280e7caf..ff783b3cc 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -141,7 +141,9 @@ bool Controller_NPad::IsDeviceHandleValid(const DeviceHandle& device_handle) { device_handle.device_index < DeviceIndex::MaxDeviceIndex; } -Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {} +Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) { + latest_vibration_values.fill({DEFAULT_VIBRATION_VALUE, DEFAULT_VIBRATION_VALUE}); +} Controller_NPad::~Controller_NPad() { OnRelease(); @@ -732,7 +734,7 @@ bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size // Send an empty vibration to stop any vibrations. vibrations[npad_index][device_index]->SetRumblePlay(0.0f, 160.0f, 0.0f, 320.0f); // Then reset the vibration value to its default value. - latest_vibration_values[npad_index][device_index] = {}; + latest_vibration_values[npad_index][device_index] = DEFAULT_VIBRATION_VALUE; } return false; @@ -890,7 +892,7 @@ void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::siz return; } - if (controller == NPadControllerType::Handheld) { + if (controller == NPadControllerType::Handheld && npad_index == HANDHELD_INDEX) { Settings::values.players.GetValue()[HANDHELD_INDEX].controller_type = MapNPadToSettingsType(controller); Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true; diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index e2e826623..bc85ca4df 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -97,10 +97,10 @@ public: }; struct DeviceHandle { - NpadType npad_type{}; - u8 npad_id{}; - DeviceIndex device_index{}; - INSERT_PADDING_BYTES(1); + NpadType npad_type; + u8 npad_id; + DeviceIndex device_index; + INSERT_PADDING_BYTES_NOINIT(1); }; static_assert(sizeof(DeviceHandle) == 4, "DeviceHandle is an invalid size"); @@ -120,13 +120,20 @@ public: static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size"); struct VibrationValue { - f32 amp_low{0.0f}; - f32 freq_low{160.0f}; - f32 amp_high{0.0f}; - f32 freq_high{320.0f}; + f32 amp_low; + f32 freq_low; + f32 amp_high; + f32 freq_high; }; static_assert(sizeof(VibrationValue) == 0x10, "Vibration is an invalid size"); + static constexpr VibrationValue DEFAULT_VIBRATION_VALUE{ + .amp_low = 0.0f, + .freq_low = 160.0f, + .amp_high = 0.0f, + .freq_high = 320.0f, + }; + struct LedPattern { explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) { position1.Assign(light1); diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp index 0df395e85..5219f2dad 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.cpp +++ b/src/core/hle/service/hid/controllers/touchscreen.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include <cstring> #include "common/common_types.h" #include "core/core_timing.h" @@ -16,7 +17,13 @@ constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; Controller_Touchscreen::Controller_Touchscreen(Core::System& system) : ControllerBase(system) {} Controller_Touchscreen::~Controller_Touchscreen() = default; -void Controller_Touchscreen::OnInit() {} +void Controller_Touchscreen::OnInit() { + for (std::size_t id = 0; id < MAX_FINGERS; ++id) { + mouse_finger_id[id] = MAX_FINGERS; + keyboard_finger_id[id] = MAX_FINGERS; + udp_finger_id[id] = MAX_FINGERS; + } +} void Controller_Touchscreen::OnRelease() {} @@ -40,38 +47,106 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin cur_entry.sampling_number = last_entry.sampling_number + 1; cur_entry.sampling_number2 = cur_entry.sampling_number; - bool pressed = false; - float x, y; - std::tie(x, y, pressed) = touch_device->GetStatus(); - auto& touch_entry = cur_entry.states[0]; - touch_entry.attribute.raw = 0; - if (!pressed && touch_btn_device) { - std::tie(x, y, pressed) = touch_btn_device->GetStatus(); + const Input::TouchStatus& mouse_status = touch_mouse_device->GetStatus(); + const Input::TouchStatus& udp_status = touch_udp_device->GetStatus(); + for (std::size_t id = 0; id < mouse_status.size(); ++id) { + mouse_finger_id[id] = UpdateTouchInputEvent(mouse_status[id], mouse_finger_id[id]); + udp_finger_id[id] = UpdateTouchInputEvent(udp_status[id], udp_finger_id[id]); } - if (pressed && Settings::values.touchscreen.enabled) { - touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width); - touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height); - touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; - touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; - touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle; - const u64 tick = core_timing.GetCPUTicks(); - touch_entry.delta_time = tick - last_touch; - last_touch = tick; - touch_entry.finger = Settings::values.touchscreen.finger; - cur_entry.entry_count = 1; - } else { - cur_entry.entry_count = 0; + + if (Settings::values.use_touch_from_button) { + const Input::TouchStatus& keyboard_status = touch_btn_device->GetStatus(); + for (std::size_t id = 0; id < mouse_status.size(); ++id) { + keyboard_finger_id[id] = + UpdateTouchInputEvent(keyboard_status[id], keyboard_finger_id[id]); + } } + std::array<Finger, 16> active_fingers; + const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), + [](const auto& finger) { return finger.pressed; }); + const auto active_fingers_count = + static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); + + const u64 tick = core_timing.GetCPUTicks(); + cur_entry.entry_count = static_cast<s32_le>(active_fingers_count); + for (std::size_t id = 0; id < MAX_FINGERS; ++id) { + auto& touch_entry = cur_entry.states[id]; + if (id < active_fingers_count) { + touch_entry.x = static_cast<u16>(active_fingers[id].x * Layout::ScreenUndocked::Width); + touch_entry.y = static_cast<u16>(active_fingers[id].y * Layout::ScreenUndocked::Height); + touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; + touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; + touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle; + touch_entry.delta_time = tick - active_fingers[id].last_touch; + fingers[active_fingers[id].id].last_touch = tick; + touch_entry.finger = active_fingers[id].id; + touch_entry.attribute.raw = active_fingers[id].attribute.raw; + } else { + // Clear touch entry + touch_entry.attribute.raw = 0; + touch_entry.x = 0; + touch_entry.y = 0; + touch_entry.diameter_x = 0; + touch_entry.diameter_y = 0; + touch_entry.rotation_angle = 0; + touch_entry.delta_time = 0; + touch_entry.finger = 0; + } + } std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(TouchScreenSharedMemory)); } void Controller_Touchscreen::OnLoadInputDevices() { - touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touchscreen.device); - if (Settings::values.use_touch_from_button) { - touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button"); - } else { - touch_btn_device.reset(); + touch_mouse_device = Input::CreateDevice<Input::TouchDevice>("engine:emu_window"); + touch_udp_device = Input::CreateDevice<Input::TouchDevice>("engine:cemuhookudp"); + touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button"); +} + +std::optional<std::size_t> Controller_Touchscreen::GetUnusedFingerID() const { + std::size_t first_free_id = 0; + while (first_free_id < MAX_FINGERS) { + if (!fingers[first_free_id].pressed) { + return first_free_id; + } else { + first_free_id++; + } + } + return std::nullopt; +} + +std::size_t Controller_Touchscreen::UpdateTouchInputEvent( + const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) { + const auto& [x, y, pressed] = touch_input; + if (pressed) { + Attributes attribute{}; + if (finger_id == MAX_FINGERS) { + const auto first_free_id = GetUnusedFingerID(); + if (!first_free_id) { + // Invalid finger id do nothing + return MAX_FINGERS; + } + finger_id = first_free_id.value(); + fingers[finger_id].pressed = true; + fingers[finger_id].id = static_cast<u32_le>(finger_id); + attribute.start_touch.Assign(1); + } + fingers[finger_id].x = x; + fingers[finger_id].y = y; + fingers[finger_id].attribute = attribute; + return finger_id; } + + if (finger_id != MAX_FINGERS) { + if (!fingers[finger_id].attribute.end_touch) { + fingers[finger_id].attribute.end_touch.Assign(1); + fingers[finger_id].attribute.start_touch.Assign(0); + return finger_id; + } + fingers[finger_id].pressed = false; + } + + return MAX_FINGERS; } + } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h index 4d9042adc..784124e25 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.h +++ b/src/core/hle/service/hid/controllers/touchscreen.h @@ -30,6 +30,18 @@ public: void OnLoadInputDevices() override; private: + static constexpr std::size_t MAX_FINGERS = 16; + + // Returns an unused finger id, if there is no fingers available std::nullopt will be returned + std::optional<std::size_t> GetUnusedFingerID() const; + + // If the touch is new it tries to assing a new finger id, if there is no fingers avaliable no + // changes will be made. Updates the coordinates if the finger id it's already set. If the touch + // ends delays the output by one frame to set the end_touch flag before finally freeing the + // finger id + std::size_t UpdateTouchInputEvent(const std::tuple<float, float, bool>& touch_input, + std::size_t finger_id); + struct Attributes { union { u32 raw{}; @@ -55,7 +67,7 @@ private: s64_le sampling_number; s64_le sampling_number2; s32_le entry_count; - std::array<TouchState, 16> states; + std::array<TouchState, MAX_FINGERS> states; }; static_assert(sizeof(TouchScreenEntry) == 0x298, "TouchScreenEntry is an invalid size"); @@ -66,9 +78,23 @@ private: }; static_assert(sizeof(TouchScreenSharedMemory) == 0x3000, "TouchScreenSharedMemory is an invalid size"); + + struct Finger { + u64_le last_touch{}; + float x{}; + float y{}; + u32_le id{}; + bool pressed{}; + Attributes attribute; + }; + TouchScreenSharedMemory shared_memory{}; - std::unique_ptr<Input::TouchDevice> touch_device; + std::unique_ptr<Input::TouchDevice> touch_mouse_device; + std::unique_ptr<Input::TouchDevice> touch_udp_device; std::unique_ptr<Input::TouchDevice> touch_btn_device; - s64_le last_touch{}; + std::array<std::size_t, MAX_FINGERS> mouse_finger_id; + std::array<std::size_t, MAX_FINGERS> keyboard_finger_id; + std::array<std::size_t, MAX_FINGERS> udp_finger_id; + std::array<Finger, MAX_FINGERS> fingers; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 8d95f74e6..2b13d6fe6 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -401,9 +401,9 @@ void Hid::SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx) { void Hid::ActivateXpad(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - u32 basic_xpad_id{}; - INSERT_PADDING_WORDS(1); - u64 applet_resource_user_id{}; + u32 basic_xpad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; }; const auto parameters{rp.PopRaw<Parameters>()}; @@ -431,9 +431,9 @@ void Hid::GetXpadIDs(Kernel::HLERequestContext& ctx) { void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - Controller_NPad::DeviceHandle sixaxis_handle{}; - INSERT_PADDING_WORDS(1); - u64 applet_resource_user_id{}; + Controller_NPad::DeviceHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; }; const auto parameters{rp.PopRaw<Parameters>()}; @@ -452,9 +452,9 @@ void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) { void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - Controller_NPad::DeviceHandle sixaxis_handle{}; - INSERT_PADDING_WORDS(1); - u64 applet_resource_user_id{}; + Controller_NPad::DeviceHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; }; const auto parameters{rp.PopRaw<Parameters>()}; @@ -473,9 +473,9 @@ void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) { void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - Controller_NPad::DeviceHandle sixaxis_handle{}; - INSERT_PADDING_WORDS(1); - u64 applet_resource_user_id{}; + Controller_NPad::DeviceHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; }; const auto parameters{rp.PopRaw<Parameters>()}; @@ -494,9 +494,9 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) { void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - Controller_NPad::DeviceHandle sixaxis_handle{}; - INSERT_PADDING_WORDS(1); - u64 applet_resource_user_id{}; + Controller_NPad::DeviceHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; }; const auto parameters{rp.PopRaw<Parameters>()}; @@ -515,10 +515,10 @@ void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - bool enable_sixaxis_sensor_fusion{}; - INSERT_PADDING_BYTES(3); - Controller_NPad::DeviceHandle sixaxis_handle{}; - u64 applet_resource_user_id{}; + bool enable_sixaxis_sensor_fusion; + INSERT_PADDING_BYTES_NOINIT(3); + Controller_NPad::DeviceHandle sixaxis_handle; + u64 applet_resource_user_id; }; const auto parameters{rp.PopRaw<Parameters>()}; @@ -556,9 +556,9 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - Controller_NPad::DeviceHandle sixaxis_handle{}; - INSERT_PADDING_WORDS(1); - u64 applet_resource_user_id{}; + Controller_NPad::DeviceHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; }; const auto parameters{rp.PopRaw<Parameters>()}; @@ -577,9 +577,9 @@ void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - Controller_NPad::DeviceHandle sixaxis_handle{}; - INSERT_PADDING_WORDS(1); - u64 applet_resource_user_id{}; + Controller_NPad::DeviceHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; }; const auto parameters{rp.PopRaw<Parameters>()}; @@ -599,9 +599,9 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - Controller_NPad::DeviceHandle sixaxis_handle{}; - INSERT_PADDING_WORDS(1); - u64 applet_resource_user_id{}; + Controller_NPad::DeviceHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; }; const auto parameters{rp.PopRaw<Parameters>()}; @@ -620,9 +620,9 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - u32 unknown{}; - INSERT_PADDING_WORDS(1); - u64 applet_resource_user_id{}; + u32 unknown; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; }; const auto parameters{rp.PopRaw<Parameters>()}; @@ -702,10 +702,10 @@ void Hid::DeactivateNpad(Kernel::HLERequestContext& ctx) { void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - u32 npad_id{}; - INSERT_PADDING_WORDS(1); - u64 applet_resource_user_id{}; - u64 unknown{}; + u32 npad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + u64 unknown; }; const auto parameters{rp.PopRaw<Parameters>()}; @@ -722,9 +722,9 @@ void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - u32 npad_id{}; - INSERT_PADDING_WORDS(1); - u64 applet_resource_user_id{}; + u32 npad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; }; const auto parameters{rp.PopRaw<Parameters>()}; @@ -756,9 +756,9 @@ void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) { // Should have no effect with how our npad sets up the data IPC::RequestParser rp{ctx}; struct Parameters { - u32 unknown{}; - INSERT_PADDING_WORDS(1); - u64 applet_resource_user_id{}; + u32 unknown; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; }; const auto parameters{rp.PopRaw<Parameters>()}; @@ -800,9 +800,9 @@ void Hid::GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - u32 npad_id{}; - INSERT_PADDING_WORDS(1); - u64 applet_resource_user_id{}; + u32 npad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; }; const auto parameters{rp.PopRaw<Parameters>()}; @@ -821,10 +821,10 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) { // TODO: Check the differences between this and SetNpadJoyAssignmentModeSingleByDefault IPC::RequestParser rp{ctx}; struct Parameters { - u32 npad_id{}; - INSERT_PADDING_WORDS(1); - u64 applet_resource_user_id{}; - u64 npad_joy_device_type{}; + u32 npad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + u64 npad_joy_device_type; }; const auto parameters{rp.PopRaw<Parameters>()}; @@ -844,9 +844,9 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) { void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - u32 npad_id{}; - INSERT_PADDING_WORDS(1); - u64 applet_resource_user_id{}; + u32 npad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; }; const auto parameters{rp.PopRaw<Parameters>()}; @@ -952,9 +952,9 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) { void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - u32 npad_id{}; - INSERT_PADDING_WORDS(1); - u64 applet_resource_user_id{}; + u32 npad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; }; const auto parameters{rp.PopRaw<Parameters>()}; @@ -971,10 +971,10 @@ void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - bool unintended_home_button_input_protection{}; - INSERT_PADDING_BYTES(3); - u32 npad_id{}; - u64 applet_resource_user_id{}; + bool unintended_home_button_input_protection; + INSERT_PADDING_BYTES_NOINIT(3); + u32 npad_id; + u64 applet_resource_user_id; }; const auto parameters{rp.PopRaw<Parameters>()}; @@ -1026,10 +1026,10 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - Controller_NPad::DeviceHandle vibration_device_handle{}; - Controller_NPad::VibrationValue vibration_value{}; - INSERT_PADDING_WORDS(1); - u64 applet_resource_user_id{}; + Controller_NPad::DeviceHandle vibration_device_handle; + Controller_NPad::VibrationValue vibration_value; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; }; const auto parameters{rp.PopRaw<Parameters>()}; @@ -1050,9 +1050,9 @@ void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) { void Hid::GetActualVibrationValue(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - Controller_NPad::DeviceHandle vibration_device_handle{}; - INSERT_PADDING_WORDS(1); - u64 applet_resource_user_id{}; + Controller_NPad::DeviceHandle vibration_device_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; }; const auto parameters{rp.PopRaw<Parameters>()}; @@ -1147,9 +1147,9 @@ void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) { void Hid::IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - Controller_NPad::DeviceHandle vibration_device_handle{}; - INSERT_PADDING_WORDS(1); - u64 applet_resource_user_id{}; + Controller_NPad::DeviceHandle vibration_device_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; }; const auto parameters{rp.PopRaw<Parameters>()}; @@ -1180,9 +1180,9 @@ void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - Controller_NPad::DeviceHandle sixaxis_handle{}; - INSERT_PADDING_WORDS(1); - u64 applet_resource_user_id{}; + Controller_NPad::DeviceHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; }; const auto parameters{rp.PopRaw<Parameters>()}; @@ -1200,9 +1200,9 @@ void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { void Hid::StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { - Controller_NPad::DeviceHandle sixaxis_handle{}; - INSERT_PADDING_WORDS(1); - u64 applet_resource_user_id{}; + Controller_NPad::DeviceHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; }; const auto parameters{rp.PopRaw<Parameters>()}; diff --git a/src/core/hle/service/lbl/lbl.cpp b/src/core/hle/service/lbl/lbl.cpp index 6ad3a2877..f4490f3d9 100644 --- a/src/core/hle/service/lbl/lbl.cpp +++ b/src/core/hle/service/lbl/lbl.cpp @@ -20,30 +20,30 @@ public: static const FunctionInfo functions[] = { {0, nullptr, "SaveCurrentSetting"}, {1, nullptr, "LoadCurrentSetting"}, - {2, nullptr, "SetCurrentBrightnessSetting"}, - {3, nullptr, "GetCurrentBrightnessSetting"}, + {2, &LBL::SetCurrentBrightnessSetting, "SetCurrentBrightnessSetting"}, + {3, &LBL::GetCurrentBrightnessSetting, "GetCurrentBrightnessSetting"}, {4, nullptr, "ApplyCurrentBrightnessSettingToBacklight"}, {5, nullptr, "GetBrightnessSettingAppliedToBacklight"}, - {6, nullptr, "SwitchBacklightOn"}, - {7, nullptr, "SwitchBacklightOff"}, - {8, nullptr, "GetBacklightSwitchStatus"}, - {9, nullptr, "EnableDimming"}, - {10, nullptr, "DisableDimming"}, - {11, nullptr, "IsDimmingEnabled"}, - {12, nullptr, "EnableAutoBrightnessControl"}, - {13, nullptr, "DisableAutoBrightnessControl"}, - {14, nullptr, "IsAutoBrightnessControlEnabled"}, - {15, nullptr, "SetAmbientLightSensorValue"}, - {16, nullptr, "GetAmbientLightSensorValue"}, - {17, nullptr, "SetBrightnessReflectionDelayLevel"}, - {18, nullptr, "GetBrightnessReflectionDelayLevel"}, - {19, nullptr, "SetCurrentBrightnessMapping"}, - {20, nullptr, "GetCurrentBrightnessMapping"}, - {21, nullptr, "SetCurrentAmbientLightSensorMapping"}, - {22, nullptr, "GetCurrentAmbientLightSensorMapping"}, - {23, nullptr, "IsAmbientLightSensorAvailable"}, - {24, nullptr, "SetCurrentBrightnessSettingForVrMode"}, - {25, nullptr, "GetCurrentBrightnessSettingForVrMode"}, + {6, &LBL::SwitchBacklightOn, "SwitchBacklightOn"}, + {7, &LBL::SwitchBacklightOff, "SwitchBacklightOff"}, + {8, &LBL::GetBacklightSwitchStatus, "GetBacklightSwitchStatus"}, + {9, &LBL::EnableDimming, "EnableDimming"}, + {10, &LBL::DisableDimming, "DisableDimming"}, + {11, &LBL::IsDimmingEnabled, "IsDimmingEnabled"}, + {12, &LBL::EnableAutoBrightnessControl, "EnableAutoBrightnessControl"}, + {13, &LBL::DisableAutoBrightnessControl, "DisableAutoBrightnessControl"}, + {14, &LBL::IsAutoBrightnessControlEnabled, "IsAutoBrightnessControlEnabled"}, + {15, &LBL::SetAmbientLightSensorValue, "SetAmbientLightSensorValue"}, + {16, &LBL::GetAmbientLightSensorValue, "GetAmbientLightSensorValue"}, + {17, &LBL::SetBrightnessReflectionDelayLevel, "SetBrightnessReflectionDelayLevel"}, + {18, &LBL::GetBrightnessReflectionDelayLevel, "GetBrightnessReflectionDelayLevel"}, + {19, &LBL::SetCurrentBrightnessMapping, "SetCurrentBrightnessMapping"}, + {20, &LBL::GetCurrentBrightnessMapping, "GetCurrentBrightnessMapping"}, + {21, &LBL::SetCurrentAmbientLightSensorMapping, "SetCurrentAmbientLightSensorMapping"}, + {22, &LBL::GetCurrentAmbientLightSensorMapping, "GetCurrentAmbientLightSensorMapping"}, + {23, &LBL::IsAmbientLightSensorAvailable, "IsAmbientLightSensorAvailable"}, + {24, &LBL::SetCurrentBrightnessSettingForVrMode, "SetCurrentBrightnessSettingForVrMode"}, + {25, &LBL::GetCurrentBrightnessSettingForVrMode, "GetCurrentBrightnessSettingForVrMode"}, {26, &LBL::EnableVrMode, "EnableVrMode"}, {27, &LBL::DisableVrMode, "DisableVrMode"}, {28, &LBL::IsVrModeEnabled, "IsVrModeEnabled"}, @@ -55,6 +55,237 @@ public: } private: + enum class BacklightSwitchStatus : u32 { + Off = 0, + On = 1, + }; + + void SetCurrentBrightnessSetting(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto brightness = rp.Pop<float>(); + + if (!std::isfinite(brightness)) { + LOG_ERROR(Service_LBL, "Brightness is infinite!"); + brightness = 0.0f; + } + + LOG_DEBUG(Service_LBL, "called brightness={}", brightness); + + current_brightness = brightness; + update_instantly = true; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetCurrentBrightnessSetting(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto brightness = current_brightness; + if (!std::isfinite(brightness)) { + LOG_ERROR(Service_LBL, "Brightness is infinite!"); + brightness = 0.0f; + } + + LOG_DEBUG(Service_LBL, "called brightness={}", brightness); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(brightness); + } + + void SwitchBacklightOn(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto fade_time = rp.Pop<u64_le>(); + LOG_WARNING(Service_LBL, "(STUBBED) called, fade_time={}", fade_time); + + backlight_enabled = true; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void SwitchBacklightOff(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto fade_time = rp.Pop<u64_le>(); + LOG_WARNING(Service_LBL, "(STUBBED) called, fade_time={}", fade_time); + + backlight_enabled = false; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetBacklightSwitchStatus(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_LBL, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.PushEnum<BacklightSwitchStatus>(backlight_enabled ? BacklightSwitchStatus::On + : BacklightSwitchStatus::Off); + } + + void EnableDimming(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_LBL, "called"); + + dimming = true; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void DisableDimming(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_LBL, "called"); + + dimming = false; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void IsDimmingEnabled(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_LBL, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(dimming); + } + + void EnableAutoBrightnessControl(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_LBL, "called"); + auto_brightness = true; + update_instantly = true; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void DisableAutoBrightnessControl(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_LBL, "called"); + auto_brightness = false; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void IsAutoBrightnessControlEnabled(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_LBL, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(auto_brightness); + } + + void SetAmbientLightSensorValue(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto light_value = rp.Pop<float>(); + + LOG_DEBUG(Service_LBL, "called light_value={}", light_value); + + ambient_light_value = light_value; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetAmbientLightSensorValue(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_LBL, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(ambient_light_value); + } + + void SetBrightnessReflectionDelayLevel(Kernel::HLERequestContext& ctx) { + // This is Intentional, this function does absolutely nothing + LOG_DEBUG(Service_LBL, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetBrightnessReflectionDelayLevel(Kernel::HLERequestContext& ctx) { + // This is intentional, the function is hard coded to return 0.0f on hardware + LOG_DEBUG(Service_LBL, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(0.0f); + } + + void SetCurrentBrightnessMapping(Kernel::HLERequestContext& ctx) { + // This is Intentional, this function does absolutely nothing + LOG_DEBUG(Service_LBL, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetCurrentBrightnessMapping(Kernel::HLERequestContext& ctx) { + // This is Intentional, this function does absolutely nothing + LOG_DEBUG(Service_LBL, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + // This function is suppose to return something but it seems like it doesn't + } + + void SetCurrentAmbientLightSensorMapping(Kernel::HLERequestContext& ctx) { + // This is Intentional, this function does absolutely nothing + LOG_DEBUG(Service_LBL, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetCurrentAmbientLightSensorMapping(Kernel::HLERequestContext& ctx) { + // This is Intentional, this function does absolutely nothing + LOG_DEBUG(Service_LBL, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + // This function is suppose to return something but it seems like it doesn't + } + + void IsAmbientLightSensorAvailable(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_LBL, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + // TODO(ogniK): Only return true if there's no device error + rb.Push(true); + } + + void SetCurrentBrightnessSettingForVrMode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto brightness = rp.Pop<float>(); + + if (!std::isfinite(brightness)) { + LOG_ERROR(Service_LBL, "Brightness is infinite!"); + brightness = 0.0f; + } + + LOG_DEBUG(Service_LBL, "called brightness={}", brightness); + + current_vr_brightness = brightness; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetCurrentBrightnessSettingForVrMode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto brightness = current_vr_brightness; + if (!std::isfinite(brightness)) { + LOG_ERROR(Service_LBL, "Brightness is infinite!"); + brightness = 0.0f; + } + + LOG_DEBUG(Service_LBL, "called brightness={}", brightness); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(brightness); + } + void EnableVrMode(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_LBL, "called"); @@ -82,6 +313,14 @@ private: } bool vr_mode_enabled = false; + float current_brightness = 1.0f; + float backlight_brightness = 1.0f; + float ambient_light_value = 0.0f; + float current_vr_brightness = 1.0f; + bool dimming = true; + bool backlight_enabled = true; + bool update_instantly = false; + bool auto_brightness = false; // TODO(ogniK): Move to system settings }; void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { diff --git a/src/core/hle/service/mii/manager.cpp b/src/core/hle/service/mii/manager.cpp index d73b90015..567a4e345 100644 --- a/src/core/hle/service/mii/manager.cpp +++ b/src/core/hle/service/mii/manager.cpp @@ -100,6 +100,7 @@ MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) { .mole_scale = static_cast<u8>(bf.mole_scale.Value()), .mole_x = static_cast<u8>(bf.mole_x.Value()), .mole_y = static_cast<u8>(bf.mole_y.Value()), + .padding = 0, }; } diff --git a/src/core/hle/service/mii/manager.h b/src/core/hle/service/mii/manager.h index 927451dea..32c27ee65 100644 --- a/src/core/hle/service/mii/manager.h +++ b/src/core/hle/service/mii/manager.h @@ -27,58 +27,58 @@ enum class SourceFlag : u32 { DECLARE_ENUM_FLAG_OPERATORS(SourceFlag); struct MiiInfo { - Common::UUID uuid{Common::INVALID_UUID}; - std::array<char16_t, 11> name{}; - u8 font_region{}; - u8 favorite_color{}; - u8 gender{}; - u8 height{}; - u8 build{}; - u8 type{}; - u8 region_move{}; - u8 faceline_type{}; - u8 faceline_color{}; - u8 faceline_wrinkle{}; - u8 faceline_make{}; - u8 hair_type{}; - u8 hair_color{}; - u8 hair_flip{}; - u8 eye_type{}; - u8 eye_color{}; - u8 eye_scale{}; - u8 eye_aspect{}; - u8 eye_rotate{}; - u8 eye_x{}; - u8 eye_y{}; - u8 eyebrow_type{}; - u8 eyebrow_color{}; - u8 eyebrow_scale{}; - u8 eyebrow_aspect{}; - u8 eyebrow_rotate{}; - u8 eyebrow_x{}; - u8 eyebrow_y{}; - u8 nose_type{}; - u8 nose_scale{}; - u8 nose_y{}; - u8 mouth_type{}; - u8 mouth_color{}; - u8 mouth_scale{}; - u8 mouth_aspect{}; - u8 mouth_y{}; - u8 beard_color{}; - u8 beard_type{}; - u8 mustache_type{}; - u8 mustache_scale{}; - u8 mustache_y{}; - u8 glasses_type{}; - u8 glasses_color{}; - u8 glasses_scale{}; - u8 glasses_y{}; - u8 mole_type{}; - u8 mole_scale{}; - u8 mole_x{}; - u8 mole_y{}; - INSERT_PADDING_BYTES(1); + Common::UUID uuid; + std::array<char16_t, 11> name; + u8 font_region; + u8 favorite_color; + u8 gender; + u8 height; + u8 build; + u8 type; + u8 region_move; + u8 faceline_type; + u8 faceline_color; + u8 faceline_wrinkle; + u8 faceline_make; + u8 hair_type; + u8 hair_color; + u8 hair_flip; + u8 eye_type; + u8 eye_color; + u8 eye_scale; + u8 eye_aspect; + u8 eye_rotate; + u8 eye_x; + u8 eye_y; + u8 eyebrow_type; + u8 eyebrow_color; + u8 eyebrow_scale; + u8 eyebrow_aspect; + u8 eyebrow_rotate; + u8 eyebrow_x; + u8 eyebrow_y; + u8 nose_type; + u8 nose_scale; + u8 nose_y; + u8 mouth_type; + u8 mouth_color; + u8 mouth_scale; + u8 mouth_aspect; + u8 mouth_y; + u8 beard_color; + u8 beard_type; + u8 mustache_type; + u8 mustache_scale; + u8 mustache_y; + u8 glasses_type; + u8 glasses_color; + u8 glasses_scale; + u8 glasses_y; + u8 mole_type; + u8 mole_scale; + u8 mole_x; + u8 mole_y; + u8 padding; std::u16string Name() const; }; @@ -324,7 +324,7 @@ public: ResultCode GetIndex(const MiiInfo& info, u32& index); private: - const Common::UUID user_id; + const Common::UUID user_id{Common::INVALID_UUID}; u64 update_counter{}; }; diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h index 72e1921ec..b78892223 100644 --- a/src/core/hle/service/time/clock_types.h +++ b/src/core/hle/service/time/clock_types.h @@ -73,19 +73,19 @@ struct TimeSpanType { static_assert(sizeof(TimeSpanType) == 8, "TimeSpanType is incorrect size"); struct ClockSnapshot { - SystemClockContext user_context{}; - SystemClockContext network_context{}; - s64 user_time{}; - s64 network_time{}; - TimeZone::CalendarTime user_calendar_time{}; - TimeZone::CalendarTime network_calendar_time{}; - TimeZone::CalendarAdditionalInfo user_calendar_additional_time{}; - TimeZone::CalendarAdditionalInfo network_calendar_additional_time{}; - SteadyClockTimePoint steady_clock_time_point{}; - TimeZone::LocationName location_name{}; - u8 is_automatic_correction_enabled{}; - u8 type{}; - INSERT_PADDING_BYTES(0x2); + SystemClockContext user_context; + SystemClockContext network_context; + s64 user_time; + s64 network_time; + TimeZone::CalendarTime user_calendar_time; + TimeZone::CalendarTime network_calendar_time; + TimeZone::CalendarAdditionalInfo user_calendar_additional_time; + TimeZone::CalendarAdditionalInfo network_calendar_additional_time; + SteadyClockTimePoint steady_clock_time_point; + TimeZone::LocationName location_name; + u8 is_automatic_correction_enabled; + u8 type; + INSERT_PADDING_BYTES_NOINIT(0x2); static ResultCode GetCurrentTime(s64& current_time, const SteadyClockTimePoint& steady_clock_time_point, diff --git a/src/core/hle/service/time/time_zone_types.h b/src/core/hle/service/time/time_zone_types.h index 9be15b53e..4a57e036d 100644 --- a/src/core/hle/service/time/time_zone_types.h +++ b/src/core/hle/service/time/time_zone_types.h @@ -45,23 +45,23 @@ static_assert(sizeof(TimeZoneRule) == 0x4000, "TimeZoneRule is incorrect size"); /// https://switchbrew.org/wiki/Glue_services#CalendarAdditionalInfo struct CalendarAdditionalInfo { - u32 day_of_week{}; - u32 day_of_year{}; + u32 day_of_week; + u32 day_of_year; std::array<char, 8> timezone_name; - u32 is_dst{}; - s32 gmt_offset{}; + u32 is_dst; + s32 gmt_offset; }; static_assert(sizeof(CalendarAdditionalInfo) == 0x18, "CalendarAdditionalInfo is incorrect size"); /// https://switchbrew.org/wiki/Glue_services#CalendarTime struct CalendarTime { - s16 year{}; - s8 month{}; - s8 day{}; - s8 hour{}; - s8 minute{}; - s8 second{}; - INSERT_PADDING_BYTES(1); + s16 year; + s8 month; + s8 day; + s8 hour; + s8 minute; + s8 second; + INSERT_PADDING_BYTES_NOINIT(1); }; static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime is incorrect size"); diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index d32eb732a..1b5750937 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -81,10 +81,14 @@ public: } bool RumblePlay(u16 amp_low, u16 amp_high) { + constexpr u32 rumble_max_duration_ms = 1000; + if (sdl_controller) { - return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high, 0) == 0; + return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high, + rumble_max_duration_ms) == 0; } else if (sdl_joystick) { - return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high, 0) == 0; + return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high, + rumble_max_duration_ms) == 0; } return false; diff --git a/src/input_common/touch_from_button.cpp b/src/input_common/touch_from_button.cpp index a07124a86..ffbe4f2ed 100644 --- a/src/input_common/touch_from_button.cpp +++ b/src/input_common/touch_from_button.cpp @@ -25,18 +25,19 @@ public: } } - std::tuple<float, float, bool> GetStatus() const override { - for (const auto& m : map) { - const bool state = std::get<0>(m)->GetStatus(); + Input::TouchStatus GetStatus() const override { + Input::TouchStatus touch_status{}; + for (std::size_t id = 0; id < map.size() && id < touch_status.size(); ++id) { + const bool state = std::get<0>(map[id])->GetStatus(); if (state) { - const float x = static_cast<float>(std::get<1>(m)) / + const float x = static_cast<float>(std::get<1>(map[id])) / static_cast<int>(Layout::ScreenUndocked::Width); - const float y = static_cast<float>(std::get<2>(m)) / + const float y = static_cast<float>(std::get<2>(map[id])) / static_cast<int>(Layout::ScreenUndocked::Height); - return {x, y, true}; + touch_status[id] = {x, y, true}; } } - return {}; + return touch_status; } private: diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp index 412d57896..e7e50d789 100644 --- a/src/input_common/udp/client.cpp +++ b/src/input_common/udp/client.cpp @@ -136,6 +136,7 @@ static void SocketLoop(Socket* socket) { Client::Client() { LOG_INFO(Input, "Udp Initialization started"); + finger_id.fill(MAX_TOUCH_FINGERS); ReloadSockets(); } @@ -176,7 +177,7 @@ void Client::ReloadSockets() { std::string server_token; std::size_t client = 0; while (std::getline(servers_ss, server_token, ',')) { - if (client == max_udp_clients) { + if (client == MAX_UDP_CLIENTS) { break; } std::stringstream server_ss(server_token); @@ -194,7 +195,7 @@ void Client::ReloadSockets() { for (std::size_t pad = 0; pad < 4; ++pad) { const std::size_t client_number = GetClientNumber(udp_input_address, udp_input_port, pad); - if (client_number != max_udp_clients) { + if (client_number != MAX_UDP_CLIENTS) { LOG_ERROR(Input, "Duplicated UDP servers found"); continue; } @@ -213,7 +214,7 @@ std::size_t Client::GetClientNumber(std::string_view host, u16 port, std::size_t return client; } } - return max_udp_clients; + return MAX_UDP_CLIENTS; } void Client::OnVersion([[maybe_unused]] Response::Version data) { @@ -259,33 +260,14 @@ void Client::OnPadData(Response::PadData data, std::size_t client) { std::lock_guard guard(clients[client].status.update_mutex); clients[client].status.motion_status = clients[client].motion.GetMotion(); - // TODO: add a setting for "click" touch. Click touch refers to a device that differentiates - // between a simple "tap" and a hard press that causes the touch screen to click. - const bool is_active = data.touch_1.is_active != 0; - - float x = 0; - float y = 0; - - if (is_active && clients[client].status.touch_calibration) { - const u16 min_x = clients[client].status.touch_calibration->min_x; - const u16 max_x = clients[client].status.touch_calibration->max_x; - const u16 min_y = clients[client].status.touch_calibration->min_y; - const u16 max_y = clients[client].status.touch_calibration->max_y; - - x = static_cast<float>(std::clamp(static_cast<u16>(data.touch_1.x), min_x, max_x) - - min_x) / - static_cast<float>(max_x - min_x); - y = static_cast<float>(std::clamp(static_cast<u16>(data.touch_1.y), min_y, max_y) - - min_y) / - static_cast<float>(max_y - min_y); + for (std::size_t id = 0; id < data.touch.size(); ++id) { + UpdateTouchInput(data.touch[id], client, id); } - clients[client].status.touch_status = {x, y, is_active}; - if (configuring) { const Common::Vec3f gyroscope = clients[client].motion.GetGyroscope(); const Common::Vec3f accelerometer = clients[client].motion.GetAcceleration(); - UpdateYuzuSettings(client, accelerometer, gyroscope, is_active); + UpdateYuzuSettings(client, accelerometer, gyroscope); } } } @@ -320,21 +302,17 @@ void Client::Reset() { } void Client::UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc, - const Common::Vec3<float>& gyro, bool touch) { + const Common::Vec3<float>& gyro) { if (gyro.Length() > 0.2f) { - LOG_DEBUG(Input, "UDP Controller {}: gyro=({}, {}, {}), accel=({}, {}, {}), touch={}", - client, gyro[0], gyro[1], gyro[2], acc[0], acc[1], acc[2], touch); + LOG_DEBUG(Input, "UDP Controller {}: gyro=({}, {}, {}), accel=({}, {}, {})", client, + gyro[0], gyro[1], gyro[2], acc[0], acc[1], acc[2]); } UDPPadStatus pad{ .host = clients[client].host, .port = clients[client].port, .pad_index = clients[client].pad_index, }; - if (touch) { - pad.touch = PadTouch::Click; - pad_queue.Push(pad); - } - for (size_t i = 0; i < 3; ++i) { + for (std::size_t i = 0; i < 3; ++i) { if (gyro[i] > 5.0f || gyro[i] < -5.0f) { pad.motion = static_cast<PadMotion>(i); pad.motion_value = gyro[i]; @@ -348,6 +326,50 @@ void Client::UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& a } } +std::optional<std::size_t> Client::GetUnusedFingerID() const { + std::size_t first_free_id = 0; + while (first_free_id < MAX_TOUCH_FINGERS) { + if (!std::get<2>(touch_status[first_free_id])) { + return first_free_id; + } else { + first_free_id++; + } + } + return std::nullopt; +} + +void Client::UpdateTouchInput(Response::TouchPad& touch_pad, std::size_t client, std::size_t id) { + // TODO: Use custom calibration per device + const Common::ParamPackage touch_param(Settings::values.touch_device); + const u16 min_x = static_cast<u16>(touch_param.Get("min_x", 100)); + const u16 min_y = static_cast<u16>(touch_param.Get("min_y", 50)); + const u16 max_x = static_cast<u16>(touch_param.Get("max_x", 1800)); + const u16 max_y = static_cast<u16>(touch_param.Get("max_y", 850)); + const std::size_t touch_id = client * 2 + id; + if (touch_pad.is_active) { + if (finger_id[touch_id] == MAX_TOUCH_FINGERS) { + const auto first_free_id = GetUnusedFingerID(); + if (!first_free_id) { + // Invalid finger id skip to next input + return; + } + finger_id[touch_id] = *first_free_id; + } + auto& [x, y, pressed] = touch_status[finger_id[touch_id]]; + x = static_cast<float>(std::clamp(static_cast<u16>(touch_pad.x), min_x, max_x) - min_x) / + static_cast<float>(max_x - min_x); + y = static_cast<float>(std::clamp(static_cast<u16>(touch_pad.y), min_y, max_y) - min_y) / + static_cast<float>(max_y - min_y); + pressed = true; + return; + } + + if (finger_id[touch_id] != MAX_TOUCH_FINGERS) { + touch_status[finger_id[touch_id]] = {}; + finger_id[touch_id] = MAX_TOUCH_FINGERS; + } +} + void Client::BeginConfiguration() { pad_queue.Clear(); configuring = true; @@ -360,7 +382,7 @@ void Client::EndConfiguration() { DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) { const std::size_t client_number = GetClientNumber(host, port, pad); - if (client_number == max_udp_clients) { + if (client_number == MAX_UDP_CLIENTS) { return clients[0].status; } return clients[client_number].status; @@ -368,12 +390,20 @@ DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t const DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) const { const std::size_t client_number = GetClientNumber(host, port, pad); - if (client_number == max_udp_clients) { + if (client_number == MAX_UDP_CLIENTS) { return clients[0].status; } return clients[client_number].status; } +Input::TouchStatus& Client::GetTouchState() { + return touch_status; +} + +const Input::TouchStatus& Client::GetTouchState() const { + return touch_status; +} + Common::SPSCQueue<UDPPadStatus>& Client::GetPadQueue() { return pad_queue; } @@ -426,24 +456,24 @@ CalibrationConfigurationJob::CalibrationConfigurationJob( current_status = Status::Ready; status_callback(current_status); } - if (data.touch_1.is_active == 0) { + if (data.touch[0].is_active == 0) { return; } - LOG_DEBUG(Input, "Current touch: {} {}", data.touch_1.x, - data.touch_1.y); - min_x = std::min(min_x, static_cast<u16>(data.touch_1.x)); - min_y = std::min(min_y, static_cast<u16>(data.touch_1.y)); + LOG_DEBUG(Input, "Current touch: {} {}", data.touch[0].x, + data.touch[0].y); + min_x = std::min(min_x, static_cast<u16>(data.touch[0].x)); + min_y = std::min(min_y, static_cast<u16>(data.touch[0].y)); if (current_status == Status::Ready) { // First touch - min data (min_x/min_y) current_status = Status::Stage1Completed; status_callback(current_status); } - if (data.touch_1.x - min_x > CALIBRATION_THRESHOLD && - data.touch_1.y - min_y > CALIBRATION_THRESHOLD) { + if (data.touch[0].x - min_x > CALIBRATION_THRESHOLD && + data.touch[0].y - min_y > CALIBRATION_THRESHOLD) { // Set the current position as max value and finishes // configuration - max_x = data.touch_1.x; - max_y = data.touch_1.y; + max_x = data.touch[0].x; + max_y = data.touch[0].y; current_status = Status::Completed; data_callback(min_x, min_y, max_x, max_y); status_callback(current_status); diff --git a/src/input_common/udp/client.h b/src/input_common/udp/client.h index 00c8b09f5..822f9c550 100644 --- a/src/input_common/udp/client.h +++ b/src/input_common/udp/client.h @@ -28,6 +28,7 @@ class Socket; namespace Response { struct PadData; struct PortInfo; +struct TouchPad; struct Version; } // namespace Response @@ -50,7 +51,6 @@ struct UDPPadStatus { std::string host{"127.0.0.1"}; u16 port{26760}; std::size_t pad_index{}; - PadTouch touch{PadTouch::Undefined}; PadMotion motion{PadMotion::Undefined}; f32 motion_value{0.0f}; }; @@ -93,6 +93,9 @@ public: DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad); const DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad) const; + Input::TouchStatus& GetTouchState(); + const Input::TouchStatus& GetTouchState() const; + private: struct ClientData { std::string host{"127.0.0.1"}; @@ -122,14 +125,25 @@ private: void StartCommunication(std::size_t client, const std::string& host, u16 port, std::size_t pad_index, u32 client_id); void UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc, - const Common::Vec3<float>& gyro, bool touch); + const Common::Vec3<float>& gyro); + + // Returns an unused finger id, if there is no fingers available std::nullopt will be + // returned + std::optional<std::size_t> GetUnusedFingerID() const; + + // Merges and updates all touch inputs into the touch_status array + void UpdateTouchInput(Response::TouchPad& touch_pad, std::size_t client, std::size_t id); bool configuring = false; // Allocate clients for 8 udp servers - const std::size_t max_udp_clients = 32; - std::array<ClientData, 4 * 8> clients; - Common::SPSCQueue<UDPPadStatus> pad_queue; + static constexpr std::size_t MAX_UDP_CLIENTS = 4 * 8; + // Each client can have up 2 touch inputs + static constexpr std::size_t MAX_TOUCH_FINGERS = MAX_UDP_CLIENTS * 2; + std::array<ClientData, MAX_UDP_CLIENTS> clients{}; + Common::SPSCQueue<UDPPadStatus> pad_queue{}; + Input::TouchStatus touch_status{}; + std::array<std::size_t, MAX_TOUCH_FINGERS> finger_id{}; }; /// An async job allowing configuration of the touchpad calibration. diff --git a/src/input_common/udp/protocol.h b/src/input_common/udp/protocol.h index fc1aea4b9..a3d276697 100644 --- a/src/input_common/udp/protocol.h +++ b/src/input_common/udp/protocol.h @@ -140,6 +140,14 @@ static_assert(sizeof(PortInfo) == 12, "UDP Response PortInfo struct has wrong si static_assert(std::is_trivially_copyable_v<PortInfo>, "UDP Response PortInfo is not trivially copyable"); +struct TouchPad { + u8 is_active{}; + u8 id{}; + u16_le x{}; + u16_le y{}; +}; +static_assert(sizeof(TouchPad) == 6, "UDP Response TouchPad struct has wrong size "); + #pragma pack(push, 1) struct PadData { PortInfo info{}; @@ -190,12 +198,7 @@ struct PadData { u8 button_13{}; } analog_button; - struct TouchPad { - u8 is_active{}; - u8 id{}; - u16_le x{}; - u16_le y{}; - } touch_1, touch_2; + std::array<TouchPad, 2> touch; u64_le motion_timestamp; @@ -222,7 +225,6 @@ static_assert(sizeof(Message<PadData>) == MAX_PACKET_SIZE, static_assert(sizeof(PadData::AnalogButton) == 12, "UDP Response AnalogButton struct has wrong size "); -static_assert(sizeof(PadData::TouchPad) == 6, "UDP Response TouchPad struct has wrong size "); static_assert(sizeof(PadData::Accelerometer) == 12, "UDP Response Accelerometer struct has wrong size "); static_assert(sizeof(PadData::Gyroscope) == 12, "UDP Response Gyroscope struct has wrong size "); diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp index c5da27a38..b630281a0 100644 --- a/src/input_common/udp/udp.cpp +++ b/src/input_common/udp/udp.cpp @@ -78,8 +78,8 @@ public: explicit UDPTouch(std::string ip_, u16 port_, u16 pad_, CemuhookUDP::Client* client_) : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {} - std::tuple<float, float, bool> GetStatus() const override { - return client->GetPadState(ip, port, pad).touch_status; + Input::TouchStatus GetStatus() const override { + return client->GetTouchState(); } private: @@ -107,32 +107,4 @@ std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamP return std::make_unique<UDPTouch>(std::move(ip), port, pad, client.get()); } -void UDPTouchFactory::BeginConfiguration() { - polling = true; - client->BeginConfiguration(); -} - -void UDPTouchFactory::EndConfiguration() { - polling = false; - client->EndConfiguration(); -} - -Common::ParamPackage UDPTouchFactory::GetNextInput() { - Common::ParamPackage params; - CemuhookUDP::UDPPadStatus pad; - auto& queue = client->GetPadQueue(); - while (queue.Pop(pad)) { - if (pad.touch == CemuhookUDP::PadTouch::Undefined) { - continue; - } - params.Set("engine", "cemuhookudp"); - params.Set("ip", pad.host); - params.Set("port", static_cast<u16>(pad.port)); - params.Set("pad_index", static_cast<u16>(pad.pad_index)); - params.Set("touch", static_cast<u16>(pad.touch)); - return params; - } - return params; -} - } // namespace InputCommon diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 33fa89583..6a5c18945 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -5,6 +5,7 @@ add_executable(tests common/ring_buffer.cpp core/core_timing.cpp tests.cpp + video_core/buffer_base.cpp ) create_target_directory_groups(tests) diff --git a/src/tests/video_core/buffer_base.cpp b/src/tests/video_core/buffer_base.cpp new file mode 100644 index 000000000..651633e9e --- /dev/null +++ b/src/tests/video_core/buffer_base.cpp @@ -0,0 +1,473 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <stdexcept> +#include <unordered_map> + +#include <catch2/catch.hpp> + +#include "common/alignment.h" +#include "common/common_types.h" +#include "video_core/buffer_cache/buffer_base.h" + +namespace { +using VideoCommon::BufferBase; +using Range = std::pair<u64, u64>; + +constexpr u64 PAGE = 4096; +constexpr u64 WORD = 4096 * 64; + +constexpr VAddr c = 0x1328914000; + +class RasterizerInterface { +public: + void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) { + const u64 page_start{addr >> Core::Memory::PAGE_BITS}; + const u64 page_end{(addr + size + Core::Memory::PAGE_SIZE - 1) >> Core::Memory::PAGE_BITS}; + for (u64 page = page_start; page < page_end; ++page) { + int& value = page_table[page]; + value += delta; + if (value < 0) { + throw std::logic_error{"negative page"}; + } + if (value == 0) { + page_table.erase(page); + } + } + } + + [[nodiscard]] int Count(VAddr addr) const noexcept { + const auto it = page_table.find(addr >> Core::Memory::PAGE_BITS); + return it == page_table.end() ? 0 : it->second; + } + + [[nodiscard]] unsigned Count() const noexcept { + unsigned count = 0; + for (const auto [index, value] : page_table) { + count += value; + } + return count; + } + +private: + std::unordered_map<u64, int> page_table; +}; +} // Anonymous namespace + +TEST_CASE("BufferBase: Small buffer", "[video_core]") { + RasterizerInterface rasterizer; + BufferBase buffer(rasterizer, c, WORD); + REQUIRE(rasterizer.Count() == 0); + buffer.UnmarkRegionAsCpuModified(c, WORD); + REQUIRE(rasterizer.Count() == WORD / PAGE); + REQUIRE(buffer.ModifiedCpuRegion(c, WORD) == Range{0, 0}); + + buffer.MarkRegionAsCpuModified(c + PAGE, 1); + REQUIRE(buffer.ModifiedCpuRegion(c, WORD) == Range{PAGE * 1, PAGE * 2}); +} + +TEST_CASE("BufferBase: Large buffer", "[video_core]") { + RasterizerInterface rasterizer; + BufferBase buffer(rasterizer, c, WORD * 32); + buffer.UnmarkRegionAsCpuModified(c, WORD * 32); + buffer.MarkRegionAsCpuModified(c + 4096, WORD * 4); + REQUIRE(buffer.ModifiedCpuRegion(c, WORD + PAGE * 2) == Range{PAGE, WORD + PAGE * 2}); + REQUIRE(buffer.ModifiedCpuRegion(c + PAGE * 2, PAGE * 6) == Range{PAGE * 2, PAGE * 8}); + REQUIRE(buffer.ModifiedCpuRegion(c, WORD * 32) == Range{PAGE, WORD * 4 + PAGE}); + REQUIRE(buffer.ModifiedCpuRegion(c + WORD * 4, PAGE) == Range{WORD * 4, WORD * 4 + PAGE}); + REQUIRE(buffer.ModifiedCpuRegion(c + WORD * 3 + PAGE * 63, PAGE) == + Range{WORD * 3 + PAGE * 63, WORD * 4}); + + buffer.MarkRegionAsCpuModified(c + WORD * 5 + PAGE * 6, PAGE); + buffer.MarkRegionAsCpuModified(c + WORD * 5 + PAGE * 8, PAGE); + REQUIRE(buffer.ModifiedCpuRegion(c + WORD * 5, WORD) == + Range{WORD * 5 + PAGE * 6, WORD * 5 + PAGE * 9}); + + buffer.UnmarkRegionAsCpuModified(c + WORD * 5 + PAGE * 8, PAGE); + REQUIRE(buffer.ModifiedCpuRegion(c + WORD * 5, WORD) == + Range{WORD * 5 + PAGE * 6, WORD * 5 + PAGE * 7}); + + buffer.MarkRegionAsCpuModified(c + PAGE, WORD * 31 + PAGE * 63); + REQUIRE(buffer.ModifiedCpuRegion(c, WORD * 32) == Range{PAGE, WORD * 32}); + + buffer.UnmarkRegionAsCpuModified(c + PAGE * 4, PAGE); + buffer.UnmarkRegionAsCpuModified(c + PAGE * 6, PAGE); + + buffer.UnmarkRegionAsCpuModified(c, WORD * 32); + REQUIRE(buffer.ModifiedCpuRegion(c, WORD * 32) == Range{0, 0}); +} + +TEST_CASE("BufferBase: Rasterizer counting", "[video_core]") { + RasterizerInterface rasterizer; + BufferBase buffer(rasterizer, c, PAGE * 2); + REQUIRE(rasterizer.Count() == 0); + buffer.UnmarkRegionAsCpuModified(c, PAGE); + REQUIRE(rasterizer.Count() == 1); + buffer.MarkRegionAsCpuModified(c, PAGE * 2); + REQUIRE(rasterizer.Count() == 0); + buffer.UnmarkRegionAsCpuModified(c, PAGE); + buffer.UnmarkRegionAsCpuModified(c + PAGE, PAGE); + REQUIRE(rasterizer.Count() == 2); + buffer.MarkRegionAsCpuModified(c, PAGE * 2); + REQUIRE(rasterizer.Count() == 0); +} + +TEST_CASE("BufferBase: Basic range", "[video_core]") { + RasterizerInterface rasterizer; + BufferBase buffer(rasterizer, c, WORD); + buffer.UnmarkRegionAsCpuModified(c, WORD); + buffer.MarkRegionAsCpuModified(c, PAGE); + int num = 0; + buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { + REQUIRE(offset == 0U); + REQUIRE(size == PAGE); + ++num; + }); + REQUIRE(num == 1U); +} + +TEST_CASE("BufferBase: Border upload", "[video_core]") { + RasterizerInterface rasterizer; + BufferBase buffer(rasterizer, c, WORD * 2); + buffer.UnmarkRegionAsCpuModified(c, WORD * 2); + buffer.MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2); + buffer.ForEachUploadRange(c, WORD * 2, [](u64 offset, u64 size) { + REQUIRE(offset == WORD - PAGE); + REQUIRE(size == PAGE * 2); + }); +} + +TEST_CASE("BufferBase: Border upload range", "[video_core]") { + RasterizerInterface rasterizer; + BufferBase buffer(rasterizer, c, WORD * 2); + buffer.UnmarkRegionAsCpuModified(c, WORD * 2); + buffer.MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2); + buffer.ForEachUploadRange(c + WORD - PAGE, PAGE * 2, [](u64 offset, u64 size) { + REQUIRE(offset == WORD - PAGE); + REQUIRE(size == PAGE * 2); + }); + buffer.MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2); + buffer.ForEachUploadRange(c + WORD - PAGE, PAGE, [](u64 offset, u64 size) { + REQUIRE(offset == WORD - PAGE); + REQUIRE(size == PAGE); + }); + buffer.ForEachUploadRange(c + WORD, PAGE, [](u64 offset, u64 size) { + REQUIRE(offset == WORD); + REQUIRE(size == PAGE); + }); +} + +TEST_CASE("BufferBase: Border upload partial range", "[video_core]") { + RasterizerInterface rasterizer; + BufferBase buffer(rasterizer, c, WORD * 2); + buffer.UnmarkRegionAsCpuModified(c, WORD * 2); + buffer.MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2); + buffer.ForEachUploadRange(c + WORD - 1, 2, [](u64 offset, u64 size) { + REQUIRE(offset == WORD - PAGE); + REQUIRE(size == PAGE * 2); + }); + buffer.MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2); + buffer.ForEachUploadRange(c + WORD - 1, 1, [](u64 offset, u64 size) { + REQUIRE(offset == WORD - PAGE); + REQUIRE(size == PAGE); + }); + buffer.ForEachUploadRange(c + WORD + 50, 1, [](u64 offset, u64 size) { + REQUIRE(offset == WORD); + REQUIRE(size == PAGE); + }); +} + +TEST_CASE("BufferBase: Partial word uploads", "[video_core]") { + RasterizerInterface rasterizer; + BufferBase buffer(rasterizer, c, 0x9d000); + int num = 0; + buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { + REQUIRE(offset == 0U); + REQUIRE(size == WORD); + ++num; + }); + REQUIRE(num == 1); + buffer.ForEachUploadRange(c + WORD, WORD, [&](u64 offset, u64 size) { + REQUIRE(offset == WORD); + REQUIRE(size == WORD); + ++num; + }); + REQUIRE(num == 2); + buffer.ForEachUploadRange(c + 0x79000, 0x24000, [&](u64 offset, u64 size) { + REQUIRE(offset == WORD * 2); + REQUIRE(size == PAGE * 0x1d); + ++num; + }); + REQUIRE(num == 3); +} + +TEST_CASE("BufferBase: Partial page upload", "[video_core]") { + RasterizerInterface rasterizer; + BufferBase buffer(rasterizer, c, WORD); + buffer.UnmarkRegionAsCpuModified(c, WORD); + int num = 0; + buffer.MarkRegionAsCpuModified(c + PAGE * 2, PAGE); + buffer.MarkRegionAsCpuModified(c + PAGE * 9, PAGE); + buffer.ForEachUploadRange(c, PAGE * 3, [&](u64 offset, u64 size) { + REQUIRE(offset == PAGE * 2); + REQUIRE(size == PAGE); + ++num; + }); + REQUIRE(num == 1); + buffer.ForEachUploadRange(c + PAGE * 7, PAGE * 3, [&](u64 offset, u64 size) { + REQUIRE(offset == PAGE * 9); + REQUIRE(size == PAGE); + ++num; + }); + REQUIRE(num == 2); +} + +TEST_CASE("BufferBase: Partial page upload with multiple words on the right") { + RasterizerInterface rasterizer; + BufferBase buffer(rasterizer, c, WORD * 8); + buffer.UnmarkRegionAsCpuModified(c, WORD * 8); + buffer.MarkRegionAsCpuModified(c + PAGE * 13, WORD * 7); + int num = 0; + buffer.ForEachUploadRange(c + PAGE * 10, WORD * 7, [&](u64 offset, u64 size) { + REQUIRE(offset == PAGE * 13); + REQUIRE(size == WORD * 7 - PAGE * 3); + ++num; + }); + REQUIRE(num == 1); + buffer.ForEachUploadRange(c + PAGE, WORD * 8, [&](u64 offset, u64 size) { + REQUIRE(offset == WORD * 7 + PAGE * 10); + REQUIRE(size == PAGE * 3); + ++num; + }); + REQUIRE(num == 2); +} + +TEST_CASE("BufferBase: Partial page upload with multiple words on the left", "[video_core]") { + RasterizerInterface rasterizer; + BufferBase buffer(rasterizer, c, WORD * 8); + buffer.UnmarkRegionAsCpuModified(c, WORD * 8); + buffer.MarkRegionAsCpuModified(c + PAGE * 13, WORD * 7); + int num = 0; + buffer.ForEachUploadRange(c + PAGE * 16, WORD * 7, [&](u64 offset, u64 size) { + REQUIRE(offset == PAGE * 16); + REQUIRE(size == WORD * 7 - PAGE * 3); + ++num; + }); + REQUIRE(num == 1); + buffer.ForEachUploadRange(c + PAGE, WORD, [&](u64 offset, u64 size) { + REQUIRE(offset == PAGE * 13); + REQUIRE(size == PAGE * 3); + ++num; + }); + REQUIRE(num == 2); +} + +TEST_CASE("BufferBase: Partial page upload with multiple words in the middle", "[video_core]") { + RasterizerInterface rasterizer; + BufferBase buffer(rasterizer, c, WORD * 8); + buffer.UnmarkRegionAsCpuModified(c, WORD * 8); + buffer.MarkRegionAsCpuModified(c + PAGE * 13, PAGE * 140); + int num = 0; + buffer.ForEachUploadRange(c + PAGE * 16, WORD, [&](u64 offset, u64 size) { + REQUIRE(offset == PAGE * 16); + REQUIRE(size == WORD); + ++num; + }); + REQUIRE(num == 1); + buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { + REQUIRE(offset == PAGE * 13); + REQUIRE(size == PAGE * 3); + ++num; + }); + REQUIRE(num == 2); + buffer.ForEachUploadRange(c, WORD * 8, [&](u64 offset, u64 size) { + REQUIRE(offset == WORD + PAGE * 16); + REQUIRE(size == PAGE * 73); + ++num; + }); + REQUIRE(num == 3); +} + +TEST_CASE("BufferBase: Empty right bits", "[video_core]") { + RasterizerInterface rasterizer; + BufferBase buffer(rasterizer, c, WORD * 2048); + buffer.UnmarkRegionAsCpuModified(c, WORD * 2048); + buffer.MarkRegionAsCpuModified(c + WORD - PAGE, PAGE * 2); + buffer.ForEachUploadRange(c, WORD * 2048, [](u64 offset, u64 size) { + REQUIRE(offset == WORD - PAGE); + REQUIRE(size == PAGE * 2); + }); +} + +TEST_CASE("BufferBase: Out of bound ranges 1", "[video_core]") { + RasterizerInterface rasterizer; + BufferBase buffer(rasterizer, c, WORD); + buffer.UnmarkRegionAsCpuModified(c, WORD); + buffer.MarkRegionAsCpuModified(c, PAGE); + int num = 0; + buffer.ForEachUploadRange(c - WORD, WORD, [&](u64 offset, u64 size) { ++num; }); + buffer.ForEachUploadRange(c + WORD, WORD, [&](u64 offset, u64 size) { ++num; }); + buffer.ForEachUploadRange(c - PAGE, PAGE, [&](u64 offset, u64 size) { ++num; }); + REQUIRE(num == 0); + buffer.ForEachUploadRange(c - PAGE, PAGE * 2, [&](u64 offset, u64 size) { ++num; }); + REQUIRE(num == 1); + buffer.MarkRegionAsCpuModified(c, WORD); + REQUIRE(rasterizer.Count() == 0); +} + +TEST_CASE("BufferBase: Out of bound ranges 2", "[video_core]") { + RasterizerInterface rasterizer; + BufferBase buffer(rasterizer, c, 0x22000); + REQUIRE_NOTHROW(buffer.UnmarkRegionAsCpuModified(c + 0x22000, PAGE)); + REQUIRE_NOTHROW(buffer.UnmarkRegionAsCpuModified(c + 0x28000, PAGE)); + REQUIRE(rasterizer.Count() == 0); + REQUIRE_NOTHROW(buffer.UnmarkRegionAsCpuModified(c + 0x21100, PAGE - 0x100)); + REQUIRE(rasterizer.Count() == 1); + REQUIRE_NOTHROW(buffer.UnmarkRegionAsCpuModified(c - 0x1000, PAGE * 2)); + buffer.UnmarkRegionAsCpuModified(c - 0x3000, PAGE * 2); + buffer.UnmarkRegionAsCpuModified(c - 0x2000, PAGE * 2); + REQUIRE(rasterizer.Count() == 2); +} + +TEST_CASE("BufferBase: Out of bound ranges 3", "[video_core]") { + RasterizerInterface rasterizer; + BufferBase buffer(rasterizer, c, 0x310720); + buffer.UnmarkRegionAsCpuModified(c, 0x310720); + REQUIRE(rasterizer.Count(c) == 1); + REQUIRE(rasterizer.Count(c + PAGE) == 1); + REQUIRE(rasterizer.Count(c + WORD) == 1); + REQUIRE(rasterizer.Count(c + WORD + PAGE) == 1); +} + +TEST_CASE("BufferBase: Sparse regions 1", "[video_core]") { + RasterizerInterface rasterizer; + BufferBase buffer(rasterizer, c, WORD); + buffer.UnmarkRegionAsCpuModified(c, WORD); + buffer.MarkRegionAsCpuModified(c + PAGE * 1, PAGE); + buffer.MarkRegionAsCpuModified(c + PAGE * 3, PAGE * 4); + buffer.ForEachUploadRange(c, WORD, [i = 0](u64 offset, u64 size) mutable { + static constexpr std::array<u64, 2> offsets{PAGE, PAGE * 3}; + static constexpr std::array<u64, 2> sizes{PAGE, PAGE * 4}; + REQUIRE(offset == offsets.at(i)); + REQUIRE(size == sizes.at(i)); + ++i; + }); +} + +TEST_CASE("BufferBase: Sparse regions 2", "[video_core]") { + RasterizerInterface rasterizer; + BufferBase buffer(rasterizer, c, 0x22000); + buffer.UnmarkRegionAsCpuModified(c, 0x22000); + REQUIRE(rasterizer.Count() == 0x22); + buffer.MarkRegionAsCpuModified(c + PAGE * 0x1B, PAGE); + buffer.MarkRegionAsCpuModified(c + PAGE * 0x21, PAGE); + buffer.ForEachUploadRange(c, WORD, [i = 0](u64 offset, u64 size) mutable { + static constexpr std::array<u64, 2> offsets{PAGE * 0x1B, PAGE * 0x21}; + static constexpr std::array<u64, 2> sizes{PAGE, PAGE}; + REQUIRE(offset == offsets.at(i)); + REQUIRE(size == sizes.at(i)); + ++i; + }); +} + +TEST_CASE("BufferBase: Single page modified range", "[video_core]") { + RasterizerInterface rasterizer; + BufferBase buffer(rasterizer, c, PAGE); + REQUIRE(buffer.IsRegionCpuModified(c, PAGE)); + buffer.UnmarkRegionAsCpuModified(c, PAGE); + REQUIRE(!buffer.IsRegionCpuModified(c, PAGE)); +} + +TEST_CASE("BufferBase: Two page modified range", "[video_core]") { + RasterizerInterface rasterizer; + BufferBase buffer(rasterizer, c, PAGE * 2); + REQUIRE(buffer.IsRegionCpuModified(c, PAGE)); + REQUIRE(buffer.IsRegionCpuModified(c + PAGE, PAGE)); + REQUIRE(buffer.IsRegionCpuModified(c, PAGE * 2)); + buffer.UnmarkRegionAsCpuModified(c, PAGE); + REQUIRE(!buffer.IsRegionCpuModified(c, PAGE)); +} + +TEST_CASE("BufferBase: Multi word modified ranges", "[video_core]") { + for (int offset = 0; offset < 4; ++offset) { + const VAddr address = c + WORD * offset; + RasterizerInterface rasterizer; + BufferBase buffer(rasterizer, address, WORD * 4); + REQUIRE(buffer.IsRegionCpuModified(address, PAGE)); + REQUIRE(buffer.IsRegionCpuModified(address + PAGE * 48, PAGE)); + REQUIRE(buffer.IsRegionCpuModified(address + PAGE * 56, PAGE)); + + buffer.UnmarkRegionAsCpuModified(address + PAGE * 32, PAGE); + REQUIRE(buffer.IsRegionCpuModified(address + PAGE, WORD)); + REQUIRE(buffer.IsRegionCpuModified(address + PAGE * 31, PAGE)); + REQUIRE(!buffer.IsRegionCpuModified(address + PAGE * 32, PAGE)); + REQUIRE(buffer.IsRegionCpuModified(address + PAGE * 33, PAGE)); + REQUIRE(buffer.IsRegionCpuModified(address + PAGE * 31, PAGE * 2)); + REQUIRE(buffer.IsRegionCpuModified(address + PAGE * 32, PAGE * 2)); + + buffer.UnmarkRegionAsCpuModified(address + PAGE * 33, PAGE); + REQUIRE(!buffer.IsRegionCpuModified(address + PAGE * 32, PAGE * 2)); + } +} + +TEST_CASE("BufferBase: Single page in large buffer", "[video_core]") { + RasterizerInterface rasterizer; + BufferBase buffer(rasterizer, c, WORD * 16); + buffer.UnmarkRegionAsCpuModified(c, WORD * 16); + REQUIRE(!buffer.IsRegionCpuModified(c, WORD * 16)); + + buffer.MarkRegionAsCpuModified(c + WORD * 12 + PAGE * 8, PAGE); + REQUIRE(buffer.IsRegionCpuModified(c, WORD * 16)); + REQUIRE(buffer.IsRegionCpuModified(c + WORD * 10, WORD * 2)); + REQUIRE(buffer.IsRegionCpuModified(c + WORD * 11, WORD * 2)); + REQUIRE(buffer.IsRegionCpuModified(c + WORD * 12, WORD * 2)); + REQUIRE(buffer.IsRegionCpuModified(c + WORD * 12 + PAGE * 4, PAGE * 8)); + REQUIRE(buffer.IsRegionCpuModified(c + WORD * 12 + PAGE * 6, PAGE * 8)); + REQUIRE(!buffer.IsRegionCpuModified(c + WORD * 12 + PAGE * 6, PAGE)); + REQUIRE(buffer.IsRegionCpuModified(c + WORD * 12 + PAGE * 7, PAGE * 2)); + REQUIRE(buffer.IsRegionCpuModified(c + WORD * 12 + PAGE * 8, PAGE * 2)); +} + +TEST_CASE("BufferBase: Out of bounds region query") { + RasterizerInterface rasterizer; + BufferBase buffer(rasterizer, c, WORD * 16); + REQUIRE(!buffer.IsRegionCpuModified(c - PAGE, PAGE)); + REQUIRE(!buffer.IsRegionCpuModified(c - PAGE * 2, PAGE)); + REQUIRE(!buffer.IsRegionCpuModified(c + WORD * 16, PAGE)); + REQUIRE(buffer.IsRegionCpuModified(c + WORD * 16 - PAGE, WORD * 64)); + REQUIRE(!buffer.IsRegionCpuModified(c + WORD * 16, WORD * 64)); +} + +TEST_CASE("BufferBase: Wrap word regions") { + RasterizerInterface rasterizer; + BufferBase buffer(rasterizer, c, WORD * 2); + buffer.UnmarkRegionAsCpuModified(c, WORD * 2); + buffer.MarkRegionAsCpuModified(c + PAGE * 63, PAGE * 2); + REQUIRE(buffer.IsRegionCpuModified(c, WORD * 2)); + REQUIRE(!buffer.IsRegionCpuModified(c + PAGE * 62, PAGE)); + REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 63, PAGE)); + REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 64, PAGE)); + REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 63, PAGE * 2)); + REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 63, PAGE * 8)); + REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 60, PAGE * 8)); + + REQUIRE(!buffer.IsRegionCpuModified(c + PAGE * 127, WORD * 16)); + buffer.MarkRegionAsCpuModified(c + PAGE * 127, PAGE); + REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 127, WORD * 16)); + REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 127, PAGE)); + REQUIRE(!buffer.IsRegionCpuModified(c + PAGE * 126, PAGE)); + REQUIRE(buffer.IsRegionCpuModified(c + PAGE * 126, PAGE * 2)); + REQUIRE(!buffer.IsRegionCpuModified(c + PAGE * 128, WORD * 16)); +} + +TEST_CASE("BufferBase: Unaligned page region query") { + RasterizerInterface rasterizer; + BufferBase buffer(rasterizer, c, WORD); + buffer.UnmarkRegionAsCpuModified(c, WORD); + buffer.MarkRegionAsCpuModified(c + 4000, 1000); + REQUIRE(buffer.IsRegionCpuModified(c, PAGE)); + REQUIRE(buffer.IsRegionCpuModified(c + PAGE, PAGE)); + REQUIRE(buffer.IsRegionCpuModified(c + 4000, 1000)); + REQUIRE(buffer.IsRegionCpuModified(c + 4000, 1)); +} diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 25a4b1c5b..bb1f8491f 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -1,6 +1,7 @@ add_subdirectory(host_shaders) add_library(video_core STATIC + buffer_cache/buffer_base.h buffer_cache/buffer_block.h buffer_cache/buffer_cache.h buffer_cache/map_interval.cpp @@ -135,8 +136,6 @@ add_library(video_core STATIC renderer_vulkan/vk_graphics_pipeline.h renderer_vulkan/vk_master_semaphore.cpp renderer_vulkan/vk_master_semaphore.h - renderer_vulkan/vk_memory_manager.cpp - renderer_vulkan/vk_memory_manager.h renderer_vulkan/vk_pipeline_cache.cpp renderer_vulkan/vk_pipeline_cache.h renderer_vulkan/vk_query_cache.cpp @@ -259,6 +258,8 @@ add_library(video_core STATIC vulkan_common/vulkan_instance.h vulkan_common/vulkan_library.cpp vulkan_common/vulkan_library.h + vulkan_common/vulkan_memory_allocator.cpp + vulkan_common/vulkan_memory_allocator.h vulkan_common/vulkan_surface.cpp vulkan_common/vulkan_surface.h vulkan_common/vulkan_wrapper.cpp @@ -287,10 +288,10 @@ target_link_libraries(video_core PRIVATE sirit) if (ENABLE_NSIGHT_AFTERMATH) if (NOT DEFINED ENV{NSIGHT_AFTERMATH_SDK}) - message(ERROR "Environment variable NSIGHT_AFTERMATH_SDK has to be provided") + message(FATAL_ERROR "Environment variable NSIGHT_AFTERMATH_SDK has to be provided") endif() if (NOT WIN32) - message(ERROR "Nsight Aftermath doesn't support non-Windows platforms") + message(FATAL_ERROR "Nsight Aftermath doesn't support non-Windows platforms") endif() target_compile_definitions(video_core PRIVATE HAS_NSIGHT_AFTERMATH) target_include_directories(video_core PRIVATE "$ENV{NSIGHT_AFTERMATH_SDK}/include") diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h new file mode 100644 index 000000000..ee8602ce9 --- /dev/null +++ b/src/video_core/buffer_cache/buffer_base.h @@ -0,0 +1,495 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <algorithm> +#include <bit> +#include <limits> +#include <utility> + +#include "common/alignment.h" +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/div_ceil.h" +#include "core/memory.h" + +namespace VideoCommon { + +enum class BufferFlagBits { + Picked = 1 << 0, +}; +DECLARE_ENUM_FLAG_OPERATORS(BufferFlagBits) + +/// Tag for creating null buffers with no storage or size +struct NullBufferParams {}; + +/** + * Range tracking buffer container. + * + * It keeps track of the modified CPU and GPU ranges on a CPU page granularity, notifying the given + * rasterizer about state changes in the tracking behavior of the buffer. + * + * The buffer size and address is forcefully aligned to CPU page boundaries. + */ +template <class RasterizerInterface> +class BufferBase { + static constexpr u64 PAGES_PER_WORD = 64; + static constexpr u64 BYTES_PER_PAGE = Core::Memory::PAGE_SIZE; + static constexpr u64 BYTES_PER_WORD = PAGES_PER_WORD * BYTES_PER_PAGE; + + /// Vector tracking modified pages tightly packed with small vector optimization + union WrittenWords { + /// Returns the pointer to the words state + [[nodiscard]] const u64* Pointer(bool is_short) const noexcept { + return is_short ? &stack : heap; + } + + /// Returns the pointer to the words state + [[nodiscard]] u64* Pointer(bool is_short) noexcept { + return is_short ? &stack : heap; + } + + u64 stack = 0; ///< Small buffers storage + u64* heap; ///< Not-small buffers pointer to the storage + }; + + struct GpuCpuWords { + explicit GpuCpuWords() = default; + explicit GpuCpuWords(u64 size_bytes_) : size_bytes{size_bytes_} { + if (IsShort()) { + cpu.stack = ~u64{0}; + gpu.stack = 0; + } else { + // Share allocation between CPU and GPU pages and set their default values + const size_t num_words = NumWords(); + u64* const alloc = new u64[num_words * 2]; + cpu.heap = alloc; + gpu.heap = alloc + num_words; + std::fill_n(cpu.heap, num_words, ~u64{0}); + std::fill_n(gpu.heap, num_words, 0); + } + // Clean up tailing bits + const u64 last_local_page = + Common::DivCeil(size_bytes % BYTES_PER_WORD, BYTES_PER_PAGE); + const u64 shift = (PAGES_PER_WORD - last_local_page) % PAGES_PER_WORD; + u64& last_word = cpu.Pointer(IsShort())[NumWords() - 1]; + last_word = (last_word << shift) >> shift; + } + + ~GpuCpuWords() { + Release(); + } + + GpuCpuWords& operator=(GpuCpuWords&& rhs) noexcept { + Release(); + size_bytes = rhs.size_bytes; + cpu = rhs.cpu; + gpu = rhs.gpu; + rhs.cpu.heap = nullptr; + return *this; + } + + GpuCpuWords(GpuCpuWords&& rhs) noexcept + : size_bytes{rhs.size_bytes}, cpu{rhs.cpu}, gpu{rhs.gpu} { + rhs.cpu.heap = nullptr; + } + + GpuCpuWords& operator=(const GpuCpuWords&) = delete; + GpuCpuWords(const GpuCpuWords&) = delete; + + /// Returns true when the buffer fits in the small vector optimization + [[nodiscard]] bool IsShort() const noexcept { + return size_bytes <= BYTES_PER_WORD; + } + + /// Returns the number of words of the buffer + [[nodiscard]] size_t NumWords() const noexcept { + return Common::DivCeil(size_bytes, BYTES_PER_WORD); + } + + /// Release buffer resources + void Release() { + if (!IsShort()) { + // CPU written words is the base for the heap allocation + delete[] cpu.heap; + } + } + + u64 size_bytes = 0; + WrittenWords cpu; + WrittenWords gpu; + }; + +public: + explicit BufferBase(RasterizerInterface& rasterizer_, VAddr cpu_addr_, u64 size_bytes) + : rasterizer{&rasterizer_}, cpu_addr{Common::AlignDown(cpu_addr_, BYTES_PER_PAGE)}, + words(Common::AlignUp(size_bytes + (cpu_addr_ - cpu_addr), BYTES_PER_PAGE)) {} + + explicit BufferBase(NullBufferParams) {} + + BufferBase& operator=(const BufferBase&) = delete; + BufferBase(const BufferBase&) = delete; + + /// Returns the inclusive CPU modified range in a begin end pair + [[nodiscard]] std::pair<u64, u64> ModifiedCpuRegion(VAddr query_cpu_addr, + u64 query_size) const noexcept { + const u64 offset = query_cpu_addr - cpu_addr; + return ModifiedRegion<false>(offset, query_size); + } + + /// Returns the inclusive GPU modified range in a begin end pair + [[nodiscard]] std::pair<u64, u64> ModifiedGpuRegion(VAddr query_cpu_addr, + u64 query_size) const noexcept { + const u64 offset = query_cpu_addr - cpu_addr; + return ModifiedRegion<true>(offset, query_size); + } + + /// Returns true if a region has been modified from the CPU + [[nodiscard]] bool IsRegionCpuModified(VAddr query_cpu_addr, u64 query_size) const noexcept { + const u64 offset = query_cpu_addr - cpu_addr; + return IsRegionModified<false>(offset, query_size); + } + + /// Returns true if a region has been modified from the GPU + [[nodiscard]] bool IsRegionGpuModified(VAddr query_cpu_addr, u64 query_size) const noexcept { + const u64 offset = query_cpu_addr - cpu_addr; + return IsRegionModified<true>(offset, query_size); + } + + /// Mark region as CPU modified, notifying the rasterizer about this change + void MarkRegionAsCpuModified(VAddr dirty_cpu_addr, u64 size) { + ChangeRegionState<true, true>(words.cpu, dirty_cpu_addr, size); + } + + /// Unmark region as CPU modified, notifying the rasterizer about this change + void UnmarkRegionAsCpuModified(VAddr dirty_cpu_addr, u64 size) { + ChangeRegionState<false, true>(words.cpu, dirty_cpu_addr, size); + } + + /// Mark region as modified from the host GPU + void MarkRegionAsGpuModified(VAddr dirty_cpu_addr, u64 size) noexcept { + ChangeRegionState<true, false>(words.gpu, dirty_cpu_addr, size); + } + + /// Unmark region as modified from the host GPU + void UnmarkRegionAsGpuModified(VAddr dirty_cpu_addr, u64 size) noexcept { + ChangeRegionState<false, false>(words.gpu, dirty_cpu_addr, size); + } + + /// Call 'func' for each CPU modified range and unmark those pages as CPU modified + template <typename Func> + void ForEachUploadRange(VAddr query_cpu_range, u64 size, Func&& func) { + ForEachModifiedRange<false, true>(query_cpu_range, size, func); + } + + /// Call 'func' for each GPU modified range and unmark those pages as GPU modified + template <typename Func> + void ForEachDownloadRange(VAddr query_cpu_range, u64 size, Func&& func) { + ForEachModifiedRange<true, false>(query_cpu_range, size, func); + } + + /// Call 'func' for each GPU modified range and unmark those pages as GPU modified + template <typename Func> + void ForEachDownloadRange(Func&& func) { + ForEachModifiedRange<true, false>(cpu_addr, SizeBytes(), func); + } + + /// Mark buffer as picked + void Pick() noexcept { + flags |= BufferFlagBits::Picked; + } + + /// Unmark buffer as picked + void Unpick() noexcept { + flags &= ~BufferFlagBits::Picked; + } + + /// Returns true when vaddr -> vaddr+size is fully contained in the buffer + [[nodiscard]] bool IsInBounds(VAddr addr, u64 size) const noexcept { + return addr >= cpu_addr && addr + size <= cpu_addr + SizeBytes(); + } + + /// Returns true if the buffer has been marked as picked + [[nodiscard]] bool IsPicked() const noexcept { + return True(flags & BufferFlagBits::Picked); + } + + /// Returns the base CPU address of the buffer + [[nodiscard]] VAddr CpuAddr() const noexcept { + return cpu_addr; + } + + /// Returns the offset relative to the given CPU address + /// @pre IsInBounds returns true + [[nodiscard]] u32 Offset(VAddr other_cpu_addr) const noexcept { + return static_cast<u32>(other_cpu_addr - cpu_addr); + } + + /// Returns the size in bytes of the buffer + [[nodiscard]] u64 SizeBytes() const noexcept { + return words.size_bytes; + } + +private: + /** + * Change the state of a range of pages + * + * @param written_words Pages to be marked or unmarked as modified + * @param dirty_addr Base address to mark or unmark as modified + * @param size Size in bytes to mark or unmark as modified + * + * @tparam enable True when the bits will be set to one, false for zero + * @tparam notify_rasterizer True when the rasterizer has to be notified about the changes + */ + template <bool enable, bool notify_rasterizer> + void ChangeRegionState(WrittenWords& written_words, u64 dirty_addr, + s64 size) noexcept(!notify_rasterizer) { + const s64 difference = dirty_addr - cpu_addr; + const u64 offset = std::max<s64>(difference, 0); + size += std::min<s64>(difference, 0); + if (offset >= SizeBytes() || size < 0) { + return; + } + u64* const state_words = written_words.Pointer(IsShort()); + const u64 offset_end = std::min(offset + size, SizeBytes()); + const u64 begin_page_index = offset / BYTES_PER_PAGE; + const u64 begin_word_index = begin_page_index / PAGES_PER_WORD; + const u64 end_page_index = Common::DivCeil(offset_end, BYTES_PER_PAGE); + const u64 end_word_index = Common::DivCeil(end_page_index, PAGES_PER_WORD); + u64 page_index = begin_page_index % PAGES_PER_WORD; + u64 word_index = begin_word_index; + while (word_index < end_word_index) { + const u64 next_word_first_page = (word_index + 1) * PAGES_PER_WORD; + const u64 left_offset = + std::min(next_word_first_page - end_page_index, PAGES_PER_WORD) % PAGES_PER_WORD; + const u64 right_offset = page_index; + u64 bits = ~u64{0}; + bits = (bits >> right_offset) << right_offset; + bits = (bits << left_offset) >> left_offset; + if constexpr (notify_rasterizer) { + NotifyRasterizer<!enable>(word_index, state_words[word_index], bits); + } + if constexpr (enable) { + state_words[word_index] |= bits; + } else { + state_words[word_index] &= ~bits; + } + page_index = 0; + ++word_index; + } + } + + /** + * Notify rasterizer about changes in the CPU tracking state of a word in the buffer + * + * @param word_index Index to the word to notify to the rasterizer + * @param current_bits Current state of the word + * @param new_bits New state of the word + * + * @tparam add_to_rasterizer True when the rasterizer should start tracking the new pages + */ + template <bool add_to_rasterizer> + void NotifyRasterizer(u64 word_index, u64 current_bits, u64 new_bits) { + u64 changed_bits = (add_to_rasterizer ? current_bits : ~current_bits) & new_bits; + VAddr addr = cpu_addr + word_index * BYTES_PER_WORD; + while (changed_bits != 0) { + const int empty_bits = std::countr_zero(changed_bits); + addr += empty_bits * BYTES_PER_PAGE; + changed_bits >>= empty_bits; + + const u32 continuous_bits = std::countr_one(changed_bits); + const u64 size = continuous_bits * BYTES_PER_PAGE; + const VAddr begin_addr = addr; + addr += size; + changed_bits = continuous_bits < PAGES_PER_WORD ? (changed_bits >> continuous_bits) : 0; + rasterizer->UpdatePagesCachedCount(begin_addr, size, add_to_rasterizer ? 1 : -1); + } + } + + /** + * Loop over each page in the given range, turn off those bits and notify the rasterizer if + * needed. Call the given function on each turned off range. + * + * @param query_cpu_range Base CPU address to loop over + * @param size Size in bytes of the CPU range to loop over + * @param func Function to call for each turned off region + * + * @tparam gpu True for host GPU pages, false for CPU pages + * @tparam notify_rasterizer True when the rasterizer should be notified about state changes + */ + template <bool gpu, bool notify_rasterizer, typename Func> + void ForEachModifiedRange(VAddr query_cpu_range, s64 size, Func&& func) { + const s64 difference = query_cpu_range - cpu_addr; + const u64 query_begin = std::max<s64>(difference, 0); + size += std::min<s64>(difference, 0); + if (query_begin >= SizeBytes() || size < 0) { + return; + } + const u64* const cpu_words = words.cpu.Pointer(IsShort()); + const u64 query_end = query_begin + std::min(static_cast<u64>(size), SizeBytes()); + u64* const state_words = (gpu ? words.gpu : words.cpu).Pointer(IsShort()); + u64* const words_begin = state_words + query_begin / BYTES_PER_WORD; + u64* const words_end = state_words + Common::DivCeil(query_end, BYTES_PER_WORD); + + const auto modified = [](u64 word) { return word != 0; }; + const auto first_modified_word = std::find_if(words_begin, words_end, modified); + if (first_modified_word == words_end) { + // Exit early when the buffer is not modified + return; + } + const auto last_modified_word = std::find_if_not(first_modified_word, words_end, modified); + + const u64 word_index_begin = std::distance(state_words, first_modified_word); + const u64 word_index_end = std::distance(state_words, last_modified_word); + + const unsigned local_page_begin = std::countr_zero(*first_modified_word); + const unsigned local_page_end = PAGES_PER_WORD - std::countl_zero(last_modified_word[-1]); + const u64 word_page_begin = word_index_begin * PAGES_PER_WORD; + const u64 word_page_end = (word_index_end - 1) * PAGES_PER_WORD; + const u64 query_page_begin = query_begin / BYTES_PER_PAGE; + const u64 query_page_end = Common::DivCeil(query_end, BYTES_PER_PAGE); + const u64 page_index_begin = std::max(word_page_begin + local_page_begin, query_page_begin); + const u64 page_index_end = std::min(word_page_end + local_page_end, query_page_end); + const u64 first_word_page_begin = page_index_begin % PAGES_PER_WORD; + const u64 last_word_page_end = (page_index_end - 1) % PAGES_PER_WORD + 1; + + u64 page_begin = first_word_page_begin; + u64 current_base = 0; + u64 current_size = 0; + bool on_going = false; + for (u64 word_index = word_index_begin; word_index < word_index_end; ++word_index) { + const bool is_last_word = word_index + 1 == word_index_end; + const u64 page_end = is_last_word ? last_word_page_end : PAGES_PER_WORD; + const u64 right_offset = page_begin; + const u64 left_offset = PAGES_PER_WORD - page_end; + u64 bits = ~u64{0}; + bits = (bits >> right_offset) << right_offset; + bits = (bits << left_offset) >> left_offset; + + const u64 current_word = state_words[word_index] & bits; + state_words[word_index] &= ~bits; + + // Exclude CPU modified pages when visiting GPU pages + const u64 word = current_word & ~(gpu ? cpu_words[word_index] : 0); + if constexpr (notify_rasterizer) { + NotifyRasterizer<true>(word_index, word, ~u64{0}); + } + u64 page = page_begin; + page_begin = 0; + + while (page < page_end) { + const int empty_bits = std::countr_zero(word >> page); + if (on_going && empty_bits != 0) { + InvokeModifiedRange(func, current_size, current_base); + current_size = 0; + on_going = false; + } + page += empty_bits; + + const int continuous_bits = std::countr_one(word >> page); + if (!on_going && continuous_bits != 0) { + current_base = word_index * PAGES_PER_WORD + page; + on_going = true; + } + current_size += continuous_bits; + page += continuous_bits; + } + } + if (on_going && current_size > 0) { + InvokeModifiedRange(func, current_size, current_base); + } + } + + template <typename Func> + void InvokeModifiedRange(Func&& func, u64 current_size, u64 current_base) { + const u64 current_size_bytes = current_size * BYTES_PER_PAGE; + const u64 offset_begin = current_base * BYTES_PER_PAGE; + const u64 offset_end = std::min(offset_begin + current_size_bytes, SizeBytes()); + func(offset_begin, offset_end - offset_begin); + } + + /** + * Returns true when a region has been modified + * + * @param offset Offset in bytes from the start of the buffer + * @param size Size in bytes of the region to query for modifications + */ + template <bool gpu> + [[nodiscard]] bool IsRegionModified(u64 offset, u64 size) const noexcept { + const u64* const cpu_words = words.cpu.Pointer(IsShort()); + const u64* const state_words = (gpu ? words.gpu : words.cpu).Pointer(IsShort()); + const u64 num_query_words = size / BYTES_PER_WORD + 1; + const u64 word_begin = offset / BYTES_PER_WORD; + const u64 word_end = std::min(word_begin + num_query_words, NumWords()); + const u64 page_limit = Common::DivCeil(offset + size, BYTES_PER_PAGE); + u64 page_index = (offset / BYTES_PER_PAGE) % PAGES_PER_WORD; + for (u64 word_index = word_begin; word_index < word_end; ++word_index, page_index = 0) { + const u64 word = state_words[word_index] & ~(gpu ? cpu_words[word_index] : 0); + if (word == 0) { + continue; + } + const u64 page_end = std::min((word_index + 1) * PAGES_PER_WORD, page_limit); + const u64 local_page_end = page_end % PAGES_PER_WORD; + const u64 page_end_shift = (PAGES_PER_WORD - local_page_end) % PAGES_PER_WORD; + if (((word >> page_index) << page_index) << page_end_shift != 0) { + return true; + } + } + return false; + } + + /** + * Returns a begin end pair with the inclusive modified region + * + * @param offset Offset in bytes from the start of the buffer + * @param size Size in bytes of the region to query for modifications + * + * @tparam gpu True to query GPU modified pages, false for CPU pages + */ + template <bool gpu> + [[nodiscard]] std::pair<u64, u64> ModifiedRegion(u64 offset, u64 size) const noexcept { + const u64* const cpu_words = words.cpu.Pointer(IsShort()); + const u64* const state_words = (gpu ? words.gpu : words.cpu).Pointer(IsShort()); + const u64 num_query_words = size / BYTES_PER_WORD + 1; + const u64 word_begin = offset / BYTES_PER_WORD; + const u64 word_end = std::min(word_begin + num_query_words, NumWords()); + const u64 page_base = offset / BYTES_PER_PAGE; + const u64 page_limit = Common::DivCeil(offset + size, BYTES_PER_PAGE); + u64 begin = std::numeric_limits<u64>::max(); + u64 end = 0; + for (u64 word_index = word_begin; word_index < word_end; ++word_index) { + const u64 word = state_words[word_index] & ~(gpu ? cpu_words[word_index] : 0); + if (word == 0) { + continue; + } + const u64 local_page_begin = std::countr_zero(word); + const u64 local_page_end = PAGES_PER_WORD - std::countl_zero(word); + const u64 page_index = word_index * PAGES_PER_WORD; + const u64 page_begin = std::max(page_index + local_page_begin, page_base); + const u64 page_end = std::min(page_index + local_page_end, page_limit); + begin = std::min(begin, page_begin); + end = std::max(end, page_end); + } + static constexpr std::pair<u64, u64> EMPTY{0, 0}; + return begin < end ? std::make_pair(begin * BYTES_PER_PAGE, end * BYTES_PER_PAGE) : EMPTY; + } + + /// Returns the number of words of the buffer + [[nodiscard]] size_t NumWords() const noexcept { + return words.NumWords(); + } + + /// Returns true when the buffer fits in the small vector optimization + [[nodiscard]] bool IsShort() const noexcept { + return words.IsShort(); + } + + RasterizerInterface* rasterizer = nullptr; + VAddr cpu_addr = 0; + GpuCpuWords words; + BufferFlagBits flags{}; +}; + +} // namespace VideoCommon diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 4c7399d5a..73f331d4c 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -55,7 +55,7 @@ foreach(FILENAME IN ITEMS ${SHADER_FILES}) OUTPUT ${SPIRV_HEADER_FILE} COMMAND - ${GLSLANGVALIDATOR} -V ${GLSL_FLAGS} --variable-name ${SPIRV_VARIABLE_NAME} -o ${SPIRV_HEADER_FILE} ${SOURCE_FILE} + ${GLSLANGVALIDATOR} -V --quiet ${GLSL_FLAGS} --variable-name ${SPIRV_VARIABLE_NAME} -o ${SPIRV_HEADER_FILE} ${SOURCE_FILE} MAIN_DEPENDENCY ${SOURCE_FILE} ) 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 @@ #include "video_core/renderer_vulkan/renderer_vulkan.h" #include "video_core/renderer_vulkan/vk_blit_screen.h" #include "video_core/renderer_vulkan/vk_master_semaphore.h" -#include "video_core/renderer_vulkan/vk_memory_manager.h" #include "video_core/renderer_vulkan/vk_rasterizer.h" #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_state_tracker.h" @@ -32,6 +31,7 @@ #include "video_core/vulkan_common/vulkan_device.h" #include "video_core/vulkan_common/vulkan_instance.h" #include "video_core/vulkan_common/vulkan_library.h" +#include "video_core/vulkan_common/vulkan_memory_allocator.h" #include "video_core/vulkan_common/vulkan_surface.h" #include "video_core/vulkan_common/vulkan_wrapper.h" @@ -137,7 +137,7 @@ bool RendererVulkan::Init() try { InitializeDevice(); Report(); - memory_manager = std::make_unique<VKMemoryManager>(*device); + memory_allocator = std::make_unique<MemoryAllocator>(*device); state_tracker = std::make_unique<StateTracker>(gpu); @@ -149,11 +149,11 @@ bool RendererVulkan::Init() try { rasterizer = std::make_unique<RasterizerVulkan>(render_window, gpu, gpu.MemoryManager(), cpu_memory, screen_info, *device, - *memory_manager, *state_tracker, *scheduler); + *memory_allocator, *state_tracker, *scheduler); blit_screen = std::make_unique<VKBlitScreen>(cpu_memory, render_window, *rasterizer, *device, - *memory_manager, *swapchain, *scheduler, screen_info); + *memory_allocator, *swapchain, *scheduler, screen_info); return true; } catch (const vk::Exception& exception) { @@ -172,7 +172,7 @@ void RendererVulkan::ShutDown() { blit_screen.reset(); scheduler.reset(); swapchain.reset(); - memory_manager.reset(); + memory_allocator.reset(); device.reset(); } 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 { class Device; class StateTracker; +class MemoryAllocator; class VKBlitScreen; -class VKMemoryManager; class VKSwapchain; class VKScheduler; @@ -75,7 +75,7 @@ private: vk::DebugUtilsMessenger debug_callback; std::unique_ptr<Device> device; - std::unique_ptr<VKMemoryManager> memory_manager; + std::unique_ptr<MemoryAllocator> memory_allocator; std::unique_ptr<StateTracker> state_tracker; std::unique_ptr<VKScheduler> scheduler; 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 @@ #include "video_core/renderer_vulkan/renderer_vulkan.h" #include "video_core/renderer_vulkan/vk_blit_screen.h" #include "video_core/renderer_vulkan/vk_master_semaphore.h" -#include "video_core/renderer_vulkan/vk_memory_manager.h" #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_shader_util.h" #include "video_core/renderer_vulkan/vk_swapchain.h" #include "video_core/surface.h" #include "video_core/textures/decoders.h" #include "video_core/vulkan_common/vulkan_device.h" +#include "video_core/vulkan_common/vulkan_memory_allocator.h" #include "video_core/vulkan_common/vulkan_wrapper.h" namespace Vulkan { @@ -115,10 +115,10 @@ struct VKBlitScreen::BufferData { VKBlitScreen::VKBlitScreen(Core::Memory::Memory& cpu_memory_, Core::Frontend::EmuWindow& render_window_, VideoCore::RasterizerInterface& rasterizer_, const Device& device_, - VKMemoryManager& memory_manager_, VKSwapchain& swapchain_, + MemoryAllocator& memory_allocator_, VKSwapchain& swapchain_, VKScheduler& scheduler_, const VKScreenInfo& screen_info_) : cpu_memory{cpu_memory_}, render_window{render_window_}, rasterizer{rasterizer_}, - device{device_}, memory_manager{memory_manager_}, swapchain{swapchain_}, + device{device_}, memory_allocator{memory_allocator_}, swapchain{swapchain_}, scheduler{scheduler_}, image_count{swapchain.GetImageCount()}, screen_info{screen_info_} { resource_ticks.resize(image_count); @@ -150,8 +150,8 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool SetUniformData(data, framebuffer); SetVertexData(data, framebuffer); - auto map = buffer_commit->Map(); - std::memcpy(map.Address(), &data, sizeof(data)); + const std::span<u8> map = buffer_commit.Map(); + std::memcpy(map.data(), &data, sizeof(data)); if (!use_accelerated) { const u64 image_offset = GetRawImageOffset(framebuffer, image_index); @@ -165,8 +165,8 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool constexpr u32 block_height_log2 = 4; const u32 bytes_per_pixel = GetBytesPerPixel(framebuffer); Tegra::Texture::UnswizzleTexture( - std::span(map.Address() + image_offset, size_bytes), std::span(host_ptr, size_bytes), - bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0); + map.subspan(image_offset, size_bytes), std::span(host_ptr, size_bytes), bytes_per_pixel, + framebuffer.width, framebuffer.height, 1, block_height_log2, 0); const VkBufferImageCopy copy{ .bufferOffset = image_offset, @@ -224,8 +224,6 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, write_barrier); }); } - map.Release(); - scheduler.Record([renderpass = *renderpass, framebuffer = *framebuffers[image_index], descriptor_set = descriptor_sets[image_index], buffer = *buffer, size = swapchain.GetSize(), pipeline = *pipeline, @@ -642,7 +640,7 @@ void VKBlitScreen::ReleaseRawImages() { raw_images.clear(); raw_buffer_commits.clear(); buffer.reset(); - buffer_commit.reset(); + buffer_commit = MemoryCommit{}; } void VKBlitScreen::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer) { @@ -659,7 +657,7 @@ void VKBlitScreen::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuff }; buffer = device.GetLogical().CreateBuffer(ci); - buffer_commit = memory_manager.Commit(buffer, true); + buffer_commit = memory_allocator.Commit(buffer, MemoryUsage::Upload); } void VKBlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) { @@ -690,7 +688,7 @@ void VKBlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) .pQueueFamilyIndices = nullptr, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, }); - raw_buffer_commits[i] = memory_manager.Commit(raw_images[i], false); + raw_buffer_commits[i] = memory_allocator.Commit(raw_images[i], MemoryUsage::DeviceLocal); raw_image_views[i] = device.GetLogical().CreateImageView(VkImageViewCreateInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .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 @@ #include <memory> -#include "video_core/renderer_vulkan/vk_memory_manager.h" +#include "video_core/vulkan_common/vulkan_memory_allocator.h" #include "video_core/vulkan_common/vulkan_wrapper.h" namespace Core { @@ -43,7 +43,7 @@ public: explicit VKBlitScreen(Core::Memory::Memory& cpu_memory, Core::Frontend::EmuWindow& render_window, VideoCore::RasterizerInterface& rasterizer, const Device& device, - VKMemoryManager& memory_manager, VKSwapchain& swapchain, + MemoryAllocator& memory_allocator, VKSwapchain& swapchain, VKScheduler& scheduler, const VKScreenInfo& screen_info); ~VKBlitScreen(); @@ -86,7 +86,7 @@ private: Core::Frontend::EmuWindow& render_window; VideoCore::RasterizerInterface& rasterizer; const Device& device; - VKMemoryManager& memory_manager; + MemoryAllocator& memory_allocator; VKSwapchain& swapchain; VKScheduler& scheduler; const std::size_t image_count; @@ -104,14 +104,14 @@ private: vk::Sampler sampler; vk::Buffer buffer; - VKMemoryCommit buffer_commit; + MemoryCommit buffer_commit; std::vector<u64> resource_ticks; std::vector<vk::Semaphore> semaphores; std::vector<vk::Image> raw_images; std::vector<vk::ImageView> raw_image_views; - std::vector<VKMemoryCommit> raw_buffer_commits; + std::vector<MemoryCommit> raw_buffer_commits; u32 raw_width = 0; u32 raw_height = 0; }; diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 58c710344..d8ad40a0f 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -36,11 +36,11 @@ constexpr VkAccessFlags TRANSFORM_FEEDBACK_WRITE_ACCESS = } // Anonymous namespace -Buffer::Buffer(const Device& device_, VKMemoryManager& memory_manager, VKScheduler& scheduler_, - VKStagingBufferPool& staging_pool_, VAddr cpu_addr_, std::size_t size_) +Buffer::Buffer(const Device& device_, MemoryAllocator& memory_allocator, VKScheduler& scheduler_, + StagingBufferPool& staging_pool_, VAddr cpu_addr_, std::size_t size_) : BufferBlock{cpu_addr_, size_}, device{device_}, scheduler{scheduler_}, staging_pool{ staging_pool_} { - const VkBufferCreateInfo ci{ + buffer = device.GetLogical().CreateBuffer(VkBufferCreateInfo{ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .pNext = nullptr, .flags = 0, @@ -49,22 +49,20 @@ Buffer::Buffer(const Device& device_, VKMemoryManager& memory_manager, VKSchedul .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, - }; - - buffer.handle = device.GetLogical().CreateBuffer(ci); - buffer.commit = memory_manager.Commit(buffer.handle, false); + }); + commit = memory_allocator.Commit(buffer, MemoryUsage::DeviceLocal); } Buffer::~Buffer() = default; void Buffer::Upload(std::size_t offset, std::size_t data_size, const u8* data) { - const auto& staging = staging_pool.GetUnusedBuffer(data_size, true); - std::memcpy(staging.commit->Map(data_size), data, data_size); + const auto& staging = staging_pool.Request(data_size, MemoryUsage::Upload); + std::memcpy(staging.mapped_span.data(), data, data_size); scheduler.RequestOutsideRenderPassOperationContext(); const VkBuffer handle = Handle(); - scheduler.Record([staging = *staging.handle, handle, offset, data_size, + scheduler.Record([staging = staging.buffer, handle, offset, data_size, &device = device](vk::CommandBuffer cmdbuf) { const VkBufferMemoryBarrier read_barrier{ .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, @@ -100,12 +98,12 @@ void Buffer::Upload(std::size_t offset, std::size_t data_size, const u8* data) { } void Buffer::Download(std::size_t offset, std::size_t data_size, u8* data) { - const auto& staging = staging_pool.GetUnusedBuffer(data_size, true); + auto staging = staging_pool.Request(data_size, MemoryUsage::Download); scheduler.RequestOutsideRenderPassOperationContext(); const VkBuffer handle = Handle(); scheduler.Record( - [staging = *staging.handle, handle, offset, data_size](vk::CommandBuffer cmdbuf) { + [staging = staging.buffer, handle, offset, data_size](vk::CommandBuffer cmdbuf) { const VkBufferMemoryBarrier barrier{ .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, .pNext = nullptr, @@ -126,7 +124,7 @@ void Buffer::Download(std::size_t offset, std::size_t data_size, u8* data) { }); scheduler.Finish(); - std::memcpy(data, staging.commit->Map(data_size), data_size); + std::memcpy(data, staging.mapped_span.data(), data_size); } void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, @@ -164,29 +162,29 @@ void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst VKBufferCache::VKBufferCache(VideoCore::RasterizerInterface& rasterizer_, Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_, - const Device& device_, VKMemoryManager& memory_manager_, + const Device& device_, MemoryAllocator& memory_allocator_, VKScheduler& scheduler_, VKStreamBuffer& stream_buffer_, - VKStagingBufferPool& staging_pool_) + StagingBufferPool& staging_pool_) : VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer>{rasterizer_, gpu_memory_, cpu_memory_, stream_buffer_}, - device{device_}, memory_manager{memory_manager_}, scheduler{scheduler_}, staging_pool{ - staging_pool_} {} + device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_}, + staging_pool{staging_pool_} {} VKBufferCache::~VKBufferCache() = default; std::shared_ptr<Buffer> VKBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) { - return std::make_shared<Buffer>(device, memory_manager, scheduler, staging_pool, cpu_addr, + return std::make_shared<Buffer>(device, memory_allocator, scheduler, staging_pool, cpu_addr, size); } VKBufferCache::BufferInfo VKBufferCache::GetEmptyBuffer(std::size_t size) { size = std::max(size, std::size_t(4)); - const auto& empty = staging_pool.GetUnusedBuffer(size, false); + const auto& empty = staging_pool.Request(size, MemoryUsage::DeviceLocal); scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([size, buffer = *empty.handle](vk::CommandBuffer cmdbuf) { + scheduler.Record([size, buffer = empty.buffer](vk::CommandBuffer cmdbuf) { cmdbuf.FillBuffer(buffer, 0, size, 0); }); - return {*empty.handle, 0, 0}; + return {empty.buffer, 0, 0}; } } // 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 @@ #include "common/common_types.h" #include "video_core/buffer_cache/buffer_cache.h" -#include "video_core/renderer_vulkan/vk_memory_manager.h" #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" #include "video_core/renderer_vulkan/vk_stream_buffer.h" +#include "video_core/vulkan_common/vulkan_memory_allocator.h" #include "video_core/vulkan_common/vulkan_wrapper.h" namespace Vulkan { class Device; -class VKMemoryManager; class VKScheduler; class Buffer final : public VideoCommon::BufferBlock { public: - explicit Buffer(const Device& device, VKMemoryManager& memory_manager, VKScheduler& scheduler, - VKStagingBufferPool& staging_pool, VAddr cpu_addr_, std::size_t size_); + explicit Buffer(const Device& device, MemoryAllocator& memory_allocator, VKScheduler& scheduler, + StagingBufferPool& staging_pool, VAddr cpu_addr_, std::size_t size_); ~Buffer(); void Upload(std::size_t offset, std::size_t data_size, const u8* data); @@ -33,7 +32,7 @@ public: std::size_t copy_size); VkBuffer Handle() const { - return *buffer.handle; + return *buffer; } u64 Address() const { @@ -43,18 +42,19 @@ public: private: const Device& device; VKScheduler& scheduler; - VKStagingBufferPool& staging_pool; + StagingBufferPool& staging_pool; - VKBuffer buffer; + vk::Buffer buffer; + MemoryCommit commit; }; class VKBufferCache final : public VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer> { public: explicit VKBufferCache(VideoCore::RasterizerInterface& rasterizer, Tegra::MemoryManager& gpu_memory, Core::Memory::Memory& cpu_memory, - const Device& device, VKMemoryManager& memory_manager, + const Device& device, MemoryAllocator& memory_allocator, VKScheduler& scheduler, VKStreamBuffer& stream_buffer, - VKStagingBufferPool& staging_pool); + StagingBufferPool& staging_pool); ~VKBufferCache(); BufferInfo GetEmptyBuffer(std::size_t size) override; @@ -64,9 +64,9 @@ protected: private: const Device& device; - VKMemoryManager& memory_manager; + MemoryAllocator& memory_allocator; VKScheduler& scheduler; - VKStagingBufferPool& staging_pool; + StagingBufferPool& staging_pool; }; } // 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( QuadArrayPass::QuadArrayPass(const Device& device_, VKScheduler& scheduler_, VKDescriptorPool& descriptor_pool_, - VKStagingBufferPool& staging_buffer_pool_, + StagingBufferPool& staging_buffer_pool_, VKUpdateDescriptorQueue& update_descriptor_queue_) : VKComputePass(device_, descriptor_pool_, BuildQuadArrayPassDescriptorSetLayoutBinding(), BuildQuadArrayPassDescriptorUpdateTemplateEntry(), @@ -177,18 +177,18 @@ QuadArrayPass::~QuadArrayPass() = default; std::pair<VkBuffer, VkDeviceSize> QuadArrayPass::Assemble(u32 num_vertices, u32 first) { const u32 num_triangle_vertices = (num_vertices / 4) * 6; const std::size_t staging_size = num_triangle_vertices * sizeof(u32); - auto& buffer = staging_buffer_pool.GetUnusedBuffer(staging_size, false); + const auto staging_ref = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal); update_descriptor_queue.Acquire(); - update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size); + update_descriptor_queue.AddBuffer(staging_ref.buffer, 0, staging_size); const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue); scheduler.RequestOutsideRenderPassOperationContext(); ASSERT(num_vertices % 4 == 0); const u32 num_quads = num_vertices / 4; - scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = *buffer.handle, num_quads, - first, set](vk::CommandBuffer cmdbuf) { + scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = staging_ref.buffer, + num_quads, first, set](vk::CommandBuffer cmdbuf) { constexpr u32 dispatch_size = 1024; cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, layout, 0, set, {}); @@ -208,11 +208,11 @@ std::pair<VkBuffer, VkDeviceSize> QuadArrayPass::Assemble(u32 num_vertices, u32 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, {}, {barrier}, {}); }); - return {*buffer.handle, 0}; + return {staging_ref.buffer, 0}; } Uint8Pass::Uint8Pass(const Device& device, VKScheduler& scheduler_, - VKDescriptorPool& descriptor_pool, VKStagingBufferPool& staging_buffer_pool_, + VKDescriptorPool& descriptor_pool, StagingBufferPool& staging_buffer_pool_, VKUpdateDescriptorQueue& update_descriptor_queue_) : VKComputePass(device, descriptor_pool, BuildInputOutputDescriptorSetBindings(), BuildInputOutputDescriptorUpdateTemplate(), {}, VULKAN_UINT8_COMP_SPV), @@ -224,15 +224,15 @@ Uint8Pass::~Uint8Pass() = default; std::pair<VkBuffer, u64> Uint8Pass::Assemble(u32 num_vertices, VkBuffer src_buffer, u64 src_offset) { const u32 staging_size = static_cast<u32>(num_vertices * sizeof(u16)); - auto& buffer = staging_buffer_pool.GetUnusedBuffer(staging_size, false); + const auto staging_ref = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal); update_descriptor_queue.Acquire(); update_descriptor_queue.AddBuffer(src_buffer, src_offset, num_vertices); - update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size); + update_descriptor_queue.AddBuffer(staging_ref.buffer, 0, staging_size); const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue); scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = *buffer.handle, set, + scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = staging_ref.buffer, set, num_vertices](vk::CommandBuffer cmdbuf) { constexpr u32 dispatch_size = 1024; cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); @@ -252,12 +252,12 @@ std::pair<VkBuffer, u64> Uint8Pass::Assemble(u32 num_vertices, VkBuffer src_buff cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, {}, barrier, {}); }); - return {*buffer.handle, 0}; + return {staging_ref.buffer, 0}; } QuadIndexedPass::QuadIndexedPass(const Device& device_, VKScheduler& scheduler_, VKDescriptorPool& descriptor_pool_, - VKStagingBufferPool& staging_buffer_pool_, + StagingBufferPool& staging_buffer_pool_, VKUpdateDescriptorQueue& update_descriptor_queue_) : VKComputePass(device_, descriptor_pool_, BuildInputOutputDescriptorSetBindings(), BuildInputOutputDescriptorUpdateTemplate(), @@ -286,15 +286,15 @@ std::pair<VkBuffer, u64> QuadIndexedPass::Assemble( const u32 num_tri_vertices = (num_vertices / 4) * 6; const std::size_t staging_size = num_tri_vertices * sizeof(u32); - auto& buffer = staging_buffer_pool.GetUnusedBuffer(staging_size, false); + const auto staging_ref = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal); update_descriptor_queue.Acquire(); update_descriptor_queue.AddBuffer(src_buffer, src_offset, input_size); - update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size); + update_descriptor_queue.AddBuffer(staging_ref.buffer, 0, staging_size); const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue); scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = *buffer.handle, set, + scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = staging_ref.buffer, set, num_tri_vertices, base_vertex, index_shift](vk::CommandBuffer cmdbuf) { static constexpr u32 dispatch_size = 1024; const std::array push_constants = {base_vertex, index_shift}; @@ -317,7 +317,7 @@ std::pair<VkBuffer, u64> QuadIndexedPass::Assemble( cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, {}, barrier, {}); }); - return {*buffer.handle, 0}; + return {staging_ref.buffer, 0}; } } // 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 @@ namespace Vulkan { class Device; +class StagingBufferPool; class VKScheduler; -class VKStagingBufferPool; class VKUpdateDescriptorQueue; class VKComputePass { @@ -45,7 +45,7 @@ class QuadArrayPass final : public VKComputePass { public: explicit QuadArrayPass(const Device& device_, VKScheduler& scheduler_, VKDescriptorPool& descriptor_pool_, - VKStagingBufferPool& staging_buffer_pool_, + StagingBufferPool& staging_buffer_pool_, VKUpdateDescriptorQueue& update_descriptor_queue_); ~QuadArrayPass(); @@ -53,15 +53,14 @@ public: private: VKScheduler& scheduler; - VKStagingBufferPool& staging_buffer_pool; + StagingBufferPool& staging_buffer_pool; VKUpdateDescriptorQueue& update_descriptor_queue; }; class Uint8Pass final : public VKComputePass { public: explicit Uint8Pass(const Device& device_, VKScheduler& scheduler_, - VKDescriptorPool& descriptor_pool_, - VKStagingBufferPool& staging_buffer_pool_, + VKDescriptorPool& descriptor_pool_, StagingBufferPool& staging_buffer_pool_, VKUpdateDescriptorQueue& update_descriptor_queue_); ~Uint8Pass(); @@ -69,7 +68,7 @@ public: private: VKScheduler& scheduler; - VKStagingBufferPool& staging_buffer_pool; + StagingBufferPool& staging_buffer_pool; VKUpdateDescriptorQueue& update_descriptor_queue; }; @@ -77,7 +76,7 @@ class QuadIndexedPass final : public VKComputePass { public: explicit QuadIndexedPass(const Device& device_, VKScheduler& scheduler_, VKDescriptorPool& descriptor_pool_, - VKStagingBufferPool& staging_buffer_pool_, + StagingBufferPool& staging_buffer_pool_, VKUpdateDescriptorQueue& update_descriptor_queue_); ~QuadIndexedPass(); @@ -87,7 +86,7 @@ public: private: VKScheduler& scheduler; - VKStagingBufferPool& staging_buffer_pool; + StagingBufferPool& staging_buffer_pool; VKUpdateDescriptorQueue& update_descriptor_queue; }; 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 @@ -// Copyright 2018 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <algorithm> -#include <optional> -#include <tuple> -#include <vector> - -#include "common/alignment.h" -#include "common/assert.h" -#include "common/common_types.h" -#include "common/logging/log.h" -#include "video_core/renderer_vulkan/vk_memory_manager.h" -#include "video_core/vulkan_common/vulkan_device.h" -#include "video_core/vulkan_common/vulkan_wrapper.h" - -namespace Vulkan { - -namespace { - -u64 GetAllocationChunkSize(u64 required_size) { - static constexpr u64 sizes[] = {16ULL << 20, 32ULL << 20, 64ULL << 20, 128ULL << 20}; - auto it = std::lower_bound(std::begin(sizes), std::end(sizes), required_size); - return it != std::end(sizes) ? *it : Common::AlignUp(required_size, 256ULL << 20); -} - -} // Anonymous namespace - -class VKMemoryAllocation final { -public: - explicit VKMemoryAllocation(const Device& device_, vk::DeviceMemory memory_, - VkMemoryPropertyFlags properties_, u64 allocation_size_, u32 type_) - : device{device_}, memory{std::move(memory_)}, properties{properties_}, - allocation_size{allocation_size_}, shifted_type{ShiftType(type_)} {} - - VKMemoryCommit Commit(VkDeviceSize commit_size, VkDeviceSize alignment) { - auto found = TryFindFreeSection(free_iterator, allocation_size, - static_cast<u64>(commit_size), static_cast<u64>(alignment)); - if (!found) { - found = TryFindFreeSection(0, free_iterator, static_cast<u64>(commit_size), - static_cast<u64>(alignment)); - if (!found) { - // Signal out of memory, it'll try to do more allocations. - return nullptr; - } - } - auto commit = std::make_unique<VKMemoryCommitImpl>(device, this, memory, *found, - *found + commit_size); - commits.push_back(commit.get()); - - // Last commit's address is highly probable to be free. - free_iterator = *found + commit_size; - - return commit; - } - - void Free(const VKMemoryCommitImpl* commit) { - ASSERT(commit); - - const auto it = std::find(std::begin(commits), std::end(commits), commit); - if (it == commits.end()) { - UNREACHABLE_MSG("Freeing unallocated commit!"); - return; - } - commits.erase(it); - } - - /// Returns whether this allocation is compatible with the arguments. - bool IsCompatible(VkMemoryPropertyFlags wanted_properties, u32 type_mask) const { - return (wanted_properties & properties) && (type_mask & shifted_type) != 0; - } - -private: - static constexpr u32 ShiftType(u32 type) { - return 1U << type; - } - - /// A memory allocator, it may return a free region between "start" and "end" with the solicited - /// requirements. - std::optional<u64> TryFindFreeSection(u64 start, u64 end, u64 size, u64 alignment) const { - u64 iterator = Common::AlignUp(start, alignment); - while (iterator + size <= end) { - const u64 try_left = iterator; - const u64 try_right = try_left + size; - - bool overlap = false; - for (const auto& commit : commits) { - const auto [commit_left, commit_right] = commit->interval; - if (try_left < commit_right && commit_left < try_right) { - // There's an overlap, continue the search where the overlapping commit ends. - iterator = Common::AlignUp(commit_right, alignment); - overlap = true; - break; - } - } - if (!overlap) { - // A free address has been found. - return try_left; - } - } - - // No free regions where found, return an empty optional. - return std::nullopt; - } - - const Device& device; ///< Vulkan device. - const vk::DeviceMemory memory; ///< Vulkan memory allocation handler. - const VkMemoryPropertyFlags properties; ///< Vulkan properties. - const u64 allocation_size; ///< Size of this allocation. - const u32 shifted_type; ///< Stored Vulkan type of this allocation, shifted. - - /// Hints where the next free region is likely going to be. - u64 free_iterator{}; - - /// Stores all commits done from this allocation. - std::vector<const VKMemoryCommitImpl*> commits; -}; - -VKMemoryManager::VKMemoryManager(const Device& device_) - : device{device_}, properties{device_.GetPhysical().GetMemoryProperties()} {} - -VKMemoryManager::~VKMemoryManager() = default; - -VKMemoryCommit VKMemoryManager::Commit(const VkMemoryRequirements& requirements, - bool host_visible) { - const u64 chunk_size = GetAllocationChunkSize(requirements.size); - - // When a host visible commit is asked, search for host visible and coherent, otherwise search - // for a fast device local type. - const VkMemoryPropertyFlags wanted_properties = - host_visible ? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT - : VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; - - if (auto commit = TryAllocCommit(requirements, wanted_properties)) { - return commit; - } - - // Commit has failed, allocate more memory. - if (!AllocMemory(wanted_properties, requirements.memoryTypeBits, chunk_size)) { - // TODO(Rodrigo): Handle these situations in some way like flushing to guest memory. - // Allocation has failed, panic. - UNREACHABLE_MSG("Ran out of VRAM!"); - return {}; - } - - // Commit again, this time it won't fail since there's a fresh allocation above. If it does, - // there's a bug. - auto commit = TryAllocCommit(requirements, wanted_properties); - ASSERT(commit); - return commit; -} - -VKMemoryCommit VKMemoryManager::Commit(const vk::Buffer& buffer, bool host_visible) { - auto commit = Commit(device.GetLogical().GetBufferMemoryRequirements(*buffer), host_visible); - buffer.BindMemory(commit->GetMemory(), commit->GetOffset()); - return commit; -} - -VKMemoryCommit VKMemoryManager::Commit(const vk::Image& image, bool host_visible) { - auto commit = Commit(device.GetLogical().GetImageMemoryRequirements(*image), host_visible); - image.BindMemory(commit->GetMemory(), commit->GetOffset()); - return commit; -} - -bool VKMemoryManager::AllocMemory(VkMemoryPropertyFlags wanted_properties, u32 type_mask, - u64 size) { - const u32 type = [&] { - for (u32 type_index = 0; type_index < properties.memoryTypeCount; ++type_index) { - const auto flags = properties.memoryTypes[type_index].propertyFlags; - if ((type_mask & (1U << type_index)) && (flags & wanted_properties)) { - // The type matches in type and in the wanted properties. - return type_index; - } - } - UNREACHABLE_MSG("Couldn't find a compatible memory type!"); - return 0U; - }(); - - // Try to allocate found type. - vk::DeviceMemory memory = device.GetLogical().TryAllocateMemory({ - .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, - .pNext = nullptr, - .allocationSize = size, - .memoryTypeIndex = type, - }); - if (!memory) { - LOG_CRITICAL(Render_Vulkan, "Device allocation failed!"); - return false; - } - - allocations.push_back(std::make_unique<VKMemoryAllocation>(device, std::move(memory), - wanted_properties, size, type)); - return true; -} - -VKMemoryCommit VKMemoryManager::TryAllocCommit(const VkMemoryRequirements& requirements, - VkMemoryPropertyFlags wanted_properties) { - for (auto& allocation : allocations) { - if (!allocation->IsCompatible(wanted_properties, requirements.memoryTypeBits)) { - continue; - } - if (auto commit = allocation->Commit(requirements.size, requirements.alignment)) { - return commit; - } - } - return {}; -} - -VKMemoryCommitImpl::VKMemoryCommitImpl(const Device& device_, VKMemoryAllocation* allocation_, - const vk::DeviceMemory& memory_, u64 begin_, u64 end_) - : device{device_}, memory{memory_}, interval{begin_, end_}, allocation{allocation_} {} - -VKMemoryCommitImpl::~VKMemoryCommitImpl() { - allocation->Free(this); -} - -MemoryMap VKMemoryCommitImpl::Map(u64 size, u64 offset_) const { - return MemoryMap(this, std::span<u8>(memory.Map(interval.first + offset_, size), size)); -} - -void VKMemoryCommitImpl::Unmap() const { - memory.Unmap(); -} - -MemoryMap VKMemoryCommitImpl::Map() const { - return Map(interval.second - interval.first); -} - -} // 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 @@ -// Copyright 2019 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <memory> -#include <span> -#include <utility> -#include <vector> -#include "common/common_types.h" -#include "video_core/vulkan_common/vulkan_wrapper.h" - -namespace Vulkan { - -class Device; -class MemoryMap; -class VKMemoryAllocation; -class VKMemoryCommitImpl; - -using VKMemoryCommit = std::unique_ptr<VKMemoryCommitImpl>; - -class VKMemoryManager final { -public: - explicit VKMemoryManager(const Device& device_); - VKMemoryManager(const VKMemoryManager&) = delete; - ~VKMemoryManager(); - - /** - * Commits a memory with the specified requeriments. - * @param requirements Requirements returned from a Vulkan call. - * @param host_visible Signals the allocator that it *must* use host visible and coherent - * memory. When passing false, it will try to allocate device local memory. - * @returns A memory commit. - */ - VKMemoryCommit Commit(const VkMemoryRequirements& requirements, bool host_visible); - - /// Commits memory required by the buffer and binds it. - VKMemoryCommit Commit(const vk::Buffer& buffer, bool host_visible); - - /// Commits memory required by the image and binds it. - VKMemoryCommit Commit(const vk::Image& image, bool host_visible); - -private: - /// Allocates a chunk of memory. - bool AllocMemory(VkMemoryPropertyFlags wanted_properties, u32 type_mask, u64 size); - - /// Tries to allocate a memory commit. - VKMemoryCommit TryAllocCommit(const VkMemoryRequirements& requirements, - VkMemoryPropertyFlags wanted_properties); - - const Device& device; ///< Device handler. - const VkPhysicalDeviceMemoryProperties properties; ///< Physical device properties. - std::vector<std::unique_ptr<VKMemoryAllocation>> allocations; ///< Current allocations. -}; - -class VKMemoryCommitImpl final { - friend VKMemoryAllocation; - friend MemoryMap; - -public: - explicit VKMemoryCommitImpl(const Device& device_, VKMemoryAllocation* allocation_, - const vk::DeviceMemory& memory_, u64 begin_, u64 end_); - ~VKMemoryCommitImpl(); - - /// Maps a memory region and returns a pointer to it. - /// It's illegal to have more than one memory map at the same time. - MemoryMap Map(u64 size, u64 offset = 0) const; - - /// Maps the whole commit and returns a pointer to it. - /// It's illegal to have more than one memory map at the same time. - MemoryMap Map() const; - - /// Returns the Vulkan memory handler. - VkDeviceMemory GetMemory() const { - return *memory; - } - - /// Returns the start position of the commit relative to the allocation. - VkDeviceSize GetOffset() const { - return static_cast<VkDeviceSize>(interval.first); - } - -private: - /// Unmaps memory. - void Unmap() const; - - const Device& device; ///< Vulkan device. - const vk::DeviceMemory& memory; ///< Vulkan device memory handler. - std::pair<u64, u64> interval{}; ///< Interval where the commit exists. - VKMemoryAllocation* allocation{}; ///< Pointer to the large memory allocation. -}; - -/// Holds ownership of a memory map. -class MemoryMap final { -public: - explicit MemoryMap(const VKMemoryCommitImpl* commit_, std::span<u8> span_) - : commit{commit_}, span{span_} {} - - ~MemoryMap() { - if (commit) { - commit->Unmap(); - } - } - - /// Prematurely releases the memory map. - void Release() { - commit->Unmap(); - commit = nullptr; - } - - /// Returns a span to the memory map. - [[nodiscard]] std::span<u8> Span() const noexcept { - return span; - } - - /// Returns the address of the memory map. - [[nodiscard]] u8* Address() const noexcept { - return span.data(); - } - - /// Returns the address of the memory map; - [[nodiscard]] operator u8*() const noexcept { - return span.data(); - } - -private: - const VKMemoryCommitImpl* commit{}; ///< Mapped memory commit. - std::span<u8> span; ///< Address to the mapped memory. -}; - -} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 02282e36f..8991505ca 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -355,14 +355,12 @@ VKPipelineCache::DecompileShaders(const FixedPipelineState& fixed_state) { SPIRVProgram program; std::vector<VkDescriptorSetLayoutBinding> bindings; - for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { + for (std::size_t index = 1; index < Maxwell::MaxShaderProgram; ++index) { const auto program_enum = static_cast<Maxwell::ShaderProgram>(index); - // Skip stages that are not enabled if (!maxwell3d.regs.IsShaderConfigEnabled(index)) { continue; } - const GPUVAddr gpu_addr = GetShaderAddress(maxwell3d, program_enum); const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); Shader* const shader = cpu_addr ? TryGet(*cpu_addr) : null_shader.get(); @@ -372,12 +370,8 @@ VKPipelineCache::DecompileShaders(const FixedPipelineState& fixed_state) { const auto& entries = shader->GetEntries(); program[stage] = { Decompile(device, shader->GetIR(), program_type, shader->GetRegistry(), specialization), - entries}; - - if (program_enum == Maxwell::ShaderProgram::VertexA) { - // VertexB was combined with VertexA, so we skip the VertexB iteration - ++index; - } + entries, + }; const u32 old_binding = specialization.base_binding; specialization.base_binding = diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index ce3db49bd..f0a111829 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -409,24 +409,24 @@ void RasterizerVulkan::DrawParameters::Draw(vk::CommandBuffer cmdbuf) const { RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_, VKScreenInfo& screen_info_, - const Device& device_, VKMemoryManager& memory_manager_, + const Device& device_, MemoryAllocator& memory_allocator_, StateTracker& state_tracker_, VKScheduler& scheduler_) : RasterizerAccelerated{cpu_memory_}, gpu{gpu_}, gpu_memory{gpu_memory_}, maxwell3d{gpu.Maxwell3D()}, kepler_compute{gpu.KeplerCompute()}, - screen_info{screen_info_}, device{device_}, memory_manager{memory_manager_}, + screen_info{screen_info_}, device{device_}, memory_allocator{memory_allocator_}, state_tracker{state_tracker_}, scheduler{scheduler_}, stream_buffer(device, scheduler), - staging_pool(device, memory_manager, scheduler), descriptor_pool(device, scheduler), + staging_pool(device, memory_allocator, scheduler), descriptor_pool(device, scheduler), update_descriptor_queue(device, scheduler), blit_image(device, scheduler, state_tracker, descriptor_pool), quad_array_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), quad_indexed_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), uint8_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), - texture_cache_runtime{device, scheduler, memory_manager, staging_pool, blit_image}, + texture_cache_runtime{device, scheduler, memory_allocator, staging_pool, blit_image}, texture_cache(texture_cache_runtime, *this, maxwell3d, kepler_compute, gpu_memory), pipeline_cache(*this, gpu, maxwell3d, kepler_compute, gpu_memory, device, scheduler, descriptor_pool, update_descriptor_queue), - buffer_cache(*this, gpu_memory, cpu_memory_, device, memory_manager, scheduler, stream_buffer, - staging_pool), + buffer_cache(*this, gpu_memory, cpu_memory_, device, memory_allocator, scheduler, + stream_buffer, staging_pool), query_cache{*this, maxwell3d, gpu_memory, device, scheduler}, fence_manager(*this, gpu, gpu_memory, texture_cache, buffer_cache, query_cache, scheduler), wfi_event(device.GetLogical().CreateEvent()), async_shaders(emu_window_) { @@ -1445,7 +1445,7 @@ VkBuffer RasterizerVulkan::DefaultBuffer() { .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, }); - default_buffer_commit = memory_manager.Commit(default_buffer, false); + default_buffer_commit = memory_allocator.Commit(default_buffer, MemoryUsage::DeviceLocal); scheduler.RequestOutsideRenderPassOperationContext(); 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 @@ #include "video_core/renderer_vulkan/vk_compute_pass.h" #include "video_core/renderer_vulkan/vk_descriptor_pool.h" #include "video_core/renderer_vulkan/vk_fence_manager.h" -#include "video_core/renderer_vulkan/vk_memory_manager.h" #include "video_core/renderer_vulkan/vk_pipeline_cache.h" #include "video_core/renderer_vulkan/vk_query_cache.h" #include "video_core/renderer_vulkan/vk_scheduler.h" @@ -30,6 +29,7 @@ #include "video_core/renderer_vulkan/vk_texture_cache.h" #include "video_core/renderer_vulkan/vk_update_descriptor.h" #include "video_core/shader/async_shaders.h" +#include "video_core/vulkan_common/vulkan_memory_allocator.h" #include "video_core/vulkan_common/vulkan_wrapper.h" namespace Core { @@ -56,7 +56,7 @@ public: explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_, VKScreenInfo& screen_info_, const Device& device_, - VKMemoryManager& memory_manager_, StateTracker& state_tracker_, + MemoryAllocator& memory_allocator_, StateTracker& state_tracker_, VKScheduler& scheduler_); ~RasterizerVulkan() override; @@ -213,12 +213,12 @@ private: VKScreenInfo& screen_info; const Device& device; - VKMemoryManager& memory_manager; + MemoryAllocator& memory_allocator; StateTracker& state_tracker; VKScheduler& scheduler; VKStreamBuffer stream_buffer; - VKStagingBufferPool staging_pool; + StagingBufferPool staging_pool; VKDescriptorPool descriptor_pool; VKUpdateDescriptorQueue update_descriptor_queue; BlitImageHelper blit_image; @@ -234,7 +234,7 @@ private: VKFenceManager fence_manager; vk::Buffer default_buffer; - VKMemoryCommit default_buffer_commit; + MemoryCommit default_buffer_commit; vk::Event wfi_event; VideoCommon::Shader::AsyncShaders async_shaders; diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index 89cbe01ad..61d52b961 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp @@ -1334,7 +1334,10 @@ private: } if (const auto comment = std::get_if<CommentNode>(&*node)) { - Name(OpUndef(t_void), comment->GetText()); + if (device.HasDebuggingToolAttached()) { + // We should insert comments with OpString instead of using named variables + Name(OpUndef(t_int), comment->GetText()); + } return {}; } 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 @@ // Refer to the license.txt file included. #include <algorithm> -#include <unordered_map> #include <utility> #include <vector> +#include <fmt/format.h> + +#include "common/assert.h" #include "common/bit_util.h" #include "common/common_types.h" #include "video_core/renderer_vulkan/vk_scheduler.h" @@ -16,45 +18,51 @@ namespace Vulkan { -VKStagingBufferPool::StagingBuffer::StagingBuffer(std::unique_ptr<VKBuffer> buffer_) - : buffer{std::move(buffer_)} {} - -VKStagingBufferPool::VKStagingBufferPool(const Device& device_, VKMemoryManager& memory_manager_, - VKScheduler& scheduler_) - : device{device_}, memory_manager{memory_manager_}, scheduler{scheduler_} {} +StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& memory_allocator_, + VKScheduler& scheduler_) + : device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_} {} -VKStagingBufferPool::~VKStagingBufferPool() = default; +StagingBufferPool::~StagingBufferPool() = default; -VKBuffer& VKStagingBufferPool::GetUnusedBuffer(std::size_t size, bool host_visible) { - if (const auto buffer = TryGetReservedBuffer(size, host_visible)) { - return *buffer; +StagingBufferRef StagingBufferPool::Request(size_t size, MemoryUsage usage) { + if (const std::optional<StagingBufferRef> ref = TryGetReservedBuffer(size, usage)) { + return *ref; } - return CreateStagingBuffer(size, host_visible); + return CreateStagingBuffer(size, usage); } -void VKStagingBufferPool::TickFrame() { - current_delete_level = (current_delete_level + 1) % NumLevels; +void StagingBufferPool::TickFrame() { + current_delete_level = (current_delete_level + 1) % NUM_LEVELS; - ReleaseCache(true); - ReleaseCache(false); + ReleaseCache(MemoryUsage::DeviceLocal); + ReleaseCache(MemoryUsage::Upload); + ReleaseCache(MemoryUsage::Download); } -VKBuffer* VKStagingBufferPool::TryGetReservedBuffer(std::size_t size, bool host_visible) { - for (StagingBuffer& entry : GetCache(host_visible)[Common::Log2Ceil64(size)].entries) { - if (!scheduler.IsFree(entry.tick)) { - continue; +std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t size, + MemoryUsage usage) { + StagingBuffers& cache_level = GetCache(usage)[Common::Log2Ceil64(size)]; + + const auto is_free = [this](const StagingBuffer& entry) { + return scheduler.IsFree(entry.tick); + }; + auto& entries = cache_level.entries; + const auto hint_it = entries.begin() + cache_level.iterate_index; + auto it = std::find_if(entries.begin() + cache_level.iterate_index, entries.end(), is_free); + if (it == entries.end()) { + it = std::find_if(entries.begin(), hint_it, is_free); + if (it == hint_it) { + return std::nullopt; } - entry.tick = scheduler.CurrentTick(); - return &*entry.buffer; } - return nullptr; + cache_level.iterate_index = std::distance(entries.begin(), it) + 1; + it->tick = scheduler.CurrentTick(); + return it->Ref(); } -VKBuffer& VKStagingBufferPool::CreateStagingBuffer(std::size_t size, bool host_visible) { +StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage usage) { const u32 log2 = Common::Log2Ceil64(size); - - auto buffer = std::make_unique<VKBuffer>(); - buffer->handle = device.GetLogical().CreateBuffer({ + vk::Buffer buffer = device.GetLogical().CreateBuffer({ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .pNext = nullptr, .flags = 0, @@ -66,49 +74,63 @@ VKBuffer& VKStagingBufferPool::CreateStagingBuffer(std::size_t size, bool host_v .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, }); - buffer->commit = memory_manager.Commit(buffer->handle, host_visible); - - std::vector<StagingBuffer>& entries = GetCache(host_visible)[log2].entries; - StagingBuffer& entry = entries.emplace_back(std::move(buffer)); - entry.tick = scheduler.CurrentTick(); - return *entry.buffer; -} - -VKStagingBufferPool::StagingBuffersCache& VKStagingBufferPool::GetCache(bool host_visible) { - return host_visible ? host_staging_buffers : device_staging_buffers; + if (device.HasDebuggingToolAttached()) { + ++buffer_index; + buffer.SetObjectNameEXT(fmt::format("Staging Buffer {}", buffer_index).c_str()); + } + MemoryCommit commit = memory_allocator.Commit(buffer, usage); + const std::span<u8> mapped_span = IsHostVisible(usage) ? commit.Map() : std::span<u8>{}; + + StagingBuffer& entry = GetCache(usage)[log2].entries.emplace_back(StagingBuffer{ + .buffer = std::move(buffer), + .commit = std::move(commit), + .mapped_span = mapped_span, + .tick = scheduler.CurrentTick(), + }); + return entry.Ref(); } -void VKStagingBufferPool::ReleaseCache(bool host_visible) { - auto& cache = GetCache(host_visible); - const u64 size = ReleaseLevel(cache, current_delete_level); - if (size == 0) { - return; +StagingBufferPool::StagingBuffersCache& StagingBufferPool::GetCache(MemoryUsage usage) { + switch (usage) { + case MemoryUsage::DeviceLocal: + return device_local_cache; + case MemoryUsage::Upload: + return upload_cache; + case MemoryUsage::Download: + return download_cache; + default: + UNREACHABLE_MSG("Invalid memory usage={}", usage); + return upload_cache; } } -u64 VKStagingBufferPool::ReleaseLevel(StagingBuffersCache& cache, std::size_t log2) { - static constexpr std::size_t deletions_per_tick = 16; +void StagingBufferPool::ReleaseCache(MemoryUsage usage) { + ReleaseLevel(GetCache(usage), current_delete_level); +} +void StagingBufferPool::ReleaseLevel(StagingBuffersCache& cache, size_t log2) { + constexpr size_t deletions_per_tick = 16; auto& staging = cache[log2]; auto& entries = staging.entries; - const std::size_t old_size = entries.size(); + const size_t old_size = entries.size(); const auto is_deleteable = [this](const StagingBuffer& entry) { return scheduler.IsFree(entry.tick); }; - const std::size_t begin_offset = staging.delete_index; - const std::size_t end_offset = std::min(begin_offset + deletions_per_tick, old_size); - const auto begin = std::begin(entries) + begin_offset; - const auto end = std::begin(entries) + end_offset; + const size_t begin_offset = staging.delete_index; + const size_t end_offset = std::min(begin_offset + deletions_per_tick, old_size); + const auto begin = entries.begin() + begin_offset; + const auto end = entries.begin() + end_offset; entries.erase(std::remove_if(begin, end, is_deleteable), end); - const std::size_t new_size = entries.size(); + const size_t new_size = entries.size(); staging.delete_index += deletions_per_tick; if (staging.delete_index >= new_size) { staging.delete_index = 0; } - - return (1ULL << log2) * (old_size - new_size); + if (staging.iterate_index > new_size) { + staging.iterate_index = 0; + } } } // 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 @@ #include "common/common_types.h" -#include "video_core/renderer_vulkan/vk_memory_manager.h" +#include "video_core/vulkan_common/vulkan_memory_allocator.h" #include "video_core/vulkan_common/vulkan_wrapper.h" namespace Vulkan { @@ -17,55 +17,65 @@ namespace Vulkan { class Device; class VKScheduler; -struct VKBuffer final { - vk::Buffer handle; - VKMemoryCommit commit; +struct StagingBufferRef { + VkBuffer buffer; + std::span<u8> mapped_span; }; -class VKStagingBufferPool final { +class StagingBufferPool { public: - explicit VKStagingBufferPool(const Device& device, VKMemoryManager& memory_manager, - VKScheduler& scheduler); - ~VKStagingBufferPool(); + explicit StagingBufferPool(const Device& device, MemoryAllocator& memory_allocator, + VKScheduler& scheduler); + ~StagingBufferPool(); - VKBuffer& GetUnusedBuffer(std::size_t size, bool host_visible); + StagingBufferRef Request(size_t size, MemoryUsage usage); void TickFrame(); private: - struct StagingBuffer final { - explicit StagingBuffer(std::unique_ptr<VKBuffer> buffer); - - std::unique_ptr<VKBuffer> buffer; + struct StagingBuffer { + vk::Buffer buffer; + MemoryCommit commit; + std::span<u8> mapped_span; u64 tick = 0; + + StagingBufferRef Ref() const noexcept { + return { + .buffer = *buffer, + .mapped_span = mapped_span, + }; + } }; - struct StagingBuffers final { + struct StagingBuffers { std::vector<StagingBuffer> entries; - std::size_t delete_index = 0; + size_t delete_index = 0; + size_t iterate_index = 0; }; - static constexpr std::size_t NumLevels = sizeof(std::size_t) * CHAR_BIT; - using StagingBuffersCache = std::array<StagingBuffers, NumLevels>; + static constexpr size_t NUM_LEVELS = sizeof(size_t) * CHAR_BIT; + using StagingBuffersCache = std::array<StagingBuffers, NUM_LEVELS>; - VKBuffer* TryGetReservedBuffer(std::size_t size, bool host_visible); + std::optional<StagingBufferRef> TryGetReservedBuffer(size_t size, MemoryUsage usage); - VKBuffer& CreateStagingBuffer(std::size_t size, bool host_visible); + StagingBufferRef CreateStagingBuffer(size_t size, MemoryUsage usage); - StagingBuffersCache& GetCache(bool host_visible); + StagingBuffersCache& GetCache(MemoryUsage usage); - void ReleaseCache(bool host_visible); + void ReleaseCache(MemoryUsage usage); - u64 ReleaseLevel(StagingBuffersCache& cache, std::size_t log2); + void ReleaseLevel(StagingBuffersCache& cache, size_t log2); const Device& device; - VKMemoryManager& memory_manager; + MemoryAllocator& memory_allocator; VKScheduler& scheduler; - StagingBuffersCache host_staging_buffers; - StagingBuffersCache device_staging_buffers; + StagingBuffersCache device_local_cache; + StagingBuffersCache upload_cache; + StagingBuffersCache download_cache; - std::size_t current_delete_level = 0; + size_t current_delete_level = 0; + u64 buffer_index = 0; }; } // 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 0f9e93e48..aa7c5d7c6 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 @@ #include "video_core/engines/fermi_2d.h" #include "video_core/renderer_vulkan/blit_image.h" #include "video_core/renderer_vulkan/maxwell_to_vk.h" -#include "video_core/renderer_vulkan/vk_memory_manager.h" #include "video_core/renderer_vulkan/vk_rasterizer.h" #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" #include "video_core/renderer_vulkan/vk_texture_cache.h" #include "video_core/vulkan_common/vulkan_device.h" +#include "video_core/vulkan_common/vulkan_memory_allocator.h" #include "video_core/vulkan_common/vulkan_wrapper.h" namespace Vulkan { @@ -570,10 +570,18 @@ void TextureCacheRuntime::Finish() { } ImageBufferMap TextureCacheRuntime::MapUploadBuffer(size_t size) { - const auto& buffer = staging_buffer_pool.GetUnusedBuffer(size, true); - return ImageBufferMap{ - .handle = *buffer.handle, - .map = buffer.commit->Map(size), + const auto staging_ref = staging_buffer_pool.Request(size, MemoryUsage::Upload); + return { + .handle = staging_ref.buffer, + .span = staging_ref.mapped_span, + }; +} + +ImageBufferMap TextureCacheRuntime::MapDownloadBuffer(size_t size) { + const auto staging_ref = staging_buffer_pool.Request(size, MemoryUsage::Download); + return { + .handle = staging_ref.buffer, + .span = staging_ref.mapped_span, }; } @@ -804,9 +812,9 @@ Image::Image(TextureCacheRuntime& runtime, const ImageInfo& info_, GPUVAddr gpu_ image(MakeImage(runtime.device, info)), buffer(MakeBuffer(runtime.device, info)), aspect_mask(ImageAspectMask(info.format)) { if (image) { - commit = runtime.memory_manager.Commit(image, false); + commit = runtime.memory_allocator.Commit(image, MemoryUsage::DeviceLocal); } else { - commit = runtime.memory_manager.Commit(buffer, false); + commit = runtime.memory_allocator.Commit(buffer, MemoryUsage::DeviceLocal); } if (IsPixelFormatASTC(info.format) && !runtime.device.IsOptimalAstcSupported()) { 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 @@ #include <compare> #include <span> -#include "video_core/renderer_vulkan/vk_memory_manager.h" #include "video_core/texture_cache/texture_cache.h" +#include "video_core/vulkan_common/vulkan_memory_allocator.h" #include "video_core/vulkan_common/vulkan_wrapper.h" namespace Vulkan { @@ -19,14 +19,13 @@ using VideoCommon::Offset2D; using VideoCommon::RenderTargets; using VideoCore::Surface::PixelFormat; -class VKScheduler; -class VKStagingBufferPool; - class BlitImageHelper; class Device; class Image; class ImageView; class Framebuffer; +class StagingBufferPool; +class VKScheduler; struct RenderPassKey { constexpr auto operator<=>(const RenderPassKey&) const noexcept = default; @@ -60,18 +59,18 @@ struct ImageBufferMap { } [[nodiscard]] std::span<u8> Span() const noexcept { - return map.Span(); + return span; } VkBuffer handle; - MemoryMap map; + std::span<u8> span; }; struct TextureCacheRuntime { const Device& device; VKScheduler& scheduler; - VKMemoryManager& memory_manager; - VKStagingBufferPool& staging_buffer_pool; + MemoryAllocator& memory_allocator; + StagingBufferPool& staging_buffer_pool; BlitImageHelper& blit_image_helper; std::unordered_map<RenderPassKey, vk::RenderPass> renderpass_cache; @@ -79,10 +78,7 @@ struct TextureCacheRuntime { [[nodiscard]] ImageBufferMap MapUploadBuffer(size_t size); - [[nodiscard]] ImageBufferMap MapDownloadBuffer(size_t size) { - // TODO: Have a special function for this - return MapUploadBuffer(size); - } + [[nodiscard]] ImageBufferMap MapDownloadBuffer(size_t size); void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src, const std::array<Offset2D, 2>& dst_region, @@ -141,7 +137,7 @@ private: VKScheduler* scheduler; vk::Image image; vk::Buffer buffer; - VKMemoryCommit commit; + MemoryCommit commit; VkImageAspectFlags aspect_mask = 0; bool initialized = false; }; diff --git a/src/video_core/shader/node.h b/src/video_core/shader/node.h index b54d33763..c9840b75e 100644 --- a/src/video_core/shader/node.h +++ b/src/video_core/shader/node.h @@ -465,6 +465,14 @@ public: return operands.size(); } + NodeBlock& GetOperands() { + return operands; + } + + const NodeBlock& GetOperands() const { + return operands; + } + [[nodiscard]] const Node& operator[](std::size_t operand_index) const { return operands.at(operand_index); } diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp index a4987ffc6..625a5eb46 100644 --- a/src/video_core/shader/shader_ir.cpp +++ b/src/video_core/shader/shader_ir.cpp @@ -388,9 +388,54 @@ void ShaderIR::SetInternalFlagsFromInteger(NodeBlock& bb, Node value, bool sets_ if (!sets_cc) { return; } - Node zerop = Operation(OperationCode::LogicalIEqual, std::move(value), Immediate(0)); - SetInternalFlag(bb, InternalFlag::Zero, std::move(zerop)); - LOG_WARNING(HW_GPU, "Condition codes implementation is incomplete"); + switch (value->index()) { + case 0: // Operation Node + SearchOperands(bb, value); + break; + case 2: // General Purpose Node + if (const auto* gpr = std::get_if<GprNode>(value.get())) { + LOG_DEBUG(HW_GPU, "GprNode: index={}", gpr->GetIndex()); + Node zerop = Operation(OperationCode::LogicalIEqual, std::move(value), + Immediate(gpr->GetIndex())); + SetInternalFlag(bb, InternalFlag::Zero, std::move(zerop)); + } + break; + + default: + Node zerop = Operation(OperationCode::LogicalIEqual, std::move(value), Immediate(0)); + SetInternalFlag(bb, InternalFlag::Zero, std::move(zerop)); + LOG_WARNING(HW_GPU, "Node Type: {}", value->index()); + break; + } +} + +void ShaderIR::SearchOperands(NodeBlock& nb, Node var) { + const auto* op = std::get_if<OperationNode>(var.get()); + if (op == nullptr) { + return; + } + + if (op->GetOperandsCount() == 0) { + return; + } + + for (auto& operand : op->GetOperands()) { + switch (operand->index()) { + case 0: // Operation Node + return SearchOperands(nb, operand); + case 2: // General Purpose Node + if (const auto* gpr = std::get_if<GprNode>(operand.get())) { + LOG_DEBUG(HW_GPU, "Child GprNode: index={}", gpr->GetIndex()); + Node zerop = Operation(OperationCode::LogicalIEqual, std::move(operand), + Immediate(gpr->GetIndex())); + SetInternalFlag(nb, InternalFlag::Zero, std::move(zerop)); + } + break; + default: + LOG_WARNING(HW_GPU, "Child Node Type: {}", operand->index()); + break; + } + } } Node ShaderIR::BitfieldExtract(Node value, u32 offset, u32 bits) { diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index 0c6ab0f07..0afa39531 100644 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h @@ -346,6 +346,9 @@ private: /// Access a bindless image sampler. ImageEntry& GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type); + /// Recursive Iteration over the OperationNode operands, searching for GprNodes. + void SearchOperands(NodeBlock& nb, Node var); + /// Extracts a sequence of bits from a node Node BitfieldExtract(Node value, u32 offset, u32 bits); diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp index ce8fcfe0a..b23424523 100644 --- a/src/video_core/texture_cache/util.cpp +++ b/src/video_core/texture_cache/util.cpp @@ -679,7 +679,7 @@ u32 CalculateLayerSize(const ImageInfo& info) noexcept { } std::array<u32, MAX_MIP_LEVELS> CalculateMipLevelOffsets(const ImageInfo& info) noexcept { - ASSERT(info.resources.levels <= MAX_MIP_LEVELS); + ASSERT(info.resources.levels <= static_cast<s32>(MAX_MIP_LEVELS)); const LevelInfo level_info = MakeLevelInfo(info); std::array<u32, MAX_MIP_LEVELS> offsets{}; u32 offset = 0; diff --git a/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp index 8d10ac29e..7a9d00d4f 100644 --- a/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp +++ b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp @@ -12,21 +12,12 @@ #include <fmt/format.h> -#define VK_NO_PROTOTYPES -#include <vulkan/vulkan.h> - -#include <GFSDK_Aftermath.h> -#include <GFSDK_Aftermath_Defines.h> -#include <GFSDK_Aftermath_GpuCrashDump.h> -#include <GFSDK_Aftermath_GpuCrashDumpDecoding.h> - #include "common/common_paths.h" #include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" #include "common/scope_exit.h" - -#include "video_core/renderer_vulkan/nsight_aftermath_tracker.h" +#include "video_core/vulkan_common/nsight_aftermath_tracker.h" namespace Vulkan { @@ -53,7 +44,7 @@ NsightAftermathTracker::NsightAftermathTracker() { !dl.GetSymbol("GFSDK_Aftermath_GpuCrashDump_GetJSON", &GFSDK_Aftermath_GpuCrashDump_GetJSON)) { LOG_ERROR(Render_Vulkan, "Failed to load Nsight Aftermath function pointers"); - return false; + return; } dump_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir) + "gpucrash"; diff --git a/src/video_core/vulkan_common/nsight_aftermath_tracker.h b/src/video_core/vulkan_common/nsight_aftermath_tracker.h index cee3847fb..1ce8d4e8e 100644 --- a/src/video_core/vulkan_common/nsight_aftermath_tracker.h +++ b/src/video_core/vulkan_common/nsight_aftermath_tracker.h @@ -8,8 +8,9 @@ #include <string> #include <vector> -#define VK_NO_PROTOTYPES -#include <vulkan/vulkan.h> +#include "common/common_types.h" +#include "common/dynamic_library.h" +#include "video_core/vulkan_common/vulkan_wrapper.h" #ifdef HAS_NSIGHT_AFTERMATH #include <GFSDK_Aftermath_Defines.h> @@ -17,9 +18,6 @@ #include <GFSDK_Aftermath_GpuCrashDumpDecoding.h> #endif -#include "common/common_types.h" -#include "common/dynamic_library.h" - namespace Vulkan { class NsightAftermathTracker { diff --git a/src/video_core/vulkan_common/vulkan_debug_callback.h b/src/video_core/vulkan_common/vulkan_debug_callback.h index 2efcd244c..b0519f132 100644 --- a/src/video_core/vulkan_common/vulkan_debug_callback.h +++ b/src/video_core/vulkan_common/vulkan_debug_callback.h @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#pragma once + #include "video_core/vulkan_common/vulkan_wrapper.h" namespace Vulkan { diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 37d7b45a3..5b4209c72 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -84,21 +84,6 @@ VkFormatFeatureFlags GetFormatFeatures(VkFormatProperties properties, FormatType } } -[[nodiscard]] bool IsRDNA(std::string_view device_name, VkDriverIdKHR driver_id) { - static constexpr std::array RDNA_DEVICES{ - "5700", - "5600", - "5500", - "5300", - }; - if (driver_id != VK_DRIVER_ID_AMD_PROPRIETARY_KHR) { - return false; - } - return std::any_of(RDNA_DEVICES.begin(), RDNA_DEVICES.end(), [device_name](const char* name) { - return device_name.find(name) != std::string_view::npos; - }); -} - std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::PhysicalDevice physical) { static constexpr std::array formats{ VK_FORMAT_A8B8G8R8_UNORM_PACK32, @@ -436,14 +421,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR "Blacklisting RADV for VK_EXT_extended_dynamic state, likely due to a bug in yuzu"); ext_extended_dynamic_state = false; } - if (ext_extended_dynamic_state && IsRDNA(properties.deviceName, driver_id)) { - // AMD's proprietary driver supports VK_EXT_extended_dynamic_state but on RDNA devices it - // seems to cause stability issues - LOG_WARNING( - Render_Vulkan, - "Blacklisting AMD proprietary on RDNA devices from VK_EXT_extended_dynamic_state"); - ext_extended_dynamic_state = false; - } graphics_queue = logical.GetQueue(graphics_family); present_queue = logical.GetQueue(present_family); 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 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <bit> +#include <optional> +#include <vector> + +#include "common/alignment.h" +#include "common/assert.h" +#include "common/common_types.h" +#include "common/logging/log.h" +#include "video_core/vulkan_common/vulkan_device.h" +#include "video_core/vulkan_common/vulkan_memory_allocator.h" +#include "video_core/vulkan_common/vulkan_wrapper.h" + +namespace Vulkan { +namespace { +struct Range { + u64 begin; + u64 end; + + [[nodiscard]] bool Contains(u64 iterator, u64 size) const noexcept { + return iterator < end && begin < iterator + size; + } +}; + +[[nodiscard]] u64 AllocationChunkSize(u64 required_size) { + static constexpr std::array sizes{ + 0x1000ULL << 10, 0x1400ULL << 10, 0x1800ULL << 10, 0x1c00ULL << 10, 0x2000ULL << 10, + 0x3200ULL << 10, 0x4000ULL << 10, 0x6000ULL << 10, 0x8000ULL << 10, 0xA000ULL << 10, + 0x10000ULL << 10, 0x18000ULL << 10, 0x20000ULL << 10, + }; + static_assert(std::is_sorted(sizes.begin(), sizes.end())); + + const auto it = std::ranges::lower_bound(sizes, required_size); + return it != sizes.end() ? *it : Common::AlignUp(required_size, 4ULL << 20); +} + +[[nodiscard]] VkMemoryPropertyFlags MemoryUsagePropertyFlags(MemoryUsage usage) { + switch (usage) { + case MemoryUsage::DeviceLocal: + return VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + case MemoryUsage::Upload: + return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + case MemoryUsage::Download: + return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | + VK_MEMORY_PROPERTY_HOST_CACHED_BIT; + } + UNREACHABLE_MSG("Invalid memory usage={}", usage); + return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; +} +} // Anonymous namespace + +class MemoryAllocation { +public: + explicit MemoryAllocation(const Device& device_, vk::DeviceMemory memory_, + VkMemoryPropertyFlags properties, u64 allocation_size_, u32 type) + : device{device_}, memory{std::move(memory_)}, allocation_size{allocation_size_}, + property_flags{properties}, shifted_memory_type{1U << type} {} + + [[nodiscard]] std::optional<MemoryCommit> Commit(VkDeviceSize size, VkDeviceSize alignment) { + const std::optional<u64> alloc = FindFreeRegion(size, alignment); + if (!alloc) { + // Signal out of memory, it'll try to do more allocations. + return std::nullopt; + } + const Range range{ + .begin = *alloc, + .end = *alloc + size, + }; + commits.insert(std::ranges::upper_bound(commits, *alloc, {}, &Range::begin), range); + return std::make_optional<MemoryCommit>(this, *memory, *alloc, *alloc + size); + } + + void Free(u64 begin) { + const auto it = std::ranges::find(commits, begin, &Range::begin); + ASSERT_MSG(it != commits.end(), "Invalid commit"); + commits.erase(it); + } + + [[nodiscard]] std::span<u8> Map() { + if (memory_mapped_span.empty()) { + u8* const raw_pointer = memory.Map(0, allocation_size); + memory_mapped_span = std::span<u8>(raw_pointer, allocation_size); + } + return memory_mapped_span; + } + + /// Returns whether this allocation is compatible with the arguments. + [[nodiscard]] bool IsCompatible(VkMemoryPropertyFlags flags, u32 type_mask) const { + return (flags & property_flags) && (type_mask & shifted_memory_type) != 0; + } + +private: + [[nodiscard]] static constexpr u32 ShiftType(u32 type) { + return 1U << type; + } + + [[nodiscard]] std::optional<u64> FindFreeRegion(u64 size, u64 alignment) noexcept { + ASSERT(std::has_single_bit(alignment)); + const u64 alignment_log2 = std::countr_zero(alignment); + std::optional<u64> candidate; + u64 iterator = 0; + auto commit = commits.begin(); + while (iterator + size <= allocation_size) { + candidate = candidate.value_or(iterator); + if (commit == commits.end()) { + break; + } + if (commit->Contains(*candidate, size)) { + candidate = std::nullopt; + } + iterator = Common::AlignUpLog2(commit->end, alignment_log2); + ++commit; + } + return candidate; + } + + const Device& device; ///< Vulkan device. + const vk::DeviceMemory memory; ///< Vulkan memory allocation handler. + const u64 allocation_size; ///< Size of this allocation. + const VkMemoryPropertyFlags property_flags; ///< Vulkan memory property flags. + const u32 shifted_memory_type; ///< Shifted Vulkan memory type. + std::vector<Range> commits; ///< All commit ranges done from this allocation. + std::span<u8> memory_mapped_span; ///< Memory mapped span. Empty if not queried before. +}; + +MemoryCommit::MemoryCommit(MemoryAllocation* allocation_, VkDeviceMemory memory_, u64 begin_, + u64 end_) noexcept + : allocation{allocation_}, memory{memory_}, begin{begin_}, end{end_} {} + +MemoryCommit::~MemoryCommit() { + Release(); +} + +MemoryCommit& MemoryCommit::operator=(MemoryCommit&& rhs) noexcept { + Release(); + allocation = std::exchange(rhs.allocation, nullptr); + memory = rhs.memory; + begin = rhs.begin; + end = rhs.end; + span = std::exchange(rhs.span, std::span<u8>{}); + return *this; +} + +MemoryCommit::MemoryCommit(MemoryCommit&& rhs) noexcept + : allocation{std::exchange(rhs.allocation, nullptr)}, memory{rhs.memory}, begin{rhs.begin}, + end{rhs.end}, span{std::exchange(rhs.span, std::span<u8>{})} {} + +std::span<u8> MemoryCommit::Map() { + if (span.empty()) { + span = allocation->Map().subspan(begin, end - begin); + } + return span; +} + +void MemoryCommit::Release() { + if (allocation) { + allocation->Free(begin); + } +} + +MemoryAllocator::MemoryAllocator(const Device& device_) + : device{device_}, properties{device_.GetPhysical().GetMemoryProperties()} {} + +MemoryAllocator::~MemoryAllocator() = default; + +MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements& requirements, MemoryUsage usage) { + // Find the fastest memory flags we can afford with the current requirements + const VkMemoryPropertyFlags flags = MemoryPropertyFlags(requirements.memoryTypeBits, usage); + if (std::optional<MemoryCommit> commit = TryCommit(requirements, flags)) { + return std::move(*commit); + } + // Commit has failed, allocate more memory. + // TODO(Rodrigo): Handle out of memory situations in some way like flushing to guest memory. + AllocMemory(flags, requirements.memoryTypeBits, AllocationChunkSize(requirements.size)); + + // Commit again, this time it won't fail since there's a fresh allocation above. + // If it does, there's a bug. + return TryCommit(requirements, flags).value(); +} + +MemoryCommit MemoryAllocator::Commit(const vk::Buffer& buffer, MemoryUsage usage) { + auto commit = Commit(device.GetLogical().GetBufferMemoryRequirements(*buffer), usage); + buffer.BindMemory(commit.Memory(), commit.Offset()); + return commit; +} + +MemoryCommit MemoryAllocator::Commit(const vk::Image& image, MemoryUsage usage) { + auto commit = Commit(device.GetLogical().GetImageMemoryRequirements(*image), usage); + image.BindMemory(commit.Memory(), commit.Offset()); + return commit; +} + +void MemoryAllocator::AllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size) { + const u32 type = FindType(flags, type_mask).value(); + vk::DeviceMemory memory = device.GetLogical().AllocateMemory({ + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .pNext = nullptr, + .allocationSize = size, + .memoryTypeIndex = type, + }); + allocations.push_back( + std::make_unique<MemoryAllocation>(device, std::move(memory), flags, size, type)); +} + +std::optional<MemoryCommit> MemoryAllocator::TryCommit(const VkMemoryRequirements& requirements, + VkMemoryPropertyFlags flags) { + for (auto& allocation : allocations) { + if (!allocation->IsCompatible(flags, requirements.memoryTypeBits)) { + continue; + } + if (auto commit = allocation->Commit(requirements.size, requirements.alignment)) { + return commit; + } + } + return std::nullopt; +} + +VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask, MemoryUsage usage) const { + return MemoryPropertyFlags(type_mask, MemoryUsagePropertyFlags(usage)); +} + +VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask, + VkMemoryPropertyFlags flags) const { + if (FindType(flags, type_mask)) { + // Found a memory type with those requirements + return flags; + } + if (flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) { + // Remove host cached bit in case it's not supported + return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_HOST_CACHED_BIT); + } + if (flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) { + // Remove device local, if it's not supported by the requested resource + return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + } + UNREACHABLE_MSG("No compatible memory types found"); + return 0; +} + +std::optional<u32> MemoryAllocator::FindType(VkMemoryPropertyFlags flags, u32 type_mask) const { + for (u32 type_index = 0; type_index < properties.memoryTypeCount; ++type_index) { + const VkMemoryPropertyFlags type_flags = properties.memoryTypes[type_index].propertyFlags; + if ((type_mask & (1U << type_index)) && (type_flags & flags)) { + // The type matches in type and in the wanted properties. + return type_index; + } + } + // Failed to find index + return std::nullopt; +} + +bool IsHostVisible(MemoryUsage usage) noexcept { + switch (usage) { + case MemoryUsage::DeviceLocal: + return false; + case MemoryUsage::Upload: + case MemoryUsage::Download: + return true; + } + UNREACHABLE_MSG("Invalid memory usage={}", usage); + return false; +} + +} // 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..9e6cfabf9 --- /dev/null +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.h @@ -0,0 +1,117 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <span> +#include <utility> +#include <vector> +#include "common/common_types.h" +#include "video_core/vulkan_common/vulkan_wrapper.h" + +namespace Vulkan { + +class Device; +class MemoryMap; +class MemoryAllocation; + +/// Hints and requirements for the backing memory type of a commit +enum class MemoryUsage { + DeviceLocal, ///< Hints device local usages, fastest memory type to read and write from the GPU + Upload, ///< Requires a host visible memory type optimized for CPU to GPU uploads + Download, ///< Requires a host visible memory type optimized for GPU to CPU readbacks +}; + +/// Ownership handle of a memory commitment. +/// Points to a subregion of a memory allocation. +class MemoryCommit { +public: + explicit MemoryCommit() noexcept = default; + explicit MemoryCommit(MemoryAllocation* allocation_, VkDeviceMemory memory_, u64 begin_, + u64 end_) noexcept; + ~MemoryCommit(); + + MemoryCommit& operator=(MemoryCommit&&) noexcept; + MemoryCommit(MemoryCommit&&) noexcept; + + MemoryCommit& operator=(const MemoryCommit&) = delete; + MemoryCommit(const MemoryCommit&) = delete; + + /// Returns a host visible memory map. + /// It will map the backing allocation if it hasn't been mapped before. + std::span<u8> Map(); + + /// Returns the Vulkan memory handler. + VkDeviceMemory Memory() const { + return memory; + } + + /// Returns the start position of the commit relative to the allocation. + VkDeviceSize Offset() const { + return static_cast<VkDeviceSize>(begin); + } + +private: + void Release(); + + MemoryAllocation* allocation{}; ///< Pointer to the large memory allocation. + VkDeviceMemory memory{}; ///< Vulkan device memory handler. + u64 begin{}; ///< Beginning offset in bytes to where the commit exists. + u64 end{}; ///< Offset in bytes where the commit ends. + std::span<u8> span; ///< Host visible memory span. Empty if not queried before. +}; + +/// Memory allocator container. +/// Allocates and releases memory allocations on demand. +class MemoryAllocator { +public: + explicit MemoryAllocator(const Device& device_); + ~MemoryAllocator(); + + MemoryAllocator& operator=(const MemoryAllocator&) = delete; + MemoryAllocator(const MemoryAllocator&) = delete; + + /** + * Commits a memory with the specified requirements. + * + * @param requirements Requirements returned from a Vulkan call. + * @param usage Indicates how the memory will be used. + * + * @returns A memory commit. + */ + MemoryCommit Commit(const VkMemoryRequirements& requirements, MemoryUsage usage); + + /// Commits memory required by the buffer and binds it. + MemoryCommit Commit(const vk::Buffer& buffer, MemoryUsage usage); + + /// Commits memory required by the image and binds it. + MemoryCommit Commit(const vk::Image& image, MemoryUsage usage); + +private: + /// Allocates a chunk of memory. + void AllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size); + + /// Tries to allocate a memory commit. + std::optional<MemoryCommit> TryCommit(const VkMemoryRequirements& requirements, + VkMemoryPropertyFlags flags); + + /// Returns the fastest compatible memory property flags from a wanted usage. + VkMemoryPropertyFlags MemoryPropertyFlags(u32 type_mask, MemoryUsage usage) const; + + /// Returns the fastest compatible memory property flags from the wanted flags. + VkMemoryPropertyFlags MemoryPropertyFlags(u32 type_mask, VkMemoryPropertyFlags flags) const; + + /// Returns index to the fastest memory type compatible with the passed requirements. + std::optional<u32> FindType(VkMemoryPropertyFlags flags, u32 type_mask) const; + + const Device& device; ///< Device handle. + const VkPhysicalDeviceMemoryProperties properties; ///< Physical device properties. + std::vector<std::unique_ptr<MemoryAllocation>> allocations; ///< Current allocations. +}; + +/// Returns true when a memory usage is guaranteed to be host visible. +bool IsHostVisible(MemoryUsage usage) noexcept; + +} // namespace Vulkan diff --git a/src/yuzu/applets/profile_select.cpp b/src/yuzu/applets/profile_select.cpp index 4bf2bfd40..0a4c48b3d 100644 --- a/src/yuzu/applets/profile_select.cpp +++ b/src/yuzu/applets/profile_select.cpp @@ -93,7 +93,7 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent) const auto& profiles = profile_manager->GetAllUsers(); for (const auto& user : profiles) { - Service::Account::ProfileBase profile; + Service::Account::ProfileBase profile{}; if (!profile_manager->GetProfileBase(user, profile)) continue; diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index e6c8f18af..4528eb196 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -394,7 +394,7 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) { input_subsystem->GetMouse()->PressButton(x, y, event->button()); if (event->button() == Qt::LeftButton) { - this->TouchPressed(x, y); + this->TouchPressed(x, y, 0); } emit MouseActivity(); @@ -409,7 +409,7 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { auto pos = event->pos(); const auto [x, y] = ScaleTouch(pos); input_subsystem->GetMouse()->MouseMove(x, y); - this->TouchMoved(x, y); + this->TouchMoved(x, y, 0); emit MouseActivity(); } @@ -423,36 +423,72 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { input_subsystem->GetMouse()->ReleaseButton(event->button()); if (event->button() == Qt::LeftButton) { - this->TouchReleased(); + this->TouchReleased(0); } } void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { - // TouchBegin always has exactly one touch point, so take the .first() - const auto [x, y] = ScaleTouch(event->touchPoints().first().pos()); - this->TouchPressed(x, y); + QList<QTouchEvent::TouchPoint> touch_points = event->touchPoints(); + for (const auto& touch_point : touch_points) { + if (!TouchUpdate(touch_point)) { + TouchStart(touch_point); + } + } } void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) { - QPointF pos; - int active_points = 0; - - // average all active touch points - for (const auto& tp : event->touchPoints()) { - if (tp.state() & (Qt::TouchPointPressed | Qt::TouchPointMoved | Qt::TouchPointStationary)) { - active_points++; - pos += tp.pos(); + QList<QTouchEvent::TouchPoint> touch_points = event->touchPoints(); + for (const auto& touch_point : touch_points) { + if (!TouchUpdate(touch_point)) { + TouchStart(touch_point); } } + // Release all inactive points + for (std::size_t id = 0; id < touch_ids.size(); ++id) { + if (!TouchExist(touch_ids[id], touch_points)) { + touch_ids[id] = 0; + this->TouchReleased(id + 1); + } + } +} - pos /= active_points; +void GRenderWindow::TouchEndEvent() { + for (std::size_t id = 0; id < touch_ids.size(); ++id) { + if (touch_ids[id] != 0) { + touch_ids[id] = 0; + this->TouchReleased(id + 1); + } + } +} - const auto [x, y] = ScaleTouch(pos); - this->TouchMoved(x, y); +bool GRenderWindow::TouchStart(const QTouchEvent::TouchPoint& touch_point) { + for (std::size_t id = 0; id < touch_ids.size(); ++id) { + if (touch_ids[id] == 0) { + touch_ids[id] = touch_point.id() + 1; + const auto [x, y] = ScaleTouch(touch_point.pos()); + this->TouchPressed(x, y, id + 1); + return true; + } + } + return false; } -void GRenderWindow::TouchEndEvent() { - this->TouchReleased(); +bool GRenderWindow::TouchUpdate(const QTouchEvent::TouchPoint& touch_point) { + for (std::size_t id = 0; id < touch_ids.size(); ++id) { + if (touch_ids[id] == static_cast<std::size_t>(touch_point.id() + 1)) { + const auto [x, y] = ScaleTouch(touch_point.pos()); + this->TouchMoved(x, y, id + 1); + return true; + } + } + return false; +} + +bool GRenderWindow::TouchExist(std::size_t id, + const QList<QTouchEvent::TouchPoint>& touch_points) const { + return std::any_of(touch_points.begin(), touch_points.end(), [id](const auto& point) { + return id == static_cast<std::size_t>(point.id() + 1); + }); } bool GRenderWindow::event(QEvent* event) { diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 339095509..b5ec7de07 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -11,6 +11,7 @@ #include <QImage> #include <QThread> +#include <QTouchEvent> #include <QWidget> #include <QWindow> @@ -21,7 +22,6 @@ class GRenderWindow; class GMainWindow; class QKeyEvent; -class QTouchEvent; class QStringList; namespace InputCommon { @@ -191,6 +191,10 @@ private: void TouchUpdateEvent(const QTouchEvent* event); void TouchEndEvent(); + bool TouchStart(const QTouchEvent::TouchPoint& touch_point); + bool TouchUpdate(const QTouchEvent::TouchPoint& touch_point); + bool TouchExist(std::size_t id, const QList<QTouchEvent::TouchPoint>& touch_points) const; + void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override; bool InitializeOpenGL(); @@ -215,6 +219,8 @@ private: bool first_frame = false; + std::array<std::size_t, 16> touch_ids{}; + protected: void showEvent(QShowEvent* event) override; bool eventFilter(QObject* object, QEvent* event) override; diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index cda448718..8d85a1986 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -464,13 +464,7 @@ void Config::ReadMouseValues() { void Config::ReadTouchscreenValues() { Settings::values.touchscreen.enabled = ReadSetting(QStringLiteral("touchscreen_enabled"), true).toBool(); - Settings::values.touchscreen.device = - ReadSetting(QStringLiteral("touchscreen_device"), QStringLiteral("engine:emu_window")) - .toString() - .toStdString(); - Settings::values.touchscreen.finger = - ReadSetting(QStringLiteral("touchscreen_finger"), 0).toUInt(); Settings::values.touchscreen.rotation_angle = ReadSetting(QStringLiteral("touchscreen_angle"), 0).toUInt(); Settings::values.touchscreen.diameter_x = @@ -563,7 +557,8 @@ void Config::ReadMotionTouchValues() { .toString() .toStdString(); Settings::values.touch_device = - ReadSetting(QStringLiteral("touch_device"), QStringLiteral("engine:emu_window")) + ReadSetting(QStringLiteral("touch_device"), + QStringLiteral("min_x:100,min_y:50,max_x:1800,max_y:850")) .toString() .toStdString(); Settings::values.use_touch_from_button = @@ -1005,7 +1000,8 @@ void Config::SavePlayerValue(std::size_t player_index) { static_cast<u8>(Settings::ControllerType::ProController)); if (!player_prefix.isEmpty()) { - WriteSetting(QStringLiteral("%1connected").arg(player_prefix), player.connected, false); + WriteSetting(QStringLiteral("%1connected").arg(player_prefix), player.connected, + player_index == 0); WriteSetting(QStringLiteral("%1vibration_enabled").arg(player_prefix), player.vibration_enabled, true); WriteSetting(QStringLiteral("%1vibration_strength").arg(player_prefix), @@ -1087,10 +1083,7 @@ void Config::SaveTouchscreenValues() { const auto& touchscreen = Settings::values.touchscreen; WriteSetting(QStringLiteral("touchscreen_enabled"), touchscreen.enabled, true); - WriteSetting(QStringLiteral("touchscreen_device"), QString::fromStdString(touchscreen.device), - QStringLiteral("engine:emu_window")); - WriteSetting(QStringLiteral("touchscreen_finger"), touchscreen.finger, 0); WriteSetting(QStringLiteral("touchscreen_angle"), touchscreen.rotation_angle, 0); WriteSetting(QStringLiteral("touchscreen_diameter_x"), touchscreen.diameter_x, 15); WriteSetting(QStringLiteral("touchscreen_diameter_y"), touchscreen.diameter_y, 15); diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 46ea026e4..13f0351d4 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -575,6 +575,16 @@ void ConfigureInputPlayer::ApplyConfiguration() { std::transform(motions_param.begin(), motions_param.end(), motions.begin(), [](const Common::ParamPackage& param) { return param.Serialize(); }); + + // Apply configuration for handheld + if (player_index == 0) { + auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; + if (player.controller_type == Settings::ControllerType::Handheld) { + handheld = player; + } + handheld.connected = ui->groupConnectedController->isChecked() && + player.controller_type == Settings::ControllerType::Handheld; + } } void ConfigureInputPlayer::TryConnectSelectedController() { diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp index caaa85930..1f2b792e4 100644 --- a/src/yuzu/configuration/configure_motion_touch.cpp +++ b/src/yuzu/configuration/configure_motion_touch.cpp @@ -81,19 +81,11 @@ void CalibrationConfigurationDialog::UpdateButtonText(const QString& text) { cancel_button->setText(text); } -constexpr std::array<std::pair<const char*, const char*>, 2> TouchProviders = {{ - {"emu_window", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "Emulator Window")}, - {"cemuhookudp", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "CemuhookUDP")}, -}}; - ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_) : QDialog(parent), input_subsystem{input_subsystem_}, ui(std::make_unique<Ui::ConfigureMotionTouch>()) { ui->setupUi(this); - for (const auto& [provider, name] : TouchProviders) { - ui->touch_provider->addItem(tr(name), QString::fromUtf8(provider)); - } ui->udp_learn_more->setOpenExternalLinks(true); ui->udp_learn_more->setText( @@ -112,10 +104,7 @@ ConfigureMotionTouch::~ConfigureMotionTouch() = default; void ConfigureMotionTouch::SetConfiguration() { const Common::ParamPackage motion_param(Settings::values.motion_device); const Common::ParamPackage touch_param(Settings::values.touch_device); - const std::string touch_engine = touch_param.Get("engine", "emu_window"); - ui->touch_provider->setCurrentIndex( - ui->touch_provider->findData(QString::fromStdString(touch_engine))); ui->touch_from_button_checkbox->setChecked(Settings::values.use_touch_from_button); touch_from_button_maps = Settings::values.touch_from_button_maps; for (const auto& touch_map : touch_from_button_maps) { @@ -148,30 +137,21 @@ void ConfigureMotionTouch::SetConfiguration() { } void ConfigureMotionTouch::UpdateUiDisplay() { - const QString touch_engine = ui->touch_provider->currentData().toString(); const QString cemuhook_udp = QStringLiteral("cemuhookudp"); ui->motion_sensitivity_label->setVisible(true); ui->motion_sensitivity->setVisible(true); - if (touch_engine == cemuhook_udp) { - ui->touch_calibration->setVisible(true); - ui->touch_calibration_config->setVisible(true); - ui->touch_calibration_label->setVisible(true); - ui->touch_calibration->setText( - QStringLiteral("(%1, %2) - (%3, %4)").arg(min_x).arg(min_y).arg(max_x).arg(max_y)); - } else { - ui->touch_calibration->setVisible(false); - ui->touch_calibration_config->setVisible(false); - ui->touch_calibration_label->setVisible(false); - } + ui->touch_calibration->setVisible(true); + ui->touch_calibration_config->setVisible(true); + ui->touch_calibration_label->setVisible(true); + ui->touch_calibration->setText( + QStringLiteral("(%1, %2) - (%3, %4)").arg(min_x).arg(min_y).arg(max_x).arg(max_y)); ui->udp_config_group_box->setVisible(true); } void ConfigureMotionTouch::ConnectEvents() { - connect(ui->touch_provider, qOverload<int>(&QComboBox::currentIndexChanged), this, - [this](int index) { UpdateUiDisplay(); }); connect(ui->udp_test, &QPushButton::clicked, this, &ConfigureMotionTouch::OnCemuhookUDPTest); connect(ui->udp_add, &QPushButton::clicked, this, &ConfigureMotionTouch::OnUDPAddServer); connect(ui->udp_remove, &QPushButton::clicked, this, &ConfigureMotionTouch::OnUDPDeleteServer); @@ -327,16 +307,11 @@ void ConfigureMotionTouch::ApplyConfiguration() { return; } - std::string touch_engine = ui->touch_provider->currentData().toString().toStdString(); - Common::ParamPackage touch_param{}; - if (touch_engine == "cemuhookudp") { - touch_param.Set("min_x", min_x); - touch_param.Set("min_y", min_y); - touch_param.Set("max_x", max_x); - touch_param.Set("max_y", max_y); - } - touch_param.Set("engine", std::move(touch_engine)); + touch_param.Set("min_x", min_x); + touch_param.Set("min_y", min_y); + touch_param.Set("max_x", max_x); + touch_param.Set("max_y", max_y); Settings::values.touch_device = touch_param.Serialize(); Settings::values.use_touch_from_button = ui->touch_from_button_checkbox->isChecked(); diff --git a/src/yuzu/configuration/configure_motion_touch.ui b/src/yuzu/configuration/configure_motion_touch.ui index ebca835ac..1e35ea946 100644 --- a/src/yuzu/configuration/configure_motion_touch.ui +++ b/src/yuzu/configuration/configure_motion_touch.ui @@ -68,23 +68,9 @@ <item> <layout class="QHBoxLayout"> <item> - <widget class="QLabel" name="touch_provider_label"> - <property name="text"> - <string>Touch Provider:</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="touch_provider"/> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout"> - <item> <widget class="QLabel" name="touch_calibration_label"> <property name="text"> - <string>Calibration:</string> + <string>UDP Calibration:</string> </property> </widget> </item> diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp index 13d9a4757..d102a43af 100644 --- a/src/yuzu/configuration/configure_profile_manager.cpp +++ b/src/yuzu/configuration/configure_profile_manager.cpp @@ -40,7 +40,7 @@ QString GetImagePath(Common::UUID uuid) { } QString GetAccountUsername(const Service::Account::ProfileManager& manager, Common::UUID uuid) { - Service::Account::ProfileBase profile; + Service::Account::ProfileBase profile{}; if (!manager.GetProfileBase(uuid, profile)) { return {}; } @@ -147,7 +147,7 @@ void ConfigureProfileManager::SetConfiguration() { void ConfigureProfileManager::PopulateUserList() { const auto& profiles = profile_manager->GetAllUsers(); for (const auto& user : profiles) { - Service::Account::ProfileBase profile; + Service::Account::ProfileBase profile{}; if (!profile_manager->GetProfileBase(user, profile)) continue; @@ -212,7 +212,7 @@ void ConfigureProfileManager::RenameUser() { const auto uuid = profile_manager->GetUser(user); ASSERT(uuid); - Service::Account::ProfileBase profile; + Service::Account::ProfileBase profile{}; if (!profile_manager->GetProfileBase(*uuid, profile)) return; diff --git a/src/yuzu/configuration/configure_service.cpp b/src/yuzu/configuration/configure_service.cpp index 0de7a4f0b..b580cfff2 100644 --- a/src/yuzu/configuration/configure_service.cpp +++ b/src/yuzu/configuration/configure_service.cpp @@ -9,6 +9,7 @@ #include "ui_configure_service.h" #include "yuzu/configuration/configure_service.h" +#ifdef YUZU_ENABLE_BOXCAT namespace { QString FormatEventStatusString(const Service::BCAT::EventStatus& status) { QString out; @@ -32,6 +33,7 @@ QString FormatEventStatusString(const Service::BCAT::EventStatus& status) { return out; } } // Anonymous namespace +#endif ConfigureService::ConfigureService(QWidget* parent) : QWidget(parent), ui(std::make_unique<Ui::ConfigureService>()) { diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.cpp b/src/yuzu/configuration/configure_touchscreen_advanced.cpp index 7d7cc00b7..29c86c7bc 100644 --- a/src/yuzu/configuration/configure_touchscreen_advanced.cpp +++ b/src/yuzu/configuration/configure_touchscreen_advanced.cpp @@ -33,21 +33,18 @@ void ConfigureTouchscreenAdvanced::RetranslateUI() { } void ConfigureTouchscreenAdvanced::ApplyConfiguration() { - Settings::values.touchscreen.finger = ui->finger_box->value(); Settings::values.touchscreen.diameter_x = ui->diameter_x_box->value(); Settings::values.touchscreen.diameter_y = ui->diameter_y_box->value(); Settings::values.touchscreen.rotation_angle = ui->angle_box->value(); } void ConfigureTouchscreenAdvanced::LoadConfiguration() { - ui->finger_box->setValue(Settings::values.touchscreen.finger); ui->diameter_x_box->setValue(Settings::values.touchscreen.diameter_x); ui->diameter_y_box->setValue(Settings::values.touchscreen.diameter_y); ui->angle_box->setValue(Settings::values.touchscreen.rotation_angle); } void ConfigureTouchscreenAdvanced::RestoreDefaults() { - ui->finger_box->setValue(0); ui->diameter_x_box->setValue(15); ui->diameter_y_box->setValue(15); ui->angle_box->setValue(0); diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.ui b/src/yuzu/configuration/configure_touchscreen_advanced.ui index 30ceccddb..88e7cf050 100644 --- a/src/yuzu/configuration/configure_touchscreen_advanced.ui +++ b/src/yuzu/configuration/configure_touchscreen_advanced.ui @@ -65,20 +65,13 @@ </property> </spacer> </item> - <item row="2" column="1"> + <item row="1" column="1"> <widget class="QLabel" name="label_4"> <property name="text"> <string>Touch Diameter Y</string> </property> </widget> </item> - <item row="0" column="1"> - <widget class="QLabel" name="label"> - <property name="text"> - <string>Finger</string> - </property> - </widget> - </item> <item row="0" column="3"> <spacer name="horizontalSpacer_2"> <property name="orientation"> @@ -92,37 +85,27 @@ </property> </spacer> </item> - <item row="1" column="1"> + <item row="0" column="1"> <widget class="QLabel" name="label_3"> <property name="text"> <string>Touch Diameter X</string> </property> </widget> </item> - <item row="0" column="2"> - <widget class="QSpinBox" name="finger_box"> - <property name="minimumSize"> - <size> - <width>80</width> - <height>0</height> - </size> - </property> - </widget> - </item> - <item row="3" column="1"> + <item row="2" column="1"> <widget class="QLabel" name="label_5"> <property name="text"> <string>Rotational Angle</string> </property> </widget> </item> - <item row="1" column="2"> + <item row="0" column="2"> <widget class="QSpinBox" name="diameter_x_box"/> </item> - <item row="2" column="2"> + <item row="1" column="2"> <widget class="QSpinBox" name="diameter_y_box"/> </item> - <item row="3" column="2"> + <item row="2" column="2"> <widget class="QSpinBox" name="angle_box"/> </item> </layout> diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 37b0d1a0e..9afd5b45f 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -173,8 +173,8 @@ void GameList::OnItemExpanded(const QModelIndex& item) { return; } - auto* game_dir = item.data(GameListDir::GameDirRole).value<UISettings::GameDir*>(); - game_dir->expanded = tree_view->isExpanded(item); + UISettings::values.game_dirs[item.data(GameListDir::GameDirRole).toInt()].expanded = + tree_view->isExpanded(item); } // Event in order to filter the gamelist after editing the searchfield @@ -262,9 +262,9 @@ void GameList::OnUpdateThemedIcons() { Qt::DecorationRole); break; case GameListItemType::CustomDir: { - const UISettings::GameDir* game_dir = - child->data(GameListDir::GameDirRole).value<UISettings::GameDir*>(); - const QString icon_name = QFileInfo::exists(game_dir->path) + const UISettings::GameDir& game_dir = + UISettings::values.game_dirs[child->data(GameListDir::GameDirRole).toInt()]; + const QString icon_name = QFileInfo::exists(game_dir.path) ? QStringLiteral("folder") : QStringLiteral("bad_folder"); child->setData( @@ -366,7 +366,7 @@ void GameList::AddDirEntry(GameListDir* entry_items) { item_model->invisibleRootItem()->appendRow(entry_items); tree_view->setExpanded( entry_items->index(), - entry_items->data(GameListDir::GameDirRole).value<UISettings::GameDir*>()->expanded); + UISettings::values.game_dirs[entry_items->data(GameListDir::GameDirRole).toInt()].expanded); } void GameList::AddEntry(const QList<QStandardItem*>& entry_items, GameListDir* parent) { @@ -549,7 +549,7 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri void GameList::AddCustomDirPopup(QMenu& context_menu, QModelIndex selected) { UISettings::GameDir& game_dir = - *selected.data(GameListDir::GameDirRole).value<UISettings::GameDir*>(); + UISettings::values.game_dirs[selected.data(GameListDir::GameDirRole).toInt()]; QAction* deep_scan = context_menu.addAction(tr("Scan Subfolders")); QAction* delete_dir = context_menu.addAction(tr("Remove Game Directory")); @@ -568,8 +568,7 @@ void GameList::AddCustomDirPopup(QMenu& context_menu, QModelIndex selected) { } void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) { - UISettings::GameDir& game_dir = - *selected.data(GameListDir::GameDirRole).value<UISettings::GameDir*>(); + const int game_dir_index = selected.data(GameListDir::GameDirRole).toInt(); QAction* move_up = context_menu.addAction(tr("\u25B2 Move Up")); QAction* move_down = context_menu.addAction(tr("\u25bc Move Down")); @@ -580,34 +579,39 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) { move_up->setEnabled(row > 0); move_down->setEnabled(row < item_model->rowCount() - 2); - connect(move_up, &QAction::triggered, [this, selected, row, &game_dir] { - // find the indices of the items in settings and swap them - std::swap(UISettings::values.game_dirs[UISettings::values.game_dirs.indexOf(game_dir)], - UISettings::values.game_dirs[UISettings::values.game_dirs.indexOf( - *selected.sibling(row - 1, 0) - .data(GameListDir::GameDirRole) - .value<UISettings::GameDir*>())]); + connect(move_up, &QAction::triggered, [this, selected, row, game_dir_index] { + const int other_index = selected.sibling(row - 1, 0).data(GameListDir::GameDirRole).toInt(); + // swap the items in the settings + std::swap(UISettings::values.game_dirs[game_dir_index], + UISettings::values.game_dirs[other_index]); + // swap the indexes held by the QVariants + item_model->setData(selected, QVariant(other_index), GameListDir::GameDirRole); + item_model->setData(selected.sibling(row - 1, 0), QVariant(game_dir_index), + GameListDir::GameDirRole); // move the treeview items QList<QStandardItem*> item = item_model->takeRow(row); item_model->invisibleRootItem()->insertRow(row - 1, item); - tree_view->setExpanded(selected, game_dir.expanded); + tree_view->setExpanded(selected, UISettings::values.game_dirs[game_dir_index].expanded); }); - connect(move_down, &QAction::triggered, [this, selected, row, &game_dir] { - // find the indices of the items in settings and swap them - std::swap(UISettings::values.game_dirs[UISettings::values.game_dirs.indexOf(game_dir)], - UISettings::values.game_dirs[UISettings::values.game_dirs.indexOf( - *selected.sibling(row + 1, 0) - .data(GameListDir::GameDirRole) - .value<UISettings::GameDir*>())]); + connect(move_down, &QAction::triggered, [this, selected, row, game_dir_index] { + const int other_index = selected.sibling(row + 1, 0).data(GameListDir::GameDirRole).toInt(); + // swap the items in the settings + std::swap(UISettings::values.game_dirs[game_dir_index], + UISettings::values.game_dirs[other_index]); + // swap the indexes held by the QVariants + item_model->setData(selected, QVariant(other_index), GameListDir::GameDirRole); + item_model->setData(selected.sibling(row + 1, 0), QVariant(game_dir_index), + GameListDir::GameDirRole); // move the treeview items const QList<QStandardItem*> item = item_model->takeRow(row); item_model->invisibleRootItem()->insertRow(row + 1, item); - tree_view->setExpanded(selected, game_dir.expanded); + tree_view->setExpanded(selected, UISettings::values.game_dirs[game_dir_index].expanded); }); - connect(open_directory_location, &QAction::triggered, - [this, game_dir] { emit OpenDirectory(game_dir.path); }); + connect(open_directory_location, &QAction::triggered, [this, game_dir_index] { + emit OpenDirectory(UISettings::values.game_dirs[game_dir_index].path); + }); } void GameList::LoadCompatibilityList() { diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index df935022d..f25445f18 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h @@ -230,7 +230,7 @@ public: setData(type(), TypeRole); UISettings::GameDir* game_dir = &directory; - setData(QVariant::fromValue(game_dir), GameDirRole); + setData(QVariant(UISettings::values.game_dirs.indexOf(directory)), GameDirRole); const int icon_size = std::min(static_cast<int>(UISettings::values.icon_size), 64); switch (dir_type) { diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 41ef6f6b8..f76102459 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -296,10 +296,6 @@ void Config::ReadValues() { sdl2_config->GetBoolean("ControlsGeneral", "motion_enabled", true)); Settings::values.touchscreen.enabled = sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true); - Settings::values.touchscreen.device = - sdl2_config->Get("ControlsGeneral", "touch_device", "engine:emu_window"); - Settings::values.touchscreen.finger = - sdl2_config->GetInteger("ControlsGeneral", "touch_finger", 0); Settings::values.touchscreen.rotation_angle = sdl2_config->GetInteger("ControlsGeneral", "touch_angle", 0); Settings::values.touchscreen.diameter_x = diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index e32bed5e6..7843d5167 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp @@ -29,16 +29,16 @@ EmuWindow_SDL2::~EmuWindow_SDL2() { } void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { - TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); + TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0), 0); input_subsystem->GetMouse()->MouseMove(x, y); } void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { if (button == SDL_BUTTON_LEFT) { if (state == SDL_PRESSED) { - TouchPressed((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); + TouchPressed((unsigned)std::max(x, 0), (unsigned)std::max(y, 0), 0); } else { - TouchReleased(); + TouchReleased(0); } } else if (button == SDL_BUTTON_RIGHT) { if (state == SDL_PRESSED) { @@ -66,16 +66,16 @@ void EmuWindow_SDL2::OnFingerDown(float x, float y) { // 3DS does const auto [px, py] = TouchToPixelPos(x, y); - TouchPressed(px, py); + TouchPressed(px, py, 0); } void EmuWindow_SDL2::OnFingerMotion(float x, float y) { const auto [px, py] = TouchToPixelPos(x, y); - TouchMoved(px, py); + TouchMoved(px, py, 0); } void EmuWindow_SDL2::OnFingerUp() { - TouchReleased(); + TouchReleased(0); } void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { |