From 8a31ffaf222ef68bdf722fe6bb28ba68a739a929 Mon Sep 17 00:00:00 2001 From: zhupengfei Date: Wed, 12 Aug 2020 23:20:04 +0800 Subject: [PATCH] core, video_core: Fixes to make savestates work 1. Acquire the context before initializing renderer when using async gpu 2. Do not try present when renderer is nullptr This has some potential race condition but is also what we do in qt 3. Synchronize before serializing video core (WaitForProcessing) For this, the GPU thread is changed to pop commands *after* processing. 4. Avoid waiting on future fences Such events can exist in core timing queue when deserializing. --- .../app/src/main/jni/emu_window/emu_window.cpp | 2 +- src/video_core/gpu.cpp | 6 ++++++ src/video_core/gpu.h | 2 ++ src/video_core/gpu_thread.cpp | 14 +++++++++++--- src/video_core/gpu_thread.h | 9 ++++++++- src/video_core/renderer_opengl/renderer_opengl.cpp | 4 ++++ src/video_core/video_core.cpp | 1 + 7 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/android/app/src/main/jni/emu_window/emu_window.cpp b/src/android/app/src/main/jni/emu_window/emu_window.cpp index 71a5ff633..118e33e4c 100644 --- a/src/android/app/src/main/jni/emu_window/emu_window.cpp +++ b/src/android/app/src/main/jni/emu_window/emu_window.cpp @@ -246,7 +246,7 @@ void EmuWindow_Android::TryPresenting() { } } eglSwapInterval(egl_display, Settings::values.use_vsync_new ? 1 : 0); - if (VideoCore::g_renderer->TryPresent()) { + if (VideoCore::g_renderer && VideoCore::g_renderer->TryPresent()) { eglSwapBuffers(egl_display, egl_surface); } } diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index b75157b40..d5423c739 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -13,6 +13,8 @@ GPUBackend::GPUBackend(VideoCore::RendererBase& renderer) : renderer{renderer} { GPUBackend::~GPUBackend() = default; +void GPUBackend::WaitForProcessing() {} + GPUSerial::GPUSerial(Core::System& system, VideoCore::RendererBase& renderer) : GPUBackend(renderer), system{system} {} @@ -83,4 +85,8 @@ void GPUParallel::InvalidateRegion(VAddr addr, u64 size) { gpu_thread.InvalidateRegion(addr, size); } +void GPUParallel::WaitForProcessing() { + gpu_thread.WaitForProcessing(); +} + } // namespace VideoCore diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index d239704ba..b47023dc5 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -29,6 +29,7 @@ public: virtual void FlushRegion(VAddr addr, u64 size) = 0; virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0; virtual void InvalidateRegion(VAddr addr, u64 size) = 0; + virtual void WaitForProcessing(); protected: VideoCore::RendererBase& renderer; @@ -65,6 +66,7 @@ public: void FlushRegion(VAddr addr, u64 size) override; void FlushAndInvalidateRegion(VAddr addr, u64 size) override; void InvalidateRegion(VAddr addr, u64 size) override; + void WaitForProcessing() override; private: GPUThread::ThreadManager gpu_thread; diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index 5cc22433c..fc62e69ae 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp @@ -32,12 +32,12 @@ static void RunThread(VideoCore::RendererBase& renderer, SynchState& state, Core Frontend::ScopeAcquireContext acquire_context{renderer.GetRenderWindow()}; - CommandDataContainer next; while (state.is_running) { state.WaitForCommands(); - CommandDataContainer next; - while (state.queue.Pop(next)) { + while (!state.queue.Empty()) { + CommandDataContainer next = state.queue.Front(); + auto command = &next.data; auto fence = next.fence; if (const auto submit_list = std::get_if(command)) { @@ -62,6 +62,7 @@ static void RunThread(VideoCore::RendererBase& renderer, SynchState& state, Core UNREACHABLE(); } state.signaled_fence = next.fence; + state.queue.Pop(); } } } @@ -211,8 +212,15 @@ u64 ThreadManager::PushCommand(CommandData&& command_data) { return fence; } +void ThreadManager::WaitForProcessing() { + state.WaitForProcessing(); +} + MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192)); void SynchState::WaitForSynchronization(u64 fence) { + if (fence > last_fence) { // We don't want to wait infinitely + return; + } if (signaled_fence >= fence) { return; } diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h index 27e33d7f8..ade649cdb 100644 --- a/src/video_core/gpu_thread.h +++ b/src/video_core/gpu_thread.h @@ -169,9 +169,14 @@ struct SynchState final { // commands_condition.wait(lock, [this] { return !queue.Empty(); }); } + void WaitForProcessing() { + while (!queue.Empty() && is_running) + ; + } + using CommandQueue = Common::SPSCQueue; CommandQueue queue; - u64 last_fence{}; + std::atomic last_fence{}; std::atomic signaled_fence{}; }; @@ -195,6 +200,8 @@ public: void InvalidateRegion(VAddr addr, u64 size); + void WaitForProcessing(); + private: void Synchronize(u64 fence, Settings::GpuTimingMode mode); diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index f74fcb7d1..6e59b1f47 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -1212,6 +1212,10 @@ VideoCore::ResultStatus RendererOpenGL::Init() { } #endif + if (Settings::values.use_asynchronous_gpu_emulation) { + render_window.MakeCurrent(); + } + const char* gl_version{reinterpret_cast(glGetString(GL_VERSION))}; const char* gpu_vendor{reinterpret_cast(glGetString(GL_VENDOR))}; const char* gpu_model{reinterpret_cast(glGetString(GL_RENDERER))}; diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index 2e32953de..e0e234418 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp @@ -104,6 +104,7 @@ u16 GetResolutionScaleFactor() { template void serialize(Archive& ar, const unsigned int) { + g_gpu->WaitForProcessing(); ar& Pica::g_state; }