Android: Add both a lost and restore phase.

Lost = delete, restore = create.  Let's stick to never overlapping.

May help #8912.
This commit is contained in:
Unknown W. Brackets 2016-09-10 20:29:58 -07:00
parent 29f5763d32
commit 92d930887f
23 changed files with 109 additions and 10 deletions

View File

@ -534,6 +534,10 @@ void GPU_DX9::DeviceLost() {
framebufferManager_.DeviceLost(); framebufferManager_.DeviceLost();
} }
void GPU_DX9::DeviceRestore() {
// Nothing needed.
}
void GPU_DX9::InitClear() { void GPU_DX9::InitClear() {
ScheduleEvent(GPU_EVENT_INIT_CLEAR); ScheduleEvent(GPU_EVENT_INIT_CLEAR);
} }

View File

@ -56,6 +56,7 @@ public:
bool PerformStencilUpload(u32 dest, int size) override; bool PerformStencilUpload(u32 dest, int size) override;
void ClearCacheNextFrame() override; void ClearCacheNextFrame() override;
void DeviceLost() override; // Only happens on Android. Drop all textures and shaders. void DeviceLost() override; // Only happens on Android. Drop all textures and shaders.
void DeviceRestore() override;
void DumpNextFrame() override; void DumpNextFrame() override;
void DoState(PointerWrap &p) override; void DoState(PointerWrap &p) override;

View File

@ -207,14 +207,18 @@ void DrawEngineGLES::DestroyDeviceObjects() {
} }
} }
void DrawEngineGLES::GLRestore() { void DrawEngineGLES::GLLost() {
ILOG("TransformDrawEngine::GLRestore()"); ILOG("TransformDrawEngine::GLLost()");
// The objects have already been deleted. // The objects have already been deleted.
bufferNameCache_.clear(); bufferNameCache_.clear();
bufferNameInfo_.clear(); bufferNameInfo_.clear();
freeSizedBuffers_.clear(); freeSizedBuffers_.clear();
bufferNameCacheSize_ = 0; bufferNameCacheSize_ = 0;
ClearTrackedVertexArrays(); ClearTrackedVertexArrays();
}
void DrawEngineGLES::GLRestore() {
ILOG("TransformDrawEngine::GLRestore()");
InitDeviceObjects(); InitDeviceObjects();
} }

View File

@ -129,6 +129,7 @@ public:
void RestoreVAO(); void RestoreVAO();
void InitDeviceObjects(); void InitDeviceObjects();
void DestroyDeviceObjects(); void DestroyDeviceObjects();
void GLLost() override;
void GLRestore() override; void GLRestore() override;
void Resized(); void Resized();

View File

@ -651,6 +651,10 @@ void GPU_GLES::DeviceLost() {
fragmentTestCache_.Clear(false); fragmentTestCache_.Clear(false);
depalShaderCache_.Clear(); depalShaderCache_.Clear();
framebufferManager_.DeviceLost(); framebufferManager_.DeviceLost();
}
void GPU_GLES::DeviceRestore() {
ILOG("GPU_GLES: DeviceRestore");
UpdateVsyncInterval(true); UpdateVsyncInterval(true);
} }

View File

@ -60,6 +60,7 @@ public:
bool PerformStencilUpload(u32 dest, int size) override; bool PerformStencilUpload(u32 dest, int size) override;
void ClearCacheNextFrame() override; void ClearCacheNextFrame() override;
void DeviceLost() override; // Only happens on Android. Drop all textures and shaders. void DeviceLost() override; // Only happens on Android. Drop all textures and shaders.
void DeviceRestore() override;
void DumpNextFrame() override; void DumpNextFrame() override;
void DoState(PointerWrap &p) override; void DoState(PointerWrap &p) override;

View File

@ -268,6 +268,7 @@ public:
virtual void EnableInterrupts(bool enable) = 0; virtual void EnableInterrupts(bool enable) = 0;
virtual void DeviceLost() = 0; virtual void DeviceLost() = 0;
virtual void DeviceRestore() = 0;
virtual void ReapplyGfxState() = 0; virtual void ReapplyGfxState() = 0;
virtual void SyncThread(bool force = false) = 0; virtual void SyncThread(bool force = false) = 0;
virtual void SyncBeginFrame() = 0; virtual void SyncBeginFrame() = 0;

View File

@ -43,6 +43,7 @@ public:
void ClearCacheNextFrame() override {} void ClearCacheNextFrame() override {}
void DeviceLost() override {} void DeviceLost() override {}
void DeviceRestore() override {}
void DumpNextFrame() override {} void DumpNextFrame() override {}
void Resized() override {} void Resized() override {}

View File

@ -78,6 +78,10 @@ void SoftGPU::DeviceLost() {
// Handled by thin3d. // Handled by thin3d.
} }
void SoftGPU::DeviceRestore() {
// Handled by thin3d.
}
SoftGPU::~SoftGPU() { SoftGPU::~SoftGPU() {
vformat->Release(); vformat->Release();
vformat = nullptr; vformat = nullptr;

View File

@ -69,6 +69,7 @@ public:
void ClearCacheNextFrame() override {} void ClearCacheNextFrame() override {}
void DeviceLost() override; void DeviceLost() override;
void DeviceRestore() override;
void DumpNextFrame() override {} void DumpNextFrame() override {}
void Resized() override {} void Resized() override {}

View File

@ -1947,6 +1947,10 @@ void GPU_Vulkan::DeviceLost() {
// TODO // TODO
} }
void GPU_Vulkan::DeviceRestore() {
// TODO
}
void GPU_Vulkan::GetStats(char *buffer, size_t bufsize) { void GPU_Vulkan::GetStats(char *buffer, size_t bufsize) {
const DrawEngineVulkanStats &drawStats = drawEngine_.GetStats(); const DrawEngineVulkanStats &drawStats = drawEngine_.GetStats();
float vertexAverageCycles = gpuStats.numVertsSubmitted > 0 ? (float)gpuStats.vertexGPUCycles / (float)gpuStats.numVertsSubmitted : 0.0f; float vertexAverageCycles = gpuStats.numVertsSubmitted > 0 ? (float)gpuStats.vertexGPUCycles / (float)gpuStats.numVertsSubmitted : 0.0f;

View File

@ -62,6 +62,7 @@ public:
bool PerformStencilUpload(u32 dest, int size) override; bool PerformStencilUpload(u32 dest, int size) override;
void ClearCacheNextFrame() override; void ClearCacheNextFrame() override;
void DeviceLost() override; // Only happens on Android. Drop all textures and shaders. void DeviceLost() override; // Only happens on Android. Drop all textures and shaders.
void DeviceRestore() override;
void DumpNextFrame() override; void DumpNextFrame() override;
void DoState(PointerWrap &p) override; void DoState(PointerWrap &p) override;

View File

@ -1097,6 +1097,12 @@ void EmuScreen::deviceLost() {
ILOG("EmuScreen::deviceLost()"); ILOG("EmuScreen::deviceLost()");
if (gpu) if (gpu)
gpu->DeviceLost(); gpu->DeviceLost();
}
void EmuScreen::deviceRestore() {
ILOG("EmuScreen::deviceRestore()");
if (gpu)
gpu->DeviceRestore();
RecreateViews(); RecreateViews();
} }

View File

@ -38,6 +38,7 @@ public:
void update(InputState &input) override; void update(InputState &input) override;
void render() override; void render() override;
void deviceLost() override; void deviceLost() override;
void deviceRestore() override;
void dialogFinished(const Screen *dialog, DialogResult result) override; void dialogFinished(const Screen *dialog, DialogResult result) override;
void sendMessage(const char *msg, const char *value) override; void sendMessage(const char *msg, const char *value) override;

View File

@ -808,11 +808,16 @@ void NativeUpdate(InputState &input) {
screenManager->update(input); screenManager->update(input);
} }
void NativeDeviceRestore() { void NativeDeviceLost() {
if (g_gameInfoCache) if (g_gameInfoCache)
g_gameInfoCache->Clear(); g_gameInfoCache->Clear();
screenManager->deviceLost(); screenManager->deviceLost();
gl_lost();
}
void NativeDeviceRestore() {
NativeDeviceLost();
screenManager->deviceRestore();
if (GetGPUBackend() == GPUBackend::OPENGL) { if (GetGPUBackend() == GPUBackend::OPENGL) {
gl_restore(); gl_restore();
} }

View File

@ -697,6 +697,7 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayRender(JNIEnv *env,
extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayShutdown(JNIEnv *env, jobject obj) { extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayShutdown(JNIEnv *env, jobject obj) {
ILOG("NativeApp.displayShutdown()"); ILOG("NativeApp.displayShutdown()");
if (renderer_inited) { if (renderer_inited) {
NativeDeviceLost();
NativeShutdownGraphics(); NativeShutdownGraphics();
renderer_inited = false; renderer_inited = false;
NativeMessageReceived("recreateviews", ""); NativeMessageReceived("recreateviews", "");

View File

@ -57,6 +57,24 @@ void gl_restore() {
inRestore = false; inRestore = false;
} }
void gl_lost() {
inLost = true;
if (!holders) {
WLOG("GL resource holder not initialized, cannot process restore request");
inLost = false;
return;
}
// TODO: We should really do this when we get the context back, not during gl_lost...
ILOG("gl_lost() clearing %i items:", (int)holders->size());
for (size_t i = 0; i < holders->size(); i++) {
ILOG("gl_lost(%i / %i, %p, %08x)", (int)(i + 1), (int)holders->size(), (*holders)[i], *((uint32_t *)((*holders)[i])));
(*holders)[i]->GLLost();
}
ILOG("gl_lost() completed on %i items:", (int)holders->size());
inLost = false;
}
void gl_lost_manager_init() { void gl_lost_manager_init() {
if (holders) { if (holders) {
FLOG("Double GL lost manager init"); FLOG("Double GL lost manager init");

View File

@ -7,6 +7,7 @@ class GfxResourceHolder {
public: public:
virtual ~GfxResourceHolder() {} virtual ~GfxResourceHolder() {}
virtual void GLRestore() = 0; virtual void GLRestore() = 0;
virtual void GLLost() = 0;
}; };
void gl_lost_manager_init(); void gl_lost_manager_init();
@ -15,5 +16,8 @@ void gl_lost_manager_shutdown();
void register_gl_resource_holder(GfxResourceHolder *holder); void register_gl_resource_holder(GfxResourceHolder *holder);
void unregister_gl_resource_holder(GfxResourceHolder *holder); void unregister_gl_resource_holder(GfxResourceHolder *holder);
// Notifies all objects it's time to forget / delete things.
void gl_lost();
// Notifies all objects that it's time to be restored. // Notifies all objects that it's time to be restored.
void gl_restore(); void gl_restore();

View File

@ -220,7 +220,7 @@ bool glsl_recompile(GLSLProgram *program, std::string *error_message) {
return true; return true;
} }
void GLSLProgram::GLRestore() { void GLSLProgram::GLLost() {
// Quoth http://developer.android.com/reference/android/opengl/GLSurfaceView.Renderer.html; // Quoth http://developer.android.com/reference/android/opengl/GLSurfaceView.Renderer.html;
// "Note that when the EGL context is lost, all OpenGL resources associated with that context will be automatically deleted. // "Note that when the EGL context is lost, all OpenGL resources associated with that context will be automatically deleted.
// You do not need to call the corresponding "glDelete" methods such as glDeleteTextures to manually delete these lost resources." // You do not need to call the corresponding "glDelete" methods such as glDeleteTextures to manually delete these lost resources."
@ -228,9 +228,12 @@ void GLSLProgram::GLRestore() {
// glDeleteShader(this->vsh_); // glDeleteShader(this->vsh_);
// glDeleteShader(this->fsh_); // glDeleteShader(this->fsh_);
// glDeleteProgram(this->program_); // glDeleteProgram(this->program_);
this->program_ = 0; program_ = 0;
this->vsh_ = 0; vsh_ = 0;
this->fsh_ = 0; fsh_ = 0;
}
void GLSLProgram::GLRestore() {
ILOG("Restoring GLSL program %s/%s", ILOG("Restoring GLSL program %s/%s",
strlen(this->vshader_filename) > 0 ? this->vshader_filename : "(mem)", strlen(this->vshader_filename) > 0 ? this->vshader_filename : "(mem)",
strlen(this->fshader_filename) > 0 ? this->fshader_filename : "(mem)"); strlen(this->fshader_filename) > 0 ? this->fshader_filename : "(mem)");

View File

@ -43,6 +43,7 @@ struct GLSLProgram : public GfxResourceHolder {
GLuint fsh_; GLuint fsh_;
GLuint program_; GLuint program_;
void GLLost() override;
void GLRestore() override; void GLRestore() override;
}; };

View File

@ -207,8 +207,12 @@ public:
glBindBuffer(target_, buffer_); glBindBuffer(target_, buffer_);
} }
void GLLost() override {
buffer_ = 0;
}
void GLRestore() override { void GLRestore() override {
ILOG("Recreating vertex buffer after glLost"); ILOG("Recreating vertex buffer after gl_restore");
knownSize_ = 0; // Will cause a new glBufferData call. Should genBuffers again though? knownSize_ = 0; // Will cause a new glBufferData call. Should genBuffers again though?
glGenBuffers(1, &buffer_); glGenBuffers(1, &buffer_);
} }
@ -235,6 +239,10 @@ public:
} }
const std::string &GetSource() const { return source_; } const std::string &GetSource() const { return source_; }
void Unset() {
shader_ = 0;
}
~Thin3DGLShader() { ~Thin3DGLShader() {
glDeleteShader(shader_); glDeleteShader(shader_);
} }
@ -257,7 +265,7 @@ bool Thin3DGLShader::Compile(const char *source) {
source = temp.c_str(); source = temp.c_str();
} }
glShaderSource(shader_, 1, &source, 0); glShaderSource(shader_, 1, &source, nullptr);
glCompileShader(shader_); glCompileShader(shader_);
GLint success = 0; GLint success = 0;
glGetShaderiv(shader_, GL_COMPILE_STATUS, &success); glGetShaderiv(shader_, GL_COMPILE_STATUS, &success);
@ -283,6 +291,7 @@ public:
void Unapply(); void Unapply();
void Compile(); void Compile();
void GLRestore() override; void GLRestore() override;
void GLLost() override;
bool RequiresBuffer() override { bool RequiresBuffer() override {
return id_ != 0; return id_ != 0;
} }
@ -323,6 +332,12 @@ public:
void SetVector(const char *name, float *value, int n) override; void SetVector(const char *name, float *value, int n) override;
void SetMatrix4x4(const char *name, const float value[16]) override; void SetMatrix4x4(const char *name, const float value[16]) override;
void GLLost() override {
program_ = 0;
vshader->Unset();
fshader->Unset();
}
void GLRestore() override { void GLRestore() override {
vshader->Compile(vshader->GetSource().c_str()); vshader->Compile(vshader->GetSource().c_str());
fshader->Compile(fshader->GetSource().c_str()); fshader->Compile(fshader->GetSource().c_str());
@ -550,15 +565,19 @@ public:
glBindTexture(target_, tex_); glBindTexture(target_, tex_);
} }
void GLRestore() override { void GLLost() override {
// We can assume that the texture is gone. // We can assume that the texture is gone.
tex_ = 0; tex_ = 0;
generatedMips_ = false; generatedMips_ = false;
}
void GLRestore() override {
if (!filename_.empty()) { if (!filename_.empty()) {
if (LoadFromFile(filename_.c_str())) { if (LoadFromFile(filename_.c_str())) {
ILOG("Reloaded lost texture %s", filename_.c_str()); ILOG("Reloaded lost texture %s", filename_.c_str());
} else { } else {
ELOG("Failed to reload lost texture %s", filename_.c_str()); ELOG("Failed to reload lost texture %s", filename_.c_str());
tex_ = 0;
} }
} else { } else {
WLOG("Texture %p cannot be restored - has no filename", this); WLOG("Texture %p cannot be restored - has no filename", this);
@ -663,6 +682,10 @@ void Thin3DGLVertexFormat::Compile() {
lastBase_ = -1; lastBase_ = -1;
} }
void Thin3DGLVertexFormat::GLLost() {
id_ = 0;
}
void Thin3DGLVertexFormat::GLRestore() { void Thin3DGLVertexFormat::GLRestore() {
Compile(); Compile();
} }

View File

@ -147,6 +147,14 @@ void ScreenManager::deviceLost() {
// TODO: Change this when it becomes necessary. // TODO: Change this when it becomes necessary.
} }
void ScreenManager::deviceRestore() {
for (size_t i = 0; i < stack_.size(); i++) {
stack_[i].screen->deviceRestore();
}
// Dialogs too? Nah, they should only use the standard UI texture anyway.
// TODO: Change this when it becomes necessary.
}
Screen *ScreenManager::topScreen() const { Screen *ScreenManager::topScreen() const {
if (!stack_.empty()) if (!stack_.empty())
return stack_.back().screen; return stack_.back().screen;

View File

@ -50,6 +50,7 @@ public:
virtual void render() {} virtual void render() {}
virtual void postRender() {} virtual void postRender() {}
virtual void deviceLost() {} virtual void deviceLost() {}
virtual void deviceRestore() {}
virtual void resized() {} virtual void resized() {}
virtual void dialogFinished(const Screen *dialog, DialogResult result) {} virtual void dialogFinished(const Screen *dialog, DialogResult result) {}
virtual bool touch(const TouchInput &touch) { return false; } virtual bool touch(const TouchInput &touch) { return false; }
@ -103,6 +104,7 @@ public:
void render(); void render();
void resized(); void resized();
void deviceLost(); void deviceLost();
void deviceRestore();
void shutdown(); void shutdown();
// Push a dialog box in front. Currently 1-level only. // Push a dialog box in front. Currently 1-level only.