mirror of
https://github.com/RPCSX/rpcsx.git
synced 2026-04-04 14:08:37 +00:00
Savestates Support For PS3 Emulation (#10478)
This commit is contained in:
parent
969b9eb89d
commit
fcd297ffb2
154 changed files with 4948 additions and 635 deletions
|
|
@ -1214,7 +1214,7 @@ protected:
|
|||
|
||||
public:
|
||||
static constexpr usz align = Align;
|
||||
using enable_bitcopy = std::true_type;
|
||||
ENABLE_BITWISE_SERIALIZATION;
|
||||
|
||||
atomic_t() noexcept = default;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue