Quellcode durchsuchen

More (untested) deferred rendering

Marko Pintera vor 13 Jahren
Ursprung
Commit
ca8447b5e1

+ 5 - 3
CamelotD3D9Renderer/Include/CmD3D9RenderSystem.h

@@ -182,8 +182,11 @@ namespace CamelotEngine
 
 		// Overridden RenderSystem functions
 		String validateConfigOptions();
-		RenderWindow* _initialise( bool autoCreateWindow, const String& windowTitle = "OGRE Render Window"  );
-		/// @copydoc RenderSystem::_createRenderWindow
+		/**
+		 * @copydoc RenderSystem::startUp
+		 */
+		RenderWindow* startUp(bool runOnSeparateThread, bool autoCreateWindow, const String& windowTitle = "Camelot Render Window");
+		/// @copydoc RenderSystem::createRenderWindow
 		RenderWindow* createRenderWindow(const String &name, unsigned int width, unsigned int height, 
 			bool fullScreen, const NameValuePairList *miscParams = 0);
 		
@@ -203,7 +206,6 @@ namespace CamelotEngine
 		const String& getName() const;
 		// Low-level overridden members
 		void setConfigOption( const String &name, const String &value );
-		void reinitialise();
 		void shutdown();
 		void destroyRenderTarget(const String& name);
 		VertexElementType getColorVertexElementType() const;

+ 2 - 9
CamelotD3D9Renderer/Source/CmD3D9RenderSystem.cpp

@@ -483,7 +483,7 @@ namespace CamelotEngine
 		return StringUtil::BLANK;
 	}
 	//---------------------------------------------------------------------
-	RenderWindow* D3D9RenderSystem::_initialise( bool autoCreateWindow, const String& windowTitle )
+	RenderWindow* D3D9RenderSystem::startUp(bool runOnSeparateThread, bool autoCreateWindow, const String& windowTitle )
 	{
 		RenderWindow* autoWindow = NULL;
 
@@ -594,18 +594,11 @@ namespace CamelotEngine
 		}	
 
 		// call superclass method
-		RenderSystem::_initialise( autoCreateWindow );
-
+		RenderSystem::startUp(runOnSeparateThread, autoCreateWindow);
 
 		return autoWindow;
 	}
 	//---------------------------------------------------------------------
-	void D3D9RenderSystem::reinitialise()
-	{
-		this->shutdown();
-		this->_initialise( true );
-	}
-	//---------------------------------------------------------------------
 	void D3D9RenderSystem::shutdown()
 	{
 		RenderSystem::shutdown();

+ 2 - 6
CamelotGLRenderer/Include/CmGLRenderSystem.h

@@ -150,7 +150,7 @@ namespace CamelotEngine {
         /** See
           RenderSystem
          */
-        RenderWindow* _initialise(bool autoCreateWindow, const String& windowTitle = "OGRE Render Window");
+        RenderWindow* startUp(bool runOnSeparateThread, bool autoCreateWindow, const String& windowTitle = "Camelot Render Window");
         /** See
           RenderSystem
          */
@@ -159,16 +159,12 @@ namespace CamelotEngine {
           RenderSystem
          */
 				void initialiseFromRenderSystemCapabilities(RenderSystemCapabilities* caps, RenderTarget* primary);
-        /** See
-          RenderSystem
-         */
-        void reinitialise(void); // Used if settings changed mid-rendering
         /** See
           RenderSystem
          */
         void shutdown(void);
         
-		/// @copydoc RenderSystem::_createRenderWindow
+		/// @copydoc RenderSystem::createRenderWindow
 		RenderWindow* createRenderWindow(const String &name, unsigned int width, unsigned int height, 
 			bool fullScreen, const NameValuePairList *miscParams = 0);
 

+ 2 - 8
CamelotGLRenderer/Source/CmGLRenderSystem.cpp

@@ -155,13 +155,13 @@ namespace CamelotEngine {
 		return mGLSupport->validateConfig();
 	}
 
-	RenderWindow* GLRenderSystem::_initialise(bool autoCreateWindow, const String& windowTitle)
+	RenderWindow* GLRenderSystem::startUp(bool runOnSeparateThread, bool autoCreateWindow, const String& windowTitle)
 	{
 		mGLSupport->start();
 
 		RenderWindow* autoWindow = mGLSupport->createWindow(autoCreateWindow, this, windowTitle);
 
-		RenderSystem::_initialise(autoCreateWindow, windowTitle);
+		RenderSystem::startUp(runOnSeparateThread, autoCreateWindow, windowTitle);
 
 		return autoWindow;
 	}
@@ -867,12 +867,6 @@ namespace CamelotEngine {
 		mGLInitialised = true;
 	}
 
-	void GLRenderSystem::reinitialise(void)
-	{
-		this->shutdown();
-		this->_initialise(true);
-	}
-
 	void GLRenderSystem::shutdown(void)
 	{
 		RenderSystem::shutdown();

+ 27 - 5
CamelotRenderer/Include/CmDeferredRenderSystem.h

@@ -2,6 +2,7 @@
 
 #include "CmPrerequisites.h"
 #include "CmRenderSystem.h"
+#include <boost/function.hpp>
 
 namespace CamelotEngine
 {
@@ -10,7 +11,7 @@ namespace CamelotEngine
 	class CM_EXPORT DeferredRenderSystem
 	{
 	public:
-		DeferredRenderSystem(CM_THREAD_ID_TYPE threadId);
+		~DeferredRenderSystem();
 
 		/**
 		 * @brief	Wrapper around RenderSystem method of the same name. See RenderSystem doc.
@@ -298,18 +299,39 @@ namespace CamelotEngine
 		// Finalized and ready for rendering
 		vector<DeferredGpuCommand*>::type* mReadyRenderCommandBuffer;
 
+		bool mIsShutdown;
+
 		CM_THREAD_ID_TYPE mMyThreadId;
 		CM_MUTEX(mCommandBufferMutex)
 
 		/**
-		 * @brief	Plays all queued commands
+		 * @brief	Throw an exception if the current thread is not the original
+		 * 			thread the DeferredRenderSystem was started on.
+		 */
+		void throwIfInvalidThread();
+
+		/**
+		 * @brief	Called when there are some commands ready for processing. Usually meant to signal
+		 * 			the render thread.
+		 */
+		boost::function<void()> NotifyCommandsReady;
+
+		/************************************************************************/
+		/* 					CALLABLE ONLY FROM RENDERSYSTEM                     */
+		/************************************************************************/
+		friend class RenderSystem;
+
+		DeferredRenderSystem(CM_THREAD_ID_TYPE threadId, boost::function<void()> commandsReadyCallback);
+
+		/**
+		 * @brief	Plays all queued commands. Should only be called from the render thread,
+		 * 			and is normally called by the RenderSystem internally.
 		 */
 		void playbackCommands();
 
 		/**
-		 * @brief	Throw an exception if the current thread is not the original
-		 * 			thread the DeferredRenderSystem was started on.
+		 * @brief	Query if this object has any commands ready for rendering.
 		 */
-		void throwIfInvalidThread();
+		bool hasReadyCommands();
 	};
 }

+ 1 - 0
CamelotRenderer/Include/CmPrerequisites.h

@@ -144,6 +144,7 @@ namespace CamelotEngine
 	typedef std::shared_ptr<RendererFactory> RendererFactoryPtr;
 	typedef std::shared_ptr<WorkQueue> WorkQueuePtr;
 	typedef std::shared_ptr<PassParameters> PassParametersPtr;
+	typedef std::shared_ptr<DeferredRenderSystem> DeferredRenderSystemPtr;
 
 	typedef std::shared_ptr<Component> ComponentPtr;
 	typedef std::shared_ptr<GameObject> GameObjectPtr;

+ 83 - 8
CamelotRenderer/Include/CmRenderSystem.h

@@ -160,9 +160,10 @@ namespace CamelotEngine
 		virtual String validateConfigOptions(void) = 0;
 
 		/** Start up the renderer using the settings selected (Or the defaults if none have been selected).
-		@remarks
-		Called by Root::setRenderSystem. Shouldn't really be called
-		directly, although  this can be done if the app wants to.
+		@param
+		runOnSeparateThread 
+		If true, a separate internal render thread will be created, and you will only be allowed to call RenderSystem methods
+		from DeferredRenderSystem created by RenderSystem::createDeferred().
 		@param
 		autoCreateWindow If true, creates a render window
 		automatically, based on settings chosen so far. This saves
@@ -175,7 +176,7 @@ namespace CamelotEngine
 		@returns
 		A pointer to the automatically created window, if requested, otherwise null.
 		*/
-		virtual RenderWindow* _initialise(bool autoCreateWindow, const String& windowTitle = "Camelot Render Window");
+		virtual RenderWindow* startUp(bool runOnSeparateThread, bool autoCreateWindow, const String& windowTitle = "Camelot Render Window");
 
 
 		/** Query the real capabilities of the GPU and driver in the RenderSystem*/
@@ -189,10 +190,6 @@ namespace CamelotEngine
 		*/
 		virtual void useCustomRenderSystemCapabilities(RenderSystemCapabilities* capabilities);
 
-		/** Restart the renderer (normally following a change in settings).
-		*/
-		virtual void reinitialise(void) = 0;
-
 		/** Shutdown the renderer and cleanup resources.
 		*/
 		virtual void shutdown(void);
@@ -1032,6 +1029,84 @@ namespace CamelotEngine
 		virtual void initialiseFromRenderSystemCapabilities(RenderSystemCapabilities* caps, RenderTarget* primary) = 0;
 
 		DriverVersion mDriverVersion;
+
+		/************************************************************************/
+		/* 								THREADING	                     		*/
+		/************************************************************************/
+
+		class RenderWorkerFunc CM_THREAD_WORKER_INHERIT
+		{
+		public:
+			RenderWorkerFunc(RenderSystem* rs);
+
+			void operator()();
+
+		private:
+			RenderSystem* mRS;
+		};
+
+		RenderWorkerFunc* mRenderThreadFunc;
+		bool mUsingSeparateRenderThread;
+		bool mRenderThreadShutdown;
+
+		CM_THREAD_ID_TYPE mRenderThreadId;
+		CM_THREAD_SYNCHRONISER(mDeferredRSInitCondition)
+		CM_MUTEX(mDeferredRSInitMutex)
+		CM_THREAD_SYNCHRONISER(mDeferredRSReadyCondition)
+		CM_MUTEX(mDeferredRSMutex)
+
+#if CM_THREAD_SUPPORT
+		CM_THREAD_TYPE* mRenderThread;
+#endif
+
+		vector<DeferredRenderSystemPtr>::type mDeferredRenderSystems;
+
+		/**
+		 * @brief	Initializes a separate render thread. Should only be called once.
+		 */
+		void initRenderThread();
+
+		/**
+		 * @brief	Main function of the render thread. Called once thread is started.
+		 */
+		void runRenderThread();
+
+		/**
+		 * @brief	Shutdowns the render thread. It will complete all ready commands
+		 * 			before shutdown.
+		 */
+		void shutdownRenderThread();
+
+		/**
+		 * @brief	Internal method that gets called by DeferredRenderSystems when 
+		 * 			they have commands ready for rendering.
+		 */
+		void notifyCommandsReadyCallback();
+
+		/**
+		 * @brief	Throws an exception if current thread isn't the render thread;
+		 */
+		void throwIfInvalidThread();
+
+	public:
+		/**
+		 * @brief	Returns the id of the render thread. If a separate render thread
+		 * 			is not used, then it returns the id of the thread RenderSystem
+		 * 			was initialized on.
+		 */
+		CM_THREAD_ID_TYPE getRenderThreadId() const { return mRenderThreadId; }
+
+		/**
+		 * @brief	Creates a new deferred render system that you can use for rendering on 
+		 * 			a non-render thread. You can have as many of these as you wish, the only limitation
+		 * 			is that you do not use a single instance on more than one thread. Each thread
+		 * 			requires its own deferred render system.
+		 * 			
+		 * @remark	Be aware that creating a new deferred render system requires all active render commands
+		 * 			to complete, so it can potentially be a very slow operation as the thread will be blocked
+		 * 			until rendering is done.
+		 */
+		DeferredRenderSystemPtr createDeferredRenderSystem();
 	};
 	/** @} */
 	/** @} */

+ 1 - 1
CamelotRenderer/Source/CmApplication.cpp

@@ -49,7 +49,7 @@ namespace CamelotEngine
 		RendererManager::setActive("ForwardRenderer");
 
 		RenderSystem* renderSystem = RenderSystemManager::getActive();
-		renderSystem->_initialise(false, "Camelot Renderer");
+		renderSystem->startUp(false, false, "Camelot Renderer");
 
 		mPrimaryRenderWindow = renderSystem->createRenderWindow("Camelot Renderer", 800, 600, false);
 

+ 43 - 6
CamelotRenderer/Source/CmDeferredRenderSystem.cpp

@@ -5,12 +5,35 @@
 
 namespace CamelotEngine
 {
-	DeferredRenderSystem::DeferredRenderSystem(CM_THREAD_ID_TYPE threadId)
-		:mMyThreadId(threadId)
+	DeferredRenderSystem::DeferredRenderSystem(CM_THREAD_ID_TYPE threadId, boost::function<void()> commandsReadyCallback)
+		:mMyThreadId(threadId), NotifyCommandsReady(commandsReadyCallback)
 	{
 		mActiveRenderCommandBuffer = new vector<DeferredGpuCommand*>::type();
 	}
 
+	DeferredRenderSystem::~DeferredRenderSystem()
+	{
+		CM_LOCK_MUTEX(mCommandBufferMutex)
+
+		if(mReadyRenderCommandBuffer != nullptr)
+		{
+			for(auto iter = mReadyRenderCommandBuffer->begin(); iter != mReadyRenderCommandBuffer->end(); ++iter)
+				delete *iter;
+
+			delete mReadyRenderCommandBuffer;
+			mReadyRenderCommandBuffer = nullptr;
+		}
+
+		if(mActiveRenderCommandBuffer != nullptr)
+		{
+			for(auto iter = mActiveRenderCommandBuffer->begin(); iter != mActiveRenderCommandBuffer->end(); ++iter)
+				delete *iter;
+
+			delete mActiveRenderCommandBuffer;
+			mActiveRenderCommandBuffer = nullptr;
+		}
+	}
+
 	void DeferredRenderSystem::render(const RenderOperation& op)
 	{
 		throwIfInvalidThread();
@@ -339,13 +362,19 @@ namespace CamelotEngine
 
 			mReadyRenderCommandBuffer = mActiveRenderCommandBuffer;
 			mActiveRenderCommandBuffer = new vector<DeferredGpuCommand*>::type();
+
+			if(mReadyRenderCommandBuffer != nullptr && mReadyRenderCommandBuffer->size() > 0)
+				NotifyCommandsReady();
 		}
 	}
 
 	void DeferredRenderSystem::playbackCommands()
 	{
-		// TODO - Throw exception if this is not called from render thread
-		
+		RenderSystem* rs = RenderSystemManager::getActive();
+
+		if(rs->getRenderThreadId() != CM_THREAD_CURRENT_ID)
+			CM_EXCEPT(InternalErrorException, "This method should only be called from the render thread.");
+
 		vector<DeferredGpuCommand*>::type* currentCommands = nullptr;
 		{
 			CM_LOCK_MUTEX(mCommandBufferMutex)
@@ -357,8 +386,6 @@ namespace CamelotEngine
 		if(currentCommands == nullptr)
 			return;
 
-		RenderSystem* rs = RenderSystemManager::getActive();
-
 		for(auto iter = currentCommands->begin(); iter != currentCommands->end(); ++iter)
 		{
 			(*iter)->submitCommand(rs);
@@ -368,6 +395,16 @@ namespace CamelotEngine
 		delete currentCommands;
 	}
 
+	bool DeferredRenderSystem::hasReadyCommands()
+	{
+		CM_LOCK_MUTEX(mCommandBufferMutex)
+
+			if(mReadyRenderCommandBuffer != nullptr && mReadyRenderCommandBuffer->size() > 0)
+				return true;
+
+		return false;
+	}
+
 	void DeferredRenderSystem::throwIfInvalidThread()
 	{
 #if CM_THREAD_SUPPORT != 0

+ 128 - 12
CamelotRenderer/Source/CmRenderSystem.cpp

@@ -39,6 +39,8 @@ THE SOFTWARE.
 #include "CmRenderWindow.h"
 #include "CmHardwarePixelBuffer.h"
 #include "CmHardwareOcclusionQuery.h"
+#include "CmDeferredRenderSystem.h"
+#include "boost/bind.hpp"
 
 namespace CamelotEngine {
 
@@ -62,6 +64,9 @@ namespace CamelotEngine {
 		, mRealCapabilities(0)
 		, mCurrentCapabilities(0)
 		, mUseCustomCapabilities(false)
+		, mUsingSeparateRenderThread(false)
+		, mRenderThreadFunc(nullptr)
+		, mRenderThreadShutdown(false)
     {
     }
 
@@ -88,21 +93,14 @@ namespace CamelotEngine {
 		}
     }
     //-----------------------------------------------------------------------
-    RenderWindow* RenderSystem::_initialise(bool autoCreateWindow, const String& windowTitle)
+    RenderWindow* RenderSystem::startUp(bool runOnSeparateThread, bool autoCreateWindow, const String& windowTitle)
     {
-        // Have I been registered by call to Root::setRenderSystem?
-		/** Don't do this anymore, just allow via Root
-        RenderSystem* regPtr = Root::getSingleton().getRenderSystem();
-        if (!regPtr || regPtr != this)
-            // Register self - library user has come to me direct
-            Root::getSingleton().setRenderSystem(this);
-		*/
-
-
         // Subclasses should take it from here
         // They should ALL call this superclass method from
-        //   their own initialise() implementations.
-        
+        //   their own startUp() implementations.
+
+		mRenderThreadId = CM_THREAD_CURRENT_ID;
+
         mVertexProgramBound = false;
 		mGeometryProgramBound = false;
         mFragmentProgramBound = false;
@@ -367,6 +365,9 @@ namespace CamelotEngine {
 		mRenderTargets.clear();
 
 		mPrioritisedRenderTargets.clear();
+
+		if(mUsingSeparateRenderThread)
+			shutdownRenderThread();
     }
     //-----------------------------------------------------------------------
     void RenderSystem::beginGeometryCount(void)
@@ -525,5 +526,120 @@ namespace CamelotEngine {
         // Make compiler happy
         return false;
 	}
+
+	void RenderSystem::initRenderThread()
+	{
+		mRenderThreadFunc = new RenderWorkerFunc(this);
+
+#if CM_THREAD_SUPPORT
+		CM_THREAD_CREATE(t, *mRenderThreadFunc);
+		mRenderThread = t;
+
+		CM_LOCK_MUTEX_NAMED(mDeferredRSInitMutex, lock)
+		CM_THREAD_WAIT(mDeferredRSInitCondition, mDeferredRSInitMutex, lock)
+
+		// TODO - Block here until I am sure render thread is running and initialized
+#else
+		CM_EXCEPT(InternalErrorException, "Attempting to start a render thread but Camelot isn't compiled with thread support.");
+#endif
+	}
+
+	void RenderSystem::runRenderThread()
+	{
+		mRenderThreadId = CM_THREAD_CURRENT_ID;
+		mUsingSeparateRenderThread = true;
+
+		CM_THREAD_NOTIFY_ALL(mDeferredRSInitCondition)
+
+		while(true)
+		{
+			if(mRenderThreadShutdown)
+				return;
+				
+			// Wait until we get some ready commands
+			{
+				CM_LOCK_MUTEX_NAMED(mDeferredRSMutex, lock)
+
+				bool anyCommandsReady = false;
+				for(auto iter = mDeferredRenderSystems.begin(); iter != mDeferredRenderSystems.end(); ++iter)
+				{
+					if((*iter)->hasReadyCommands())
+					{
+						anyCommandsReady = true;
+						break;
+					}
+				}
+
+				if(!anyCommandsReady)
+					CM_THREAD_WAIT(mDeferredRSReadyCondition, mDeferredRSMutex, lock)
+			}
+
+			{
+				CM_LOCK_MUTEX(mDeferredRSMutex);
+
+				for(auto iter = mDeferredRenderSystems.begin(); iter != mDeferredRenderSystems.end(); ++iter)
+				{
+					(*iter)->playbackCommands();
+				}
+			}
+		}
+
+	}
+
+	void RenderSystem::shutdownRenderThread()
+	{
+		mRenderThreadShutdown = true;
+
+		// Wake all threads. They will quit after they see the shutdown flag
+		CM_THREAD_NOTIFY_ALL(mDeferredRSReadyCondition)
+
+		mRenderThread->join();
+		CM_THREAD_DESTROY(mRenderThread);
+
+		mRenderThread = nullptr;
+		mUsingSeparateRenderThread = false;
+		mRenderThreadId = CM_THREAD_CURRENT_ID;
+		mDeferredRenderSystems.clear();
+	}
+
+	void RenderSystem::notifyCommandsReadyCallback()
+	{
+		CM_THREAD_NOTIFY_ALL(mDeferredRSReadyCondition)
+	}
+
+	DeferredRenderSystemPtr RenderSystem::createDeferredRenderSystem()
+	{
+		DeferredRenderSystemPtr newDeferredRS = DeferredRenderSystemPtr(
+			new DeferredRenderSystem(CM_THREAD_CURRENT_ID, boost::bind(&RenderSystem::notifyCommandsReadyCallback, this))
+			);
+
+		{
+			CM_LOCK_MUTEX(mDeferredRSMutex);
+			mDeferredRenderSystems.push_back(newDeferredRS);
+		}
+
+		return newDeferredRS;
+	}
+
+	void RenderSystem::throwIfInvalidThread()
+	{
+		if(CM_THREAD_CURRENT_ID != getRenderThreadId())
+			CM_EXCEPT(InternalErrorException, "Calling the render system from a non-render thread!");
+	}
+
+	/************************************************************************/
+	/* 								THREAD WORKER                      		*/
+	/************************************************************************/
+
+	RenderSystem::RenderWorkerFunc::RenderWorkerFunc(RenderSystem* rs)
+		:mRS(rs)
+	{
+		assert(mRS != nullptr);
+	}
+
+	void RenderSystem::RenderWorkerFunc::operator()()
+	{
+		mRS->runRenderThread();
+	}
 }