Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Savestates: Asynchronous compression #14847

Merged
merged 7 commits into from Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 8 additions & 5 deletions Utilities/JIT.cpp
Expand Up @@ -10,6 +10,7 @@
#include "util/v128.hpp"
#include "util/simd.hpp"
#include "Crypto/unzip.h"

#include <charconv>

#ifdef __linux__
Expand Down Expand Up @@ -1170,17 +1171,19 @@ class ObjectCache final : public llvm::ObjectCache
//fs::file(name, fs::rewrite).write(obj.getBufferStart(), obj.getBufferSize());
name.append(".gz");

const std::vector<u8> zbuf = zip(reinterpret_cast<const u8*>(obj.getBufferStart()), obj.getBufferSize());
fs::file module_file(name, fs::rewrite);

if (zbuf.empty())
if (!module_file)
{
jit_log.error("LLVM: Failed to compress module: %s", _module->getName().data());
jit_log.error("LLVM: Failed to create module file: %s (%s)", name, fs::g_tls_error);
return;
}

if (!fs::write_file(name, fs::rewrite, zbuf.data(), zbuf.size()))
if (!zip(obj.getBufferStart(), obj.getBufferSize(), module_file))
{
jit_log.error("LLVM: Failed to create module file: %s (%s)", name, fs::g_tls_error);
jit_log.error("LLVM: Failed to compress module: %s", _module->getName().data());
module_file.close();
fs::remove_file(name);
return;
}

Expand Down
62 changes: 17 additions & 45 deletions rpcs3/Crypto/unzip.cpp
Expand Up @@ -3,6 +3,8 @@

#include <zlib.h>

#include "util/serialization_ext.hpp"

std::vector<u8> unzip(const void* src, usz size)
{
if (!src || !size) [[unlikely]]
Expand Down Expand Up @@ -130,65 +132,35 @@ bool unzip(const void* src, usz size, fs::file& out)
return is_valid;
}

std::vector<u8> zip(const void* src, usz size)
bool zip(const void* src, usz size, fs::file& out, bool multi_thread_it)
{
if (!src || !size)
if (!src || !size || !out)
{
return {};
return false;
}

const uLong zsz = compressBound(::narrow<u32>(size)) + 256;
std::vector<u8> out(zsz);

z_stream zs{};
#ifndef _MSC_VER
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#endif
int res = deflateInit2(&zs, 9, Z_DEFLATED, 16 + 15, 9, Z_DEFAULT_STRATEGY);
if (res != Z_OK)
{
return {};
}
#ifndef _MSC_VER
#pragma GCC diagnostic pop
#endif
zs.avail_in = static_cast<u32>(size);
zs.next_in = reinterpret_cast<const u8*>(src);
zs.avail_out = static_cast<u32>(out.size());
zs.next_out = out.data();
utils::serial compressor(!multi_thread_it || size < 0x40'0000);
compressor.m_file_handler = make_compressed_serialization_file_handler(out);

res = deflate(&zs, Z_FINISH);
std::string_view buffer_view{static_cast<const char*>(src), size};

switch (res)
while (!buffer_view.empty())
{
case Z_OK:
case Z_STREAM_END:
if (zs.avail_out)
if (!compressor.m_file_handler->is_valid())
{
out.resize(zsz - zs.avail_out);
return false;
}
break;
default:
out.clear();
break;
}

deflateEnd(&zs);
const std::string_view slice = buffer_view.substr(0, 0x50'0000);

return out;
}

bool zip(const void* src, usz size, fs::file& out)
{
if (!src || !size || !out)
{
return false;
compressor(slice);
compressor.breathe();
buffer_view = buffer_view.substr(slice.size());
}

const std::vector zipped = zip(src, size);
compressor.m_file_handler->finalize(compressor);

if (zipped.empty() || out.write(zipped.data(), zipped.size()) != zipped.size())
if (!compressor.m_file_handler->is_valid())
{
return false;
}
Expand Down
10 changes: 1 addition & 9 deletions rpcs3/Crypto/unzip.h
Expand Up @@ -16,15 +16,7 @@ inline bool unzip(const std::vector<u8>& src, fs::file& out)
return unzip(src.data(), src.size(), out);
}

std::vector<u8> zip(const void* src, usz size);

template <typename T>
inline std::vector<u8> zip(const T& src)
{
return zip(src.data(), src.size());
}

bool zip(const void* src, usz size, fs::file& out);
bool zip(const void* src, usz size, fs::file& out, bool multi_thread_it = false);

template <typename T>
inline bool zip(const T& src, fs::file& out)
Expand Down
1 change: 1 addition & 0 deletions rpcs3/Emu/CMakeLists.txt
Expand Up @@ -50,6 +50,7 @@ target_sources(rpcs3_emu PRIVATE
../util/dyn_lib.cpp
../util/sysinfo.cpp
../util/cpu_stats.cpp
../util/serialization_ext.cpp
../../Utilities/bin_patch.cpp
../../Utilities/cheat_info.cpp
../../Utilities/cond.cpp
Expand Down
9 changes: 6 additions & 3 deletions rpcs3/Emu/Cell/PPUThread.cpp
Expand Up @@ -3541,7 +3541,10 @@ namespace

fs::stat_t get_stat() override
{
return m_file.get_stat();
fs::stat_t stat = m_file.get_stat();
stat.size = std::min<u64>(utils::sub_saturate<u64>(stat.size, m_off), m_max_size);
stat.is_writable = false;
return stat;
}

bool trunc(u64) override
Expand All @@ -3558,7 +3561,7 @@ namespace

u64 read_at(u64 offset, void* buffer, u64 size) override
{
return m_file.read_at(offset + m_off, buffer, std::min<u64>(size, utils::sub_saturate<u64>(m_max_size, m_pos)));
return m_file.read_at(offset + m_off, buffer, std::min<u64>(size, utils::sub_saturate<u64>(m_max_size, offset)));
}

u64 write(const void*, u64) override
Expand All @@ -3585,7 +3588,7 @@ namespace

u64 size() override
{
return m_file.size();
return std::min<u64>(utils::sub_saturate<u64>(m_file.size(), m_off), m_max_size);
}
};
}
Expand Down
25 changes: 19 additions & 6 deletions rpcs3/Emu/Memory/vm.cpp
Expand Up @@ -1617,14 +1617,14 @@ namespace vm
const v128 _5 = _1 | _2;
const v128 _6 = _3 | _4;
const v128 _7 = _5 | _6;
return _7 == v128{};
return gv_testz(_7);
}

static void serialize_memory_bytes(utils::serial& ar, u8* ptr, usz size)
{
ensure((size % 4096) == 0);

for (; size; ptr += 128 * 8)
for (usz iter_count = 0; size; iter_count++, ptr += 128 * 8)
{
const usz process_size = std::min<usz>(size, 128 * 8);
size -= process_size;
Expand All @@ -1633,12 +1633,20 @@ namespace vm

if (ar.is_writing())
{
for (usz i = 0; i < process_size; i += 128)
for (usz i = 0; i < process_size; i += 128 * 2)
{
if (!check_cache_line_zero(ptr + i))
const u64 sample64_1 = read_from_ptr<u64>(ptr, i);
const u64 sample64_2 = read_from_ptr<u64>(ptr, i + 128);

// Speed up testing in scenarios where it is likely non-zero data
if (sample64_1 && sample64_2)
{
bitmap |= 1u << (i / 128);
bitmap |= 3u << (i / 128);
continue;
}

bitmap |= (check_cache_line_zero(ptr + i + 0) ? 0 : 1) << (i / 128);
bitmap |= (check_cache_line_zero(ptr + i + 128) ? 0 : 2) << (i / 128);
}
}

Expand All @@ -1665,8 +1673,13 @@ namespace vm
i += block_count * 128;
}

ar.breathe();
if (iter_count % 256 == 0)
{
ar.breathe();
}
}

ar.breathe();
}

void block_t::save(utils::serial& ar, std::map<utils::shm*, usz>& shared)
Expand Down
3 changes: 1 addition & 2 deletions rpcs3/Emu/RSX/RSXThread.cpp
Expand Up @@ -4,7 +4,6 @@
#include "Emu/Cell/PPUCallback.h"
#include "Emu/Cell/SPUThread.h"
#include "Emu/Cell/timers.hpp"
#include "Emu/savestate_utils.hpp"

#include "Capture/rsx_capture.h"
#include "Common/BufferUtils.h"
Expand All @@ -20,7 +19,7 @@
#include "Emu/Cell/lv2/sys_event.h"
#include "Emu/Cell/lv2/sys_time.h"
#include "Emu/Cell/Modules/cellGcmSys.h"
#include "Emu/savestate_utils.hpp"
#include "util/serialization_ext.hpp"
#include "Overlays/overlay_perf_metrics.h"
#include "Overlays/overlay_message.h"
#include "Program/GLSLCommon.h"
Expand Down
10 changes: 6 additions & 4 deletions rpcs3/Emu/System.cpp
Expand Up @@ -9,6 +9,7 @@
#include "Emu/perf_monitor.hpp"
#include "Emu/vfs_config.h"
#include "Emu/IPC_config.h"
#include "Emu/savestate_utils.hpp"

#include "Emu/Cell/ErrorCodes.h"
#include "Emu/Cell/PPUThread.h"
Expand Down Expand Up @@ -40,8 +41,6 @@
#include "../Crypto/unself.h"
#include "../Crypto/unzip.h"
#include "util/logs.hpp"
#include "util/serialization.hpp"
#include "savestate_utils.hpp"

#include <fstream>
#include <memory>
Expand Down Expand Up @@ -2932,6 +2931,7 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s

// Save it first for maximum timing accuracy
const u64 timestamp = get_timebased_time();
const u64 start_time = get_system_time();

sys_log.notice("All threads have been stopped.");

Expand Down Expand Up @@ -3128,7 +3128,9 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s
ar.seek_end();
ar.m_file_handler->finalize(ar);

if (!file.commit())
fs::stat_t file_stat{};

if (!file.commit() || !fs::get_stat(path, file_stat))
{
sys_log.error("Failed to write savestate to file! (path='%s', %s)", path, fs::g_tls_error);
savestate = false;
Expand All @@ -3152,7 +3154,7 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s
sys_log.success("Old savestate has been removed: path='%s'", old_path2);
}

sys_log.success("Saved savestate! path='%s'", path);
sys_log.success("Saved savestate! path='%s' (file_size=0x%x, time_to_save=%gs)", path, file_stat.size, (get_system_time() - start_time) / 1000000.);

if (!g_cfg.savestate.suspend_emu)
{
Expand Down