diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 52fc64711..e67b76828 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -128,6 +128,7 @@ else() # GCC may warn when it ignores attributes like maybe_unused, # which is a problem for older versions (e.g. GCC 11). add_compile_options("-Wno-attributes") + add_compile_options("-Wno-interference-size") endif() if (MINGW) diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts index 3a34cbf0f..cc24879e9 100644 --- a/src/android/app/build.gradle.kts +++ b/src/android/app/build.gradle.kts @@ -29,7 +29,7 @@ android { namespace = "org.citra.citra_emu" compileSdkVersion = "android-34" - ndkVersion = "25.2.9519653" + ndkVersion = "26.1.10909125" compileOptions { sourceCompatibility = JavaVersion.VERSION_17 diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index c95ca9f86..bca836ca8 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h @@ -83,14 +83,6 @@ class GMainWindow : public QMainWindow { /// Max number of recently loaded items to keep track of static const int max_recent_files_item = 10; - // TODO: Make use of this! - enum { - UI_IDLE, - UI_EMU_BOOTING, - UI_EMU_RUNNING, - UI_EMU_STOPPING, - }; - public: void filterBarSetChecked(bool state); void UpdateUITheme(); diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index fa6b7a7af..64b22e6b1 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -90,7 +90,6 @@ add_library(citra_common STATIC file_util.cpp file_util.h hash.h - linear_disk_cache.h literals.h logging/backend.cpp logging/backend.h @@ -110,7 +109,6 @@ add_library(citra_common STATIC microprofile.cpp microprofile.h microprofileui.h - misc.cpp param_package.cpp param_package.h polyfill_thread.h diff --git a/src/common/bit_set.h b/src/common/bit_set.h index 0598b60df..0f2c36ebf 100644 --- a/src/common/bit_set.h +++ b/src/common/bit_set.h @@ -2,78 +2,14 @@ #pragma once +#include #include -#ifdef _WIN32 -#include -#endif #include -#include #include #include "common/common_types.h" -// namespace avoids conflict with OS X Carbon; don't use BitSet directly namespace Common { -// Helper functions: - -#ifdef _MSC_VER -template -static inline int CountSetBits(T v) { - // from https://graphics.stanford.edu/~seander/bithacks.html - // GCC has this built in, but MSVC's intrinsic will only emit the actual - // POPCNT instruction, which we're not depending on - v = v - ((v >> 1) & (T) ~(T)0 / 3); - v = (v & (T) ~(T)0 / 15 * 3) + ((v >> 2) & (T) ~(T)0 / 15 * 3); - v = (v + (v >> 4)) & (T) ~(T)0 / 255 * 15; - return (T)(v * ((T) ~(T)0 / 255)) >> (sizeof(T) - 1) * 8; -} -static inline int LeastSignificantSetBit(u8 val) { - unsigned long index; - _BitScanForward(&index, val); - return (int)index; -} -static inline int LeastSignificantSetBit(u16 val) { - unsigned long index; - _BitScanForward(&index, val); - return (int)index; -} -static inline int LeastSignificantSetBit(u32 val) { - unsigned long index; - _BitScanForward(&index, val); - return (int)index; -} -static inline int LeastSignificantSetBit(u64 val) { - unsigned long index; - _BitScanForward64(&index, val); - return (int)index; -} -#else -static inline int CountSetBits(u8 val) { - return __builtin_popcount(val); -} -static inline int CountSetBits(u16 val) { - return __builtin_popcount(val); -} -static inline int CountSetBits(u32 val) { - return __builtin_popcount(val); -} -static inline int CountSetBits(u64 val) { - return __builtin_popcountll(val); -} -static inline int LeastSignificantSetBit(u8 val) { - return __builtin_ctz(val); -} -static inline int LeastSignificantSetBit(u16 val) { - return __builtin_ctz(val); -} -static inline int LeastSignificantSetBit(u32 val) { - return __builtin_ctz(val); -} -static inline int LeastSignificantSetBit(u64 val) { - return __builtin_ctzll(val); -} -#endif - // Similar to std::bitset, this is a class which encapsulates a bitset, i.e. // using the set bits of an integer to represent a set of integers. Like that // class, it acts like an array of bools: @@ -92,22 +28,19 @@ static inline int LeastSignificantSetBit(u64 val) { // operation.) // - Counting set bits using .Count() - see comment on that method. -// TODO: use constexpr when MSVC gets out of the Dark Ages - template + requires std::is_unsigned_v class BitSet { - static_assert(!std::is_signed_v, "BitSet should not be used with signed types"); - public: // A reference to a particular bit, returned from operator[]. class Ref { public: - Ref(Ref&& other) : m_bs(other.m_bs), m_mask(other.m_mask) {} - Ref(BitSet* bs, IntTy mask) : m_bs(bs), m_mask(mask) {} - operator bool() const { + constexpr Ref(Ref&& other) : m_bs(other.m_bs), m_mask(other.m_mask) {} + constexpr Ref(BitSet* bs, IntTy mask) : m_bs(bs), m_mask(mask) {} + constexpr operator bool() const { return (m_bs->m_val & m_mask) != 0; } - bool operator=(bool set) { + constexpr bool operator=(bool set) { m_bs->m_val = (m_bs->m_val & ~m_mask) | (set ? m_mask : 0); return set; } @@ -120,26 +53,26 @@ public: // A STL-like iterator is required to be able to use range-based for loops. class Iterator { public: - Iterator(const Iterator& other) : m_val(other.m_val) {} - Iterator(IntTy val) : m_val(val) {} - int operator*() { + constexpr Iterator(const Iterator& other) : m_val(other.m_val) {} + constexpr Iterator(IntTy val) : m_val(val) {} + constexpr int operator*() { // This will never be called when m_val == 0, because that would be the end() iterator - return LeastSignificantSetBit(m_val); + return std::countr_zero(m_val); } - Iterator& operator++() { + constexpr Iterator& operator++() { // Unset least significant set bit m_val &= m_val - IntTy(1); return *this; } - Iterator operator++(int _) { + constexpr Iterator operator++(int) { Iterator other(*this); ++*this; return other; } - bool operator==(Iterator other) const { + constexpr bool operator==(Iterator other) const { return m_val == other.m_val; } - bool operator!=(Iterator other) const { + constexpr bool operator!=(Iterator other) const { return m_val != other.m_val; } @@ -147,74 +80,69 @@ public: IntTy m_val; }; - BitSet() : m_val(0) {} - explicit BitSet(IntTy val) : m_val(val) {} - BitSet(std::initializer_list init) { + constexpr BitSet() : m_val(0) {} + constexpr explicit BitSet(IntTy val) : m_val(val) {} + constexpr BitSet(std::initializer_list init) { m_val = 0; for (int bit : init) m_val |= (IntTy)1 << bit; } - static BitSet AllTrue(std::size_t count) { + constexpr static BitSet AllTrue(std::size_t count) { return BitSet(count == sizeof(IntTy) * 8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1)); } - Ref operator[](std::size_t bit) { + constexpr Ref operator[](std::size_t bit) { return Ref(this, (IntTy)1 << bit); } - const Ref operator[](std::size_t bit) const { + constexpr const Ref operator[](std::size_t bit) const { return (*const_cast(this))[bit]; } - bool operator==(BitSet other) const { + constexpr bool operator==(BitSet other) const { return m_val == other.m_val; } - bool operator!=(BitSet other) const { + constexpr bool operator!=(BitSet other) const { return m_val != other.m_val; } - bool operator<(BitSet other) const { + constexpr bool operator<(BitSet other) const { return m_val < other.m_val; } - bool operator>(BitSet other) const { + constexpr bool operator>(BitSet other) const { return m_val > other.m_val; } - BitSet operator|(BitSet other) const { + constexpr BitSet operator|(BitSet other) const { return BitSet(m_val | other.m_val); } - BitSet operator&(BitSet other) const { + constexpr BitSet operator&(BitSet other) const { return BitSet(m_val & other.m_val); } - BitSet operator^(BitSet other) const { + constexpr BitSet operator^(BitSet other) const { return BitSet(m_val ^ other.m_val); } - BitSet operator~() const { + constexpr BitSet operator~() const { return BitSet(~m_val); } - BitSet& operator|=(BitSet other) { + constexpr BitSet& operator|=(BitSet other) { return *this = *this | other; } - BitSet& operator&=(BitSet other) { + constexpr BitSet& operator&=(BitSet other) { return *this = *this & other; } - BitSet& operator^=(BitSet other) { + constexpr BitSet& operator^=(BitSet other) { return *this = *this ^ other; } operator u32() = delete; - operator bool() { + constexpr operator bool() { return m_val != 0; } - - // Warning: Even though on modern CPUs this is a single fast instruction, - // Dolphin's official builds do not currently assume POPCNT support on x86, - // so slower explicit bit twiddling is generated. Still should generally - // be faster than a loop. - unsigned int Count() const { - return CountSetBits(m_val); + constexpr u32 Count() const { + return std::popcount(m_val); } - Iterator begin() const { + constexpr Iterator begin() const { return Iterator(m_val); } - Iterator end() const { + constexpr Iterator end() const { return Iterator(0); } diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index ff7452f6c..8f109a8d3 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h @@ -4,7 +4,6 @@ #pragma once -#include #include "common/common_types.h" /// Textually concatenates two tokens. The double-expansion is required by the C preprocessor. @@ -46,14 +45,8 @@ __declspec(dllimport) void __stdcall DebugBreak(void); #endif #ifdef _MSC_VER -#if (_MSC_VER < 1900) -// Function Cross-Compatibility -#define snprintf _snprintf -#endif - // Locale Cross-Compatibility #define locale_t _locale_t - #endif // _MSC_VER #define DECLARE_ENUM_FLAG_OPERATORS(type) \ @@ -109,9 +102,3 @@ __declspec(dllimport) void __stdcall DebugBreak(void); using T = std::underlying_type_t; \ return static_cast(key) == 0; \ } - -// Generic function to get last error message. -// Call directly after the command or use the error num. -// This function might change the error code. -// Defined in Misc.cpp. -[[nodiscard]] std::string GetLastErrorMsg(); diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 4b302a893..5b0ba817a 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -14,6 +14,7 @@ #include "common/assert.h" #include "common/common_funcs.h" #include "common/common_paths.h" +#include "common/error.h" #include "common/file_util.h" #include "common/logging/log.h" #include "common/scope_exit.h" @@ -90,6 +91,8 @@ // REMEMBER: strdup considered harmful! namespace FileUtil { +using Common::GetLastErrorMsg; + // Remove any ending forward slashes from directory paths // Modifies argument. static void StripTailDirSlashes(std::string& fname) { diff --git a/src/common/linear_disk_cache.h b/src/common/linear_disk_cache.h deleted file mode 100644 index ee9cd5afa..000000000 --- a/src/common/linear_disk_cache.h +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include -#include -#include "common/common_types.h" - -// defined in Version.cpp -extern const char* scm_rev_git_str; - -// On disk format: -// header{ -// u32 'DCAC'; -// u32 version; // svn_rev -// u16 sizeof(key_type); -// u16 sizeof(value_type); -//} - -// key_value_pair{ -// u32 value_size; -// key_type key; -// value_type[value_size] value; -//} - -template -class LinearDiskCacheReader { -public: - virtual void Read(const K& key, const V* value, u32 value_size) = 0; -}; - -// Dead simple unsorted key-value store with append functionality. -// No random read functionality, all reading is done in OpenAndRead. -// Keys and values can contain any characters, including \0. -// -// Suitable for caching generated shader bytecode between executions. -// Not tuned for extreme performance but should be reasonably fast. -// Does not support keys or values larger than 2GB, which should be reasonable. -// Keys must have non-zero length; values can have zero length. - -// K and V are some POD type -// K : the key type -// V : value array type -template -class LinearDiskCache { -public: - // return number of read entries - u32 OpenAndRead(const char* filename, LinearDiskCacheReader& reader) { - using std::ios_base; - - // close any currently opened file - Close(); - m_num_entries = 0; - - // try opening for reading/writing - OpenFStream(m_file, filename, ios_base::in | ios_base::out | ios_base::binary); - - m_file.seekg(0, std::ios::end); - std::fstream::pos_type end_pos = m_file.tellg(); - m_file.seekg(0, std::ios::beg); - std::fstream::pos_type start_pos = m_file.tellg(); - std::streamoff file_size = end_pos - start_pos; - - if (m_file.is_open() && ValidateHeader()) { - // good header, read some key/value pairs - K key; - - std::vector value; - u32 value_size; - u32 entry_number; - - std::fstream::pos_type last_pos = m_file.tellg(); - - while (Read(&value_size)) { - std::streamoff next_extent = - (last_pos - start_pos) + sizeof(value_size) + value_size; - if (next_extent > file_size) - break; - - value.clear(); - value.resize(value_size); - - // read key/value and pass to reader - if (Read(&key) && Read(value.data(), value_size) && Read(&entry_number) && - entry_number == m_num_entries + 1) { - reader.Read(key, value.data(), value_size); - } else { - break; - } - - m_num_entries++; - last_pos = m_file.tellg(); - } - m_file.seekp(last_pos); - m_file.clear(); - - value.clear(); - return m_num_entries; - } - - // failed to open file for reading or bad header - // close and recreate file - Close(); - m_file.open(filename, ios_base::out | ios_base::trunc | ios_base::binary); - WriteHeader(); - return 0; - } - - void Sync() { - m_file.flush(); - } - - void Close() { - if (m_file.is_open()) - m_file.close(); - // clear any error flags - m_file.clear(); - } - - // Appends a key-value pair to the store. - void Append(const K& key, const V* value, u32 value_size) { - // TODO: Should do a check that we don't already have "key"? (I think each caller does that - // already.) - Write(&value_size); - Write(&key); - Write(value, value_size); - m_num_entries++; - Write(&m_num_entries); - } - -private: - void WriteHeader() { - Write(&m_header); - } - - bool ValidateHeader() { - char file_header[sizeof(Header)]; - - return (Read(file_header, sizeof(Header)) && - !memcmp((const char*)&m_header, file_header, sizeof(Header))); - } - - template - bool Write(const D* data, u32 count = 1) { - return m_file.write((const char*)data, count * sizeof(D)).good(); - } - - template - bool Read(const D* data, u32 count = 1) { - return m_file.read((char*)data, count * sizeof(D)).good(); - } - - struct Header { - Header() : id(*(u32*)"DCAC"), key_t_size(sizeof(K)), value_t_size(sizeof(V)) { - std::memcpy(ver, scm_rev_git_str, 40); - } - - const u32 id; - const u16 key_t_size, value_t_size; - char ver[40]; - - } m_header; - - std::fstream m_file; - u32 m_num_entries; -}; diff --git a/src/common/misc.cpp b/src/common/misc.cpp deleted file mode 100644 index 1a95e29be..000000000 --- a/src/common/misc.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include -#ifdef _WIN32 -#include -#else -#include -#include -#endif - -#include "common/common_funcs.h" - -// Generic function to get last error message. -// Call directly after the command or use the error num. -// This function might change the error code. -std::string GetLastErrorMsg() { -#ifdef _WIN32 - LPSTR err_str; - - DWORD res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - reinterpret_cast(&err_str), 1, nullptr); - if (!res) { - return "(FormatMessageA failed to format error)"; - } - std::string ret(err_str); - LocalFree(err_str); - return ret; -#else - char err_str[255]; -#if (defined(__GLIBC__) || __ANDROID_API__ >= 23) && \ - (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600)) - // Thread safe (GNU-specific) - const char* str = strerror_r(errno, err_str, sizeof(err_str)); - return std::string(str); -#else - // Thread safe (XSI-compliant) - int second_err = strerror_r(errno, err_str, sizeof(err_str)); - if (second_err != 0) { - return "(strerror_r failed to format error)"; - } - return std::string(err_str); -#endif // GLIBC etc. -#endif // _WIN32 -} diff --git a/src/common/ring_buffer.h b/src/common/ring_buffer.h index 9b7e32b2c..b19a409d6 100644 --- a/src/common/ring_buffer.h +++ b/src/common/ring_buffer.h @@ -105,9 +105,7 @@ public: private: // It is important to separate the below atomics for performance reasons: // Having them on the same cache-line would result in false-sharing between them. - // TODO: Remove this ifdef whenever clang and GCC support - // std::hardware_destructive_interference_size. -#if defined(_MSC_VER) && _MSC_VER >= 1911 +#ifdef __cpp_lib_hardware_interference_size static constexpr std::size_t padding_size = std::hardware_destructive_interference_size - sizeof(std::atomic_size_t); #else diff --git a/src/common/swap.h b/src/common/swap.h index f5976a09c..a80e191dc 100644 --- a/src/common/swap.h +++ b/src/common/swap.h @@ -17,43 +17,14 @@ #pragma once -#include - #if defined(_MSC_VER) #include #endif +#include #include +#include #include "common/common_types.h" -// GCC -#ifdef __GNUC__ - -#if __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) && !defined(COMMON_LITTLE_ENDIAN) -#define COMMON_LITTLE_ENDIAN 1 -#elif __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) && !defined(COMMON_BIG_ENDIAN) -#define COMMON_BIG_ENDIAN 1 -#endif - -// LLVM/clang -#elif defined(__clang__) - -#if __LITTLE_ENDIAN__ && !defined(COMMON_LITTLE_ENDIAN) -#define COMMON_LITTLE_ENDIAN 1 -#elif __BIG_ENDIAN__ && !defined(COMMON_BIG_ENDIAN) -#define COMMON_BIG_ENDIAN 1 -#endif - -// MSVC -#elif defined(_MSC_VER) && !defined(COMMON_BIG_ENDIAN) && !defined(COMMON_LITTLE_ENDIAN) - -#define COMMON_LITTLE_ENDIAN 1 -#endif - -// Worst case, default to little endian. -#if !COMMON_BIG_ENDIAN && !COMMON_LITTLE_ENDIAN -#define COMMON_LITTLE_ENDIAN 1 -#endif - namespace Common { #ifdef _MSC_VER @@ -675,17 +646,8 @@ struct AddEndian { }; // Alias LETag/BETag as KeepTag/SwapTag depending on the system -#if COMMON_LITTLE_ENDIAN - -using LETag = KeepTag; -using BETag = SwapTag; - -#else - -using BETag = KeepTag; -using LETag = SwapTag; - -#endif +using LETag = std::conditional_t; +using BETag = std::conditional_t; // Aliases for LE types using u16_le = AddEndian::type; diff --git a/src/common/thread.cpp b/src/common/thread.cpp index 119514ab1..73cc4948b 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp @@ -11,6 +11,7 @@ #include #elif defined(_WIN32) #include +#include "common/string_util.h" #else #if defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__) #include @@ -82,29 +83,8 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) { #ifdef _MSC_VER // Sets the debugger-visible name of the current thread. -// Uses trick documented in: -// https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code void SetCurrentThreadName(const char* name) { - static const DWORD MS_VC_EXCEPTION = 0x406D1388; - -#pragma pack(push, 8) - struct THREADNAME_INFO { - DWORD dwType; // must be 0x1000 - LPCSTR szName; // pointer to name (in user addr space) - DWORD dwThreadID; // thread ID (-1=caller thread) - DWORD dwFlags; // reserved for future use, must be zero - } info; -#pragma pack(pop) - - info.dwType = 0x1000; - info.szName = name; - info.dwThreadID = std::numeric_limits::max(); - info.dwFlags = 0; - - __try { - RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info); - } __except (EXCEPTION_CONTINUE_EXECUTION) { - } + SetThreadDescription(GetCurrentThread(), UTF8ToUTF16W(name).data()); } #else // !MSVC_VER, so must be POSIX threads diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp index ce69cf0ce..994108a68 100644 --- a/src/core/file_sys/archive_sdmc.cpp +++ b/src/core/file_sys/archive_sdmc.cpp @@ -5,6 +5,7 @@ #include #include #include "common/archives.h" +#include "common/error.h" #include "common/file_util.h" #include "common/logging/log.h" #include "common/settings.h" @@ -103,7 +104,7 @@ ResultVal> SDMCArchive::OpenFileBase(const Path& pa FileUtil::IOFile file(full_path, mode.write_flag ? "r+b" : "rb"); if (!file.IsOpen()) { - LOG_CRITICAL(Service_FS, "Error opening {}: {}", full_path, GetLastErrorMsg()); + LOG_CRITICAL(Service_FS, "Error opening {}: {}", full_path, Common::GetLastErrorMsg()); return ERROR_NOT_FOUND; } diff --git a/src/core/hle/service/gsp/gsp_gpu.h b/src/core/hle/service/gsp/gsp_gpu.h index 7de8191a2..048733898 100644 --- a/src/core/hle/service/gsp/gsp_gpu.h +++ b/src/core/hle/service/gsp/gsp_gpu.h @@ -97,10 +97,8 @@ struct FrameBufferUpdate { static_assert(sizeof(FrameBufferUpdate) == 0x40, "Struct has incorrect size"); // TODO: Not sure if this padding is correct. // Chances are the second block is stored at offset 0x24 rather than 0x20. -#ifndef _MSC_VER static_assert(offsetof(FrameBufferUpdate, framebuffer_info[1]) == 0x20, "FrameBufferInfo element has incorrect alignment"); -#endif /// GSP command struct Command { diff --git a/src/core/hw/lcd.h b/src/core/hw/lcd.h index 9667c7c4c..8e681609b 100644 --- a/src/core/hw/lcd.h +++ b/src/core/hw/lcd.h @@ -64,22 +64,14 @@ private: }; static_assert(std::is_standard_layout::value, "Structure does not use standard layout"); -// TODO: MSVC does not support using offsetof() on non-static data members even though this -// is technically allowed since C++11. This macro should be enabled once MSVC adds -// support for that. -#ifndef _MSC_VER #define ASSERT_REG_POSITION(field_name, position) \ static_assert(offsetof(Regs, field_name) == position * 4, \ "Field " #field_name " has invalid position") - ASSERT_REG_POSITION(color_fill_top, 0x81); ASSERT_REG_POSITION(backlight_top, 0x90); ASSERT_REG_POSITION(color_fill_bottom, 0x281); ASSERT_REG_POSITION(backlight_bottom, 0x290); -#undef ASSERT_REG_POSITION -#endif // !defined(_MSC_VER) - extern Regs g_regs; template