Skip to content

Commit

Permalink
Workaround: resolve MSAA by copying to backbuffer
Browse files Browse the repository at this point in the history
- On Nvidia, MSAA resolve uses a different, higher-quality sampling filter when resolving to the window backbuffer.
- This difference is very perceptible when rendering thin lines with high contrast.
- This does not present a significant performance cost on mid-high spec systems. Ideally this workaround is dynamically enabled based on driver version etc.
  • Loading branch information
sturnclaw committed Sep 6, 2023
1 parent c30a491 commit 46ac5c7
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 3 deletions.
6 changes: 4 additions & 2 deletions src/graphics/opengl/CommandBufferGL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,8 @@ void CommandList::ExecuteBlitRenderTargetCmd(const BlitRenderTargetCmd &cmd)
// invalidate cached render target state
stateCache->SetRenderTarget(nullptr);

cmd.srcTarget->Bind(RenderTarget::READ);
if (cmd.srcTarget)
cmd.srcTarget->Bind(RenderTarget::READ);

// dstTarget can be null if blitting to the window implicit backbuffer
if (cmd.dstTarget)
Expand All @@ -343,7 +344,8 @@ void CommandList::ExecuteBlitRenderTargetCmd(const BlitRenderTargetCmd &cmd)
cmd.dstExtents.x, cmd.dstExtents.y, cmd.dstExtents.x + cmd.dstExtents.w, cmd.dstExtents.y + cmd.dstExtents.h,
mask, cmd.linearFilter ? GL_LINEAR : GL_NEAREST);

cmd.srcTarget->Unbind(RenderTarget::READ);
if (cmd.srcTarget)
cmd.srcTarget->Unbind(RenderTarget::READ);

// dstTarget can be null if blitting to the window implicit backbuffer
if (cmd.dstTarget)
Expand Down
21 changes: 20 additions & 1 deletion src/graphics/opengl/RendererGL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ namespace Graphics {
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);

// HACK (sturnclaw): request RGBA backbuffer specifically for the purpose of using
// it as an intermediate multisample resolve target with RGBA textures.
// See ResolveRenderTarget() for more details
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);

winFlags |= (vs.hidden ? SDL_WINDOW_HIDDEN : SDL_WINDOW_SHOWN);
if (!vs.hidden && vs.fullscreen) // TODO: support for borderless fullscreen and changing window size
winFlags |= SDL_WINDOW_FULLSCREEN;
Expand Down Expand Up @@ -638,7 +643,21 @@ namespace Graphics {
{
bool hasDepthTexture = src->GetDepthTexture() && dst->GetDepthTexture();

m_drawCommandList->AddBlitRenderTargetCmd(src, dst, extents, extents, true, hasDepthTexture);
// HACK (sturnclaw): work around NVidia undocumented behavior of using a higher-quality filtering
// kernel when resolving to window backbuffer instead of offscreen FBO.
// Otherwise there's a distinct visual quality loss when performing MSAA resolve to offscreen FBO.
// Ideally this should be replaced by using a custom MSAA resolve shader; however builtin resolve
// usually has better performance (ref: https://therealmjp.github.io/posts/msaa-resolve-filters/)
// NOTE: this behavior appears to be independent of setting GL_MULTISAMPLE_FILTER_HINT_NV on Linux
if (!hasDepthTexture && extents.w <= m_width && extents.h <= m_height) {
ViewportExtents tmpExtents = { 0, 0, extents.w, extents.h };

m_drawCommandList->AddBlitRenderTargetCmd(src, nullptr, extents, tmpExtents, true);
m_drawCommandList->AddBlitRenderTargetCmd(nullptr, dst, tmpExtents, extents, true);
} else {
m_drawCommandList->AddBlitRenderTargetCmd(src, dst, extents, extents, true, hasDepthTexture);
}

m_drawCommandList->AddRenderPassCmd(m_activeRenderTarget, m_viewport);
}

Expand Down

0 comments on commit 46ac5c7

Please sign in to comment.