common: Miscellaneous cleanups (#7239)

* code: Remove some old msvc workarounds

* android: Upgrade to NDK 26

* Allows access to newer libc++

* common/swap: Make use of std::endian

Allows removing a bunch of defines in favor of a two liner.

* common: Remove misc.cpp

* GetLastErrorMsg has been in error.h for a while and also helps removing a depedency from a hot header like common_funcs

* common: use SetThreadDescription API for thread names

* common: Remove linear disk cache

* Has never been used?

* bit_set: Make constexpr

* ring_buffer: Use feature macro

* bit_set: Use <bit> and concepts

* gsp_gpu: Restore comment

* core: Ignore GCC warning

---------

Co-authored-by: Lioncash <mathew1800@gmail.com>
Co-authored-by: Liam <byteslice@airmail.cc>
This commit is contained in:
GPUCode 2023-12-14 16:26:33 +02:00 committed by GitHub
parent 15ea0c6336
commit 2b20082581
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 50 additions and 426 deletions

View file

@ -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)

View file

@ -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

View file

@ -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();

View file

@ -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

View file

@ -2,78 +2,14 @@
#pragma once
#include <bit>
#include <cstddef>
#ifdef _WIN32
#include <intrin.h>
#endif
#include <initializer_list>
#include <new>
#include <type_traits>
#include "common/common_types.h"
// namespace avoids conflict with OS X Carbon; don't use BitSet<T> directly
namespace Common {
// Helper functions:
#ifdef _MSC_VER
template <typename T>
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 <typename IntTy>
requires std::is_unsigned_v<IntTy>
class BitSet {
static_assert(!std::is_signed_v<IntTy>, "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<int> init) {
constexpr BitSet() : m_val(0) {}
constexpr explicit BitSet(IntTy val) : m_val(val) {}
constexpr BitSet(std::initializer_list<int> 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<BitSet*>(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);
}

View file

@ -4,7 +4,6 @@
#pragma once
#include <string>
#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<type>; \
return static_cast<T>(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();

View file

@ -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) {

View file

@ -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 <fstream>
#include <vector>
#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 <typename K, typename V>
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 <typename K, typename V>
class LinearDiskCache {
public:
// return number of read entries
u32 OpenAndRead(const char* filename, LinearDiskCacheReader<K, V>& 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<V> 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 <typename D>
bool Write(const D* data, u32 count = 1) {
return m_file.write((const char*)data, count * sizeof(D)).good();
}
template <typename D>
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;
};

View file

@ -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 <cstddef>
#ifdef _WIN32
#include <windows.h>
#else
#include <cerrno>
#include <cstring>
#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<LPSTR>(&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
}

View file

@ -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

View file

@ -17,43 +17,14 @@
#pragma once
#include <type_traits>
#if defined(_MSC_VER)
#include <cstdlib>
#endif
#include <bit>
#include <cstring>
#include <type_traits>
#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<T, SwapTag> {
};
// 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<std::endian::native == std::endian::little, KeepTag, SwapTag>;
using BETag = std::conditional_t<std::endian::native == std::endian::big, KeepTag, SwapTag>;
// Aliases for LE types
using u16_le = AddEndian<u16, LETag>::type;

View file

@ -11,6 +11,7 @@
#include <mach/mach.h>
#elif defined(_WIN32)
#include <windows.h>
#include "common/string_util.h"
#else
#if defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
#include <pthread_np.h>
@ -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<DWORD>::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

View file

@ -5,6 +5,7 @@
#include <algorithm>
#include <memory>
#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<std::unique_ptr<FileBackend>> 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;
}

View file

@ -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 {

View file

@ -64,22 +64,14 @@ private:
};
static_assert(std::is_standard_layout<Regs>::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 <typename T>