// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #pragma once #include "gpu_device.h" #include "gpu_texture.h" #include class GPUFramebufferManagerBase { protected: struct Key { GPUTexture* rts[GPUDevice::MAX_RENDER_TARGETS]; GPUTexture* ds; u32 num_rts; u32 flags; bool operator==(const Key& rhs) const; bool operator!=(const Key& rhs) const; bool ContainsRT(const GPUTexture* tex) const; }; struct KeyHash { size_t operator()(const Key& key) const; }; }; template class GPUFramebufferManager : public GPUFramebufferManagerBase { public: GPUFramebufferManager() = default; ~GPUFramebufferManager(); FBOType Lookup(GPUTexture* const* rts, u32 num_rts, GPUTexture* ds, u32 flags); void RemoveReferences(const GPUTexture* tex); void RemoveRTReferences(const GPUTexture* tex); void RemoveDSReferences(const GPUTexture* tex); void Clear(); private: using MapType = std::unordered_map; MapType m_map; }; template GPUFramebufferManager::~GPUFramebufferManager() { Clear(); } template FBOType GPUFramebufferManager::Lookup(GPUTexture* const* rts, u32 num_rts, GPUTexture* ds, u32 flags) { Key key; for (u32 i = 0; i < num_rts; i++) key.rts[i] = rts[i]; for (u32 i = num_rts; i < GPUDevice::MAX_RENDER_TARGETS; i++) key.rts[i] = nullptr; key.ds = ds; key.num_rts = num_rts; key.flags = flags; auto it = m_map.find(key); if (it == m_map.end()) { FBOType fbo = FactoryFunc(rts, num_rts, ds, flags); if (!fbo) return fbo; it = m_map.emplace(key, fbo).first; } return it->second; } template void GPUFramebufferManager::RemoveRTReferences(const GPUTexture* tex) { DebugAssert(tex->IsRenderTarget()); for (auto it = m_map.begin(); it != m_map.end();) { if (!it->first.ContainsRT(tex)) { ++it; continue; } DestroyFunc(it->second); it = m_map.erase(it); } } template void GPUFramebufferManager::RemoveDSReferences(const GPUTexture* tex) { DebugAssert(tex->IsDepthStencil()); for (auto it = m_map.begin(); it != m_map.end();) { if (it->first.ds != tex) { ++it; continue; } DestroyFunc(it->second); it = m_map.erase(it); } } template void GPUFramebufferManager::RemoveReferences(const GPUTexture* tex) { if (tex->IsRenderTarget()) RemoveRTReferences(tex); else if (tex->IsDepthStencil()) RemoveDSReferences(tex); } template void GPUFramebufferManager::Clear() { for (auto it : m_map) DestroyFunc(it.second); m_map.clear(); }