소스 검색

- Asynchronous loading of textures (WIP)

Panagiotis Christopoulos Charitos 14 년 전
부모
커밋
1acc0b4a53
4개의 변경된 파일258개의 추가작업 그리고 2개의 파일을 삭제
  1. 114 0
      anki/resource/AsyncOperator.cpp
  2. 86 0
      anki/resource/AsyncOperator.h
  3. 56 0
      anki/resource/AsyncTextureResourceManager.h
  4. 2 2
      anki/resource/ResourceManager.h

+ 114 - 0
anki/resource/AsyncOperator.cpp

@@ -0,0 +1,114 @@
+#include "anki/resource/AsyncOperator.h"
+#include "anki/core/Logger.h"
+#include "anki/core/Globals.h"
+#include "anki/util/HighRezTimer.h"
+
+
+namespace anki {
+
+
+//==============================================================================
+void AsyncOperator::start()
+{
+	ANKI_INFO("Starting async operator thread...");
+	thread = boost::thread(&AsyncOperator::workingFunc, this);
+}
+
+
+//==============================================================================
+void AsyncOperator::putBack(Request* newReq)
+{
+	ANKI_INFO("New request (" << newReq->getInfo() << ")");
+	boost::mutex::scoped_lock lock(mutexReq);
+	requests.push_back(newReq);
+	lock.unlock();
+
+	condVar.notify_one();
+}
+
+
+//==============================================================================
+void AsyncOperator::workingFunc()
+{
+	while(1)
+	{
+		Request* req;
+
+		// Wait for something
+		{
+			boost::mutex::scoped_lock lock(mutexReq);
+			while(requests.empty())
+			{
+				ANKI_INFO("Waiting...");
+				condVar.wait(lock);
+			}
+
+			req = requests.front();
+			requests.pop_front();
+		}
+
+		if(req == NULL)
+		{
+			return;
+		}
+
+		// Exec the loader
+		bool ok = true;
+		try
+		{
+			req->exec();
+			ANKI_INFO("Request served (" << req->getInfo() << ")");
+		}
+		catch(const std::exception& e)
+		{
+			ANKI_ERROR("Request failed (" << req->getInfo() << "): " <<
+				e.what());
+			ok = false;
+		}
+
+		req->ok = ok;
+
+		// Put back the response
+		{
+			boost::mutex::scoped_lock lock(mutexRes);
+			responses.push_back(req);
+		}
+	} // end thread loop
+}
+
+
+//==============================================================================
+uint AsyncOperator::execPostLoad(float maxTime)
+{
+	uint count = 0;
+	HighRezTimer t;
+	t.start();
+
+	while(true)
+	{
+		boost::mutex::scoped_lock lock(mutexRes);
+		if(responses.empty())
+		{
+			break;
+		}
+
+		Request* resp = responses.front();
+		responses.pop_front();
+		lock.unlock();
+
+		resp->postExec(*this);
+		delete resp;
+		++count;
+
+		// Leave if you passed the max time
+		if(t.getElapsedTime() >= maxTime)
+		{
+			break;
+		}
+	}
+
+	return count;
+}
+
+
+} // end namespace

+ 86 - 0
anki/resource/AsyncOperator.h

@@ -0,0 +1,86 @@
+#ifndef ANKI_RESOURCE_ASYNC_LOADER_H
+#define ANKI_RESOURCE_ASYNC_LOADER_H
+
+#include <list>
+#include <string>
+#include <boost/thread/thread.hpp>
+#include <boost/thread/mutex.hpp>
+
+
+namespace anki {
+
+
+/// Asynchronous operator
+///
+/// It creates a thread that executes requests on demand. It contains a queue
+/// with requests.
+/// @code async.pushBack(new RequestDerived(...)); @endcode
+/// The AsyncOperator gets the ownership of the request and de-allocates it
+/// when the request is served. Its not meant to be destroyed because of a
+/// deadlock.
+class AsyncOperator
+{
+	public:
+		/// XXX
+		class Request
+		{
+			public:
+				bool ok;
+
+				/// Called in the worker thread
+				virtual void exec() = 0;
+
+				/// Called in the main thread after the request is served
+				virtual void postExec(AsyncOperator& al) = 0;
+
+				/// XXX
+				virtual std::string getInfo() const
+				{
+					return "no info";
+				}
+		};
+
+		/// Default constructor starts the thread
+		AsyncOperator()
+		{
+			start();
+		}
+		
+		/// Do nothing
+		~AsyncOperator()
+		{}
+
+		/// Add a new request in the queue
+		void putBack(Request* newReq);
+
+		/// Handle the served requests
+		///
+		/// Steps:
+		/// - Gets the served requests
+		/// - Executes the Request::postExec for those requests
+		/// - Deletes them
+		///
+		/// @param[in] availableTime Max time to spend in the Request::postExec
+		/// @return The number of requests served
+		uint execPostLoad(float availableTime);
+
+	private:
+		std::list<Request*> requests;
+		std::list<Request*> responses;
+		boost::mutex mutexReq; ///< Protect the requests container
+		boost::mutex mutexRes; ///< Protect the responses container
+		boost::thread thread;
+		boost::condition_variable condVar;
+
+		/// The thread function. It waits for something in the requests
+		/// container
+		void workingFunc();
+
+		void start(); ///< Start thread
+};
+
+
+} // end namespace
+
+
+#endif

+ 56 - 0
anki/resource/AsyncTextureResourceManager.h

@@ -0,0 +1,56 @@
+#ifndef ANKI_RESOURCE_ASYNC_TEXTURE_RESOURCE_MANAGER_H
+#define ANKI_RESOURCE_ASYNC_TEXTURE_RESOURCE_MANAGER_H
+
+#include "anki/resource/ResourceManager.h"
+#include "anki/resource/AsyncOperator.h"
+#include <boost/scoped_ptr.hpp>
+
+
+namespace anki {
+
+
+class Texture;
+
+
+/// @addtogroup resource
+/// @{
+
+/// XXX
+class AsyncTextureResourceManager: public ResourceManager<Texture>
+{
+	public:
+
+	private:
+		/// XXX
+		class Request: public AsyncOperator::Request
+		{
+			public:
+				std::string filename;
+				Image img;
+				Texture** ppTex;
+
+				Request(const char* fname, Texture**& ppTex_)
+				:	filename(fname),
+				 	ppTex(ppTex_)
+				{}
+
+				/// Implements AsyncOperator::Request::exec
+				void exec();
+
+				/// Implements AsyncOperator::Request::postExec
+				void postExec(AsyncOperator& al);
+
+				/// Re-implements AsyncOperator::Request::getInfo
+				std::string getInfo() const;
+		};
+
+		boost::scoped_ptr<AsyncOperator> ao;
+
+};
+/// @}
+
+
+} // namespace anki
+
+
+#endif

+ 2 - 2
anki/resource/ResourceManager.h

@@ -57,8 +57,8 @@ class ResourceManager
 		/// Dealocate the resource. Its separate for two reasons:
 		/// - Because we want to specialize it for the async loaded resources
 		/// - Because we cannot have the operator delete in a template body.
-		///   Apparently the compiler is to dump to decide
-		void deallocRsrc(Type* rsrc);
+		/// Apparently the compiler is to dump to decide
+		virtual void deallocRsrc(Type* rsrc);
 };