// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "RenderTargetPool.h" #include "GPUDevice.h" #include "Engine/Engine/Engine.h" struct Entry { bool IsOccupied; GPUTexture* RT; uint64 LastFrameTaken; uint64 LastFrameReleased; uint32 DescriptionHash; }; namespace { Array TemporaryRTs(64); } void RenderTargetPool::Flush(bool force) { const uint64 framesOffset = 10; 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))) { // Release tmp.RT->DeleteObjectNow(); TemporaryRTs.RemoveAt(i); i--; if (TemporaryRTs.IsEmpty()) break; } } } GPUTexture* RenderTargetPool::Get(const GPUTextureDescription& desc) { // Find free render target with the same properties const uint32 descHash = GetHash(desc); for (int32 i = 0; i < TemporaryRTs.Count(); i++) { auto& tmp = TemporaryRTs[i]; if (!tmp.IsOccupied && tmp.DescriptionHash == descHash) { ASSERT(tmp.RT); // Mark as used tmp.IsOccupied = true; tmp.LastFrameTaken = Engine::FrameCount; return tmp.RT; } } #if !BUILD_RELEASE if (TemporaryRTs.Count() > 2000) { LOG(Fatal, "Too many textures allocated in RenderTargetPool. Know your limits, sir!"); return nullptr; } #endif // Create new rt const String name = TEXT("TemporaryRT_") + StringUtils::ToString(TemporaryRTs.Count()); auto newRenderTarget = GPUDevice::Instance->CreateTexture(name); if (newRenderTarget->Init(desc)) { 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); return newRenderTarget; } void RenderTargetPool::Release(GPUTexture* rt) { if (!rt) return; for (int32 i = 0; i < TemporaryRTs.Count(); i++) { auto& tmp = TemporaryRTs[i]; if (tmp.RT == rt) { // Mark as free ASSERT(tmp.IsOccupied); tmp.IsOccupied = false; tmp.LastFrameReleased = Engine::FrameCount; return; } } LOG(Error, "Trying to release temporary render target which has not been registred in service!"); }