浏览代码

More work on fixing up multithreading and getting everything to work

Marko Pintera 13 年之前
父节点
当前提交
c6b1e6a880

+ 1 - 1
CamelotD3D9Renderer/Include/CmD3D9GpuProgram.h

@@ -79,7 +79,7 @@ namespace CamelotEngine {
             populate any implementation-specific extras (like named parameters) where
             they are appropriate.
         */
-        virtual GpuProgramParametersSharedPtr createParameters(void);
+        virtual void createParameters_internal(AsyncOp& op);
 	protected:    
 		bool mColumnMajorMatrices;
 		ID3DXBuffer* mpExternalMicrocode;

+ 1 - 1
CamelotD3D9Renderer/Include/CmD3D9HLSLProgram.h

@@ -117,7 +117,7 @@ namespace CamelotEngine {
         /// Overridden from GpuProgram
         bool isSupported(void) const;
         /// Overridden from GpuProgram
-        GpuProgramParametersSharedPtr createParameters(void);
+        void createParameters_internal(AsyncOp& op);
         /// Overridden from GpuProgram
         const String& getLanguage(void) const;
 

+ 5 - 4
CamelotD3D9Renderer/Source/CmD3D9GpuProgram.cpp

@@ -30,6 +30,7 @@ THE SOFTWARE.
 #include "CmException.h"
 #include "CmD3D9Mappings.h"
 #include "CmD3D9RenderSystem.h"
+#include "CmAsyncOp.h"
 
 namespace CamelotEngine {
     //-----------------------------------------------------------------------------
@@ -157,15 +158,15 @@ namespace CamelotEngine {
 		SAFE_RELEASE(errors);
 	}
     //-----------------------------------------------------------------------
-    GpuProgramParametersSharedPtr D3D9GpuProgram::createParameters(void)
+    void D3D9GpuProgram::createParameters_internal(AsyncOp& op)
     {
         // Call superclass
-        GpuProgramParametersSharedPtr params = GpuProgram::createParameters();
+        GpuProgram::createParameters_internal(op);
+
+		GpuProgramParametersSharedPtr params = op.getReturnValue<GpuProgramParametersSharedPtr>();
 
         // Need to transpose matrices if compiled with column-major matrices
         params->setTransposeMatrices(mColumnMajorMatrices);
-
-        return params;
     }	
 	//-----------------------------------------------------------------------------
     D3D9GpuVertexProgram::D3D9GpuVertexProgram() 

+ 5 - 4
CamelotD3D9Renderer/Source/CmD3D9HLSLProgram.cpp

@@ -31,6 +31,7 @@ THE SOFTWARE.
 #include "CmException.h"
 #include "CmRenderSystem.h"
 #include "CmRenderSystemManager.h"
+#include "CmAsyncOp.h"
 #include "CmD3D9HLSLProgramRTTI.h"
 
 namespace CamelotEngine {
@@ -546,15 +547,15 @@ namespace CamelotEngine {
 		return rs->getCapabilities_internal()->isShaderProfileSupported(hlslProfile);
     }
     //-----------------------------------------------------------------------
-    GpuProgramParametersSharedPtr D3D9HLSLProgram::createParameters(void)
+    void D3D9HLSLProgram::createParameters_internal(AsyncOp& op)
     {
         // Call superclass
-        GpuProgramParametersSharedPtr params = HighLevelGpuProgram::createParameters();
+       HighLevelGpuProgram::createParameters_internal(op);
+
+	    GpuProgramParametersSharedPtr params = op.getReturnValue<GpuProgramParametersSharedPtr>();
 
         // Need to transpose matrices if compiled with column-major matrices
         params->setTransposeMatrices(mColumnMajorMatrices);
-
-        return params;
     }
     //-----------------------------------------------------------------------
     void D3D9HLSLProgram::setTarget(const String& target)

+ 1 - 1
CamelotD3D9Renderer/Source/CmD3D9RenderSystem.cpp

@@ -1475,7 +1475,7 @@ namespace CamelotEngine
 	//---------------------------------------------------------------------
 	D3D9ResourceManager* D3D9RenderSystem::getResourceManager()
 	{
-		THROW_IF_NOT_RENDER_THREAD_STATIC;
+		// No need to check if we're on render thread as this is synced up internally
 
 		return msD3D9RenderSystem->mResourceManager;
 	}

+ 3 - 1
CamelotFBXImporter/Source/CmFBXImporter.cpp

@@ -47,7 +47,9 @@ namespace CamelotEngine
 
 		shutDownSdk(fbxManager);
 
-		MeshHandle mesh(new Mesh());
+		MeshHandle mesh = MeshHandle(Mesh::create());
+
+		mesh.waitUntilLoaded();
 		mesh->setMeshData(meshData);
 
 		return mesh;

+ 2 - 0
CamelotFreeImgImporter/Source/CmFreeImgImporter.cpp

@@ -138,6 +138,8 @@ namespace CamelotEngine
 		TextureHandle newTexture(Texture::create(TEX_TYPE_2D, 
 			imgData->getWidth(), imgData->getHeight(), imgData->getNumMipmaps(), imgData->getFormat()));
 
+		newTexture.waitUntilLoaded();
+
 		for(size_t mip = 0; mip <= imgData->getNumMipmaps(); ++mip)
 		{
 			PixelData src = imgData->getPixels(mip);

+ 5 - 2
CamelotRenderer/Include/CmGpuProgram.h

@@ -112,11 +112,13 @@ namespace CamelotEngine {
 		/// Create the internal params named mapping structures
 		void createNamedParameterMappingStructures(bool recreateIfExists = true) const;
 
+		void throwIfNotRenderThread() const;
+
 	public:
 
 		GpuProgram();
 
-		virtual ~GpuProgram() {}
+		virtual ~GpuProgram();
 
 		/**
 		 * @brief	Initializes the gpu program. This must be called right after the program is constructed. 
@@ -182,7 +184,8 @@ namespace CamelotEngine {
             populate any implementation-specific extras (like named parameters) where
             they are appropriate.
         */
-        virtual GpuProgramParametersSharedPtr createParameters(void);
+		GpuProgramParametersSharedPtr createParameters(void);
+        virtual void createParameters_internal(AsyncOp& op);
 
 		/** Returns a string that specifies the language of the gpu programs as specified
         in a material script. ie: asm, cg, hlsl, glsl

+ 1 - 1
CamelotRenderer/Include/CmHighLevelGpuProgram.h

@@ -120,7 +120,7 @@ namespace CamelotEngine {
             HighLevelGpuProgramManager. This method creates a new instance of a parameters
             object containing the definition of the parameters this program understands.
         */
-        GpuProgramParametersSharedPtr createParameters(void);
+        void createParameters_internal(AsyncOp& op);
 
         /** @copydoc GpuProgram::getBindingDelegate */
         GpuProgram* getBindingDelegate_internal(void) { return mAssemblerProgram.get(); }

+ 2 - 2
CamelotRenderer/Include/CmMaterial.h

@@ -16,12 +16,12 @@ namespace CamelotEngine
 	class CM_EXPORT Material : public Resource
 	{
 	public:
-		Material() {}
+		Material();
 
 		/**
 		 * @brief	Overridden from Resource.
 		 */
-		virtual void initialize_internal() { }
+		virtual void initialize_internal();
 
 		/**
 		 * @brief	Sets a shader that will be used by the material. 

+ 9 - 4
CamelotRenderer/Include/CmMesh.h

@@ -25,13 +25,16 @@ namespace CamelotEngine
 	class CM_EXPORT Mesh : public Resource
 	{
 	public:
-		Mesh();
 		virtual ~Mesh();
 
+		void initialize();
+		virtual void initialize_internal();
+
 		/**
 		 * @brief	Mesh data that is used for initializing the mesh. Needs to be set before calling load.
 		 */
 		void setMeshData(MeshDataPtr meshData);
+		void setMeshData_internal(MeshDataPtr meshData);
 
 		/**
 		 * @brief	Gets the mesh data from the GPU. This method is slow so be careful when you call it.
@@ -40,16 +43,16 @@ namespace CamelotEngine
 
 		RenderOperation getRenderOperation(UINT32 subMeshIdx = 0) const;
 
-		virtual void initialize_internal();
-
 	private:
-		MeshDataPtr mMeshData;
+		Mesh();
 
 		VertexData* mVertexData;
 		IndexData* mIndexData;
 
 		vector<SubMesh>::type mSubMeshes;
 
+		void throwIfNotRenderThread() const;
+
 		/************************************************************************/
 		/* 								SERIALIZATION                      		*/
 		/************************************************************************/
@@ -58,6 +61,8 @@ namespace CamelotEngine
 		static RTTITypeBase* getRTTIStatic();
 		virtual RTTITypeBase* getRTTI() const;
 
+		static MeshPtr createEmpty();
+
 		/************************************************************************/
 		/* 								STATICS		                     		*/
 		/************************************************************************/

+ 2 - 2
CamelotRenderer/Include/CmResource.h

@@ -23,9 +23,9 @@ namespace CamelotEngine
 		bool isInitialized() const { return mIsInitialized; }
 
 		/**
-		 * @brief	Blocks the current thread until the resource is fully loaded.
+		 * @brief	Blocks the current thread until the resource is fully initialized.
 		 */
-		void waitUntilLoaded();
+		void waitUntilInitialized();
 
 	protected:
 		friend class Resources;

+ 22 - 4
CamelotRenderer/Include/CmResourceHandle.h

@@ -34,6 +34,13 @@ namespace CamelotEngine
 		 */
 		bool isLoaded() const;
 
+		/**
+		 * @brief	Blocks the current thread until the resource is fully loaded.
+		 * 			
+		 * @note	Careful not to call this on the thread that does the loading.
+		 */
+		void waitUntilLoaded() const;
+
 	protected:
 		ResourceHandleBase();
 
@@ -49,6 +56,10 @@ namespace CamelotEngine
 		}
 	private:
 		friend class Resources;
+
+		CM_STATIC_THREAD_SYNCHRONISER(mResourceCreatedCondition)
+		CM_STATIC_MUTEX(mResourceCreatedMutex)
+
 		/**
 		 * @brief	Sets the created flag to true. Should only be called
 		 * 			by Resources class after loading of the resource is fully done.
@@ -61,6 +72,9 @@ namespace CamelotEngine
 		 */
 		void ResourceHandleBase::setUUID(const String& uuid);
 
+	protected:
+		inline void throwIfNotLoaded() const;
+
 		/************************************************************************/
 		/* 								RTTI		                     		*/
 		/************************************************************************/
@@ -105,15 +119,19 @@ namespace CamelotEngine
 		// TODO Low priority - User can currently try to access these even if resource ptr is not resolved
 		T* get() const 
 		{ 
-			if(!isLoaded()) 
-				return nullptr; 
-			
+			throwIfNotLoaded();
+
 			return static_cast<T*>(mData->mPtr.get()); 
 		}
 		T* operator->() const { return get(); }
 		T& operator*() const { return *get(); }
 
-		std::shared_ptr<T> getInternalPtr() { if(!isLoaded()) return nullptr; return std::static_pointer_cast<T>(mData->mPtr); }
+		std::shared_ptr<T> getInternalPtr() 
+		{ 
+			throwIfNotLoaded();
+
+			return std::static_pointer_cast<T>(mData->mPtr); 
+		}
 
 		template<class _Ty>
 		struct CM_Bool_struct

+ 30 - 2
CamelotRenderer/Source/CmGpuProgram.cpp

@@ -33,8 +33,15 @@ THE SOFTWARE.
 #include "CmException.h"
 #include "CmRenderSystem.h"
 #include "CmRenderSystemManager.h"
+#include "CmAsyncOp.h"
 #include "CmGpuProgramRTTI.h"
 
+#if CM_DEBUG_MODE
+#define THROW_IF_NOT_RENDER_THREAD throwIfNotRenderThread();
+#else
+#define THROW_IF_NOT_RENDER_THREAD 
+#endif
+
 namespace CamelotEngine
 {
     //-----------------------------------------------------------------------------
@@ -44,6 +51,11 @@ namespace CamelotEngine
     {
 		createParameterMappingStructures();
     }
+	//----------------------------------------------------------------------------
+	GpuProgram::~GpuProgram()
+	{
+		THROW_IF_NOT_RENDER_THREAD;
+	}
     //-----------------------------------------------------------------------------
     void GpuProgram::setType(GpuProgramType t)
     {
@@ -121,9 +133,18 @@ namespace CamelotEngine
 		if (recreateIfExists || (mConstantDefs == nullptr))
 			mConstantDefs = GpuNamedConstantsPtr(new GpuNamedConstants());
 	}
+	//---------------------------------------------------------------------
+	GpuProgramParametersSharedPtr GpuProgram::createParameters(void)
+	{
+		AsyncOp op = RenderSystemManager::getActive()->queueResourceReturnCommand(boost::bind(&GpuProgram::createParameters_internal, this, _1), true);
+
+		return op.getReturnValue<GpuProgramParametersSharedPtr>();
+	}
     //-----------------------------------------------------------------------------
-    GpuProgramParametersSharedPtr GpuProgram::createParameters(void)
+    void GpuProgram::createParameters_internal(AsyncOp& op)
     {
+		THROW_IF_NOT_RENDER_THREAD
+
         // Default implementation simply returns standard parameters.
         GpuProgramParametersSharedPtr ret = GpuProgramParametersSharedPtr(new GpuProgramParameters());	
 		
@@ -135,7 +156,7 @@ namespace CamelotEngine
 		// link shared logical / physical map for low-level use
 		ret->_setLogicalIndexes(mFloatLogicalToPhysical, mIntLogicalToPhysical, mSamplerLogicalToPhysical);
 
-        return ret;
+		op.completeOperation(ret);
     }
     //-----------------------------------------------------------------------
     const String& GpuProgram::getLanguage(void) const
@@ -144,6 +165,12 @@ namespace CamelotEngine
 
         return language;
     }
+	//----------------------------------------------------------------------------- 
+	void GpuProgram::throwIfNotRenderThread() const
+	{
+		if(CM_THREAD_CURRENT_ID != RenderSystemManager::getActive()->getRenderThreadId())
+			CM_EXCEPT(InternalErrorException, "Calling an internal texture method from a non-render thread!");
+	}
 
 	/************************************************************************/
 	/* 								SERIALIZATION                      		*/
@@ -159,3 +186,4 @@ namespace CamelotEngine
 	}
 }
 
+#undef THROW_IF_NOT_RENDER_THREAD 

+ 15 - 2
CamelotRenderer/Source/CmHighLevelGpuProgram.cpp

@@ -30,6 +30,15 @@ THE SOFTWARE.
 #include "CmRenderSystemManager.h"
 #include "CmRenderSystem.h"
 #include "CmException.h"
+#include "CmRenderSystem.h"
+#include "CmRenderSystemManager.h"
+#include "CmAsyncOp.h"
+
+#if CM_DEBUG_MODE
+#define THROW_IF_NOT_RENDER_THREAD throwIfNotRenderThread();
+#else
+#define THROW_IF_NOT_RENDER_THREAD 
+#endif
 
 namespace CamelotEngine
 {
@@ -81,8 +90,10 @@ namespace CamelotEngine
         // superclasses will trigger unload
     }
     //---------------------------------------------------------------------------
-    GpuProgramParametersSharedPtr HighLevelGpuProgram::createParameters(void)
+    void HighLevelGpuProgram::createParameters_internal(AsyncOp& op)
     {
+		THROW_IF_NOT_RENDER_THREAD
+
         // Make sure param defs are loaded
         GpuProgramParametersSharedPtr params = GpuProgramParametersSharedPtr(new GpuProgramParameters());
 		// Only populate named parameters if we can support this program
@@ -91,7 +102,7 @@ namespace CamelotEngine
 			populateParameterNames(params);
 		}
 
-        return params;
+		op.completeOperation(params);
     }
     //---------------------------------------------------------------------------
     void HighLevelGpuProgram::loadHighLevel(void)
@@ -162,3 +173,5 @@ namespace CamelotEngine
 		return HighLevelGpuProgramManager::instance().create(source, entryPoint, language, gptype, profile);
 	}
 }
+
+#undef THROW_IF_NOT_RENDER_THREAD 

+ 20 - 0
CamelotRenderer/Source/CmMaterial.cpp

@@ -11,6 +11,17 @@
 
 namespace CamelotEngine
 {
+	Material::Material()
+	{
+		// Material doesn't do anything render thread specific, so we can just initialize right away
+		initialize_internal();
+	}
+
+	void Material::initialize_internal()
+	{
+		Resource::initialize_internal();
+	}
+
 	void Material::setShader(ShaderPtr shader)
 	{
 		mShader = shader;
@@ -37,15 +48,24 @@ namespace CamelotEngine
 
 					GpuProgramHandle vertProgram = curPass->getVertexProgram();
 					if(vertProgram)
+					{
+						vertProgram.waitUntilLoaded();
 						params->mVertParams = vertProgram->createParameters();
+					}
 
 					GpuProgramHandle fragProgram = curPass->getFragmentProgram();
 					if(fragProgram)
+					{
+						fragProgram.waitUntilLoaded();
 						params->mFragParams = fragProgram->createParameters();
+					}
 
 					GpuProgramHandle geomProgram = curPass->getGeometryProgram();
 					if(geomProgram)
+					{
+						geomProgram.waitUntilLoaded();
 						params->mGeomParams = geomProgram->createParameters();	
+					}
 
 					mParameters.push_back(params);
 				}

+ 168 - 123
CamelotRenderer/Source/CmMesh.cpp

@@ -5,6 +5,14 @@
 #include "CmVector3.h"
 #include "CmDebug.h"
 #include "CmHardwareBufferManager.h"
+#include "CmRenderSystem.h"
+#include "CmRenderSystemManager.h"
+
+#if CM_DEBUG_MODE
+#define THROW_IF_NOT_RENDER_THREAD throwIfNotRenderThread();
+#else
+#define THROW_IF_NOT_RENDER_THREAD 
+#endif
 
 namespace CamelotEngine
 {
@@ -16,6 +24,8 @@ namespace CamelotEngine
 
 	Mesh::~Mesh()
 	{
+		THROW_IF_NOT_RENDER_THREAD;
+
 		if(mVertexData)
 			delete mVertexData;
 
@@ -25,7 +35,134 @@ namespace CamelotEngine
 
 	void Mesh::setMeshData(MeshDataPtr meshData)
 	{
-		mMeshData = meshData;
+		RenderSystemManager::getActive()->queueResourceCommand(boost::bind(&Mesh::setMeshData_internal, this, meshData));
+	}
+
+	void Mesh::setMeshData_internal(MeshDataPtr meshData)
+	{
+		THROW_IF_NOT_RENDER_THREAD;
+
+		if(meshData == nullptr)
+		{
+			CM_EXCEPT(InternalErrorException, "Cannot load mesh. Mesh data is null.");
+		}
+
+		// Submeshes
+		for(UINT32 i = 0; i < meshData->subMeshes.size(); i++)
+			mSubMeshes.push_back(SubMesh(meshData->subMeshes[i].indexOffset, meshData->subMeshes[i].indexCount));
+
+		// Indices
+		mIndexData = new IndexData();
+
+		mIndexData->indexCount = meshData->indexCount;
+		mIndexData->indexBuffer = HardwareBufferManager::instance().createIndexBuffer(
+			HardwareIndexBuffer::IT_16BIT,
+			mIndexData->indexCount, 
+			HardwareBuffer::HBU_STATIC_WRITE_ONLY);
+
+		UINT16* idxData = static_cast<UINT16*>(mIndexData->indexBuffer->lock(HardwareBuffer::HBL_NORMAL));
+
+		for(UINT32 i = 0; i < mIndexData->indexCount; i++)
+		{
+			idxData[i] = (UINT16)meshData->index[i];
+		}
+
+		mIndexData->indexBuffer->unlock();
+
+		// Vertices
+		mVertexData = new VertexData();
+
+		mVertexData->vertexStart = 0;
+		mVertexData->vertexCount = meshData->vertexCount;
+
+		mVertexData->vertexDeclaration = meshData->declaration->clone();
+
+		for(auto iter = meshData->vertexBuffers.begin(); iter != meshData->vertexBuffers.end(); ++iter)
+		{
+			int streamIdx = iter->first; 
+
+			HardwareVertexBufferPtr vertexBuffer = HardwareBufferManager::instance().createVertexBuffer(
+				mVertexData->vertexDeclaration->getVertexSize(streamIdx),
+				mVertexData->vertexCount,
+				HardwareBuffer::HBU_STATIC_WRITE_ONLY);
+
+			mVertexData->vertexBufferBinding->setBinding(streamIdx, vertexBuffer);
+
+			UINT32 vertexSize = vertexBuffer->getVertexSize();
+			UINT8* vertBufferData = static_cast<UINT8*>(vertexBuffer->lock(HardwareBuffer::HBL_NORMAL));
+
+			UINT32 numElements = mVertexData->vertexDeclaration->getElementCount();
+
+			for(UINT32 j = 0; j < numElements; j++)
+			{
+				const VertexElement* element = mVertexData->vertexDeclaration->getElement(j);
+				VertexElementSemantic semantic = element->getSemantic();
+				UINT32 offset = element->getOffset();
+				UINT32 elemSize = element->getSize();
+
+				std::shared_ptr<MeshData::VertexData> vertexData = meshData->vertexBuffers[streamIdx];
+
+				UINT8* source = nullptr;
+				switch(semantic)
+				{
+				case VES_POSITION:
+					if(vertexData->vertex)
+						source = (UINT8*)vertexData->vertex;
+
+					break;
+				case VES_DIFFUSE:
+					if(vertexData->color)
+						source = (UINT8*)vertexData->color;
+
+					break;
+				case VES_NORMAL:
+					if(vertexData->normal)
+						source = (UINT8*)vertexData->normal;	
+
+					break;
+				case VES_TANGENT:
+					if(vertexData->tangent)
+						source = (UINT8*)vertexData->tangent;	
+
+					break;
+				case VES_BITANGENT:
+					if(vertexData->bitangent)
+						source = (UINT8*)vertexData->bitangent;	
+
+					break;
+				case VES_TEXTURE_COORDINATES:
+					if(element->getIndex() == 0)
+					{
+						if(vertexData->uv0)
+							source = (UINT8*)vertexData->uv0;	
+					}
+					else if(element->getIndex() == 1)
+					{
+						if(vertexData->uv1)
+							source = (UINT8*)vertexData->uv1;	
+					}
+
+					break;
+				default:
+					break;
+				}
+
+				if(source != nullptr)
+				{
+					for(UINT32 k = 0; k < mVertexData->vertexCount; k++)
+						memcpy(&vertBufferData[k * vertexSize + offset], &source[k * elemSize], elemSize);
+				}
+				else
+				{
+					LOGWRN("Vertex declaration contains semantic (" + toString(semantic) + ") but mesh doesn't have data for it. Data for the semantic will be zeroed out.");
+
+					for(UINT32 k = 0; k < mVertexData->vertexCount; k++)
+						memset(&vertBufferData[k * vertexSize + offset], 0, elemSize);
+				}
+			}
+
+			vertexBuffer->unlock();
+		}
 	}
 
 	MeshDataPtr Mesh::getMeshData()
@@ -159,132 +296,28 @@ namespace CamelotEngine
 
 		return ro;
 	}
+	void Mesh::initialize()
+	{
+		RenderSystemManager::getActive()->queueResourceCommand(boost::bind(&Mesh::initialize_internal, this));
+	}
 
 	void Mesh::initialize_internal()
 	{
-		if(mMeshData == nullptr)
-		{
-			CM_EXCEPT(InternalErrorException, "Cannot load mesh. Mesh data hasn't been set.");
-		}
-
-		// Submeshes
-		for(UINT32 i = 0; i < mMeshData->subMeshes.size(); i++)
-			mSubMeshes.push_back(SubMesh(mMeshData->subMeshes[i].indexOffset, mMeshData->subMeshes[i].indexCount));
-
-		// Indices
-		mIndexData = new IndexData();
-
-		mIndexData->indexCount = mMeshData->indexCount;
-		mIndexData->indexBuffer = HardwareBufferManager::instance().createIndexBuffer(
-			HardwareIndexBuffer::IT_16BIT,
-			mIndexData->indexCount, 
-			HardwareBuffer::HBU_STATIC_WRITE_ONLY);
-
-		UINT16* idxData = static_cast<UINT16*>(mIndexData->indexBuffer->lock(HardwareBuffer::HBL_NORMAL));
-
-		for(UINT32 i = 0; i < mIndexData->indexCount; i++)
-		{
-			idxData[i] = (UINT16)mMeshData->index[i];
-		}
-
-		mIndexData->indexBuffer->unlock();
-
-		// Vertices
-		mVertexData = new VertexData();
-
-		mVertexData->vertexStart = 0;
-		mVertexData->vertexCount = mMeshData->vertexCount;
-
-		mVertexData->vertexDeclaration = mMeshData->declaration->clone();
-
-		for(auto iter = mMeshData->vertexBuffers.begin(); iter != mMeshData->vertexBuffers.end(); ++iter)
-		{
-			int streamIdx = iter->first; 
-
-			HardwareVertexBufferPtr vertexBuffer = HardwareBufferManager::instance().createVertexBuffer(
-				mVertexData->vertexDeclaration->getVertexSize(streamIdx),
-				mVertexData->vertexCount,
-				HardwareBuffer::HBU_STATIC_WRITE_ONLY);
-
-			mVertexData->vertexBufferBinding->setBinding(streamIdx, vertexBuffer);
-
-			UINT32 vertexSize = vertexBuffer->getVertexSize();
-			UINT8* vertBufferData = static_cast<UINT8*>(vertexBuffer->lock(HardwareBuffer::HBL_NORMAL));
-
-			UINT32 numElements = mVertexData->vertexDeclaration->getElementCount();
-
-			for(UINT32 j = 0; j < numElements; j++)
-			{
-				const VertexElement* element = mVertexData->vertexDeclaration->getElement(j);
-				VertexElementSemantic semantic = element->getSemantic();
-				UINT32 offset = element->getOffset();
-				UINT32 elemSize = element->getSize();
-
-				std::shared_ptr<MeshData::VertexData> vertexData = mMeshData->vertexBuffers[streamIdx];
-
-				UINT8* source = nullptr;
-				switch(semantic)
-				{
-				case VES_POSITION:
-					if(vertexData->vertex)
-						source = (UINT8*)vertexData->vertex;
-
-					break;
-				case VES_DIFFUSE:
-					if(vertexData->color)
-						source = (UINT8*)vertexData->color;
-
-					break;
-				case VES_NORMAL:
-					if(vertexData->normal)
-						source = (UINT8*)vertexData->normal;	
-
-					break;
-				case VES_TANGENT:
-					if(vertexData->tangent)
-						source = (UINT8*)vertexData->tangent;	
-
-					break;
-				case VES_BITANGENT:
-					if(vertexData->bitangent)
-						source = (UINT8*)vertexData->bitangent;	
+		THROW_IF_NOT_RENDER_THREAD;
 
-					break;
-				case VES_TEXTURE_COORDINATES:
-					if(element->getIndex() == 0)
-					{
-						if(vertexData->uv0)
-							source = (UINT8*)vertexData->uv0;	
-					}
-					else if(element->getIndex() == 1)
-					{
-						if(vertexData->uv1)
-							source = (UINT8*)vertexData->uv1;	
-					}
+		// TODO Low priority - Initialize an empty mesh. A better way would be to only initialize the mesh
+		// once we set the proper mesh data (then we don't have to do it twice), but this makes the code less complex.
+		// Consider changing it if there are performance issues.
+		MeshDataPtr meshData = MeshDataPtr(new MeshData());
+		setMeshData_internal(meshData);
 
-					break;
-				default:
-					break;
-				}
-
-				if(source != nullptr)
-				{
-					for(UINT32 k = 0; k < mVertexData->vertexCount; k++)
-						memcpy(&vertBufferData[k * vertexSize + offset], &source[k * elemSize], elemSize);
-				}
-				else
-				{
-					LOGWRN("Vertex declaration contains semantic (" + toString(semantic) + ") but mesh doesn't have data for it. Data for the semantic will be zeroed out.");
-
-					for(UINT32 k = 0; k < mVertexData->vertexCount; k++)
-						memset(&vertBufferData[k * vertexSize + offset], 0, elemSize);
-				}
-			}
-
-			vertexBuffer->unlock();
-		}
+		Resource::initialize_internal();
+	}
 
-		mMeshData = nullptr;
+	void Mesh::throwIfNotRenderThread() const
+	{
+		if(CM_THREAD_CURRENT_ID != RenderSystemManager::getActive()->getRenderThreadId())
+			CM_EXCEPT(InternalErrorException, "Calling an internal texture method from a non-render thread!");
 	}
 
 	/************************************************************************/
@@ -307,6 +340,18 @@ namespace CamelotEngine
 
 	MeshPtr Mesh::create()
 	{
-		return MeshPtr(new Mesh());
+		MeshPtr mesh = MeshPtr(new Mesh());
+		mesh->initialize();
+
+		return mesh;
 	}
-}
+
+	MeshPtr Mesh::createEmpty()
+	{
+		MeshPtr mesh = MeshPtr(new Mesh());
+
+		return mesh;
+	}
+}
+
+#undef THROW_IF_NOT_RENDER_THREAD

+ 7 - 1
CamelotRenderer/Source/CmMeshRTTI.h

@@ -17,9 +17,15 @@ namespace CamelotEngine
 			addReflectablePtrField("mMeshData", 0, &MeshRTTI::getMeshData, &MeshRTTI::setMeshData);
 		}
 
+		virtual void onDeserializationEnded(IReflectable* obj)
+		{
+			Mesh* mesh = static_cast<Mesh*>(obj);
+			mesh->initialize();
+		}
+
 		virtual std::shared_ptr<IReflectable> newRTTIObject() 
 		{
-			return std::shared_ptr<Mesh>(new Mesh());
+			return std::shared_ptr<Mesh>(Mesh::createEmpty());
 		}
 
 		virtual const String& getRTTIName() 

+ 13 - 4
CamelotRenderer/Source/CmResource.cpp

@@ -19,20 +19,29 @@ namespace CamelotEngine
 
 	void Resource::initialize_internal()
 	{
-		mIsInitialized = true;
+		{
+			CM_LOCK_MUTEX(mResourceLoadedMutex);
+			mIsInitialized = true;
+		}	
 
 		CM_THREAD_NOTIFY_ALL(mResourceLoadedCondition);
 	}
 
-	void Resource::waitUntilLoaded()
+	void Resource::waitUntilInitialized()
 	{
 #if CM_DEBUG_MODE
 		if(CM_THREAD_CURRENT_ID == RenderSystemManager::getActive()->getRenderThreadId())
 			CM_EXCEPT(InternalErrorException, "You cannot call this method on the render thread. It will cause a deadlock!");
 #endif
 
-		CM_LOCK_MUTEX_NAMED(mResourceLoadedMutex, lock);
-		CM_THREAD_WAIT(mResourceLoadedCondition, mResourceLoadedMutex, lock);
+		if(!mIsInitialized)
+		{
+			CM_LOCK_MUTEX_NAMED(mResourceLoadedMutex, lock);
+			while(!mIsInitialized)
+			{
+				CM_THREAD_WAIT(mResourceLoadedCondition, mResourceLoadedMutex, lock);
+			}
+		}
 	}
 		
 	RTTITypeBase* Resource::getRTTIStatic()

+ 35 - 1
CamelotRenderer/Source/CmResourceHandle.cpp

@@ -6,6 +6,9 @@
 
 namespace CamelotEngine
 {
+	CM_STATIC_THREAD_SYNCHRONISER_CLASS_INSTANCE(mResourceCreatedCondition, ResourceHandleBase)
+	CM_STATIC_MUTEX_CLASS_INSTANCE(mResourceCreatedMutex, ResourceHandleBase)
+
 	RTTITypeBase* ResourceHandleData::getRTTIStatic()
 	{
 		return ResourceHandleDataRTTI::instance();
@@ -26,6 +29,20 @@ namespace CamelotEngine
 		return (mData->mIsCreated && mData->mPtr != nullptr && mData->mPtr->isInitialized()); 
 	}
 
+	void ResourceHandleBase::waitUntilLoaded() const
+	{
+		if(!mData->mIsCreated)
+		{
+			CM_LOCK_MUTEX_NAMED(mResourceCreatedMutex, lock);
+			while(!mData->mIsCreated)
+			{
+				CM_THREAD_WAIT(mResourceCreatedCondition, mResourceCreatedMutex, lock);
+			}
+		}
+
+		mData->mPtr->waitUntilInitialized();
+	}
+
 	void ResourceHandleBase::resolve(std::shared_ptr<Resource> ptr) 
 	{ 
 		init(ptr);
@@ -48,7 +65,24 @@ namespace CamelotEngine
 		if(mData->mPtr)
 		{
 			mData->mUUID = mData->mPtr->getUUID();
-			mData->mIsCreated = true; 
+		
+			if(!mData->mIsCreated)
+			{
+				CM_LOCK_MUTEX(mResourceCreatedMutex);
+				{
+					mData->mIsCreated = true; 
+				}
+				
+				CM_THREAD_NOTIFY_ALL(mResourceCreatedCondition);
+			}
+		}
+	}
+
+	void ResourceHandleBase::throwIfNotLoaded() const
+	{
+		if(!isLoaded()) 
+		{
+			CM_EXCEPT(InternalErrorException, "Trying to access a resource that hasn't been loaded yet.");
 		}
 	}
 

+ 4 - 5
CamelotRenderer/Source/CmResources.cpp

@@ -223,7 +223,8 @@ namespace CamelotEngine
 
 	void Resources::create(BaseResourceHandle resource, const String& filePath, bool overwrite)
 	{
-		assert(resource.get() != nullptr);
+		if(!resource.isLoaded())
+			resource.waitUntilLoaded();
 
 		if(metaExists_UUID(resource->getUUID()))
 			CM_EXCEPT(InvalidParametersException, "Specified resource already exists.");
@@ -249,10 +250,8 @@ namespace CamelotEngine
 
 	void Resources::save(BaseResourceHandle resource)
 	{
-		// TODO - Check if the resource is fully loaded or not. If its not loaded either wait for it to be loaded,
-		// or break out
-
-		assert(resource.get() != nullptr);
+		if(!resource.isLoaded())
+			resource.waitUntilLoaded();
 
 		if(!metaExists_UUID(resource->getUUID()))
 			CM_EXCEPT(InvalidParametersException, "Cannot find resource meta-data. Please call Resources::create before trying to save the resource.");