diff --git a/Source/Engine/Graphics/RenderBuffers.cpp b/Source/Engine/Graphics/RenderBuffers.cpp index e0f4869ad..7ee900858 100644 --- a/Source/Engine/Graphics/RenderBuffers.cpp +++ b/Source/Engine/Graphics/RenderBuffers.cpp @@ -192,6 +192,9 @@ bool RenderBuffers::Init(int32 width, int32 height) _viewport = Viewport(0, 0, static_cast(width), static_cast(height)); LastEyeAdaptationTime = 0; + // Flush any pool render targets to prevent over-allocating GPU memory when resizing game viewport + RenderTargetPool::Flush(false, 4); + return result; } diff --git a/Source/Engine/Graphics/RenderTargetPool.cpp b/Source/Engine/Graphics/RenderTargetPool.cpp index fe22b4eaf..891610ab8 100644 --- a/Source/Engine/Graphics/RenderTargetPool.cpp +++ b/Source/Engine/Graphics/RenderTargetPool.cpp @@ -7,35 +7,31 @@ struct Entry { - bool IsOccupied; GPUTexture* RT; - uint64 LastFrameTaken; uint64 LastFrameReleased; uint32 DescriptionHash; + bool IsOccupied; }; namespace { - Array TemporaryRTs(64); + Array TemporaryRTs; } -void RenderTargetPool::Flush(bool force) +void RenderTargetPool::Flush(bool force, int32 framesOffset) { - const uint64 framesOffset = 3 * 60; + if (framesOffset < 0) + framesOffset = 3 * 60; // For how many frames RTs should be cached (by default) const uint64 maxReleaseFrame = Engine::FrameCount - framesOffset; force |= Engine::ShouldExit(); for (int32 i = 0; i < TemporaryRTs.Count(); i++) { - auto& tmp = TemporaryRTs[i]; - - if (!tmp.IsOccupied && (force || (tmp.LastFrameReleased < maxReleaseFrame))) + const auto& e = TemporaryRTs[i]; + if (!e.IsOccupied && (force || e.LastFrameReleased < maxReleaseFrame)) { - // Release - tmp.RT->DeleteObjectNow(); - TemporaryRTs.RemoveAt(i); - i--; - + e.RT->DeleteObjectNow(); + TemporaryRTs.RemoveAt(i--); if (TemporaryRTs.IsEmpty()) break; } @@ -48,19 +44,14 @@ GPUTexture* RenderTargetPool::Get(const GPUTextureDescription& desc) const uint32 descHash = GetHash(desc); for (int32 i = 0; i < TemporaryRTs.Count(); i++) { - auto& tmp = TemporaryRTs[i]; - - if (!tmp.IsOccupied && tmp.DescriptionHash == descHash) + auto& e = TemporaryRTs[i]; + if (!e.IsOccupied && e.DescriptionHash == descHash) { - ASSERT(tmp.RT); - // Mark as used - tmp.IsOccupied = true; - tmp.LastFrameTaken = Engine::FrameCount; - return tmp.RT; + e.IsOccupied = true; + return e.RT; } } - #if !BUILD_RELEASE if (TemporaryRTs.Count() > 2000) { @@ -71,24 +62,23 @@ GPUTexture* RenderTargetPool::Get(const GPUTextureDescription& desc) // Create new rt const String name = TEXT("TemporaryRT_") + StringUtils::ToString(TemporaryRTs.Count()); - auto newRenderTarget = GPUDevice::Instance->CreateTexture(name); - if (newRenderTarget->Init(desc)) + GPUTexture* rt = GPUDevice::Instance->CreateTexture(name); + if (rt->Init(desc)) { - Delete(newRenderTarget); + Delete(rt); LOG(Error, "Cannot create temporary render target. Description: {0}", desc.ToString()); return nullptr; } // Create temporary rt entry - Entry entry; - entry.IsOccupied = true; - entry.LastFrameReleased = 0; - entry.LastFrameTaken = Engine::FrameCount; - entry.RT = newRenderTarget; - entry.DescriptionHash = descHash; - TemporaryRTs.Add(entry); + Entry e; + e.IsOccupied = true; + e.LastFrameReleased = 0; + e.RT = rt; + e.DescriptionHash = descHash; + TemporaryRTs.Add(e); - return newRenderTarget; + return rt; } void RenderTargetPool::Release(GPUTexture* rt) @@ -98,14 +88,13 @@ void RenderTargetPool::Release(GPUTexture* rt) for (int32 i = 0; i < TemporaryRTs.Count(); i++) { - auto& tmp = TemporaryRTs[i]; - - if (tmp.RT == rt) + auto& e = TemporaryRTs[i]; + if (e.RT == rt) { // Mark as free - ASSERT(tmp.IsOccupied); - tmp.IsOccupied = false; - tmp.LastFrameReleased = Engine::FrameCount; + ASSERT(e.IsOccupied); + e.IsOccupied = false; + e.LastFrameReleased = Engine::FrameCount; return; } } diff --git a/Source/Engine/Graphics/RenderTargetPool.h b/Source/Engine/Graphics/RenderTargetPool.h index 345e5c81e..4ff69e2ea 100644 --- a/Source/Engine/Graphics/RenderTargetPool.h +++ b/Source/Engine/Graphics/RenderTargetPool.h @@ -15,7 +15,8 @@ public: /// Flushes the temporary render targets. /// /// True if release unused render targets by force, otherwise will use a few frames of delay. - static void Flush(bool force = false); + /// Amount of previous frames that should persist in the pool after flush. Resources used more than given value wil be freed. Use value of -1 to auto pick default duration. + static void Flush(bool force = false, int32 framesOffset = -1); /// /// Gets a temporary render target.