Files
FlaxEngine/Source/Engine/GraphicsDevice/OpenGL/FBOCache.cpp
2020-12-07 23:40:54 +01:00

168 lines
3.8 KiB
C++

// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "FBOCache.h"
#if GRAPHICS_API_OPENGL
#include "TextureOGL.h"
#include "RenderToolsOGL.h"
FBOCache::FBOCache()
: Table(2048)
{
}
FBOCache::~FBOCache()
{
Dispose();
}
GLuint FBOCache::GetFBO(uint32 rtCount, GPUTextureViewOGL* depthStencil, GPUTextureViewOGL* rts[])
{
ASSERT(rtCount > 0 || depthStencil != nullptr);
Key key(rtCount, depthStencil, rts);
GLuint fbo;
if (!Table.TryGet(key, fbo))
{
// Create new FBO
glGenFramebuffers(1, &fbo);
VALIDATE_OPENGL_RESULT();
// Bind FBO
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
VALIDATE_OPENGL_RESULT();
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
VALIDATE_OPENGL_RESULT();
// Initialize FBO
for (int32 i = 0; i < rtCount; i++)
{
rts[i]->AttachToFramebuffer(GL_COLOR_ATTACHMENT0 + i);
}
if(depthStencil)
{
GLenum attachmentPoint = PixelFormatExtensions::HasStencil(depthStencil->GetFormat()) ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT;
depthStencil->AttachToFramebuffer(attachmentPoint);
}
// We now need to set mapping between shader outputs and
// color attachments. This largely redundant step is performed
// by glDrawBuffers()
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
};
// 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(rtCount, DrawBuffers);
VALIDATE_OPENGL_RESULT();
// Validate
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
const Char* str = TEXT("Unknown");
switch (status)
{
#define STATUS_TO_STR(name) case name: str = TEXT(#name); break
STATUS_TO_STR(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
STATUS_TO_STR(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT);
STATUS_TO_STR(GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER);
STATUS_TO_STR(GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER);
STATUS_TO_STR(GL_FRAMEBUFFER_UNSUPPORTED);
STATUS_TO_STR(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE);
STATUS_TO_STR(GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS);
#undef STATUS_TO_STR
}
LOG(Error, "Framebuffer is incomplete. Status: {0}", str);
}
// Register in cache
Table.Add(key, fbo);
}
return fbo;
}
void FBOCache::OnTextureRelease(TextureOGL* texture)
{
for (auto i = Table.Begin(); i.IsNotEnd(); ++i)
{
if (i->Key.HasReference(texture))
{
GLuint fbo = i->Value;
glDeleteFramebuffers(1, &fbo);
Table.Remove(i);
}
}
}
void FBOCache::Dispose()
{
for (auto i = Table.Begin(); i.IsNotEnd(); ++i)
{
GLuint fbo = i->Value;
glDeleteFramebuffers(1, &fbo);
}
Table.Clear();
}
FBOCache::Key::Key(uint32 rtCount, GPUTextureViewOGL* depthStencil, GPUTextureViewOGL* rts[])
{
Hash = rtCount * 371;
HashCombinePointer(Hash, depthStencil);
RTCount = rtCount;
DepthStencil = depthStencil;
for (int32 i = 0; i < rtCount; i++)
{
RT[i] = rts[i];
HashCombinePointer(Hash, rts[i]);
}
}
bool FBOCache::Key::HasReference(TextureOGL* texture)
{
for (int32 i = 0; i < RTCount; i++)
{
if (RT[i] && RT[i]->GetTexture() == texture)
return true;
}
return (DepthStencil && DepthStencil->GetTexture() == texture);
}
bool FBOCache::Key::operator==(const Key & other) const
{
if (Hash != 0 && other.Hash != 0 && Hash != other.Hash)
return false;
if (RTCount != other.RTCount)
return false;
for (int32 rt = 0; rt < RTCount; rt++)
{
if (RT[rt] != other.RT[rt])
return false;
}
return DepthStencil == other.DepthStencil;
}
#endif