Browse Source

Refactored view queuing in Renderer.
RenderSurface can define multiple viewports.
RenderSurface viewports can be set to update when visible in the scene (default, same as previous functionality), update always, or to update only when manually queued. The last two modes are useful when rendering a scene view eg. for use in a UI widget.

Lasse Öörni 12 years ago
parent
commit
b2645460ef

+ 4 - 2
Docs/Reference.dox

@@ -974,7 +974,7 @@ Zones also define a lightmask and a shadowmask (with all bits set by default.) A
 
 \page AuxiliaryViews Auxiliary views
 
-Auxiliary views are viewports defined into a RenderSurface. These will be rendered whenever the texture containing the surface is visible, and can be typically used to implement for example reflections. The texture in question must have been created in rendertarget mode, see Texture's \ref Texture2D::SetSize "SetSize()" function.
+Auxiliary views are viewports defined into a RenderSurface. By default, these will be rendered whenever the texture containing the surface is visible, and can be typically used to implement for example camera displays or reflections. The texture in question must have been created in rendertarget mode, see Texture's \ref Texture2D::SetSize "SetSize()" function.
 
 The viewport is not assigned directly to the texture because of cube map support: a renderable cube map has 6 render surfaces, and done this way, a different camera could be assigned to each.
 
@@ -983,9 +983,11 @@ A "backup texture" can be assigned to the rendertarget texture: because it is il
 Rendering detailed auxiliary views can easily have a large performance impact. Some things you can do for optimization with the auxiliary view camera:
 
 - Set the far clip distance as small as possible.
-- Set the camera's viewmask to for example VIEW_REFLECTION, then clear that viewmask bit from objects you don't need rendered.
+- Use viewmasks on the camera and the scene objects to only render some of the objects in the auxiliary view.
 - Use the camera's \ref Camera::SetViewOverrideFlags "SetViewOverrideFlags()" function to disable shadows, to disable occlusion, or force the lowest material quality.
 
+The surface can also be configured to always update its viewports, or to only update when manually requested. See \ref RenderSurface::SetUpdateMode "SetUpdateMode()". For example an editor widget showing a rendered texture might use either of those modes. Call \ref RenderSurface::QueueUpdate "QueueUpdate()" to request a manual update of the surface on the current frame.
+
 
 \page Input %Input
 

+ 1 - 1
Engine/Container/Ptr.h

@@ -344,7 +344,7 @@ public:
     
     /// Check if the pointer is null.
     bool Null() const { return refCount_ == 0; }
-    /// Check if the pointer is not null. It does not matter whether the object has expired or not.
+    /// Check if the pointer is not null.
     bool NotNull() const { return refCount_ != 0; }
     /// Return the object's reference count, or 0 if null pointer or if object has expired.
     int Refs() const { return (refCount_ && refCount_->refs_ >= 0) ? refCount_->refs_ : 0; }

+ 12 - 3
Engine/Engine/GraphicsAPI.cpp

@@ -384,6 +384,10 @@ static void RegisterTextures(asIScriptEngine* engine)
     engine->RegisterEnumValue("CubeMapFace", "FACE_POSITIVE_Z", FACE_POSITIVE_Z);
     engine->RegisterEnumValue("CubeMapFace", "FACE_NEGATIVE_Z", FACE_NEGATIVE_Z);
     
+    engine->RegisterEnum("RenderSurfaceUpdateMode");
+    engine->RegisterEnumValue("RenderSurfaceUpdateMode", "SURFACE_MANUALUPDATE", SURFACE_MANUALUPDATE);
+    engine->RegisterEnumValue("RenderSurfaceUpdateMode", "SURFACE_UPDATEVISIBLE", SURFACE_UPDATEVISIBLE);
+    engine->RegisterEnumValue("RenderSurfaceUpdateMode", "SURFACE_UPDATEALWAYS", SURFACE_UPDATEALWAYS);
     RegisterTexture<Texture>(engine, "Texture");
     
     RegisterObject<Viewport>(engine, "Viewport");
@@ -403,12 +407,17 @@ static void RegisterTextures(asIScriptEngine* engine)
     engine->RegisterObjectType("RenderSurface", 0, asOBJ_REF);
     engine->RegisterObjectBehaviour("RenderSurface", asBEHAVE_ADDREF, "void f()", asMETHOD(RenderSurface, AddRef), asCALL_THISCALL);
     engine->RegisterObjectBehaviour("RenderSurface", asBEHAVE_RELEASE, "void f()", asMETHOD(RenderSurface, ReleaseRef), asCALL_THISCALL);
+    engine->RegisterObjectMethod("RenderSurface", "void QueueUpdate()", asMETHOD(RenderSurface, QueueUpdate), asCALL_THISCALL);
     engine->RegisterObjectMethod("RenderSurface", "Texture@+ get_parentTexture() const", asMETHOD(RenderSurface, GetParentTexture), asCALL_THISCALL);
     engine->RegisterObjectMethod("RenderSurface", "int get_width() const", asMETHOD(RenderSurface, GetWidth), asCALL_THISCALL);
     engine->RegisterObjectMethod("RenderSurface", "int get_height() const", asMETHOD(RenderSurface, GetHeight), asCALL_THISCALL);
     engine->RegisterObjectMethod("RenderSurface", "TextureUsage get_usage() const", asMETHOD(RenderSurface, GetUsage), asCALL_THISCALL);
-    engine->RegisterObjectMethod("RenderSurface", "void set_viewport(Viewport@+)", asMETHOD(RenderSurface, SetViewport), asCALL_THISCALL);
-    engine->RegisterObjectMethod("RenderSurface", "Viewport@+ get_viewport() const", asMETHOD(RenderSurface, GetViewport), asCALL_THISCALL);
+    engine->RegisterObjectMethod("RenderSurface", "void set_numViewports(uint)", asMETHOD(RenderSurface, SetNumViewports), asCALL_THISCALL);
+    engine->RegisterObjectMethod("RenderSurface", "uint get_numViewports() const", asMETHOD(RenderSurface, GetNumViewports), asCALL_THISCALL);
+    engine->RegisterObjectMethod("RenderSurface", "void set_viewports(uint, Viewport@+)", asMETHOD(RenderSurface, SetViewport), asCALL_THISCALL);
+    engine->RegisterObjectMethod("RenderSurface", "Viewport@+ get_viewports(uint) const", asMETHOD(RenderSurface, GetViewport), asCALL_THISCALL);
+    engine->RegisterObjectMethod("RenderSurface", "void set_updateMode(RenderSurfaceUpdateMode)", asMETHOD(RenderSurface, SetUpdateMode), asCALL_THISCALL);
+    engine->RegisterObjectMethod("RenderSurface", "RenderSurfaceUpdateMode get_updateMode() const", asMETHOD(RenderSurface, GetUpdateMode), asCALL_THISCALL);
     engine->RegisterObjectMethod("RenderSurface", "void set_linkedRenderTarget(RenderSurface@+)", asMETHOD(RenderSurface, SetLinkedRenderTarget), asCALL_THISCALL);
     engine->RegisterObjectMethod("RenderSurface", "RenderSurface@+ get_linkedRenderTarget() const", asMETHOD(RenderSurface, GetLinkedRenderTarget), asCALL_THISCALL);
     engine->RegisterObjectMethod("RenderSurface", "void set_linkedDepthStencil(RenderSurface@+)", asMETHOD(RenderSurface, SetLinkedDepthStencil), asCALL_THISCALL);
@@ -1028,7 +1037,7 @@ static void RegisterRenderer(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Renderer", "void ReloadShaders() const", asMETHOD(Renderer, ReloadShaders), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_numViewports(uint)", asMETHOD(Renderer, SetNumViewports), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "uint get_numViewports() const", asMETHOD(Renderer, GetNumViewports), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Renderer", "bool set_viewports(uint, Viewport@+)", asMETHOD(Renderer, SetViewport), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Renderer", "void set_viewports(uint, Viewport@+)", asMETHOD(Renderer, SetViewport), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "Viewport@+ get_viewports(uint) const", asMETHOD(Renderer, GetViewport), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void SetDefaultRenderPath(XMLFile@+)", asMETHODPR(Renderer, SetDefaultRenderPath, (XMLFile*), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_defaultRenderPath(RenderPath@+)", asMETHODPR(Renderer, SetDefaultRenderPath, (RenderPath*), void), asCALL_THISCALL);

+ 57 - 4
Engine/Graphics/Direct3D9/D3D9RenderSurface.cpp

@@ -25,6 +25,7 @@
 #include "Graphics.h"
 #include "GraphicsImpl.h"
 #include "Log.h"
+#include "Renderer.h"
 #include "RenderSurface.h"
 #include "Scene.h"
 #include "Texture.h"
@@ -36,7 +37,9 @@ namespace Urho3D
 
 RenderSurface::RenderSurface(Texture* parentTexture) :
     parentTexture_(parentTexture),
-    surface_(0)
+    surface_(0),
+    updateMode_(SURFACE_UPDATEVISIBLE),
+    updateQueued_(false)
 {
 }
 
@@ -45,10 +48,22 @@ RenderSurface::~RenderSurface()
     Release();
 }
 
-void RenderSurface::SetViewport(Viewport* viewport)
+void RenderSurface::SetNumViewports(unsigned num)
 {
-    if (viewport)
-        viewport_ = viewport;
+    viewports_.Resize(num);
+}
+
+void RenderSurface::SetViewport(unsigned index, Viewport* viewport)
+{
+    if (index >= viewports_.Size())
+        viewports_.Resize(index + 1);
+    
+    viewports_[index] = viewport;
+}
+
+void RenderSurface::SetUpdateMode(RenderSurfaceUpdateMode mode)
+{
+    updateMode_ = mode;
 }
 
 void RenderSurface::SetLinkedRenderTarget(RenderSurface* renderTarget)
@@ -63,6 +78,34 @@ void RenderSurface::SetLinkedDepthStencil(RenderSurface* depthStencil)
         linkedDepthStencil_ = depthStencil;
 }
 
+void RenderSurface::QueueUpdate()
+{
+    if (!updateQueued_)
+    {
+        bool hasValidView = false;
+        
+        // Verify that there is at least 1 non-null viewport, as otherwise Renderer will not accept the surface and the update flag
+        // will be left on
+        for (unsigned i = 0; i < viewports_.Size(); ++i)
+        {
+            if (viewports_[i])
+            {
+                hasValidView = true;
+                break;
+            }
+        }
+        
+        if (hasValidView)
+        {
+            Renderer* renderer = parentTexture_->GetSubsystem<Renderer>();
+            if (renderer)
+                renderer->QueueRenderSurface(this);
+            
+            updateQueued_ = true;
+        }
+    }
+}
+
 void RenderSurface::Release()
 {
     Graphics* graphics = parentTexture_->GetGraphics();
@@ -100,4 +143,14 @@ TextureUsage RenderSurface::GetUsage() const
     return parentTexture_->GetUsage();
 }
 
+Viewport* RenderSurface::GetViewport(unsigned index) const
+{
+    return index < viewports_.Size() ? viewports_[index] : (Viewport*)0;
+}
+
+void RenderSurface::WasUpdated()
+{
+    updateQueued_ = false;
+}
+
 }

+ 23 - 6
Engine/Graphics/Direct3D9/D3D9RenderSurface.h

@@ -42,12 +42,18 @@ public:
     /// Destruct.
     ~RenderSurface();
     
-    /// Set viewport for auxiliary view rendering.
-    void SetViewport(Viewport* viewport);
+    /// Set number of viewports.
+    void SetNumViewports(unsigned num);
+    /// Set viewport.
+    void SetViewport(unsigned index, Viewport* viewport);
+    /// Set viewport update mode. Default is to update when visible.
+    void SetUpdateMode(RenderSurfaceUpdateMode mode);
     /// Set linked color rendertarget.
     void SetLinkedRenderTarget(RenderSurface* renderTarget);
     /// Set linked depth-stencil surface.
     void SetLinkedDepthStencil(RenderSurface* depthStencil);
+    /// Queue manual update of the viewports.
+    void QueueUpdate();
     /// Release surface.
     void Release();
     
@@ -61,24 +67,35 @@ public:
     int GetHeight() const;
     /// Return usage.
     TextureUsage GetUsage() const;
-    /// Return auxiliary view rendering viewport.
-    Viewport* GetViewport() const { return viewport_; }
+    /// Return number of viewports.
+    unsigned GetNumViewports() const { return viewports_.Size(); }
+    /// Return viewport by index.
+    Viewport* GetViewport(unsigned index) const;
+    /// Return viewport update mode.
+    RenderSurfaceUpdateMode GetUpdateMode() const { return updateMode_; }
     /// Return linked color rendertarget.
     RenderSurface* GetLinkedRenderTarget() const { return linkedRenderTarget_; }
     /// Return linked depth-stencil surface.
     RenderSurface* GetLinkedDepthStencil() const { return linkedDepthStencil_; }
     
+    /// Clear update flag. Called by Renderer.
+    void WasUpdated();
+    
 private:
     /// Parent texture.
     Texture* parentTexture_;
     /// Direct3D surface.
     void* surface_;
-    /// Viewport.
-    SharedPtr<Viewport> viewport_;
+    /// Viewports.
+    Vector<SharedPtr<Viewport> > viewports_;
     /// Linked color buffer.
     WeakPtr<RenderSurface> linkedRenderTarget_;
     /// Linked depth buffer.
     WeakPtr<RenderSurface> linkedDepthStencil_;
+    /// Update mode for viewports.
+    RenderSurfaceUpdateMode updateMode_;
+    /// Update queued flag.
+    bool updateQueued_;
 };
 
 }

+ 12 - 0
Engine/Graphics/Direct3D9/D3D9Texture2D.cpp

@@ -23,6 +23,7 @@
 #include "Precompiled.h"
 #include "Context.h"
 #include "Graphics.h"
+#include "GraphicsEvents.h"
 #include "GraphicsImpl.h"
 #include "Log.h"
 #include "Renderer.h"
@@ -166,6 +167,11 @@ bool Texture2D::SetSize(int width, int height, unsigned format, TextureUsage usa
         pool_ = D3DPOOL_DEFAULT;
     }
     
+    if (usage == TEXTURE_RENDERTARGET)
+        SubscribeToEvent(E_RENDERSURFACEUPDATE, HANDLER(Texture2D, HandleRenderSurfaceUpdate));
+    else
+        UnsubscribeFromEvent(E_RENDERSURFACEUPDATE);
+
     width_ = width;
     height_ = height;
     format_ = format;
@@ -554,4 +560,10 @@ bool Texture2D::Create()
     return true;
 }
 
+void Texture2D::HandleRenderSurfaceUpdate(StringHash eventType, VariantMap& eventData)
+{
+    if (renderSurface_ && renderSurface_->GetUpdateMode() == SURFACE_UPDATEALWAYS)
+        renderSurface_->QueueUpdate();
+}
+
 }

+ 2 - 0
Engine/Graphics/Direct3D9/D3D9Texture2D.h

@@ -68,6 +68,8 @@ public:
 private:
     /// Create texture.
     bool Create();
+    /// Handle render surface update event.
+    void HandleRenderSurfaceUpdate(StringHash eventType, VariantMap& eventData);
     
     /// Render surface.
     SharedPtr<RenderSurface> renderSurface_;

+ 15 - 0
Engine/Graphics/Direct3D9/D3D9TextureCube.cpp

@@ -24,6 +24,7 @@
 #include "Context.h"
 #include "FileSystem.h"
 #include "Graphics.h"
+#include "GraphicsEvents.h"
 #include "GraphicsImpl.h"
 #include "Log.h"
 #include "Profiler.h"
@@ -159,6 +160,11 @@ bool TextureCube::SetSize(int size, unsigned format, TextureUsage usage)
         pool_ = D3DPOOL_DEFAULT;
     }
     
+    if (usage == TEXTURE_RENDERTARGET)
+        SubscribeToEvent(E_RENDERSURFACEUPDATE, HANDLER(TextureCube, HandleRenderSurfaceUpdate));
+    else
+        UnsubscribeFromEvent(E_RENDERSURFACEUPDATE);
+    
     width_ = size;
     height_ = size;
     format_ = format;
@@ -645,4 +651,13 @@ bool TextureCube::Create()
     return true;
 }
 
+void TextureCube::HandleRenderSurfaceUpdate(StringHash eventType, VariantMap& eventData)
+{
+    for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+    {
+        if (renderSurfaces_[i] && renderSurfaces_[i]->GetUpdateMode() == SURFACE_UPDATEALWAYS)
+            renderSurfaces_[i]->QueueUpdate();
+    }
+}
+
 }

+ 2 - 0
Engine/Graphics/Direct3D9/D3D9TextureCube.h

@@ -71,6 +71,8 @@ public:
 private:
     /// Create texture.
     bool Create();
+    /// Handle render surface update event.
+    void HandleRenderSurfaceUpdate(StringHash eventType, VariantMap& eventData);
     
     /// Render surfaces.
     SharedPtr<RenderSurface> renderSurfaces_[MAX_CUBEMAP_FACES];

+ 8 - 0
Engine/Graphics/GraphicsDefs.h

@@ -188,6 +188,14 @@ enum CubeMapFace
     MAX_CUBEMAP_FACES
 };
 
+/// Update mode for render surface viewports.
+enum RenderSurfaceUpdateMode
+{
+    SURFACE_MANUALUPDATE = 0,
+    SURFACE_UPDATEVISIBLE,
+    SURFACE_UPDATEALWAYS
+};
+
 /// Shader types.
 enum ShaderType
 {

+ 5 - 0
Engine/Graphics/GraphicsEvents.h

@@ -51,6 +51,11 @@ EVENT(E_GRAPHICSFEATURES, GraphicsFeatures)
 {
 }
 
+/// Request for queuing autoupdated rendersurfaces
+EVENT(E_RENDERSURFACEUPDATE, RenderSurfaceUpdate)
+{
+}
+
 /// Frame rendering started.
 EVENT(E_BEGINRENDERING, BeginRendering)
 {

+ 57 - 4
Engine/Graphics/OpenGL/OGLRenderSurface.cpp

@@ -25,6 +25,7 @@
 #include "Graphics.h"
 #include "GraphicsImpl.h"
 #include "Log.h"
+#include "Renderer.h"
 #include "RenderSurface.h"
 #include "Scene.h"
 #include "Texture.h"
@@ -45,7 +46,9 @@ namespace Urho3D
 RenderSurface::RenderSurface(Texture* parentTexture, unsigned target) :
     parentTexture_(parentTexture),
     target_(target),
-    renderBuffer_(0)
+    renderBuffer_(0),
+    updateMode_(SURFACE_UPDATEVISIBLE),
+    updateQueued_(false)
 {
 }
 
@@ -54,10 +57,22 @@ RenderSurface::~RenderSurface()
     Release();
 }
 
-void RenderSurface::SetViewport(Viewport* viewport)
+void RenderSurface::SetNumViewports(unsigned num)
 {
-    if (viewport)
-        viewport_ = viewport;
+    viewports_.Resize(num);
+}
+
+void RenderSurface::SetViewport(unsigned index, Viewport* viewport)
+{
+    if (index >= viewports_.Size())
+        viewports_.Resize(index + 1);
+    
+    viewports_[index] = viewport;
+}
+
+void RenderSurface::SetUpdateMode(RenderSurfaceUpdateMode mode)
+{
+    updateMode_ = mode;
 }
 
 void RenderSurface::SetLinkedRenderTarget(RenderSurface* renderTarget)
@@ -72,6 +87,34 @@ void RenderSurface::SetLinkedDepthStencil(RenderSurface* depthStencil)
         linkedDepthStencil_ = depthStencil;
 }
 
+void RenderSurface::QueueUpdate()
+{
+    if (!updateQueued_)
+    {
+        bool hasValidView = false;
+        
+        // Verify that there is at least 1 non-null viewport, as otherwise Renderer will not accept the surface and the update flag
+        // will be left on
+        for (unsigned i = 0; i < viewports_.Size(); ++i)
+        {
+            if (viewports_[i])
+            {
+                hasValidView = true;
+                break;
+            }
+        }
+        
+        if (hasValidView)
+        {
+            Renderer* renderer = parentTexture_->GetSubsystem<Renderer>();
+            if (renderer)
+                renderer->QueueRenderSurface(this);
+            
+            updateQueued_ = true;
+        }
+    }
+}
+
 bool RenderSurface::CreateRenderBuffer(unsigned width, unsigned height, unsigned format)
 {
     Graphics* graphics = parentTexture_->GetGraphics();
@@ -148,4 +191,14 @@ TextureUsage RenderSurface::GetUsage() const
     return parentTexture_->GetUsage();
 }
 
+Viewport* RenderSurface::GetViewport(unsigned index) const
+{
+    return index < viewports_.Size() ? viewports_[index] : (Viewport*)0;
+}
+
+void RenderSurface::WasUpdated()
+{
+    updateQueued_ = false;
+}
+
 }

+ 23 - 6
Engine/Graphics/OpenGL/OGLRenderSurface.h

@@ -44,12 +44,18 @@ public:
     /// Destruct.
     ~RenderSurface();
     
-    /// Set viewport for auxiliary view rendering.
-    void SetViewport(Viewport* viewport);
+    /// Set number of viewports.
+    void SetNumViewports(unsigned num);
+    /// Set viewport.
+    void SetViewport(unsigned index, Viewport* viewport);
+    /// Set viewport update mode. Default is to update when visible.
+    void SetUpdateMode(RenderSurfaceUpdateMode mode);
     /// Set linked color rendertarget.
     void SetLinkedRenderTarget(RenderSurface* renderTarget);
     /// Set linked depth-stencil surface.
     void SetLinkedDepthStencil(RenderSurface* depthStencil);
+    /// Queue manual update of the viewports.
+    void QueueUpdate();
     /// Create a renderbuffer. Return true if successful.
     bool CreateRenderBuffer(unsigned width, unsigned height, unsigned format);
     /// Handle device loss.
@@ -69,13 +75,20 @@ public:
     int GetHeight() const;
     /// Return usage.
     TextureUsage GetUsage() const;
-    /// Return auxiliary view rendering viewport.
-    Viewport* GetViewport() const { return viewport_; }
+    /// Return number of viewports.
+    unsigned GetNumViewports() const { return viewports_.Size(); }
+    /// Return viewport by index.
+    Viewport* GetViewport(unsigned index) const;
+    /// Return viewport update mode.
+    RenderSurfaceUpdateMode GetUpdateMode() const { return updateMode_; }
     /// Return linked color buffer.
     RenderSurface* GetLinkedRenderTarget() const { return linkedRenderTarget_; }
     /// Return linked depth buffer.
     RenderSurface* GetLinkedDepthStencil() const { return linkedDepthStencil_; }
     
+    /// Clear update flag. Called by Renderer.
+    void WasUpdated();
+    
 private:
     /// Parent texture.
     Texture* parentTexture_;
@@ -83,12 +96,16 @@ private:
     unsigned target_;
     /// OpenGL renderbuffer.
     unsigned renderBuffer_;
-    /// Viewport.
-    SharedPtr<Viewport> viewport_;
+    /// Viewports.
+    Vector<SharedPtr<Viewport> > viewports_;
     /// Linked color buffer.
     WeakPtr<RenderSurface> linkedRenderTarget_;
     /// Linked depth buffer.
     WeakPtr<RenderSurface> linkedDepthStencil_;
+    /// Update mode for viewports.
+    RenderSurfaceUpdateMode updateMode_;
+    /// Update queued flag.
+    bool updateQueued_;
 };
 
 }

+ 15 - 3
Engine/Graphics/OpenGL/OGLTexture2D.cpp

@@ -23,6 +23,7 @@
 #include "Precompiled.h"
 #include "Context.h"
 #include "Graphics.h"
+#include "GraphicsEvents.h"
 #include "GraphicsImpl.h"
 #include "Log.h"
 #include "Profiler.h"
@@ -145,12 +146,12 @@ bool Texture2D::SetSize(int width, int height, unsigned format, TextureUsage usa
 {
     // Delete the old rendersurface if any
     renderSurface_.Reset();
-    
+
     if (usage >= TEXTURE_RENDERTARGET)
     {
         renderSurface_ = new RenderSurface(this, GL_TEXTURE_2D);
         dynamic_ = true;
-        
+
         // Clamp mode addressing by default, nearest filtering, and mipmaps disabled
         addressMode_[COORD_U] = ADDRESS_CLAMP;
         addressMode_[COORD_V] = ADDRESS_CLAMP;
@@ -159,7 +160,12 @@ bool Texture2D::SetSize(int width, int height, unsigned format, TextureUsage usa
     }
     else
         dynamic_ = usage == TEXTURE_DYNAMIC;
-    
+
+    if (usage == TEXTURE_RENDERTARGET)
+        SubscribeToEvent(E_RENDERSURFACEUPDATE, HANDLER(Texture2D, HandleRenderSurfaceUpdate));
+    else
+        UnsubscribeFromEvent(E_RENDERSURFACEUPDATE);
+
     width_ = width;
     height_ = height;
     format_ = format;
@@ -467,4 +473,10 @@ bool Texture2D::Create()
     return success;
 }
 
+void Texture2D::HandleRenderSurfaceUpdate(StringHash eventType, VariantMap& eventData)
+{
+    if (renderSurface_ && renderSurface_->GetUpdateMode() == SURFACE_UPDATEALWAYS)
+        renderSurface_->QueueUpdate();
+}
+
 }

+ 2 - 0
Engine/Graphics/OpenGL/OGLTexture2D.h

@@ -67,6 +67,8 @@ public:
 private:
     /// Create texture.
     bool Create();
+    /// Handle render surface update event.
+    void HandleRenderSurfaceUpdate(StringHash eventType, VariantMap& eventData);
     
     /// Render surface.
     SharedPtr<RenderSurface> renderSurface_;

+ 17 - 2
Engine/Graphics/OpenGL/OGLTextureCube.cpp

@@ -24,6 +24,7 @@
 #include "Context.h"
 #include "FileSystem.h"
 #include "Graphics.h"
+#include "GraphicsEvents.h"
 #include "GraphicsImpl.h"
 #include "Log.h"
 #include "Profiler.h"
@@ -155,8 +156,13 @@ bool TextureCube::SetSize(int size, unsigned format, TextureUsage usage)
         dynamic_ = true;
     }
     else
-        dynamic_ = usage == TEXTURE_DYNAMIC;
-    
+         dynamic_ = usage == TEXTURE_DYNAMIC;
+
+    if (usage == TEXTURE_RENDERTARGET)
+        SubscribeToEvent(E_RENDERSURFACEUPDATE, HANDLER(TextureCube, HandleRenderSurfaceUpdate));
+    else
+        UnsubscribeFromEvent(E_RENDERSURFACEUPDATE);
+
     width_ = size;
     height_ = size;
     format_ = format;
@@ -564,4 +570,13 @@ bool TextureCube::Create()
     return success;
 }
 
+void TextureCube::HandleRenderSurfaceUpdate(StringHash eventType, VariantMap& eventData)
+{
+    for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+    {
+        if (renderSurfaces_[i] && renderSurfaces_[i]->GetUpdateMode() == SURFACE_UPDATEALWAYS)
+            renderSurfaces_[i]->QueueUpdate();
+    }
+}
+
 }

+ 2 - 0
Engine/Graphics/OpenGL/OGLTextureCube.h

@@ -71,6 +71,8 @@ public:
 private:
     /// Create texture.
     bool Create();
+    /// Handle render surface update event.
+    void HandleRenderSurfaceUpdate(StringHash eventType, VariantMap& eventData);
     
     /// Render surfaces.
     SharedPtr<RenderSurface> renderSurfaces_[MAX_CUBEMAP_FACES];

+ 54 - 50
Engine/Graphics/Renderer.cpp

@@ -292,7 +292,6 @@ Renderer::Renderer(Context* context) :
     
     // Try to initialize right now, but skip if screen mode is not yet set
     Initialize();
-    SetNumViewports(1);
 }
 
 Renderer::~Renderer()
@@ -302,30 +301,14 @@ Renderer::~Renderer()
 void Renderer::SetNumViewports(unsigned num)
 {
     viewports_.Resize(num);
-    
-    for (unsigned i = 0; i < viewports_.Size(); ++i)
-    {
-        if (!viewports_[i])
-            viewports_[i] = new Viewport(context_);
-    }
 }
 
-bool Renderer::SetViewport(unsigned index, Viewport* viewport)
+void Renderer::SetViewport(unsigned index, Viewport* viewport)
 {
     if (index >= viewports_.Size())
-    {
-        LOGERROR("Viewport index out of bounds");
-        return false;
-    }
-    
-    if (!viewport)
-    {
-        LOGERROR("Null viewport");
-        return false;
-    }
+        viewports_.Resize(index + 1);
     
     viewports_[index] = viewport;
-    return true;
 }
 
 void Renderer::SetDefaultRenderPath(RenderPath* renderPath)
@@ -584,21 +567,42 @@ void Renderer::Update(float timeStep)
     if (shadersDirty_)
         LoadShaders();
     
-    // Process all viewports. Use reverse order, because during rendering the order will be reversed again
-    // to handle auxiliary view dependencies correctly
+    // Queue update of the main viewports. Use reverse order, as rendering order is also reverse
+    // to render auxiliary views before dependant main views
     for (unsigned i = viewports_.Size() - 1; i < viewports_.Size(); --i)
+        QueueViewport(0, viewports_[i]);
+    
+    // Gather other render surfaces that are autoupdated
+    SendEvent(E_RENDERSURFACEUPDATE);
+    
+    // Process gathered views. This may queue further views (render surfaces that are only updated when visible)
+    for (unsigned i = 0; i < queuedViews_.Size(); ++i)
     {
-        unsigned mainView = numViews_;
-        Viewport* viewport = viewports_[i];
-        if (!viewport || !AddView(0, viewport))
+        WeakPtr<RenderSurface>& renderTarget = queuedViews_[i].first_;
+        WeakPtr<Viewport>& viewport = queuedViews_[i].second_;
+        
+        // Null pointer means backbuffer view. Differentiate between that and an expired rendersurface
+        if ((renderTarget.NotNull() && renderTarget.Expired()) || viewport.Expired())
+            continue;
+        
+        // Allocate new view if necessary
+        if (numViews_ == views_.Size())
+            views_.Push(SharedPtr<View>(new View(context_)));
+        
+        // Check if view can be defined successfully (has valid scene, camera and octree)
+        assert(numViews_ < views_.Size());
+        View* view = views_[numViews_];
+        if (!view->Define(renderTarget, viewport))
             continue;
         
+        ++numViews_;
+        
         const IntRect& viewRect = viewport->GetRect();
         Scene* scene = viewport->GetScene();
+        Octree* octree = scene->GetComponent<Octree>();
         
         // Update octree (perform early update for drawables which need that, and reinsert moved drawables.)
         // However, if the same scene is viewed from multiple cameras, update the octree only once
-        Octree* octree = scene->GetComponent<Octree>();
         if (!updatedOctrees_.Contains(octree))
         {
             frame_.camera_ = viewport->GetCamera();
@@ -608,21 +612,26 @@ void Renderer::Update(float timeStep)
             octree->Update(frame_);
             updatedOctrees_.Insert(octree);
             
-            // Set also the view for the debug graphics already here, so that it can use culling
+            // Set also the view for the debug renderer already here, so that it can use culling
             /// \todo May result in incorrect debug geometry culling if the same scene is drawn from multiple viewports
             DebugRenderer* debug = scene->GetComponent<DebugRenderer>();
             if (debug)
                 debug->SetView(viewport->GetCamera());
         }
         
-        // Update the viewport's main view and any auxiliary views it has created
-        for (unsigned i = mainView; i < numViews_; ++i)
-        {
-            // Reset shadow map allocations; they can be reused between views as each is rendered completely at a time
-            ResetShadowMapAllocations();
-            views_[i]->Update(frame_);
-        }
+        // Update view. This may queue further views. Reset shadow map allocations, as they can be reused between views.
+        view->Update(frame_);
+    }
+    
+    // Reset update flag from queued render surfaces. At this point no new views can be added on this frame.
+    for (unsigned i = 0; i < queuedViews_.Size(); ++i)
+    {
+        WeakPtr<RenderSurface>& renderTarget = queuedViews_[i].first_;
+        if (renderTarget)
+            renderTarget->WasUpdated();
     }
+    
+    queuedViews_.Clear();
 }
 
 void Renderer::Render()
@@ -716,29 +725,24 @@ void Renderer::DrawDebugGeometry(bool depthTest)
     }
 }
 
-bool Renderer::AddView(RenderSurface* renderTarget, Viewport* viewport)
+void Renderer::QueueRenderSurface(RenderSurface* renderTarget)
 {
-    // If using a rendertarget texture, make sure it will not be rendered to multiple times
     if (renderTarget)
     {
-        for (unsigned i = 0; i < numViews_; ++i)
-        {
-            if (views_[i]->GetRenderTarget() == renderTarget)
-                return false;
-        }
+        unsigned numViewports = renderTarget->GetNumViewports();
+        
+        for (unsigned i = 0; i < numViewports; ++i)
+            QueueViewport(renderTarget, renderTarget->GetViewport(i));
     }
-    
-    assert(numViews_ <= views_.Size());
-    if (numViews_ == views_.Size())
-        views_.Push(SharedPtr<View>(new View(context_)));
-    
-    if (views_[numViews_]->Define(renderTarget, viewport))
+}
+
+void Renderer::QueueViewport(RenderSurface* renderTarget, Viewport* viewport)
+{
+    if (viewport)
     {
-        ++numViews_;
-        return true;
+        queuedViews_.Push(Pair<WeakPtr<RenderSurface>, WeakPtr<Viewport> >(WeakPtr<RenderSurface>(renderTarget),
+            WeakPtr<Viewport>(viewport)));
     }
-    else
-        return false;
 }
 
 void Renderer::GetLightVolumeShaders(PODVector<ShaderVariation*>& lightVS, PODVector<ShaderVariation*>& lightPS, const String& vsName, const String& psName)

+ 12 - 7
Engine/Graphics/Renderer.h

@@ -163,10 +163,10 @@ public:
     /// Destruct.
     virtual ~Renderer();
     
-    /// Set number of viewports to render.
+    /// Set number of backbuffer viewports to render.
     void SetNumViewports(unsigned num);
-    /// Set a viewport. Return true if successful.
-    bool SetViewport(unsigned index, Viewport* viewport);
+    /// Set a backbuffer viewport.
+    void SetViewport(unsigned index, Viewport* viewport);
     /// Set default renderpath.
     void SetDefaultRenderPath(RenderPath* renderPath);
     /// Set default renderpath from an XML file.
@@ -208,9 +208,9 @@ public:
     /// Force reload of shaders.
     void ReloadShaders();
     
-    /// Return number of viewports.
+    /// Return number of backbuffer viewports.
     unsigned GetNumViewports() const { return viewports_.Size(); }
-    /// Return viewport.
+    /// Return backbuffer viewport by index.
     Viewport* GetViewport(unsigned index) const;
     /// Return default renderpath.
     RenderPath* GetDefaultRenderPath() const;
@@ -295,8 +295,11 @@ public:
     void Render();
     /// Add debug geometry to the debug renderer.
     void DrawDebugGeometry(bool depthTest);
-    /// Add a view. Return true if successful.
-    bool AddView(RenderSurface* renderTarget, Viewport* viewport);
+    /// Queue a render surface's viewports for rendering. Called by the surface, or by View.
+    void QueueRenderSurface(RenderSurface* renderTarget);
+    /// Queue a viewport for rendering. Null surface means backbuffer.
+    void QueueViewport(RenderSurface* renderTarget, Viewport* viewport);
+    
     /// Populate light volume shaders.
     void GetLightVolumeShaders(PODVector<ShaderVariation*>& lightVS, PODVector<ShaderVariation*>& lightPS, const String& vsName, const String& psName);
     /// Return volume geometry for a light.
@@ -422,6 +425,8 @@ private:
     HashMap<Pair<Light*, Camera*>, Rect> lightScissorCache_;
     /// Viewports.
     Vector<SharedPtr<Viewport> > viewports_;
+    /// Queued views.
+    Vector<Pair<WeakPtr<RenderSurface>, WeakPtr<Viewport> > > queuedViews_;
     /// Views.
     Vector<SharedPtr<View> > views_;
     /// Octrees that have been updated during the frame.

+ 8 - 16
Engine/Graphics/View.cpp

@@ -905,7 +905,7 @@ void View::GetBatches()
                 const SourceBatch& srcBatch = batches[j];
                 
                 // Check here if the material refers to a rendertarget texture with camera(s) attached
-                // Only check this for the main view (null rendertarget)
+                // Only check this for backbuffer views (null rendertarget)
                 if (srcBatch.material_ && srcBatch.material_->GetAuxViewFrameNumber() != frame_.frameNumber_ && !renderTarget_)
                     CheckMaterialForAuxView(srcBatch.material_);
                 
@@ -2295,20 +2295,16 @@ void View::CheckMaterialForAuxView(Material* material)
     
     for (unsigned i = 0; i < MAX_MATERIAL_TEXTURE_UNITS; ++i)
     {
-        // Have to check cube & 2D textures separately
         Texture* texture = textures[i];
-        if (texture)
+        if (texture && texture->GetUsage() == TEXTURE_RENDERTARGET)
         {
+            // Have to check cube & 2D textures separately
             if (texture->GetType() == Texture2D::GetTypeStatic())
             {
                 Texture2D* tex2D = static_cast<Texture2D*>(texture);
                 RenderSurface* target = tex2D->GetRenderSurface();
-                if (target)
-                {
-                    Viewport* viewport = target->GetViewport();
-                    if (viewport && viewport->GetScene() && viewport->GetCamera())
-                        renderer_->AddView(target, viewport);
-                }
+                if (target && target->GetUpdateMode() == SURFACE_UPDATEVISIBLE)
+                    target->QueueUpdate();
             }
             else if (texture->GetType() == TextureCube::GetTypeStatic())
             {
@@ -2316,18 +2312,14 @@ void View::CheckMaterialForAuxView(Material* material)
                 for (unsigned j = 0; j < MAX_CUBEMAP_FACES; ++j)
                 {
                     RenderSurface* target = texCube->GetRenderSurface((CubeMapFace)j);
-                    if (target)
-                    {
-                        Viewport* viewport = target->GetViewport();
-                        if (viewport && viewport->GetScene() && viewport->GetCamera())
-                            renderer_->AddView(target, viewport);
-                    }
+                    if (target && target->GetUpdateMode() == SURFACE_UPDATEVISIBLE)
+                        target->QueueUpdate();
                 }
             }
         }
     }
     
-    // Set frame number so that we can early-out next time we come across this material on the same frame
+    // Flag as processed so we can early-out next time we come across this material on the same frame
     material->MarkForAuxView(frame_.frameNumber_);
 }