diff --git a/Graphics/GraphicsEngineOpenGL/include/FBOCache.hpp b/Graphics/GraphicsEngineOpenGL/include/FBOCache.hpp index 761123b678..a99eaf1aee 100644 --- a/Graphics/GraphicsEngineOpenGL/include/FBOCache.hpp +++ b/Graphics/GraphicsEngineOpenGL/include/FBOCache.hpp @@ -32,11 +32,11 @@ #include "SpinLock.hpp" #include "HashUtils.hpp" #include "GLObjectWrapper.hpp" +#include "TextureBaseGL.hpp" namespace Diligent { -class TextureBaseGL; class TextureViewGLImpl; class GLContextState; @@ -68,7 +68,7 @@ class FBOCache const GLObjectWrappers::GLFrameBufferObj& GetFBO(Uint32 Width, Uint32 Height, GLContextState& ContextState); // NOTE: the function may bind a framebuffer, so the FBO in the GL context state must be invalidated. - const GLObjectWrappers::GLFrameBufferObj& GetReadFBO(TextureBaseGL* pTex, Uint32 ArraySlice, Uint32 MipLevel); + const GLObjectWrappers::GLFrameBufferObj& GetFBO(TextureBaseGL* pTex, Uint32 ArraySlice, Uint32 MipLevel, TextureBaseGL::FRAMEBUFFER_TARGET_FLAGS Targets); void OnReleaseTexture(ITexture* pTexture); diff --git a/Graphics/GraphicsEngineOpenGL/src/FBOCache.cpp b/Graphics/GraphicsEngineOpenGL/src/FBOCache.cpp index ea05754442..503c5675ec 100644 --- a/Graphics/GraphicsEngineOpenGL/src/FBOCache.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/FBOCache.cpp @@ -346,7 +346,10 @@ inline GLenum GetFramebufferAttachmentPoint(TEXTURE_FORMAT Format) } } -const GLObjectWrappers::GLFrameBufferObj& FBOCache::GetReadFBO(TextureBaseGL* pTex, Uint32 ArraySlice, Uint32 MipLevel) +const GLObjectWrappers::GLFrameBufferObj& FBOCache::GetFBO(TextureBaseGL* pTex, + Uint32 ArraySlice, + Uint32 MipLevel, + TextureBaseGL::FRAMEBUFFER_TARGET_FLAGS Targets) { const auto& TexDesc = pTex->GetDesc(); @@ -357,8 +360,10 @@ const GLObjectWrappers::GLFrameBufferObj& FBOCache::GetReadFBO(TextureBaseGL* pT auto& RTV0{Key.RTVDescs[0]}; RTV0.Format = TexDesc.Format; RTV0.TextureDim = TexDesc.Type; - // Use SRV to make AttachToFramebuffer only attach to read framebuffer - RTV0.ViewType = TEXTURE_VIEW_SHADER_RESOURCE; + RTV0.ViewType = (Targets & TextureBaseGL::FRAMEBUFFER_TARGET_FLAG_DRAW) ? + TEXTURE_VIEW_RENDER_TARGET : // Also OK for depth attachments + TEXTURE_VIEW_SHADER_RESOURCE; + RTV0.FirstArraySlice = ArraySlice; RTV0.MostDetailedMip = MipLevel; RTV0.NumArraySlices = 1; @@ -373,10 +378,18 @@ const GLObjectWrappers::GLFrameBufferObj& FBOCache::GetReadFBO(TextureBaseGL* pT // Create a new FBO GLObjectWrappers::GLFrameBufferObj NewFBO{true}; - glBindFramebuffer(GL_READ_FRAMEBUFFER, NewFBO); - DEV_CHECK_GL_ERROR("Failed to bind new FBO as read framebuffer"); + if (Targets & TextureBaseGL::FRAMEBUFFER_TARGET_FLAG_READ) + { + glBindFramebuffer(GL_READ_FRAMEBUFFER, NewFBO); + DEV_CHECK_GL_ERROR("Failed to bind new FBO as read framebuffer"); + } + if (Targets & TextureBaseGL::FRAMEBUFFER_TARGET_FLAG_DRAW) + { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, NewFBO); + DEV_CHECK_GL_ERROR("Failed to bind new FBO as draw framebuffer"); + } - pTex->AttachToFramebuffer(RTV0, GetFramebufferAttachmentPoint(TexDesc.Format), TextureBaseGL::FRAMEBUFFER_TARGET_FLAG_READ); + pTex->AttachToFramebuffer(RTV0, GetFramebufferAttachmentPoint(TexDesc.Format), Targets); GLenum Status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER); if (Status != GL_FRAMEBUFFER_COMPLETE) diff --git a/Graphics/GraphicsEngineOpenGL/src/TextureBaseGL.cpp b/Graphics/GraphicsEngineOpenGL/src/TextureBaseGL.cpp index 2d2911d00c..40f758c81e 100644 --- a/Graphics/GraphicsEngineOpenGL/src/TextureBaseGL.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/TextureBaseGL.cpp @@ -625,9 +625,21 @@ void TextureBaseGL::CopyData(DeviceContextGLImpl* pDeviceCtxGL, else #endif { + bool UseBlitFramebuffer = IsDefaultBackBuffer; + if (!UseBlitFramebuffer && m_pDevice->GetDeviceInfo().Type == RENDER_DEVICE_TYPE_GLES) + { + const auto& FmtAttribs = GetTextureFormatAttribs(m_Desc.Format); + if (FmtAttribs.ComponentType == COMPONENT_TYPE_DEPTH || + FmtAttribs.ComponentType == COMPONENT_TYPE_DEPTH_STENCIL) + { + // glCopyTexSubImage* does not support depth formats in GLES + UseBlitFramebuffer = true; + } + } + auto& GLState = pDeviceCtxGL->GetContextState(); - // Copy operations (glCopyTexSubImage2D and family, glBindFramebuffer) are affected by scissor test! + // Copy operations (glCopyTexSubImage* and glBindFramebuffer) are affected by scissor test! GLboolean ScissorEnabled = GLState.GetScissorTestEnabled(); if (ScissorEnabled) GLState.EnableScissorTest(false); @@ -643,8 +655,8 @@ void TextureBaseGL::CopyData(DeviceContextGLImpl* pDeviceCtxGL, VERIFY_EXPR(SrcSlice == 0 || SrcTexDesc.IsArray()); VERIFY_EXPR((pSrcBox->MinZ == 0 && DepthSlice == 0) || SrcTexDesc.Is3D()); const auto SrcFramebufferSlice = SrcSlice + pSrcBox->MinZ + DepthSlice; - // NOTE: GetReadFBO may bind a framebuffer. - const auto& ReadFBO = FBOCache.GetReadFBO(pSrcTextureGL, SrcFramebufferSlice, SrcMipLevel); + // NOTE: GetFBO may bind a framebuffer, so we need to invalidate it in the GL context state. + const auto& ReadFBO = FBOCache.GetFBO(pSrcTextureGL, SrcFramebufferSlice, SrcMipLevel, TextureBaseGL::FRAMEBUFFER_TARGET_FLAG_READ); SrcFboHandle = ReadFBO; } @@ -654,8 +666,10 @@ void TextureBaseGL::CopyData(DeviceContextGLImpl* pDeviceCtxGL, } glBindFramebuffer(GL_READ_FRAMEBUFFER, SrcFboHandle); DEV_CHECK_GL_ERROR("Failed to bind read framebuffer"); + DEV_CHECK_ERR(glCheckFramebufferStatus(GL_READ_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE, + "Read framebuffer is incomplete: ", GetFramebufferStatusString(glCheckFramebufferStatus(GL_READ_FRAMEBUFFER))); - if (!IsDefaultBackBuffer) + if (!UseBlitFramebuffer) { CopyTexSubimageAttribs CopyAttribs{*pSrcBox}; CopyAttribs.DstMip = DstMipLevel; @@ -667,14 +681,29 @@ void TextureBaseGL::CopyData(DeviceContextGLImpl* pDeviceCtxGL, } else { - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, pDeviceCtxGL->GetDefaultFBO()); - GLenum Status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER); - if (Status != GL_FRAMEBUFFER_COMPLETE) + GLuint DstFboHandle = 0; + if (IsDefaultBackBuffer) { - const Char* StatusString = GetFramebufferStatusString(Status); - LOG_ERROR("Draw framebuffer is incomplete. FB status: ", StatusString); - UNEXPECTED("Draw framebuffer is incomplete"); + DstFboHandle = pDeviceCtxGL->GetDefaultFBO(); } + else + { + // Get draw framebuffer for the destination subimage + + auto& FBOCache = m_pDevice->GetFBOCache(GLState.GetCurrentGLContext()); + VERIFY_EXPR(DstSlice == 0 || m_Desc.IsArray()); + VERIFY_EXPR((DstZ == 0 && DepthSlice == 0) || m_Desc.Is3D()); + const auto DstFramebufferSlice = DstSlice + DstZ + DepthSlice; + // NOTE: GetFBO may bind a framebuffer, so we need to invalidate it in the GL context state. + const auto& DrawFBO = FBOCache.GetFBO(this, DstFramebufferSlice, DstMipLevel, TextureBaseGL::FRAMEBUFFER_TARGET_FLAG_DRAW); + + DstFboHandle = DrawFBO; + } + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, DstFboHandle); + DEV_CHECK_GL_ERROR("Failed to bind draw framebuffer"); + DEV_CHECK_ERR(glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE, + "Draw framebuffer is incomplete: ", GetFramebufferStatusString(glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER))); const auto CopyMask = GetFramebufferCopyMask(SrcTexDesc.Format); DEV_CHECK_ERR(CopyMask == GetFramebufferCopyMask(m_Desc.Format),