OpenGLRenderer: Fix FboFramebuffer making context switches

When a window was created and rendered onto, FBO rendering was made on the device reference context which required a context switch.

This has been fixed with OpenGLFboFramebuffer managing a per-context framebuffer and creating one when needed
This commit is contained in:
SirLynix 2023-02-26 13:43:21 +01:00
parent 2e8ea0e887
commit 421e684344
5 changed files with 115 additions and 50 deletions

View File

@ -9,9 +9,11 @@
#include <Nazara/Prerequisites.hpp>
#include <Nazara/OpenGLRenderer/OpenGLFramebuffer.hpp>
#include <Nazara/OpenGLRenderer/Wrapper/Context.hpp>
#include <Nazara/OpenGLRenderer/Wrapper/Framebuffer.hpp>
#include <memory>
#include <vector>
#include <unordered_map>
namespace Nz
{
@ -22,14 +24,14 @@ namespace Nz
class NAZARA_OPENGLRENDERER_API OpenGLFboFramebuffer final : public OpenGLFramebuffer
{
public:
OpenGLFboFramebuffer(OpenGLDevice& device, const std::vector<std::shared_ptr<Texture>>& attachments);
OpenGLFboFramebuffer(OpenGLDevice& device, std::vector<std::shared_ptr<Texture>> attachments);
OpenGLFboFramebuffer(const OpenGLFboFramebuffer&) = delete;
OpenGLFboFramebuffer(OpenGLFboFramebuffer&&) noexcept = default;
OpenGLFboFramebuffer(OpenGLFboFramebuffer&&) = delete;
~OpenGLFboFramebuffer() = default;
void Activate() const override;
inline const Vector2ui& GetAttachmentSize(std::size_t i) const;
inline Vector2ui GetAttachmentSize(std::size_t i) const;
std::size_t GetColorBufferCount() const override;
@ -41,9 +43,20 @@ namespace Nz
OpenGLFboFramebuffer& operator=(OpenGLFboFramebuffer&&) = delete;
private:
GL::Framebuffer m_framebuffer;
GL::Framebuffer& CreateFramebuffer(const GL::Context& context) const;
struct ContextFramebuffer
{
GL::Framebuffer framebuffer;
NazaraSlot(GL::Context, OnContextDestruction, onContextDestruction);
};
std::size_t m_colorAttachmentCount;
std::vector<Vector2ui> m_attachmentSizes;
std::string m_debugName;
std::vector<std::shared_ptr<Texture>> m_attachments;
mutable std::unordered_map<const GL::Context*, ContextFramebuffer> m_framebuffers;
OpenGLDevice& m_device;
Vector2ui m_size;
};
}

View File

@ -7,9 +7,9 @@
namespace Nz
{
inline const Vector2ui& OpenGLFboFramebuffer::GetAttachmentSize(std::size_t i) const
inline Vector2ui OpenGLFboFramebuffer::GetAttachmentSize(std::size_t i) const
{
return m_attachmentSizes[i];
return Vector2ui(m_attachments[i]->GetSize());
}
}

View File

@ -199,6 +199,8 @@ namespace Nz::GL
static const Context* GetCurrentContext();
static bool SetCurrentContext(const Context* context);
NazaraSignal(OnContextDestruction, Context* /*context*/);
protected:
virtual bool Activate() const = 0;
virtual void Desactivate() const = 0;

View File

@ -11,29 +11,87 @@
namespace Nz
{
OpenGLFboFramebuffer::OpenGLFboFramebuffer(OpenGLDevice& device, const std::vector<std::shared_ptr<Texture>>& attachments) :
OpenGLFramebuffer(FramebufferType::Texture)
OpenGLFboFramebuffer::OpenGLFboFramebuffer(OpenGLDevice& device, std::vector<std::shared_ptr<Texture>> attachments) :
OpenGLFramebuffer(FramebufferType::Texture),
m_attachments(std::move(attachments)),
m_device(device)
{
if (!m_framebuffer.Create(device.GetReferenceContext()))
m_colorAttachmentCount = 0;
for (std::size_t i = 0; i < m_attachments.size(); ++i)
{
assert(m_attachments[i]);
Vector2ui textureSize = Vector2ui(m_attachments[i]->GetSize());
if (i == 0)
m_size = textureSize;
else
m_size.Minimize(textureSize);
PixelFormat textureFormat = m_attachments[i]->GetFormat();
if (PixelFormatInfo::GetContent(textureFormat) == PixelFormatContent::ColorRGBA)
m_colorAttachmentCount++;
}
// Create a framebuffer with the current context to ensure its completeness
const GL::Context* currentContext = GL::Context::GetCurrentContext();
if (!currentContext)
{
currentContext = &m_device.GetReferenceContext();
GL::Context::SetCurrentContext(currentContext);
}
CreateFramebuffer(*currentContext);
}
void OpenGLFboFramebuffer::Activate() const
{
const GL::Context* currentContext = GL::Context::GetCurrentContext();
if (!currentContext)
{
currentContext = &m_device.GetReferenceContext();
GL::Context::SetCurrentContext(currentContext);
}
GLuint fbo;
auto it = m_framebuffers.find(currentContext);
if (it == m_framebuffers.end())
fbo = CreateFramebuffer(*currentContext).GetObjectId();
else
fbo = it->second.framebuffer.GetObjectId();
currentContext->BindFramebuffer(GL::FramebufferTarget::Draw, fbo);
}
std::size_t OpenGLFboFramebuffer::GetColorBufferCount() const
{
return m_colorAttachmentCount;
}
const Vector2ui& OpenGLFboFramebuffer::GetSize() const
{
return m_size;
}
void OpenGLFboFramebuffer::UpdateDebugName(std::string_view name)
{
m_debugName = name;
for (auto&& [context, framebufferEntry] : m_framebuffers)
framebufferEntry.framebuffer.SetDebugName(m_debugName);
}
GL::Framebuffer& OpenGLFboFramebuffer::CreateFramebuffer(const GL::Context& context) const
{
GL::Framebuffer framebuffer;
if (!framebuffer.Create(context))
throw std::runtime_error("failed to create framebuffer object");
std::size_t colorAttachmentCount = 0;
bool hasDepth = false;
bool hasStencil = false;
m_attachmentSizes.resize(attachments.size());
for (std::size_t i = 0; i < attachments.size(); ++i)
for (std::size_t i = 0; i < m_attachments.size(); ++i)
{
assert(attachments[i]);
const OpenGLTexture& glTexture = static_cast<const OpenGLTexture&>(*attachments[i]);
Vector2ui textureSize = Vector2ui(glTexture.GetSize());
m_attachmentSizes[i] = textureSize;
if (i == 0)
m_size = textureSize;
else
m_size.Minimize(textureSize);
assert(m_attachments[i]);
const OpenGLTexture& glTexture = static_cast<const OpenGLTexture&>(*m_attachments[i]);
PixelFormat textureFormat = glTexture.GetFormat();
@ -94,31 +152,34 @@ namespace Nz
assert(texViewInfo.baseArrayLayer < faceTargets.size());
GLenum texTarget = faceTargets[texViewInfo.baseArrayLayer];
m_framebuffer.Texture2D(GL_COLOR_ATTACHMENT0, texTarget, parentTexture.GetTexture().GetObjectId(), texViewInfo.baseMipLevel);
framebuffer.Texture2D(GL_COLOR_ATTACHMENT0, texTarget, parentTexture.GetTexture().GetObjectId(), texViewInfo.baseMipLevel);
break;
}
case ImageType::E1D:
case ImageType::E2D:
m_framebuffer.Texture2D(GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, parentTexture.GetTexture().GetObjectId(), texViewInfo.baseMipLevel);
framebuffer.Texture2D(GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, parentTexture.GetTexture().GetObjectId(), texViewInfo.baseMipLevel);
break;
case ImageType::E1D_Array:
case ImageType::E2D_Array:
case ImageType::E3D:
m_framebuffer.TextureLayer(GL_COLOR_ATTACHMENT0, parentTexture.GetTexture().GetObjectId(), texViewInfo.baseArrayLayer, texViewInfo.baseMipLevel);
framebuffer.TextureLayer(GL_COLOR_ATTACHMENT0, parentTexture.GetTexture().GetObjectId(), texViewInfo.baseArrayLayer, texViewInfo.baseMipLevel);
break;
}
}
else
m_framebuffer.Texture2D(attachment, ToOpenGL(OpenGLTexture::ToTextureTarget(glTexture.GetType())), glTexture.GetTexture().GetObjectId());
framebuffer.Texture2D(attachment, ToOpenGL(OpenGLTexture::ToTextureTarget(glTexture.GetType())), glTexture.GetTexture().GetObjectId());
}
GLenum status = m_framebuffer.Check();
GLenum status = framebuffer.Check();
if (status != GL_FRAMEBUFFER_COMPLETE)
throw std::runtime_error("invalid framebuffer: 0x" + NumberToString(status, 16));
m_colorAttachmentCount = colorAttachmentCount;
if (!m_debugName.empty())
framebuffer.SetDebugName(m_debugName);
assert(m_colorAttachmentCount == colorAttachmentCount);
if (m_colorAttachmentCount > 0)
{
@ -126,34 +187,21 @@ namespace Nz
for (std::size_t i = 0; i < m_colorAttachmentCount; ++i)
fboDrawBuffers[i] = GLenum(GL_COLOR_ATTACHMENT0 + i);
m_framebuffer.DrawBuffers(SafeCast<GLsizei>(m_colorAttachmentCount), fboDrawBuffers.data());
framebuffer.DrawBuffers(SafeCast<GLsizei>(m_colorAttachmentCount), fboDrawBuffers.data());
}
else
{
GLenum buffer = GL_NONE;
m_framebuffer.DrawBuffers(1, &buffer);
framebuffer.DrawBuffers(1, &buffer);
}
}
void OpenGLFboFramebuffer::Activate() const
{
const GL::Context& context = m_framebuffer.EnsureContext();
auto& framebufferEntry = m_framebuffers[&context];
framebufferEntry.framebuffer = std::move(framebuffer);
framebufferEntry.onContextDestruction.Connect(context.OnContextDestruction, [this](GL::Context* context)
{
m_framebuffers.erase(context);
});
context.BindFramebuffer(GL::FramebufferTarget::Draw, m_framebuffer.GetObjectId());
}
std::size_t OpenGLFboFramebuffer::GetColorBufferCount() const
{
return m_colorAttachmentCount;
}
const Vector2ui& OpenGLFboFramebuffer::GetSize() const
{
return m_size;
}
void OpenGLFboFramebuffer::UpdateDebugName(std::string_view name)
{
m_framebuffer.SetDebugName(name);
return framebufferEntry.framebuffer;
}
}

View File

@ -1023,6 +1023,8 @@ namespace Nz::GL
void Context::OnContextRelease()
{
OnContextDestruction(this);
m_blitFramebuffers.reset();
m_vaoCache.Clear();
}