diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index 52fef5dba..620bb913a 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -292,12 +292,15 @@ X_STATUS Emulator::LaunchStfsContainer(std::wstring path) { } void Emulator::Pause() { - auto lock = global_critical_region::AcquireDirect(); if (paused_) { return; } paused_ = true; + // Don't hold the lock on this (so any waits follow through) + graphics_system_->Pause(); + + auto lock = global_critical_region::AcquireDirect(); auto threads = kernel_state()->object_table()->GetObjectsByType( kernel::XObject::kTypeThread); @@ -320,6 +323,8 @@ void Emulator::Resume() { paused_ = false; XELOGD("! EMULATOR RESUMED !"); + graphics_system_->Resume(); + auto threads = kernel_state()->object_table()->GetObjectsByType( kernel::XObject::kTypeThread); @@ -349,6 +354,7 @@ bool Emulator::SaveToFile(const std::wstring& path) { // It's important we don't hold the global lock here! XThreads need to step // forward (possibly through guarded regions) without worry! + graphics_system_->Save(&stream); kernel_state_->Save(&stream); memory_->Save(&stream); map->Close(stream.offset()); @@ -376,6 +382,9 @@ bool Emulator::RestoreFromFile(const std::wstring& path) { return false; } + if (!graphics_system_->Restore(&stream)) { + return false; + } if (!kernel_state_->Restore(&stream)) { return false; } diff --git a/src/xenia/gpu/command_processor.cc b/src/xenia/gpu/command_processor.cc index 40188ea3c..a86c5b7eb 100644 --- a/src/xenia/gpu/command_processor.cc +++ b/src/xenia/gpu/command_processor.cc @@ -11,6 +11,7 @@ #include +#include "xenia/base/byte_stream.h" #include "xenia/base/logging.h" #include "xenia/base/math.h" #include "xenia/base/profiling.h" @@ -163,6 +164,59 @@ void CommandProcessor::WorkerThreadMain() { ShutdownContext(); } +void CommandProcessor::Pause() { + if (paused_) { + return; + } + paused_ = true; + + threading::Fence fence; + CallInThread([&fence]() { + fence.Signal(); + threading::Thread::GetCurrentThread()->Suspend(); + }); + + // HACK - Prevents a hang in IssueSwap() + swap_state_.pending = false; + + fence.Wait(); +} + +void CommandProcessor::Resume() { + if (!paused_) { + return; + } + paused_ = false; + + worker_thread_->thread()->Resume(); +} + +bool CommandProcessor::Save(ByteStream* stream) { + assert_true(paused_); + + stream->Write(primary_buffer_ptr_); + stream->Write(primary_buffer_size_); + stream->Write(read_ptr_index_); + stream->Write(read_ptr_update_freq_); + stream->Write(read_ptr_writeback_ptr_); + stream->Write(write_ptr_index_.load()); + + return true; +} + +bool CommandProcessor::Restore(ByteStream* stream) { + assert_true(paused_); + + primary_buffer_ptr_ = stream->Read(); + primary_buffer_size_ = stream->Read(); + read_ptr_index_ = stream->Read(); + read_ptr_update_freq_ = stream->Read(); + read_ptr_writeback_ptr_ = stream->Read(); + write_ptr_index_.store(stream->Read()); + + return true; +} + bool CommandProcessor::SetupContext() { return true; } void CommandProcessor::ShutdownContext() { context_.reset(); } diff --git a/src/xenia/gpu/command_processor.h b/src/xenia/gpu/command_processor.h index 0d20bdecd..7202b65cf 100644 --- a/src/xenia/gpu/command_processor.h +++ b/src/xenia/gpu/command_processor.h @@ -28,6 +28,9 @@ #include "xenia/ui/graphics_context.h" namespace xe { + +class ByteStream; + namespace gpu { class GraphicsSystem; @@ -91,6 +94,13 @@ class CommandProcessor { void ExecutePacket(uint32_t ptr, uint32_t count); + bool is_paused() const { return paused_; } + void Pause(); + void Resume(); + + bool Save(ByteStream* stream); + bool Restore(ByteStream* stream); + protected: class RingbufferReader; @@ -214,6 +224,9 @@ class CommandProcessor { Shader* active_vertex_shader_ = nullptr; Shader* active_pixel_shader_ = nullptr; + + bool paused_ = false; + }; } // namespace gpu diff --git a/src/xenia/gpu/graphics_system.cc b/src/xenia/gpu/graphics_system.cc index 617c74a65..fe5602f94 100644 --- a/src/xenia/gpu/graphics_system.cc +++ b/src/xenia/gpu/graphics_system.cc @@ -9,6 +9,7 @@ #include "xenia/gpu/graphics_system.h" +#include "xenia/base/byte_stream.h" #include "xenia/base/clock.h" #include "xenia/base/logging.h" #include "xenia/base/math.h" @@ -246,5 +247,31 @@ void GraphicsSystem::BeginTracing() { void GraphicsSystem::EndTracing() { command_processor_->EndTracing(); } +void GraphicsSystem::Pause() { + paused_ = true; + + command_processor_->Pause(); +} + +void GraphicsSystem::Resume() { + paused_ = false; + + command_processor_->Resume(); +} + +bool GraphicsSystem::Save(ByteStream* stream) { + stream->Write(interrupt_callback_); + stream->Write(interrupt_callback_data_); + + return command_processor_->Save(stream); +} + +bool GraphicsSystem::Restore(ByteStream* stream) { + interrupt_callback_ = stream->Read(); + interrupt_callback_data_ = stream->Read(); + + return command_processor_->Restore(stream); +} + } // namespace gpu } // namespace xe diff --git a/src/xenia/gpu/graphics_system.h b/src/xenia/gpu/graphics_system.h index 19c93aa93..06360a3a8 100644 --- a/src/xenia/gpu/graphics_system.h +++ b/src/xenia/gpu/graphics_system.h @@ -61,6 +61,13 @@ class GraphicsSystem { void BeginTracing(); void EndTracing(); + bool is_paused() const { return paused_; } + void Pause(); + void Resume(); + + bool Save(ByteStream* stream); + bool Restore(ByteStream* stream); + protected: GraphicsSystem(); @@ -90,6 +97,8 @@ class GraphicsSystem { RegisterFile register_file_; std::unique_ptr command_processor_; + + bool paused_ = false; }; } // namespace gpu