Skip to content

Commit

Permalink
[GL] Fixed resolving of multi-sampled render-target on EndRenderPass().
Browse files Browse the repository at this point in the history
Multi-sampled render-targets must be resolved on EndRenderPass(), not just on the next BeginRenderPass() call.
Otherwise, rendering to a texture once (with a multi-sampled render target) and then reading its content via ReadTexture() will fail.
  • Loading branch information
LukasBanana committed Jul 5, 2024
1 parent 03a8000 commit b50c4e9
Show file tree
Hide file tree
Showing 12 changed files with 53 additions and 15 deletions.
5 changes: 5 additions & 0 deletions sources/Renderer/OpenGL/Command/GLCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,11 @@ struct GLCmdClearBuffers
// AttachmentClear attachments[numAttachments];
};

struct GLCmdResolveRenderTarget
{
GLRenderTarget* renderTarget;
};

struct GLCmdBindVertexArray
{
GLSharedContextVertexArray* vertexArray;
Expand Down
6 changes: 6 additions & 0 deletions sources/Renderer/OpenGL/Command/GLCommandAssembler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,12 @@ static std::size_t AssembleGLCommand(const GLOpcode opcode, const void* pc, JITC
compiler.CallMember(&GLStateManager::ClearBuffers, g_stateMngrArg, cmd->numAttachments, (cmd + 1));
return sizeof(*cmd);
}
case GLOpcodeResolveRenderTarget:
{
auto cmd = reinterpret_cast<const GLCmdResolveRenderTarget*>(pc);
compiler.CallMember(&GLRenderTarget::ResolveMultisampled, pc->renderTarget, g_stateMngrArg);
return sizeof(*cmd);
}
case GLOpcodeBindVertexArray:
{
auto cmd = reinterpret_cast<const GLCmdBindVertexArray*>(pc);
Expand Down
6 changes: 6 additions & 0 deletions sources/Renderer/OpenGL/Command/GLCommandExecutor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@ static std::size_t ExecuteGLCommand(const GLOpcode opcode, const void* pc, GLSta
stateMngr->ClearBuffers(cmd->numAttachments, reinterpret_cast<const AttachmentClear*>(cmd + 1));
return (sizeof(*cmd) + sizeof(AttachmentClear)*cmd->numAttachments);
}
case GLOpcodeResolveRenderTarget:
{
auto cmd = reinterpret_cast<const GLCmdResolveRenderTarget*>(pc);
cmd->renderTarget->ResolveMultisampled(*stateMngr);
return sizeof(*cmd);
}
case GLOpcodeBindVertexArray:
{
auto cmd = reinterpret_cast<const GLCmdBindVertexArray*>(pc);
Expand Down
1 change: 1 addition & 0 deletions sources/Renderer/OpenGL/Command/GLCommandOpcode.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ enum GLOpcode : std::uint8_t
GLOpcodeClear,
GLOpcodeClearAttachmentsWithRenderPass,
GLOpcodeClearBuffers,
GLOpcodeResolveRenderTarget,
GLOpcodeBindVertexArray,
GLOpcodeBindElementArrayBufferToVAO,
GLOpcodeBindBufferBase,
Expand Down
22 changes: 21 additions & 1 deletion sources/Renderer/OpenGL/Command/GLDeferredCommandBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "GLDeferredCommandBuffer.h"
#include "GLCommand.h"
#include <LLGL/Constants.h>
#include <LLGL/TypeInfo.h>

#include "../../TextureUtils.h"
#include "../GLSwapChain.h"
Expand Down Expand Up @@ -502,11 +503,30 @@ void GLDeferredCommandBuffer::BeginRenderPass(
::memcpy(cmd + 1, clearValues, sizeof(ClearValue)*numClearValues);
}
}

/*
Cache render target if it needs to be resolved at the following EndRenderPass() call.
This is one of the few states the deferred GL command buffer caches
as it must be guaranteed that this render pass is followed by a call to EndRenderPass() operating on the same render-target.
*/
if (!LLGL::IsInstanceOf<SwapChain>(renderTarget))
{
auto& renderTargetGL = LLGL_CAST(GLRenderTarget&, renderTarget);
if (renderTargetGL.CanResolveMultisampledFBO())
renderTargetToResolve_ = &renderTargetGL;
}
}

void GLDeferredCommandBuffer::EndRenderPass()
{
// dummy
if (renderTargetToResolve_ != nullptr)
{
auto cmd = AllocCommand<GLCmdResolveRenderTarget>(GLOpcodeResolveRenderTarget);
{
cmd->renderTarget = renderTargetToResolve_;
}
renderTargetToResolve_ = nullptr;
}
}

void GLDeferredCommandBuffer::Clear(long flags, const ClearValue& clearValue)
Expand Down
1 change: 1 addition & 0 deletions sources/Renderer/OpenGL/Command/GLDeferredCommandBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ class GLDeferredCommandBuffer final : public GLCommandBuffer

long flags_ = 0;
GLVirtualCommandBuffer buffer_;
GLRenderTarget* renderTargetToResolve_ = nullptr;

#ifdef LLGL_ENABLE_JIT_COMPILER
std::unique_ptr<JITProgram> executable_;
Expand Down
4 changes: 3 additions & 1 deletion sources/Renderer/OpenGL/Command/GLImmediateCommandBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,9 @@ void GLImmediateCommandBuffer::BeginRenderPass(

void GLImmediateCommandBuffer::EndRenderPass()
{
// dummy
/* Resolve previously bound render target */
if (GLRenderTarget* renderTarget = stateMngr_->GetBoundRenderTarget())
renderTarget->ResolveMultisampled(*stateMngr_);
}

void GLImmediateCommandBuffer::Clear(long flags, const ClearValue& clearValue)
Expand Down
1 change: 1 addition & 0 deletions sources/Renderer/OpenGL/GLStaticAssertions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ LLGL_ASSERT_STDLAYOUT_STRUCT( GLCmdClearStencil );
LLGL_ASSERT_STDLAYOUT_STRUCT( GLCmdClear );
LLGL_ASSERT_STDLAYOUT_STRUCT( GLCmdClearAttachmentsWithRenderPass );
LLGL_ASSERT_STDLAYOUT_STRUCT( GLCmdClearBuffers );
LLGL_ASSERT_STDLAYOUT_STRUCT( GLCmdResolveRenderTarget );
LLGL_ASSERT_STDLAYOUT_STRUCT( GLCmdBindVertexArray );
LLGL_ASSERT_STDLAYOUT_STRUCT( GLCmdBindElementArrayBufferToVAO );
LLGL_ASSERT_STDLAYOUT_STRUCT( GLCmdBindBufferBase );
Expand Down
9 changes: 0 additions & 9 deletions sources/Renderer/OpenGL/RenderState/GLStateManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1524,9 +1524,6 @@ GLuint GLStateManager::GetBoundProgramPipeline() const

void GLStateManager::BindRenderTarget(RenderTarget& renderTarget, GLStateManager** nextStateManager)
{
/* Resolve previously bound render target */
ResolveMultisampledRenderTarget();

/* Bind render target/context */
if (LLGL::IsInstanceOf<SwapChain>(renderTarget))
{
Expand Down Expand Up @@ -1824,12 +1821,6 @@ void GLStateManager::RestoreWriteMasks(GLIntermediateBufferWriteMasks& intermedi

/* ----- Render pass ----- */

void GLStateManager::ResolveMultisampledRenderTarget()
{
if (auto* renderTarget = GetBoundRenderTarget())
renderTarget->ResolveMultisampled(*this);
}

void GLStateManager::ClearAttachmentsWithRenderPass(
const GLRenderPass& renderPassGL,
std::uint32_t numClearValues,
Expand Down
3 changes: 0 additions & 3 deletions sources/Renderer/OpenGL/RenderState/GLStateManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -344,9 +344,6 @@ class GLStateManager

/* ----- Render pass ----- */

// Blits the currently bound render target
void ResolveMultisampledRenderTarget();

std::uint32_t ClearColorBuffers(
const std::uint8_t* colorBuffers,
std::uint32_t numClearValues,
Expand Down
7 changes: 6 additions & 1 deletion sources/Renderer/OpenGL/Texture/GLRenderTarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,14 @@ const RenderPass* GLRenderTarget::GetRenderPass() const
return renderPass_;
}

bool GLRenderTarget::CanResolveMultisampledFBO() const
{
return (framebufferResolve_.Valid() && !drawBuffersResolve_.empty());
}

void GLRenderTarget::ResolveMultisampled(GLStateManager& stateMngr)
{
if (framebufferResolve_.Valid() && !drawBuffersResolve_.empty())
if (CanResolveMultisampledFBO())
{
stateMngr.BindFramebuffer(GLFramebufferTarget::DrawFramebuffer, framebufferResolve_.GetID());
stateMngr.BindFramebuffer(GLFramebufferTarget::ReadFramebuffer, framebuffer_.GetID());
Expand Down
3 changes: 3 additions & 0 deletions sources/Renderer/OpenGL/Texture/GLRenderTarget.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ class GLRenderTarget final : public RenderTarget
GLRenderTarget(const RenderingLimits& limits, const RenderTargetDescriptor& desc);
~GLRenderTarget();

// Returns true if this render-target can resolve its multi-sampled FBO into a single sampled FBO.
bool CanResolveMultisampledFBO() const;

// Blits the multi-sample framebuffer onto the default framebuffer.
void ResolveMultisampled(GLStateManager& stateMngr);

Expand Down

0 comments on commit b50c4e9

Please sign in to comment.