diff --git a/Graphics/GraphicsEngine/include/DeviceContextBase.hpp b/Graphics/GraphicsEngine/include/DeviceContextBase.hpp index 9a1e3c47a1..02caea7f84 100644 --- a/Graphics/GraphicsEngine/include/DeviceContextBase.hpp +++ b/Graphics/GraphicsEngine/include/DeviceContextBase.hpp @@ -1204,7 +1204,7 @@ inline bool DeviceContextBase::SetSubpassRenderTargets() m_FramebufferWidth = FBDesc.Width; m_FramebufferHeight = FBDesc.Height; m_FramebufferSlices = FBDesc.NumArraySlices; - VERIFY_EXPR(m_FramebufferSamples > 0); + VERIFY_EXPR((m_FramebufferSamples > 0) || (Subpass.RenderTargetAttachmentCount == 0 && Subpass.pDepthStencilAttachment == nullptr)); return BindRenderTargets; } diff --git a/Graphics/GraphicsEngineOpenGL/include/FBOCache.hpp b/Graphics/GraphicsEngineOpenGL/include/FBOCache.hpp index 4a4ba28ba6..40e7b72520 100644 --- a/Graphics/GraphicsEngineOpenGL/include/FBOCache.hpp +++ b/Graphics/GraphicsEngineOpenGL/include/FBOCache.hpp @@ -55,7 +55,9 @@ class FBOCache static GLObjectWrappers::GLFrameBufferObj CreateFBO(GLContextState& ContextState, Uint32 NumRenderTargets, TextureViewGLImpl* ppRTVs[], - TextureViewGLImpl* pDSV); + TextureViewGLImpl* pDSV, + Uint32 DefaultWidth = 0, + Uint32 DefaultHeight = 0); const GLObjectWrappers::GLFrameBufferObj& GetFBO(Uint32 NumRenderTargets, TextureViewGLImpl* ppRTVs[], diff --git a/Graphics/GraphicsEngineOpenGL/src/FBOCache.cpp b/Graphics/GraphicsEngineOpenGL/src/FBOCache.cpp index a12c4c80cc..e8fe17401f 100644 --- a/Graphics/GraphicsEngineOpenGL/src/FBOCache.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/FBOCache.cpp @@ -112,7 +112,9 @@ void FBOCache::OnReleaseTexture(ITexture* pTexture) GLObjectWrappers::GLFrameBufferObj FBOCache::CreateFBO(GLContextState& ContextState, Uint32 NumRenderTargets, TextureViewGLImpl* ppRTVs[], - TextureViewGLImpl* pDSV) + TextureViewGLImpl* pDSV, + Uint32 DefaultWidth, + Uint32 DefaultHeight) { GLObjectWrappers::GLFrameBufferObj FBO{true}; @@ -167,35 +169,56 @@ GLObjectWrappers::GLFrameBufferObj FBOCache::CreateFBO(GLContextState& Contex pDepthTexGL->AttachToFramebuffer(DSVDesc, AttachmentPoint); } - // We now need to set mapping between shader outputs and - // color attachments. This largely redundant step is performed - // by glDrawBuffers() - // clang-format off - static constexpr GLenum DrawBuffers[] = + if (NumRenderTargets > 0) { - 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 - }; - // clang-format on - - // 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(NumRenderTargets, DrawBuffers); - CHECK_GL_ERROR("Failed to set draw buffers via glDrawBuffers()"); + // We now need to set mapping between shader outputs and + // color attachments. This largely redundant step is performed + // by glDrawBuffers() + static constexpr 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(NumRenderTargets, DrawBuffers); + CHECK_GL_ERROR("Failed to set draw buffers via glDrawBuffers()"); + } + else if (pDSV == nullptr) + { + // Framebuffer without attachments + DEV_CHECK_ERR(DefaultWidth > 0 && DefaultHeight > 0, "Framebuffer without attachment requires non-zero default width and height"); +#ifdef GL_ARB_framebuffer_no_attachments + glFramebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH, DefaultWidth); + CHECK_GL_ERROR("Failed to set framebuffer default width"); + + glFramebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_HEIGHT, DefaultHeight); + CHECK_GL_ERROR("Failed to set framebuffer default height"); + + glFramebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_LAYERS, 1); + CHECK_GL_ERROR("Failed to set framebuffer default layer count"); + + glFramebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_SAMPLES, 1); + CHECK_GL_ERROR("Failed to set framebuffer default sample count"); +#else + DEV_ERROR("Framebuffers without attachments are not supported on this platform"); +#endif + } GLenum Status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (Status != GL_FRAMEBUFFER_COMPLETE) diff --git a/Graphics/GraphicsEngineOpenGL/src/FramebufferGLImpl.cpp b/Graphics/GraphicsEngineOpenGL/src/FramebufferGLImpl.cpp index 96af518b6b..e81e626a13 100644 --- a/Graphics/GraphicsEngineOpenGL/src/FramebufferGLImpl.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/FramebufferGLImpl.cpp @@ -110,7 +110,7 @@ FramebufferGLImpl::FramebufferGLImpl(IReferenceCounters* pRefCounters, } auto RenderTargetFBO = UseDefaultFBO(SubpassDesc.RenderTargetAttachmentCount, ppRTVs, pDSV) ? GLObjectWrappers::GLFrameBufferObj{false} : - FBOCache::CreateFBO(CtxState, SubpassDesc.RenderTargetAttachmentCount, ppRTVs, pDSV); + FBOCache::CreateFBO(CtxState, SubpassDesc.RenderTargetAttachmentCount, ppRTVs, pDSV, Desc.Width, Desc.Height); GLObjectWrappers::GLFrameBufferObj ResolveFBO{false}; if (SubpassDesc.pResolveAttachments != nullptr) @@ -126,7 +126,7 @@ FramebufferGLImpl::FramebufferGLImpl(IReferenceCounters* pRefCounters, } ResolveFBO = UseDefaultFBO(SubpassDesc.RenderTargetAttachmentCount, ppRsvlViews, nullptr) ? GLObjectWrappers::GLFrameBufferObj{false} : - FBOCache::CreateFBO(CtxState, SubpassDesc.RenderTargetAttachmentCount, ppRsvlViews, nullptr); + FBOCache::CreateFBO(CtxState, SubpassDesc.RenderTargetAttachmentCount, ppRsvlViews, nullptr, Desc.Width, Desc.Height); } RenderTargetFBO.SetName(m_Desc.Name); diff --git a/Tests/DiligentCoreAPITest/src/ComputeShaderTest.cpp b/Tests/DiligentCoreAPITest/src/ComputeShaderTest.cpp index 7261fa2a10..a4ecfc1e27 100644 --- a/Tests/DiligentCoreAPITest/src/ComputeShaderTest.cpp +++ b/Tests/DiligentCoreAPITest/src/ComputeShaderTest.cpp @@ -282,8 +282,7 @@ TEST(ComputeShaderTest, GenerateMips_CSInterference) pSwapChain->Present(); } - -TEST(ComputeShaderTest, FillTexturePS) +static void TestFillTexturePS(bool UseRenderPass) { auto* pEnv = GPUTestingEnvironment::GetInstance(); auto* pDevice = pEnv->GetDevice(); @@ -295,6 +294,8 @@ TEST(ComputeShaderTest, FillTexturePS) auto* pSwapChain = pEnv->GetSwapChain(); auto* pContext = pEnv->GetDeviceContext(); + const auto& SCDesc = pSwapChain->GetDesc(); + GPUTestingEnvironment::ScopedReset EnvironmentAutoReset; RefCntAutoPtr pTestingSwapChain{pSwapChain, IID_TestingSwapChain}; @@ -335,6 +336,30 @@ TEST(ComputeShaderTest, FillTexturePS) PSOCreateInfo.pVS = pVS; PSOCreateInfo.pPS = pPS; + RefCntAutoPtr pRenderPass; + RefCntAutoPtr pFramebuffer; + if (UseRenderPass) + { + SubpassDesc Subpass; + RenderPassDesc RPDesc; + RPDesc.Name = "Compute shader test - render pass"; + RPDesc.pSubpasses = &Subpass; + RPDesc.SubpassCount = 1; + pDevice->CreateRenderPass(RPDesc, &pRenderPass); + ASSERT_TRUE(pRenderPass != nullptr); + + PSOCreateInfo.GraphicsPipeline.pRenderPass = pRenderPass; + + FramebufferDesc FBDesc; + FBDesc.Name = "Compute shader test - framebuffer"; + FBDesc.pRenderPass = pRenderPass; + FBDesc.Width = SCDesc.Width; + FBDesc.Height = SCDesc.Height; + FBDesc.NumArraySlices = 1; + pDevice->CreateFramebuffer(FBDesc, &pFramebuffer); + ASSERT_TRUE(pFramebuffer != nullptr); + } + RefCntAutoPtr pPSO; pDevice->CreateGraphicsPipelineState(PSOCreateInfo, &pPSO); ASSERT_NE(pPSO, nullptr); @@ -345,18 +370,45 @@ TEST(ComputeShaderTest, FillTexturePS) pPSO->CreateShaderResourceBinding(&pSRB, true); ASSERT_NE(pSRB, nullptr); + ITextureView* pRTVs[] = {pSwapChain->GetCurrentBackBufferRTV()}; + pContext->SetRenderTargets(1, pRTVs, pSwapChain->GetDepthBufferDSV(), RESOURCE_STATE_TRANSITION_MODE_TRANSITION); + pContext->SetPipelineState(pPSO); pContext->CommitShaderResources(pSRB, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); - pContext->SetRenderTargets(0, nullptr, nullptr, RESOURCE_STATE_TRANSITION_MODE_NONE); + if (UseRenderPass) + { + BeginRenderPassAttribs BeginRPAttribs; + BeginRPAttribs.pRenderPass = pRenderPass; + BeginRPAttribs.pFramebuffer = pFramebuffer; + pContext->BeginRenderPass(BeginRPAttribs); + } + else + { + pContext->SetRenderTargets(0, nullptr, nullptr, RESOURCE_STATE_TRANSITION_MODE_NONE); + } - const auto& SCDesc = pSwapChain->GetDesc(); - Viewport VP{SCDesc}; + Viewport VP{SCDesc}; pContext->SetViewports(1, &VP, SCDesc.Width, SCDesc.Height); pContext->Draw(DrawAttribs{3, DRAW_FLAG_VERIFY_ALL}); + if (UseRenderPass) + { + pContext->EndRenderPass(); + } + pSwapChain->Present(); } +TEST(ComputeShaderTest, FillTexturePS) +{ + TestFillTexturePS(false); +} + +TEST(ComputeShaderTest, FillTexturePS_InRenderPass) +{ + TestFillTexturePS(true); +} + } // namespace