diff --git a/Graphics/GraphicsEngineOpenGL/include/DeviceContextGLImpl.hpp b/Graphics/GraphicsEngineOpenGL/include/DeviceContextGLImpl.hpp index f1279746d5..73957379a2 100644 --- a/Graphics/GraphicsEngineOpenGL/include/DeviceContextGLImpl.hpp +++ b/Graphics/GraphicsEngineOpenGL/include/DeviceContextGLImpl.hpp @@ -299,6 +299,7 @@ class DeviceContextGLImpl final : public DeviceContextBase virtual void ResetRenderTargets() override final; + GLuint GetDefaultFBO() const; protected: friend class BufferGLImpl; diff --git a/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp b/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp index b7dc08f57e..84aab703d2 100644 --- a/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp @@ -362,6 +362,11 @@ void DeviceContextGLImpl::SetSwapChain(ISwapChainGL* pSwapChain) m_pSwapChain = pSwapChain; } +GLuint DeviceContextGLImpl::GetDefaultFBO() const +{ + return m_pSwapChain ? m_pSwapChain->GetDefaultFBO() : 0; +} + void DeviceContextGLImpl::CommitRenderTargets() { DEV_CHECK_ERR(m_pActiveRenderPass == nullptr, "This method must not be called inside render pass"); @@ -374,7 +379,7 @@ void DeviceContextGLImpl::CommitRenderTargets() GLuint DefaultFBOHandle = m_pSwapChain->GetDefaultFBO(); if (m_DefaultFBO != DefaultFBOHandle) { - m_DefaultFBO = GLObjectWrappers::GLFrameBufferObj{true, GLObjectWrappers::GLFBOCreateReleaseHelper(DefaultFBOHandle)}; + m_DefaultFBO = GLObjectWrappers::GLFrameBufferObj{true, GLObjectWrappers::GLFBOCreateReleaseHelper{DefaultFBOHandle}}; } m_ContextState.BindFBO(m_DefaultFBO); } @@ -468,7 +473,7 @@ void DeviceContextGLImpl::BeginSubpass() GLuint DefaultFBOHandle = m_pSwapChain->GetDefaultFBO(); if (m_DefaultFBO != DefaultFBOHandle) { - m_DefaultFBO = GLObjectWrappers::GLFrameBufferObj{true, GLObjectWrappers::GLFBOCreateReleaseHelper(DefaultFBOHandle)}; + m_DefaultFBO = GLObjectWrappers::GLFrameBufferObj{true, GLObjectWrappers::GLFBOCreateReleaseHelper{DefaultFBOHandle}}; } m_ContextState.BindFBO(m_DefaultFBO); } diff --git a/Graphics/GraphicsEngineOpenGL/src/TextureBaseGL.cpp b/Graphics/GraphicsEngineOpenGL/src/TextureBaseGL.cpp index 5b8110de2f..86946777f8 100644 --- a/Graphics/GraphicsEngineOpenGL/src/TextureBaseGL.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/TextureBaseGL.cpp @@ -550,6 +550,34 @@ void TextureBaseGL::UpdateData(GLContextState& CtxState, Uint32 MipLevel, Uint32 //} // +inline GLenum GetFramebufferAttachmentPoint(TEXTURE_FORMAT Format) +{ + const auto& FmtAttribs = GetTextureFormatAttribs(Format); + switch (FmtAttribs.ComponentType) + { + case COMPONENT_TYPE_DEPTH: + return GL_DEPTH_ATTACHMENT; + case COMPONENT_TYPE_DEPTH_STENCIL: + return GL_DEPTH_STENCIL_ATTACHMENT; + default: + return GL_COLOR_ATTACHMENT0; + } +} + +inline GLbitfield GetFramebufferCopyMask(TEXTURE_FORMAT Format) +{ + const auto& FmtAttribs = GetTextureFormatAttribs(Format); + switch (FmtAttribs.ComponentType) + { + case COMPONENT_TYPE_DEPTH: + return GL_DEPTH_BUFFER_BIT; + case COMPONENT_TYPE_DEPTH_STENCIL: + return GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; + default: + return GL_COLOR_BUFFER_BIT; + } +} + void TextureBaseGL::CopyData(DeviceContextGLImpl* pDeviceCtxGL, TextureBaseGL* pSrcTextureGL, Uint32 SrcMipLevel, @@ -612,48 +640,77 @@ void TextureBaseGL::CopyData(DeviceContextGLImpl* pDeviceCtxGL, #endif { auto& GLState = pDeviceCtxGL->GetContextState(); + // Invalidate FBO as we will use glBindFramebuffer directly + GLState.InvalidateFBO(); - GLObjectWrappers::GLFrameBufferObj ReadFBO{!IsDefaultBackBuffer}; - GLState.BindFBO(ReadFBO); + GLObjectWrappers::GLFrameBufferObj ReadFBO{pSrcTextureGL->GetGLHandle() != 0}; + glBindFramebuffer(GL_READ_FRAMEBUFFER, ReadFBO ? ReadFBO : pDeviceCtxGL->GetDefaultFBO()); + DEV_CHECK_GL_ERROR("Failed to bind read framebuffer"); for (Uint32 DepthSlice = 0; DepthSlice < pSrcBox->Depth(); ++DepthSlice) { - if (!IsDefaultBackBuffer) + if (ReadFBO) { // Attach source subimage to read framebuffer TextureViewDesc SRVVDesc; - SRVVDesc.TextureDim = SrcTexDesc.Type; - SRVVDesc.ViewType = TEXTURE_VIEW_SHADER_RESOURCE; + SRVVDesc.TextureDim = SrcTexDesc.Type; + SRVVDesc.ViewType = TEXTURE_VIEW_SHADER_RESOURCE; + VERIFY_EXPR(SrcSlice == 0 || SrcTexDesc.IsArray()); + VERIFY_EXPR((pSrcBox->MinZ == 0 && DepthSlice == 0) || SrcTexDesc.Is3D()); SRVVDesc.FirstArraySlice = SrcSlice + pSrcBox->MinZ + DepthSlice; SRVVDesc.MostDetailedMip = SrcMipLevel; SRVVDesc.NumArraySlices = 1; ValidatedAndCorrectTextureViewDesc(m_Desc, SRVVDesc); - pSrcTextureGL->AttachToFramebuffer(SRVVDesc, GL_COLOR_ATTACHMENT0); + pSrcTextureGL->AttachToFramebuffer(SRVVDesc, GetFramebufferAttachmentPoint(SrcTexDesc.Format)); GLenum Status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER); if (Status != GL_FRAMEBUFFER_COMPLETE) { const Char* StatusString = GetFramebufferStatusString(Status); - LOG_ERROR("Framebuffer is incomplete. FB status: ", StatusString); - UNEXPECTED("Framebuffer is incomplete"); + LOG_ERROR("Read framebuffer is incomplete. FB status: ", StatusString); + UNEXPECTED("Read framebuffer is incomplete"); } } - else + + if (!IsDefaultBackBuffer) { - VERIFY(pSrcBox->Depth() == 1, "Default framebuffer only has one slice"); + CopyTexSubimageAttribs CopyAttribs{*pSrcBox}; + CopyAttribs.DstMip = DstMipLevel; + CopyAttribs.DstLayer = DstSlice; + CopyAttribs.DstX = DstX; + CopyAttribs.DstY = DstY; + CopyAttribs.DstZ = DstZ + DepthSlice; + CopyTexSubimage(GLState, CopyAttribs); } + else + { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, pDeviceCtxGL->GetDefaultFBO()); + GLenum Status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER); + if (Status != GL_FRAMEBUFFER_COMPLETE) + { + const Char* StatusString = GetFramebufferStatusString(Status); + LOG_ERROR("Draw framebuffer is incomplete. FB status: ", StatusString); + UNEXPECTED("Draw framebuffer is incomplete"); + } - CopyTexSubimageAttribs CopyAttribs{*pSrcBox}; - CopyAttribs.DstMip = DstMipLevel; - CopyAttribs.DstLayer = DstSlice; - CopyAttribs.DstX = DstX; - CopyAttribs.DstY = DstY; - CopyAttribs.DstZ = DstZ + DepthSlice; - CopyTexSubimage(GLState, CopyAttribs); + const auto CopyMask = GetFramebufferCopyMask(SrcTexDesc.Format); + DEV_CHECK_ERR(CopyMask == GetFramebufferCopyMask(m_Desc.Format), + "Src and dst framebuffer copy masks must be the same"); + glBlitFramebuffer(pSrcBox->MinX, + pSrcBox->MinY, + pSrcBox->MaxX, + pSrcBox->MaxY, + DstX, + DstY, + DstX + pSrcBox->Width(), + DstY + pSrcBox->Height(), + CopyMask, + GL_NEAREST); + DEV_CHECK_GL_ERROR("Failed to bind blit framebuffer"); + } } - GLState.InvalidateFBO(); GLState.BindTexture(-1, GetBindTarget(), GLObjectWrappers::GLTextureObj::Null()); pDeviceCtxGL->CommitRenderTargets(); }