Kaynağa Gözat

Refactored CoreObject so no initialization or destruction happens on core thread

Marko Pintera 11 yıl önce
ebeveyn
işleme
67e4ac133c

+ 6 - 0
BansheeCore/Include/BsBlendState.h

@@ -157,6 +157,12 @@ namespace BansheeEngine
 		 */
 		const BlendProperties& getProperties() const;
 
+		/**
+		 * @brief	Returns the default blend state that you may use
+		 * 			when no other is available.
+		 */
+		static const SPtr<BlendStateCore>& getDefault();
+
 	protected:
 		friend class RenderStateCoreManager;
 

+ 20 - 65
BansheeCore/Include/BsCoreObject.h

@@ -23,10 +23,8 @@ namespace BansheeEngine
 		 */
 		enum Flags
 		{
-			CGO_INITIALIZED = 0x01, /**< Object has been fully initialized and may be used. */
-			CGO_INIT_ON_CORE_THREAD = 0x02, /**< Object requires initialization on core thread. */
-			CGO_SCHEDULED_FOR_INIT = 0x04, /**< Object has been scheduled for initialization but core thread has not completed it yet. */
-			CGO_SCHEDULED_FOR_DELETE = 0x08 /**< Object has been scheduled for deletion but core thread has not completed it yet. */
+			CGO_DESTROYED = 0x01, /**< Object has been destroyed and shouldn't be used. */
+			CGO_INIT_ON_CORE_THREAD = 0x02 /**< Object requires initialization on core thread. */
 		};
 
 	public:
@@ -62,13 +60,9 @@ namespace BansheeEngine
 		virtual void initialize();
 
 		/**
-		 * @brief	Returns true if the object has been properly initialized. You are not
-		 * 			allowed to call any methods on the resource until you are sure resource is initialized.
-		 * 			
-		 * @note	Normally CPU objects are initialized on creation and this will never be false, and GPU
-		 * 			objects are initialized when the core thread processes them.
+		 * @brief	Returns true if the object has been destroyed. Destroyed object should not be used.
 		 */
-		bool isInitialized() const { return (mFlags & CGO_INITIALIZED) != 0; }
+		bool isDestroyed() const { return (mFlags & CGO_DESTROYED) != 0; }
 
 		/**
 		 * @brief	Blocks the current thread until the resource is fully initialized.
@@ -94,20 +88,12 @@ namespace BansheeEngine
 		 * @brief	Internal method. Schedules the object to be destroyed, and then deleted.
 		 */
 		template<class T, class MemAlloc>
-		static void _deleteDelayed(CoreObject* obj)
+		static void _delete(CoreObject* obj)
 		{
-			_deleteDelayedInternal(obj);
-
-			if(obj->isInitialized())
-			{
-				std::shared_ptr<CoreObject> thisPtr(obj);
-				obj->_setThisPtr(thisPtr);
+			if (!obj->isDestroyed())
 				obj->destroy();
-			}
-			else
-			{
-				bs_delete<MemAlloc, T>((T*)obj);
-			}
+
+			bs_delete<MemAlloc, T>((T*)obj);
 		}
 
 		/**
@@ -132,29 +118,6 @@ namespace BansheeEngine
 		void syncToCore(CoreAccessor& accessor);
 
 	protected:
-		/**
-		 * @brief	Frees all of the internal resources. All derived classes that have something to free
-		 * 			should do it here instead of their destructor. All derived classes need to call this base method when they're done.
-		 * 			
-		 * @note	For objects with "CGO_INIT_ON_CORE_THREAD" flag this is scheduled to be executed on the core thread, 
-		 * 			so normally you want to destroy all GPU specific resources here.
-		 */
-		virtual void destroyCore();
-
-		/**
-		 * @brief	Initializes all the internal resources of this object. Needs to be called before doing
-		 * 			any operations with the object. All derived classes also need to call this base method.
-		 * 			
-		 * @note	For objects with "CGO_INIT_ON_CORE_THREAD" flag this is scheduled to be executed on the core thread, 
-		 * 			so normally you want to initialize all GPU specific resources here.
-		 */
-		virtual void initializeCore();
-
-		/**
-		 * @brief	Performs some internal checks when an object is being deleted.
-		 */
-		static void _deleteDelayedInternal(CoreObject* obj);
-
 		/**
 		 * @brief	Queues a command to be executed on the core thread, without a return value.
 		 * 			
@@ -162,7 +125,7 @@ namespace BansheeEngine
 		 * 			make sure the object is not deleted before the command executes. Can be null if the 
 		 * 			function is static or global.
 		 */
-		static void queueGpuCommand(std::shared_ptr<CoreObject>& obj, std::function<void()> func);
+		static void queueGpuCommand(const SPtr<CoreObjectCore>& obj, std::function<void()> func);
 
 		/**
 		 * @brief	Queues a command to be executed on the core thread, with a return value in the form of AsyncOp.
@@ -173,15 +136,10 @@ namespace BansheeEngine
 		 * 			make sure the object is not deleted before the command executes. Can be null if the
 		 * 			function is static or global.
 		 */
-		static AsyncOp queueReturnGpuCommand(std::shared_ptr<CoreObject>& obj, std::function<void(AsyncOp&)> func);
+		static AsyncOp queueReturnGpuCommand(const SPtr<CoreObjectCore>& obj, std::function<void(AsyncOp&)> func);
 
-		bool isScheduledToBeInitialized() const { return (mFlags & CGO_SCHEDULED_FOR_INIT) != 0; }
-		bool isScheduledToBeDeleted() const { return (mFlags & CGO_SCHEDULED_FOR_DELETE) != 0; }
 		bool requiresInitOnCoreThread() const { return (mFlags & CGO_INIT_ON_CORE_THREAD) != 0; }
-
-		void setIsInitialized(bool initialized) { mFlags = initialized ? mFlags | CGO_INITIALIZED : mFlags & ~CGO_INITIALIZED; }
-		void setScheduledToBeInitialized(bool scheduled) { mFlags = scheduled ? mFlags | CGO_SCHEDULED_FOR_INIT : mFlags & ~CGO_SCHEDULED_FOR_INIT; }
-		void setScheduledToBeDeleted(bool scheduled) { mFlags = scheduled ? mFlags | CGO_SCHEDULED_FOR_DELETE : mFlags & ~CGO_SCHEDULED_FOR_DELETE; }
+		void setIsDestroyed(bool destroyed) { mFlags = destroyed ? mFlags | CGO_DESTROYED : mFlags & ~CGO_DESTROYED; }
 	private:
 		friend class CoreObjectManager;
 
@@ -190,14 +148,11 @@ namespace BansheeEngine
 		UINT64 mInternalID; // ID == 0 is not a valid ID
 		std::weak_ptr<CoreObject> mThis;
 
-		BS_STATIC_THREAD_SYNCHRONISER(mCoreGpuObjectLoadedCondition)
-		BS_STATIC_MUTEX(mCoreGpuObjectLoadedMutex)
-
 		/**
 		 * @brief	Queues object initialization command on the core thread. The command is added to the
 		 * 			primary core thread queue and will be executed as soon as the core thread is ready.
 		 */
-		static void queueInitializeGpuCommand(std::shared_ptr<CoreObject>& obj);
+		static void queueInitializeGpuCommand(const SPtr<CoreObjectCore>& obj);
 
 		/**
 		 * @brief	Queues object destruction command on the core thread. The command is added to the
@@ -206,17 +161,17 @@ namespace BansheeEngine
 		 *
 		 * @note	It is up to the caller to ensure no other accessors attempt to use this object.
 		 */
-		static void queueDestroyGpuCommand(std::shared_ptr<CoreObject>& obj);
+		static void queueDestroyGpuCommand(const SPtr<CoreObjectCore>& obj);
 
 		/**
 		 * @brief	Helper wrapper method used for queuing commands with no return value on the core thread.
 		 */
-		static void executeGpuCommand(std::shared_ptr<CoreObject> obj, std::function<void()> func);
+		static void executeGpuCommand(const SPtr<CoreObjectCore>& obj, std::function<void()> func);
 
 		/**
 		 * @brief	Helper wrapper method used for queuing commands with a return value on the core thread.
 		 */
-		static void executeReturnGpuCommand(std::shared_ptr<CoreObject> obj, std::function<void(AsyncOp&)> func, AsyncOp& op); 
+		static void executeReturnGpuCommand(const SPtr<CoreObjectCore>& obj, std::function<void(AsyncOp&)> func, AsyncOp& op);
 
 	protected:
 		/************************************************************************/
@@ -289,7 +244,7 @@ namespace BansheeEngine
 	std::shared_ptr<Type> bs_core_ptr(Args &&...args)
 	{
 		return std::shared_ptr<Type>(bs_new<Type, MainAlloc>(std::forward<Args>(args)...),
-			&CoreObject::_deleteDelayed<Type, MainAlloc>, StdAlloc<Type, PtrDataAlloc>());
+			&CoreObject::_delete<Type, MainAlloc>, StdAlloc<Type, PtrDataAlloc>());
 	}
 
 	/**
@@ -302,7 +257,7 @@ namespace BansheeEngine
 	std::shared_ptr<Type> bs_core_ptr(Args &&...args)
 	{
 		return std::shared_ptr<Type>(bs_new<Type, MainAlloc>(std::forward<Args>(args)...),
-			&CoreObject::_deleteDelayed<Type, MainAlloc>, StdAlloc<Type, GenAlloc>());
+			&CoreObject::_delete<Type, MainAlloc>, StdAlloc<Type, GenAlloc>());
 	}
 
 	/**
@@ -315,7 +270,7 @@ namespace BansheeEngine
 	std::shared_ptr<Type> bs_core_ptr(Args &&...args)
 	{
 		return std::shared_ptr<Type>(bs_new<Type, GenAlloc>(std::forward<Args>(args)...),
-			&CoreObject::_deleteDelayed<Type, GenAlloc>, StdAlloc<Type, GenAlloc>());
+			&CoreObject::_delete<Type, GenAlloc>, StdAlloc<Type, GenAlloc>());
 	}
 
 	/**
@@ -327,7 +282,7 @@ namespace BansheeEngine
 	template<class Type, class MainAlloc>
 	std::shared_ptr<Type> bs_core_ptr(Type* data)
 	{
-		return std::shared_ptr<Type>(data, &CoreObject::_deleteDelayed<Type, MainAlloc>, StdAlloc<Type, GenAlloc>());  
+		return std::shared_ptr<Type>(data, &CoreObject::_delete<Type, MainAlloc>, StdAlloc<Type, GenAlloc>());  
 	}
 
 	/**
@@ -339,6 +294,6 @@ namespace BansheeEngine
 	template<class Type, class MainAlloc, class PtrDataAlloc>
 	std::shared_ptr<Type> bs_core_ptr(Type* data)
 	{
-		return std::shared_ptr<Type>(data, &CoreObject::_deleteDelayed<Type, MainAlloc>, StdAlloc<Type, PtrDataAlloc>());  
+		return std::shared_ptr<Type>(data, &CoreObject::_delete<Type, MainAlloc>, StdAlloc<Type, PtrDataAlloc>());  
 	}
 }

+ 33 - 1
BansheeCore/Include/BsCoreObjectCore.h

@@ -16,6 +16,16 @@ namespace BansheeEngine
 	 */
 	class BS_CORE_EXPORT CoreObjectCore
 	{
+	protected:
+		/**
+		 * @brief	Values that represent current state of the object
+		 */
+		enum Flags
+		{
+			CGCO_INITIALIZED = 0x01, /**< Object has been initialized and can be used. */
+			CGCO_SCHEDULED_FOR_INIT = 0x02 /**< Object has been scheduled for initialization but core thread has not completed it yet. */
+		};
+
 	public:
 		CoreObjectCore();
 		virtual ~CoreObjectCore();
@@ -23,7 +33,7 @@ namespace BansheeEngine
 		/**
 		 * @brief	Called on the core thread when the object is first created.
 		 */
-		virtual void initialize() { }
+		virtual void initialize();
 
 		/**
 		 * @brief	Internal method. Sets a shared this pointer to this object. This MUST be called immediately after construction.
@@ -81,7 +91,29 @@ namespace BansheeEngine
 		 */
 		bool isCoreDirty() const { return mCoreDirtyFlags != 0; }
 
+		/**
+		 * @brief	Blocks the current thread until the resource is fully initialized.
+		 * 			
+		 * @note	If you call this without calling initialize first a deadlock will occur.
+		 * 			You should not call this from core thread.
+		 */
+		void synchronize();
+
+		/**
+		 * @brief	Returns true if the object has been properly initialized. You are not
+		 * 			allowed to call any methods on the object until it is initialized.
+		 */
+		bool isInitialized() const { return (mFlags & CGCO_INITIALIZED) != 0; }
+		bool isScheduledToBeInitialized() const { return (mFlags & CGCO_SCHEDULED_FOR_INIT) != 0; }
+
+		void setIsInitialized(bool initialized) { mFlags = initialized ? mFlags | CGCO_INITIALIZED : mFlags & ~CGCO_INITIALIZED; }
+		void setScheduledToBeInitialized(bool scheduled) { mFlags = scheduled ? mFlags | CGCO_SCHEDULED_FOR_INIT : mFlags & ~CGCO_SCHEDULED_FOR_INIT; }
+
 		UINT32 mCoreDirtyFlags;
+		volatile UINT8 mFlags;
 		std::weak_ptr<CoreObjectCore> mThis;
+
+		BS_STATIC_THREAD_SYNCHRONISER(mCoreGpuObjectLoadedCondition)
+		BS_STATIC_MUTEX(mCoreGpuObjectLoadedMutex)
 	};
 }

+ 5 - 0
BansheeCore/Include/BsDepthStencilState.h

@@ -163,6 +163,11 @@ namespace BansheeEngine
 		 */
 		const DepthStencilProperties& getProperties() const;
 
+		/**
+		 * @brief	Returns the default depth stencil state that you may use when no other is available.
+		 */
+		static const SPtr<DepthStencilStateCore>& getDefault();
+
 	protected:
 		friend class RenderStateCoreManager;
 

+ 5 - 0
BansheeCore/Include/BsRasterizerState.h

@@ -138,6 +138,11 @@ namespace BansheeEngine
 		 */
 		const RasterizerProperties& getProperties() const;
 
+		/**
+		 * @brief	Returns the default rasterizer state.
+		 */
+		static const SPtr<RasterizerStateCore>& getDefault();
+
 	protected:
 		friend class RenderStateCoreManager;
 

+ 26 - 0
BansheeCore/Include/BsRenderStateManager.h

@@ -112,6 +112,32 @@ namespace BansheeEngine
 		 */
 		SPtr<BlendStateCore> createBlendState(const BLEND_STATE_DESC& desc) const;
 
+		/**
+		 * @brief	Gets a sampler state initialized with default options.
+		 */
+		const SPtr<SamplerStateCore>& getDefaultSamplerState() const;
+
+		/**
+		 * @brief	Gets a blend state initialized with default options.
+		 */
+		const SPtr<BlendStateCore>& getDefaultBlendState() const;
+
+		/**
+		 * @brief	Gets a rasterizer state initialized with default options.
+		 */
+		const SPtr<RasterizerStateCore>& getDefaultRasterizerState() const;
+
+		/**
+		 * @brief	Gets a depth stencil state initialized with default options.
+		 */
+		const SPtr<DepthStencilStateCore>& getDefaultDepthStencilState() const;
+
+	private:
+		mutable SPtr<SamplerStateCore> mDefaultSamplerState;
+		mutable SPtr<BlendStateCore> mDefaultBlendState;
+		mutable SPtr<RasterizerStateCore> mDefaultRasterizerState;
+		mutable SPtr<DepthStencilStateCore> mDefaultDepthStencilState;
+
 	protected:
 		friend class SamplerState;
 		friend class BlendState;

+ 5 - 0
BansheeCore/Include/BsSamplerState.h

@@ -111,6 +111,11 @@ namespace BansheeEngine
 		 */
 		const SamplerProperties& getProperties() const;
 
+		/**
+		 * @brief	Returns the default sampler state.
+		 */
+		static const SPtr<SamplerStateCore>& getDefault();
+
 	protected:
 		friend class RenderStateCoreManager;
 

+ 5 - 0
BansheeCore/Source/BsBlendState.cpp

@@ -77,6 +77,11 @@ namespace BansheeEngine
 		return mProperties;
 	}
 
+	const SPtr<BlendStateCore>& BlendStateCore::getDefault()
+	{
+		return RenderStateCoreManager::instance().getDefaultBlendState();
+	}
+
 	BlendState::BlendState(const BLEND_STATE_DESC& desc)
 		:mProperties(desc)
 	{ }

+ 26 - 112
BansheeCore/Source/BsCoreObject.cpp

@@ -10,9 +10,6 @@ using namespace std::placeholders;
 
 namespace BansheeEngine
 {
-	BS_STATIC_THREAD_SYNCHRONISER_CLASS_INSTANCE(mCoreGpuObjectLoadedCondition, CoreObject)
-	BS_STATIC_MUTEX_CLASS_INSTANCE(mCoreGpuObjectLoadedMutex, CoreObject)
-
 	CoreObject::CoreObject(bool initializeOnRenderThread)
 		: mFlags(0), mInternalID(0), mCoreDirtyFlags(0xFFFFFFFF)
 	{
@@ -22,7 +19,7 @@ namespace BansheeEngine
 
 	CoreObject::~CoreObject() 
 	{
-		if(isInitialized())
+		if(!isDestroyed())
 		{
 			// Object must be released with destroy() otherwise engine can still try to use it, even if it was destructed
 			// (e.g. if an object has one of its methods queued in a command queue, and is destructed, you will be accessing invalid memory)
@@ -42,106 +39,45 @@ namespace BansheeEngine
 
 	void CoreObject::destroy()
 	{
+		setIsDestroyed(true);
+
 		if(requiresInitOnCoreThread())
 		{
-			setScheduledToBeDeleted(true);
+			assert(BS_THREAD_CURRENT_ID != CoreThread::instance().getCoreThreadId() && "Cannot destroy sim thead object from core thread.");
 
-			if(BS_THREAD_CURRENT_ID == CoreThread::instance().getCoreThreadId())
-				mThis.lock()->destroyCore();
-			else
-				queueDestroyGpuCommand(mThis.lock());
+			queueDestroyGpuCommand(mCoreSpecific);
 		}
-		else
-		{
-			destroyCore();
-		}
-	}
-
-	void CoreObject::destroyCore()
-	{
-#if BS_DEBUG_MODE
-		if(!isInitialized())
-			BS_EXCEPT(InternalErrorException, "Trying to destroy an object that is already destroyed (or it never was initialized).");
-#endif
 
 		mCoreSpecific = nullptr;
-
-		setIsInitialized(false);
 	}
 
 	void CoreObject::initialize()
 	{
-#if BS_DEBUG_MODE
-		if(isInitialized() || isScheduledToBeInitialized())
-			BS_EXCEPT(InternalErrorException, "Trying to initialize an object that is already initialized.");
-#endif
-
 		mCoreSpecific = createCore();
 
-		if(requiresInitOnCoreThread())
-		{
-			setScheduledToBeInitialized(true);
-
-			if(BS_THREAD_CURRENT_ID == CoreThread::instance().getCoreThreadId())
-				mThis.lock()->initializeCore();
-			else
-				queueInitializeGpuCommand(mThis.lock());
-		}
-		else
-		{
-			initializeCore();
-		}
-	}
-
-	void CoreObject::initializeCore()
-	{
 		if (mCoreSpecific != nullptr)
-			mCoreSpecific->initialize();
-
-		if(requiresInitOnCoreThread())
 		{
+			if (requiresInitOnCoreThread())
 			{
-				BS_LOCK_MUTEX(mCoreGpuObjectLoadedMutex);
-				setIsInitialized(true);
-			}	
+				mCoreSpecific->setScheduledToBeInitialized(true);
 
-			setScheduledToBeInitialized(false);
+				assert(BS_THREAD_CURRENT_ID != CoreThread::instance().getCoreThreadId() && "Cannot initialize sim thread object from core thread.");
 
-			BS_THREAD_NOTIFY_ALL(mCoreGpuObjectLoadedCondition);
-		}
-		else
-		{
-			setIsInitialized(true);
-		}
-	}
-
-	void CoreObject::synchronize()
-	{
-		if(!isInitialized())
-		{
-			if(requiresInitOnCoreThread())
-			{
-#if BS_DEBUG_MODE
-				if(BS_THREAD_CURRENT_ID == CoreThread::instance().getCoreThreadId())
-					BS_EXCEPT(InternalErrorException, "You cannot call this method on the core thread. It will cause a deadlock!");
-#endif
-
-				BS_LOCK_MUTEX_NAMED(mCoreGpuObjectLoadedMutex, lock);
-				while(!isInitialized())
-				{
-					if(!isScheduledToBeInitialized())
-						BS_EXCEPT(InternalErrorException, "Attempting to wait until initialization finishes but object is not scheduled to be initialized.");
-
-					BS_THREAD_WAIT(mCoreGpuObjectLoadedCondition, mCoreGpuObjectLoadedMutex, lock);
-				}
+				queueInitializeGpuCommand(mCoreSpecific);
 			}
 			else
 			{
-				BS_EXCEPT(InternalErrorException, "Attempting to wait until initialization finishes but object is not scheduled to be initialized.");
+				mCoreSpecific->initialize();
 			}
 		}
 	}
 
+	void CoreObject::synchronize()
+	{
+		if (mCoreSpecific != nullptr)
+			mCoreSpecific->synchronize();
+	}
+
 	void CoreObject::syncToCore(CoreAccessor& accessor)
 	{
 		if (!isCoreDirty())
@@ -184,29 +120,7 @@ namespace BansheeEngine
 		mThis = ptrThis;
 	}
 
-	void CoreObject::_deleteDelayedInternal(CoreObject* obj)
-	{
-		assert(obj != nullptr);
-
-		// This method usually gets called automatically by the shared pointer when all references are released. The process:
-		// - If the object wasn't initialized delete it right away
-		// - Otherwise:
-		//  - We re-create the reference to the object by setting mThis pointer
-		//  - We queue the object to be destroyed so all of its GPU resources may be released on the core thread
-		//    - destroy() makes sure it keeps a reference of mThis so object isn't deleted
-		//    - Once the destroy() finishes the reference is removed and the default shared_ptr deleter is called
-
-#if BS_DEBUG_MODE
-		if(obj->isScheduledToBeInitialized())
-		{
-			BS_EXCEPT(InternalErrorException, "Object scheduled to be initialized, yet it's being deleted. " \
-				"By design objects queued in the command queue should always have a reference count >= 1, therefore never be deleted " \
-				"while still in the queue.");
-		}
-#endif
-	}
-
-	void CoreObject::queueGpuCommand(std::shared_ptr<CoreObject>& obj, std::function<void()> func)
+	void CoreObject::queueGpuCommand(const SPtr<CoreObjectCore>& obj, std::function<void()> func)
 	{
 		// We call another internal method and go through an additional layer of abstraction in order to keep an active
 		// reference to the obj (saved in the bound function).
@@ -215,36 +129,36 @@ namespace BansheeEngine
 		gCoreAccessor().queueCommand(std::bind(&CoreObject::executeGpuCommand, obj, func));
 	}
 
-	AsyncOp CoreObject::queueReturnGpuCommand(std::shared_ptr<CoreObject>& obj, std::function<void(AsyncOp&)> func)
+	AsyncOp CoreObject::queueReturnGpuCommand(const SPtr<CoreObjectCore>& obj, std::function<void(AsyncOp&)> func)
 	{
 		// See queueGpuCommand
 		return gCoreAccessor().queueReturnCommand(std::bind(&CoreObject::executeReturnGpuCommand, obj, func, _1));
 	}
 
-	void CoreObject::queueInitializeGpuCommand(std::shared_ptr<CoreObject>& obj)
+	void CoreObject::queueInitializeGpuCommand(const SPtr<CoreObjectCore>& obj)
 	{
-		std::function<void()> func = std::bind(&CoreObject::initializeCore, obj.get());
+		std::function<void()> func = std::bind(&CoreObjectCore::initialize, obj.get());
 
 		CoreThread::instance().queueCommand(std::bind(&CoreObject::executeGpuCommand, obj, func));
 	}
 
-	void CoreObject::queueDestroyGpuCommand(std::shared_ptr<CoreObject>& obj)
+	void CoreObject::queueDestroyGpuCommand(const SPtr<CoreObjectCore>& obj)
 	{
-		std::function<void()> func = std::bind(&CoreObject::destroyCore, obj.get());
+		std::function<void()> func = [&](){}; // Do nothing function. We just need the shared pointer to stay alive until it reaches the core thread
 
 		gCoreAccessor().queueCommand(std::bind(&CoreObject::executeGpuCommand, obj, func));
 	}
 
-	void CoreObject::executeGpuCommand(std::shared_ptr<CoreObject> obj, std::function<void()> func)
+	void CoreObject::executeGpuCommand(const SPtr<CoreObjectCore>& obj, std::function<void()> func)
 	{
-		volatile std::shared_ptr<CoreObject> objParam = obj; // Makes sure obj isn't optimized out?
+		volatile std::shared_ptr<CoreObjectCore> objParam = obj; // Makes sure obj isn't optimized out?
 
 		func();
 	}
 
-	void CoreObject::executeReturnGpuCommand(std::shared_ptr<CoreObject> obj, std::function<void(AsyncOp&)> func, AsyncOp& op)
+	void CoreObject::executeReturnGpuCommand(const SPtr<CoreObjectCore>& obj, std::function<void(AsyncOp&)> func, AsyncOp& op)
 	{
-		volatile std::shared_ptr<CoreObject> objParam = obj; // Makes sure obj isn't optimized out?
+		volatile std::shared_ptr<CoreObjectCore> objParam = obj; // Makes sure obj isn't optimized out?
 
 		func(op);
 	}

+ 36 - 1
BansheeCore/Source/BsCoreObjectCore.cpp

@@ -3,8 +3,11 @@
 
 namespace BansheeEngine
 {
+	BS_STATIC_THREAD_SYNCHRONISER_CLASS_INSTANCE(mCoreGpuObjectLoadedCondition, CoreObjectCore)
+		BS_STATIC_MUTEX_CLASS_INSTANCE(mCoreGpuObjectLoadedMutex, CoreObjectCore)
+
 	CoreObjectCore::CoreObjectCore()
-		:mCoreDirtyFlags(0xFFFFFFFF)
+		:mCoreDirtyFlags(0xFFFFFFFF), mFlags(0)
 	{ }
 
 	CoreObjectCore::~CoreObjectCore()
@@ -12,6 +15,38 @@ namespace BansheeEngine
 		THROW_IF_NOT_CORE_THREAD;
 	}
 
+	void CoreObjectCore::initialize()
+	{
+		{
+			BS_LOCK_MUTEX(mCoreGpuObjectLoadedMutex);
+			setIsInitialized(true);
+		}
+
+		setScheduledToBeInitialized(false);
+
+		BS_THREAD_NOTIFY_ALL(mCoreGpuObjectLoadedCondition);
+	}
+
+	void CoreObjectCore::synchronize()
+	{
+		if (!isInitialized())
+		{
+#if BS_DEBUG_MODE
+			if (BS_THREAD_CURRENT_ID == CoreThread::instance().getCoreThreadId())
+				BS_EXCEPT(InternalErrorException, "You cannot call this method on the core thread. It will cause a deadlock!");
+#endif
+
+			BS_LOCK_MUTEX_NAMED(mCoreGpuObjectLoadedMutex, lock);
+			while (!isInitialized())
+			{
+				if (!isScheduledToBeInitialized())
+					BS_EXCEPT(InternalErrorException, "Attempting to wait until initialization finishes but object is not scheduled to be initialized.");
+
+				BS_THREAD_WAIT(mCoreGpuObjectLoadedCondition, mCoreGpuObjectLoadedMutex, lock);
+			}
+		}
+	}
+
 	void CoreObjectCore::_setThisPtr(std::shared_ptr<CoreObjectCore> ptrThis)
 	{
 		mThis = ptrThis;

+ 3 - 3
BansheeCore/Source/BsCoreRenderer.cpp

@@ -73,17 +73,17 @@ namespace BansheeEngine
 		if (pass->getBlendState() != nullptr)
 			rs.setBlendState(pass->getBlendState());
 		else
-			rs.setBlendState(BlendState::getDefault()->getCore());
+			rs.setBlendState(BlendStateCore::getDefault());
 
 		if (pass->getDepthStencilState() != nullptr)
 			rs.setDepthStencilState(pass->getDepthStencilState(), pass->getStencilRefValue());
 		else
-			rs.setDepthStencilState(DepthStencilState::getDefault()->getCore(), pass->getStencilRefValue());
+			rs.setDepthStencilState(DepthStencilStateCore::getDefault(), pass->getStencilRefValue());
 
 		if (pass->getRasterizerState() != nullptr)
 			rs.setRasterizerState(pass->getRasterizerState());
 		else
-			rs.setRasterizerState(RasterizerState::getDefault()->getCore());
+			rs.setRasterizerState(RasterizerStateCore::getDefault());
 	}
 
 	void CoreRenderer::draw(const SPtr<MeshCoreBase>& mesh, const SubMesh& subMesh)

+ 3 - 12
BansheeCore/Source/BsCoreThread.cpp

@@ -162,14 +162,9 @@ namespace BansheeEngine
 
 	AsyncOp CoreThread::queueReturnCommand(std::function<void(AsyncOp&)> commandCallback, bool blockUntilComplete)
 	{
-		AsyncOp op;
-
-		if(BS_THREAD_CURRENT_ID == getCoreThreadId())
-		{
-			commandCallback(op); // Execute immediately
-			return op;
-		}
+		assert(BS_THREAD_CURRENT_ID != getCoreThreadId() && "Cannot queue commands on the core thread for the core thread");
 
+		AsyncOp op;
 		UINT32 commandId = -1;
 		{
 			BS_LOCK_MUTEX(mCommandQueueMutex);
@@ -193,11 +188,7 @@ namespace BansheeEngine
 
 	void CoreThread::queueCommand(std::function<void()> commandCallback, bool blockUntilComplete)
 	{
-		if(BS_THREAD_CURRENT_ID == getCoreThreadId())
-		{
-			commandCallback(); // Execute immediately
-			return;
-		}
+		assert(BS_THREAD_CURRENT_ID != getCoreThreadId() && "Cannot queue commands on the core thread for the core thread");
 
 		UINT32 commandId = -1;
 		{

+ 5 - 0
BansheeCore/Source/BsDepthStencilState.cpp

@@ -24,6 +24,11 @@ namespace BansheeEngine
 		return mProperties;
 	}
 
+	const SPtr<DepthStencilStateCore>& DepthStencilStateCore::getDefault()
+	{
+		return RenderStateCoreManager::instance().getDefaultDepthStencilState();
+	}
+
 	DepthStencilState::DepthStencilState(const DEPTH_STENCIL_STATE_DESC& desc)
 		:mProperties(desc)
 	{

+ 2 - 2
BansheeCore/Source/BsMeshHeap.cpp

@@ -660,7 +660,7 @@ namespace BansheeEngine
 
 		mMeshes[meshIdx] = transientMeshPtr;
 
-		queueGpuCommand(getThisPtr(), std::bind(&MeshHeapCore::alloc, getCore().get(), transientMeshPtr->getCore(), meshData));
+		queueGpuCommand(getCore(), std::bind(&MeshHeapCore::alloc, getCore().get(), transientMeshPtr->getCore(), meshData));
 
 		return transientMeshPtr;
 	}
@@ -674,7 +674,7 @@ namespace BansheeEngine
 		mesh->markAsDestroyed();
 		mMeshes.erase(iterFind);
 
-		queueGpuCommand(getThisPtr(), std::bind(&MeshHeapCore::dealloc, getCore().get(), mesh->getCore()));
+		queueGpuCommand(getCore(), std::bind(&MeshHeapCore::dealloc, getCore().get(), mesh->getCore()));
 	}
 
 	SPtr<MeshHeapCore> MeshHeap::getCore() const

+ 5 - 0
BansheeCore/Source/BsRasterizerState.cpp

@@ -21,6 +21,11 @@ namespace BansheeEngine
 		return mProperties;
 	}
 
+	const SPtr<RasterizerStateCore>& RasterizerStateCore::getDefault()
+	{
+		return RenderStateCoreManager::instance().getDefaultRasterizerState();
+	}
+
 	RasterizerState::RasterizerState(const RASTERIZER_STATE_DESC& desc)
 		: mProperties(desc)
 	{

+ 32 - 0
BansheeCore/Source/BsRenderStateManager.cpp

@@ -142,6 +142,38 @@ namespace BansheeEngine
 		return blendState;
 	}
 
+	const SPtr<SamplerStateCore>& RenderStateCoreManager::getDefaultSamplerState() const
+	{
+		if (mDefaultSamplerState == nullptr)
+			mDefaultSamplerState = createSamplerState(SAMPLER_STATE_DESC());
+
+		return mDefaultSamplerState;
+	}
+
+	const SPtr<BlendStateCore>& RenderStateCoreManager::getDefaultBlendState() const
+	{
+		if (mDefaultBlendState == nullptr)
+			mDefaultBlendState = createBlendState(BLEND_STATE_DESC());
+
+		return mDefaultBlendState;
+	}
+
+	const SPtr<RasterizerStateCore>& RenderStateCoreManager::getDefaultRasterizerState() const
+	{
+		if (mDefaultRasterizerState == nullptr)
+			mDefaultRasterizerState = createRasterizerState(RASTERIZER_STATE_DESC());
+
+		return mDefaultRasterizerState;
+	}
+
+	const SPtr<DepthStencilStateCore>& RenderStateCoreManager::getDefaultDepthStencilState() const
+	{
+		if (mDefaultDepthStencilState == nullptr)
+			mDefaultDepthStencilState = createDepthStencilState(DEPTH_STENCIL_STATE_DESC());
+
+		return mDefaultDepthStencilState;
+	}
+
 	SPtr<SamplerStateCore> RenderStateCoreManager::createSamplerStateInternal(const SAMPLER_STATE_DESC& desc) const
 	{
 		SPtr<SamplerStateCore> samplerState = bs_shared_ptr<SamplerStateCore, GenAlloc>(new (bs_alloc<SamplerStateCore>()) SamplerStateCore(desc));

+ 5 - 0
BansheeCore/Source/BsSamplerState.cpp

@@ -42,6 +42,11 @@ namespace BansheeEngine
 		return mProperties;
 	}
 
+	const SPtr<SamplerStateCore>& SamplerStateCore::getDefault()
+	{
+		return RenderStateCoreManager::instance().getDefaultSamplerState();
+	}
+
 	SamplerState::SamplerState(const SAMPLER_STATE_DESC& desc)
 		:mProperties(desc)
 	{

+ 1 - 1
BansheeD3D11RenderSystem/Source/BsD3D11RenderAPI.cpp

@@ -567,7 +567,7 @@ namespace BansheeEngine
 			SPtr<SamplerStateCore>& samplerState = bindableParams->getSamplerState(iter->second.slot);
 
 			if(samplerState == nullptr)
-				setSamplerState(gptype, iter->second.slot, SamplerState::getDefault()->getCore());
+				setSamplerState(gptype, iter->second.slot, SamplerStateCore::getDefault());
 			else
 				setSamplerState(gptype, iter->second.slot, samplerState);
 		}

+ 1 - 1
BansheeD3D9RenderSystem/Source/BsD3D9RenderAPI.cpp

@@ -280,7 +280,7 @@ namespace BansheeEngine
 		{
 			SPtr<SamplerStateCore> samplerState = bindableParams->getSamplerState(iter->second.slot);
 			if(samplerState == nullptr)
-				setSamplerState(gptype, iter->second.slot, SamplerState::getDefault()->getCore());
+				setSamplerState(gptype, iter->second.slot, SamplerStateCore::getDefault());
 			else
 				setSamplerState(gptype, iter->second.slot, samplerState);
 		}

+ 1 - 1
BansheeD3D9RenderSystem/Source/BsD3D9RenderWindowManager.cpp

@@ -22,7 +22,7 @@ namespace BansheeEngine
 
 		D3D9RenderWindow* window = new (bs_alloc<D3D9RenderWindow, PoolAlloc>()) D3D9RenderWindow(desc, mRenderSystem->getInstanceHandle());
 
-		return RenderWindowPtr(window, &CoreObject::_deleteDelayed<D3D9RenderWindow, PoolAlloc>);
+		return RenderWindowPtr(window, &CoreObject::_delete<D3D9RenderWindow, PoolAlloc>);
 	}
 
 	D3D9RenderWindowCoreManager::D3D9RenderWindowCoreManager(D3D9RenderAPI* renderSystem)

+ 2 - 2
BansheeEditor/Source/BsDockManager.cpp

@@ -459,10 +459,10 @@ namespace BansheeEngine
 		float invViewportWidth = 1.0f / (viewport->getWidth() * 0.5f);
 		float invViewportHeight = 1.0f / (viewport->getHeight() * 0.5f);
 
-		if(mDropOverlayMesh == nullptr || !mDropOverlayMesh.isLoaded() || !mDropOverlayMesh->isInitialized())
+		if(!mDropOverlayMesh.isLoaded())
 			return;
 
-		if(mDropOverlayMat == nullptr || !mDropOverlayMat.isLoaded() || !mDropOverlayMat->isInitialized())
+		if(!mDropOverlayMat.isLoaded())
 			return;
 
 		mDropOverlayMat->setFloat("invViewportWidth", invViewportWidth);

+ 1 - 1
BansheeEditorExec/BsEditorExec.cpp

@@ -11,7 +11,7 @@ int CALLBACK WinMain(
 	_In_  int nCmdShow
 	)
 {
-	EditorApplication::startUp(RenderSystemPlugin::OpenGL);
+	EditorApplication::startUp(RenderSystemPlugin::DX11);
 	EditorApplication::instance().runMainLoop();
 	EditorApplication::shutDown();
 

+ 0 - 2
BansheeGLRenderSystem/Source/BsGLMultiRenderTexture.cpp

@@ -70,8 +70,6 @@ namespace BansheeEngine
 		{
 			mFB->unbindDepthStencil();
 		}
-
-		MultiRenderTextureCore::initialize();
 	}
 
 	void GLMultiRenderTextureCore::getCustomAttribute(const String& name, void* pData) const

+ 1 - 1
BansheeGLRenderSystem/Source/BsGLRenderAPI.cpp

@@ -281,7 +281,7 @@ namespace BansheeEngine
 			SPtr<SamplerStateCore>& samplerState = bindableParams->getSamplerState(iter->second.slot);
 
 			if(samplerState == nullptr)
-				setSamplerState(gptype, iter->second.slot, SamplerState::getDefault()->getCore());
+				setSamplerState(gptype, iter->second.slot, SamplerStateCore::getDefault());
 			else
 				setSamplerState(gptype, iter->second.slot, samplerState);
 

+ 1 - 1
BansheeGLRenderSystem/Source/BsWin32GLSupport.cpp

@@ -36,7 +36,7 @@ namespace BansheeEngine
 		}
 
 		Win32Window* window = new (bs_alloc<Win32Window, PoolAlloc>()) Win32Window(desc, *this);
-		return RenderWindowPtr(window, &CoreObject::_deleteDelayed<Win32Window, PoolAlloc>);
+		return RenderWindowPtr(window, &CoreObject::_delete<Win32Window, PoolAlloc>);
 	}
 
 	SPtr<RenderWindowCore> Win32GLSupport::newWindowCore(RENDER_WINDOW_DESC& desc)

+ 0 - 10
TODO.txt

@@ -2,20 +2,10 @@
 
 Renderable TODO:
 
- - Restore RenderableElement (it was in RenderableProxy files)
- - I need to update Renderable with world transform
-   - I've removed SceneObject dirty flags so that is no longer done anywhere
-   - RenderableHandler should keep track of world transform
-     - Also I should keep track of SceneObject active state to hide/show the object as needed
-   - Make sure to call gSceneManager().updateRenderableTransforms(); BEFORE syncing
-     and not like now when it's called in renderAll after sync
-
 Disallow CoreObject creation from core thread
  - Add asserts in CoreObject::destroy and CoreObject::initialize
  - Possibly also add asserts to CoreThread::queueCommand and CoreThread::queueReturnCommand
 
-Refactor RenderSystem and CoreThreadAccessor methods (move them to RenderSystem and make current RenderSystem a RenderSystemCore)
-
  New issues:
   - Since Mesh refactor when I select/deselect a gizmo the entire render texture flashes white for one frame (might be related to RT refactor instead)