168 lines
3.8 KiB
C++
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
|