瀏覽代碼

Better overlay rendering
Made sure each render target only gets cleared/presented once per frame

Marko Pintera 12 年之前
父節點
當前提交
8928baaf44

+ 1 - 1
BansheeEngine/Include/BsCamera.h

@@ -516,7 +516,7 @@ namespace BansheeEngine {
         */
         virtual ~Camera();
 
-		void init(CM::RenderTargetPtr target = nullptr,
+		void initialize(CM::RenderTargetPtr target = nullptr,
 			float left = 0.0f, float top = 0.0f,
 			float width = 1.0f, float height = 1.0f,
 			int ZOrder = 0);

+ 0 - 9
BansheeEngine/Include/BsGUIManager.h

@@ -17,17 +17,8 @@ namespace BansheeEngine
 		void registerWidget(GUIWidget* widget);
 		void unregisterWidget(GUIWidget* widget);
 
-		/**
-		 * @brief	Attaches a widget to a window. One widget can be attached to multiple input. Calling this
-		 * 			guarantees that the widget will receieve any input received by the window.
-		 */
-		void attachWidgetToWindow(const CM::RenderWindow* window, GUIWidget* widget);
-
-		void detachWidgetFromWindow(const CM::RenderWindow* window, GUIWidget* widget);
-
 		void update();
 	private:
 		std::vector<GUIWidget*> mWidgets;
-		std::unordered_map<const CM::RenderWindow*, std::vector<GUIWidget*>> mWindowWidgetMap;
 	};
 }

+ 13 - 1
BansheeEngine/Include/BsGUIWidget.h

@@ -13,13 +13,24 @@ namespace BansheeEngine
 	public:
 		virtual ~GUIWidget();
 
+		/**
+		 * @brief	Initializes the GUIWidget. Must be called in order for GUIWidget to start rendering.
+		 *
+		 * @param 	target			Target onto which we want to render the widget.
+		 * @param	ownerWindow   	Window that contains the widget. This will be the source of all input
+		 * 							for the widget. "target" and "ownerWindow" may be the same object.
+		 */
+		void initialize(CM::Viewport* target, const CM::RenderWindow* ownerWindow);
+
 		void setSkin(const GUISkin* skin);
 		const GUISkin* getGUISkin() const;
 
 		bool inBounds(const CM::Int2& position) const;
 		bool mouseEvent(const GUIMouseEvent& ev);
 
-		virtual void render(const Camera* camera, CM::RenderContext& renderContext) const;
+		virtual void render(CM::RenderContext& renderContext) const;
+
+		const CM::RenderWindow* getOwnerWindow() const { return mOwnerWindow; }
 	protected:
 		friend class CM::SceneObject;
 		friend class GUIElement;
@@ -32,6 +43,7 @@ namespace BansheeEngine
 		void updateMeshes() const;
 		void updateBounds() const;
 
+		const CM::RenderWindow* mOwnerWindow;
 		std::vector<GUIElement*> mElements;
 		
 		mutable CM::ORect mBounds;

+ 12 - 1
BansheeEngine/Include/BsOverlay.h

@@ -17,12 +17,23 @@ namespace BansheeEngine
 	public:
 		virtual ~Overlay();
 
-		virtual void render(const Camera* camera, CM::RenderContext& renderContext) const = 0;
+		/**
+		 * @brief	Initializes the GUIWidget. Must be called in order for GUIWidget to start rendering.
+		 *
+		 * @param 	target			Target onto which we want to render the widget.
+		 */
+		void initialize(CM::Viewport* target);
+
+		virtual void render(CM::RenderContext& renderContext) const = 0;
 		virtual void update() {}
 
+		CM::Viewport* getTarget() const { return mRenderTarget; }
+
 	protected:
 		friend class CM::SceneObject;
 
+		CM::Viewport* mRenderTarget;
+
 		Overlay(const CM::HSceneObject& parent);
 	};
 }

+ 10 - 7
BansheeEngine/Include/BsOverlayManager.h

@@ -7,9 +7,9 @@
 namespace BansheeEngine
 {
 	/**
-	 * @brief	Takes care which overlay gets rendered on which camera.
+	 * @brief	Takes care which overlay gets rendered on which render target.
 	 * 			
-	 * @note	Overlays could have been stored directly on a Camera but this class
+	 * @note	Overlays could have been stored directly on a RenderTarget but this class
 	 * 			was created to decouple the connection.
 	 * 
 	 * @see Overlay
@@ -17,12 +17,15 @@ namespace BansheeEngine
 	class BS_EXPORT OverlayManager : public CM::Module<OverlayManager>
 	{
 	public:
-		void render(const Camera* camera, CM::RenderContext& renderContext) const;
+		void render(CM::ViewportPtr& target, CM::RenderContext& renderContext) const;
 
-		void attachOverlay(const Camera* camera, const Overlay* overlay);
-		void detachOverlay(const Camera* camera, const Overlay* overlay);
-		void detachOverlayFromAll(const Overlay* overlay);
 	private:
-		std::unordered_map<const Camera*, std::unordered_set<const Overlay*>> mOverlaysPerCamera;
+		friend class Overlay;
+
+		void attachOverlay(const CM::Viewport* target, const Overlay* overlay);
+		void detachOverlay(const CM::Viewport* target, const Overlay* overlay);
+		void detachOverlayFromAll(const Overlay* overlay);
+
+		std::unordered_map<const CM::Viewport*, std::unordered_set<const Overlay*>> mOverlaysPerTarget;
 	};
 }

+ 1 - 1
BansheeEngine/Source/BsCamera.cpp

@@ -89,7 +89,7 @@ namespace BansheeEngine
     Camera::~Camera()
     {
     }
-	void Camera::init(RenderTargetPtr target, float left, float top, float width, float height, int ZOrder)
+	void Camera::initialize(RenderTargetPtr target, float left, float top, float width, float height, int ZOrder)
 	{
 		target->waitUntilInitialized();
 

+ 7 - 60
BansheeEngine/Source/BsGUIManager.cpp

@@ -35,61 +35,6 @@ namespace BansheeEngine
 
 		if(findIter != end(mWidgets))
 			mWidgets.erase(findIter);
-
-		for(auto& windowMap : mWindowWidgetMap)
-		{
-			auto& widgets = windowMap.second;
-			auto iterFind = std::find(begin(widgets), end(widgets), widget);
-
-			if(iterFind != end(widgets))
-				widgets.erase(iterFind);
-		}
-	}
-
-	void GUIManager::attachWidgetToWindow(const RenderWindow* window, GUIWidget* widget)
-	{
-		auto findIter = mWindowWidgetMap.find(window);
-
-		if(findIter == mWindowWidgetMap.end())
-		{
-			mWindowWidgetMap.insert(std::make_pair(window, std::vector<GUIWidget*>()));
-			findIter = mWindowWidgetMap.find(window);
-		}
-
-		std::vector<GUIWidget*>& widgets = findIter->second;
-
-		bool found = false;
-		for(auto& existingWidget : widgets)
-		{
-			if(existingWidget == widget)
-			{
-				found = true;
-				break;
-			}
-		}
-
-		if(!found)
-			widgets.push_back(widget);
-	}
-
-	void GUIManager::detachWidgetFromWindow(const CM::RenderWindow* window, GUIWidget* widget)
-	{
-		auto findIter = mWindowWidgetMap.find(window);
-
-		if(findIter == mWindowWidgetMap.end())
-		{
-			CM_EXCEPT(InternalErrorException, "Cannot find window to detach the widget from.");
-		}
-
-		std::vector<GUIWidget*>& widgets = findIter->second;
-		auto findIter2 = std::find(begin(widgets), end(widgets), widget);
-
-		if(findIter2 == widgets.end())
-		{
-			CM_EXCEPT(InternalErrorException, "Cannot find widget attached to the specified window.");
-		}
-
-		widgets.erase(findIter2);
 	}
 
 	void GUIManager::update()
@@ -97,9 +42,9 @@ namespace BansheeEngine
 #if CM_DEBUG_MODE
 		// Checks if all referenced windows actually exist
 		std::vector<RenderWindow*> activeWindows = RenderWindowManager::instance().getRenderWindows();
-		for(auto& window : mWindowWidgetMap)
+		for(auto& widget : mWidgets)
 		{
-			auto iterFind = std::find(begin(activeWindows), end(activeWindows), window.first);
+			auto iterFind = std::find(begin(activeWindows), end(activeWindows), widget->getOwnerWindow());
 
 			if(iterFind == activeWindows.end())
 			{
@@ -109,12 +54,14 @@ namespace BansheeEngine
 		}
 #endif
 
-		for(auto& window : mWindowWidgetMap)
+		for(auto& widget : mWidgets)
 		{
-			if(!window.first->getHasFocus())
+			const RenderWindow* window = widget->getOwnerWindow();
+
+			if(!window->getHasFocus())
 				continue;
 
-			Int2 screenPos = Cursor::getWindowPosition(*window.first);
+			Int2 screenPos = Cursor::getWindowPosition(*window);
 			GUIMouseEvent mouseEvent(screenPos);
 
 			for(auto& widget : mWidgets)

+ 12 - 4
BansheeEngine/Source/BsGUIWidget.cpp

@@ -9,6 +9,7 @@
 #include "CmPass.h"
 #include "CmMesh.h"
 #include "CmInt2.h"
+#include "BsOverlayManager.h"
 #include "BsCamera.h"
 #include "CmViewport.h"
 #include "CmSceneObject.h"
@@ -20,7 +21,7 @@ namespace BansheeEngine
 	GUISkin GUIWidget::DefaultSkin;
 
 	GUIWidget::GUIWidget(const HSceneObject& parent)
-		:Overlay(parent), mSkin(nullptr)
+		:Overlay(parent), mSkin(nullptr), mOwnerWindow(nullptr)
 	{
 		GUIManager::instance().registerWidget(this);
 	}
@@ -37,6 +38,13 @@ namespace BansheeEngine
 		mElements.clear();
 	}
 
+	void GUIWidget::initialize(Viewport* target, const RenderWindow* ownerWindow)
+	{
+		Overlay::initialize(target);
+
+		mOwnerWindow = ownerWindow;
+	}
+
 	void GUIWidget::registerElement(GUIElement* elem)
 	{
 		mElements.push_back(elem);
@@ -224,7 +232,7 @@ namespace BansheeEngine
 		mBounds.applyTransform(worldTfrm);
 	}
 
-	void GUIWidget::render(const Camera* camera, RenderContext& renderContext) const
+	void GUIWidget::render(RenderContext& renderContext) const
 	{
 		// Mesh is re-created every frame. There might be a better approach that only recreates it upon change,
 		// but for now it seems like too much hassle for something like GUI that is pretty dynamic anyway.
@@ -238,8 +246,8 @@ namespace BansheeEngine
 
 			// TODO - Possible optimization. I currently divide by width/height inside the shader, while it
 			// might be more optimal to just scale the mesh as the resolution changes?
-			float invViewportWidth = 1.0f / (camera->getViewport()->getWidth() * 0.5f);
-			float invViewportHeight = 1.0f / (camera->getViewport()->getHeight() * 0.5f);
+			float invViewportWidth = 1.0f / (getTarget()->getWidth() * 0.5f);
+			float invViewportHeight = 1.0f / (getTarget()->getHeight() * 0.5f);
 
 			material->setFloat("invViewportWidth", invViewportWidth);
 			material->setFloat("invViewportHeight", invViewportHeight);

+ 11 - 1
BansheeEngine/Source/BsOverlay.cpp

@@ -6,7 +6,7 @@ using namespace CamelotFramework;
 namespace BansheeEngine
 {
 	Overlay::Overlay(const HSceneObject& parent)
-		:Component(parent)
+		:Component(parent), mRenderTarget(nullptr)
 	{
 
 	}
@@ -15,4 +15,14 @@ namespace BansheeEngine
 	{
 		OverlayManager::instance().detachOverlayFromAll(this);
 	}
+
+	void Overlay::initialize(CM::Viewport* target)
+	{
+		if(mRenderTarget != nullptr)
+			OverlayManager::instance().detachOverlay(mRenderTarget, this);
+
+		mRenderTarget = target;
+
+		OverlayManager::instance().attachOverlay(mRenderTarget, this);	
+	}
 }

+ 11 - 9
BansheeEngine/Source/BsOverlayManager.cpp

@@ -6,32 +6,34 @@ using namespace CamelotFramework;
 
 namespace BansheeEngine
 {
-	void OverlayManager::render(const Camera* camera, RenderContext& renderContext) const
+	void OverlayManager::render(CM::ViewportPtr& target, RenderContext& renderContext) const
 	{
-		auto overlays = mOverlaysPerCamera.find(camera);
+		auto overlays = mOverlaysPerTarget.find(target.get());
 
-		if(overlays == mOverlaysPerCamera.end())
+		if(overlays == mOverlaysPerTarget.end())
 			return;
 
+		renderContext.setViewport(target);
+
 		for(auto& overlay : overlays->second)
 		{
-			overlay->render(camera, renderContext);
+			overlay->render(renderContext);
 		}
 	}
 
-	void OverlayManager::attachOverlay(const Camera* camera, const Overlay* overlay)
+	void OverlayManager::attachOverlay(const CM::Viewport* target, const Overlay* overlay)
 	{
-		mOverlaysPerCamera[camera].insert(overlay);
+		mOverlaysPerTarget[target].insert(overlay);
 	}
 
-	void OverlayManager::detachOverlay(const Camera* camera, const Overlay* overlay)
+	void OverlayManager::detachOverlay(const CM::Viewport* target, const Overlay* overlay)
 	{
-		mOverlaysPerCamera[camera].erase(overlay);
+		mOverlaysPerTarget[target].erase(overlay);
 	}
 
 	void OverlayManager::detachOverlayFromAll(const Overlay* overlay)
 	{
-		for(auto& overlays : mOverlaysPerCamera)
+		for(auto& overlays : mOverlaysPerTarget)
 		{
 			overlays.second.erase(overlay);
 		}

+ 28 - 19
BansheeForwardRenderer/Source/BsForwardRenderer.cpp

@@ -33,22 +33,40 @@ namespace BansheeEngine
 	void ForwardRenderer::renderAll() 
 	{
 		RenderContext& renderContext = gMainRC();
-
 		const vector<HCamera>::type& allCameras = gSceneManager().getAllCameras();
-		for(auto iter = allCameras.begin(); iter != allCameras.end(); ++iter)
+
+		// Find all unique render targets
+		std::unordered_set<RenderTargetPtr> renderTargets;
+		for(auto& camera : allCameras)
 		{
-			render(*iter);
+			RenderTargetPtr target = camera->getViewport()->getTarget();
+			auto findIter = renderTargets.find(target);
 
-			ViewportPtr vp = (*iter)->getViewport();
-			if(vp != nullptr)
+			if(findIter == renderTargets.end())
 			{
-				RenderTargetPtr rt = vp->getTarget();
-
-				if(rt != nullptr)
-					renderContext.swapBuffers(rt); // TODO - This is wrong as potentially multiple viewports can share a single render target, and swap shouldn't
-				// be done for every one of them
+				renderTargets.insert(target);
 			}
 		}
+
+		// Clear all targets
+		for(auto& target : renderTargets)
+			renderContext.clear(target, FBT_COLOR | FBT_DEPTH, Color::Blue);
+
+		renderContext.beginFrame();
+
+		// Render all cameras
+		for(auto& camera : allCameras)
+			render(camera);
+
+		// Render overlays for all targets
+		for(auto& camera : allCameras)
+			OverlayManager::instance().render(camera->getViewport(), renderContext);
+
+		renderContext.endFrame();
+
+		// Swap all targets
+		for(auto& target : renderTargets)
+			renderContext.swapBuffers(target);
 	}
 
 	void ForwardRenderer::render(const HCamera& camera) 
@@ -66,10 +84,6 @@ namespace BansheeEngine
 
 		Matrix4 viewProjMatrix = projMatrixCstm * viewMatrixCstm;
 
-		renderContext.clear(camera->getViewport()->getTarget(), FBT_COLOR | FBT_DEPTH, Color::Blue);
-
-		renderContext.beginFrame();
-
 		// TODO - sort renderables by material/pass/parameters to minimize state changes
 		for(auto iter = allRenderables.begin(); iter != allRenderables.end(); ++iter)
 		{
@@ -100,11 +114,6 @@ namespace BansheeEngine
 			}
 		}
 
-		// Render overlays for this camera
-		OverlayManager::instance().render(camera.get(), renderContext);
-
-		renderContext.endFrame();
-
 		// TODO - Sort renderables
 		// Render them
 	}

+ 2 - 1
CamelotClient/CamelotClient.cpp

@@ -69,7 +69,7 @@ int CALLBACK WinMain(
 	HSceneObject cameraGO = SceneObject::create("MainCamera");
 	HCamera camera = cameraGO->addComponent<Camera>();
 
-	camera->init(renderWindow, 0.0f, 0.0f, 1.0f, 1.0f, 0);
+	camera->initialize(renderWindow, 0.0f, 0.0f, 1.0f, 1.0f, 0);
 	cameraGO->setPosition(Vector3(0,50,1240));
 	cameraGO->lookAt(Vector3(0,50,-300));
 	camera->setNearClipDistance(5);
@@ -82,6 +82,7 @@ int CALLBACK WinMain(
 
 	HSceneObject testTextGO = SceneObject::create("TestText");
 	GameObjectHandle<TestTextSprite> textSprite = testTextGO->addComponent<TestTextSprite>();
+	textSprite->initialize(camera->getViewport().get(), renderWindow.get());
 
 	textSprite->init(camera, "Testing in a new row, does this work?");
 

+ 4 - 17
CamelotClient/CmEditorWindow.cpp

@@ -30,39 +30,26 @@ namespace BansheeEditor
 		mRenderWindow = RenderWindow::create(renderWindowDesc, gApplication().getPrimaryWindow());
 
 		HSceneObject so = SceneObject::create("EditorWindow-" + name);
-		mGUI = so->addComponent<GUIWidget>();
-		GUIManager::instance().attachWidgetToWindow(mRenderWindow.get(), mGUI.get());
 
 		GameObjectHandle<UpdateCallback> updateCallback = so->addComponent<UpdateCallback>();
-
 		updateCallback->onUpdate.connect(boost::bind(&EditorWindow::update, this));
 
 		HCamera camera = so->addComponent<Camera>();
-		camera->init(mRenderWindow, 0.0f, 0.0f, 1.0f, 1.0f, 0);
+		camera->initialize(mRenderWindow, 0.0f, 0.0f, 1.0f, 1.0f, 0);
 		camera->setNearClipDistance(5);
 		camera->setAspectRatio(1.0f);
 		camera->setIgnoreSceneRenderables(true);
-		
-		//// DEBUG ONLY - Skin should exist externally
-		//mSkin = CM_NEW(GUISkin, GUIAlloc) GUISkin();
 
-		OverlayManager::instance().attachOverlay(camera.get(), mGUI.get());		
-
-		//GUIElementStyle labelStyle;
-		//labelStyle.font = dbgFont;
-		//labelStyle.fontSize = dbgFontSize;
-
-		//mSkin->setStyle(GUILabel::getGUITypeName(), labelStyle);
+		mGUI = so->addComponent<GUIWidget>();
+		mGUI->initialize(camera->getViewport().get(), mRenderWindow.get());
 
-		//gui->setSkin(mSkin);
-		//// END DEBUG
+		//// DEBUG
 		mGUI->setSkin(&EngineGUI::instance().getSkin());
 		mDbgLabel = GUILabel::create(mGUI.get(), "Testing test", renderWindowDesc.width);
 	}
 
 	EditorWindow::~EditorWindow()
 	{
-		GUIManager::instance().detachWidgetFromWindow(mRenderWindow.get(), mGUI.get());
 		mRenderWindow->destroy();
 	}
 

+ 0 - 2
CamelotClient/CmTestTextSprite.cpp

@@ -28,8 +28,6 @@ namespace CamelotFramework
 
 	void TestTextSprite::init(const HCamera& camera, const String& text)
 	{
-		OverlayManager::instance().attachOverlay(camera.get(), this);		
-
 		setSkin(&EngineGUI::instance().getSkin());
 
 		GUILabel::create(this, text, 400, 400, true, THA_Right, TVA_Bottom);