Savestates Support For PS3 Emulation (#10478)

This commit is contained in:
Elad Ashkenazi 2022-07-04 16:02:17 +03:00 committed by GitHub
parent 969b9eb89d
commit fcd297ffb2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
154 changed files with 4948 additions and 635 deletions

View file

@ -1214,7 +1214,7 @@ protected:
public:
static constexpr usz align = Align;
using enable_bitcopy = std::true_type;
ENABLE_BITWISE_SERIALIZATION;
atomic_t() noexcept = default;

View file

@ -180,7 +180,7 @@ namespace stx
using under = decltype(int_or_enum());
public:
using enable_bitcopy = std::true_type;
ENABLE_BITWISE_SERIALIZATION;
se_t() noexcept = default;

View file

@ -7,9 +7,12 @@
#include <utility>
#include <type_traits>
#include <algorithm>
enum class thread_state : u32;
extern thread_local std::string_view g_tls_serialize_name;
namespace stx
{
// Simplified typemap with exactly one object of each used type, non-moveable. Initialized on init(). Destroyed on clear().
@ -58,14 +61,32 @@ namespace stx
// Save default constructor and destructor and optional joining operation
struct typeinfo
{
bool(*create)(uchar* ptr, manual_typemap&) noexcept = nullptr;
bool(*create)(uchar* ptr, manual_typemap&, utils::serial*, std::string_view) noexcept = nullptr;
void(*stop)(void* ptr, thread_state) noexcept = nullptr;
void(*save)(void* ptr, utils::serial&) noexcept = nullptr;
void(*destroy)(void* ptr) noexcept = nullptr;
std::string_view name{};
std::string_view name;
template <typename T>
static bool call_ctor(uchar* ptr, manual_typemap& _this) noexcept
static bool call_ctor(uchar* ptr, manual_typemap& _this, utils::serial* ar, std::string_view name) noexcept
{
if (ar)
{
if constexpr (std::is_constructible_v<T, manual_typemap&, exact_t<utils::serial&>>)
{
g_tls_serialize_name = name;
new (ptr) T(_this, exact_t<utils::serial&>(*ar));
return true;
}
if constexpr (std::is_constructible_v<T, exact_t<utils::serial&>>)
{
g_tls_serialize_name = name;
new (ptr) T(exact_t<utils::serial&>(*ar));
return true;
}
}
// Allow passing reference to "this"
if constexpr (std::is_constructible_v<T, manual_typemap&>)
{
@ -96,6 +117,19 @@ namespace stx
*std::launder(static_cast<T*>(ptr)) = state;
}
#ifdef _MSC_VER
template <typename T>
static void call_save(void*, utils::serial&) noexcept
{
}
#endif
template <typename T> requires requires (T& a) { a.save(std::declval<stx::exact_t<utils::serial&>>()); }
static void call_save(void* ptr, utils::serial& ar) noexcept
{
std::launder(static_cast<T*>(ptr))->save(stx::exact_t<utils::serial&>(ar));
}
template <typename T>
static typeinfo make_typeinfo()
{
@ -110,6 +144,13 @@ namespace stx
r.stop = &call_stop<T>;
}
// TODO: Unconnement and remove call_save overload when MSVC implements it
#ifndef _MSC_VER
if constexpr (!!(requires (T& a) { a.save(std::declval<stx::exact_t<utils::serial&>>()); }))
#endif
{
r.save = &call_save<T>;
}
#ifdef _MSC_VER
constexpr std::string_view name = parse_type(__FUNCSIG__);
#else
@ -181,15 +222,31 @@ namespace stx
*m_info++ = nullptr;
}
void init(bool reset = true)
void init(bool reset = true, utils::serial* ar = nullptr)
{
if (reset)
{
this->reset();
}
// Use unique_ptr to reduce header dependencies in this commonly used header
const auto order = std::make_unique<std::pair<double, const type_info<typeinfo>*>[]>(stx::typelist<typeinfo>().count());
usz pos = 0;
for (const auto& type : stx::typelist<typeinfo>())
{
order[pos++] = {type.init_pos(), std::addressof(type)};
}
std::stable_sort(order.get(), order.get() + stx::typelist<typeinfo>().count(), [](auto a, auto b)
{
return a.first < b.first;
});
for (pos = 0; pos < stx::typelist<typeinfo>().count(); pos++)
{
const auto& type = *order[pos].second;
const u32 id = type.index();
uchar* data = (Size ? +m_data : m_list) + type.pos();
@ -199,13 +256,15 @@ namespace stx
continue;
}
if (type.create(data, *this))
if (type.create(data, *this, ar, type.name))
{
*m_order++ = data;
*m_info++ = &type;
m_init[id] = true;
}
}
g_tls_serialize_name = {};
}
void clear()
@ -262,6 +321,32 @@ namespace stx
}
}
void save(utils::serial& ar)
{
if (!m_init)
{
return;
}
// Get actual number of created objects
u32 _max = 0;
for (const auto& type : stx::typelist<typeinfo>())
{
if (m_init[type.index()])
{
// Skip object if not created
_max++;
}
}
// Save data in forward order
for (u32 i = _max; i; i--)
{
if (auto save = (*std::prev(m_info, i))->save) save(*std::prev(m_order, i), ar);
}
}
// Check if object is not initialized but shall be initialized first (to use in initializing other objects)
template <typename T>
void need() noexcept
@ -294,6 +379,8 @@ namespace stx
As* obj = nullptr;
g_tls_serialize_name = get_name<T, As>();
if constexpr (Size != 0)
{
obj = new (m_data + stx::typeoffset<typeinfo, std::decay_t<T>>()) std::decay_t<As>(std::forward<Args>(args)...);
@ -303,6 +390,8 @@ namespace stx
obj = new (m_list + stx::typeoffset<typeinfo, std::decay_t<T>>()) std::decay_t<As>(std::forward<Args>(args)...);
}
g_tls_serialize_name = {};
*m_order++ = obj;
*m_info++ = &stx::typedata<typeinfo, std::decay_t<T>, std::decay_t<As>>();
return obj;
@ -337,6 +426,12 @@ namespace stx
}
}
template <typename T, typename As = T>
static std::string_view get_name() noexcept
{
return stx::typedata<typeinfo, std::decay_t<T>, std::decay_t<As>>().name;
}
// Obtain object pointer if initialized
template <typename T>
T* try_get() const noexcept

View file

@ -3,21 +3,6 @@
#include "util/types.hpp"
#include <vector>
namespace stx
{
template <typename T>
struct exact_t
{
T obj;
exact_t(T&& _obj) : obj(std::forward<T>(_obj)) {}
// TODO: More conversions
template <typename U> requires (std::is_same_v<U&, T>)
operator U&() const { return obj; };
};
}
namespace utils
{
template <typename T>
@ -52,6 +37,10 @@ namespace utils
std::vector<u8> data;
usz pos = umax;
serial() = default;
serial(const serial&) = delete;
~serial() = default;
// Checks if this instance is currently used for serialization
bool is_writing() const
{
@ -299,7 +288,7 @@ namespace utils
}
// Convert serialization manager to deserializion manager (can't go the other way)
// If no arg provided reuse saved buffer
// If no arg is provided reuse saved buffer
void set_reading_state(std::vector<u8>&& _data = std::vector<u8>{})
{
if (!_data.empty())

View file

@ -3,6 +3,8 @@
#include "util/types.hpp"
#include "util/shared_ptr.hpp"
#include <string_view>
#ifndef _MSC_VER
#define ATTR_PURE __attribute__((pure))
#else
@ -24,6 +26,7 @@ namespace stx
u32 size = 1;
u32 align = 1;
u32 begin = 0;
double order;
// Next typeinfo in linked list
type_info* next = nullptr;
@ -31,7 +34,7 @@ namespace stx
// Auxiliary pointer to base type
const type_info* base = nullptr;
type_info(Info info, u32 size, u32 align, const type_info* base = nullptr) noexcept;
type_info(Info info, u32 size, u32 align, double order, const type_info* base = nullptr) noexcept;
friend type_counter<Info>;
@ -55,6 +58,11 @@ namespace stx
{
return begin + size;
}
ATTR_PURE double init_pos() const
{
return order;
}
};
// Class for automatic type registration for given Info type
@ -177,14 +185,25 @@ namespace stx
return typelist_v;
}
template <typename T> requires requires () { T::savestate_init_pos + 0.; }
constexpr double get_savestate_init_pos()
{
return T::savestate_init_pos;
}
template <typename T> requires (!(requires () { T::savestate_init_pos + 0.; }))
constexpr double get_savestate_init_pos()
{
return {};
}
template <typename Info> template <typename T>
const type_info<Info> type_counter<Info>::type{Info::template make_typeinfo<T>(), sizeof(T), alignof(T)};
const type_info<Info> type_counter<Info>::type{Info::template make_typeinfo<T>(), sizeof(T), alignof(T), get_savestate_init_pos<T>()};
template <typename Info> template <typename T, typename As>
const type_info<Info> type_counter<Info>::dyn_type{Info::template make_typeinfo<As>(), sizeof(As), alignof(As), &type_counter<Info>::template type<T>};
const type_info<Info> type_counter<Info>::dyn_type{Info::template make_typeinfo<As>(), sizeof(As), alignof(As), get_savestate_init_pos<T>(), &type_counter<Info>::template type<T>};
template <typename Info>
type_info<Info>::type_info(Info info, u32 _size, u32 _align, const type_info<Info>* cbase) noexcept
type_info<Info>::type_info(Info info, u32 _size, u32 _align, double order, const type_info<Info>* cbase) noexcept
: Info(info)
{
auto& tl = typelist<Info>();
@ -193,6 +212,7 @@ namespace stx
this->size = _size > this->size ? _size : this->size;
this->align = _align > this->align ? _align : this->align;
this->base = cbase;
this->order = order;
// Update global max alignment
tl.first.align = _align > tl.first.align ? _align : tl.first.align;

View file

@ -1081,6 +1081,21 @@ constexpr bool is_same_ptr(const volatile Y* ptr)
template <typename X, typename Y>
concept PtrSame = (is_same_ptr<X, Y>());
namespace stx
{
template <typename T>
struct exact_t
{
T obj;
exact_t(T&& _obj) : obj(std::forward<T>(_obj)) {}
// TODO: More conversions
template <typename U> requires (std::is_same_v<U&, T>)
operator U&() const { return obj; };
};
}
namespace utils
{
struct serial;
@ -1088,3 +1103,24 @@ namespace utils
template <typename T>
extern bool serialize(utils::serial& ar, T& obj);
#define USING_SERIALIZATION_VERSION(name) []()\
{\
extern void using_##name##_serialization();\
using_##name##_serialization();\
}()
#define USING_SERIALIZATION_VERSION_COND(cond, name) [&]()\
{\
extern void using_##name##_serialization();\
if (static_cast<bool>(cond)) using_##name##_serialization();\
}()
#define GET_SERIALIZATION_VERSION(name) []()\
{\
extern u32 get_##name##_serialization_version();\
return get_##name##_serialization_version();\
}()
#define ENABLE_BITWISE_SERIALIZATION using enable_bitcopy = std::true_type;
#define SAVESTATE_INIT_POS(x) static constexpr double savestate_init_pos = (x)

View file

@ -79,7 +79,7 @@ union alignas(16) v128
return std::bit_cast<T>(*this);
}
using enable_bitcopy = std::true_type;
ENABLE_BITWISE_SERIALIZATION;
static v128 from64(u64 _0, u64 _1 = 0)
{

View file

@ -73,7 +73,7 @@ namespace utils
atomic_t<void*> m_ptr{nullptr};
public:
explicit shm(u32 size, u32 flags = 0);
explicit shm(u64 size, u32 flags = 0);
// Construct with specified path as sparse file storage
shm(u64 size, const std::string& storage);

View file

@ -395,8 +395,8 @@ namespace utils
void memory_release(void* pointer, usz size)
{
#ifdef _WIN32
ensure(::VirtualFree(pointer, 0, MEM_RELEASE));
unmap_mappping_memory(reinterpret_cast<u64>(pointer), size);
ensure(::VirtualFree(pointer, 0, MEM_RELEASE));
#else
ensure(::munmap(pointer, size) != -1);
#endif
@ -457,7 +457,7 @@ namespace utils
#endif
}
shm::shm(u32 size, u32 flags)
shm::shm(u64 size, u32 flags)
: m_flags(flags)
, m_size(utils::align(size, 0x10000))
{