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

Keep references to loaded resources

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

+ 11 - 1
CamelotRenderer/Include/CmResources.h

@@ -32,6 +32,12 @@ namespace CamelotEngine
 			ResourcePtr rawResource;
 			ResourcePtr rawResource;
 		};
 		};
 
 
+		struct CM_EXPORT ResourceAsyncOp
+		{
+			BaseResourceRef resource;
+			WorkQueue::RequestID requestID;
+		};
+
 		typedef std::shared_ptr<ResourceLoadRequest> ResourceLoadRequestPtr;
 		typedef std::shared_ptr<ResourceLoadRequest> ResourceLoadRequestPtr;
 		typedef std::shared_ptr<ResourceLoadResponse> ResourceLoadResponsePtr;
 		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
 		WorkQueuePtr mWorkQueue; // TODO Low priority - I might want to make this more global so other classes can use it
 		UINT16 mWorkQueueChannel;
 		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 loadMetaData();
 		void saveMetaData(const ResourceMetaDataPtr metaData);
 		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());
 		ResourceLoadRequestPtr resRequest = boost::any_cast<ResourceLoadRequestPtr>(req->getData());
 
 
 		ResourceLoadResponsePtr resResponse = ResourceLoadResponsePtr(new Resources::ResourceLoadResponse());
 		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);
 		return new WorkQueue::Response(req, true, resResponse);
 	}
 	}
@@ -32,16 +32,28 @@ namespace CamelotEngine
 
 
 	void Resources::ResourceResponseHandler::handleResponse(const WorkQueue::Response* res, const WorkQueue* srcQ)
 	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())
 		if(res->getRequest()->getAborted())
 			return;
 			return;
 
 
 		if(res->succeeded())
 		if(res->succeeded())
 		{
 		{
 			ResourceLoadResponsePtr resResponse = boost::any_cast<ResourceLoadResponsePtr>(res->getData());
 			ResourceLoadResponsePtr resResponse = boost::any_cast<ResourceLoadResponsePtr>(res->getData());
-			ResourceLoadRequestPtr resRequest = boost::any_cast<ResourceLoadRequestPtr>(res->getRequest()->getData());
-
+			
 			resResponse->rawResource->init();
 			resResponse->rawResource->init();
 			resRequest->resource.resolve(resResponse->rawResource);
 			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
 		else
 		{
 		{
@@ -103,47 +115,12 @@ namespace CamelotEngine
 
 
 	BaseResourceRef Resources::load(const String& filePath)
 	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)
 	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)
 	BaseResourceRef Resources::loadFromUUID(const String& uuid)
@@ -172,7 +149,53 @@ namespace CamelotEngine
 		return loadAsync(metaEntry->mPath);
 		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;
 		FileSerializer fs;
 		std::shared_ptr<IReflectable> loadedData = fs.decode(filePath);
 		std::shared_ptr<IReflectable> loadedData = fs.decode(filePath);
@@ -297,7 +320,7 @@ namespace CamelotEngine
 		if(findIter != mResourceMetaData.end())
 		if(findIter != mResourceMetaData.end())
 			return findIter->second->mPath;
 			return findIter->second->mPath;
 		else
 		else
-			return "";
+			return StringUtil::BLANK;
 	}
 	}
 
 
 	const String& Resources::getUUIDFromPath(const String& path) const
 	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
  - HLSL & Cg don't handle include files yet
 
 
 Mid priority TODO:
 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
  - GpuProgram default parameters might not be needed. The parameters change with each use of the gpu program anyway
  - Mesh loading:
  - 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.
   - 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)
  - 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
  - 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.
  - 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
  - Device reset and resource re-loading in general
 
 
 Optional TODO:
 Optional TODO:

+ 10 - 3
CamelotUtility/Include/CmWorkQueue.h

@@ -317,6 +317,13 @@ namespace CamelotEngine
 		/** Remove a Response handler. */
 		/** Remove a Response handler. */
 		void removeResponseHandler(UINT16 channel, ResponseHandler* rh);
 		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.
 		/** Add a new request to the queue.
 		@param channel The channel this request will go into = 0; the channel is the top-level
 		@param channel The channel this request will go into = 0; the channel is the top-level
 			categorisation of the request
 			categorisation of the request
@@ -332,6 +339,9 @@ namespace CamelotEngine
 		RequestID addRequest(UINT16 channel, const boost::any& rData, UINT8 retryCount = 0, 
 		RequestID addRequest(UINT16 channel, const boost::any& rData, UINT8 retryCount = 0, 
 			bool forceSynchronous = false);
 			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.
 		/** Abort a previously issued request.
 		If the request is still waiting to be processed, it will be 
 		If the request is still waiting to be processed, it will be 
 		removed from the queue.
 		removed from the queue.
@@ -405,9 +415,6 @@ namespace CamelotEngine
 		Response* processRequest(Request* r);
 		Response* processRequest(Request* r);
 		void processResponse(Response* 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
 		/** 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
 			are items in the queue, or suspend the thread until new items are added
 			otherwise.
 			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, 
 	WorkQueue::RequestID WorkQueue::addRequest(UINT16 channel, 
 		const boost::any& rData, UINT8 retryCount, bool forceSynchronous)
 		const boost::any& rData, UINT8 retryCount, bool forceSynchronous)
 	{
 	{
@@ -213,6 +224,25 @@ namespace CamelotEngine {
 
 
 		return rid;
 		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)
 	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()
 	void WorkQueue::processNextRequest()
 	{
 	{
 		Request* request = 0;
 		Request* request = 0;