Skip to content
This repository was archived by the owner on Mar 21, 2025. It is now read-only.

code: Nuke savestate support #42

Merged
merged 1 commit into from
Mar 25, 2024
Merged

code: Nuke savestate support #42

merged 1 commit into from
Mar 25, 2024

Conversation

raphaelthegreat
Copy link
Collaborator

@raphaelthegreat raphaelthegreat commented Mar 24, 2024

Now that we no longer have the daily Nightly builds putting a gun over our heads I can finally do what my heart always desired, nuke save states to the shadow realm 😄

Why do this?

It turns out boost serialization is quite terrible choice for serializing complex HLE emulator internals:

  • With boost the serialization function is a template which means it must reside in the header file. This means that unless we use macros to automate the template instantiation, we must include boost headers in the header which pollute the codebase and make build times longer.
  • Boost serialization has features like class versions and file version which help with maintaining backwards compatibility. We have no intention of doing this and it makes the code more complex and bloated so it's useless.
  • boost doesn't support serialization of types like std::optional and certain containers which made it necessary to add custom implementations in common which are quite hefty code wise. Also forced usage of boost::optional in many places when it wasn't really necessary.
  • (Most important) boost provides little to no control to us in cases where it matters. It doesn't allow us to pass construction arguments to object and thus requires objects to be default constructible. This forced many objects to have two constructors and use global state which is terrible. boost would also completely recreate objects behind our back and necessitate hacks like

    citra/src/core/core.cpp

    Lines 761 to 765 in 8433057

    // Re-register gpu callback, because gsp service changed after service_manager got
    // serialized
    auto gsp = service_manager->GetService<Service::GSP::GSP_GPU>("gsp::Gpu");
    gpu->SetInterruptHandler(
    [gsp](Service::GSP::InterruptId interrupt_id) { gsp->SignalInterrupt(interrupt_id); });
  • boost is slow. Loading savestates, especially on android, takes a lot of time when it could be much faster. With a custom implementation we could bring the time way down.
  • boost prevents any major refactors in the emulator core. Savestates were the reason fastmem was never implemented as they could not be easily adjusted to work in that new memory model. boost is weeded heavily into service and kernel code which makes any major refactor of them extremely hard. boost compile errors are often because it requires many macros to properly setup up and probably adds a lot of code bloat to almost everything as well.

With all these in mind, I have completely removed boost serialization from the codebase for the time being. That will allow us to be much more flexible with rewrites and improvements to core, without worrying about how to reconcile save-state support. In the future we should employ a completely custom solution, could be either as a library or in the common library (I'm leaning towards the latter for now). Such a solution should:

  • Allow non-default construction of objects
  • Easy serialization of binary structures (possibly add a tag to these so the serialization system can know when to auto-serialize them)
  • Flexibility in the way data structures can be serialized. We should be able to choose when to completely re-initialize a structure or simply serialize each member (in case of service map)
  • By having the serialization function be a normal member function we are much more free to move things around and change this. We can pass extra arguments like the system instance when needed so no problems with global state.

If this were to be a library something like blobify can be considered as its interface is quite easy to use, though it seems to lack the ability to pick and chose which members to serialize. For a in-house system, Dolphin's ChunkFile is a good reference, it's simple, fast and lightweight. Objects implement the Do method for serialization. I feel a combination of these approaches might be best

In addition utilizing a custom implementation we can be much smarter about how we serialize structures and in what order. boost enforced strict order before which required a few hacki-sh behaviors like "locking" the VMManager during deserialization. Especially the kernel, after we refactor it to use the slab heap we could make it way faster

When will save states be back?

Idk, first the kernel needs to be refactored and then I might also add fastmem support so we already have the features the old system blocked, before fully designing and implementing the new system. There are still a few remnants of save states even after this PR, but I want to think of how to better abstract them. The BackingMem abstraction uses shared_ptr and are quite heavy, given their purpose is to just hold some memory

@Miguel-hrvs
Copy link
Contributor

For a successful msys2 compilation, first we'll need to merge rtiangha's llvm fixes.

@raphaelthegreat
Copy link
Collaborator Author

Merged, let me rebase

@raphaelthegreat raphaelthegreat changed the base branch from master to dev March 24, 2024 22:56
@PabloMK7 PabloMK7 merged commit ac792f7 into PabloMK7:dev Mar 25, 2024
12 checks passed
@DonelBueno
Copy link

Why wasn't this merged to master?

@raphaelthegreat
Copy link
Collaborator Author

Many people use savestates so it was better to not touch the master branch everyone works on

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants