Skip to content

Commit d2479b9

Browse files
OpenGL: properly disable draw buffers when write mask is zero
1 parent 27a60c8 commit d2479b9

File tree

8 files changed

+249
-53
lines changed

8 files changed

+249
-53
lines changed

Graphics/GraphicsEngineOpenGL/include/DeviceContextGLImpl.hpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019-2024 Diligent Graphics LLC
2+
* Copyright 2019-2025 Diligent Graphics LLC
33
* Copyright 2015-2019 Egor Yusov
44
*
55
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -355,7 +355,10 @@ class DeviceContextGLImpl final : public DeviceContextBase<EngineGLImplTraits>
355355

356356
RefCntAutoPtr<ISwapChainGL> m_pSwapChain;
357357

358-
bool m_IsDefaultFBOBound = false;
358+
bool m_IsDefaultFBOBound = false;
359+
bool m_DrawBuffersCommitted = false;
360+
361+
GLObjectWrappers::GLFrameBufferObj* m_DrawFBO = nullptr;
359362

360363
GLObjectWrappers::GLFrameBufferObj m_DefaultFBO;
361364

Graphics/GraphicsEngineOpenGL/include/FBOCache.hpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019-2023 Diligent Graphics LLC
2+
* Copyright 2019-2025 Diligent Graphics LLC
33
* Copyright 2015-2019 Egor Yusov
44
*
55
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -60,10 +60,10 @@ class FBOCache
6060
Uint32 DefaultWidth = 0,
6161
Uint32 DefaultHeight = 0);
6262

63-
const GLObjectWrappers::GLFrameBufferObj& GetFBO(Uint32 NumRenderTargets,
64-
TextureViewGLImpl* ppRTVs[],
65-
TextureViewGLImpl* pDSV,
66-
GLContextState& ContextState);
63+
GLObjectWrappers::GLFrameBufferObj& GetFBO(Uint32 NumRenderTargets,
64+
TextureViewGLImpl* ppRTVs[],
65+
TextureViewGLImpl* pDSV,
66+
GLContextState& ContextState);
6767

6868
const GLObjectWrappers::GLFrameBufferObj& GetFBO(Uint32 Width, Uint32 Height, GLContextState& ContextState);
6969

Graphics/GraphicsEngineOpenGL/include/FramebufferGLImpl.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019-2022 Diligent Graphics LLC
2+
* Copyright 2019-2025 Diligent Graphics LLC
33
* Copyright 2015-2019 Egor Yusov
44
*
55
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -65,7 +65,7 @@ class FramebufferGLImpl final : public FramebufferBase<EngineGLImplTraits>
6565
GLObjectWrappers::GLFrameBufferObj Resolve;
6666
};
6767

68-
const SubpassFramebuffers& GetSubpassFramebuffer(Uint32 subpass)
68+
SubpassFramebuffers& GetSubpassFramebuffer(Uint32 subpass)
6969
{
7070
return m_SubpassFramebuffers[subpass];
7171
}

Graphics/GraphicsEngineOpenGL/include/GLContextState.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,13 @@ class GLContextState
8080
void SetBlendFactors(const float* BlendFactors);
8181
void SetBlendState(const BlendStateDesc& BSDsc, Uint32 RenderTargetMask, Uint32 SampleMask);
8282

83-
Bool GetDepthWritesEnabled(){ return m_DSState.m_DepthWritesEnableState; }
84-
Bool GetScissorTestEnabled(){ return m_RSState.ScissorTestEnable; }
83+
Bool GetDepthWritesEnabled() const { return m_DSState.m_DepthWritesEnableState; }
84+
Bool GetScissorTestEnabled() const { return m_RSState.ScissorTestEnable; }
8585
void GetColorWriteMask(Uint32 RTIndex, Uint32& WriteMask, Bool& bIsIndexed);
8686
void SetColorWriteMask(Uint32 WriteMask);
8787
void SetColorWriteMaskIndexed(Uint32 RTIndex, Uint32 WriteMask);
8888

89-
Uint8 GetStencilWriteMask(){ return static_cast<Uint8>(m_DSState.m_StencilWriteMask); }
89+
Uint8 GetStencilWriteMask() const { return static_cast<Uint8>(m_DSState.m_StencilWriteMask); }
9090

9191
void GetBoundImage(Uint32 Index, GLuint& GLHandle, GLint& MipLevel, GLboolean& IsLayered, GLint& Layer, GLenum& Access, GLenum& Format) const;
9292

Graphics/GraphicsEngineOpenGL/include/GLObjectWrapper.hpp

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,20 @@ class GLFrameBufferObj : public GLObjWrapper<GLFBOCreateReleaseHelper>
359359
void SetDrawBuffers(uint32_t NumDrawBuffers, uint32_t DrawBuffersMask = ~0u)
360360
{
361361
if (!*this)
362+
{
363+
UNEXPECTED("Setting draw buffers on the default FBO is not allowed");
362364
return;
365+
}
366+
367+
if (NumDrawBuffers == ~0u)
368+
{
369+
if (m_NumDrawBuffers == ~0u)
370+
{
371+
UNEXPECTED("Number of draw buffers is not set. Using ~0u as NumDrawBuffers is not allowed");
372+
return;
373+
}
374+
NumDrawBuffers = m_NumDrawBuffers;
375+
}
363376

364377
if (NumDrawBuffers == 0)
365378
return;
@@ -381,8 +394,19 @@ class GLFrameBufferObj : public GLObjWrapper<GLFBOCreateReleaseHelper>
381394
DEV_CHECK_ERR(glGetError() == GL_NO_ERROR, "Failed to set draw buffers via glDrawBuffers()");
382395
}
383396

397+
uint32_t GetNumDrawBuffers() const
398+
{
399+
VERIFY(m_NumDrawBuffers != ~0u, "Draw buffers were not set. Call SetDrawBuffers() first");
400+
return m_NumDrawBuffers != ~0u ? m_NumDrawBuffers : 0;
401+
}
402+
uint32_t GetDrawBuffersMask() const
403+
{
404+
VERIFY(m_NumDrawBuffers != ~0u, "Draw buffers were not set. Call SetDrawBuffers() first");
405+
return m_DrawBuffersMask;
406+
}
407+
384408
private:
385-
uint32_t m_NumDrawBuffers = 0;
409+
uint32_t m_NumDrawBuffers = ~0u;
386410
uint32_t m_DrawBuffersMask = 0;
387411
};
388412

Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ void DeviceContextGLImpl::SetPipelineState(IPipelineState* pPipelineState)
147147
}
148148
}
149149
m_ContextState.InvalidateVAO();
150+
m_DrawBuffersCommitted = false;
150151
}
151152
else
152153
{
@@ -218,7 +219,9 @@ void DeviceContextGLImpl::InvalidateState()
218219
m_BindInfo.Invalidate();
219220
m_BoundWritableTextures.clear();
220221
m_BoundWritableBuffers.clear();
221-
m_IsDefaultFBOBound = false;
222+
m_IsDefaultFBOBound = false;
223+
m_DrawBuffersCommitted = false;
224+
m_DrawFBO = nullptr;
222225
}
223226

224227
void DeviceContextGLImpl::SetIndexBuffer(IBuffer* pIndexBuffer, Uint64 ByteOffset, RESOURCE_STATE_TRANSITION_MODE StateTransitionMode)
@@ -378,6 +381,7 @@ void DeviceContextGLImpl::CommitRenderTargets()
378381
m_DefaultFBO = GLObjectWrappers::GLFrameBufferObj{true, GLObjectWrappers::GLFBOCreateReleaseHelper{DefaultFBOHandle}};
379382
}
380383
m_ContextState.BindFBO(m_DefaultFBO);
384+
m_DrawFBO = nullptr;
381385
}
382386
else
383387
{
@@ -406,11 +410,12 @@ void DeviceContextGLImpl::CommitRenderTargets()
406410

407411
FBOCache& FboCache = m_pDevice->GetFBOCache(m_ContextState.GetCurrentGLContext());
408412

409-
const GLObjectWrappers::GLFrameBufferObj& FBO = FboCache.GetFBO(NumRenderTargets, pBoundRTVs, m_pBoundDepthStencil, m_ContextState);
413+
m_DrawFBO = &FboCache.GetFBO(NumRenderTargets, pBoundRTVs, m_pBoundDepthStencil, m_ContextState);
410414
// Even though the write mask only applies to writes to a framebuffer, the mask state is NOT
411415
// Framebuffer state. So it is NOT part of a Framebuffer Object or the Default Framebuffer.
412416
// Binding a new framebuffer will NOT affect the mask.
413-
m_ContextState.BindFBO(FBO);
417+
m_ContextState.BindFBO(*m_DrawFBO);
418+
m_DrawBuffersCommitted = false;
414419
}
415420
// Set the viewport to match the render target size
416421
SetViewports(1, nullptr, 0, 0);
@@ -446,7 +451,9 @@ void DeviceContextGLImpl::SetRenderTargetsExt(const SetRenderTargetsAttribs& Att
446451
void DeviceContextGLImpl::ResetRenderTargets()
447452
{
448453
TDeviceContextBase::ResetRenderTargets();
449-
m_IsDefaultFBOBound = false;
454+
m_IsDefaultFBOBound = false;
455+
m_DrawBuffersCommitted = false;
456+
m_DrawFBO = nullptr;
450457
m_ContextState.InvalidateFBO();
451458
}
452459

@@ -459,10 +466,12 @@ void DeviceContextGLImpl::BeginSubpass()
459466
const SubpassDesc& SubpassDesc = RPDesc.pSubpasses[m_SubpassIndex];
460467
const FramebufferDesc& FBDesc = m_pBoundFramebuffer->GetDesc();
461468

462-
const GLObjectWrappers::GLFrameBufferObj& RenderTargetFBO = m_pBoundFramebuffer->GetSubpassFramebuffer(m_SubpassIndex).RenderTarget;
469+
GLObjectWrappers::GLFrameBufferObj& RenderTargetFBO = m_pBoundFramebuffer->GetSubpassFramebuffer(m_SubpassIndex).RenderTarget;
463470
if (RenderTargetFBO != 0)
464471
{
465472
m_ContextState.BindFBO(RenderTargetFBO);
473+
m_DrawFBO = &RenderTargetFBO;
474+
m_DrawBuffersCommitted = false;
466475
}
467476
else
468477
{
@@ -472,8 +481,10 @@ void DeviceContextGLImpl::BeginSubpass()
472481
m_DefaultFBO = GLObjectWrappers::GLFrameBufferObj{true, GLObjectWrappers::GLFBOCreateReleaseHelper{DefaultFBOHandle}};
473482
}
474483
m_ContextState.BindFBO(m_DefaultFBO);
484+
m_DrawFBO = nullptr;
475485
}
476486

487+
477488
for (Uint32 rt = 0; rt < SubpassDesc.RenderTargetAttachmentCount; ++rt)
478489
{
479490
const AttachmentReference& RTAttachmentRef = SubpassDesc.pRenderTargetAttachments[rt];
@@ -748,6 +759,22 @@ void DeviceContextGLImpl::PrepareForDraw(DRAW_FLAGS Flags, bool IsIndexed, GLenu
748759

749760
const GLObjectWrappers::GLFrameBufferObj& FBO = FboCache.GetFBO(m_FramebufferWidth, m_FramebufferHeight, m_ContextState);
750761
m_ContextState.BindFBO(FBO);
762+
m_DrawFBO = nullptr;
763+
}
764+
765+
if (!m_DrawBuffersCommitted)
766+
{
767+
if (!m_IsDefaultFBOBound && m_NumBoundRenderTargets > 0 && m_DrawFBO != nullptr)
768+
{
769+
if (m_pPipelineState)
770+
{
771+
VERIFY(m_pPipelineState->GetGraphicsPipelineDesc().NumRenderTargets == m_NumBoundRenderTargets,
772+
"The number of render targets in the pipeline state (", m_pPipelineState->GetGraphicsPipelineDesc().NumRenderTargets,
773+
") does not match the number of bound render targets (", m_NumBoundRenderTargets, ")");
774+
m_DrawFBO->SetDrawBuffers(~0u, m_pPipelineState->GetRenderTargetMask());
775+
}
776+
}
777+
m_DrawBuffersCommitted = true;
751778
}
752779

753780
#ifdef DILIGENT_DEVELOPMENT

Graphics/GraphicsEngineOpenGL/src/FBOCache.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -225,10 +225,10 @@ GLObjectWrappers::GLFrameBufferObj FBOCache::CreateFBO(GLContextState& Contex
225225
return FBO;
226226
}
227227

228-
const GLObjectWrappers::GLFrameBufferObj& FBOCache::GetFBO(Uint32 NumRenderTargets,
229-
TextureViewGLImpl* ppRTVs[],
230-
TextureViewGLImpl* pDSV,
231-
GLContextState& ContextState)
228+
GLObjectWrappers::GLFrameBufferObj& FBOCache::GetFBO(Uint32 NumRenderTargets,
229+
TextureViewGLImpl* ppRTVs[],
230+
TextureViewGLImpl* pDSV,
231+
GLContextState& ContextState)
232232
{
233233
// Pop null render targets from the end of the list
234234
while (NumRenderTargets > 0 && ppRTVs[NumRenderTargets - 1] == nullptr)

0 commit comments

Comments
 (0)