/* * Copyright 2019-2021 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * In no event and under no legal theory, whether in tort (including negligence), * contract, or otherwise, unless required by applicable law (such as deliberate * and grossly negligent acts) or agreed to in writing, shall any Contributor be * liable for any damages, including any direct, indirect, special, incidental, * or consequential damages of any character arising as a result of this License or * out of the use or inability to use the software (including but not limited to damages * for loss of goodwill, work stoppage, computer failure or malfunction, or any and * all other commercial damages or losses), even if such Contributor has been advised * of the possibility of such damages. */ #include "pch.h" #include "FBOCache.hpp" #include "RenderDeviceGLImpl.hpp" #include "TextureBaseGL.hpp" #include "GLContextState.hpp" namespace Diligent { bool FBOCache::FBOCacheKey::operator==(const FBOCacheKey& Key) const { if (Hash != 0 && Key.Hash != 0 && Hash != Key.Hash) return false; if (NumRenderTargets != Key.NumRenderTargets) return false; for (Uint32 rt = 0; rt < NumRenderTargets; ++rt) { if (RTIds[rt] != Key.RTIds[rt]) return false; if (RTIds[rt]) { if (!(RTVDescs[rt] == Key.RTVDescs[rt])) return false; } } if (DSId != Key.DSId) return false; if (DSId) { if (!(DSVDesc == Key.DSVDesc)) return false; } return true; } std::size_t FBOCache::FBOCacheKeyHashFunc::operator()(const FBOCacheKey& Key) const { if (Key.Hash == 0) { std::hash TexViewDescHasher; Key.Hash = 0; HashCombine(Key.Hash, Key.NumRenderTargets); for (Uint32 rt = 0; rt < Key.NumRenderTargets; ++rt) { HashCombine(Key.Hash, Key.RTIds[rt]); if (Key.RTIds[rt]) HashCombine(Key.Hash, TexViewDescHasher(Key.RTVDescs[rt])); } HashCombine(Key.Hash, Key.DSId); if (Key.DSId) HashCombine(Key.Hash, TexViewDescHasher(Key.DSVDesc)); } return Key.Hash; } FBOCache::FBOCache() { m_Cache.max_load_factor(0.5f); m_TexIdToKey.max_load_factor(0.5f); } FBOCache::~FBOCache() { VERIFY(m_Cache.empty(), "FBO cache is not empty. Are there any unreleased objects?"); VERIFY(m_TexIdToKey.empty(), "TexIdToKey cache is not empty."); } void FBOCache::OnReleaseTexture(ITexture* pTexture) { ThreadingTools::LockHelper CacheLock(m_CacheLockFlag); auto* pTexGL = ValidatedCast(pTexture); // Find all FBOs that this texture used in auto EqualRange = m_TexIdToKey.equal_range(pTexGL->GetUniqueID()); for (auto It = EqualRange.first; It != EqualRange.second; ++It) { m_Cache.erase(It->second); } m_TexIdToKey.erase(EqualRange.first, EqualRange.second); } GLObjectWrappers::GLFrameBufferObj FBOCache::CreateFBO(GLContextState& ContextState, Uint32 NumRenderTargets, TextureViewGLImpl* ppRTVs[], TextureViewGLImpl* pDSV) { GLObjectWrappers::GLFrameBufferObj FBO{true}; ContextState.BindFBO(FBO); // Initialize the FBO for (Uint32 rt = 0; rt < NumRenderTargets; ++rt) { if (auto* pRTView = ppRTVs[rt]) { const auto& RTVDesc = pRTView->GetDesc(); auto* pColorTexGL = pRTView->GetTexture(); pColorTexGL->AttachToFramebuffer(RTVDesc, GL_COLOR_ATTACHMENT0 + rt); } } if (pDSV != nullptr) { const auto& DSVDesc = pDSV->GetDesc(); auto* pDepthTexGL = pDSV->GetTexture(); GLenum AttachmentPoint = 0; if (DSVDesc.Format == TEX_FORMAT_D32_FLOAT || DSVDesc.Format == TEX_FORMAT_D16_UNORM) { #ifdef DILIGENT_DEBUG { const auto GLTexFmt = pDepthTexGL->GetGLTexFormat(); VERIFY(GLTexFmt == GL_DEPTH_COMPONENT32F || GLTexFmt == GL_DEPTH_COMPONENT16, "Inappropriate internal texture format (", GLTexFmt, ") for depth attachment. GL_DEPTH_COMPONENT32F or GL_DEPTH_COMPONENT16 is expected"); } #endif AttachmentPoint = GL_DEPTH_ATTACHMENT; } else if (DSVDesc.Format == TEX_FORMAT_D32_FLOAT_S8X24_UINT || DSVDesc.Format == TEX_FORMAT_D24_UNORM_S8_UINT) { #ifdef DILIGENT_DEBUG { const auto GLTexFmt = pDepthTexGL->GetGLTexFormat(); VERIFY(GLTexFmt == GL_DEPTH24_STENCIL8 || GLTexFmt == GL_DEPTH32F_STENCIL8, "Inappropriate internal texture format (", GLTexFmt, ") for depth-stencil attachment. GL_DEPTH24_STENCIL8 or GL_DEPTH32F_STENCIL8 is expected"); } #endif AttachmentPoint = GL_DEPTH_STENCIL_ATTACHMENT; } else { UNEXPECTED(GetTextureFormatAttribs(DSVDesc.Format).Name, " is not valid depth-stencil view format"); } pDepthTexGL->AttachToFramebuffer(DSVDesc, AttachmentPoint); } // We now need to set mapping between shader outputs and // color attachments. This largely redundant step is performed // by glDrawBuffers() // clang-format off static const GLenum DrawBuffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3, GL_COLOR_ATTACHMENT4, GL_COLOR_ATTACHMENT5, GL_COLOR_ATTACHMENT6, GL_COLOR_ATTACHMENT7, GL_COLOR_ATTACHMENT8, GL_COLOR_ATTACHMENT9, GL_COLOR_ATTACHMENT10, GL_COLOR_ATTACHMENT11, GL_COLOR_ATTACHMENT12, GL_COLOR_ATTACHMENT13, GL_COLOR_ATTACHMENT14, GL_COLOR_ATTACHMENT15 }; // clang-format on // The state set by glDrawBuffers() is part of the state of the framebuffer. // So it can be set up once and left it set. glDrawBuffers(NumRenderTargets, DrawBuffers); CHECK_GL_ERROR("Failed to set draw buffers via glDrawBuffers()"); GLenum Status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (Status != GL_FRAMEBUFFER_COMPLETE) { const Char* StatusString = "Unknown"; switch (Status) { // clang-format off case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: StatusString = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; break; case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: StatusString = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; break; case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: StatusString = "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER"; break; case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: StatusString = "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER"; break; case GL_FRAMEBUFFER_UNSUPPORTED: StatusString = "GL_FRAMEBUFFER_UNSUPPORTED"; break; case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: StatusString = "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"; break; case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: StatusString = "GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS"; break; // clang-format on } LOG_ERROR("Framebuffer is incomplete. FB status: ", StatusString); UNEXPECTED("Framebuffer is incomplete"); } return FBO; } const GLObjectWrappers::GLFrameBufferObj& FBOCache::GetFBO(Uint32 NumRenderTargets, TextureViewGLImpl* ppRTVs[], TextureViewGLImpl* pDSV, GLContextState& ContextState) { // Pop null render targets from the end of the list while (NumRenderTargets > 0 && ppRTVs[NumRenderTargets - 1] == nullptr) --NumRenderTargets; VERIFY(NumRenderTargets != 0 || pDSV != nullptr, "At least one render target or a depth-stencil buffer must be provided"); // Lock the cache ThreadingTools::LockHelper CacheLock(m_CacheLockFlag); // Construct the key FBOCacheKey Key; VERIFY(NumRenderTargets < MAX_RENDER_TARGETS, "Too many render targets are being set"); NumRenderTargets = std::min(NumRenderTargets, MAX_RENDER_TARGETS); Key.NumRenderTargets = NumRenderTargets; for (Uint32 rt = 0; rt < NumRenderTargets; ++rt) { auto* pRTView = ppRTVs[rt]; if (pRTView == nullptr) continue; auto* pColorTexGL = pRTView->GetTexture(); pColorTexGL->TextureMemoryBarrier( MEMORY_BARRIER_FRAMEBUFFER, // Reads and writes via framebuffer object attachments after the // barrier will reflect data written by shaders prior to the barrier. // Additionally, framebuffer writes issued after the barrier will wait // on the completion of all shader writes issued prior to the barrier. ContextState); Key.RTIds[rt] = pColorTexGL->GetUniqueID(); Key.RTVDescs[rt] = pRTView->GetDesc(); } if (pDSV) { auto* pDepthTexGL = pDSV->GetTexture(); pDepthTexGL->TextureMemoryBarrier(MEMORY_BARRIER_FRAMEBUFFER, ContextState); Key.DSId = pDepthTexGL->GetUniqueID(); Key.DSVDesc = pDSV->GetDesc(); } // Try to find FBO in the map auto It = m_Cache.find(Key); if (It != m_Cache.end()) { return It->second; } else { // Create a new FBO auto NewFBO = CreateFBO(ContextState, NumRenderTargets, ppRTVs, pDSV); auto NewElems = m_Cache.emplace(std::make_pair(Key, std::move(NewFBO))); // New element must be actually inserted VERIFY(NewElems.second, "New element was not inserted"); if (Key.DSId != 0) m_TexIdToKey.insert(std::make_pair(Key.DSId, Key)); for (Uint32 rt = 0; rt < NumRenderTargets; ++rt) { if (Key.RTIds[rt] != 0) m_TexIdToKey.insert(std::make_pair(Key.RTIds[rt], Key)); } return NewElems.first->second; } } } // namespace Diligent