GLES: Fix race crash on shutdown.

This happens when there are pointers in step commands that get freed.
This commit is contained in:
Unknown W. Brackets 2018-01-20 12:05:30 -08:00 committed by Henrik Rydgård
parent acc3e39b67
commit 30a60018a0
3 changed files with 35 additions and 0 deletions

View File

@ -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();

View File

@ -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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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);

View File

@ -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;