Przeglądaj źródła

To reduce Linux / NVIDIA performance issues, use another FBO for shadow rendering. Commit FBO changes only at SetDepthStencil().

Lasse Öörni 14 lat temu
rodzic
commit
f83b968576

+ 1 - 1
Docs/Reference.dox

@@ -495,7 +495,7 @@ Finally note that due to OpenGL framebuffer object limitations an extra framebuf
 
 - On Direct3D9 the depth-stencil surface can be equal or larger in size than the color rendertarget. On OpenGL the sizes must always match. Furthermore, OpenGL can not use the backbuffer depth-stencil surface when rendering to a texture. To overcome these limitations, Graphics will create correctly sized depth-stencil surfaces on demand whenever a texture is set as a color rendertarget, and a null depth-stencil is specified.
 
-- On Direct3D9 the viewport will be reset to full size when the first color rendertarget is changed. On OpenGL this does not happen. To ensure correct operation, always set the viewport manually after changing the rendertarget.
+- On Direct3D9 the viewport will be reset to full size when the first color rendertarget is changed. On OpenGL this does not happen. Furthermore, to circumvent or reduce performance issues related to switching rendertargets, on OpenGL the rendertarget changes are committed only when the depth-stencil surface is set. Therefore, to ensure correct operation on both APIs, always use this sequence: set rendertargets, set the depth-stencil surface and finally set the viewport.
 
 - On OpenGL modifying a texture will cause it to be momentarily set on the first texture unit. If another texture was set there, the assignment will be lost. Graphics performs a check to not assign textures redundantly, so it is safe and recommended to always set all needed textures before rendering.
 

+ 83 - 107
Engine/Graphics/OpenGL/OGLGraphics.cpp

@@ -269,9 +269,16 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool vsync, bool
     glGetIntegerv(GL_DEPTH_BITS, &impl_->windowDepthBits_);
     impl_->depthBits_ = impl_->windowDepthBits_;
     
-    // Create the FBO
-    glGenFramebuffersEXT(1, &impl_->fbo_);
-    
+    // Create the FBOs and set the read buffer once (FBO will not be read)
+    glGenFramebuffersEXT(1, &impl_->fbo_);    
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, impl_->fbo_);
+    glReadBuffer(GL_NONE);
+    glGenFramebuffersEXT(1, &impl_->depthOnlyFbo_);    
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, impl_->depthOnlyFbo_);
+    glReadBuffer(GL_NONE);
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+    impl_->boundFbo_ = 0;
+
     // Set initial state to match Direct3D
     glEnable(GL_DEPTH_TEST);
     SetCullMode(CULL_CCW);
@@ -1185,52 +1192,9 @@ void Graphics::SetRenderTarget(unsigned index, RenderSurface* renderTarget)
                     SetTexture(i, textures_[i]->GetBackupTexture());
             }
         }
-        
-        // Bind the FBO to be able to make changes to it
-        if (!impl_->fboBound_)
-        {
-            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, impl_->fbo_);
-            impl_->fboBound_ = true;
-        }
-        
-        if (renderTarget)
-        {
-            Texture* texture = renderTarget->GetParentTexture();
-            
-            // If texture's parameters are dirty, update before attaching
-            if (texture->GetParametersDirty())
-            {
-                SetTextureForUpdate(texture);
-                texture->UpdateParameters();
-                SetTexture(0, 0);
-            }
-            
-            glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + index, renderTarget->GetTarget(), texture->GetGPUObject(), 0);
-        }
-        else
-            glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + index, GL_TEXTURE_2D, 0, 0);
-        
-        // Disable color buffer writing/reading if only a depth texture is to be used:
-        // otherwise it is an OpenGL error (incomplete framebuffer)
-        SetDrawBuffers();
-        
-        // If all rendertargets and the depth buffer are not textures, revert to backbuffer rendering
-        bool noFBO = (depthStencil_ == 0);
-        for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
-        {
-            if (renderTargets_[i])
-            {
-                noFBO = false;
-                break;
-            }
-        }
-        
-        if (noFBO && impl_->fboBound_)
-        {
-            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
-            impl_->fboBound_ = false;
-        }
     }
+
+    // Note: the rendertargets are actually committed during SetDepthStencil()
 }
 
 void Graphics::SetRenderTarget(unsigned index, Texture2D* texture)
@@ -1244,9 +1208,30 @@ void Graphics::SetRenderTarget(unsigned index, Texture2D* texture)
 
 void Graphics::SetDepthStencil(RenderSurface* depthStencil)
 {
+    depthStencil_ = depthStencil;
+
+    unsigned targetFbo = 0;
+    if (renderTargets_[0])
+        targetFbo = impl_->fbo_;
+    else if (depthStencil_)
+        targetFbo = impl_->depthOnlyFbo_;
+
+    if (impl_->boundFbo_ != targetFbo)
+    {
+        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, targetFbo);
+        impl_->boundFbo_ = targetFbo;
+    }
+
+    // If we are returning to backbuffer rendering, no further operations necessary
+    if (!targetFbo)
+    {
+        impl_->depthBits_ = impl_->windowDepthBits_;
+        return;
+    }
+
     // If we are using a rendertarget texture, it is required in OpenGL to also have an own depth-stencil
     // Create a new depth-stencil texture as necessary to be able to provide similar behaviour as Direct3D9
-    if (renderTargets_[0] && !depthStencil)
+    if (renderTargets_[0] && !depthStencil_)
     {
         int width = renderTargets_[0]->GetWidth();
         int height = renderTargets_[0]->GetHeight();
@@ -1258,37 +1243,25 @@ void Graphics::SetDepthStencil(RenderSurface* depthStencil)
             int searchKey = (width << 16) | height;
             HashMap<int, SharedPtr<Texture2D> >::Iterator i = depthTextures_.Find(searchKey);
             if (i != depthTextures_.End())
-                depthStencil = i->second_->GetRenderSurface();
+                depthStencil_ = i->second_->GetRenderSurface();
             else
             {
                 SharedPtr<Texture2D> newDepthTexture(new Texture2D(context_));
                 newDepthTexture->SetSize(width, height, GetDepthStencilFormat(), TEXTURE_DEPTHSTENCIL);
                 depthTextures_[searchKey] = newDepthTexture;
-                depthStencil = newDepthTexture->GetRenderSurface();
+                depthStencil_ = newDepthTexture->GetRenderSurface();
             }
         }
     }
     
-    if (depthStencil != depthStencil_)
+    if (targetFbo == impl_->fbo_)
     {
-        /// \todo Should check that the texture actually is in depth format
-        depthStencil_ = depthStencil;
-        
-        // Bind the FBO to be able to make changes to it
-        if (!impl_->fboBound_)
-        {
-            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, impl_->fbo_);
-            impl_->fboBound_ = true;
-        }
-        
-        if (depthStencil)
+        for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
         {
-            // Bind either a renderbuffer or a depth texture, depending on what is available
-            Texture* texture = depthStencil->GetParentTexture();
-            bool hasStencil = texture->GetFormat() == GetDepthStencilFormat();
-            unsigned renderBufferID = depthStencil->GetRenderBuffer();
-            if (!renderBufferID)
+            if (renderTargets_[i])
             {
+                Texture* texture = renderTargets_[i]->GetParentTexture();
+            
                 // If texture's parameters are dirty, update before attaching
                 if (texture->GetParametersDirty())
                 {
@@ -1297,52 +1270,56 @@ void Graphics::SetDepthStencil(RenderSurface* depthStencil)
                     SetTexture(0, 0);
                 }
                 
-                glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, texture->GetGPUObject(), 0);
-                if (hasStencil)
-                    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, texture->GetGPUObject(), 0);
-                else
-                    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0);
-                
-                impl_->depthBits_ = texture->GetDepthBits();
+                glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + i, renderTargets_[i]->GetTarget(),
+                    texture->GetGPUObject(), 0);
             }
             else
-            {
-                glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, renderBufferID);
-                if (hasStencil)
-                    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, renderBufferID);
-                else
-                    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0);
-                impl_->depthBits_ = 24;
-            }
-        }
-        else
-        {
-            glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0);
-            glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0);
-            impl_->depthBits_ = impl_->windowDepthBits_;
-        }
-        
-        // Disable color buffer writing/reading if only a depth texture is to be used:
-        // otherwise it is an OpenGL error (incomplete framebuffer)
+                glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + i, GL_TEXTURE_2D, 0, 0);
+        }        
+    
         SetDrawBuffers();
-        
-        // If all rendertargets and the depth buffer are not textures, revert to backbuffer rendering
-        bool noFBO = (depthStencil_ == 0);
-        for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
+    }
+      
+    if (depthStencil_)
+    {
+        // Bind either a renderbuffer or a depth texture, depending on what is available
+        Texture* texture = depthStencil_->GetParentTexture();
+        bool hasStencil = texture->GetFormat() == GetDepthStencilFormat();
+        unsigned renderBufferID = depthStencil_->GetRenderBuffer();
+        if (!renderBufferID)
         {
-            if (renderTargets_[i])
+            // If texture's parameters are dirty, update before attaching
+            if (texture->GetParametersDirty())
             {
-                noFBO = false;
-                break;
+                SetTextureForUpdate(texture);
+                texture->UpdateParameters();
+                SetTexture(0, 0);
             }
+                
+            glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, texture->GetGPUObject(), 0);
+            if (hasStencil)
+                glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, texture->GetGPUObject(), 0);
+            else
+                glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0);
+                
+            impl_->depthBits_ = texture->GetDepthBits();
         }
-        
-        if (noFBO && impl_->fboBound_)
+        else
         {
-            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
-            impl_->fboBound_ = false;
+            glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, renderBufferID);
+            if (hasStencil)
+                glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, renderBufferID);
+            else
+                glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0);
+            impl_->depthBits_ = 24;
         }
     }
+    else
+    {
+        glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0);
+        glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0);
+        impl_->depthBits_ = impl_->windowDepthBits_;
+    }
 }
 
 void Graphics::SetDepthStencil(Texture2D* texture)
@@ -1844,7 +1821,9 @@ void Graphics::Release(bool clearGPUObjects, bool closeWindow)
     if (impl_->fbo_)
     {
         glDeleteFramebuffersEXT(1, &impl_->fbo_);
+        glDeleteFramebuffersEXT(1, &impl_->depthOnlyFbo_);
         impl_->fbo_ = 0;
+        impl_->depthOnlyFbo_ = 0;
     }
     
     // When the new context is initialized, it will have default state again
@@ -1978,8 +1957,6 @@ void Graphics::SetDrawBuffers()
     }
     
     impl_->drawBuffers_ = newDrawBuffers;
-    
-    glReadBuffer(GL_NONE);
 }
 
 bool Graphics::CheckFramebuffer()
@@ -2037,7 +2014,6 @@ void Graphics::ResetCachedState()
     impl_->activeTexture_ = 0;
     impl_->drawBuffers_ = M_MAX_UNSIGNED;
     impl_->enabledAttributes_ = 0;
-    impl_->fboBound_ = false;
 }
 
 void Graphics::SetTextureUnitMappings()

+ 3 - 2
Engine/Graphics/OpenGL/OGLGraphicsImpl.cpp

@@ -36,11 +36,12 @@ GraphicsImpl::GraphicsImpl() :
     activeTexture_(0),
     drawBuffers_(M_MAX_UNSIGNED),
     fbo_(0),
+    depthOnlyFbo_(0),
+    boundFbo_(0),
     enabledAttributes_(0),
     pixelFormat_(0),
     depthBits_(0),
-    windowDepthBits_(0),
-    fboBound_(false)
+    windowDepthBits_(0)
 {
 }
 

+ 4 - 2
Engine/Graphics/OpenGL/OGLGraphicsImpl.h

@@ -49,6 +49,10 @@ private:
     unsigned drawBuffers_;
     /// Frame buffer object. Reused whenever rendering to a texture.
     unsigned fbo_;
+    /// Frame buffer object for depth-only rendering.
+    unsigned depthOnlyFbo_;
+    /// Currently bound frame buffer object.
+    unsigned boundFbo_;
     /// Vertex attributes in use.
     unsigned enabledAttributes_;
     /// Current pixel format.
@@ -57,8 +61,6 @@ private:
     int depthBits_;
     /// Backbuffer depth bits.
     int windowDepthBits_;
-    /// FBO bound flag.
-    bool fboBound_;
 };
 
 /// Store execution context specific to the GLFW window

+ 2 - 0
Engine/Graphics/View.cpp

@@ -1155,6 +1155,8 @@ void View::RenderBatchesDeferred()
         graphics_->ResetRenderTarget(1);
         graphics_->ResetRenderTarget(2);
         graphics_->ResetRenderTarget(3);
+        graphics_->SetDepthStencil(depthStencil);
+        graphics_->SetViewport(viewRect_);
     }
     
     // Render shadow maps + light volumes