diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 845036945c..59eb3184c1 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -207,6 +207,19 @@ void TextureCacheGLES::StartFrame() { InvalidateLastTexture(); timesInvalidatedAllThisFrame_ = 0; + GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + if (!lowMemoryMode_ && renderManager->SawOutOfMemory()) { + lowMemoryMode_ = true; + decimationCounter_ = 0; + + I18NCategory *err = GetI18NCategory("Error"); + if (standardScaleFactor_ > 1) { + host->NotifyUserMessage(err->T("Warning: Video memory FULL, reducing upscaling and switching to slow caching mode"), 2.0f); + } else { + host->NotifyUserMessage(err->T("Warning: Video memory FULL, switching to slow caching mode"), 2.0f); + } + } + if (texelsScaledThisFrame_) { // INFO_LOG(G3D, "Scaled %i texels", texelsScaledThisFrame_); } @@ -803,11 +816,6 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r // glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, w, h, components2, dstFmt, pixelData); } else { PROFILE_THIS_SCOPE("loadtex"); - // Avoid misleading errors in texture upload, these are common. - GLenum err = glGetError(); - if (err) { - WARN_LOG(G3D, "Got an error BEFORE texture upload: %08x (%s)", err, GLEnumToString(err).c_str()); - } if (IsFakeMipmapChange()) render_->TextureImage(entry.textureName, 0, w, h, components, components2, dstFmt, pixelData); else diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 7fc9cb5fdf..83820ff7e4 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -23,6 +23,9 @@ GLuint g_defaultFBO = 0; void GLQueueRunner::CreateDeviceObjects() { glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropyLevel_); glGenVertexArrays(1, &globalVAO_); + + // An eternal optimist. + sawOutOfMemory_ = false; } void GLQueueRunner::DestroyDeviceObjects() { @@ -36,6 +39,7 @@ void GLQueueRunner::DestroyDeviceObjects() { void GLQueueRunner::RunInitSteps(const std::vector &steps) { glActiveTexture(GL_TEXTURE0); GLuint boundTexture = (GLuint)-1; + bool allocatedTextures = false; for (int i = 0; i < steps.size(); i++) { const GLRInitStep &step = steps[i]; @@ -197,6 +201,7 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { { boundTexture = (GLuint)-1; InitCreateFramebuffer(step); + allocatedTextures = true; break; } case GLRInitStepType::TEXTURE_IMAGE: @@ -211,6 +216,7 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { Crash(); // For things to show in RenderDoc, need to split into glTexImage2D(..., nullptr) and glTexSubImage. glTexImage2D(tex->target, step.texture_image.level, step.texture_image.internalFormat, step.texture_image.width, step.texture_image.height, 0, step.texture_image.format, step.texture_image.type, step.texture_image.data); + allocatedTextures = true; delete[] step.texture_image.data; CHECK_GL_ERROR_IF_DEBUG(); tex->wrapS = GL_CLAMP_TO_EDGE; @@ -242,6 +248,21 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { break; } } + + // TODO: Use GL_KHR_no_error or a debug callback, where supported? + if (allocatedTextures) { + // Users may use replacements or scaling, with high render resolutions, and run out of VRAM. + // This detects that, rather than looking like PPSSPP is broken. + // Calling glGetError() isn't great, but at the end of init shouldn't be too bad... + GLenum err = glGetError(); + if (err == GL_OUT_OF_MEMORY) { + WARN_LOG_REPORT(G3D, "GL ran out of GPU memory; switching to low memory mode"); + sawOutOfMemory_ = true; + } else if (err != GL_NO_ERROR) { + // We checked the err anyway, might as well log if there is one. + WARN_LOG(G3D, "Got an error after init: %08x (%s)", err, GLEnumToString(err).c_str()); + } + } } void GLQueueRunner::InitCreateFramebuffer(const GLRInitStep &step) { diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index 498177c178..e04f2f495e 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -328,6 +328,10 @@ public: targetWidth_ = width; targetHeight_ = height; } + + bool SawOutOfMemory() { + return sawOutOfMemory_; + } private: void InitCreateFramebuffer(const GLRInitStep &step); @@ -374,4 +378,6 @@ private: GLuint AllocTextureName(); // Texture name cache. Ripped straight from TextureCacheGLES. std::vector nameCache_; + + bool sawOutOfMemory_ = false; }; diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index 90502cbb24..abc4fb7ced 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -653,6 +653,10 @@ public: void StopThread(); + bool SawOutOfMemory() { + return queueRunner_.SawOutOfMemory(); + } + private: void BeginSubmitFrame(int frame); void EndSubmitFrame(int frame);