diff --git a/nel/include/nel/3d/driver.h b/nel/include/nel/3d/driver.h index d1c88e852c..80d0e5b84a 100644 --- a/nel/include/nel/3d/driver.h +++ b/nel/include/nel/3d/driver.h @@ -318,6 +318,9 @@ class IDriver : public NLMISC::CRefCount /// Clear the current target surface stencil buffer. The function ignores the viewport settings but uses the scissor. virtual bool clearStencilBuffer(sint stencilval=0) = 0; + /// Clear both the depth and stencil buffers in one call. Drivers may override for efficiency. + virtual bool clearDepthStencil(float zval=1, sint stencilval=0) { clearZBuffer(zval); clearStencilBuffer(stencilval); return true; } + /// Set the color mask filter through where the operation done will pass virtual void setColorMask(bool bRed, bool bGreen, bool bBlue, bool bAlpha) = 0; // @} diff --git a/nel/src/3d/driver/opengl3/driver_opengl3.cpp b/nel/src/3d/driver/opengl3/driver_opengl3.cpp index d6b70ced80..b51b65805b 100644 --- a/nel/src/3d/driver/opengl3/driver_opengl3.cpp +++ b/nel/src/3d/driver/opengl3/driver_opengl3.cpp @@ -710,6 +710,18 @@ bool CDriverGL3::activeFrameBufferObject(ITexture * tex) } else { + // Invalidate depth/stencil attachments before unbinding the FBO. + // On ANGLE (Windows/Android WebGL), switching away from an FBO without + // invalidating forces an expensive depth/stencil resolve/copy. This hint + // tells the driver the data is no longer needed, avoiding the stall. + // (STORE_OP_DONT_CARE on Vulkan, DiscardView on D3D11) + const GLenum attachments[2] = { GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT }; +#ifdef USE_OPENGLES3 + nglInvalidateFramebuffer(GL_FRAMEBUFFER, 2, attachments); +#else + if (_Extensions.ARBInvalidateSubdata) + nglInvalidateFramebuffer(GL_FRAMEBUFFER, 2, attachments); +#endif _DriverGLStates.forceBindFramebuffer(0); return true; } @@ -753,7 +765,8 @@ bool CDriverGL3::clearZBuffer(float zval) { H_AUTO_OGL(CDriverGL3_clearZBuffer); - _DriverGLStates.enableZWrite(true); + // glClearBufferfv is not affected by the depth write mask (GL spec), + // so enableZWrite(true) is not needed here. nglClearBufferfv(GL_DEPTH, 0, &zval); return true; @@ -769,6 +782,19 @@ bool CDriverGL3::clearStencilBuffer(sint stencilval) return true; } +// -------------------------------------------------- +bool CDriverGL3::clearDepthStencil(float zval, sint stencilval) +{ + H_AUTO_OGL(CDriverGL3_clearDepthStencil) + + // Combined depth/stencil clear: one GL call instead of two. + // On ANGLE/D3D11 this maps to a single ClearDepthStencilView with + // D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, avoiding two separate calls. + nglClearBufferfi(GL_DEPTH_STENCIL, 0, zval, stencilval); + + return true; +} + // -------------------------------------------------- void CDriverGL3::setColorMask (bool bRed, bool bGreen, bool bBlue, bool bAlpha) { @@ -838,6 +864,21 @@ bool CDriverGL3::swapBuffers() _SwapBufferSync[syncI] = nglFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); nlassert(_SwapBufferSync[syncI]); + // Invalidate the default framebuffer's depth/stencil before the swap. + // The depth/stencil data is never needed across frames. Without this hint, + // ANGLE stores the depth/stencil during present (an expensive resolve/copy + // on D3D11, and STORE_OP_STORE on Vulkan). With invalidation, ANGLE can + // skip the store (DiscardView on D3D11, STORE_OP_DONT_CARE on Vulkan). + { + const GLenum attachments[2] = { GL_DEPTH, GL_STENCIL }; +#ifdef USE_OPENGLES3 + nglInvalidateFramebuffer(GL_FRAMEBUFFER, 2, attachments); +#else + if (_Extensions.ARBInvalidateSubdata) + nglInvalidateFramebuffer(GL_FRAMEBUFFER, 2, attachments); +#endif + } + ++_SwapBufferCounter; if (!_WndActive) diff --git a/nel/src/3d/driver/opengl3/driver_opengl3.h b/nel/src/3d/driver/opengl3/driver_opengl3.h index 98cf599a2b..689b085995 100644 --- a/nel/src/3d/driver/opengl3/driver_opengl3.h +++ b/nel/src/3d/driver/opengl3/driver_opengl3.h @@ -542,6 +542,7 @@ class CDriverGL3 : public IDriver virtual bool clearZBuffer(float zval=1); virtual bool clearStencilBuffer(sint stencilval=0); + virtual bool clearDepthStencil(float zval=1, sint stencilval=0); virtual void setColorMask (bool bRed, bool bGreen, bool bBlue, bool bAlpha); virtual void setDepthRange(float znear, float zfar); virtual void getDepthRange(float &znear, float &zfar) const; diff --git a/nel/src/3d/driver/opengl3/driver_opengl3_extension.cpp b/nel/src/3d/driver/opengl3/driver_opengl3_extension.cpp index 2cfd037a06..122323e2e2 100644 --- a/nel/src/3d/driver/opengl3/driver_opengl3_extension.cpp +++ b/nel/src/3d/driver/opengl3/driver_opengl3_extension.cpp @@ -183,6 +183,7 @@ PFNGLGENERATEMIPMAPPROC nglGenerateMipmap; PFNGLBLITFRAMEBUFFERPROC nglBlitFramebuffer; PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC nglRenderbufferStorageMultisample; PFNGLFRAMEBUFFERTEXTURELAYERPROC nglFramebufferTextureLayer; +PFNGLINVALIDATEFRAMEBUFFERPROC nglInvalidateFramebuffer; PFNGLACTIVETEXTUREPROC nglActiveTexture; @@ -503,6 +504,9 @@ static bool setupGLCore(std::vector &glext) CHECK_ADDRESS(PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC, glRenderbufferStorageMultisample); CHECK_ADDRESS(PFNGLFRAMEBUFFERTEXTURELAYERPROC, glFramebufferTextureLayer); + // GL 4.3 / ARB_invalidate_subdata: optional on GL 3.3, always available on GLES 3.0 + nglInvalidateFramebuffer = (PFNGLINVALIDATEFRAMEBUFFERPROC)nglGetProcAddress("glInvalidateFramebuffer"); + CHECK_ADDRESS(PFNGLACTIVETEXTUREPROC, glActiveTexture); CHECK_ADDRESS(PFNGLCOMPRESSEDTEXIMAGE3DPROC, glCompressedTexImage3D); @@ -603,6 +607,14 @@ static bool setupAMDPinnedMemory(std::vector &glext) return true; } +// ********************************* +static bool setupARBInvalidateSubdata(std::vector &glext) +{ + CHECK_EXT_2("GL_ARB_invalidate_subdata"); + + return true; +} + // ********************************* static bool setupNVXGPUMemoryInfo(std::vector &glext) { @@ -701,6 +713,9 @@ bool registerGlExtensions(CGlExtensions &ext) // Check GL_AMD_pinned_memory ext.AMDPinnedMemory = false; // setupAMDPinnedMemory(glext); // TODO: Proper frame sync check + // Check GL_ARB_invalidate_subdata (GL 4.3 core; optional on GL 3.3) + ext.ARBInvalidateSubdata = setupARBInvalidateSubdata(glext); + // Memory info extensions ext.NVXGPUMemoryInfo = setupNVXGPUMemoryInfo(glext); ext.ATIMeminfo = setupATIMeminfo(glext); diff --git a/nel/src/3d/driver/opengl3/driver_opengl3_extension.h b/nel/src/3d/driver/opengl3/driver_opengl3_extension.h index 136197a970..4a2402d4c0 100644 --- a/nel/src/3d/driver/opengl3/driver_opengl3_extension.h +++ b/nel/src/3d/driver/opengl3/driver_opengl3_extension.h @@ -52,6 +52,7 @@ struct CGlExtensions bool EXTTextureCompressionS3TC; bool EXTTextureFilterAnisotropic; float EXTTextureFilterAnisotropicMaximum; + bool ARBInvalidateSubdata; bool AMDPinnedMemory; // Extensions to get memory info @@ -95,6 +96,7 @@ struct CGlExtensions EXTTextureCompressionS3TC = false; EXTTextureFilterAnisotropic = false; EXTTextureFilterAnisotropicMaximum = 0.f; + ARBInvalidateSubdata = false; AMDPinnedMemory = false; NVXGPUMemoryInfo = false; @@ -133,6 +135,7 @@ struct CGlExtensions result += "\n Buffers: "; result += AMDPinnedMemory ? "AMDPinnedMemory " : ""; + result += ARBInvalidateSubdata ? "ARBInvalidateSubdata " : ""; result += "\n Memory info: "; result += NVXGPUMemoryInfo ? "NVXGPUMemoryInfo " : ""; @@ -284,6 +287,7 @@ namespace NLDRIVERGL3 { #define nglBlitFramebuffer glBlitFramebuffer #define nglRenderbufferStorageMultisample glRenderbufferStorageMultisample #define nglFramebufferTextureLayer glFramebufferTextureLayer +#define nglInvalidateFramebuffer glInvalidateFramebuffer #define nglActiveTexture glActiveTexture @@ -491,6 +495,7 @@ extern PFNGLGENERATEMIPMAPPROC nglGenerateMipmap; extern PFNGLBLITFRAMEBUFFERPROC nglBlitFramebuffer; extern PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC nglRenderbufferStorageMultisample; extern PFNGLFRAMEBUFFERTEXTURELAYERPROC nglFramebufferTextureLayer; +extern PFNGLINVALIDATEFRAMEBUFFERPROC nglInvalidateFramebuffer; extern PFNGLACTIVETEXTUREPROC nglActiveTexture; diff --git a/nel/src/3d/driver/opengl3/driver_opengl3_texture.cpp b/nel/src/3d/driver/opengl3/driver_opengl3_texture.cpp index c27b84a6e3..71c5f5b4c3 100644 --- a/nel/src/3d/driver/opengl3/driver_opengl3_texture.cpp +++ b/nel/src/3d/driver/opengl3/driver_opengl3_texture.cpp @@ -248,12 +248,32 @@ bool CTextureDrvInfosGL3::activeFrameBufferObject(ITexture * tex) { _Driver->_DriverGLStates.forceBindTexture(TextureMode, 0); _Driver->_DriverGLStates.forceBindFramebuffer(FBOId); + // Do NOT invalidate depth/stencil here at bind time. + // The application clears depth/stencil immediately after binding, + // which allows ANGLE to use LOAD_OP_CLEAR for the render pass. + // An invalidation here would downgrade that to LOAD_OP_DONT_CARE + // followed by a mid-pass vkCmdClearAttachments, which is slower. } else return false; } else { + // Invalidate depth/stencil attachments before unbinding the FBO. + // On ANGLE (Windows/Android WebGL), switching away from an FBO without + // invalidating forces an expensive depth/stencil resolve/copy. This hint + // tells the driver the data is no longer needed, avoiding the stall. + // (STORE_OP_DONT_CARE on Vulkan, DiscardView on D3D11) + if (AttachDepthStencil) + { + const GLenum attachments[2] = { GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT }; +#ifdef USE_OPENGLES3 + nglInvalidateFramebuffer(GL_FRAMEBUFFER, 2, attachments); +#else + if (_Driver->_Extensions.ARBInvalidateSubdata) + nglInvalidateFramebuffer(GL_FRAMEBUFFER, 2, attachments); +#endif + } _Driver->_DriverGLStates.forceBindFramebuffer(0); } @@ -1765,7 +1785,10 @@ bool CDriverGL3::setRenderTarget (ITexture *tex, uint32 x, uint32 y, uint32 widt } else if (_RenderTargetFBO) { - activeFrameBufferObject(NULL); + // Deactivate through the texture-level method so it can use per-FBO + // state (e.g. AttachDepthStencil) for conditional invalidation. + CTextureDrvInfosGL3* gltext = (CTextureDrvInfosGL3*)(ITextureDrvInfos*)(_RenderTargetFBO->TextureDrvShare->DrvTexture); + gltext->activeFrameBufferObject(NULL); setupViewport(_OldViewport); _OldViewport = _CurrViewport; diff --git a/nel/src/3d/driver_user.cpp b/nel/src/3d/driver_user.cpp index 9c4c3889e6..79ee670c56 100644 --- a/nel/src/3d/driver_user.cpp +++ b/nel/src/3d/driver_user.cpp @@ -1357,8 +1357,7 @@ void CDriverUser::clearBuffers(CRGBA col) NL3D_HAUTO_CLEAR_DRIVER; _Driver->clear2D(col); - _Driver->clearZBuffer(); - _Driver->clearStencilBuffer(); + _Driver->clearDepthStencil(); } // *************************************************************************** void CDriverUser::swapBuffers()