Browse Source

Further fixes and optimizations to View render target pingpong handling. Update Material memory use when modified programmatically.

Lasse Öörni 12 years ago
parent
commit
030f890ed5

+ 20 - 7
Source/Engine/Graphics/Material.cpp

@@ -116,6 +116,7 @@ Material::Material(Context* context) :
 {
 {
     SetNumTechniques(1);
     SetNumTechniques(1);
     ResetToDefaults();
     ResetToDefaults();
+    RefreshMemoryUse();
 }
 }
 
 
 Material::~Material()
 Material::~Material()
@@ -234,13 +235,7 @@ bool Material::Load(const XMLElement& source)
         SetDepthBias(BiasParameters(depthBiasElem.GetFloat("constant"), depthBiasElem.GetFloat("slopescaled")));
         SetDepthBias(BiasParameters(depthBiasElem.GetFloat("constant"), depthBiasElem.GetFloat("slopescaled")));
     
     
     // Calculate memory use
     // Calculate memory use
-    unsigned memoryUse = sizeof(Material);
-    
-    memoryUse += techniques_.Size() * sizeof(TechniqueEntry);
-    memoryUse += MAX_MATERIAL_TEXTURE_UNITS * sizeof(SharedPtr<Texture>);
-    memoryUse += shaderParameters_.Size() * sizeof(MaterialShaderParameter);
-    
-    SetMemoryUse(memoryUse);
+    RefreshMemoryUse();
     CheckOcclusion();
     CheckOcclusion();
     return true;
     return true;
 }
 }
@@ -307,6 +302,7 @@ void Material::SetNumTechniques(unsigned num)
         return;
         return;
     
     
     techniques_.Resize(num);
     techniques_.Resize(num);
+    RefreshMemoryUse();
 }
 }
 
 
 void Material::SetTechnique(unsigned index, Technique* tech, unsigned qualityLevel, float lodDistance)
 void Material::SetTechnique(unsigned index, Technique* tech, unsigned qualityLevel, float lodDistance)
@@ -340,6 +336,8 @@ void Material::SetShaderParameter(const String& name, const Variant& value)
             specular_ = vec.x_ > 0.0f || vec.y_ > 0.0f || vec.z_ > 0.0f;
             specular_ = vec.x_ > 0.0f || vec.y_ > 0.0f || vec.z_ > 0.0f;
         }
         }
     }
     }
+    
+    RefreshMemoryUse();
 }
 }
 
 
 void Material::SetTexture(TextureUnit unit, Texture* texture)
 void Material::SetTexture(TextureUnit unit, Texture* texture)
@@ -404,6 +402,8 @@ void Material::RemoveShaderParameter(const String& name)
 
 
     if (nameHash == PSP_MATSPECCOLOR)
     if (nameHash == PSP_MATSPECCOLOR)
         specular_ = false;
         specular_ = false;
+    
+    RefreshMemoryUse();
 }
 }
 
 
 void Material::ReleaseShaders()
 void Material::ReleaseShaders()
@@ -426,8 +426,10 @@ SharedPtr<Material> Material::Clone(const String& cloneName) const
     for (unsigned i = 0; i < MAX_MATERIAL_TEXTURE_UNITS; ++i)
     for (unsigned i = 0; i < MAX_MATERIAL_TEXTURE_UNITS; ++i)
         ret->textures_[i] = textures_[i];
         ret->textures_[i] = textures_[i];
     ret->occlusion_ = occlusion_;
     ret->occlusion_ = occlusion_;
+    ret->specular_ = specular_;
     ret->cullMode_ = cullMode_;
     ret->cullMode_ = cullMode_;
     ret->shadowCullMode_ = shadowCullMode_;
     ret->shadowCullMode_ = shadowCullMode_;
+    ret->RefreshMemoryUse();
     
     
     return ret;
     return ret;
 }
 }
@@ -518,4 +520,15 @@ void Material::ResetToDefaults()
     depthBias_ = BiasParameters(0.0f, 0.0f);
     depthBias_ = BiasParameters(0.0f, 0.0f);
 }
 }
 
 
+void Material::RefreshMemoryUse()
+{
+    unsigned memoryUse = sizeof(Material);
+    
+    memoryUse += techniques_.Size() * sizeof(TechniqueEntry);
+    memoryUse += MAX_MATERIAL_TEXTURE_UNITS * sizeof(SharedPtr<Texture>);
+    memoryUse += shaderParameters_.Size() * sizeof(MaterialShaderParameter);
+    
+    SetMemoryUse(memoryUse);
+}
+
 }
 }

+ 2 - 0
Source/Engine/Graphics/Material.h

@@ -155,6 +155,8 @@ private:
     void CheckOcclusion();
     void CheckOcclusion();
     /// Reset to defaults.
     /// Reset to defaults.
     void ResetToDefaults();
     void ResetToDefaults();
+    /// Recalculate the memory used by the material.
+    void RefreshMemoryUse();
     
     
     /// Techniques.
     /// Techniques.
     Vector<TechniqueEntry> techniques_;
     Vector<TechniqueEntry> techniques_;

+ 87 - 47
Source/Engine/Graphics/View.cpp

@@ -401,14 +401,8 @@ bool View::Define(RenderSurface* renderTarget, Viewport* viewport)
         // Check if ambient pass and G-buffer rendering happens at the same time
         // Check if ambient pass and G-buffer rendering happens at the same time
         if (command.type_ == CMD_SCENEPASS && command.outputNames_.Size() > 1)
         if (command.type_ == CMD_SCENEPASS && command.outputNames_.Size() > 1)
         {
         {
-            for (unsigned j = 0; j < command.outputNames_.Size(); ++j)
-            {
-                if (!command.outputNames_[j].Compare("viewport", false))
-                {
-                    deferredAmbient_ = true;
-                    break;
-                }
-            }
+            if (CheckViewportWrite(command))
+                deferredAmbient_ = true;
         }
         }
         
         
         if (command.type_ == CMD_LIGHTVOLUMES)
         if (command.type_ == CMD_LIGHTVOLUMES)
@@ -1242,29 +1236,61 @@ void View::ExecuteRenderPathCommands()
         
         
         // Set for safety in case of empty renderpath
         // Set for safety in case of empty renderpath
         currentRenderTarget_ = substituteRenderTarget_ ? substituteRenderTarget_ : renderTarget_;
         currentRenderTarget_ = substituteRenderTarget_ ? substituteRenderTarget_ : renderTarget_;
+        currentViewportTexture_ = 0;
 
 
         bool viewportModified = false;
         bool viewportModified = false;
         bool isPingponging = false;
         bool isPingponging = false;
         
         
+        unsigned lastCommandIndex = 0;
+        for (unsigned i = 0; i < renderPath_->commands_.Size(); ++i)
+        {
+            RenderPathCommand& command = renderPath_->commands_[i];
+            if (IsNecessary(command))
+                lastCommandIndex = i;
+        }
+
         for (unsigned i = 0; i < renderPath_->commands_.Size(); ++i)
         for (unsigned i = 0; i < renderPath_->commands_.Size(); ++i)
         {
         {
             RenderPathCommand& command = renderPath_->commands_[i];
             RenderPathCommand& command = renderPath_->commands_[i];
             if (!IsNecessary(command))
             if (!IsNecessary(command))
                 continue;
                 continue;
+
+            bool viewportRead = CheckViewportRead(command);
+            bool viewportWrite = CheckViewportWrite(command);
+            bool beginPingpong = CheckPingpong(i);
             
             
             // Has the viewport been modified and will be read as a texture by the current command?
             // Has the viewport been modified and will be read as a texture by the current command?
-            if (CheckViewportRead(command) && viewportModified)
+            if (viewportRead && viewportModified)
             {
             {
+                // Start pingponging without a blit if already rendering to the substitute render target
+                if (currentRenderTarget_ && currentRenderTarget_ == substituteRenderTarget_ && beginPingpong)
+                    isPingponging = true;
+                
                 // If not using pingponging, simply resolve/copy to the first viewport texture
                 // If not using pingponging, simply resolve/copy to the first viewport texture
                 if (!isPingponging)
                 if (!isPingponging)
                 {
                 {
                     if (!currentRenderTarget_)
                     if (!currentRenderTarget_)
+                    {
                         graphics_->ResolveToTexture(viewportTextures_[0], viewRect_);
                         graphics_->ResolveToTexture(viewportTextures_[0], viewRect_);
+                        currentViewportTexture_ = viewportTextures_[0];
+                        viewportModified = false;
+                    }
                     else
                     else
                     {
                     {
-                        /// \todo Must use dynamic_cast because the render target could also be a cube map. That will not currently work
-                        BlitFramebuffer(dynamic_cast<Texture2D*>(currentRenderTarget_->GetParentTexture()),
-                            viewportTextures_[0]->GetRenderSurface(), false);
+                        if (viewportWrite)
+                        {
+                            BlitFramebuffer(static_cast<Texture2D*>(currentRenderTarget_->GetParentTexture()),
+                                viewportTextures_[0]->GetRenderSurface(), false);
+                            currentViewportTexture_ = viewportTextures_[0];
+                            viewportModified = false;
+                        }
+                        else
+                        {
+                            // If the current render target is already a texture, and we are not writing to it, can read that
+                            // texture directly instead of blitting. However keep the viewport dirty flag in case a later command
+                            // will do both read and write, and then we need to blit / resolve
+                            currentViewportTexture_ = static_cast<Texture2D*>(currentRenderTarget_->GetParentTexture());
+                        }
                     }
                     }
                 }
                 }
                 else
                 else
@@ -1272,20 +1298,27 @@ void View::ExecuteRenderPathCommands()
                     // Swap the pingpong double buffer sides. Texture 0 will be read next
                     // Swap the pingpong double buffer sides. Texture 0 will be read next
                     viewportTextures_[1] = viewportTextures_[0];
                     viewportTextures_[1] = viewportTextures_[0];
                     viewportTextures_[0] = static_cast<Texture2D*>(currentRenderTarget_->GetParentTexture());
                     viewportTextures_[0] = static_cast<Texture2D*>(currentRenderTarget_->GetParentTexture());
+                    currentViewportTexture_ = viewportTextures_[0];
+                    viewportModified = false;
                 }
                 }
-
-                viewportModified = false;
             }
             }
+            
+            if (beginPingpong)
+                isPingponging = true;
 
 
-            // Check if current command begins / continues the pingpong chain. The final command in the renderpath ends the chain
-            // by rendering to the destination render target again
-            if (CheckPingpong(i))
+            // Determine viewport write target
+            if (viewportWrite)
             {
             {
-                isPingponging = true;
-                currentRenderTarget_ = viewportTextures_[1]->GetRenderSurface();
+                if (isPingponging)
+                {
+                    currentRenderTarget_ = viewportTextures_[1]->GetRenderSurface();
+                    // If the render path ends into a quad, it can be redirected to the final render target
+                    if (i == lastCommandIndex && command.type_ == CMD_QUAD)
+                        currentRenderTarget_ = renderTarget_;
+                }
+                else
+                    currentRenderTarget_ = substituteRenderTarget_ ? substituteRenderTarget_ : renderTarget_;
             }
             }
-            else
-                currentRenderTarget_ = substituteRenderTarget_ ? substituteRenderTarget_ : renderTarget_;
 
 
             switch (command.type_)
             switch (command.type_)
             {
             {
@@ -1388,7 +1421,7 @@ void View::ExecuteRenderPathCommands()
             }
             }
 
 
             // If current command output to the viewport, mark it modified
             // If current command output to the viewport, mark it modified
-            if (!command.outputNames_[0].Compare("viewport", false))
+            if (viewportWrite)
                 viewportModified = true;
                 viewportModified = true;
         }
         }
     }
     }
@@ -1480,7 +1513,7 @@ void View::SetTextures(RenderPathCommand& command)
         // Bind the rendered output
         // Bind the rendered output
         if (!command.textureNames_[i].Compare("viewport", false))
         if (!command.textureNames_[i].Compare("viewport", false))
         {
         {
-            graphics_->SetTexture(i, viewportTextures_[0]);
+            graphics_->SetTexture(i, currentViewportTexture_);
             continue;
             continue;
         }
         }
         
         
@@ -1589,35 +1622,40 @@ bool View::CheckViewportRead(const RenderPathCommand& command)
     return false;
     return false;
 }
 }
 
 
+bool View::CheckViewportWrite(const RenderPathCommand& command)
+{
+    for (unsigned i = 0; i < command.outputNames_.Size(); ++i)
+    {
+        if (!command.outputNames_[i].Compare("viewport", false))
+            return true;
+    }
+    
+    return false;
+}
+
+
 bool View::CheckPingpong(unsigned index)
 bool View::CheckPingpong(unsigned index)
 {
 {
+    // Current command must be a viewport-writing quad to begin the pingpong chain
     RenderPathCommand& current = renderPath_->commands_[index];
     RenderPathCommand& current = renderPath_->commands_[index];
-    if (current.type_ != CMD_QUAD || current.outputNames_.Size() != 1 || current.outputNames_[0].Compare("viewport", false))
+    if (current.type_ != CMD_QUAD || !CheckViewportWrite(current))
         return false;
         return false;
 
 
     // If there are commands other than quads that target the viewport, we must keep rendering to the final target and resolving
     // If there are commands other than quads that target the viewport, we must keep rendering to the final target and resolving
     // to a viewport texture when necessary instead of pingponging, as a scene pass is not guaranteed to fill the entire viewport
     // to a viewport texture when necessary instead of pingponging, as a scene pass is not guaranteed to fill the entire viewport
     for (unsigned i = index + 1; i < renderPath_->commands_.Size(); ++i)
     for (unsigned i = index + 1; i < renderPath_->commands_.Size(); ++i)
     {
     {
-        RenderPathCommand& command = renderPath_->commands_[index];
-        if (!IsNecessary(command))
-            continue;
-        if (!command.outputNames_[0].Compare("viewport", false) && command.type_ != CMD_QUAD)
-            return false;
-    }
-
-    // Pingponging is OK when there are several quad commands in sequence at the end of the renderpath
-    for (unsigned i = index + 1; i < renderPath_->commands_.Size(); ++i)
-    {
-        RenderPathCommand& command = renderPath_->commands_[index];
+        RenderPathCommand& command = renderPath_->commands_[i];
         if (!IsNecessary(command))
         if (!IsNecessary(command))
             continue;
             continue;
-        if (command.type_ == CMD_QUAD && current.outputNames_.Size() == 1 && current.outputNames_[0].Compare("viewport", false))
-            return true;
+        if (CheckViewportWrite(command))
+        {
+            if (command.type_ != CMD_QUAD)
+                return false;
+        }
     }
     }
 
 
-    // Note: the last quad command does not pingpong; it should write to the final destination rendertarget
-    return false;
+    return true;
 }
 }
 
 
 void View::AllocateScreenBuffers()
 void View::AllocateScreenBuffers()
@@ -1631,7 +1669,6 @@ void View::AllocateScreenBuffers()
     if ((deferred_ && !renderTarget_) || (deferredAmbient_ && renderTarget_ && renderTarget_->GetParentTexture()->GetFormat() !=
     if ((deferred_ && !renderTarget_) || (deferredAmbient_ && renderTarget_ && renderTarget_->GetParentTexture()->GetFormat() !=
         Graphics::GetRGBAFormat()))
         Graphics::GetRGBAFormat()))
         needSubstitute = true;
         needSubstitute = true;
-
     #endif
     #endif
     // If backbuffer is antialiased when using deferred rendering, need to reserve a buffer
     // If backbuffer is antialiased when using deferred rendering, need to reserve a buffer
     if (deferred_ && !renderTarget_ && graphics_->GetMultiSample() > 1)
     if (deferred_ && !renderTarget_ && graphics_->GetMultiSample() > 1)
@@ -1661,9 +1698,15 @@ void View::AllocateScreenBuffers()
     }
     }
 
 
     if (hasViewportRead)
     if (hasViewportRead)
+    {
         ++numViewportTextures;
         ++numViewportTextures;
-    if (hasPingpong && !needSubstitute)
-        ++numViewportTextures;
+        // If we have viewport read and target is a cube map, must allocate a substitute target instead as BlitFramebuffer()
+        // does not support reading a cube map
+        if (renderTarget_ && renderTarget_->GetParentTexture()->GetType() == TextureCube::GetTypeStatic())
+            needSubstitute = true;
+        if (hasPingpong && !needSubstitute)
+            ++numViewportTextures;
+    }
 
 
     // Allocate screen buffers with filtering active in case the quad commands need that
     // Allocate screen buffers with filtering active in case the quad commands need that
     // Follow the sRGB mode of the destination render target
     // Follow the sRGB mode of the destination render target
@@ -1678,8 +1721,8 @@ void View::AllocateScreenBuffers()
         viewportTextures_[i] = i < numViewportTextures ? renderer_->GetScreenBuffer(rtSize_.x_, rtSize_.y_, format, true, sRGB) :
         viewportTextures_[i] = i < numViewportTextures ? renderer_->GetScreenBuffer(rtSize_.x_, rtSize_.y_, format, true, sRGB) :
             (Texture2D*)0;
             (Texture2D*)0;
     }
     }
-    // If has allocated the substitute render target, it can also be used for pingponging
-    if (substituteRenderTarget_ && hasPingpong)
+    // If using a substitute render target and pingponging, the substitute can act as the second viewport texture
+    if (numViewportTextures == 1 && substituteRenderTarget_)
         viewportTextures_[1] = static_cast<Texture2D*>(substituteRenderTarget_->GetParentTexture());
         viewportTextures_[1] = static_cast<Texture2D*>(substituteRenderTarget_->GetParentTexture());
     
     
     // Allocate extra render targets defined by the rendering path
     // Allocate extra render targets defined by the rendering path
@@ -1710,10 +1753,7 @@ void View::AllocateScreenBuffers()
 void View::BlitFramebuffer(Texture2D* source, RenderSurface* destination, bool depthWrite)
 void View::BlitFramebuffer(Texture2D* source, RenderSurface* destination, bool depthWrite)
 {
 {
     if (!source)
     if (!source)
-    {
-        LOGERROR("Null source texture in BlitFramebuffer");
         return;
         return;
-    }
 
 
     PROFILE(BlitFramebuffer);
     PROFILE(BlitFramebuffer);
     
     

+ 6 - 2
Source/Engine/Graphics/View.h

@@ -165,9 +165,11 @@ private:
     void RenderQuad(RenderPathCommand& command);
     void RenderQuad(RenderPathCommand& command);
     /// Check if a command is enabled and has content to render. To be called only after render update has completed for the frame.
     /// Check if a command is enabled and has content to render. To be called only after render update has completed for the frame.
     bool IsNecessary(const RenderPathCommand& command);
     bool IsNecessary(const RenderPathCommand& command);
-    /// Check if a command reads the rendered scene.
+    /// Check if a command reads the destination render target.
     bool CheckViewportRead(const RenderPathCommand& command);
     bool CheckViewportRead(const RenderPathCommand& command);
-    /// Check whether a command should use pingponging instead of simple resolve to viewport texture.
+    /// Check if a command writes into the destination render target.
+    bool CheckViewportWrite(const RenderPathCommand& command);
+    /// Check whether a command should use pingponging instead of resolve from destination render target to viewport texture.
     bool CheckPingpong(unsigned index);
     bool CheckPingpong(unsigned index);
     /// Allocate needed screen buffers.
     /// Allocate needed screen buffers.
     void AllocateScreenBuffers();
     void AllocateScreenBuffers();
@@ -268,6 +270,8 @@ private:
     Texture2D* viewportTextures_[MAX_VIEWPORT_TEXTURES];
     Texture2D* viewportTextures_[MAX_VIEWPORT_TEXTURES];
     /// Color rendertarget active for the current renderpath command.
     /// Color rendertarget active for the current renderpath command.
     RenderSurface* currentRenderTarget_;
     RenderSurface* currentRenderTarget_;
+    /// Texture containing the latest viewport texture.
+    Texture2D* currentViewportTexture_;
     /// Viewport rectangle.
     /// Viewport rectangle.
     IntRect viewRect_;
     IntRect viewRect_;
     /// Viewport size.
     /// Viewport size.