From 30a60018a0aa44c3a4f550e2cefa5e311a5c1d33 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 20 Jan 2018 12:05:30 -0800 Subject: [PATCH] GLES: Fix race crash on shutdown. This happens when there are pointers in step commands that get freed. --- GPU/GLES/GPU_GLES.cpp | 4 ++++ ext/native/thin3d/GLRenderManager.cpp | 27 +++++++++++++++++++++++++++ ext/native/thin3d/GLRenderManager.h | 4 ++++ 3 files changed, 35 insertions(+) diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index 5417a24a8..f58c47ddb 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -184,6 +184,10 @@ GPU_GLES::GPU_GLES(GraphicsContext *gfxCtx, Draw::DrawContext *draw) } GPU_GLES::~GPU_GLES() { + GLRenderManager *render = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + render->Wipe(); + render->WaitUntilQueueIdle(); + framebufferManagerGL_->DestroyAllFBOs(); shaderManagerGL_->ClearCache(true); depalShaderCache_.Clear(); diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 33990e3db..d272f2571 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -148,10 +148,12 @@ void GLRenderManager::StopThread() { Crash(); } frameData.readyForRun = false; + frameData.readyForSubmit = false; for (size_t i = 0; i < frameData.steps.size(); i++) { delete frameData.steps[i]; } frameData.steps.clear(); + frameData.initSteps.clear(); std::unique_lock lock(frameData.push_mutex); while (!frameData.readyForFence) { @@ -306,6 +308,7 @@ void GLRenderManager::BeginFrame() { frameData.push_condVar.wait(lock); } frameData.readyForFence = false; + frameData.readyForSubmit = true; } VLOG("PUSH: Fencing %d", curFrame); @@ -381,7 +384,9 @@ void GLRenderManager::Submit(int frame, bool triggerFence) { VLOG("PULL: Frame %d.readyForFence = true", frame); std::unique_lock lock(frameData.push_mutex); + assert(frameData.readyForSubmit); frameData.readyForFence = true; + frameData.readyForSubmit = false; frameData.push_condVar.notify_all(); } } @@ -466,6 +471,7 @@ void GLRenderManager::FlushSync() { frameData.push_condVar.wait(lock); } frameData.readyForFence = false; + frameData.readyForSubmit = true; } } @@ -484,17 +490,38 @@ void GLRenderManager::EndSyncFrame(int frame) { if (useThread_) { std::unique_lock lock(frameData.push_mutex); frameData.readyForFence = true; + frameData.readyForSubmit = true; frameData.push_condVar.notify_all(); } } void GLRenderManager::Wipe() { + initSteps_.clear(); for (auto step : steps_) { delete step; } steps_.clear(); } +void GLRenderManager::WaitUntilQueueIdle() { + if (!useThread_) { + // Must be idle, nothing to do. + return; + } + + // Just wait for all frames to be ready. + for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) { + FrameData &frameData = frameData_[i]; + + std::unique_lock lock(frameData.push_mutex); + // Ignore unsubmitted frames. + while (!frameData.readyForFence && !frameData.readyForSubmit) { + VLOG("PUSH: Waiting for frame[%d].readyForFence = 1 (wait idle)", i); + frameData.push_condVar.wait(lock); + } + } +} + GLPushBuffer::GLPushBuffer(GLRenderManager *render, GLuint target, size_t size) : render_(render), target_(target), size_(size) { bool res = AddBuffer(); assert(res); diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index abc4fb7ce..703cda773 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -211,6 +211,9 @@ public: // Zaps queued up commands. Use if you know there's a risk you've queued up stuff that has already been deleted. Can happen during in-game shutdown. void Wipe(); + // Wait until no frames are pending. Use during shutdown before freeing pointers. + void WaitUntilQueueIdle(); + // Creation commands. These were not needed in Vulkan since there we can do that on the main thread. GLRTexture *CreateTexture(GLenum target) { GLRInitStep step{ GLRInitStepType::CREATE_TEXTURE }; @@ -676,6 +679,7 @@ private: bool readyForFence = true; bool readyForRun = false; + bool readyForSubmit = false; bool skipSwap = false; GLRRunType type = GLRRunType::END;