Skip to content

Commit

Permalink
GL backend: use FBO cache in TextureBaseGL::CopyData
Browse files Browse the repository at this point in the history
  • Loading branch information
TheMostDiligent committed Sep 28, 2023
1 parent 83db67b commit 25e40e9
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 50 deletions.
6 changes: 5 additions & 1 deletion Graphics/GraphicsEngineOpenGL/include/FBOCache.hpp
Original file line number Diff line number Diff line change
@@ -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");
Expand Down Expand Up @@ -36,6 +36,7 @@
namespace Diligent
{

class TextureBaseGL;
class TextureViewGLImpl;
class GLContextState;

Expand Down Expand Up @@ -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:
Expand Down
89 changes: 76 additions & 13 deletions Graphics/GraphicsEngineOpenGL/src/FBOCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,18 +67,17 @@ std::size_t FBOCache::FBOCacheKeyHashFunc::operator()(const FBOCacheKey& Key) co
{
if (Key.Hash == 0)
{
std::hash<TextureViewDesc> 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;
}
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand Down
51 changes: 15 additions & 36 deletions Graphics/GraphicsEngineOpenGL/src/TextureBaseGL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -643,35 +629,28 @@ void TextureBaseGL::CopyData(DeviceContextGLImpl* pDeviceCtxGL,
// 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 FboHandle = 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");
}
FboHandle = ReadFBO;
}
else
{
FboHandle = pDeviceCtxGL->GetDefaultFBO();
}
glBindFramebuffer(GL_READ_FRAMEBUFFER, FboHandle);
DEV_CHECK_GL_ERROR("Failed to bind read framebuffer");

if (!IsDefaultBackBuffer)
{
Expand Down

0 comments on commit 25e40e9

Please sign in to comment.