From 24048d1b241d4f0f346befdc68d97cfdb8ef012a Mon Sep 17 00:00:00 2001 From: assiduous Date: Thu, 28 Sep 2023 16:49:22 -0700 Subject: [PATCH] GL backend: use FBO cache in TextureBaseGL::CopyData --- .../GraphicsEngineOpenGL/include/FBOCache.hpp | 6 +- .../GraphicsEngineOpenGL/src/FBOCache.cpp | 89 ++++++++++++++++--- .../src/TextureBaseGL.cpp | 56 ++++-------- 3 files changed, 99 insertions(+), 52 deletions(-) diff --git a/Graphics/GraphicsEngineOpenGL/include/FBOCache.hpp b/Graphics/GraphicsEngineOpenGL/include/FBOCache.hpp index f044c89ec..761123b67 100644 --- a/Graphics/GraphicsEngineOpenGL/include/FBOCache.hpp +++ b/Graphics/GraphicsEngineOpenGL/include/FBOCache.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 Diligent Graphics LLC + * Copyright 2019-2023 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -36,6 +36,7 @@ namespace Diligent { +class TextureBaseGL; class TextureViewGLImpl; class GLContextState; @@ -66,6 +67,9 @@ class FBOCache const GLObjectWrappers::GLFrameBufferObj& GetFBO(Uint32 Width, Uint32 Height, GLContextState& ContextState); + // NOTE: the function may bind a framebuffer, so the FBO in the GL context state must be invalidated. + const GLObjectWrappers::GLFrameBufferObj& GetReadFBO(TextureBaseGL* pTex, Uint32 ArraySlice, Uint32 MipLevel); + void OnReleaseTexture(ITexture* pTexture); private: diff --git a/Graphics/GraphicsEngineOpenGL/src/FBOCache.cpp b/Graphics/GraphicsEngineOpenGL/src/FBOCache.cpp index d2b6ef23c..02aa4fb08 100644 --- a/Graphics/GraphicsEngineOpenGL/src/FBOCache.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/FBOCache.cpp @@ -67,18 +67,17 @@ std::size_t FBOCache::FBOCacheKeyHashFunc::operator()(const FBOCacheKey& Key) co { if (Key.Hash == 0) { - std::hash TexViewDescHasher; Key.Hash = 0; HashCombine(Key.Hash, Key.NumRenderTargets, Key.Width, Key.Height); 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.RTVDescs[rt]); } HashCombine(Key.Hash, Key.DSId); if (Key.DSId) - HashCombine(Key.Hash, TexViewDescHasher(Key.DSVDesc)); + HashCombine(Key.Hash, Key.DSVDesc); } return Key.Hash; } @@ -289,18 +288,18 @@ const GLObjectWrappers::GLFrameBufferObj& FBOCache::GetFBO(Uint32 Nu // 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"); + auto it_inserted = m_Cache.emplace(Key, std::move(NewFBO)); + // New FBO must be actually inserted + VERIFY(it_inserted.second, "New FBO was not inserted"); if (Key.DSId != 0) - m_TexIdToKey.insert(std::make_pair(Key.DSId, Key)); + m_TexIdToKey.emplace(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)); + m_TexIdToKey.emplace(Key.RTIds[rt], Key); } - fbo_it = NewElems.first; + fbo_it = it_inserted.first; } return fbo_it->second; } @@ -321,10 +320,74 @@ const GLObjectWrappers::GLFrameBufferObj& FBOCache::GetFBO(Uint32 Width, Uint32 // Create a new FBO auto NewFBO = CreateFBO(ContextState, 0, nullptr, nullptr, Width, Height); - 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"); - fbo_it = NewElems.first; + auto it_inserted = m_Cache.emplace(Key, std::move(NewFBO)); + // New FBO must be actually inserted + VERIFY(it_inserted.second, "New FBO was not inserted"); + fbo_it = it_inserted.first; + } + return fbo_it->second; +} + +inline GLenum GetFramebufferAttachmentPoint(TEXTURE_FORMAT Format) +{ + const auto& FmtAttribs = GetTextureFormatAttribs(Format); + switch (FmtAttribs.ComponentType) + { + case COMPONENT_TYPE_DEPTH: + return GL_DEPTH_ATTACHMENT; + case COMPONENT_TYPE_DEPTH_STENCIL: + return GL_DEPTH_STENCIL_ATTACHMENT; + default: + return GL_COLOR_ATTACHMENT0; + } +} + +const GLObjectWrappers::GLFrameBufferObj& FBOCache::GetReadFBO(TextureBaseGL* pTex, Uint32 ArraySlice, Uint32 MipLevel) +{ + const auto& TexDesc = pTex->GetDesc(); + + FBOCacheKey Key; + Key.NumRenderTargets = 1; + Key.RTIds[0] = pTex->GetUniqueID(); + + auto& RTV0{Key.RTVDescs[0]}; + RTV0.Format = TexDesc.Format; + RTV0.TextureDim = TexDesc.Type; + // Use SRV to make AttachToFramebuffer only attach to read framebuffer + RTV0.ViewType = TEXTURE_VIEW_SHADER_RESOURCE; + RTV0.FirstArraySlice = ArraySlice; + RTV0.MostDetailedMip = MipLevel; + RTV0.NumArraySlices = 1; + + // Lock the cache + Threading::SpinLockGuard CacheGuard{m_CacheLock}; + + // Try to find FBO in the map + auto fbo_it = m_Cache.find(Key); + if (fbo_it == m_Cache.end()) + { + // Create a new FBO + GLObjectWrappers::GLFrameBufferObj NewFBO{true}; + + glBindFramebuffer(GL_READ_FRAMEBUFFER, NewFBO); + DEV_CHECK_GL_ERROR("Failed to bind new FBO as read framebuffer"); + + pTex->AttachToFramebuffer(RTV0, GetFramebufferAttachmentPoint(TexDesc.Format)); + + GLenum Status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER); + if (Status != GL_FRAMEBUFFER_COMPLETE) + { + const Char* StatusString = GetFramebufferStatusString(Status); + LOG_ERROR("Read framebuffer is incomplete. FB status: ", StatusString); + UNEXPECTED("Read framebuffer is incomplete"); + } + + auto it_inserted = m_Cache.emplace(Key, std::move(NewFBO)); + // New FBO must be actually inserted + VERIFY(it_inserted.second, "New FBO was not inserted"); + m_TexIdToKey.emplace(Key.RTIds[0], Key); + + fbo_it = it_inserted.first; } return fbo_it->second; } diff --git a/Graphics/GraphicsEngineOpenGL/src/TextureBaseGL.cpp b/Graphics/GraphicsEngineOpenGL/src/TextureBaseGL.cpp index 5c8ebb3bf..2d04f8efe 100644 --- a/Graphics/GraphicsEngineOpenGL/src/TextureBaseGL.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/TextureBaseGL.cpp @@ -550,20 +550,6 @@ void TextureBaseGL::UpdateData(GLContextState& CtxState, Uint32 MipLevel, Uint32 //} // -inline GLenum GetFramebufferAttachmentPoint(TEXTURE_FORMAT Format) -{ - const auto& FmtAttribs = GetTextureFormatAttribs(Format); - switch (FmtAttribs.ComponentType) - { - case COMPONENT_TYPE_DEPTH: - return GL_DEPTH_ATTACHMENT; - case COMPONENT_TYPE_DEPTH_STENCIL: - return GL_DEPTH_STENCIL_ATTACHMENT; - default: - return GL_COLOR_ATTACHMENT0; - } -} - inline GLbitfield GetFramebufferCopyMask(TEXTURE_FORMAT Format) { const auto& FmtAttribs = GetTextureFormatAttribs(Format); @@ -640,38 +626,29 @@ void TextureBaseGL::CopyData(DeviceContextGLImpl* pDeviceCtxGL, #endif { auto& GLState = pDeviceCtxGL->GetContextState(); - // Invalidate FBO as we will use glBindFramebuffer directly - GLState.InvalidateFBO(); - - GLObjectWrappers::GLFrameBufferObj ReadFBO{pSrcTextureGL->GetGLHandle() != 0}; - glBindFramebuffer(GL_READ_FRAMEBUFFER, ReadFBO ? ReadFBO : pDeviceCtxGL->GetDefaultFBO()); - DEV_CHECK_GL_ERROR("Failed to bind read framebuffer"); for (Uint32 DepthSlice = 0; DepthSlice < pSrcBox->Depth(); ++DepthSlice) { - if (ReadFBO) + GLuint SrcFboHandle = 0; + if (pSrcTextureGL->GetGLHandle() != 0) { - // Attach source subimage to read framebuffer - TextureViewDesc SRVVDesc; - SRVVDesc.TextureDim = SrcTexDesc.Type; - SRVVDesc.ViewType = TEXTURE_VIEW_SHADER_RESOURCE; + // Get read framebuffer for the source subimage + + auto& FBOCache = m_pDevice->GetFBOCache(GLState.GetCurrentGLContext()); VERIFY_EXPR(SrcSlice == 0 || SrcTexDesc.IsArray()); VERIFY_EXPR((pSrcBox->MinZ == 0 && DepthSlice == 0) || SrcTexDesc.Is3D()); - SRVVDesc.FirstArraySlice = SrcSlice + pSrcBox->MinZ + DepthSlice; - SRVVDesc.MostDetailedMip = SrcMipLevel; - SRVVDesc.NumArraySlices = 1; - ValidatedAndCorrectTextureViewDesc(m_Desc, SRVVDesc); - - pSrcTextureGL->AttachToFramebuffer(SRVVDesc, GetFramebufferAttachmentPoint(SrcTexDesc.Format)); + const auto SrcFramebufferSlice = SrcSlice + pSrcBox->MinZ + DepthSlice; + // NOTE: GetReadFBO may bind a framebuffer. + const auto& ReadFBO = FBOCache.GetReadFBO(pSrcTextureGL, SrcFramebufferSlice, SrcMipLevel); - GLenum Status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER); - if (Status != GL_FRAMEBUFFER_COMPLETE) - { - const Char* StatusString = GetFramebufferStatusString(Status); - LOG_ERROR("Read framebuffer is incomplete. FB status: ", StatusString); - UNEXPECTED("Read framebuffer is incomplete"); - } + SrcFboHandle = ReadFBO; } + else + { + SrcFboHandle = pDeviceCtxGL->GetDefaultFBO(); + } + glBindFramebuffer(GL_READ_FRAMEBUFFER, SrcFboHandle); + DEV_CHECK_GL_ERROR("Failed to bind read framebuffer"); if (!IsDefaultBackBuffer) { @@ -711,6 +688,9 @@ void TextureBaseGL::CopyData(DeviceContextGLImpl* pDeviceCtxGL, } } + // Invalidate FBO as we used glBindFramebuffer directly + GLState.InvalidateFBO(); + GLState.BindTexture(-1, GetBindTarget(), GLObjectWrappers::GLTextureObj::Null()); pDeviceCtxGL->CommitRenderTargets(); }