Просмотр исходного кода

Keep references to loaded resources

Marko Pintera 13 лет назад
Родитель
Сommit
54be900b15

+ 11 - 1
CamelotRenderer/Include/CmResources.h

@@ -32,6 +32,12 @@ namespace CamelotEngine
 			ResourcePtr rawResource;
 		};
 
+		struct CM_EXPORT ResourceAsyncOp
+		{
+			BaseResourceRef resource;
+			WorkQueue::RequestID requestID;
+		};
+
 		typedef std::shared_ptr<ResourceLoadRequest> ResourceLoadRequestPtr;
 		typedef std::shared_ptr<ResourceLoadResponse> ResourceLoadResponsePtr;
 
@@ -136,7 +142,11 @@ namespace CamelotEngine
 		WorkQueuePtr mWorkQueue; // TODO Low priority - I might want to make this more global so other classes can use it
 		UINT16 mWorkQueueChannel;
 
-		ResourcePtr loadInternal(const String& filePath);
+		unordered_map<String, BaseResourceRef>::type mLoadedResources; // TODO Low priority - I'm not sure how will filePath as key do performance wise
+		unordered_map<String, ResourceAsyncOp>::type mInProgressResources; // Resources that are being asynchronously loaded
+
+		BaseResourceRef loadInternal(const String& filePath, bool synchronous); 
+		ResourcePtr loadFromDiskAndDeserialize(const String& filePath);
 
 		void loadMetaData();
 		void saveMetaData(const ResourceMetaDataPtr metaData);

+ 65 - 42
CamelotRenderer/Source/CmResources.cpp

@@ -20,7 +20,7 @@ namespace CamelotEngine
 		ResourceLoadRequestPtr resRequest = boost::any_cast<ResourceLoadRequestPtr>(req->getData());
 
 		ResourceLoadResponsePtr resResponse = ResourceLoadResponsePtr(new Resources::ResourceLoadResponse());
-		resResponse->rawResource = gResources().loadInternal(resRequest->filePath);
+		resResponse->rawResource = gResources().loadFromDiskAndDeserialize(resRequest->filePath);
 
 		return new WorkQueue::Response(req, true, resResponse);
 	}
@@ -32,16 +32,28 @@ namespace CamelotEngine
 
 	void Resources::ResourceResponseHandler::handleResponse(const WorkQueue::Response* res, const WorkQueue* srcQ)
 	{
+		ResourceLoadRequestPtr resRequest = boost::any_cast<ResourceLoadRequestPtr>(res->getRequest()->getData());
+
+		auto iterFind = gResources().mInProgressResources.find(resRequest->filePath);
+		gResources().mInProgressResources.erase(iterFind);
+
 		if(res->getRequest()->getAborted())
 			return;
 
 		if(res->succeeded())
 		{
 			ResourceLoadResponsePtr resResponse = boost::any_cast<ResourceLoadResponsePtr>(res->getData());
-			ResourceLoadRequestPtr resRequest = boost::any_cast<ResourceLoadRequestPtr>(res->getRequest()->getData());
-
+			
 			resResponse->rawResource->init();
 			resRequest->resource.resolve(resResponse->rawResource);
+
+			if(!gResources().metaExists_UUID(resResponse->rawResource->getUUID()))
+			{
+				gDebug().logWarning("Loading a resource that doesn't have meta-data. Creating meta-data automatically. Resource path: " + resRequest->filePath);
+				gResources().addMetaData(resResponse->rawResource->getUUID(), resRequest->filePath);
+			}
+
+			gResources().mLoadedResources[resRequest->filePath] = resRequest->resource;
 		}
 		else
 		{
@@ -103,47 +115,12 @@ namespace CamelotEngine
 
 	BaseResourceRef Resources::load(const String& filePath)
 	{
-		// TODO - Make sure this uses the same code as loadAsync. Only forceSyncronous
-
-		// TODO - Low priority. Check is file path valid?
-
-		//BaseResourceRef resource = loadInternal(filePath);
-		//resource->init();
-
-		//// TODO - This should probably be called in loadInternal, but it isn't thread safe
-		////        - loadAsync never calls this code and it should
-		//if(!metaExists_UUID(resource->getUUID()))
-		//{
-		//	gDebug().logWarning("Loading a resource that doesn't have meta-data. Creating meta-data automatically. Resource path: " + filePath);
-		//	addMetaData(resource->getUUID(), filePath);
-		//}
-
-		//return resource;
-		
-		BaseResourceRef newResource;
-
-		ResourceLoadRequestPtr resRequest = ResourceLoadRequestPtr(new Resources::ResourceLoadRequest());
-		resRequest->filePath = filePath;
-		resRequest->resource = newResource;
-
-		mWorkQueue->addRequest(mWorkQueueChannel, resRequest, 0, true);
-
-		return newResource;
+		return loadInternal(filePath, true);
 	}
 
 	BaseResourceRef Resources::loadAsync(const String& filePath)
 	{
-		// TODO - Low priority. Check is file path valid?
-
-		BaseResourceRef newResource;
-
-		ResourceLoadRequestPtr resRequest = ResourceLoadRequestPtr(new Resources::ResourceLoadRequest());
-		resRequest->filePath = filePath;
-		resRequest->resource = newResource;
-
-		mWorkQueue->addRequest(mWorkQueueChannel, resRequest, 0, false);
-
-		return newResource;
+		return loadInternal(filePath, false);
 	}
 
 	BaseResourceRef Resources::loadFromUUID(const String& uuid)
@@ -172,7 +149,53 @@ namespace CamelotEngine
 		return loadAsync(metaEntry->mPath);
 	}
 
-	ResourcePtr Resources::loadInternal(const String& filePath)
+	BaseResourceRef Resources::loadInternal(const String& filePath, bool synchronous)
+	{
+		auto iterFind = mLoadedResources.find(filePath);
+		if(iterFind != mLoadedResources.end()) // Resource is already loaded
+		{
+			return iterFind->second;
+		}
+
+		auto iterFind2 = mInProgressResources.find(filePath);
+		if(iterFind2 != mInProgressResources.end()) // We're already loading this resource
+		{
+			ResourceAsyncOp& asyncOp = iterFind2->second;
+
+			if(!synchronous)
+				return asyncOp.resource;
+			else
+			{
+				// It's already being loaded asynchronously but we want it right away,
+				// so abort the async operation and load it right away.
+				mWorkQueue->abortRequest(asyncOp.requestID);
+			}
+		}
+
+		if(!FileSystem::fileExists(filePath))
+		{
+			gDebug().logWarning("Specified file: " + filePath + " doesn't exist.");
+			return nullptr;
+		}
+
+		BaseResourceRef newResource;
+
+		ResourceLoadRequestPtr resRequest = ResourceLoadRequestPtr(new Resources::ResourceLoadRequest());
+		resRequest->filePath = filePath;
+		resRequest->resource = newResource;
+
+		WorkQueue::RequestID requestId = mWorkQueue->peekNextFreeRequestId();
+		ResourceAsyncOp newAsyncOp;
+		newAsyncOp.resource = newResource;
+		newAsyncOp.requestID = requestId;
+
+		mInProgressResources[filePath] = newAsyncOp;
+
+		mWorkQueue->addRequest(mWorkQueueChannel, resRequest, 0, synchronous);
+		return newResource;
+	}
+
+	ResourcePtr Resources::loadFromDiskAndDeserialize(const String& filePath)
 	{
 		FileSerializer fs;
 		std::shared_ptr<IReflectable> loadedData = fs.decode(filePath);
@@ -297,7 +320,7 @@ namespace CamelotEngine
 		if(findIter != mResourceMetaData.end())
 			return findIter->second->mPath;
 		else
-			return "";
+			return StringUtil::BLANK;
 	}
 
 	const String& Resources::getUUIDFromPath(const String& path) const

+ 7 - 0
CamelotRenderer/TODO.txt

@@ -19,6 +19,12 @@ HIGH PRIORITY TODO:
  - HLSL & Cg don't handle include files yet
 
 Mid priority TODO:
+ - Separate render thread
+   - CommandBuffer (technically it should be just a RenderQueue I think)
+   - Main thread updates components, finds visible meshes and creates render device. At end of execution it fills up render queue
+   - Render thread renders everything in the render queue, and also creates resources (although RenderQueue name doesn't make sense if its used for resources too)
+   - Before I start with this I should probably strip down render system to a bare minimum
+
  - GpuProgram default parameters might not be needed. The parameters change with each use of the gpu program anyway
  - Mesh loading:
   - Example Freefall mesh has one index per vertex, and there are 17k+ vertices. I think I need a post-process step that optimizes them.
@@ -43,6 +49,7 @@ Low priority TODO:
  - Removed unused methods from D3D9 and GL render systems (mostly fixed function stuff)
  - Viewport needs to be updated when I call RenderTarget::setFullscreen/finishSwitchingFullscreen/updateWindowRect/windowMovedOrResized. Currently it's not
  - D3D9Texture::createTextureResources is commented out at the moment. It gets called on device reset, and at that point I should reload texture resources.
+  - I should probably keep all resources by DX managed. OpenGL apparently keeps a mirror of all its resources anyway.
  - Device reset and resource re-loading in general
 
 Optional TODO:

+ 10 - 3
CamelotUtility/Include/CmWorkQueue.h

@@ -317,6 +317,13 @@ namespace CamelotEngine
 		/** Remove a Response handler. */
 		void removeResponseHandler(UINT16 channel, ResponseHandler* rh);
 
+		/**
+		 * @brief	Gets the next free request identifier.
+		 *
+		 * @return	The next free request identifier.
+		 */
+		RequestID peekNextFreeRequestId();
+
 		/** Add a new request to the queue.
 		@param channel The channel this request will go into = 0; the channel is the top-level
 			categorisation of the request
@@ -332,6 +339,9 @@ namespace CamelotEngine
 		RequestID addRequest(UINT16 channel, const boost::any& rData, UINT8 retryCount = 0, 
 			bool forceSynchronous = false);
 
+		/// Put a Request on the queue with a specific RequestID.
+		void addRequestWithRID(RequestID rid, UINT16 channel, const boost::any& rData, UINT8 retryCount);
+
 		/** Abort a previously issued request.
 		If the request is still waiting to be processed, it will be 
 		removed from the queue.
@@ -405,9 +415,6 @@ namespace CamelotEngine
 		Response* processRequest(Request* r);
 		void processResponse(Response* r);
 
-		/// Put a Request on the queue with a specific RequestID.
-		void addRequestWithRID(RequestID rid, UINT16 channel, const boost::any& rData, UINT8 retryCount);
-
 		/** To be called by a separate thread; will return immediately if there
 			are items in the queue, or suspend the thread until new items are added
 			otherwise.

+ 30 - 19
CamelotUtility/Source/CmWorkQueue.cpp

@@ -182,6 +182,17 @@ namespace CamelotEngine {
 		}
 	}
 	//---------------------------------------------------------------------
+	WorkQueue::RequestID WorkQueue::peekNextFreeRequestId()
+	{
+		{
+			// lock to acquire rid and push request to the queue
+			CM_LOCK_MUTEX(mRequestMutex)
+
+			RequestID rid = mRequestCount + 1;
+			return rid;
+		}
+	}
+	//---------------------------------------------------------------------
 	WorkQueue::RequestID WorkQueue::addRequest(UINT16 channel, 
 		const boost::any& rData, UINT8 retryCount, bool forceSynchronous)
 	{
@@ -213,6 +224,25 @@ namespace CamelotEngine {
 
 		return rid;
 
+	}
+	//---------------------------------------------------------------------
+	void WorkQueue::addRequestWithRID(WorkQueue::RequestID rid, UINT16 channel, 
+		const boost::any& rData, UINT8 retryCount)
+	{
+		// lock to push request to the queue
+		CM_LOCK_MUTEX(mRequestMutex)
+
+		if (mShuttingDown)
+			return;
+
+		Request* req = new Request(channel, rData, retryCount, rid);
+
+#if CM_THREAD_SUPPORT
+		mRequestQueue.push_back(req);
+		notifyWorkers();
+#else
+		processRequestResponse(req, true);
+#endif
 	}
 	//---------------------------------------------------------------------
 	void WorkQueue::abortRequest(RequestID id)
@@ -505,25 +535,6 @@ namespace CamelotEngine {
 		}
 	}
 	//---------------------------------------------------------------------
-	void WorkQueue::addRequestWithRID(WorkQueue::RequestID rid, UINT16 channel, 
-		const boost::any& rData, UINT8 retryCount)
-	{
-		// lock to push request to the queue
-		CM_LOCK_MUTEX(mRequestMutex)
-
-			if (mShuttingDown)
-				return;
-
-		Request* req = new Request(channel, rData, retryCount, rid);
-
-#if CM_THREAD_SUPPORT
-		mRequestQueue.push_back(req);
-		notifyWorkers();
-#else
-		processRequestResponse(req, true);
-#endif
-	}
-	//---------------------------------------------------------------------
 	void WorkQueue::processNextRequest()
 	{
 		Request* request = 0;