2
0
Эх сурвалжийг харах

Added FPS limiter for editor
More accurate core thread profiling
Fixed build scripts so they work with VS2015

BearishSun 10 жил өмнө
parent
commit
7f8ed5478c

+ 6 - 6
BansheeCore/BansheeCore.vcxproj

@@ -154,7 +154,7 @@
     <Link>
       <SubSystem>NotSet</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
-      <AdditionalDependencies>BansheeUtility.lib;nvtt.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>BansheeUtility.lib;nvtt.lib;Winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalLibraryDirectories>../lib/x86/$(Configuration);../Dependencies/lib/x86/Debug</AdditionalLibraryDirectories>
       <ImportLibrary>..\lib\x86\$(Configuration)\$(TargetName).lib</ImportLibrary>
     </Link>
@@ -176,7 +176,7 @@
     <Link>
       <SubSystem>NotSet</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
-      <AdditionalDependencies>BansheeUtility.lib;nvtt.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>BansheeUtility.lib;nvtt.lib;Winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalLibraryDirectories>../lib/$(Platform)/$(Configuration);../Dependencies/lib/x64/Debug</AdditionalLibraryDirectories>
       <ImportLibrary>..\lib\$(Platform)\$(Configuration)\$(TargetName).lib</ImportLibrary>
     </Link>
@@ -204,7 +204,7 @@
       <GenerateDebugInformation>false</GenerateDebugInformation>
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
-      <AdditionalDependencies>BansheeUtility.lib;nvtt.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>BansheeUtility.lib;nvtt.lib;Winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalLibraryDirectories>../lib/x86/$(Configuration);../Dependencies/lib/x86/Release</AdditionalLibraryDirectories>
       <ImportLibrary>..\lib\x86\$(Configuration)\$(TargetName).lib</ImportLibrary>
     </Link>
@@ -233,7 +233,7 @@
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
-      <AdditionalDependencies>BansheeUtility.lib;nvtt.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>BansheeUtility.lib;nvtt.lib;Winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalLibraryDirectories>../lib/x86/$(Configuration);../Dependencies/lib/x86/DebugRelease</AdditionalLibraryDirectories>
       <ImportLibrary>..\lib\x86\$(Configuration)\$(TargetName).lib</ImportLibrary>
     </Link>
@@ -261,7 +261,7 @@
       <GenerateDebugInformation>false</GenerateDebugInformation>
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
-      <AdditionalDependencies>BansheeUtility.lib;nvtt.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>BansheeUtility.lib;nvtt.lib;Winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalLibraryDirectories>../lib/$(Platform)/$(Configuration);../Dependencies/lib/x64/Release</AdditionalLibraryDirectories>
       <ImportLibrary>..\lib\$(Platform)\$(Configuration)\$(TargetName).lib</ImportLibrary>
     </Link>
@@ -290,7 +290,7 @@
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
-      <AdditionalDependencies>BansheeUtility.lib;nvtt.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>BansheeUtility.lib;nvtt.lib;Winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalLibraryDirectories>../lib/$(Platform)/$(Configuration);../Dependencies/lib/x64/DebugRelease</AdditionalLibraryDirectories>
       <ImportLibrary>..\lib\$(Platform)\$(Configuration)\$(TargetName).lib</ImportLibrary>
     </Link>

+ 6 - 0
BansheeCore/Include/BsCoreApplication.h

@@ -50,6 +50,9 @@ namespace BansheeEngine
 			/**	Stops a (infinite) main loop from running. The loop will complete its current cycle before stopping. */
 			void stopMainLoop();
 
+			/** Changes the maximum FPS the application is allowed to run in. Zero means unlimited. */
+			void setFPSLimit(UINT32 limit);
+
 			/**
 			 * Issues a request for the application to close. Application may choose to ignore the request depending on the
 			 * circumstances and the implementation.
@@ -111,6 +114,9 @@ namespace BansheeEngine
 		RenderWindowPtr mPrimaryWindow;
 		START_UP_DESC mStartUpDesc;
 
+		UINT64 mFrameStep; // Microseconds
+		UINT64 mLastFrameTime; // Microseconds
+
 		DynLib* mRendererPlugin;
 
 		Map<DynLib*, UpdatePluginFunc> mPluginUpdateFunctions;

+ 10 - 0
BansheeCore/Include/BsMeshData.h

@@ -230,6 +230,16 @@ namespace BansheeEngine
 		static MeshDataPtr combine(const Vector<MeshDataPtr>& elements, const Vector<Vector<SubMesh>>& allSubMeshes,
 			Vector<SubMesh>& subMeshes);
 
+		/**
+		 * Constructs a new object that can hold number of vertices described by the provided vertex data description. As 
+		 * well as a number of indices of the provided type.
+		 */
+		static MeshDataPtr create(UINT32 numVertices, UINT32 numIndexes, const VertexDataDescPtr& vertexData, 
+			IndexType indexType = IT_32BIT)
+		{
+			return bs_shared_ptr_new<MeshData>(numVertices, numIndexes, vertexData, indexType);
+		}
+
 	protected:
 		/**	Returns the size of the internal buffer in bytes. */
 		UINT32 getInternalBufferSize() const override;

+ 10 - 0
BansheeCore/Include/BsPlatform.h

@@ -304,6 +304,16 @@ namespace BansheeEngine
 		 */
 		static void resetNonClientAreas(const RenderWindowCore& window);
 
+		/** 
+		 * Causes the current thread to pause execution for the specified amount of time.
+		 *
+		 * @param[in]	duration	Duration in milliseconds. Providing zero will give up the current time-slice.
+		 *
+		 * @note	This method relies on timer granularity being set to 1 millisecond. If it is not, you can expect
+		 *			this method to potentially take significantly longer if you are providing it with low ms values (<10).
+		 */
+		static void sleep(UINT32 duration);
+
 		/**
 		 * Creates a drop target that you can use for tracking OS drag and drop operations performed over a certain area 
 		 * on the specified window.

+ 3 - 0
BansheeCore/Include/BsRenderTexture.h

@@ -63,6 +63,9 @@ namespace BansheeEngine
 		/** @copydoc CoreObjectCore::initialize */
 		virtual void initialize();
 
+		/** @copydoc TextureCoreManager::createRenderTexture(const RENDER_TEXTURE_DESC&) */
+		static SPtr<RenderTextureCore> create(const RENDER_TEXTURE_CORE_DESC& desc);
+
 		/**
 		 * Returns a color surface texture you may bind as an input to an GPU program.
 		 *

+ 39 - 4
BansheeCore/Source/BsCoreApplication.cpp

@@ -47,8 +47,8 @@
 namespace BansheeEngine
 {
 	CoreApplication::CoreApplication(START_UP_DESC desc)
-		:mPrimaryWindow(nullptr), mIsFrameRenderingFinished(true), mRunMainLoop(false), 
-		mRendererPlugin(nullptr), mSimThreadId(BS_THREAD_CURRENT_ID), mStartUpDesc(desc)
+		:mPrimaryWindow(nullptr), mIsFrameRenderingFinished(true), mRunMainLoop(false), mLastFrameTime(0),
+		mRendererPlugin(nullptr), mSimThreadId(BS_THREAD_CURRENT_ID), mStartUpDesc(desc), mFrameStep(16666)
 	{ }
 
 	CoreApplication::~CoreApplication()
@@ -161,8 +161,37 @@ namespace BansheeEngine
 	{
 		mRunMainLoop = true;
 
+		gCoreThread().queueCommand(std::bind(&CoreApplication::beginCoreProfiling, this));
 		while(mRunMainLoop)
 		{
+			// Limit FPS if needed
+			if (mFrameStep > 0)
+			{
+				UINT64 currentTime = gTime().getTimePrecise();
+				UINT64 nextFrameTime = mLastFrameTime + mFrameStep;
+				while (nextFrameTime > currentTime)
+				{
+					UINT32 waitTime = (UINT32)(nextFrameTime - currentTime);
+
+					// If waiting for longer, sleep
+					if (waitTime >= 2000)
+					{
+						Platform::sleep(waitTime / 1000);
+						currentTime = gTime().getTimePrecise();
+					}
+					else
+					{
+						// Otherwise we just spin, sleep timer granularity is too low and we might end up wasting a 
+						// millisecond otherwise. 
+						// Note: For mobiles where power might be more important than input latency, consider using sleep.
+						while(nextFrameTime > currentTime)
+							currentTime = gTime().getTimePrecise();
+					}
+				}
+
+				mLastFrameTime = currentTime;
+			}
+
 			gProfilerCPU().beginThread("Sim");
 
 			Platform::_update();
@@ -181,9 +210,9 @@ namespace BansheeEngine
 
 			PROFILE_CALL(gCoreSceneManager()._update(), "SceneManager");
 
-			gCoreThread().queueCommand(std::bind(&CoreApplication::beginCoreProfiling, this));
 			gCoreThread().queueCommand(std::bind(&RenderWindowCoreManager::_update, RenderWindowCoreManager::instancePtr()));
 			gCoreThread().queueCommand(std::bind(&QueryManager::_update, QueryManager::instancePtr()));
+			gCoreThread().queueCommand(std::bind(&CoreApplication::endCoreProfiling, this));
 
 			// Update plugins
 			for (auto& pluginUpdateFunc : mPluginUpdateFunctions)
@@ -214,17 +243,18 @@ namespace BansheeEngine
 				mIsFrameRenderingFinished = false;
 			}
 
+			gCoreThread().queueCommand(std::bind(&CoreApplication::beginCoreProfiling, this));
 			gCoreThread().queueCommand(&Platform::_coreUpdate);
 
 			gCoreThread().update(); 
 			gCoreThread().submitAccessors(); 
 
-			gCoreThread().queueCommand(std::bind(&CoreApplication::endCoreProfiling, this));
 			gCoreThread().queueCommand(std::bind(&CoreApplication::frameRenderingFinishedCallback, this));
 
 			gProfilerCPU().endThread();
 			gProfiler()._update();
 		}
+		gCoreThread().queueCommand(std::bind(&CoreApplication::endCoreProfiling, this));
 
 		// Wait until last core frame is finished before exiting
 		{
@@ -260,6 +290,11 @@ namespace BansheeEngine
 		stopMainLoop();
 	}
 
+	void CoreApplication::setFPSLimit(UINT32 limit)
+	{
+		mFrameStep = (UINT64)1000000 / limit;
+	}
+
 	void CoreApplication::frameRenderingFinishedCallback()
 	{
 		BS_LOCK_MUTEX(mFrameRenderingFinishedMutex);

+ 0 - 5
BansheeCore/Source/BsMaterialParams.cpp

@@ -2,15 +2,12 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #include "BsMaterialParams.h"
 #include "BsMaterialParamsRTTI.h"
-#include "BsProfilerCPU.h"
 #include "BsShader.h"
 
 namespace BansheeEngine
 {
 	MaterialParams::MaterialParams(const HShader& shader)
 	{
-		gProfilerCPU().beginSample("Create material params");
-
 		mDataSize = 0;
 
 		auto& dataParams = shader->getDataParams();
@@ -105,8 +102,6 @@ namespace BansheeEngine
 
 			mSamplerIdx++;
 		}
-
-		gProfilerCPU().endSample("Create material params");
 	}
 
 	MaterialParams::~MaterialParams()

+ 985 - 985
BansheeCore/Source/BsProfilerCPU.cpp

@@ -1,988 +1,988 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsProfilerCPU.h"
-#include "BsDebug.h"
-#include "BsPlatform.h"
-
-namespace BansheeEngine
-{
-	ProfilerCPU::Timer::Timer()
-	{
-		time = 0.0f;
-	}
-
-	void ProfilerCPU::Timer::start()
-	{
-		startTime = getCurrentTime();
-	}
-
-	void ProfilerCPU::Timer::stop()
-	{
-		time += getCurrentTime() - startTime;
-	}
-
-	void ProfilerCPU::Timer::reset()
-	{
-		time = 0.0f;
-	}
-
-	inline double ProfilerCPU::Timer::getCurrentTime() 
-	{
-		return PlatformUtility::queryPerformanceTimerMs();
-	}
-
-	ProfilerCPU::TimerPrecise::TimerPrecise()
-	{
-		cycles = 0;
-	}
-
-	void ProfilerCPU::TimerPrecise::start()
-	{
-		startCycles = getNumCycles();
-	}
-
-	void ProfilerCPU::TimerPrecise::stop()
-	{
-		cycles += getNumCycles() - startCycles;
-	}
-
-	void ProfilerCPU::TimerPrecise::reset()
-	{
-		cycles = 0;
-	}
-
-	inline UINT64 ProfilerCPU::TimerPrecise::getNumCycles() 
-	{
-#if BS_COMPILER == BS_COMPILER_GNUC
-		asm volatile("cpuid" : : : "%eax", "%ebx", "%ecx", "%edx" );
-		UINT32 __a,__d;
-		asm volatile("rdtsc" : "=a" (__a), "=d" (__d));
-		return ( UINT64(__a) | UINT64(__d) << 32 );
-#else
-		int a[4];
-		int b = 0;
-		__cpuid(a, b);
-		return __rdtsc();
-#endif		
-	}
-
-	ProfilerCPU::ProfileData::ProfileData(FrameAlloc* alloc)
-		:samples(alloc)
-	{ }
-
-	void ProfilerCPU::ProfileData::beginSample()
-	{
-		memAllocs = MemoryCounter::getNumAllocs();
-		memFrees = MemoryCounter::getNumFrees();
-
-		timer.reset();
-		timer.start();
-	}
-
-	void ProfilerCPU::ProfileData::endSample()
-	{
-		timer.stop();
-
-		UINT64 numAllocs = MemoryCounter::getNumAllocs() - memAllocs;
-		UINT64 numFrees = MemoryCounter::getNumFrees() - memFrees;
-
-		samples.push_back(ProfileSample(timer.time, numAllocs, numFrees));
-	}
-
-	void ProfilerCPU::ProfileData::resumeLastSample()
-	{
-		timer.start();
-		samples.erase(samples.end() - 1);
-	}
-
-	ProfilerCPU::PreciseProfileData::PreciseProfileData(FrameAlloc* alloc)
-		:samples(alloc)
-	{ }
-
-	void ProfilerCPU::PreciseProfileData::beginSample()
-	{
-		memAllocs = MemoryCounter::getNumAllocs();
-		memFrees = MemoryCounter::getNumFrees();
-
-		timer.reset();
-		timer.start();
-	}
-
-	void ProfilerCPU::PreciseProfileData::endSample()
-	{
-		timer.stop();
-
-		UINT64 numAllocs = MemoryCounter::getNumAllocs() - memAllocs;
-		UINT64 numFrees = MemoryCounter::getNumFrees() - memFrees;
-
-		samples.push_back(PreciseProfileSample(timer.cycles, numAllocs, numFrees));
-	}
-
-	void ProfilerCPU::PreciseProfileData::resumeLastSample()
-	{
-		timer.start();
-		samples.erase(samples.end() - 1);
-	}
-
-	BS_THREADLOCAL ProfilerCPU::ThreadInfo* ProfilerCPU::ThreadInfo::activeThread = nullptr;
-
-	ProfilerCPU::ThreadInfo::ThreadInfo()
-		:isActive(false), rootBlock(nullptr), frameAlloc(1024 * 512), activeBlocks(nullptr)
-	{
-
-	}
-
-	void ProfilerCPU::ThreadInfo::begin(const char* _name)
-	{
-		if(isActive)
-		{
-			LOGWRN("Profiler::beginThread called on a thread that was already being sampled");
-			return;
-		}
-
-		if(rootBlock == nullptr)
-			rootBlock = getBlock(_name);
-
-		activeBlock = ActiveBlock(ActiveSamplingType::Basic, rootBlock);
-		if (activeBlocks == nullptr)
-			activeBlocks = frameAlloc.alloc<Stack<ActiveBlock, StdFrameAlloc<ActiveBlock>>>(&frameAlloc);
-
-		activeBlocks->push(activeBlock);
-		
-		rootBlock->basic.beginSample();
-		isActive = true;
-	}
-
-	void ProfilerCPU::ThreadInfo::end()
-	{
-		if(activeBlock.type == ActiveSamplingType::Basic)
-			activeBlock.block->basic.endSample();
-		else
-			activeBlock.block->precise.endSample();
-
-		activeBlocks->pop();
-
-		if(!isActive)
-			LOGWRN("Profiler::endThread called on a thread that isn't being sampled.");
-
-		if (activeBlocks->size() > 0)
-		{
-			LOGWRN("Profiler::endThread called but not all sample pairs were closed. Sampling data will not be valid.");
-
-			while (activeBlocks->size() > 0)
-			{
-				ActiveBlock& curBlock = activeBlocks->top();
-				if(curBlock.type == ActiveSamplingType::Basic)
-					curBlock.block->basic.endSample();
-				else
-					curBlock.block->precise.endSample();
-
-				activeBlocks->pop();
-			}
-		}
-
-		isActive = false;
-		activeBlock = ActiveBlock();
-
-		frameAlloc.dealloc(activeBlocks);
-		activeBlocks = nullptr;
-	}
-
-	void ProfilerCPU::ThreadInfo::reset()
-	{
-		if(isActive)
-			end();
-
-		if(rootBlock != nullptr)
-			releaseBlock(rootBlock);
-
-		rootBlock = nullptr;
-		frameAlloc.clear(); // Note: This never actually frees memory
-	}
-
-	ProfilerCPU::ProfiledBlock* ProfilerCPU::ThreadInfo::getBlock(const char* name)
-	{
-		ProfiledBlock* block = frameAlloc.alloc<ProfiledBlock>(&frameAlloc);
-		block->name = (char*)frameAlloc.alloc(((UINT32)strlen(name) + 1) * sizeof(char));
-		strcpy(block->name, name);
-
-		return block;
-	}
-
-	void ProfilerCPU::ThreadInfo::releaseBlock(ProfiledBlock* block)
-	{
-		frameAlloc.dealloc((UINT8*)block->name);
-		frameAlloc.dealloc(block);
-	}
-
-	ProfilerCPU::ProfiledBlock::ProfiledBlock(FrameAlloc* alloc)
-		:children(alloc), basic(alloc), precise(alloc)
-	{ }
-
-	ProfilerCPU::ProfiledBlock::~ProfiledBlock()
-	{
-		ThreadInfo* thread = ThreadInfo::activeThread;
-
-		for(auto& child : children)
-			thread->releaseBlock(child);
-
-		children.clear();
-	}
-
-	ProfilerCPU::ProfiledBlock* ProfilerCPU::ProfiledBlock::findChild(const char* name) const
-	{
-		for(auto& child : children)
-		{
-			if(strcmp(child->name, name) == 0)
-				return child;
-		}
-
-		return nullptr;
-	}
-
-	ProfilerCPU::ProfilerCPU()
-		:mBasicTimerOverhead(0.0), mPreciseTimerOverhead(0), mBasicSamplingOverheadMs(0.0), mPreciseSamplingOverheadCycles(0),
-		mBasicSamplingOverheadCycles(0), mPreciseSamplingOverheadMs(0.0)
-	{
-		// TODO - We only estimate overhead on program start. It might be better to estimate it each time beginThread is called,
-		// and keep separate values per thread.
-		estimateTimerOverhead();
-	}
-
-	ProfilerCPU::~ProfilerCPU()
-	{
-		reset();
-
-		BS_LOCK_MUTEX(mThreadSync);
-
-		for(auto& threadInfo : mActiveThreads)
-			bs_delete<ThreadInfo, ProfilerAlloc>(threadInfo);
-	}
-
-	void ProfilerCPU::beginThread(const char* name)
-	{
-		ThreadInfo* thread = ThreadInfo::activeThread;
-		if(thread == nullptr)
-		{
-			ThreadInfo::activeThread = bs_new<ThreadInfo, ProfilerAlloc>();
-			thread = ThreadInfo::activeThread;
-
-			{
-				BS_LOCK_MUTEX(mThreadSync);
-
-				mActiveThreads.push_back(thread);
-			}
-		}
-
-		thread->begin(name);
-	}
-
-	void ProfilerCPU::endThread()
-	{
-		// I don't do a nullcheck where on purpose, so endSample can be called ASAP
-		ThreadInfo::activeThread->end();
-	}
-
-	void ProfilerCPU::beginSample(const char* name)
-	{
-		ThreadInfo* thread = ThreadInfo::activeThread;
-		if(thread == nullptr || !thread->isActive)
-		{
-			beginThread("Unknown");
-			thread = ThreadInfo::activeThread;
-		}
-
-		ProfiledBlock* parent = thread->activeBlock.block;
-		ProfiledBlock* block = nullptr;
-		
-		if(parent != nullptr)
-			block = parent->findChild(name);
-
-		if(block == nullptr)
-		{
-			block = thread->getBlock(name);
-
-			if(parent != nullptr)
-				parent->children.push_back(block);
-			else
-				thread->rootBlock->children.push_back(block);
-		}
-
-		thread->activeBlock = ActiveBlock(ActiveSamplingType::Basic, block);
-		thread->activeBlocks->push(thread->activeBlock);
-
-		block->basic.beginSample();
-	}
-
-	void ProfilerCPU::endSample(const char* name)
-	{
-		ThreadInfo* thread = ThreadInfo::activeThread;
-		ProfiledBlock* block = thread->activeBlock.block;
-
-#if BS_DEBUG_MODE
-		if(block == nullptr)
-		{
-			LOGWRN("Mismatched CPUProfiler::endSample. No beginSample was called.");
-			return;
-		}
-
-		if(thread->activeBlock.type == ActiveSamplingType::Precise)
-		{
-			LOGWRN("Mismatched CPUProfiler::endSample. Was expecting Profiler::endSamplePrecise.");
-			return;
-		}
-
-		if(strcmp(block->name, name) != 0)
-		{
-			LOGWRN("Mismatched CPUProfiler::endSample. Was expecting \"" + String(block->name) + 
-				"\" but got \"" + String(name) + "\". Sampling data will not be valid.");
-			return;
-		}
-#endif
-
-		block->basic.endSample();
-
-		thread->activeBlocks->pop();
-
-		if (!thread->activeBlocks->empty())
-			thread->activeBlock = thread->activeBlocks->top();
-		else
-			thread->activeBlock = ActiveBlock();
-	}
-
-	void ProfilerCPU::beginSamplePrecise(const char* name)
-	{
-		// Note: There is a (small) possibility a context switch will happen during this measurement in which case result will be skewed. 
-		// Increasing thread priority might help. This is generally only a problem with code that executes a long time (10-15+ ms - depending on OS quant length)
-		
-		ThreadInfo* thread = ThreadInfo::activeThread;
-		if(thread == nullptr || !thread->isActive)
-			beginThread("Unknown");
-
-		ProfiledBlock* parent = thread->activeBlock.block;
-		ProfiledBlock* block = nullptr;
-		
-		if(parent != nullptr)
-			block = parent->findChild(name);
-
-		if(block == nullptr)
-		{
-			block = thread->getBlock(name);
-
-			if(parent != nullptr)
-				parent->children.push_back(block);
-			else
-				thread->rootBlock->children.push_back(block);
-		}
-
-		thread->activeBlock = ActiveBlock(ActiveSamplingType::Precise, block);
-		thread->activeBlocks->push(thread->activeBlock);
-
-		block->precise.beginSample();
-	}
-
-	void ProfilerCPU::endSamplePrecise(const char* name)
-	{
-		ThreadInfo* thread = ThreadInfo::activeThread;
-		ProfiledBlock* block = thread->activeBlock.block;
-
-#if BS_DEBUG_MODE
-		if(block == nullptr)
-		{
-			LOGWRN("Mismatched Profiler::endSamplePrecise. No beginSamplePrecise was called.");
-			return;
-		}
-
-		if(thread->activeBlock.type == ActiveSamplingType::Basic)
-		{
-			LOGWRN("Mismatched CPUProfiler::endSamplePrecise. Was expecting Profiler::endSample.");
-			return;
-		}
-
-		if (strcmp(block->name, name) != 0)
-		{
-			LOGWRN("Mismatched Profiler::endSamplePrecise. Was expecting \"" + String(block->name) + 
-				"\" but got \"" + String(name) + "\". Sampling data will not be valid.");
-			return;
-		}
-#endif
-
-		block->precise.endSample();
-
-		thread->activeBlocks->pop();
-
-		if (!thread->activeBlocks->empty())
-			thread->activeBlock = thread->activeBlocks->top();
-		else
-			thread->activeBlock = ActiveBlock();
-	}
-
-	void ProfilerCPU::reset()
-	{
-		ThreadInfo* thread = ThreadInfo::activeThread;
-
-		if(thread != nullptr)
-			thread->reset();
-	}
-
-	CPUProfilerReport ProfilerCPU::generateReport()
-	{
-		CPUProfilerReport report;
-
-		ThreadInfo* thread = ThreadInfo::activeThread;
-		if(thread == nullptr)
-			return report;
-
-		if(thread->isActive)
-			thread->end();
-
-		// We need to separate out basic and precise data and form two separate hierarchies
-		if(thread->rootBlock == nullptr)
-			return report;
-
-		struct TempEntry
-		{
-			TempEntry(ProfiledBlock* _parentBlock, UINT32 _entryIdx)
-				:parentBlock(_parentBlock), entryIdx(_entryIdx)
-			{ }
-
-			ProfiledBlock* parentBlock;
-			UINT32 entryIdx;
-			ProfilerVector<UINT32> childIndexes;
-		};
-
-		ProfilerVector<CPUProfilerBasicSamplingEntry> basicEntries;
-		ProfilerVector<CPUProfilerPreciseSamplingEntry> preciseEntries;	
-
-		// Fill up flatHierarchy array in a way so we always process children before parents
-		ProfilerStack<UINT32> todo;
-		ProfilerVector<TempEntry> flatHierarchy;
-
-		UINT32 entryIdx = 0;
-		todo.push(entryIdx);
-		flatHierarchy.push_back(TempEntry(thread->rootBlock, entryIdx));
-
-		entryIdx++;
-		while(!todo.empty())
-		{
-			UINT32 curDataIdx = todo.top();
-			ProfiledBlock* curBlock = flatHierarchy[curDataIdx].parentBlock;
-
-			todo.pop();
-
-			for(auto& child : curBlock->children)
-			{
-				flatHierarchy[curDataIdx].childIndexes.push_back(entryIdx);
-
-				todo.push(entryIdx);
-				flatHierarchy.push_back(TempEntry(child, entryIdx));
-
-				entryIdx++;
-			}
-		}
-		
-		// Calculate sampling data for all entries
-		basicEntries.resize(flatHierarchy.size());
-		preciseEntries.resize(flatHierarchy.size());
-
-		for(auto iter = flatHierarchy.rbegin(); iter != flatHierarchy.rend(); ++iter)
-		{
-			TempEntry& curData = *iter;
-			ProfiledBlock* curBlock = curData.parentBlock;
-
-			CPUProfilerBasicSamplingEntry* entryBasic = &basicEntries[curData.entryIdx];
-			CPUProfilerPreciseSamplingEntry* entryPrecise = &preciseEntries[curData.entryIdx];
-
-			// Calculate basic data
-			entryBasic->data.name = String(curBlock->name);
-
-			entryBasic->data.memAllocs = 0;
-			entryBasic->data.memFrees = 0;
-			entryBasic->data.totalTimeMs = 0.0;
-			entryBasic->data.maxTimeMs = 0.0;
-			for(auto& sample : curBlock->basic.samples)
-			{
-				entryBasic->data.totalTimeMs += sample.time;
-				entryBasic->data.maxTimeMs = std::max(entryBasic->data.maxTimeMs, sample.time);
-				entryBasic->data.memAllocs += sample.numAllocs;
-				entryBasic->data.memFrees += sample.numFrees;
-			}
-
-			entryBasic->data.numCalls = (UINT32)curBlock->basic.samples.size();
-
-			if(entryBasic->data.numCalls > 0)
-				entryBasic->data.avgTimeMs = entryBasic->data.totalTimeMs / entryBasic->data.numCalls;
-
-			double totalChildTime = 0.0;
-			for(auto& childIdx : curData.childIndexes)
-			{
-				CPUProfilerBasicSamplingEntry* childEntry = &basicEntries[childIdx];
-				totalChildTime += childEntry->data.totalTimeMs;
-				childEntry->data.pctOfParent = (float)(childEntry->data.totalTimeMs / entryBasic->data.totalTimeMs);
-
-				entryBasic->data.estimatedOverheadMs += childEntry->data.estimatedOverheadMs;
-			}
-
-			entryBasic->data.estimatedOverheadMs += curBlock->basic.samples.size() * mBasicSamplingOverheadMs;
-			entryBasic->data.estimatedOverheadMs += curBlock->precise.samples.size() * mPreciseSamplingOverheadMs;
-
-			entryBasic->data.totalSelfTimeMs = entryBasic->data.totalTimeMs - totalChildTime;
-
-			if(entryBasic->data.numCalls > 0)
-				entryBasic->data.avgSelfTimeMs = entryBasic->data.totalSelfTimeMs / entryBasic->data.numCalls;
-
-			entryBasic->data.estimatedSelfOverheadMs = mBasicTimerOverhead;
-
-			// Calculate precise data
-			entryPrecise->data.name = String(curBlock->name);
-
-			entryPrecise->data.memAllocs = 0;
-			entryPrecise->data.memFrees = 0;
-			entryPrecise->data.totalCycles = 0;
-			entryPrecise->data.maxCycles = 0;
-			for(auto& sample : curBlock->precise.samples)
-			{
-				entryPrecise->data.totalCycles += sample.cycles;
-				entryPrecise->data.maxCycles = std::max(entryPrecise->data.maxCycles, sample.cycles);
-				entryPrecise->data.memAllocs += sample.numAllocs;
-				entryPrecise->data.memFrees += sample.numFrees;
-			}
-
-			entryPrecise->data.numCalls = (UINT32)curBlock->precise.samples.size();
-
-			if(entryPrecise->data.numCalls > 0)
-				entryPrecise->data.avgCycles = entryPrecise->data.totalCycles / entryPrecise->data.numCalls;
-
-			UINT64 totalChildCycles = 0;
-			for(auto& childIdx : curData.childIndexes)
-			{
-				CPUProfilerPreciseSamplingEntry* childEntry = &preciseEntries[childIdx];
-				totalChildCycles += childEntry->data.totalCycles;
-				childEntry->data.pctOfParent = childEntry->data.totalCycles / (float)entryPrecise->data.totalCycles;
-
-				entryPrecise->data.estimatedOverhead += childEntry->data.estimatedOverhead;
-			}
-
-			entryPrecise->data.estimatedOverhead += curBlock->precise.samples.size() * mPreciseSamplingOverheadCycles;
-			entryPrecise->data.estimatedOverhead += curBlock->basic.samples.size() * mBasicSamplingOverheadCycles;
-
-			entryPrecise->data.totalSelfCycles = entryPrecise->data.totalCycles - totalChildCycles;
-
-			if(entryPrecise->data.numCalls > 0)
-				entryPrecise->data.avgSelfCycles = entryPrecise->data.totalSelfCycles / entryPrecise->data.numCalls;
-
-			entryPrecise->data.estimatedSelfOverhead = mPreciseTimerOverhead;
-		}
-
-		// Prune empty basic entries
-		ProfilerStack<UINT32> finalBasicHierarchyTodo;
-		ProfilerStack<UINT32> parentBasicEntryIndexes;
-		ProfilerVector<TempEntry> newBasicEntries;
-
-		finalBasicHierarchyTodo.push(0);
-
-		entryIdx = 0;
-		parentBasicEntryIndexes.push(entryIdx);
-		newBasicEntries.push_back(TempEntry(nullptr, entryIdx));
-
-		entryIdx++;
-
-		while(!finalBasicHierarchyTodo.empty())
-		{
-			UINT32 parentEntryIdx = parentBasicEntryIndexes.top();
-			parentBasicEntryIndexes.pop();
-
-			UINT32 curEntryIdx = finalBasicHierarchyTodo.top();
-			TempEntry& curEntry = flatHierarchy[curEntryIdx];
-			finalBasicHierarchyTodo.pop();
-
-			for(auto& childIdx : curEntry.childIndexes)
-			{
-				finalBasicHierarchyTodo.push(childIdx);
-
-				CPUProfilerBasicSamplingEntry& basicEntry = basicEntries[childIdx];
-				if(basicEntry.data.numCalls > 0)
-				{
-					newBasicEntries.push_back(TempEntry(nullptr, childIdx));
-					newBasicEntries[parentEntryIdx].childIndexes.push_back(entryIdx);
-
-					parentBasicEntryIndexes.push(entryIdx);
-
-					entryIdx++;
-				}
-				else
-					parentBasicEntryIndexes.push(parentEntryIdx);
-			}
-		}
-
-		if(newBasicEntries.size() > 0)
-		{
-			ProfilerVector<CPUProfilerBasicSamplingEntry*> finalBasicEntries;
-
-			report.mBasicSamplingRootEntry = basicEntries[newBasicEntries[0].entryIdx];
-			finalBasicEntries.push_back(&report.mBasicSamplingRootEntry);
-
-			finalBasicHierarchyTodo.push(0);
-
-			while(!finalBasicHierarchyTodo.empty())
-			{
-				UINT32 curEntryIdx = finalBasicHierarchyTodo.top();
-				finalBasicHierarchyTodo.pop();
-
-				TempEntry& curEntry = newBasicEntries[curEntryIdx];
-
-				CPUProfilerBasicSamplingEntry* basicEntry = finalBasicEntries[curEntryIdx];
-
-				basicEntry->childEntries.resize(curEntry.childIndexes.size());
-				UINT32 idx = 0;
-
-				for(auto& childIdx : curEntry.childIndexes)
-				{
-					TempEntry& childEntry = newBasicEntries[childIdx];
-					basicEntry->childEntries[idx] = basicEntries[childEntry.entryIdx];
-
-					finalBasicEntries.push_back(&(basicEntry->childEntries[idx]));
-					finalBasicHierarchyTodo.push(childIdx);
-					idx++;
-				}
-			}
-		}
-
-		// Prune empty precise entries
-		ProfilerStack<UINT32> finalPreciseHierarchyTodo;
-		ProfilerStack<UINT32> parentPreciseEntryIndexes;
-		ProfilerVector<TempEntry> newPreciseEntries;
-
-		finalPreciseHierarchyTodo.push(0);
-
-		entryIdx = 0;
-		parentPreciseEntryIndexes.push(entryIdx);
-		newPreciseEntries.push_back(TempEntry(nullptr, entryIdx));
-
-		entryIdx++;
-
-		while(!finalPreciseHierarchyTodo.empty())
-		{
-			UINT32 parentEntryIdx = parentPreciseEntryIndexes.top();
-			parentPreciseEntryIndexes.pop();
-
-			UINT32 curEntryIdx = finalPreciseHierarchyTodo.top();
-			TempEntry& curEntry = flatHierarchy[curEntryIdx];
-			finalPreciseHierarchyTodo.pop();
-
-			for(auto& childIdx : curEntry.childIndexes)
-			{
-				finalPreciseHierarchyTodo.push(childIdx);
-
-				CPUProfilerPreciseSamplingEntry& preciseEntry = preciseEntries[childIdx];
-				if(preciseEntry.data.numCalls > 0)
-				{
-					newPreciseEntries.push_back(TempEntry(nullptr, childIdx));
-					newPreciseEntries[parentEntryIdx].childIndexes.push_back(entryIdx);
-
-					parentPreciseEntryIndexes.push(entryIdx);
-
-					entryIdx++;
-				}
-				else
-					parentPreciseEntryIndexes.push(parentEntryIdx);
-			}
-		}
-
-		if(newPreciseEntries.size() > 0)
-		{
-			ProfilerVector<CPUProfilerPreciseSamplingEntry*> finalPreciseEntries;
-
-			report.mPreciseSamplingRootEntry = preciseEntries[newPreciseEntries[0].entryIdx];
-			finalPreciseEntries.push_back(&report.mPreciseSamplingRootEntry);
-
-			finalPreciseHierarchyTodo.push(0);
-
-			while(!finalPreciseHierarchyTodo.empty())
-			{
-				UINT32 curEntryIdx = finalPreciseHierarchyTodo.top();
-				finalPreciseHierarchyTodo.pop();
-
-				TempEntry& curEntry = newPreciseEntries[curEntryIdx];
-
-				CPUProfilerPreciseSamplingEntry* preciseEntry = finalPreciseEntries[curEntryIdx];
-
-				preciseEntry->childEntries.resize(curEntry.childIndexes.size());
-				UINT32 idx = 0;
-
-				for(auto& childIdx : curEntry.childIndexes)
-				{
-					TempEntry& childEntry = newPreciseEntries[childIdx];
-					preciseEntry->childEntries[idx] = preciseEntries[childEntry.entryIdx];
-
-					finalPreciseEntries.push_back(&preciseEntry->childEntries.back());
-					finalPreciseHierarchyTodo.push(childIdx);
-					idx++;
-				}
-			}
-		}
-
-		return report;
-	}
-
-	void ProfilerCPU::estimateTimerOverhead()
-	{
-		// Get an idea of how long timer calls and RDTSC takes
-		const UINT32 reps = 1000, sampleReps = 20;
-
-		mBasicTimerOverhead = 1000000.0;
-		mPreciseTimerOverhead = 1000000;
-		for (UINT32 tries = 0; tries < 20; tries++) 
-		{
-			Timer timer;
-			for (UINT32 i = 0; i < reps; i++) 
-			{
-				timer.start();
-				timer.stop();
-			}
-
-			double avgTime = double(timer.time)/double(reps);
-			if (avgTime < mBasicTimerOverhead)
-				mBasicTimerOverhead = avgTime;
-
-			TimerPrecise timerPrecise;
-			for (UINT32 i = 0; i < reps; i++) 
-			{
-				timerPrecise.start();
-				timerPrecise.stop();
-			}
-
-			UINT64 avgCycles = timerPrecise.cycles/reps;
-			if (avgCycles < mPreciseTimerOverhead)
-				mPreciseTimerOverhead = avgCycles;
-		}
-
-		mBasicSamplingOverheadMs = 1000000.0;
-		mPreciseSamplingOverheadMs = 1000000.0;
-		mBasicSamplingOverheadCycles = 1000000;
-		mPreciseSamplingOverheadCycles = 1000000;
-		for (UINT32 tries = 0; tries < 3; tries++) 
-		{
-			/************************************************************************/
-			/* 				AVERAGE TIME IN MS FOR BASIC SAMPLING                   */
-			/************************************************************************/
-
-			Timer timerA;
-			timerA.start();
-
-			beginThread("Main");
-
-			// Two different cases that can effect performance, one where
-			// sample already exists and other where new one needs to be created
-			for (UINT32 i = 0; i < sampleReps; i++) 
-			{
-				beginSample("TestAvg1");
-				endSample("TestAvg1");
-				beginSample("TestAvg2");
-				endSample("TestAvg2");
-				beginSample("TestAvg3");
-				endSample("TestAvg3");
-				beginSample("TestAvg4");
-				endSample("TestAvg4");
-				beginSample("TestAvg5");
-				endSample("TestAvg5");
-				beginSample("TestAvg6");
-				endSample("TestAvg6");
-				beginSample("TestAvg7");
-				endSample("TestAvg7");
-				beginSample("TestAvg8");
-				endSample("TestAvg8");
-				beginSample("TestAvg9");
-				endSample("TestAvg9");
-				beginSample("TestAvg10");
-				endSample("TestAvg10");
-			}
-
-			for (UINT32 i = 0; i < sampleReps * 5; i++) 
-			{
-				beginSample(("TestAvg#" + toString(i)).c_str());
-				endSample(("TestAvg#" + toString(i)).c_str());
-			}
-
-			endThread();
-
-			timerA.stop();
-
-			reset();
-
-			double avgTimeBasic = double(timerA.time)/double(sampleReps * 10 + sampleReps * 5) - mBasicTimerOverhead;
-			if (avgTimeBasic < mBasicSamplingOverheadMs)
-				mBasicSamplingOverheadMs = avgTimeBasic;
-
-			/************************************************************************/
-			/* 					AVERAGE CYCLES FOR BASIC SAMPLING                   */
-			/************************************************************************/
-
-			TimerPrecise timerPreciseA;
-			timerPreciseA.start();
-
-			beginThread("Main");
-
-			// Two different cases that can effect performance, one where
-			// sample already exists and other where new one needs to be created
-			for (UINT32 i = 0; i < sampleReps; i++) 
-			{
-				beginSample("TestAvg1");
-				endSample("TestAvg1");
-				beginSample("TestAvg2");
-				endSample("TestAvg2");
-				beginSample("TestAvg3");
-				endSample("TestAvg3");
-				beginSample("TestAvg4");
-				endSample("TestAvg4");
-				beginSample("TestAvg5");
-				endSample("TestAvg5");
-				beginSample("TestAvg6");
-				endSample("TestAvg6");
-				beginSample("TestAvg7");
-				endSample("TestAvg7");
-				beginSample("TestAvg8");
-				endSample("TestAvg8");
-				beginSample("TestAvg9");
-				endSample("TestAvg9");
-				beginSample("TestAvg10");
-				endSample("TestAvg10");
-			}
-
-			for (UINT32 i = 0; i < sampleReps * 5; i++) 
-			{
-				beginSample(("TestAvg#" + toString(i)).c_str());
-				endSample(("TestAvg#" + toString(i)).c_str());
-			}
-
-			endThread();
-			timerPreciseA.stop();
-
-			reset();
-
-			UINT64 avgCyclesBasic = timerPreciseA.cycles/(sampleReps * 10 + sampleReps * 5) - mPreciseTimerOverhead;
-			if (avgCyclesBasic < mBasicSamplingOverheadCycles)
-				mBasicSamplingOverheadCycles = avgCyclesBasic;
-
-			/************************************************************************/
-			/* 				AVERAGE TIME IN MS FOR PRECISE SAMPLING                 */
-			/************************************************************************/
-
-			Timer timerB;
-			timerB.start();
-			beginThread("Main");
-
-			// Two different cases that can effect performance, one where
-			// sample already exists and other where new one needs to be created
-			for (UINT32 i = 0; i < sampleReps; i++) 
-			{
-				beginSamplePrecise("TestAvg1");
-				endSamplePrecise("TestAvg1");
-				beginSamplePrecise("TestAvg2");
-				endSamplePrecise("TestAvg2");
-				beginSamplePrecise("TestAvg3");
-				endSamplePrecise("TestAvg3");
-				beginSamplePrecise("TestAvg4");
-				endSamplePrecise("TestAvg4");
-				beginSamplePrecise("TestAvg5");
-				endSamplePrecise("TestAvg5");
-				beginSamplePrecise("TestAvg6");
-				endSamplePrecise("TestAvg6");
-				beginSamplePrecise("TestAvg7");
-				endSamplePrecise("TestAvg7");
-				beginSamplePrecise("TestAvg8");
-				endSamplePrecise("TestAvg8");
-				beginSamplePrecise("TestAvg9");
-				endSamplePrecise("TestAvg9");
-				beginSamplePrecise("TestAvg10");
-				endSamplePrecise("TestAvg10");
-			}
-
-			for (UINT32 i = 0; i < sampleReps * 5; i++) 
-			{
-				beginSamplePrecise(("TestAvg#" + toString(i)).c_str());
-				endSamplePrecise(("TestAvg#" + toString(i)).c_str());
-			}
-
-			endThread();
-			timerB.stop();
-
-			reset();
-
-			double avgTimesPrecise = timerB.time/(sampleReps * 10 + sampleReps * 5);
-			if (avgTimesPrecise < mPreciseSamplingOverheadMs)
-				mPreciseSamplingOverheadMs = avgTimesPrecise;
-
-			/************************************************************************/
-			/* 				AVERAGE CYCLES FOR PRECISE SAMPLING                     */
-			/************************************************************************/
-
-			TimerPrecise timerPreciseB;
-			timerPreciseB.start();
-			beginThread("Main");
-
-			// Two different cases that can effect performance, one where
-			// sample already exists and other where new one needs to be created
-			for (UINT32 i = 0; i < sampleReps; i++) 
-			{
-				beginSamplePrecise("TestAvg1");
-				endSamplePrecise("TestAvg1");
-				beginSamplePrecise("TestAvg2");
-				endSamplePrecise("TestAvg2");
-				beginSamplePrecise("TestAvg3");
-				endSamplePrecise("TestAvg3");
-				beginSamplePrecise("TestAvg4");
-				endSamplePrecise("TestAvg4");
-				beginSamplePrecise("TestAvg5");
-				endSamplePrecise("TestAvg5");
-				beginSamplePrecise("TestAvg6");
-				endSamplePrecise("TestAvg6");
-				beginSamplePrecise("TestAvg7");
-				endSamplePrecise("TestAvg7");
-				beginSamplePrecise("TestAvg8");
-				endSamplePrecise("TestAvg8");
-				beginSamplePrecise("TestAvg9");
-				endSamplePrecise("TestAvg9");
-				beginSamplePrecise("TestAvg10");
-				endSamplePrecise("TestAvg10");
-			}
-
-			for (UINT32 i = 0; i < sampleReps * 5; i++) 
-			{
-				beginSamplePrecise(("TestAvg#" + toString(i)).c_str());
-				endSamplePrecise(("TestAvg#" + toString(i)).c_str());
-			}
-
-			endThread();
-			timerPreciseB.stop();
-
-			reset();
-
-			UINT64 avgCyclesPrecise = timerPreciseB.cycles/(sampleReps * 10 + sampleReps * 5);
-			if (avgCyclesPrecise < mPreciseSamplingOverheadCycles)
-				mPreciseSamplingOverheadCycles = avgCyclesPrecise;
-		}
-	}
-
-	CPUProfilerBasicSamplingEntry::Data::Data()
-		:numCalls(0), avgTimeMs(0.0), maxTimeMs(0.0), totalTimeMs(0.0),
-		avgSelfTimeMs(0.0), totalSelfTimeMs(0.0), estimatedSelfOverheadMs(0.0),
-		estimatedOverheadMs(0.0), pctOfParent(1.0f)
-	{ }
-
-	CPUProfilerPreciseSamplingEntry::Data::Data()
-		:numCalls(0), avgCycles(0), maxCycles(0), totalCycles(0),
-		avgSelfCycles(0), totalSelfCycles(0), estimatedSelfOverhead(0),
-		estimatedOverhead(0), pctOfParent(1.0f)
-	{ }
-
-	CPUProfilerReport::CPUProfilerReport()
-	{
-
-	}
-
-	ProfilerCPU& gProfilerCPU()
-	{
-		return ProfilerCPU::instance();
-	}
+#include "BsProfilerCPU.h"
+#include "BsDebug.h"
+#include "BsPlatform.h"
+
+namespace BansheeEngine
+{
+	ProfilerCPU::Timer::Timer()
+	{
+		time = 0.0f;
+	}
+
+	void ProfilerCPU::Timer::start()
+	{
+		startTime = getCurrentTime();
+	}
+
+	void ProfilerCPU::Timer::stop()
+	{
+		time += getCurrentTime() - startTime;
+	}
+
+	void ProfilerCPU::Timer::reset()
+	{
+		time = 0.0f;
+	}
+
+	inline double ProfilerCPU::Timer::getCurrentTime() 
+	{
+		return PlatformUtility::queryPerformanceTimerMs();
+	}
+
+	ProfilerCPU::TimerPrecise::TimerPrecise()
+	{
+		cycles = 0;
+	}
+
+	void ProfilerCPU::TimerPrecise::start()
+	{
+		startCycles = getNumCycles();
+	}
+
+	void ProfilerCPU::TimerPrecise::stop()
+	{
+		cycles += getNumCycles() - startCycles;
+	}
+
+	void ProfilerCPU::TimerPrecise::reset()
+	{
+		cycles = 0;
+	}
+
+	inline UINT64 ProfilerCPU::TimerPrecise::getNumCycles() 
+	{
+#if BS_COMPILER == BS_COMPILER_GNUC
+		asm volatile("cpuid" : : : "%eax", "%ebx", "%ecx", "%edx" );
+		UINT32 __a,__d;
+		asm volatile("rdtsc" : "=a" (__a), "=d" (__d));
+		return ( UINT64(__a) | UINT64(__d) << 32 );
+#else
+		int a[4];
+		int b = 0;
+		__cpuid(a, b);
+		return __rdtsc();
+#endif		
+	}
+
+	ProfilerCPU::ProfileData::ProfileData(FrameAlloc* alloc)
+		:samples(alloc)
+	{ }
+
+	void ProfilerCPU::ProfileData::beginSample()
+	{
+		memAllocs = MemoryCounter::getNumAllocs();
+		memFrees = MemoryCounter::getNumFrees();
+
+		timer.reset();
+		timer.start();
+	}
+
+	void ProfilerCPU::ProfileData::endSample()
+	{
+		timer.stop();
+
+		UINT64 numAllocs = MemoryCounter::getNumAllocs() - memAllocs;
+		UINT64 numFrees = MemoryCounter::getNumFrees() - memFrees;
+
+		samples.push_back(ProfileSample(timer.time, numAllocs, numFrees));
+	}
+
+	void ProfilerCPU::ProfileData::resumeLastSample()
+	{
+		timer.start();
+		samples.erase(samples.end() - 1);
+	}
+
+	ProfilerCPU::PreciseProfileData::PreciseProfileData(FrameAlloc* alloc)
+		:samples(alloc)
+	{ }
+
+	void ProfilerCPU::PreciseProfileData::beginSample()
+	{
+		memAllocs = MemoryCounter::getNumAllocs();
+		memFrees = MemoryCounter::getNumFrees();
+
+		timer.reset();
+		timer.start();
+	}
+
+	void ProfilerCPU::PreciseProfileData::endSample()
+	{
+		timer.stop();
+
+		UINT64 numAllocs = MemoryCounter::getNumAllocs() - memAllocs;
+		UINT64 numFrees = MemoryCounter::getNumFrees() - memFrees;
+
+		samples.push_back(PreciseProfileSample(timer.cycles, numAllocs, numFrees));
+	}
+
+	void ProfilerCPU::PreciseProfileData::resumeLastSample()
+	{
+		timer.start();
+		samples.erase(samples.end() - 1);
+	}
+
+	BS_THREADLOCAL ProfilerCPU::ThreadInfo* ProfilerCPU::ThreadInfo::activeThread = nullptr;
+
+	ProfilerCPU::ThreadInfo::ThreadInfo()
+		:isActive(false), rootBlock(nullptr), frameAlloc(1024 * 512), activeBlocks(nullptr)
+	{
+
+	}
+
+	void ProfilerCPU::ThreadInfo::begin(const char* _name)
+	{
+		if(isActive)
+		{
+			LOGWRN("Profiler::beginThread called on a thread that was already being sampled");
+			return;
+		}
+
+		if(rootBlock == nullptr)
+			rootBlock = getBlock(_name);
+
+		activeBlock = ActiveBlock(ActiveSamplingType::Basic, rootBlock);
+		if (activeBlocks == nullptr)
+			activeBlocks = frameAlloc.alloc<Stack<ActiveBlock, StdFrameAlloc<ActiveBlock>>>(&frameAlloc);
+
+		activeBlocks->push(activeBlock);
+		
+		rootBlock->basic.beginSample();
+		isActive = true;
+	}
+
+	void ProfilerCPU::ThreadInfo::end()
+	{
+		if(activeBlock.type == ActiveSamplingType::Basic)
+			activeBlock.block->basic.endSample();
+		else
+			activeBlock.block->precise.endSample();
+
+		activeBlocks->pop();
+
+		if(!isActive)
+			LOGWRN("Profiler::endThread called on a thread that isn't being sampled.");
+
+		if (activeBlocks->size() > 0)
+		{
+			LOGWRN("Profiler::endThread called but not all sample pairs were closed. Sampling data will not be valid.");
+
+			while (activeBlocks->size() > 0)
+			{
+				ActiveBlock& curBlock = activeBlocks->top();
+				if(curBlock.type == ActiveSamplingType::Basic)
+					curBlock.block->basic.endSample();
+				else
+					curBlock.block->precise.endSample();
+
+				activeBlocks->pop();
+			}
+		}
+
+		isActive = false;
+		activeBlock = ActiveBlock();
+
+		frameAlloc.dealloc(activeBlocks);
+		activeBlocks = nullptr;
+	}
+
+	void ProfilerCPU::ThreadInfo::reset()
+	{
+		if(isActive)
+			end();
+
+		if(rootBlock != nullptr)
+			releaseBlock(rootBlock);
+
+		rootBlock = nullptr;
+		frameAlloc.clear(); // Note: This never actually frees memory
+	}
+
+	ProfilerCPU::ProfiledBlock* ProfilerCPU::ThreadInfo::getBlock(const char* name)
+	{
+		ProfiledBlock* block = frameAlloc.alloc<ProfiledBlock>(&frameAlloc);
+		block->name = (char*)frameAlloc.alloc(((UINT32)strlen(name) + 1) * sizeof(char));
+		strcpy(block->name, name);
+
+		return block;
+	}
+
+	void ProfilerCPU::ThreadInfo::releaseBlock(ProfiledBlock* block)
+	{
+		frameAlloc.dealloc((UINT8*)block->name);
+		frameAlloc.dealloc(block);
+	}
+
+	ProfilerCPU::ProfiledBlock::ProfiledBlock(FrameAlloc* alloc)
+		:children(alloc), basic(alloc), precise(alloc)
+	{ }
+
+	ProfilerCPU::ProfiledBlock::~ProfiledBlock()
+	{
+		ThreadInfo* thread = ThreadInfo::activeThread;
+
+		for(auto& child : children)
+			thread->releaseBlock(child);
+
+		children.clear();
+	}
+
+	ProfilerCPU::ProfiledBlock* ProfilerCPU::ProfiledBlock::findChild(const char* name) const
+	{
+		for(auto& child : children)
+		{
+			if(strcmp(child->name, name) == 0)
+				return child;
+		}
+
+		return nullptr;
+	}
+
+	ProfilerCPU::ProfilerCPU()
+		:mBasicTimerOverhead(0.0), mPreciseTimerOverhead(0), mBasicSamplingOverheadMs(0.0), mPreciseSamplingOverheadCycles(0),
+		mBasicSamplingOverheadCycles(0), mPreciseSamplingOverheadMs(0.0)
+	{
+		// TODO - We only estimate overhead on program start. It might be better to estimate it each time beginThread is called,
+		// and keep separate values per thread.
+		estimateTimerOverhead();
+	}
+
+	ProfilerCPU::~ProfilerCPU()
+	{
+		reset();
+
+		BS_LOCK_MUTEX(mThreadSync);
+
+		for(auto& threadInfo : mActiveThreads)
+			bs_delete<ThreadInfo, ProfilerAlloc>(threadInfo);
+	}
+
+	void ProfilerCPU::beginThread(const char* name)
+	{
+		ThreadInfo* thread = ThreadInfo::activeThread;
+		if(thread == nullptr)
+		{
+			ThreadInfo::activeThread = bs_new<ThreadInfo, ProfilerAlloc>();
+			thread = ThreadInfo::activeThread;
+
+			{
+				BS_LOCK_MUTEX(mThreadSync);
+
+				mActiveThreads.push_back(thread);
+			}
+		}
+
+		thread->begin(name);
+	}
+
+	void ProfilerCPU::endThread()
+	{
+		// I don't do a nullcheck where on purpose, so endSample can be called ASAP
+		ThreadInfo::activeThread->end();
+	}
+
+	void ProfilerCPU::beginSample(const char* name)
+	{
+		ThreadInfo* thread = ThreadInfo::activeThread;
+		if(thread == nullptr || !thread->isActive)
+		{
+			beginThread("Unknown");
+			thread = ThreadInfo::activeThread;
+		}
+
+		ProfiledBlock* parent = thread->activeBlock.block;
+		ProfiledBlock* block = nullptr;
+		
+		if(parent != nullptr)
+			block = parent->findChild(name);
+
+		if(block == nullptr)
+		{
+			block = thread->getBlock(name);
+
+			if(parent != nullptr)
+				parent->children.push_back(block);
+			else
+				thread->rootBlock->children.push_back(block);
+		}
+
+		thread->activeBlock = ActiveBlock(ActiveSamplingType::Basic, block);
+		thread->activeBlocks->push(thread->activeBlock);
+
+		block->basic.beginSample();
+	}
+
+	void ProfilerCPU::endSample(const char* name)
+	{
+		ThreadInfo* thread = ThreadInfo::activeThread;
+		ProfiledBlock* block = thread->activeBlock.block;
+
+#if BS_DEBUG_MODE
+		if(block == nullptr)
+		{
+			LOGWRN("Mismatched CPUProfiler::endSample. No beginSample was called.");
+			return;
+		}
+
+		if(thread->activeBlock.type == ActiveSamplingType::Precise)
+		{
+			LOGWRN("Mismatched CPUProfiler::endSample. Was expecting Profiler::endSamplePrecise.");
+			return;
+		}
+
+		if(strcmp(block->name, name) != 0)
+		{
+			LOGWRN("Mismatched CPUProfiler::endSample. Was expecting \"" + String(block->name) + 
+				"\" but got \"" + String(name) + "\". Sampling data will not be valid.");
+			return;
+		}
+#endif
+
+		block->basic.endSample();
+
+		thread->activeBlocks->pop();
+
+		if (!thread->activeBlocks->empty())
+			thread->activeBlock = thread->activeBlocks->top();
+		else
+			thread->activeBlock = ActiveBlock();
+	}
+
+	void ProfilerCPU::beginSamplePrecise(const char* name)
+	{
+		// Note: There is a (small) possibility a context switch will happen during this measurement in which case result will be skewed. 
+		// Increasing thread priority might help. This is generally only a problem with code that executes a long time (10-15+ ms - depending on OS quant length)
+		
+		ThreadInfo* thread = ThreadInfo::activeThread;
+		if(thread == nullptr || !thread->isActive)
+			beginThread("Unknown");
+
+		ProfiledBlock* parent = thread->activeBlock.block;
+		ProfiledBlock* block = nullptr;
+		
+		if(parent != nullptr)
+			block = parent->findChild(name);
+
+		if(block == nullptr)
+		{
+			block = thread->getBlock(name);
+
+			if(parent != nullptr)
+				parent->children.push_back(block);
+			else
+				thread->rootBlock->children.push_back(block);
+		}
+
+		thread->activeBlock = ActiveBlock(ActiveSamplingType::Precise, block);
+		thread->activeBlocks->push(thread->activeBlock);
+
+		block->precise.beginSample();
+	}
+
+	void ProfilerCPU::endSamplePrecise(const char* name)
+	{
+		ThreadInfo* thread = ThreadInfo::activeThread;
+		ProfiledBlock* block = thread->activeBlock.block;
+
+#if BS_DEBUG_MODE
+		if(block == nullptr)
+		{
+			LOGWRN("Mismatched Profiler::endSamplePrecise. No beginSamplePrecise was called.");
+			return;
+		}
+
+		if(thread->activeBlock.type == ActiveSamplingType::Basic)
+		{
+			LOGWRN("Mismatched CPUProfiler::endSamplePrecise. Was expecting Profiler::endSample.");
+			return;
+		}
+
+		if (strcmp(block->name, name) != 0)
+		{
+			LOGWRN("Mismatched Profiler::endSamplePrecise. Was expecting \"" + String(block->name) + 
+				"\" but got \"" + String(name) + "\". Sampling data will not be valid.");
+			return;
+		}
+#endif
+
+		block->precise.endSample();
+
+		thread->activeBlocks->pop();
+
+		if (!thread->activeBlocks->empty())
+			thread->activeBlock = thread->activeBlocks->top();
+		else
+			thread->activeBlock = ActiveBlock();
+	}
+
+	void ProfilerCPU::reset()
+	{
+		ThreadInfo* thread = ThreadInfo::activeThread;
+
+		if(thread != nullptr)
+			thread->reset();
+	}
+
+	CPUProfilerReport ProfilerCPU::generateReport()
+	{
+		CPUProfilerReport report;
+
+		ThreadInfo* thread = ThreadInfo::activeThread;
+		if(thread == nullptr)
+			return report;
+
+		if(thread->isActive)
+			thread->end();
+
+		// We need to separate out basic and precise data and form two separate hierarchies
+		if(thread->rootBlock == nullptr)
+			return report;
+
+		struct TempEntry
+		{
+			TempEntry(ProfiledBlock* _parentBlock, UINT32 _entryIdx)
+				:parentBlock(_parentBlock), entryIdx(_entryIdx)
+			{ }
+
+			ProfiledBlock* parentBlock;
+			UINT32 entryIdx;
+			ProfilerVector<UINT32> childIndexes;
+		};
+
+		ProfilerVector<CPUProfilerBasicSamplingEntry> basicEntries;
+		ProfilerVector<CPUProfilerPreciseSamplingEntry> preciseEntries;	
+
+		// Fill up flatHierarchy array in a way so we always process children before parents
+		ProfilerStack<UINT32> todo;
+		ProfilerVector<TempEntry> flatHierarchy;
+
+		UINT32 entryIdx = 0;
+		todo.push(entryIdx);
+		flatHierarchy.push_back(TempEntry(thread->rootBlock, entryIdx));
+
+		entryIdx++;
+		while(!todo.empty())
+		{
+			UINT32 curDataIdx = todo.top();
+			ProfiledBlock* curBlock = flatHierarchy[curDataIdx].parentBlock;
+
+			todo.pop();
+
+			for(auto& child : curBlock->children)
+			{
+				flatHierarchy[curDataIdx].childIndexes.push_back(entryIdx);
+
+				todo.push(entryIdx);
+				flatHierarchy.push_back(TempEntry(child, entryIdx));
+
+				entryIdx++;
+			}
+		}
+		
+		// Calculate sampling data for all entries
+		basicEntries.resize(flatHierarchy.size());
+		preciseEntries.resize(flatHierarchy.size());
+
+		for(auto iter = flatHierarchy.rbegin(); iter != flatHierarchy.rend(); ++iter)
+		{
+			TempEntry& curData = *iter;
+			ProfiledBlock* curBlock = curData.parentBlock;
+
+			CPUProfilerBasicSamplingEntry* entryBasic = &basicEntries[curData.entryIdx];
+			CPUProfilerPreciseSamplingEntry* entryPrecise = &preciseEntries[curData.entryIdx];
+
+			// Calculate basic data
+			entryBasic->data.name = String(curBlock->name);
+
+			entryBasic->data.memAllocs = 0;
+			entryBasic->data.memFrees = 0;
+			entryBasic->data.totalTimeMs = 0.0;
+			entryBasic->data.maxTimeMs = 0.0;
+			for(auto& sample : curBlock->basic.samples)
+			{
+				entryBasic->data.totalTimeMs += sample.time;
+				entryBasic->data.maxTimeMs = std::max(entryBasic->data.maxTimeMs, sample.time);
+				entryBasic->data.memAllocs += sample.numAllocs;
+				entryBasic->data.memFrees += sample.numFrees;
+			}
+
+			entryBasic->data.numCalls = (UINT32)curBlock->basic.samples.size();
+
+			if(entryBasic->data.numCalls > 0)
+				entryBasic->data.avgTimeMs = entryBasic->data.totalTimeMs / entryBasic->data.numCalls;
+
+			double totalChildTime = 0.0;
+			for(auto& childIdx : curData.childIndexes)
+			{
+				CPUProfilerBasicSamplingEntry* childEntry = &basicEntries[childIdx];
+				totalChildTime += childEntry->data.totalTimeMs;
+				childEntry->data.pctOfParent = (float)(childEntry->data.totalTimeMs / entryBasic->data.totalTimeMs);
+
+				entryBasic->data.estimatedOverheadMs += childEntry->data.estimatedOverheadMs;
+			}
+
+			entryBasic->data.estimatedOverheadMs += curBlock->basic.samples.size() * mBasicSamplingOverheadMs;
+			entryBasic->data.estimatedOverheadMs += curBlock->precise.samples.size() * mPreciseSamplingOverheadMs;
+
+			entryBasic->data.totalSelfTimeMs = entryBasic->data.totalTimeMs - totalChildTime;
+
+			if(entryBasic->data.numCalls > 0)
+				entryBasic->data.avgSelfTimeMs = entryBasic->data.totalSelfTimeMs / entryBasic->data.numCalls;
+
+			entryBasic->data.estimatedSelfOverheadMs = mBasicTimerOverhead;
+
+			// Calculate precise data
+			entryPrecise->data.name = String(curBlock->name);
+
+			entryPrecise->data.memAllocs = 0;
+			entryPrecise->data.memFrees = 0;
+			entryPrecise->data.totalCycles = 0;
+			entryPrecise->data.maxCycles = 0;
+			for(auto& sample : curBlock->precise.samples)
+			{
+				entryPrecise->data.totalCycles += sample.cycles;
+				entryPrecise->data.maxCycles = std::max(entryPrecise->data.maxCycles, sample.cycles);
+				entryPrecise->data.memAllocs += sample.numAllocs;
+				entryPrecise->data.memFrees += sample.numFrees;
+			}
+
+			entryPrecise->data.numCalls = (UINT32)curBlock->precise.samples.size();
+
+			if(entryPrecise->data.numCalls > 0)
+				entryPrecise->data.avgCycles = entryPrecise->data.totalCycles / entryPrecise->data.numCalls;
+
+			UINT64 totalChildCycles = 0;
+			for(auto& childIdx : curData.childIndexes)
+			{
+				CPUProfilerPreciseSamplingEntry* childEntry = &preciseEntries[childIdx];
+				totalChildCycles += childEntry->data.totalCycles;
+				childEntry->data.pctOfParent = childEntry->data.totalCycles / (float)entryPrecise->data.totalCycles;
+
+				entryPrecise->data.estimatedOverhead += childEntry->data.estimatedOverhead;
+			}
+
+			entryPrecise->data.estimatedOverhead += curBlock->precise.samples.size() * mPreciseSamplingOverheadCycles;
+			entryPrecise->data.estimatedOverhead += curBlock->basic.samples.size() * mBasicSamplingOverheadCycles;
+
+			entryPrecise->data.totalSelfCycles = entryPrecise->data.totalCycles - totalChildCycles;
+
+			if(entryPrecise->data.numCalls > 0)
+				entryPrecise->data.avgSelfCycles = entryPrecise->data.totalSelfCycles / entryPrecise->data.numCalls;
+
+			entryPrecise->data.estimatedSelfOverhead = mPreciseTimerOverhead;
+		}
+
+		// Prune empty basic entries
+		ProfilerStack<UINT32> finalBasicHierarchyTodo;
+		ProfilerStack<UINT32> parentBasicEntryIndexes;
+		ProfilerVector<TempEntry> newBasicEntries;
+
+		finalBasicHierarchyTodo.push(0);
+
+		entryIdx = 0;
+		parentBasicEntryIndexes.push(entryIdx);
+		newBasicEntries.push_back(TempEntry(nullptr, entryIdx));
+
+		entryIdx++;
+
+		while(!finalBasicHierarchyTodo.empty())
+		{
+			UINT32 parentEntryIdx = parentBasicEntryIndexes.top();
+			parentBasicEntryIndexes.pop();
+
+			UINT32 curEntryIdx = finalBasicHierarchyTodo.top();
+			TempEntry& curEntry = flatHierarchy[curEntryIdx];
+			finalBasicHierarchyTodo.pop();
+
+			for(auto& childIdx : curEntry.childIndexes)
+			{
+				finalBasicHierarchyTodo.push(childIdx);
+
+				CPUProfilerBasicSamplingEntry& basicEntry = basicEntries[childIdx];
+				if(basicEntry.data.numCalls > 0)
+				{
+					newBasicEntries.push_back(TempEntry(nullptr, childIdx));
+					newBasicEntries[parentEntryIdx].childIndexes.push_back(entryIdx);
+
+					parentBasicEntryIndexes.push(entryIdx);
+
+					entryIdx++;
+				}
+				else
+					parentBasicEntryIndexes.push(parentEntryIdx);
+			}
+		}
+
+		if(newBasicEntries.size() > 0)
+		{
+			ProfilerVector<CPUProfilerBasicSamplingEntry*> finalBasicEntries;
+
+			report.mBasicSamplingRootEntry = basicEntries[newBasicEntries[0].entryIdx];
+			finalBasicEntries.push_back(&report.mBasicSamplingRootEntry);
+
+			finalBasicHierarchyTodo.push(0);
+
+			while(!finalBasicHierarchyTodo.empty())
+			{
+				UINT32 curEntryIdx = finalBasicHierarchyTodo.top();
+				finalBasicHierarchyTodo.pop();
+
+				TempEntry& curEntry = newBasicEntries[curEntryIdx];
+
+				CPUProfilerBasicSamplingEntry* basicEntry = finalBasicEntries[curEntryIdx];
+
+				basicEntry->childEntries.resize(curEntry.childIndexes.size());
+				UINT32 idx = 0;
+
+				for(auto& childIdx : curEntry.childIndexes)
+				{
+					TempEntry& childEntry = newBasicEntries[childIdx];
+					basicEntry->childEntries[idx] = basicEntries[childEntry.entryIdx];
+
+					finalBasicEntries.push_back(&(basicEntry->childEntries[idx]));
+					finalBasicHierarchyTodo.push(childIdx);
+					idx++;
+				}
+			}
+		}
+
+		// Prune empty precise entries
+		ProfilerStack<UINT32> finalPreciseHierarchyTodo;
+		ProfilerStack<UINT32> parentPreciseEntryIndexes;
+		ProfilerVector<TempEntry> newPreciseEntries;
+
+		finalPreciseHierarchyTodo.push(0);
+
+		entryIdx = 0;
+		parentPreciseEntryIndexes.push(entryIdx);
+		newPreciseEntries.push_back(TempEntry(nullptr, entryIdx));
+
+		entryIdx++;
+
+		while(!finalPreciseHierarchyTodo.empty())
+		{
+			UINT32 parentEntryIdx = parentPreciseEntryIndexes.top();
+			parentPreciseEntryIndexes.pop();
+
+			UINT32 curEntryIdx = finalPreciseHierarchyTodo.top();
+			TempEntry& curEntry = flatHierarchy[curEntryIdx];
+			finalPreciseHierarchyTodo.pop();
+
+			for(auto& childIdx : curEntry.childIndexes)
+			{
+				finalPreciseHierarchyTodo.push(childIdx);
+
+				CPUProfilerPreciseSamplingEntry& preciseEntry = preciseEntries[childIdx];
+				if(preciseEntry.data.numCalls > 0)
+				{
+					newPreciseEntries.push_back(TempEntry(nullptr, childIdx));
+					newPreciseEntries[parentEntryIdx].childIndexes.push_back(entryIdx);
+
+					parentPreciseEntryIndexes.push(entryIdx);
+
+					entryIdx++;
+				}
+				else
+					parentPreciseEntryIndexes.push(parentEntryIdx);
+			}
+		}
+
+		if(newPreciseEntries.size() > 0)
+		{
+			ProfilerVector<CPUProfilerPreciseSamplingEntry*> finalPreciseEntries;
+
+			report.mPreciseSamplingRootEntry = preciseEntries[newPreciseEntries[0].entryIdx];
+			finalPreciseEntries.push_back(&report.mPreciseSamplingRootEntry);
+
+			finalPreciseHierarchyTodo.push(0);
+
+			while(!finalPreciseHierarchyTodo.empty())
+			{
+				UINT32 curEntryIdx = finalPreciseHierarchyTodo.top();
+				finalPreciseHierarchyTodo.pop();
+
+				TempEntry& curEntry = newPreciseEntries[curEntryIdx];
+
+				CPUProfilerPreciseSamplingEntry* preciseEntry = finalPreciseEntries[curEntryIdx];
+
+				preciseEntry->childEntries.resize(curEntry.childIndexes.size());
+				UINT32 idx = 0;
+
+				for(auto& childIdx : curEntry.childIndexes)
+				{
+					TempEntry& childEntry = newPreciseEntries[childIdx];
+					preciseEntry->childEntries[idx] = preciseEntries[childEntry.entryIdx];
+
+					finalPreciseEntries.push_back(&preciseEntry->childEntries.back());
+					finalPreciseHierarchyTodo.push(childIdx);
+					idx++;
+				}
+			}
+		}
+
+		return report;
+	}
+
+	void ProfilerCPU::estimateTimerOverhead()
+	{
+		// Get an idea of how long timer calls and RDTSC takes
+		const UINT32 reps = 1000, sampleReps = 20;
+
+		mBasicTimerOverhead = 1000000.0;
+		mPreciseTimerOverhead = 1000000;
+		for (UINT32 tries = 0; tries < 20; tries++) 
+		{
+			Timer timer;
+			for (UINT32 i = 0; i < reps; i++) 
+			{
+				timer.start();
+				timer.stop();
+			}
+
+			double avgTime = double(timer.time)/double(reps);
+			if (avgTime < mBasicTimerOverhead)
+				mBasicTimerOverhead = avgTime;
+
+			TimerPrecise timerPrecise;
+			for (UINT32 i = 0; i < reps; i++) 
+			{
+				timerPrecise.start();
+				timerPrecise.stop();
+			}
+
+			UINT64 avgCycles = timerPrecise.cycles/reps;
+			if (avgCycles < mPreciseTimerOverhead)
+				mPreciseTimerOverhead = avgCycles;
+		}
+
+		mBasicSamplingOverheadMs = 1000000.0;
+		mPreciseSamplingOverheadMs = 1000000.0;
+		mBasicSamplingOverheadCycles = 1000000;
+		mPreciseSamplingOverheadCycles = 1000000;
+		for (UINT32 tries = 0; tries < 3; tries++) 
+		{
+			/************************************************************************/
+			/* 				AVERAGE TIME IN MS FOR BASIC SAMPLING                   */
+			/************************************************************************/
+
+			Timer timerA;
+			timerA.start();
+
+			beginThread("Main");
+
+			// Two different cases that can effect performance, one where
+			// sample already exists and other where new one needs to be created
+			for (UINT32 i = 0; i < sampleReps; i++) 
+			{
+				beginSample("TestAvg1");
+				endSample("TestAvg1");
+				beginSample("TestAvg2");
+				endSample("TestAvg2");
+				beginSample("TestAvg3");
+				endSample("TestAvg3");
+				beginSample("TestAvg4");
+				endSample("TestAvg4");
+				beginSample("TestAvg5");
+				endSample("TestAvg5");
+				beginSample("TestAvg6");
+				endSample("TestAvg6");
+				beginSample("TestAvg7");
+				endSample("TestAvg7");
+				beginSample("TestAvg8");
+				endSample("TestAvg8");
+				beginSample("TestAvg9");
+				endSample("TestAvg9");
+				beginSample("TestAvg10");
+				endSample("TestAvg10");
+			}
+
+			for (UINT32 i = 0; i < sampleReps * 5; i++) 
+			{
+				beginSample(("TestAvg#" + toString(i)).c_str());
+				endSample(("TestAvg#" + toString(i)).c_str());
+			}
+
+			endThread();
+
+			timerA.stop();
+
+			reset();
+
+			double avgTimeBasic = double(timerA.time)/double(sampleReps * 10 + sampleReps * 5) - mBasicTimerOverhead;
+			if (avgTimeBasic < mBasicSamplingOverheadMs)
+				mBasicSamplingOverheadMs = avgTimeBasic;
+
+			/************************************************************************/
+			/* 					AVERAGE CYCLES FOR BASIC SAMPLING                   */
+			/************************************************************************/
+
+			TimerPrecise timerPreciseA;
+			timerPreciseA.start();
+
+			beginThread("Main");
+
+			// Two different cases that can effect performance, one where
+			// sample already exists and other where new one needs to be created
+			for (UINT32 i = 0; i < sampleReps; i++) 
+			{
+				beginSample("TestAvg1");
+				endSample("TestAvg1");
+				beginSample("TestAvg2");
+				endSample("TestAvg2");
+				beginSample("TestAvg3");
+				endSample("TestAvg3");
+				beginSample("TestAvg4");
+				endSample("TestAvg4");
+				beginSample("TestAvg5");
+				endSample("TestAvg5");
+				beginSample("TestAvg6");
+				endSample("TestAvg6");
+				beginSample("TestAvg7");
+				endSample("TestAvg7");
+				beginSample("TestAvg8");
+				endSample("TestAvg8");
+				beginSample("TestAvg9");
+				endSample("TestAvg9");
+				beginSample("TestAvg10");
+				endSample("TestAvg10");
+			}
+
+			for (UINT32 i = 0; i < sampleReps * 5; i++) 
+			{
+				beginSample(("TestAvg#" + toString(i)).c_str());
+				endSample(("TestAvg#" + toString(i)).c_str());
+			}
+
+			endThread();
+			timerPreciseA.stop();
+
+			reset();
+
+			UINT64 avgCyclesBasic = timerPreciseA.cycles/(sampleReps * 10 + sampleReps * 5) - mPreciseTimerOverhead;
+			if (avgCyclesBasic < mBasicSamplingOverheadCycles)
+				mBasicSamplingOverheadCycles = avgCyclesBasic;
+
+			/************************************************************************/
+			/* 				AVERAGE TIME IN MS FOR PRECISE SAMPLING                 */
+			/************************************************************************/
+
+			Timer timerB;
+			timerB.start();
+			beginThread("Main");
+
+			// Two different cases that can effect performance, one where
+			// sample already exists and other where new one needs to be created
+			for (UINT32 i = 0; i < sampleReps; i++) 
+			{
+				beginSamplePrecise("TestAvg1");
+				endSamplePrecise("TestAvg1");
+				beginSamplePrecise("TestAvg2");
+				endSamplePrecise("TestAvg2");
+				beginSamplePrecise("TestAvg3");
+				endSamplePrecise("TestAvg3");
+				beginSamplePrecise("TestAvg4");
+				endSamplePrecise("TestAvg4");
+				beginSamplePrecise("TestAvg5");
+				endSamplePrecise("TestAvg5");
+				beginSamplePrecise("TestAvg6");
+				endSamplePrecise("TestAvg6");
+				beginSamplePrecise("TestAvg7");
+				endSamplePrecise("TestAvg7");
+				beginSamplePrecise("TestAvg8");
+				endSamplePrecise("TestAvg8");
+				beginSamplePrecise("TestAvg9");
+				endSamplePrecise("TestAvg9");
+				beginSamplePrecise("TestAvg10");
+				endSamplePrecise("TestAvg10");
+			}
+
+			for (UINT32 i = 0; i < sampleReps * 5; i++) 
+			{
+				beginSamplePrecise(("TestAvg#" + toString(i)).c_str());
+				endSamplePrecise(("TestAvg#" + toString(i)).c_str());
+			}
+
+			endThread();
+			timerB.stop();
+
+			reset();
+
+			double avgTimesPrecise = timerB.time/(sampleReps * 10 + sampleReps * 5);
+			if (avgTimesPrecise < mPreciseSamplingOverheadMs)
+				mPreciseSamplingOverheadMs = avgTimesPrecise;
+
+			/************************************************************************/
+			/* 				AVERAGE CYCLES FOR PRECISE SAMPLING                     */
+			/************************************************************************/
+
+			TimerPrecise timerPreciseB;
+			timerPreciseB.start();
+			beginThread("Main");
+
+			// Two different cases that can effect performance, one where
+			// sample already exists and other where new one needs to be created
+			for (UINT32 i = 0; i < sampleReps; i++) 
+			{
+				beginSamplePrecise("TestAvg1");
+				endSamplePrecise("TestAvg1");
+				beginSamplePrecise("TestAvg2");
+				endSamplePrecise("TestAvg2");
+				beginSamplePrecise("TestAvg3");
+				endSamplePrecise("TestAvg3");
+				beginSamplePrecise("TestAvg4");
+				endSamplePrecise("TestAvg4");
+				beginSamplePrecise("TestAvg5");
+				endSamplePrecise("TestAvg5");
+				beginSamplePrecise("TestAvg6");
+				endSamplePrecise("TestAvg6");
+				beginSamplePrecise("TestAvg7");
+				endSamplePrecise("TestAvg7");
+				beginSamplePrecise("TestAvg8");
+				endSamplePrecise("TestAvg8");
+				beginSamplePrecise("TestAvg9");
+				endSamplePrecise("TestAvg9");
+				beginSamplePrecise("TestAvg10");
+				endSamplePrecise("TestAvg10");
+			}
+
+			for (UINT32 i = 0; i < sampleReps * 5; i++) 
+			{
+				beginSamplePrecise(("TestAvg#" + toString(i)).c_str());
+				endSamplePrecise(("TestAvg#" + toString(i)).c_str());
+			}
+
+			endThread();
+			timerPreciseB.stop();
+
+			reset();
+
+			UINT64 avgCyclesPrecise = timerPreciseB.cycles/(sampleReps * 10 + sampleReps * 5);
+			if (avgCyclesPrecise < mPreciseSamplingOverheadCycles)
+				mPreciseSamplingOverheadCycles = avgCyclesPrecise;
+		}
+	}
+
+	CPUProfilerBasicSamplingEntry::Data::Data()
+		:numCalls(0), avgTimeMs(0.0), maxTimeMs(0.0), totalTimeMs(0.0),
+		avgSelfTimeMs(0.0), totalSelfTimeMs(0.0), estimatedSelfOverheadMs(0.0),
+		estimatedOverheadMs(0.0), pctOfParent(1.0f)
+	{ }
+
+	CPUProfilerPreciseSamplingEntry::Data::Data()
+		:numCalls(0), avgCycles(0), maxCycles(0), totalCycles(0),
+		avgSelfCycles(0), totalSelfCycles(0), estimatedSelfOverhead(0),
+		estimatedOverhead(0), pctOfParent(1.0f)
+	{ }
+
+	CPUProfilerReport::CPUProfilerReport()
+	{
+
+	}
+
+	ProfilerCPU& gProfilerCPU()
+	{
+		return ProfilerCPU::instance();
+	}
 }

+ 215 - 210
BansheeCore/Source/BsRenderTexture.cpp

@@ -1,212 +1,217 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsRenderTexture.h"
-#include "BsException.h"
-#include "BsPixelBuffer.h"
-#include "BsTexture.h"
-#include "BsTextureManager.h"
-#include "BsResources.h"
-#include "BsCoreThread.h"
-#include "BsFrameAlloc.h"
-
-namespace BansheeEngine
-{
-	RenderTextureProperties::RenderTextureProperties(const RENDER_TEXTURE_DESC& desc, bool requiresFlipping)
-	{
-		HTexture texture = desc.colorSurface.texture;
-
-		if (texture.isLoaded())
-		{
-			const TextureProperties& props = texture->getProperties();
-			construct(&props, requiresFlipping);
-		}
-		else
-			construct(nullptr, requiresFlipping);
-	}
-
-	RenderTextureProperties::RenderTextureProperties(const RENDER_TEXTURE_CORE_DESC& desc, bool requiresFlipping)
-	{
-		SPtr<TextureCore> texture = desc.colorSurface.texture;
-
-		if (texture != nullptr)
-		{
-			const TextureProperties& props = texture->getProperties();
-			construct(&props, requiresFlipping);
-		}
-		else
-			construct(nullptr, requiresFlipping);
-	}
-
-	void RenderTextureProperties::construct(const TextureProperties* textureProps, bool requiresFlipping)
-	{
-		if (textureProps != nullptr)
-		{
-			mWidth = textureProps->getWidth();
-			mHeight = textureProps->getHeight();
-			mColorDepth = BansheeEngine::PixelUtil::getNumElemBits(textureProps->getFormat());
-			mHwGamma = textureProps->isHardwareGammaEnabled();
-			mMultisampleCount = textureProps->getMultisampleCount();
-		}
-
-		mActive = true;
-		mIsWindow = false;
-		mRequiresTextureFlipping = requiresFlipping;
-	}
-
-	RenderTextureCore::RenderTextureCore(const RENDER_TEXTURE_CORE_DESC& desc)
-		:mColorSurface(nullptr), mDepthStencilSurface(nullptr), mDesc(desc)
-	{ }
-
-	RenderTextureCore::~RenderTextureCore()
-	{ 
-		if (mColorSurface != nullptr)
-			TextureCore::releaseView(mColorSurface);
-
-		if (mDepthStencilSurface != nullptr)
-			TextureCore::releaseView(mDepthStencilSurface);
-	}
-
-	void RenderTextureCore::initialize()
-	{
-		RenderTargetCore::initialize();
-
-		const RENDER_SURFACE_CORE_DESC& colorSurface = mDesc.colorSurface;
-		if (colorSurface.texture != nullptr)
-		{
-			SPtr<TextureCore> texture = colorSurface.texture;
-
-			if (texture->getProperties().getUsage() != TU_RENDERTARGET)
-				BS_EXCEPT(InvalidParametersException, "Provided texture is not created with render target usage.");
-
-			mColorSurface = TextureCore::requestView(texture, colorSurface.mipLevel, 1,
-				colorSurface.face, 1, GVU_RENDERTARGET);
-		}
-
-		const RENDER_SURFACE_CORE_DESC& depthStencilSurface = mDesc.depthStencilSurface;
-		if (depthStencilSurface.texture != nullptr)
-		{
-			SPtr<TextureCore> texture = depthStencilSurface.texture;
-
-			if (texture->getProperties().getUsage() != TU_DEPTHSTENCIL)
-				BS_EXCEPT(InvalidParametersException, "Provided texture is not created with depth stencil usage.");
-
-			mDepthStencilSurface = TextureCore::requestView(texture, depthStencilSurface.mipLevel, 1,
-				depthStencilSurface.face, 1, GVU_DEPTHSTENCIL);
-		}
-
-		throwIfBuffersDontMatch();
-
-		assert(mColorSurface != nullptr);
-		assert(mColorSurface->getTexture() != nullptr);
-
-		SPtr<TextureCore> colorTexture = mColorSurface->getTexture();
-		const TextureProperties& texProps = colorTexture->getProperties();
-
-		if ((mColorSurface->getFirstArraySlice() + mColorSurface->getNumArraySlices()) > texProps.getNumFaces())
-		{
-			BS_EXCEPT(InvalidParametersException, "Provided number of faces is out of range. Face: " +
-				toString(mColorSurface->getFirstArraySlice() + mColorSurface->getNumArraySlices()) +
-				". Max num faces: " + toString(texProps.getNumFaces()));
-		}
-
-		if (mColorSurface->getMostDetailedMip() > texProps.getNumMipmaps())
-		{
-			BS_EXCEPT(InvalidParametersException, "Provided number of mip maps is out of range. Mip level: " +
-				toString(mColorSurface->getMostDetailedMip()) + ". Max num mipmaps: " + toString(texProps.getNumMipmaps()));
-		}
-	}
-
-	void RenderTextureCore::syncToCore(const CoreSyncData& data)
-	{
-		RenderTextureProperties& props = const_cast<RenderTextureProperties&>(getProperties());
-		props = data.getData<RenderTextureProperties>();
-	}
-
-	const RenderTextureProperties& RenderTextureCore::getProperties() const
-	{
-		return static_cast<const RenderTextureProperties&>(getPropertiesInternal());
-	}
-
-	void RenderTextureCore::throwIfBuffersDontMatch() const
-	{
-		if (mColorSurface == nullptr || mDepthStencilSurface == nullptr)
-			return;
-
-		const TextureProperties& colorProps = mColorSurface->getTexture()->getProperties();
-		const TextureProperties& depthProps = mDepthStencilSurface->getTexture()->getProperties();
-
-		if (colorProps.getWidth() != depthProps.getWidth() ||
-			colorProps.getHeight() != depthProps.getHeight() ||
-			colorProps.getMultisampleCount() != depthProps.getMultisampleCount())
-		{
-			String errorInfo = "\nWidth: " + toString(colorProps.getWidth()) + "/" + toString(depthProps.getWidth());
-			errorInfo += "\nHeight: " + toString(colorProps.getHeight()) + "/" + toString(depthProps.getHeight());
-			errorInfo += "\nMultisample Count: " + toString(colorProps.getMultisampleCount()) + "/" + toString(depthProps.getMultisampleCount());
-
-			BS_EXCEPT(InvalidParametersException, "Provided texture and depth stencil buffer don't match!" + errorInfo);
-		}
-	}
-
-	RenderTexturePtr RenderTexture::create(TextureType textureType, UINT32 width, UINT32 height, 
-		PixelFormat format, bool hwGamma, UINT32 multisampleCount, 
-		bool createDepth, PixelFormat depthStencilFormat)
-	{
-		return TextureManager::instance().createRenderTexture(textureType, width, height, format, hwGamma, 
-			multisampleCount, createDepth, depthStencilFormat);
-	}
-
-	RenderTexturePtr RenderTexture::create(const RENDER_TEXTURE_DESC& desc)
-	{
-		return TextureManager::instance().createRenderTexture(desc);
-	}
-
-	SPtr<RenderTextureCore> RenderTexture::getCore() const 
-	{ 
-		return std::static_pointer_cast<RenderTextureCore>(mCoreSpecific); 
-	}
-
-	RenderTexture::RenderTexture(const RENDER_TEXTURE_DESC& desc)
-	{
-		mDesc = desc;
-
-		if (desc.colorSurface.texture != nullptr)
-			mBindableColorTex = desc.colorSurface.texture;
-
-		if (desc.depthStencilSurface.texture != nullptr)
-			mBindableDepthStencilTex = desc.depthStencilSurface.texture;
-	}
-
-	SPtr<CoreObjectCore> RenderTexture::createCore() const
-	{
-		RENDER_TEXTURE_CORE_DESC coreDesc;
-
-		if (mDesc.colorSurface.texture.isLoaded())
-			coreDesc.colorSurface.texture = mDesc.colorSurface.texture->getCore();
-
-		if (mDesc.depthStencilSurface.texture.isLoaded())
-			coreDesc.depthStencilSurface.texture = mDesc.depthStencilSurface.texture->getCore();
-
-		coreDesc.colorSurface.face = mDesc.colorSurface.face;
-		coreDesc.colorSurface.mipLevel = mDesc.colorSurface.mipLevel;
-		coreDesc.depthStencilSurface.face = mDesc.depthStencilSurface.face;
-		coreDesc.depthStencilSurface.mipLevel = mDesc.depthStencilSurface.mipLevel;
-
-		return TextureCoreManager::instance().createRenderTextureInternal(coreDesc);
-	}
-
-	CoreSyncData RenderTexture::syncToCore(FrameAlloc* allocator)
-	{
-		UINT32 size = sizeof(RenderTextureProperties);
-		UINT8* buffer = allocator->alloc(size);
-
-		RenderTextureProperties& props = const_cast<RenderTextureProperties&>(getProperties());
-
-		memcpy(buffer, &props, size);
-		return CoreSyncData(buffer, size);
-	}
-
-	const RenderTextureProperties& RenderTexture::getProperties() const
-	{
-		return static_cast<const RenderTextureProperties&>(getPropertiesInternal());
-	}
-}
+#include "BsRenderTexture.h"
+#include "BsException.h"
+#include "BsPixelBuffer.h"
+#include "BsTexture.h"
+#include "BsTextureManager.h"
+#include "BsResources.h"
+#include "BsCoreThread.h"
+#include "BsFrameAlloc.h"
+
+namespace BansheeEngine
+{
+	RenderTextureProperties::RenderTextureProperties(const RENDER_TEXTURE_DESC& desc, bool requiresFlipping)
+	{
+		HTexture texture = desc.colorSurface.texture;
+
+		if (texture.isLoaded())
+		{
+			const TextureProperties& props = texture->getProperties();
+			construct(&props, requiresFlipping);
+		}
+		else
+			construct(nullptr, requiresFlipping);
+	}
+
+	RenderTextureProperties::RenderTextureProperties(const RENDER_TEXTURE_CORE_DESC& desc, bool requiresFlipping)
+	{
+		SPtr<TextureCore> texture = desc.colorSurface.texture;
+
+		if (texture != nullptr)
+		{
+			const TextureProperties& props = texture->getProperties();
+			construct(&props, requiresFlipping);
+		}
+		else
+			construct(nullptr, requiresFlipping);
+	}
+
+	void RenderTextureProperties::construct(const TextureProperties* textureProps, bool requiresFlipping)
+	{
+		if (textureProps != nullptr)
+		{
+			mWidth = textureProps->getWidth();
+			mHeight = textureProps->getHeight();
+			mColorDepth = BansheeEngine::PixelUtil::getNumElemBits(textureProps->getFormat());
+			mHwGamma = textureProps->isHardwareGammaEnabled();
+			mMultisampleCount = textureProps->getMultisampleCount();
+		}
+
+		mActive = true;
+		mIsWindow = false;
+		mRequiresTextureFlipping = requiresFlipping;
+	}
+
+	RenderTextureCore::RenderTextureCore(const RENDER_TEXTURE_CORE_DESC& desc)
+		:mColorSurface(nullptr), mDepthStencilSurface(nullptr), mDesc(desc)
+	{ }
+
+	RenderTextureCore::~RenderTextureCore()
+	{ 
+		if (mColorSurface != nullptr)
+			TextureCore::releaseView(mColorSurface);
+
+		if (mDepthStencilSurface != nullptr)
+			TextureCore::releaseView(mDepthStencilSurface);
+	}
+
+	void RenderTextureCore::initialize()
+	{
+		RenderTargetCore::initialize();
+
+		const RENDER_SURFACE_CORE_DESC& colorSurface = mDesc.colorSurface;
+		if (colorSurface.texture != nullptr)
+		{
+			SPtr<TextureCore> texture = colorSurface.texture;
+
+			if (texture->getProperties().getUsage() != TU_RENDERTARGET)
+				BS_EXCEPT(InvalidParametersException, "Provided texture is not created with render target usage.");
+
+			mColorSurface = TextureCore::requestView(texture, colorSurface.mipLevel, 1,
+				colorSurface.face, 1, GVU_RENDERTARGET);
+		}
+
+		const RENDER_SURFACE_CORE_DESC& depthStencilSurface = mDesc.depthStencilSurface;
+		if (depthStencilSurface.texture != nullptr)
+		{
+			SPtr<TextureCore> texture = depthStencilSurface.texture;
+
+			if (texture->getProperties().getUsage() != TU_DEPTHSTENCIL)
+				BS_EXCEPT(InvalidParametersException, "Provided texture is not created with depth stencil usage.");
+
+			mDepthStencilSurface = TextureCore::requestView(texture, depthStencilSurface.mipLevel, 1,
+				depthStencilSurface.face, 1, GVU_DEPTHSTENCIL);
+		}
+
+		throwIfBuffersDontMatch();
+
+		assert(mColorSurface != nullptr);
+		assert(mColorSurface->getTexture() != nullptr);
+
+		SPtr<TextureCore> colorTexture = mColorSurface->getTexture();
+		const TextureProperties& texProps = colorTexture->getProperties();
+
+		if ((mColorSurface->getFirstArraySlice() + mColorSurface->getNumArraySlices()) > texProps.getNumFaces())
+		{
+			BS_EXCEPT(InvalidParametersException, "Provided number of faces is out of range. Face: " +
+				toString(mColorSurface->getFirstArraySlice() + mColorSurface->getNumArraySlices()) +
+				". Max num faces: " + toString(texProps.getNumFaces()));
+		}
+
+		if (mColorSurface->getMostDetailedMip() > texProps.getNumMipmaps())
+		{
+			BS_EXCEPT(InvalidParametersException, "Provided number of mip maps is out of range. Mip level: " +
+				toString(mColorSurface->getMostDetailedMip()) + ". Max num mipmaps: " + toString(texProps.getNumMipmaps()));
+		}
+	}
+
+	SPtr<RenderTextureCore> RenderTextureCore::create(const RENDER_TEXTURE_CORE_DESC& desc)
+	{
+		return TextureCoreManager::instance().createRenderTexture(desc);
+	}
+
+	void RenderTextureCore::syncToCore(const CoreSyncData& data)
+	{
+		RenderTextureProperties& props = const_cast<RenderTextureProperties&>(getProperties());
+		props = data.getData<RenderTextureProperties>();
+	}
+
+	const RenderTextureProperties& RenderTextureCore::getProperties() const
+	{
+		return static_cast<const RenderTextureProperties&>(getPropertiesInternal());
+	}
+
+	void RenderTextureCore::throwIfBuffersDontMatch() const
+	{
+		if (mColorSurface == nullptr || mDepthStencilSurface == nullptr)
+			return;
+
+		const TextureProperties& colorProps = mColorSurface->getTexture()->getProperties();
+		const TextureProperties& depthProps = mDepthStencilSurface->getTexture()->getProperties();
+
+		if (colorProps.getWidth() != depthProps.getWidth() ||
+			colorProps.getHeight() != depthProps.getHeight() ||
+			colorProps.getMultisampleCount() != depthProps.getMultisampleCount())
+		{
+			String errorInfo = "\nWidth: " + toString(colorProps.getWidth()) + "/" + toString(depthProps.getWidth());
+			errorInfo += "\nHeight: " + toString(colorProps.getHeight()) + "/" + toString(depthProps.getHeight());
+			errorInfo += "\nMultisample Count: " + toString(colorProps.getMultisampleCount()) + "/" + toString(depthProps.getMultisampleCount());
+
+			BS_EXCEPT(InvalidParametersException, "Provided texture and depth stencil buffer don't match!" + errorInfo);
+		}
+	}
+
+	RenderTexturePtr RenderTexture::create(TextureType textureType, UINT32 width, UINT32 height, 
+		PixelFormat format, bool hwGamma, UINT32 multisampleCount, 
+		bool createDepth, PixelFormat depthStencilFormat)
+	{
+		return TextureManager::instance().createRenderTexture(textureType, width, height, format, hwGamma, 
+			multisampleCount, createDepth, depthStencilFormat);
+	}
+
+	RenderTexturePtr RenderTexture::create(const RENDER_TEXTURE_DESC& desc)
+	{
+		return TextureManager::instance().createRenderTexture(desc);
+	}
+
+	SPtr<RenderTextureCore> RenderTexture::getCore() const 
+	{ 
+		return std::static_pointer_cast<RenderTextureCore>(mCoreSpecific); 
+	}
+
+	RenderTexture::RenderTexture(const RENDER_TEXTURE_DESC& desc)
+	{
+		mDesc = desc;
+
+		if (desc.colorSurface.texture != nullptr)
+			mBindableColorTex = desc.colorSurface.texture;
+
+		if (desc.depthStencilSurface.texture != nullptr)
+			mBindableDepthStencilTex = desc.depthStencilSurface.texture;
+	}
+
+	SPtr<CoreObjectCore> RenderTexture::createCore() const
+	{
+		RENDER_TEXTURE_CORE_DESC coreDesc;
+
+		if (mDesc.colorSurface.texture.isLoaded())
+			coreDesc.colorSurface.texture = mDesc.colorSurface.texture->getCore();
+
+		if (mDesc.depthStencilSurface.texture.isLoaded())
+			coreDesc.depthStencilSurface.texture = mDesc.depthStencilSurface.texture->getCore();
+
+		coreDesc.colorSurface.face = mDesc.colorSurface.face;
+		coreDesc.colorSurface.mipLevel = mDesc.colorSurface.mipLevel;
+		coreDesc.depthStencilSurface.face = mDesc.depthStencilSurface.face;
+		coreDesc.depthStencilSurface.mipLevel = mDesc.depthStencilSurface.mipLevel;
+
+		return TextureCoreManager::instance().createRenderTextureInternal(coreDesc);
+	}
+
+	CoreSyncData RenderTexture::syncToCore(FrameAlloc* allocator)
+	{
+		UINT32 size = sizeof(RenderTextureProperties);
+		UINT8* buffer = allocator->alloc(size);
+
+		RenderTextureProperties& props = const_cast<RenderTextureProperties&>(getProperties());
+
+		memcpy(buffer, &props, size);
+		return CoreSyncData(buffer, size);
+	}
+
+	const RenderTextureProperties& RenderTexture::getProperties() const
+	{
+		return static_cast<const RenderTextureProperties&>(getPropertiesInternal());
+	}
+}

+ 1018 - 1004
BansheeCore/Source/Win32/BsWin32Platform.cpp

@@ -1,1007 +1,1021 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "Win32/BsWin32Platform.h"
-#include "BsRenderWindow.h"
-#include "BsPixelUtil.h"
-#include "BsCoreApplication.h"
-#include "BsDebug.h"
-#include "BsRenderWindowManager.h"
-#include "Win32/BsWin32Defs.h"
-#include "Win32/BsWin32DropTarget.h"
-#include "Win32/BsWin32PlatformData.h"
-
-namespace BansheeEngine
-{
-	Event<void(const Vector2I&, OSPointerButtonStates)> Platform::onCursorMoved;
-	Event<void(const Vector2I&, OSMouseButton button, OSPointerButtonStates)> Platform::onCursorButtonPressed;
-	Event<void(const Vector2I&, OSMouseButton button, OSPointerButtonStates)> Platform::onCursorButtonReleased;
-	Event<void(const Vector2I&, OSPointerButtonStates)> Platform::onCursorDoubleClick;
-	Event<void(InputCommandType)> Platform::onInputCommand;
-	Event<void(float)> Platform::onMouseWheelScrolled;
-	Event<void(UINT32)> Platform::onCharInput;
-
-	Event<void(RenderWindowCore*)> Platform::onMouseLeftWindow;
-	Event<void()> Platform::onMouseCaptureChanged;
-
-	Platform::Pimpl* Platform::mData = bs_new<Platform::Pimpl>();
-
-	Platform::~Platform()
-	{
-		bs_delete(mData);
-		mData = nullptr;
-	}
-
-	Vector2I Platform::getCursorPosition()
-	{
-		Vector2I screenPos;
-
-		POINT cursorPos;
-		GetCursorPos(&cursorPos);
-
-		screenPos.x = cursorPos.x;
-		screenPos.y = cursorPos.y;
-
-		return screenPos;
-	}
-
-	void Platform::setCursorPosition(const Vector2I& screenPos)
-	{
-		SetCursorPos(screenPos.x, screenPos.y);
-	}
-
-	void Platform::captureMouse(const RenderWindow& window)
-	{
-		RenderWindowPtr primaryWindow = gCoreApplication().getPrimaryWindow();
-		UINT64 hwnd;
-		primaryWindow->getCustomAttribute("WINDOW", &hwnd);
-		
-		PostMessage((HWND)hwnd, WM_BS_SETCAPTURE, WPARAM((HWND)hwnd), 0);
-	}
-
-	void Platform::releaseMouseCapture()
-	{
-		RenderWindowPtr primaryWindow = gCoreApplication().getPrimaryWindow();
-		UINT64 hwnd;
-		primaryWindow->getCustomAttribute("WINDOW", &hwnd);
-
-		PostMessage((HWND)hwnd, WM_BS_RELEASECAPTURE, WPARAM((HWND)hwnd), 0);
-	}
-
-	bool Platform::isPointOverWindow(const RenderWindow& window, const Vector2I& screenPos)
-	{
-		RenderWindowPtr primaryWindow = gCoreApplication().getPrimaryWindow();
-
-		POINT point;
-		point.x = screenPos.x;
-		point.y = screenPos.y;
-
-		UINT64 hwndToCheck;
-		window.getCustomAttribute("WINDOW", &hwndToCheck);
-
-		HWND hwndUnderPos = WindowFromPoint(point);
-		return hwndUnderPos == (HWND)hwndToCheck;
-	}
-
-	void Platform::hideCursor()
-	{
-		mData->mIsCursorHidden = true;
-
-		// ShowCursor(FALSE) doesn't work. Presumably because we're in the wrong thread, and using
-		// WM_SETCURSOR in message loop to hide the cursor is smarter solution anyway.
-
-		RenderWindowPtr primaryWindow = gCoreApplication().getPrimaryWindow();
-		UINT64 hwnd;
-		primaryWindow->getCustomAttribute("WINDOW", &hwnd);
-
-		PostMessage((HWND)hwnd, WM_SETCURSOR, WPARAM((HWND)hwnd), (LPARAM)MAKELONG(HTCLIENT, WM_MOUSEMOVE));
-	}
-
-	void Platform::showCursor()
-	{
-		mData->mIsCursorHidden = false;
-
-		// ShowCursor(FALSE) doesn't work. Presumably because we're in the wrong thread, and using
-		// WM_SETCURSOR in message loop to hide the cursor is smarter solution anyway.
-
-		RenderWindowPtr primaryWindow = gCoreApplication().getPrimaryWindow();
-		UINT64 hwnd;
-		primaryWindow->getCustomAttribute("WINDOW", &hwnd);
-
-		PostMessage((HWND)hwnd, WM_SETCURSOR, WPARAM((HWND)hwnd), (LPARAM)MAKELONG(HTCLIENT, WM_MOUSEMOVE));
-	}
-
-	bool Platform::isCursorHidden() 
-	{ 
-		return mData->mIsCursorHidden; 
-	}
-
-	void Platform::clipCursorToWindow(const RenderWindow& window)
-	{
-		UINT64 hwnd;
-		window.getCustomAttribute("WINDOW", &hwnd);
-
-		// Clip cursor to the window
-		RECT clipWindowRect;
-		if(GetWindowRect((HWND)hwnd, &clipWindowRect))
-		{
-			ClipCursor(&clipWindowRect);
-		}
-	}
-
-	void Platform::clipCursorToRect(const Rect2I& screenRect)
-	{
-		RECT clipWindowRect;
-		clipWindowRect.left = screenRect.x;
-		clipWindowRect.top = screenRect.y;
-		clipWindowRect.right = screenRect.x + screenRect.width;
-		clipWindowRect.bottom = screenRect.y + screenRect.height;
-
-		ClipCursor(&clipWindowRect);
-	}
-
-	void Platform::clipCursorDisable()
-	{
-		ClipCursor(NULL);
-	}
-
-	// TODO - Add support for animated custom cursor
-	void Platform::setCursor(PixelData& pixelData, const Vector2I& hotSpot)
-	{
-		if (mData->mUsingCustomCursor)
-		{
-			SetCursor(0);
-			DestroyIcon(mData->mCursor.cursor);
-		}
-
-		mData->mUsingCustomCursor = true;
-
-		HBITMAP hBitmap = Win32Platform::createBitmap(pixelData, false); 
-		HBITMAP hMonoBitmap = CreateBitmap(pixelData.getWidth(), pixelData.getHeight(), 1, 1, nullptr);
-
-		ICONINFO iconinfo = {0};
-		iconinfo.fIcon = FALSE;
-		iconinfo.xHotspot = (DWORD)hotSpot.x;
-		iconinfo.yHotspot = (DWORD)hotSpot.y;
-		iconinfo.hbmMask = hMonoBitmap;
-		iconinfo.hbmColor = hBitmap;
-
-		mData->mCursor.cursor = CreateIconIndirect(&iconinfo);
-		
-		DeleteObject(hBitmap);          
-		DeleteObject(hMonoBitmap);
-
-		// Make sure we notify the message loop to perform the actual cursor update
-		RenderWindowPtr primaryWindow = gCoreApplication().getPrimaryWindow();
-		UINT64 hwnd;
-		primaryWindow->getCustomAttribute("WINDOW", &hwnd);
-
-		PostMessage((HWND)hwnd, WM_SETCURSOR, WPARAM((HWND)hwnd), (LPARAM)MAKELONG(HTCLIENT, WM_MOUSEMOVE));
-	}
-
-	void Platform::setIcon(const PixelData& pixelData)
-	{
-		PixelDataPtr resizedData = PixelData::create(32, 32, 1, PF_R8G8B8A8);
-		PixelUtil::scale(pixelData, *resizedData);
-
-		HBITMAP hBitmap = Win32Platform::createBitmap(pixelData, false);
-		HBITMAP hMonoBitmap = CreateBitmap(pixelData.getWidth(), pixelData.getHeight(), 1, 1, nullptr);
-
-		ICONINFO iconinfo = { 0 };
-		iconinfo.fIcon = TRUE;
-		iconinfo.xHotspot = 0;
-		iconinfo.yHotspot = 0;
-		iconinfo.hbmMask = hMonoBitmap;
-		iconinfo.hbmColor = hBitmap;
-
-		HICON icon = CreateIconIndirect(&iconinfo);
-
-		DeleteObject(hBitmap);
-		DeleteObject(hMonoBitmap);
-
-		// Make sure we notify the message loop to perform the actual cursor update
-		RenderWindowPtr primaryWindow = gCoreApplication().getPrimaryWindow();
-		UINT64 hwnd;
-		primaryWindow->getCustomAttribute("WINDOW", &hwnd);
-		
-		PostMessage((HWND)hwnd, WM_SETICON, WPARAM(ICON_BIG), (LPARAM)icon);
-	}
-
-	void Platform::setCaptionNonClientAreas(const RenderWindowCore& window, const Vector<Rect2I>& nonClientAreas)
-	{
-		BS_LOCK_MUTEX(mData->mSync);
-
-		mData->mNonClientAreas[&window].moveAreas = nonClientAreas;
-	}
-
-	void Platform::setResizeNonClientAreas(const RenderWindowCore& window, const Vector<NonClientResizeArea>& nonClientAreas)
-	{
-		BS_LOCK_MUTEX(mData->mSync);
-
-		mData->mNonClientAreas[&window].resizeAreas = nonClientAreas;
-	}
-
-	void Platform::resetNonClientAreas(const RenderWindowCore& window)
-	{
-		BS_LOCK_MUTEX(mData->mSync);
-
-		auto iterFind = mData->mNonClientAreas.find(&window);
-
-		if (iterFind != end(mData->mNonClientAreas))
-			mData->mNonClientAreas.erase(iterFind);
-	}
-
-	OSDropTarget& Platform::createDropTarget(const RenderWindow* window, int x, int y, unsigned int width, unsigned int height)
-	{
-		Win32DropTarget* win32DropTarget = nullptr;
-		auto iterFind = mData->mDropTargets.dropTargetsPerWindow.find(window);
-		if (iterFind == mData->mDropTargets.dropTargetsPerWindow.end())
-		{
-			UINT64 hwnd;
-			window->getCustomAttribute("WINDOW", &hwnd);
-
-			win32DropTarget = bs_new<Win32DropTarget>((HWND)hwnd);
-			mData->mDropTargets.dropTargetsPerWindow[window] = win32DropTarget;
-
-			{
-				BS_LOCK_MUTEX(mData->mSync);
-				mData->mDropTargets.dropTargetsToInitialize.push_back(win32DropTarget);
-			}
-		}
-		else
-			win32DropTarget = iterFind->second;
-
-		OSDropTarget* newDropTarget = new (bs_alloc<OSDropTarget>()) OSDropTarget(window, x, y, width, height);
-		win32DropTarget->registerDropTarget(newDropTarget);
-
-		return *newDropTarget;
-	}
-
-	void Platform::destroyDropTarget(OSDropTarget& target)
-	{
-		auto iterFind = mData->mDropTargets.dropTargetsPerWindow.find(target.getOwnerWindow());
-		if (iterFind == mData->mDropTargets.dropTargetsPerWindow.end())
-		{
-			LOGWRN("Attempting to destroy a drop target but cannot find its parent window.");
-		}
-		else
-		{
-			Win32DropTarget* win32DropTarget = iterFind->second;
-			win32DropTarget->unregisterDropTarget(&target);
-
-			if(win32DropTarget->getNumDropTargets() == 0)
-			{
-				mData->mDropTargets.dropTargetsPerWindow.erase(iterFind);
-
-				{
-					BS_LOCK_MUTEX(mData->mSync);
-					mData->mDropTargets.dropTargetsToDestroy.push_back(win32DropTarget);
-				}
-			}
-		}
-		
-		BS_PVT_DELETE(OSDropTarget, &target);
-	}
-
-	void Platform::_messagePump()
-	{
-		MSG  msg;
-		while (PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE))
-		{
-			TranslateMessage(&msg);
-			DispatchMessage(&msg);
-		}
-	}
-
-	void Platform::_startUp()
-	{
-		BS_LOCK_MUTEX(mData->mSync);
-
-		mData->mRequiresStartUp = true;
-	}
-
-	void Platform::_update()
-	{
-		for (auto& dropTarget : mData->mDropTargets.dropTargetsPerWindow)
-		{
-			dropTarget.second->update();
-		}
-	}
-
-	void Platform::_coreUpdate()
-	{
-		{
-			BS_LOCK_MUTEX(mData->mSync);
-			if (mData->mRequiresStartUp)
-			{
-				OleInitialize(nullptr);
-
-				mData->mRequiresStartUp = false;
-			}
-		}
-
-		{
-			BS_LOCK_MUTEX(mData->mSync);
-			for (auto& dropTargetToDestroy : mData->mDropTargets.dropTargetsToDestroy)
-			{
-				dropTargetToDestroy->unregisterWithOS();
-				dropTargetToDestroy->Release();
-			}
-
-			mData->mDropTargets.dropTargetsToDestroy.clear();
-		}
-
-		{
-			BS_LOCK_MUTEX(mData->mSync);
-			for (auto& dropTargetToInit : mData->mDropTargets.dropTargetsToInitialize)
-			{
-				dropTargetToInit->registerWithOS();
-			}
-
-			mData->mDropTargets.dropTargetsToInitialize.clear();
-		}
-
-		_messagePump();
-
-		{
-			BS_LOCK_MUTEX(mData->mSync);
-			if (mData->mRequiresShutDown)
-			{
-				OleUninitialize();
-				mData->mRequiresShutDown = false;
-			}
-		}
-	}
-
-	void Platform::_shutDown()
-	{
-		BS_LOCK_MUTEX(mData->mSync);
-
-		mData->mRequiresShutDown = true;
-	}
-
-	bool isShiftPressed = false;
-	bool isCtrlPressed = false;
-
-	/**
-	 * @brief	Translate engine non client area to win32 non client area.
-	 */
-	LRESULT translateNonClientAreaType(NonClientAreaBorderType type)
-	{
-		LRESULT dir = HTCLIENT;
-		switch(type)
-		{
-		case NonClientAreaBorderType::Left:
-			dir = HTLEFT;
-			break;
-		case NonClientAreaBorderType::TopLeft:
-			dir = HTTOPLEFT;
-			break;
-		case NonClientAreaBorderType::Top:
-			dir = HTTOP;
-			break;
-		case NonClientAreaBorderType::TopRight:
-			dir = HTTOPRIGHT;
-			break;
-		case NonClientAreaBorderType::Right:
-			dir = HTRIGHT;
-			break;
-		case NonClientAreaBorderType::BottomRight:
-			dir = HTBOTTOMRIGHT;
-			break;
-		case NonClientAreaBorderType::Bottom:
-			dir = HTBOTTOM;
-			break;
-		case NonClientAreaBorderType::BottomLeft:
-			dir = HTBOTTOMLEFT;
-			break;
-		}
-
-		return dir;
-	}
-
-	/**
-	 * @brief	Method triggered whenever a mouse event happens.
-	 */
-	void getMouseData(HWND hWnd, WPARAM wParam, LPARAM lParam, bool nonClient, Vector2I& mousePos, OSPointerButtonStates& btnStates)
-	{
-		POINT clientPoint;
-
-		clientPoint.x = GET_X_LPARAM(lParam);
-		clientPoint.y = GET_Y_LPARAM(lParam); 
-
-		if (!nonClient)
-			ClientToScreen(hWnd, &clientPoint);
-
-		mousePos.x = clientPoint.x;
-		mousePos.y = clientPoint.y;
-
-		btnStates.mouseButtons[0] = (wParam & MK_LBUTTON) != 0;
-		btnStates.mouseButtons[1] = (wParam & MK_MBUTTON) != 0;
-		btnStates.mouseButtons[2] = (wParam & MK_RBUTTON) != 0;
-		btnStates.shift = (wParam & MK_SHIFT) != 0;
-		btnStates.ctrl = (wParam & MK_CONTROL) != 0;
-	}
-
-	/**
-	 * @brief	Converts a virtual key code into an input command, if possible. Returns true
-	 *			if conversion was done.
-	 */
-	bool getCommand(unsigned int virtualKeyCode, InputCommandType& command)
-	{
-		switch (virtualKeyCode) 
-		{ 
-		case VK_LEFT:
-			command = isShiftPressed ? InputCommandType::SelectLeft : InputCommandType::CursorMoveLeft;
-			return true;
-		case VK_RIGHT:
-			command = isShiftPressed ? InputCommandType::SelectRight : InputCommandType::CursorMoveRight;
-			return true;
-		case VK_UP:
-			command = isShiftPressed ? InputCommandType::SelectUp : InputCommandType::CursorMoveUp;
-			return true;
-		case VK_DOWN:
-			command = isShiftPressed ? InputCommandType::SelectDown : InputCommandType::CursorMoveDown;
-			return true;
-		case VK_ESCAPE:
-			command = InputCommandType::Escape;
-			return true;
-		case VK_RETURN:
-			command = isShiftPressed ? InputCommandType::Return : InputCommandType::Confirm;
-			return true;
-		case VK_BACK:
-			command = InputCommandType::Backspace;
-			return true;
-		case VK_DELETE:
-			command = InputCommandType::Delete;
-			return true;
-		}
-
-		return false;
-	}
-
-	HBITMAP Win32Platform::createBitmap(const PixelData& pixelData, bool premultiplyAlpha)
-	{
-		BITMAPINFO bi;
-
-		ZeroMemory(&bi, sizeof(BITMAPINFO));
-		bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
-		bi.bmiHeader.biWidth = pixelData.getWidth();
-		bi.bmiHeader.biHeight = pixelData.getHeight();
-		bi.bmiHeader.biPlanes = 1;
-		bi.bmiHeader.biBitCount = 32;
-		bi.bmiHeader.biCompression = BI_RGB;
-
-		HDC hDC = GetDC(nullptr);
-
-		void* data = nullptr;
-		HBITMAP hBitmap = CreateDIBSection(hDC, &bi, DIB_RGB_COLORS, (void**)&data, nullptr, 0);
-
-		HDC hBitmapDC = CreateCompatibleDC(hDC);
-		ReleaseDC(nullptr, hDC);
-
-		//Select the bitmaps to DC
-		HBITMAP hOldBitmap = (HBITMAP)SelectObject(hBitmapDC, hBitmap);
-
-		//Scan each pixel of the source bitmap and create the masks
-		Color pixel;
-		DWORD *dst = (DWORD*)data;
-		for (UINT32 y = 0; y < pixelData.getHeight(); ++y)
-		{
-			for (UINT32 x = 0; x < pixelData.getWidth(); ++x)
-			{
-				pixel = pixelData.getColorAt(x, pixelData.getHeight() - y - 1);
-
-				if (premultiplyAlpha)
-				{
-					pixel.r *= pixel.a;
-					pixel.g *= pixel.a;
-					pixel.b *= pixel.a;
-				}
-
-				*dst = pixel.getAsBGRA();
-
-				dst++;
-			}
-		}
-
-		SelectObject(hBitmapDC, hOldBitmap);
-		DeleteDC(hBitmapDC);
-
-		return hBitmap;
-	}
-
-	LRESULT CALLBACK Win32Platform::_win32WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
-	{
-		if (uMsg == WM_CREATE)
-		{	// Store pointer to Win32Window in user data area
-			SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)(((LPCREATESTRUCT)lParam)->lpCreateParams));
-
-			RenderWindowCore* newWindow = (RenderWindowCore*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
-			if (newWindow != nullptr)
-			{
-				const RenderWindowProperties& props = newWindow->getProperties();
-				if (!props.isHidden())
-					ShowWindow(hWnd, SW_SHOWNOACTIVATE);
-
-				if (props.isModal())
-				{
-					if (!mData->mModalWindowStack.empty())
-					{
-						RenderWindowCore* curModalWindow = mData->mModalWindowStack.back();
-
-						UINT64 curHwnd;
-						curModalWindow->getCustomAttribute("WINDOW", &curHwnd);
-						EnableWindow((HWND)curHwnd, FALSE);
-					}
-					else
-					{
-						Vector<RenderWindowCore*> renderWindows = RenderWindowCoreManager::instance().getRenderWindows();
-						for (auto& renderWindow : renderWindows)
-						{
-							if (renderWindow == newWindow)
-								continue;
-
-							UINT64 curHwnd;
-							renderWindow->getCustomAttribute("WINDOW", &curHwnd);
-							EnableWindow((HWND)curHwnd, FALSE);
-						}
-					}
-
-					mData->mModalWindowStack.push_back(newWindow);
-				}
-				else
-				{
-					// A non-modal window was opened while another modal one is open:
-					// immediately deactivate it and make sure the modal windows stay on top
-					if (!mData->mModalWindowStack.empty())
-					{
-						EnableWindow((HWND)hWnd, FALSE);
-
-						for (auto window : mData->mModalWindowStack)
-						{
-							UINT64 curHwnd;
-							window->getCustomAttribute("WINDOW", &curHwnd);
-
-							BringWindowToTop((HWND)curHwnd);
-						}
-					}
-				}
-			}
-			else
-				ShowWindow(hWnd, SW_SHOWNOACTIVATE);
-
-			return 0;
-		}
-
-		// look up window instance
-		// note: it is possible to get a WM_SIZE before WM_CREATE
-		RenderWindowCore* win = (RenderWindowCore*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
-		if (!win)
-			return DefWindowProc(hWnd, uMsg, wParam, lParam);
-
-		switch( uMsg )
-		{
-		case WM_DESTROY:
-			{
-				bool reenableWindows = false;
-				if (!mData->mModalWindowStack.empty())
-				{
-					// Start from back because the most common case is closing the top-most modal window
-					for (auto iter = mData->mModalWindowStack.rbegin(); iter != mData->mModalWindowStack.rend(); ++iter)
-					{
-						if (*iter == win)
-						{
-							auto iterFwd = std::next(iter).base(); // erase doesn't accept reverse iter, so convert
-
-							mData->mModalWindowStack.erase(iterFwd);
-							break;
-						}
-					}
-
-					if (!mData->mModalWindowStack.empty()) // Enable next modal window
-					{
-						RenderWindowCore* curModalWindow = mData->mModalWindowStack.back();
-
-						UINT64 curHwnd;
-						curModalWindow->getCustomAttribute("WINDOW", &curHwnd);
-						EnableWindow((HWND)curHwnd, TRUE);
-					}
-					else
-						reenableWindows = true; // No more modal windows, re-enable any remaining window
-				}
-
-				if(reenableWindows)
-				{
-					Vector<RenderWindowCore*> renderWindows = RenderWindowCoreManager::instance().getRenderWindows();
-					for(auto& renderWindow : renderWindows)
-					{
-						HWND curHwnd;
-						renderWindow->getCustomAttribute("WINDOW", &curHwnd);
-						EnableWindow(curHwnd, TRUE);
-					}
-				}
-
-				return 0;
-			}
-		case WM_SETFOCUS:
-			{
-				if (!win->getProperties().hasFocus())
-					win->_windowFocusReceived();
-
-				return 0;
-			}
-		case WM_KILLFOCUS:
-			{
-				if (win->getProperties().hasFocus())
-					win->_windowFocusLost();
-
-				return 0;
-			}
-		case WM_SYSCHAR:
-			// return zero to bypass defProc and signal we processed the message, unless it's an ALT-space
-			if (wParam != VK_SPACE)
-				return 0;
-			break;
-		case WM_MOVE:
-			win->_windowMovedOrResized();
-			return 0;
-		case WM_DISPLAYCHANGE:
-			win->_windowMovedOrResized();
-			break;
-		case WM_SIZE:
-			win->_windowMovedOrResized();
-
-			if (wParam == SIZE_MAXIMIZED)
-				win->_notifyMaximized();
-			else if (wParam == SIZE_MINIMIZED)
-				win->_notifyMinimized();
-			else if (wParam == SIZE_RESTORED)
-				win->_notifyRestored();
-
-			return 0;
-		case WM_SETCURSOR:
-			if(isCursorHidden())
-				SetCursor(nullptr);
-			else
-			{
-				switch (LOWORD(lParam))
-				{
-				case HTTOPLEFT:
-					SetCursor(LoadCursor(0, IDC_SIZENWSE));
-					return 0;
-				case HTTOP:
-					SetCursor(LoadCursor(0, IDC_SIZENS));
-					return 0;
-				case HTTOPRIGHT:
-					SetCursor(LoadCursor(0, IDC_SIZENESW));
-					return 0;
-				case HTLEFT:
-					SetCursor(LoadCursor(0, IDC_SIZEWE));
-					return 0;
-				case HTRIGHT:
-					SetCursor(LoadCursor(0, IDC_SIZEWE));
-					return 0;
-				case HTBOTTOMLEFT:
-					SetCursor(LoadCursor(0, IDC_SIZENESW));
-					return 0;
-				case HTBOTTOM:
-					SetCursor(LoadCursor(0, IDC_SIZENS));
-					return 0;
-				case HTBOTTOMRIGHT:
-					SetCursor(LoadCursor(0, IDC_SIZENWSE));
-					return 0;
-				}
-
-				SetCursor(mData->mCursor.cursor);
-			}
-			return true;
-		case WM_GETMINMAXINFO:
-		{
-			// Prevent the window from going smaller than some minimu size
-			((MINMAXINFO*)lParam)->ptMinTrackSize.x = 100;
-			((MINMAXINFO*)lParam)->ptMinTrackSize.y = 100;
-
-			// Ensure maximizes window has proper size and doesn't cover the entire screen
-			const POINT ptZero = { 0, 0 };
-			HMONITOR primaryMonitor = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY);
-
-			MONITORINFO monitorInfo;
-			monitorInfo.cbSize = sizeof(MONITORINFO);
-			GetMonitorInfo(primaryMonitor, &monitorInfo);
-
-			((MINMAXINFO*)lParam)->ptMaxPosition.x = monitorInfo.rcWork.left - monitorInfo.rcMonitor.left;
-			((MINMAXINFO*)lParam)->ptMaxPosition.y = monitorInfo.rcWork.top - monitorInfo.rcMonitor.top;
-			((MINMAXINFO*)lParam)->ptMaxSize.x = monitorInfo.rcWork.right - monitorInfo.rcWork.left;
-			((MINMAXINFO*)lParam)->ptMaxSize.y = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top;
-		}
-			break;
-		case WM_CLOSE:
-			{
-				gCoreApplication().quitRequested();
-
-				return 0;
-			}
-		case WM_NCHITTEST:
-			{
-				auto iterFind = mData->mNonClientAreas.find(win);
-				if (iterFind == mData->mNonClientAreas.end())
-					break;
-
-				POINT mousePos;
-				mousePos.x = GET_X_LPARAM(lParam);
-				mousePos.y = GET_Y_LPARAM(lParam); 
-
-				ScreenToClient(hWnd, &mousePos);
-
-				Vector2I mousePosInt;
-				mousePosInt.x = mousePos.x;
-				mousePosInt.y = mousePos.y;
-
-				Vector<NonClientResizeArea>& resizeAreasPerWindow = iterFind->second.resizeAreas;
-				for(auto area : resizeAreasPerWindow)
-				{
-					if(area.area.contains(mousePosInt))
-						return translateNonClientAreaType(area.type);
-				}
-
-				Vector<Rect2I>& moveAreasPerWindow = iterFind->second.moveAreas;
-				for(auto area : moveAreasPerWindow)
-				{
-					if(area.contains(mousePosInt))
-						return HTCAPTION;
-				}
-
-				return HTCLIENT;
-			}
-		case WM_NCLBUTTONDBLCLK:
-			// Maximize/Restore on double-click
-			if (wParam == HTCAPTION)
-			{
-				WINDOWPLACEMENT windowPlacement;
-				windowPlacement.length = sizeof(WINDOWPLACEMENT);
-				GetWindowPlacement(hWnd, &windowPlacement);
-
-				if (windowPlacement.showCmd == SW_MAXIMIZE)
-					ShowWindow(hWnd, SW_RESTORE);
-				else
-					ShowWindow(hWnd, SW_MAXIMIZE);
-
-				return 0;
-			}
-			break;
-		case WM_MOUSELEAVE:
-			{
-				// Note: Right now I track only mouse leaving client area. So it's possible for the "mouse left window" callback
-				// to trigger, while the mouse is still in the non-client area of the window.
-				mData->mIsTrackingMouse = false; // TrackMouseEvent ends when this message is received and needs to be re-applied
-
-				BS_LOCK_MUTEX(mData->mSync);
-
-				if (!onMouseLeftWindow.empty())
-					onMouseLeftWindow(win);
-			}
-			return 0;
-		case WM_LBUTTONUP:
-			{
-				ReleaseCapture();
-
-				Vector2I intMousePos;
-				OSPointerButtonStates btnStates;
-
-				getMouseData(hWnd, wParam, lParam, false, intMousePos, btnStates);
-
-				if(!onCursorButtonReleased.empty())
-					onCursorButtonReleased(intMousePos, OSMouseButton::Left, btnStates);
-
-				return 0;
-			}
-		case WM_MBUTTONUP:
-			{
-				ReleaseCapture();
-
-				Vector2I intMousePos;
-				OSPointerButtonStates btnStates;
-
-				getMouseData(hWnd, wParam, lParam, false, intMousePos, btnStates);
-
-				if(!onCursorButtonReleased.empty())
-					onCursorButtonReleased(intMousePos, OSMouseButton::Middle, btnStates);
-
-				return 0;
-			}
-		case WM_RBUTTONUP:
-			{
-				ReleaseCapture();
-
-				Vector2I intMousePos;
-				OSPointerButtonStates btnStates;
-
-				getMouseData(hWnd, wParam, lParam, false, intMousePos, btnStates);
-
-				if(!onCursorButtonReleased.empty())
-					onCursorButtonReleased(intMousePos, OSMouseButton::Right, btnStates);
-
-				return 0;
-			}
-		case WM_LBUTTONDOWN:
-			{
-				SetCapture(hWnd);
-
-				Vector2I intMousePos;
-				OSPointerButtonStates btnStates;
-
-				getMouseData(hWnd, wParam, lParam, false, intMousePos, btnStates);
-
-				if(!onCursorButtonPressed.empty())
-					onCursorButtonPressed(intMousePos, OSMouseButton::Left, btnStates);
-			}
-			return 0;
-		case WM_MBUTTONDOWN:
-			{
-				SetCapture(hWnd);
-
-				Vector2I intMousePos;
-				OSPointerButtonStates btnStates;
-
-				getMouseData(hWnd, wParam, lParam, false, intMousePos, btnStates);
-
-				if(!onCursorButtonPressed.empty())
-					onCursorButtonPressed(intMousePos, OSMouseButton::Middle, btnStates);
-			}
-			return 0;
-		case WM_RBUTTONDOWN:
-			{
-				SetCapture(hWnd);
-
-				Vector2I intMousePos;
-				OSPointerButtonStates btnStates;
-
-				getMouseData(hWnd, wParam, lParam, false, intMousePos, btnStates);
-
-				if(!onCursorButtonPressed.empty())
-					onCursorButtonPressed(intMousePos, OSMouseButton::Right, btnStates);
-			}
-			return 0;
-		case WM_LBUTTONDBLCLK:
-			{
-				Vector2I intMousePos;
-				OSPointerButtonStates btnStates;
-
-				getMouseData(hWnd, wParam, lParam, false, intMousePos, btnStates);
-
-				if(!onCursorDoubleClick.empty())
-					onCursorDoubleClick(intMousePos, btnStates);
-			}
-			return 0;
-		case WM_NCMOUSEMOVE:
-		case WM_MOUSEMOVE:
-			{
-				// Set up tracking so we get notified when mouse leaves the window
-				if(!mData->mIsTrackingMouse)
-				{
-					TRACKMOUSEEVENT tme = { sizeof(tme) };
-					tme.dwFlags = TME_LEAVE;
-
-					tme.hwndTrack = hWnd;
-					TrackMouseEvent(&tme);
-
-					mData->mIsTrackingMouse = true;
-				}
-
-				Vector2I intMousePos;
-				OSPointerButtonStates btnStates;
-				
-				getMouseData(hWnd, wParam, lParam, uMsg == WM_NCMOUSEMOVE, intMousePos, btnStates);
-
-				if(!onCursorMoved.empty())
-					onCursorMoved(intMousePos, btnStates);
-
-				return 0;
-			}
-		case WM_MOUSEWHEEL:
-			{
-				INT16 wheelDelta = GET_WHEEL_DELTA_WPARAM(wParam);
-
-				float wheelDeltaFlt = wheelDelta / (float)WHEEL_DELTA;
-				if(!onMouseWheelScrolled.empty())
-					onMouseWheelScrolled(wheelDeltaFlt);
-
-				return true;
-			}
-		case WM_SYSKEYDOWN:
-		case WM_KEYDOWN:
-			{
-				if(wParam == VK_SHIFT)
-				{
-					isShiftPressed = true;
-					break;
-				}
-
-				if(wParam == VK_CONTROL)
-				{
-					isCtrlPressed = true;
-					break;
-				}
-
-				InputCommandType command = InputCommandType::Backspace;
-				if(getCommand((unsigned int)wParam, command))
-				{
-					if(!onInputCommand.empty())
-						onInputCommand(command);
-
-					return 0;
-				}
-
-				break;
-			}
-		case WM_SYSKEYUP:
-		case WM_KEYUP:
-			{
-				if(wParam == VK_SHIFT)
-				{
-					isShiftPressed = false;
-				}
-
-				if(wParam == VK_CONTROL)
-				{
-					isCtrlPressed = false;
-				}
-
-				return 0;
-			}
-		case WM_CHAR:
-			{
-				// TODO - Not handling IME input
-
-				// Ignore rarely used special command characters, usually triggered by ctrl+key
-				// combinations. (We want to keep ctrl+key free for shortcuts instead)
-				if (wParam <= 23)
-					break;
-
-				switch (wParam) 
-				{ 
-				case VK_ESCAPE:
-					break; 
-				default:    // displayable character 
-					{
-						UINT8 scanCode = (lParam >> 16) & 0xFF;
-
-						BYTE keyState[256];
-						HKL layout = GetKeyboardLayout(0);
-						if(GetKeyboardState(keyState) == 0)
-							return 0;
-
-						unsigned int vk = MapVirtualKeyEx(scanCode, MAPVK_VSC_TO_VK_EX, layout);
-						if(vk == 0)
-							return 0;
-
-						InputCommandType command = InputCommandType::Backspace;
-						if(getCommand(vk, command)) // We ignore character combinations that are special commands
-							return 0;
-
-						UINT32 finalChar = (UINT32)wParam;
-
-						if(!onCharInput.empty())
-							onCharInput(finalChar);
-
-						return 0;
-					}
-				} 
-
-				break;
-			}
-		case WM_BS_SETCAPTURE:
-			SetCapture(hWnd);
-			break;
-		case WM_BS_RELEASECAPTURE:
-			ReleaseCapture();
-			break;
-		case WM_CAPTURECHANGED:
-			if(!onMouseCaptureChanged.empty())
-				onMouseCaptureChanged();
-			return 0;
-		}
-
-		return DefWindowProc( hWnd, uMsg, wParam, lParam );
-	}
+#include "Win32/BsWin32Platform.h"
+#include "BsRenderWindow.h"
+#include "BsPixelUtil.h"
+#include "BsCoreApplication.h"
+#include "BsDebug.h"
+#include "BsRenderWindowManager.h"
+#include "Win32/BsWin32Defs.h"
+#include "Win32/BsWin32DropTarget.h"
+#include "Win32/BsWin32PlatformData.h"
+#include "TimeAPI.h"
+
+namespace BansheeEngine
+{
+	Event<void(const Vector2I&, OSPointerButtonStates)> Platform::onCursorMoved;
+	Event<void(const Vector2I&, OSMouseButton button, OSPointerButtonStates)> Platform::onCursorButtonPressed;
+	Event<void(const Vector2I&, OSMouseButton button, OSPointerButtonStates)> Platform::onCursorButtonReleased;
+	Event<void(const Vector2I&, OSPointerButtonStates)> Platform::onCursorDoubleClick;
+	Event<void(InputCommandType)> Platform::onInputCommand;
+	Event<void(float)> Platform::onMouseWheelScrolled;
+	Event<void(UINT32)> Platform::onCharInput;
+
+	Event<void(RenderWindowCore*)> Platform::onMouseLeftWindow;
+	Event<void()> Platform::onMouseCaptureChanged;
+
+	Platform::Pimpl* Platform::mData = bs_new<Platform::Pimpl>();
+
+	Platform::~Platform()
+	{
+		bs_delete(mData);
+		mData = nullptr;
+	}
+
+	Vector2I Platform::getCursorPosition()
+	{
+		Vector2I screenPos;
+
+		POINT cursorPos;
+		GetCursorPos(&cursorPos);
+
+		screenPos.x = cursorPos.x;
+		screenPos.y = cursorPos.y;
+
+		return screenPos;
+	}
+
+	void Platform::setCursorPosition(const Vector2I& screenPos)
+	{
+		SetCursorPos(screenPos.x, screenPos.y);
+	}
+
+	void Platform::captureMouse(const RenderWindow& window)
+	{
+		RenderWindowPtr primaryWindow = gCoreApplication().getPrimaryWindow();
+		UINT64 hwnd;
+		primaryWindow->getCustomAttribute("WINDOW", &hwnd);
+		
+		PostMessage((HWND)hwnd, WM_BS_SETCAPTURE, WPARAM((HWND)hwnd), 0);
+	}
+
+	void Platform::releaseMouseCapture()
+	{
+		RenderWindowPtr primaryWindow = gCoreApplication().getPrimaryWindow();
+		UINT64 hwnd;
+		primaryWindow->getCustomAttribute("WINDOW", &hwnd);
+
+		PostMessage((HWND)hwnd, WM_BS_RELEASECAPTURE, WPARAM((HWND)hwnd), 0);
+	}
+
+	bool Platform::isPointOverWindow(const RenderWindow& window, const Vector2I& screenPos)
+	{
+		RenderWindowPtr primaryWindow = gCoreApplication().getPrimaryWindow();
+
+		POINT point;
+		point.x = screenPos.x;
+		point.y = screenPos.y;
+
+		UINT64 hwndToCheck;
+		window.getCustomAttribute("WINDOW", &hwndToCheck);
+
+		HWND hwndUnderPos = WindowFromPoint(point);
+		return hwndUnderPos == (HWND)hwndToCheck;
+	}
+
+	void Platform::hideCursor()
+	{
+		mData->mIsCursorHidden = true;
+
+		// ShowCursor(FALSE) doesn't work. Presumably because we're in the wrong thread, and using
+		// WM_SETCURSOR in message loop to hide the cursor is smarter solution anyway.
+
+		RenderWindowPtr primaryWindow = gCoreApplication().getPrimaryWindow();
+		UINT64 hwnd;
+		primaryWindow->getCustomAttribute("WINDOW", &hwnd);
+
+		PostMessage((HWND)hwnd, WM_SETCURSOR, WPARAM((HWND)hwnd), (LPARAM)MAKELONG(HTCLIENT, WM_MOUSEMOVE));
+	}
+
+	void Platform::showCursor()
+	{
+		mData->mIsCursorHidden = false;
+
+		// ShowCursor(FALSE) doesn't work. Presumably because we're in the wrong thread, and using
+		// WM_SETCURSOR in message loop to hide the cursor is smarter solution anyway.
+
+		RenderWindowPtr primaryWindow = gCoreApplication().getPrimaryWindow();
+		UINT64 hwnd;
+		primaryWindow->getCustomAttribute("WINDOW", &hwnd);
+
+		PostMessage((HWND)hwnd, WM_SETCURSOR, WPARAM((HWND)hwnd), (LPARAM)MAKELONG(HTCLIENT, WM_MOUSEMOVE));
+	}
+
+	bool Platform::isCursorHidden() 
+	{ 
+		return mData->mIsCursorHidden; 
+	}
+
+	void Platform::clipCursorToWindow(const RenderWindow& window)
+	{
+		UINT64 hwnd;
+		window.getCustomAttribute("WINDOW", &hwnd);
+
+		// Clip cursor to the window
+		RECT clipWindowRect;
+		if(GetWindowRect((HWND)hwnd, &clipWindowRect))
+		{
+			ClipCursor(&clipWindowRect);
+		}
+	}
+
+	void Platform::clipCursorToRect(const Rect2I& screenRect)
+	{
+		RECT clipWindowRect;
+		clipWindowRect.left = screenRect.x;
+		clipWindowRect.top = screenRect.y;
+		clipWindowRect.right = screenRect.x + screenRect.width;
+		clipWindowRect.bottom = screenRect.y + screenRect.height;
+
+		ClipCursor(&clipWindowRect);
+	}
+
+	void Platform::clipCursorDisable()
+	{
+		ClipCursor(NULL);
+	}
+
+	// TODO - Add support for animated custom cursor
+	void Platform::setCursor(PixelData& pixelData, const Vector2I& hotSpot)
+	{
+		if (mData->mUsingCustomCursor)
+		{
+			SetCursor(0);
+			DestroyIcon(mData->mCursor.cursor);
+		}
+
+		mData->mUsingCustomCursor = true;
+
+		HBITMAP hBitmap = Win32Platform::createBitmap(pixelData, false); 
+		HBITMAP hMonoBitmap = CreateBitmap(pixelData.getWidth(), pixelData.getHeight(), 1, 1, nullptr);
+
+		ICONINFO iconinfo = {0};
+		iconinfo.fIcon = FALSE;
+		iconinfo.xHotspot = (DWORD)hotSpot.x;
+		iconinfo.yHotspot = (DWORD)hotSpot.y;
+		iconinfo.hbmMask = hMonoBitmap;
+		iconinfo.hbmColor = hBitmap;
+
+		mData->mCursor.cursor = CreateIconIndirect(&iconinfo);
+		
+		DeleteObject(hBitmap);          
+		DeleteObject(hMonoBitmap);
+
+		// Make sure we notify the message loop to perform the actual cursor update
+		RenderWindowPtr primaryWindow = gCoreApplication().getPrimaryWindow();
+		UINT64 hwnd;
+		primaryWindow->getCustomAttribute("WINDOW", &hwnd);
+
+		PostMessage((HWND)hwnd, WM_SETCURSOR, WPARAM((HWND)hwnd), (LPARAM)MAKELONG(HTCLIENT, WM_MOUSEMOVE));
+	}
+
+	void Platform::setIcon(const PixelData& pixelData)
+	{
+		PixelDataPtr resizedData = PixelData::create(32, 32, 1, PF_R8G8B8A8);
+		PixelUtil::scale(pixelData, *resizedData);
+
+		HBITMAP hBitmap = Win32Platform::createBitmap(pixelData, false);
+		HBITMAP hMonoBitmap = CreateBitmap(pixelData.getWidth(), pixelData.getHeight(), 1, 1, nullptr);
+
+		ICONINFO iconinfo = { 0 };
+		iconinfo.fIcon = TRUE;
+		iconinfo.xHotspot = 0;
+		iconinfo.yHotspot = 0;
+		iconinfo.hbmMask = hMonoBitmap;
+		iconinfo.hbmColor = hBitmap;
+
+		HICON icon = CreateIconIndirect(&iconinfo);
+
+		DeleteObject(hBitmap);
+		DeleteObject(hMonoBitmap);
+
+		// Make sure we notify the message loop to perform the actual cursor update
+		RenderWindowPtr primaryWindow = gCoreApplication().getPrimaryWindow();
+		UINT64 hwnd;
+		primaryWindow->getCustomAttribute("WINDOW", &hwnd);
+		
+		PostMessage((HWND)hwnd, WM_SETICON, WPARAM(ICON_BIG), (LPARAM)icon);
+	}
+
+	void Platform::setCaptionNonClientAreas(const RenderWindowCore& window, const Vector<Rect2I>& nonClientAreas)
+	{
+		BS_LOCK_MUTEX(mData->mSync);
+
+		mData->mNonClientAreas[&window].moveAreas = nonClientAreas;
+	}
+
+	void Platform::setResizeNonClientAreas(const RenderWindowCore& window, const Vector<NonClientResizeArea>& nonClientAreas)
+	{
+		BS_LOCK_MUTEX(mData->mSync);
+
+		mData->mNonClientAreas[&window].resizeAreas = nonClientAreas;
+	}
+
+	void Platform::resetNonClientAreas(const RenderWindowCore& window)
+	{
+		BS_LOCK_MUTEX(mData->mSync);
+
+		auto iterFind = mData->mNonClientAreas.find(&window);
+
+		if (iterFind != end(mData->mNonClientAreas))
+			mData->mNonClientAreas.erase(iterFind);
+	}
+
+	void Platform::sleep(UINT32 duration)
+	{
+		Sleep((DWORD)duration);
+	}
+
+	OSDropTarget& Platform::createDropTarget(const RenderWindow* window, int x, int y, unsigned int width, unsigned int height)
+	{
+		Win32DropTarget* win32DropTarget = nullptr;
+		auto iterFind = mData->mDropTargets.dropTargetsPerWindow.find(window);
+		if (iterFind == mData->mDropTargets.dropTargetsPerWindow.end())
+		{
+			UINT64 hwnd;
+			window->getCustomAttribute("WINDOW", &hwnd);
+
+			win32DropTarget = bs_new<Win32DropTarget>((HWND)hwnd);
+			mData->mDropTargets.dropTargetsPerWindow[window] = win32DropTarget;
+
+			{
+				BS_LOCK_MUTEX(mData->mSync);
+				mData->mDropTargets.dropTargetsToInitialize.push_back(win32DropTarget);
+			}
+		}
+		else
+			win32DropTarget = iterFind->second;
+
+		OSDropTarget* newDropTarget = new (bs_alloc<OSDropTarget>()) OSDropTarget(window, x, y, width, height);
+		win32DropTarget->registerDropTarget(newDropTarget);
+
+		return *newDropTarget;
+	}
+
+	void Platform::destroyDropTarget(OSDropTarget& target)
+	{
+		auto iterFind = mData->mDropTargets.dropTargetsPerWindow.find(target.getOwnerWindow());
+		if (iterFind == mData->mDropTargets.dropTargetsPerWindow.end())
+		{
+			LOGWRN("Attempting to destroy a drop target but cannot find its parent window.");
+		}
+		else
+		{
+			Win32DropTarget* win32DropTarget = iterFind->second;
+			win32DropTarget->unregisterDropTarget(&target);
+
+			if(win32DropTarget->getNumDropTargets() == 0)
+			{
+				mData->mDropTargets.dropTargetsPerWindow.erase(iterFind);
+
+				{
+					BS_LOCK_MUTEX(mData->mSync);
+					mData->mDropTargets.dropTargetsToDestroy.push_back(win32DropTarget);
+				}
+			}
+		}
+		
+		BS_PVT_DELETE(OSDropTarget, &target);
+	}
+
+	void Platform::_messagePump()
+	{
+		MSG  msg;
+		while (PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE))
+		{
+			TranslateMessage(&msg);
+			DispatchMessage(&msg);
+		}
+	}
+
+	void Platform::_startUp()
+	{
+		BS_LOCK_MUTEX(mData->mSync);
+
+		if (timeBeginPeriod(1) == TIMERR_NOCANDO)
+		{
+			LOGWRN("Unable to set timer resolution to 1ms. This can cause significant waste " \
+				"in performance for waiting threads.");
+		}
+
+
+		mData->mRequiresStartUp = true;
+	}
+
+	void Platform::_update()
+	{
+		for (auto& dropTarget : mData->mDropTargets.dropTargetsPerWindow)
+		{
+			dropTarget.second->update();
+		}
+	}
+
+	void Platform::_coreUpdate()
+	{
+		{
+			BS_LOCK_MUTEX(mData->mSync);
+			if (mData->mRequiresStartUp)
+			{
+				OleInitialize(nullptr);
+
+				mData->mRequiresStartUp = false;
+			}
+		}
+
+		{
+			BS_LOCK_MUTEX(mData->mSync);
+			for (auto& dropTargetToDestroy : mData->mDropTargets.dropTargetsToDestroy)
+			{
+				dropTargetToDestroy->unregisterWithOS();
+				dropTargetToDestroy->Release();
+			}
+
+			mData->mDropTargets.dropTargetsToDestroy.clear();
+		}
+
+		{
+			BS_LOCK_MUTEX(mData->mSync);
+			for (auto& dropTargetToInit : mData->mDropTargets.dropTargetsToInitialize)
+			{
+				dropTargetToInit->registerWithOS();
+			}
+
+			mData->mDropTargets.dropTargetsToInitialize.clear();
+		}
+
+		_messagePump();
+
+		{
+			BS_LOCK_MUTEX(mData->mSync);
+			if (mData->mRequiresShutDown)
+			{
+				OleUninitialize();
+				mData->mRequiresShutDown = false;
+			}
+		}
+	}
+
+	void Platform::_shutDown()
+	{
+		BS_LOCK_MUTEX(mData->mSync);
+
+		timeEndPeriod(1);
+		mData->mRequiresShutDown = true;
+	}
+
+	bool isShiftPressed = false;
+	bool isCtrlPressed = false;
+
+	/**
+	 * @brief	Translate engine non client area to win32 non client area.
+	 */
+	LRESULT translateNonClientAreaType(NonClientAreaBorderType type)
+	{
+		LRESULT dir = HTCLIENT;
+		switch(type)
+		{
+		case NonClientAreaBorderType::Left:
+			dir = HTLEFT;
+			break;
+		case NonClientAreaBorderType::TopLeft:
+			dir = HTTOPLEFT;
+			break;
+		case NonClientAreaBorderType::Top:
+			dir = HTTOP;
+			break;
+		case NonClientAreaBorderType::TopRight:
+			dir = HTTOPRIGHT;
+			break;
+		case NonClientAreaBorderType::Right:
+			dir = HTRIGHT;
+			break;
+		case NonClientAreaBorderType::BottomRight:
+			dir = HTBOTTOMRIGHT;
+			break;
+		case NonClientAreaBorderType::Bottom:
+			dir = HTBOTTOM;
+			break;
+		case NonClientAreaBorderType::BottomLeft:
+			dir = HTBOTTOMLEFT;
+			break;
+		}
+
+		return dir;
+	}
+
+	/**
+	 * @brief	Method triggered whenever a mouse event happens.
+	 */
+	void getMouseData(HWND hWnd, WPARAM wParam, LPARAM lParam, bool nonClient, Vector2I& mousePos, OSPointerButtonStates& btnStates)
+	{
+		POINT clientPoint;
+
+		clientPoint.x = GET_X_LPARAM(lParam);
+		clientPoint.y = GET_Y_LPARAM(lParam); 
+
+		if (!nonClient)
+			ClientToScreen(hWnd, &clientPoint);
+
+		mousePos.x = clientPoint.x;
+		mousePos.y = clientPoint.y;
+
+		btnStates.mouseButtons[0] = (wParam & MK_LBUTTON) != 0;
+		btnStates.mouseButtons[1] = (wParam & MK_MBUTTON) != 0;
+		btnStates.mouseButtons[2] = (wParam & MK_RBUTTON) != 0;
+		btnStates.shift = (wParam & MK_SHIFT) != 0;
+		btnStates.ctrl = (wParam & MK_CONTROL) != 0;
+	}
+
+	/**
+	 * @brief	Converts a virtual key code into an input command, if possible. Returns true
+	 *			if conversion was done.
+	 */
+	bool getCommand(unsigned int virtualKeyCode, InputCommandType& command)
+	{
+		switch (virtualKeyCode) 
+		{ 
+		case VK_LEFT:
+			command = isShiftPressed ? InputCommandType::SelectLeft : InputCommandType::CursorMoveLeft;
+			return true;
+		case VK_RIGHT:
+			command = isShiftPressed ? InputCommandType::SelectRight : InputCommandType::CursorMoveRight;
+			return true;
+		case VK_UP:
+			command = isShiftPressed ? InputCommandType::SelectUp : InputCommandType::CursorMoveUp;
+			return true;
+		case VK_DOWN:
+			command = isShiftPressed ? InputCommandType::SelectDown : InputCommandType::CursorMoveDown;
+			return true;
+		case VK_ESCAPE:
+			command = InputCommandType::Escape;
+			return true;
+		case VK_RETURN:
+			command = isShiftPressed ? InputCommandType::Return : InputCommandType::Confirm;
+			return true;
+		case VK_BACK:
+			command = InputCommandType::Backspace;
+			return true;
+		case VK_DELETE:
+			command = InputCommandType::Delete;
+			return true;
+		}
+
+		return false;
+	}
+
+	HBITMAP Win32Platform::createBitmap(const PixelData& pixelData, bool premultiplyAlpha)
+	{
+		BITMAPINFO bi;
+
+		ZeroMemory(&bi, sizeof(BITMAPINFO));
+		bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+		bi.bmiHeader.biWidth = pixelData.getWidth();
+		bi.bmiHeader.biHeight = pixelData.getHeight();
+		bi.bmiHeader.biPlanes = 1;
+		bi.bmiHeader.biBitCount = 32;
+		bi.bmiHeader.biCompression = BI_RGB;
+
+		HDC hDC = GetDC(nullptr);
+
+		void* data = nullptr;
+		HBITMAP hBitmap = CreateDIBSection(hDC, &bi, DIB_RGB_COLORS, (void**)&data, nullptr, 0);
+
+		HDC hBitmapDC = CreateCompatibleDC(hDC);
+		ReleaseDC(nullptr, hDC);
+
+		//Select the bitmaps to DC
+		HBITMAP hOldBitmap = (HBITMAP)SelectObject(hBitmapDC, hBitmap);
+
+		//Scan each pixel of the source bitmap and create the masks
+		Color pixel;
+		DWORD *dst = (DWORD*)data;
+		for (UINT32 y = 0; y < pixelData.getHeight(); ++y)
+		{
+			for (UINT32 x = 0; x < pixelData.getWidth(); ++x)
+			{
+				pixel = pixelData.getColorAt(x, pixelData.getHeight() - y - 1);
+
+				if (premultiplyAlpha)
+				{
+					pixel.r *= pixel.a;
+					pixel.g *= pixel.a;
+					pixel.b *= pixel.a;
+				}
+
+				*dst = pixel.getAsBGRA();
+
+				dst++;
+			}
+		}
+
+		SelectObject(hBitmapDC, hOldBitmap);
+		DeleteDC(hBitmapDC);
+
+		return hBitmap;
+	}
+
+	LRESULT CALLBACK Win32Platform::_win32WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+	{
+		if (uMsg == WM_CREATE)
+		{	// Store pointer to Win32Window in user data area
+			SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)(((LPCREATESTRUCT)lParam)->lpCreateParams));
+
+			RenderWindowCore* newWindow = (RenderWindowCore*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
+			if (newWindow != nullptr)
+			{
+				const RenderWindowProperties& props = newWindow->getProperties();
+				if (!props.isHidden())
+					ShowWindow(hWnd, SW_SHOWNOACTIVATE);
+
+				if (props.isModal())
+				{
+					if (!mData->mModalWindowStack.empty())
+					{
+						RenderWindowCore* curModalWindow = mData->mModalWindowStack.back();
+
+						UINT64 curHwnd;
+						curModalWindow->getCustomAttribute("WINDOW", &curHwnd);
+						EnableWindow((HWND)curHwnd, FALSE);
+					}
+					else
+					{
+						Vector<RenderWindowCore*> renderWindows = RenderWindowCoreManager::instance().getRenderWindows();
+						for (auto& renderWindow : renderWindows)
+						{
+							if (renderWindow == newWindow)
+								continue;
+
+							UINT64 curHwnd;
+							renderWindow->getCustomAttribute("WINDOW", &curHwnd);
+							EnableWindow((HWND)curHwnd, FALSE);
+						}
+					}
+
+					mData->mModalWindowStack.push_back(newWindow);
+				}
+				else
+				{
+					// A non-modal window was opened while another modal one is open:
+					// immediately deactivate it and make sure the modal windows stay on top
+					if (!mData->mModalWindowStack.empty())
+					{
+						EnableWindow((HWND)hWnd, FALSE);
+
+						for (auto window : mData->mModalWindowStack)
+						{
+							UINT64 curHwnd;
+							window->getCustomAttribute("WINDOW", &curHwnd);
+
+							BringWindowToTop((HWND)curHwnd);
+						}
+					}
+				}
+			}
+			else
+				ShowWindow(hWnd, SW_SHOWNOACTIVATE);
+
+			return 0;
+		}
+
+		// look up window instance
+		// note: it is possible to get a WM_SIZE before WM_CREATE
+		RenderWindowCore* win = (RenderWindowCore*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
+		if (!win)
+			return DefWindowProc(hWnd, uMsg, wParam, lParam);
+
+		switch( uMsg )
+		{
+		case WM_DESTROY:
+			{
+				bool reenableWindows = false;
+				if (!mData->mModalWindowStack.empty())
+				{
+					// Start from back because the most common case is closing the top-most modal window
+					for (auto iter = mData->mModalWindowStack.rbegin(); iter != mData->mModalWindowStack.rend(); ++iter)
+					{
+						if (*iter == win)
+						{
+							auto iterFwd = std::next(iter).base(); // erase doesn't accept reverse iter, so convert
+
+							mData->mModalWindowStack.erase(iterFwd);
+							break;
+						}
+					}
+
+					if (!mData->mModalWindowStack.empty()) // Enable next modal window
+					{
+						RenderWindowCore* curModalWindow = mData->mModalWindowStack.back();
+
+						UINT64 curHwnd;
+						curModalWindow->getCustomAttribute("WINDOW", &curHwnd);
+						EnableWindow((HWND)curHwnd, TRUE);
+					}
+					else
+						reenableWindows = true; // No more modal windows, re-enable any remaining window
+				}
+
+				if(reenableWindows)
+				{
+					Vector<RenderWindowCore*> renderWindows = RenderWindowCoreManager::instance().getRenderWindows();
+					for(auto& renderWindow : renderWindows)
+					{
+						HWND curHwnd;
+						renderWindow->getCustomAttribute("WINDOW", &curHwnd);
+						EnableWindow(curHwnd, TRUE);
+					}
+				}
+
+				return 0;
+			}
+		case WM_SETFOCUS:
+			{
+				if (!win->getProperties().hasFocus())
+					win->_windowFocusReceived();
+
+				return 0;
+			}
+		case WM_KILLFOCUS:
+			{
+				if (win->getProperties().hasFocus())
+					win->_windowFocusLost();
+
+				return 0;
+			}
+		case WM_SYSCHAR:
+			// return zero to bypass defProc and signal we processed the message, unless it's an ALT-space
+			if (wParam != VK_SPACE)
+				return 0;
+			break;
+		case WM_MOVE:
+			win->_windowMovedOrResized();
+			return 0;
+		case WM_DISPLAYCHANGE:
+			win->_windowMovedOrResized();
+			break;
+		case WM_SIZE:
+			win->_windowMovedOrResized();
+
+			if (wParam == SIZE_MAXIMIZED)
+				win->_notifyMaximized();
+			else if (wParam == SIZE_MINIMIZED)
+				win->_notifyMinimized();
+			else if (wParam == SIZE_RESTORED)
+				win->_notifyRestored();
+
+			return 0;
+		case WM_SETCURSOR:
+			if(isCursorHidden())
+				SetCursor(nullptr);
+			else
+			{
+				switch (LOWORD(lParam))
+				{
+				case HTTOPLEFT:
+					SetCursor(LoadCursor(0, IDC_SIZENWSE));
+					return 0;
+				case HTTOP:
+					SetCursor(LoadCursor(0, IDC_SIZENS));
+					return 0;
+				case HTTOPRIGHT:
+					SetCursor(LoadCursor(0, IDC_SIZENESW));
+					return 0;
+				case HTLEFT:
+					SetCursor(LoadCursor(0, IDC_SIZEWE));
+					return 0;
+				case HTRIGHT:
+					SetCursor(LoadCursor(0, IDC_SIZEWE));
+					return 0;
+				case HTBOTTOMLEFT:
+					SetCursor(LoadCursor(0, IDC_SIZENESW));
+					return 0;
+				case HTBOTTOM:
+					SetCursor(LoadCursor(0, IDC_SIZENS));
+					return 0;
+				case HTBOTTOMRIGHT:
+					SetCursor(LoadCursor(0, IDC_SIZENWSE));
+					return 0;
+				}
+
+				SetCursor(mData->mCursor.cursor);
+			}
+			return true;
+		case WM_GETMINMAXINFO:
+		{
+			// Prevent the window from going smaller than some minimu size
+			((MINMAXINFO*)lParam)->ptMinTrackSize.x = 100;
+			((MINMAXINFO*)lParam)->ptMinTrackSize.y = 100;
+
+			// Ensure maximizes window has proper size and doesn't cover the entire screen
+			const POINT ptZero = { 0, 0 };
+			HMONITOR primaryMonitor = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY);
+
+			MONITORINFO monitorInfo;
+			monitorInfo.cbSize = sizeof(MONITORINFO);
+			GetMonitorInfo(primaryMonitor, &monitorInfo);
+
+			((MINMAXINFO*)lParam)->ptMaxPosition.x = monitorInfo.rcWork.left - monitorInfo.rcMonitor.left;
+			((MINMAXINFO*)lParam)->ptMaxPosition.y = monitorInfo.rcWork.top - monitorInfo.rcMonitor.top;
+			((MINMAXINFO*)lParam)->ptMaxSize.x = monitorInfo.rcWork.right - monitorInfo.rcWork.left;
+			((MINMAXINFO*)lParam)->ptMaxSize.y = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top;
+		}
+			break;
+		case WM_CLOSE:
+			{
+				gCoreApplication().quitRequested();
+
+				return 0;
+			}
+		case WM_NCHITTEST:
+			{
+				auto iterFind = mData->mNonClientAreas.find(win);
+				if (iterFind == mData->mNonClientAreas.end())
+					break;
+
+				POINT mousePos;
+				mousePos.x = GET_X_LPARAM(lParam);
+				mousePos.y = GET_Y_LPARAM(lParam); 
+
+				ScreenToClient(hWnd, &mousePos);
+
+				Vector2I mousePosInt;
+				mousePosInt.x = mousePos.x;
+				mousePosInt.y = mousePos.y;
+
+				Vector<NonClientResizeArea>& resizeAreasPerWindow = iterFind->second.resizeAreas;
+				for(auto area : resizeAreasPerWindow)
+				{
+					if(area.area.contains(mousePosInt))
+						return translateNonClientAreaType(area.type);
+				}
+
+				Vector<Rect2I>& moveAreasPerWindow = iterFind->second.moveAreas;
+				for(auto area : moveAreasPerWindow)
+				{
+					if(area.contains(mousePosInt))
+						return HTCAPTION;
+				}
+
+				return HTCLIENT;
+			}
+		case WM_NCLBUTTONDBLCLK:
+			// Maximize/Restore on double-click
+			if (wParam == HTCAPTION)
+			{
+				WINDOWPLACEMENT windowPlacement;
+				windowPlacement.length = sizeof(WINDOWPLACEMENT);
+				GetWindowPlacement(hWnd, &windowPlacement);
+
+				if (windowPlacement.showCmd == SW_MAXIMIZE)
+					ShowWindow(hWnd, SW_RESTORE);
+				else
+					ShowWindow(hWnd, SW_MAXIMIZE);
+
+				return 0;
+			}
+			break;
+		case WM_MOUSELEAVE:
+			{
+				// Note: Right now I track only mouse leaving client area. So it's possible for the "mouse left window" callback
+				// to trigger, while the mouse is still in the non-client area of the window.
+				mData->mIsTrackingMouse = false; // TrackMouseEvent ends when this message is received and needs to be re-applied
+
+				BS_LOCK_MUTEX(mData->mSync);
+
+				if (!onMouseLeftWindow.empty())
+					onMouseLeftWindow(win);
+			}
+			return 0;
+		case WM_LBUTTONUP:
+			{
+				ReleaseCapture();
+
+				Vector2I intMousePos;
+				OSPointerButtonStates btnStates;
+
+				getMouseData(hWnd, wParam, lParam, false, intMousePos, btnStates);
+
+				if(!onCursorButtonReleased.empty())
+					onCursorButtonReleased(intMousePos, OSMouseButton::Left, btnStates);
+
+				return 0;
+			}
+		case WM_MBUTTONUP:
+			{
+				ReleaseCapture();
+
+				Vector2I intMousePos;
+				OSPointerButtonStates btnStates;
+
+				getMouseData(hWnd, wParam, lParam, false, intMousePos, btnStates);
+
+				if(!onCursorButtonReleased.empty())
+					onCursorButtonReleased(intMousePos, OSMouseButton::Middle, btnStates);
+
+				return 0;
+			}
+		case WM_RBUTTONUP:
+			{
+				ReleaseCapture();
+
+				Vector2I intMousePos;
+				OSPointerButtonStates btnStates;
+
+				getMouseData(hWnd, wParam, lParam, false, intMousePos, btnStates);
+
+				if(!onCursorButtonReleased.empty())
+					onCursorButtonReleased(intMousePos, OSMouseButton::Right, btnStates);
+
+				return 0;
+			}
+		case WM_LBUTTONDOWN:
+			{
+				SetCapture(hWnd);
+
+				Vector2I intMousePos;
+				OSPointerButtonStates btnStates;
+
+				getMouseData(hWnd, wParam, lParam, false, intMousePos, btnStates);
+
+				if(!onCursorButtonPressed.empty())
+					onCursorButtonPressed(intMousePos, OSMouseButton::Left, btnStates);
+			}
+			return 0;
+		case WM_MBUTTONDOWN:
+			{
+				SetCapture(hWnd);
+
+				Vector2I intMousePos;
+				OSPointerButtonStates btnStates;
+
+				getMouseData(hWnd, wParam, lParam, false, intMousePos, btnStates);
+
+				if(!onCursorButtonPressed.empty())
+					onCursorButtonPressed(intMousePos, OSMouseButton::Middle, btnStates);
+			}
+			return 0;
+		case WM_RBUTTONDOWN:
+			{
+				SetCapture(hWnd);
+
+				Vector2I intMousePos;
+				OSPointerButtonStates btnStates;
+
+				getMouseData(hWnd, wParam, lParam, false, intMousePos, btnStates);
+
+				if(!onCursorButtonPressed.empty())
+					onCursorButtonPressed(intMousePos, OSMouseButton::Right, btnStates);
+			}
+			return 0;
+		case WM_LBUTTONDBLCLK:
+			{
+				Vector2I intMousePos;
+				OSPointerButtonStates btnStates;
+
+				getMouseData(hWnd, wParam, lParam, false, intMousePos, btnStates);
+
+				if(!onCursorDoubleClick.empty())
+					onCursorDoubleClick(intMousePos, btnStates);
+			}
+			return 0;
+		case WM_NCMOUSEMOVE:
+		case WM_MOUSEMOVE:
+			{
+				// Set up tracking so we get notified when mouse leaves the window
+				if(!mData->mIsTrackingMouse)
+				{
+					TRACKMOUSEEVENT tme = { sizeof(tme) };
+					tme.dwFlags = TME_LEAVE;
+
+					tme.hwndTrack = hWnd;
+					TrackMouseEvent(&tme);
+
+					mData->mIsTrackingMouse = true;
+				}
+
+				Vector2I intMousePos;
+				OSPointerButtonStates btnStates;
+				
+				getMouseData(hWnd, wParam, lParam, uMsg == WM_NCMOUSEMOVE, intMousePos, btnStates);
+
+				if(!onCursorMoved.empty())
+					onCursorMoved(intMousePos, btnStates);
+
+				return 0;
+			}
+		case WM_MOUSEWHEEL:
+			{
+				INT16 wheelDelta = GET_WHEEL_DELTA_WPARAM(wParam);
+
+				float wheelDeltaFlt = wheelDelta / (float)WHEEL_DELTA;
+				if(!onMouseWheelScrolled.empty())
+					onMouseWheelScrolled(wheelDeltaFlt);
+
+				return true;
+			}
+		case WM_SYSKEYDOWN:
+		case WM_KEYDOWN:
+			{
+				if(wParam == VK_SHIFT)
+				{
+					isShiftPressed = true;
+					break;
+				}
+
+				if(wParam == VK_CONTROL)
+				{
+					isCtrlPressed = true;
+					break;
+				}
+
+				InputCommandType command = InputCommandType::Backspace;
+				if(getCommand((unsigned int)wParam, command))
+				{
+					if(!onInputCommand.empty())
+						onInputCommand(command);
+
+					return 0;
+				}
+
+				break;
+			}
+		case WM_SYSKEYUP:
+		case WM_KEYUP:
+			{
+				if(wParam == VK_SHIFT)
+				{
+					isShiftPressed = false;
+				}
+
+				if(wParam == VK_CONTROL)
+				{
+					isCtrlPressed = false;
+				}
+
+				return 0;
+			}
+		case WM_CHAR:
+			{
+				// TODO - Not handling IME input
+
+				// Ignore rarely used special command characters, usually triggered by ctrl+key
+				// combinations. (We want to keep ctrl+key free for shortcuts instead)
+				if (wParam <= 23)
+					break;
+
+				switch (wParam) 
+				{ 
+				case VK_ESCAPE:
+					break; 
+				default:    // displayable character 
+					{
+						UINT8 scanCode = (lParam >> 16) & 0xFF;
+
+						BYTE keyState[256];
+						HKL layout = GetKeyboardLayout(0);
+						if(GetKeyboardState(keyState) == 0)
+							return 0;
+
+						unsigned int vk = MapVirtualKeyEx(scanCode, MAPVK_VSC_TO_VK_EX, layout);
+						if(vk == 0)
+							return 0;
+
+						InputCommandType command = InputCommandType::Backspace;
+						if(getCommand(vk, command)) // We ignore character combinations that are special commands
+							return 0;
+
+						UINT32 finalChar = (UINT32)wParam;
+
+						if(!onCharInput.empty())
+							onCharInput(finalChar);
+
+						return 0;
+					}
+				} 
+
+				break;
+			}
+		case WM_BS_SETCAPTURE:
+			SetCapture(hWnd);
+			break;
+		case WM_BS_RELEASECAPTURE:
+			ReleaseCapture();
+			break;
+		case WM_CAPTURECHANGED:
+			if(!onMouseCaptureChanged.empty())
+				onMouseCaptureChanged();
+			return 0;
+		}
+
+		return DefWindowProc( hWnd, uMsg, wParam, lParam );
+	}
 }

+ 38 - 99
BansheeEditor/Include/BsEditorApplication.h

@@ -7,183 +7,124 @@
 
 namespace BansheeEngine
 {
-	/**
-	 * @brief	Types of render APIs supported by the editor.
-	 */
+	/**	Types of render APIs supported by the editor. */
 	enum class EditorRenderAPI
 	{
 		DX11,
 		OpenGL
 	};
 
-	/**
-	 * @brief	Primary editor class containing the editor entry point.
-	 */
+	/**	Primary editor class containing the editor entry point. */
 	class BS_ED_EXPORT EditorApplication : public Application
 	{
 	public:
 		EditorApplication(EditorRenderAPI renderAPI);
 		virtual ~EditorApplication();
 
-		/**
-		 * @brief	Starts the editor with the specified render system.
-		 */
+		/**	Starts the editor with the specified render system. */
 		static void startUp(EditorRenderAPI renderAPI);
 
-		/**
-		 * @brief	Checks whether the editor currently has a project loaded.
-		 */
+		/**	Checks whether the editor currently has a project loaded. */
 		bool isProjectLoaded() const { return mIsProjectLoaded; }
 
-		/**
-		 * @brief	Returns the path to the currently loaded project.
-		 */
+		/**	Returns the path to the currently loaded project. */
 		const Path& getProjectPath() const { return mProjectPath; }
 
-		/**
-		 * @brief	Returns the name of the currently loaded project.
-		 */
+		/**	Returns the name of the currently loaded project. */
 		const WString& getProjectName() const { return mProjectName; }
 
-		/**
-		 * @brief	Returns the absolute path to the built-in managed editor assembly file.
-		 */
+		/**	Returns the absolute path to the built-in managed editor assembly file. */
 		Path getEditorAssemblyPath() const;
 
-		/**
-		 * @brief	Returns the absolute path of the managed editor script assembly file.
-		 */
+		/**	Returns the absolute path of the managed editor script assembly file. */
 		Path getEditorScriptAssemblyPath() const;
 
-		/**
-		 * @copydoc	Application::getScriptAssemblyFolder
-		 */
+		/** @copydoc	Application::getScriptAssemblyFolder */
 		Path getScriptAssemblyFolder() const override;
 
-		/**
-		 * @brief	Returns a set of serializable editor settings that contain
-		 *			every globally customizable editor property.
-		 */
+		/** Returns a set of serializable editor settings that contain every globally customizable editor property. */
 		EditorSettingsPtr getEditorSettings() const { return mEditorSettings; }
 
-		/**
-		 * @brief	Returns a set of serializable project settings that contain
-		 *			every customizable property specific to a project.
-		 */
+		/** Returns a set of serializable project settings that contain every customizable property specific to a project. */
 		ProjectSettingsPtr getProjectSettings() const { return mProjectSettings; }
 
-		/**
-		 * @brief	Saves the current editor settings at the default location.
-		 */
+		/**	Saves the current editor settings at the default location. */
 		void saveEditorSettings();
 
-		/**
-		 * @brief	Saves the current project settings at the default location.
-		 *			Does nothing if no project is loaded.
-		 */
+		/** Saves the current project settings at the default location. Does nothing if no project is loaded. */
 		void saveProjectSettings();
 
-		/**
-		 * @brief	Saves any project specific data, if a project is currently loaded.
-		 */
+		/**	Saves any project specific data, if a project is currently loaded. */
 		void saveProject();
 
-		/**
-		 * @brief	Unloads the currently loaded project, if any.
-		 */
+		/**	Unloads the currently loaded project, if any. */
 		void unloadProject();
 
 		/**
-		 * @brief	Loads a new project, unloading the current one. 
+		 * Loads a new project, unloading the current one. 
 		 *
-		 * @param	path	Absolute path to the root project folder. Must be pointing
-		 *					to a valid project.
+		 * @param[in]	path	Absolute path to the root project folder. Must be pointing to a valid project.
 		 */
 		void loadProject(const Path& path);
 
 		/**
-		 * @brief	Creates a new project at the specified path.
+		 * Creates a new project at the specified path.
 		 *
-		 * @param	path	Path to the folder where to create the project in. Name of this
-		 *					folder will be the name of the project.
+		 * @param[in]	path	Path to the folder where to create the project in. Name of this folder will be the name of
+		 *						the project.
 		 */
 		void createProject(const Path& path);
 
 		/**
-		 * @brief	Checks is the provided folder a valid project.
+		 * Checks is the provided folder a valid project.
 		 *
-		 * @param	path	Absolute path to the root project folder.
+		 * @param[in]	path	Absolute path to the root project folder.
 		 */
 		bool isValidProjectPath(const Path& path);
 
-		/**
-		 * @copydoc	Application::isEditor
-		 */
+		/** @copydoc Application::isEditor */
 		bool isEditor() const override { return true; }
 	private:
-		/**
-		 * @copydoc	Module::onStartUp
-		 */
+		/** @copydoc Module::onStartUp */
 		virtual void onStartUp() override;
 
-		/**
-		 * @copydoc	Module::onShutDown
-		 */
+		/** @copydoc Module::onShutDown */
 		virtual void onShutDown() override;
 
-		/**
-		 * @copydoc	CoreApplication::preUpdate
-		 */
+		/** @copydoc CoreApplication::preUpdate */
 		virtual void preUpdate() override;
 
-		/**
-		 * @copydoc	CoreApplication::postUpdate
-		 */
+		/** @copydoc CoreApplication::postUpdate */
 		virtual void postUpdate() override;
 
-		/**
-		 * @copydoc	CoreApplication::quitRequested
-		 */
+		/** @copydoc CoreApplication::quitRequested */
 		virtual void quitRequested() override;
 
-		/**
-		 * @copydoc	Application::loadScriptSystem
-		 */
+		/** @copydoc Application::loadScriptSystem */
 		void loadScriptSystem() override;
 
 		/**
-		 * @brief	Loads the previously saved editor widget layout from the default location.
-		 *			Can return null if no layout was previously saved.
+		 * Loads the previously saved editor widget layout from the default location. Can return null if no layout was 
+		 * previously saved.
 		 */
 		EditorWidgetLayoutPtr loadWidgetLayout();
 
-		/**
-		 * @brief	Saves the provided widget layout at the default layout location.
-		 */
+		/**	Saves the provided widget layout at the default layout location. */
 		void saveWidgetLayout(const EditorWidgetLayoutPtr& layout);
 
-		/**
-		 * @brief	Loads the previously saved editor settings from the default location.
-		 *			Overwrites any current settings.
-		 */
+		/** Loads the previously saved editor settings from the default location. Overwrites any current settings. */
 		void loadEditorSettings();
 
 		/**
-		 * @brief	Loads the previously saved project settings from the default location
-		 *			within the active project. Loads default settings if no project is active.
-		 *			Overwrites any current settings.
+		 * Loads the previously saved project settings from the default location within the active project. Loads default
+		 * settings if no project is active. Overwrites any current settings.
 		 */
 		void loadProjectSettings();
 
-		/**
-		 * @copydoc	Application::getShaderIncludeHandler
-		 */
+		/** @copydoc Application::getShaderIncludeHandler */
 		virtual ShaderIncludeHandlerPtr getShaderIncludeHandler() const override;
 
-		/**
-		 * @brief	Converts a render API type supported by the editor into a type recognized by the lower
-		 * 			layers of the engine.
-		 */
+		/** Converts a render API type supported by the editor into a type recognized by the lower layers of the engine. */
 		static RenderAPIPlugin toEngineRenderAPI(EditorRenderAPI renderAPI);
 
 	private:
@@ -202,8 +143,6 @@ namespace BansheeEngine
 		DynLib* mSBansheeEditorPlugin;
 	};
 
-	/**
-	 * @brief	Returns the globally accessible instance of editor application.
-	 */
+	/**	Easy way to access EditorApplication. */
 	BS_ED_EXPORT EditorApplication& gEditorApplication();
 }

+ 35 - 86
BansheeEditor/Include/BsEditorSettings.h

@@ -10,148 +10,98 @@ namespace BansheeEngine
 {
 	struct RecentProject;
 
-	/**
-	 * @brief	Contains various globally accessible editor preferences.
-	 */
+	/**	Contains various globally accessible editor preferences. */
 	class BS_ED_EXPORT EditorSettings : public Settings
 	{
 	public:
 		EditorSettings();
 
-		/**
-		 * @brief	Checks is snapping enabled for move handles in scene view.
-		 */
+		/**	Checks is snapping enabled for move handles in scene view. */
 		bool getMoveHandleSnapActive() const { return mMoveSnapActive; }
 
-		/**
-		 * @brief	Checks is angle snapping enabled for rotate handles in scene view.
-		 */
+		/**	Checks is angle snapping enabled for rotate handles in scene view. */
 		bool getRotateHandleSnapActive() const { return mRotateSnapActive; }
 
-		/**
-		 * @brief	Gets the snap amount if move snapping is enabled. All move handles
-		 *			will move in multiples of this amount.
-		 */
+		/**	Gets the snap amount if move snapping is enabled. All move handles will move in multiples of this amount. */
 		float getMoveHandleSnap() const { return mMoveSnap; }
 
 		/**
-		 * @brief	Gets the snap amount if rotate snapping is enabled. All rotate handles
-		 *			will rotate in multiples of this amount.
+		 * Gets the snap amount if rotate snapping is enabled. All rotate handles will rotate in multiples of this amount.
 		 */
 		Degree getRotationHandleSnap() const { return mRotationSnap; }
 
-		/**
-		 * @brief	Returns the size that determines to total size of the scene view grid (its width and height).
-		 */
+		/**	Returns the size that determines to total size of the scene view grid (its width and height). */
 		UINT32 getGridSize() const { return mGridSize; }
 
-		/**
-		 * @brief	Returns the distance between scene view grid lines.
-		 */
+		/**	Returns the distance between scene view grid lines. */
 		float getGridSpacing() const { return mGridAxisSpacing; }
 
-		/**
-		 * @brief	Gets the default size of all scene view handles.
-		 */
+		/**	Gets the default size of all scene view handles. */
 		float getHandleSize() const { return mHandleSize; }
 
-		/**
-		 * @brief	Returns the currently active scene view tool (e.g. move, rotate, etc.)
-		 */
+		/**	Returns the currently active scene view tool (e.g. move, rotate, etc.). */
 		UINT32 getActiveSceneTool() const { return mActiveSceneTool; }
 
-		/**
-		 * @brief	Returns the currently active coordinate mode for scene view (e.g. global/local)
-		 */
+		/**	Returns the currently active coordinate mode for scene view (e.g. global/local). */
 		UINT32 getActiveCoordinateMode() const { return mActiveCoordinateMode; }
 
-		/**
-		 * @brief	Returns the currently active pivot mode for scene view (e.g. pivot/center)
-		 */
+		/**	Returns the currently active pivot mode for scene view (e.g. pivot/center). */
 		UINT32 getActivePivotMode() const { return mActivePivotMode; }
 
-		/**
-		 * @brief	Retrieves the path to the last project open in the editor.
-		 */
+		/**	Retrieves the path to the last project open in the editor. */
 		Path getLastOpenProject() const { return mLastOpenProject; }
 
-		/**
-		 * @brief	Retrieves whether the last open project should be automatically loaded
-		 *			on editor start up.
-		 */
+		/** Retrieves whether the last open project should be automatically loaded on editor start up. */
 		bool getAutoLoadLastProject() const { return mAutoLoadLastProject; }
 
-		/**
-		 * @brief	Retrieves a list of most recently loaded project paths and their last access times.
-		 */
+		/**	Retrieves a list of most recently loaded project paths and their last access times. */
 		const Vector<RecentProject>& getRecentProjects() const { return mRecentProjects; }
 
-		/**
-		 * @brief	Enables/disables snapping for move handles in scene view.
-		 */
+		/** Retrieves the maximum number of frames per second the editor is allowed to execute. Zero means infinite. */
+		UINT32 getFPSLimit() const { return mFPSLimit; }
+
+		/**	Enables/disables snapping for move handles in scene view. */
 		void setMoveHandleSnapActive(bool snapActive) { mMoveSnapActive = snapActive; markAsDirty(); }
 
-		/**
-		 * @brief	Enables/disables angle snapping for rotate handles in scene view.
-		 */
+		/**	Enables/disables angle snapping for rotate handles in scene view. */
 		void setRotateHandleSnapActive(bool snapActive) { mRotateSnapActive = snapActive; markAsDirty(); }
 
-		/**
-		 * @brief	Sets the move snap amount. All move handles will move in multiples of this amount.
-		 */
+		/**	Sets the move snap amount. All move handles will move in multiples of this amount. */
 		void setMoveHandleSnap(float value) { mMoveSnap = value; markAsDirty(); }
 
-		/**
-		 * @brief	Sets the rotate snap amount. All rotate handles will rotate in multiples of this amount.
-		 */
+		/**	Sets the rotate snap amount. All rotate handles will rotate in multiples of this amount. */
 		void setRotationHandleSnap(Degree value) { mRotationSnap = value; markAsDirty(); }
 
-		/**
-		 * @brief	Sets the size that determines to total size of the scene view grid (its width and height).
-		 */
+		/**	Sets the size that determines to total size of the scene view grid (its width and height). */
 		void setGridSize(UINT32 value) { mGridSize = value; markAsDirty(); }
 
-		/**
-		 * @brief	Sets the distance between scene view grid lines.
-		 */
+		/**	Sets the distance between scene view grid lines. */
 		void setGridSpacing(float value) { mGridAxisSpacing = value; markAsDirty(); }
 
-		/**
-		 * @brief	Sets the default size of all scene view handles.
-		 */
+		/**	Sets the default size of all scene view handles. */
 		void setHandleSize(float value) { mHandleSize = value; markAsDirty(); }
 
-		/**
-		 * @brief	Changes the currently active scene view tool (e.g. move, rotate, etc.)
-		 */
+		/**	Changes the currently active scene view tool (e.g. move, rotate, etc.). */
 		void setActiveSceneTool(UINT32 value) { mActiveSceneTool = value; markAsDirty(); }
 
-		/**
-		 * @brief	Changes the currently active coordinate mode for scene view (e.g. global/local)
-		 */
+		/**	Changes the currently active coordinate mode for scene view (e.g. global/local). */
 		void setActiveCoordinateMode(UINT32 value) { mActiveCoordinateMode = value; markAsDirty(); }
 
-		/**
-		 * @brief	Changes the currently active pivot mode for scene view (e.g. pivot/center)
-		 */
+		/**	Changes the currently active pivot mode for scene view (e.g. pivot/center). */
 		void setActivePivotMode(UINT32 value) { mActivePivotMode = value; markAsDirty(); }
 
-		/**
-		 * @brief	Sets the path to the last project open in the editor.
-		 */
+		/**	Sets the path to the last project open in the editor. */
 		void setLastOpenProject(const Path& value) { mLastOpenProject = value; markAsDirty(); }
 
-		/**
-		 * @brief	Sets whether the last open project should be automatically loaded
-		 *			on editor start up.
-		 */
+		/** Sets whether the last open project should be automatically loaded on editor start up. */
 		void setAutoLoadLastProject(bool value) { mAutoLoadLastProject = value; markAsDirty(); }
 
-		/**
-		 * @brief	Sets a list of most recently loaded project paths and their last access times.
-		 */
+		/**	Sets a list of most recently loaded project paths and their last access times. */
 		void setRecentProjects(const Vector<RecentProject>& value) { mRecentProjects = value; markAsDirty(); }
 
+		/** Sets the maximum number of frames per second the editor is allowed to execute. Zero means infinite. */
+		void setFPSLimit(UINT32 limit) { mFPSLimit = limit; }
+
 	private:
 		bool mMoveSnapActive;
 		bool mRotateSnapActive;
@@ -167,6 +117,7 @@ namespace BansheeEngine
 		UINT32 mActivePivotMode;
 
 		float mHandleSize;
+		UINT32 mFPSLimit;
 
 		Path mLastOpenProject;
 		bool mAutoLoadLastProject;
@@ -181,9 +132,7 @@ namespace BansheeEngine
 		virtual RTTITypeBase* getRTTI() const override;
 	};
 
-	/**
-	 * @brief	Data about a recently loaded project.
-	 */
+	/**	Data about a recently loaded project. */
 	struct RecentProject
 	{
 		Path path;

+ 4 - 0
BansheeEditor/Include/BsEditorSettingsRTTI.h

@@ -30,6 +30,8 @@ namespace BansheeEngine
 		BS_PLAIN_MEMBER(mAutoLoadLastProject);
 		BS_PLAIN_MEMBER(mRecentProjects);
 
+		BS_PLAIN_MEMBER(mFPSLimit);
+
 	public:
 		EditorSettingsRTTI()
 		{
@@ -51,6 +53,8 @@ namespace BansheeEngine
 			BS_ADD_PLAIN_FIELD(mLastOpenProject, 10);
 			BS_ADD_PLAIN_FIELD(mAutoLoadLastProject, 11);
 			BS_ADD_PLAIN_FIELD(mRecentProjects, 12);
+
+			BS_ADD_PLAIN_FIELD(mFPSLimit, 13);
 		}
 
 		virtual const String& getRTTIName() override

+ 1 - 0
BansheeEditor/Source/BsEditorApplication.cpp

@@ -173,6 +173,7 @@ namespace BansheeEngine
 		Application::postUpdate();
 
 		SplashScreen::hide();
+		setFPSLimit(mEditorSettings->getFPSLimit());
 	}
 
 	void EditorApplication::quitRequested()

+ 1 - 1
BansheeEditor/Source/BsEditorSettings.cpp

@@ -8,7 +8,7 @@ namespace BansheeEngine
 	EditorSettings::EditorSettings()
 		:mMoveSnapActive(false), mRotateSnapActive(false), mMoveSnap(0.1f), mRotationSnap(20.0f),
 		mGridSize(256), mGridAxisSpacing(1.0f), mHandleSize(0.10f), mActiveSceneTool(1 /* Move */),
-		mActiveCoordinateMode(0), mActivePivotMode(0)
+		mActiveCoordinateMode(0), mActivePivotMode(0), mFPSLimit(60)
 	{ }
 	
 	RTTITypeBase* EditorSettings::getRTTIStatic()

+ 14 - 0
MBansheeEditor/EditorSettings.cs

@@ -93,6 +93,15 @@ namespace BansheeEditor
             set { Internal_SetActivePivotMode((int)value); }
         }
 
+        /// <summary>
+        /// Maximum number of frames per second the editor is allowed to execute. Zero means infinite.
+        /// </summary>
+        public static int FPSLimit
+        {
+            get { return Internal_GetFPSLimit(); }
+            set { Internal_SetFPSLimit(value); }
+        }
+
         /// <summary>
         /// Contains the absolute path to the last open project, if any.
         /// </summary>
@@ -317,6 +326,11 @@ namespace BansheeEditor
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_SetActivePivotMode(int value);
 
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern int Internal_GetFPSLimit();
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetFPSLimit(int value);
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern string Internal_GetLastOpenProject();
         [MethodImpl(MethodImplOptions.InternalCall)]

+ 7 - 0
MBansheeEditor/SettingsWindow.cs

@@ -16,6 +16,7 @@ namespace BansheeEditor
         private GUIFloatField defaultHandleSizeField;
         private GUIToggleField autoLoadLastProjectField;
         private GUIListBoxField codeEditorField;
+        private GUIIntField fpsLimitField;
 
         /// <summary>
         /// Opens the settings window if its not open already.
@@ -58,6 +59,10 @@ namespace BansheeEditor
                 CodeEditor.ActiveEditor = availableEditors[x];
             };
 
+            fpsLimitField = new GUIIntField(new LocEdString("FPS limit"), 200);
+            fpsLimitField.OnConfirmed += () => EditorSettings.FPSLimit = fpsLimitField.Value;
+            fpsLimitField.OnFocusLost += () => EditorSettings.FPSLimit = fpsLimitField.Value;
+
             GUILayout mainLayout = GUI.AddLayoutY();
             mainLayout.AddElement(projectFoldout);
             GUILayout projectLayoutOuterY = mainLayout.AddLayoutY();
@@ -82,6 +87,7 @@ namespace BansheeEditor
             editorLayout.AddElement(defaultHandleSizeField);
             editorLayout.AddElement(autoLoadLastProjectField);
             editorLayout.AddElement(codeEditorField);
+            editorLayout.AddElement(fpsLimitField);
 
             projectFoldout.Value = true;
             editorFoldout.Value = true;
@@ -94,6 +100,7 @@ namespace BansheeEditor
         {
             defaultHandleSizeField.Value = EditorSettings.DefaultHandleSize;
             autoLoadLastProjectField.Value = EditorSettings.AutoLoadLastProject;
+            fpsLimitField.Value = EditorSettings.FPSLimit;
 
             CodeEditorType[] availableEditors = CodeEditor.AvailableEditors;
             int idx = Array.IndexOf(availableEditors, CodeEditor.ActiveEditor);

+ 12 - 4
README.md

@@ -161,17 +161,25 @@ Easiest way to get started with low-level Banshee programming is to check out th
 #### Adding and positioning a camera
 ```
   HSceneObject sceneCameraSO = SceneObject::create("SceneCamera");
-  HCamera sceneCamera = sceneCameraSO->addComponent<Camera>(window);
+  HCamera sceneCamera = sceneCameraSO->addComponent<CCamera>(window);
 
   sceneCameraSO->setPosition(Vector3(40.0f, 30.0f, 230.0f));
   sceneCameraSO->lookAt(Vector3(0, 0, 0));
 ```
 
+#### Setting up a material
+```
+   HShader diffuse = gImporter().import<Shader>("Diffuse.bsl");
+   HMaterial dragonMaterial = Material::create(diffuse);
+   
+   dragonMaterial->setTexture("albedo", dragonTexture);
+```
+
 #### Adding an object for rendering
 ```
   HSceneObject dragonSO = SceneObject::create("Dragon");
   
-  HRenderable renderable = dragonSO->addComponent<Renderable>();
+  HRenderable renderable = dragonSO->addComponent<CRenderable>();
   renderable->setMesh(dragonModel);
   renderable->setMaterial(dragonMaterial);
 ```
@@ -179,8 +187,8 @@ Easiest way to get started with low-level Banshee programming is to check out th
 #### Adding GUI
 ```
   HSceneObject guiSO = SceneObject::create("GUI");
-  HCamera guiCamera = guiSO->addComponent<Camera>(window);
-  HGUIWidget gui = guiSO->addComponent<GUIWidget>(guiCamera);
+  HCamera guiCamera = guiSO->addComponent<CCamera>(window);
+  HGUIWidget gui = guiSO->addComponent<CGUIWidget>(guiCamera);
   
   GUIPanel* guiPanel = gui->getPanel();
   GUILayout* guiLayout = guiPanel->addNewElement<GUILayoutY>();

+ 2 - 0
SBansheeEditor/Include/BsScriptEditorSettings.h

@@ -37,6 +37,8 @@ namespace BansheeEngine
 		static void internal_SetActiveCoordinateMode(UINT32 value);
 		static UINT32 internal_GetActivePivotMode();
 		static void internal_SetActivePivotMode(UINT32 value);
+		static UINT32 internal_GetFPSLimit();
+		static void internal_SetFPSLimit(UINT32 value);
 
 		static MonoString* internal_GetLastOpenProject();
 		static void internal_SetLastOpenProject(MonoString* value);

+ 14 - 0
SBansheeEditor/Source/BsScriptEditorSettings.cpp

@@ -32,6 +32,8 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_SetActiveCoordinateMode", &ScriptEditorSettings::internal_SetActiveCoordinateMode);
 		metaData.scriptClass->addInternalCall("Internal_GetActivePivotMode", &ScriptEditorSettings::internal_GetActivePivotMode);
 		metaData.scriptClass->addInternalCall("Internal_SetActivePivotMode", &ScriptEditorSettings::internal_SetActivePivotMode);
+		metaData.scriptClass->addInternalCall("Internal_GetFPSLimit", &ScriptEditorSettings::internal_GetFPSLimit);
+		metaData.scriptClass->addInternalCall("Internal_SetFPSLimit", &ScriptEditorSettings::internal_SetFPSLimit);
 		metaData.scriptClass->addInternalCall("Internal_GetLastOpenProject", &ScriptEditorSettings::internal_GetLastOpenProject);
 		metaData.scriptClass->addInternalCall("Internal_SetLastOpenProject", &ScriptEditorSettings::internal_SetLastOpenProject);
 		metaData.scriptClass->addInternalCall("Internal_GetAutoLoadLastProject", &ScriptEditorSettings::internal_GetAutoLoadLastProject);
@@ -149,6 +151,18 @@ namespace BansheeEngine
 		settings->setActivePivotMode(value);
 	}
 
+	UINT32 ScriptEditorSettings::internal_GetFPSLimit()
+	{
+		EditorSettingsPtr settings = gEditorApplication().getEditorSettings();
+		return settings->getFPSLimit();
+	}
+
+	void ScriptEditorSettings::internal_SetFPSLimit(UINT32 value)
+	{
+		EditorSettingsPtr settings = gEditorApplication().getEditorSettings();
+		settings->setFPSLimit(value);
+	}
+
 	MonoString* ScriptEditorSettings::internal_GetLastOpenProject()
 	{
 		EditorSettingsPtr settings = gEditorApplication().getEditorSettings();

+ 22 - 22
Scripts/build_editor_win_x64.py

@@ -1,22 +1,22 @@
-#!/usr/bin/python
-
-# Builds and packages the editor. Make sure you have MSBuild
-# installed and the path is valid.
-
-# Usage: "build_editor_win_x64 $Configuration"
-# Where: $Configuration - e.g. DebugRelease, Release
-
-import os
-
-msbuildPath = "C:\\Program Files (x86)\\MSBuild\\12.0\\Bin\\amd64"
-configuration = 'DebugRelease' #sys.argv[1]
-solutionPath = "..\\BansheeEngine.sln"
-
-if not os.path.exists(msbuildPath):
-    print("MSBuild path is not valid. Used path {0}: ".format(msbuildPath))
-    exit;
-
-os.environ["PATH"] += os.pathsep + msbuildPath
-os.system("msbuild {0} /p:Configuration=DebugRelease;Platform=x64".format(solutionPath))
-os.system("msbuild {0} /p:Configuration=Release;Platform=x64".format(solutionPath))
-os.system("package_editor.py " + configuration)
+#!/usr/bin/python
+
+# Builds and packages the editor. Make sure you have MSBuild
+# installed and the path is valid.
+
+# Usage: "build_editor_win_x64 $Configuration"
+# Where: $Configuration - e.g. DebugRelease, Release
+
+import os
+
+msbuildPath = "C:\\Program Files (x86)\\MSBuild\\14.0\\Bin\\amd64"
+configuration = 'DebugRelease' #sys.argv[1]
+solutionPath = "..\\BansheeEngine.sln"
+
+if not os.path.exists(msbuildPath):
+    print("MSBuild path is not valid. Used path {0}: ".format(msbuildPath))
+    exit;
+
+os.environ["PATH"] += os.pathsep + msbuildPath
+os.system("msbuild {0} /p:Configuration=DebugRelease;Platform=x64".format(solutionPath))
+os.system("msbuild {0} /p:Configuration=Release;Platform=x64".format(solutionPath))
+os.system("package_editor.py " + configuration)

+ 56 - 55
Scripts/package_editor.py

@@ -1,55 +1,56 @@
-#!/usr/bin/python
-
-# Packages the editor by copying all relevant files to the Builds directory.
-# This will not execute the editor build, and is implied the editor has been
-# been built before executing this script.
-
-# Usage: "package_editor $Configuration"
-# Where: $Configuration - e.g. Debug, DebugRelease
-
-import os
-import sys
-import shutil
-
-configuration = 'DebugRelease' #sys.argv[1]
-dataFoldersToIgnore = ['Examples', 'Raw']
-
-dataFolder = 'Data'
-assembliesFolder = 'Assemblies'
-monoFolder = 'Mono'
-libFolder = 'x64\\' + configuration + '\\'
-
-inputDataFolder = '..\\' + dataFolder
-inputBinFolder = '..\\bin\\'
-inputAssembliesFolder = inputBinFolder + assembliesFolder
-inputMonoFolder = inputBinFolder + monoFolder
-inputLibFolder = inputBinFolder + libFolder
-
-outputBaseFolder = '..\\Builds\Banshee Editor\\'
-outputDataFolder = outputBaseFolder + dataFolder
-outputBinFolder = outputBaseFolder + '\\bin\\'
-outputAssembliesFolder = outputBinFolder + assembliesFolder
-outputMonoFolder = outputBinFolder + monoFolder
-outputLibFolder = outputBaseFolder
-
-def ignore_data(path, entries):
-    if path != inputDataFolder:
-        return []
-
-    return list(set(dataFoldersToIgnore) & set(entries))    
-
-def package_editor():
-    if os.path.exists(outputBaseFolder):
-        shutil.rmtree(outputBaseFolder)
-
-    os.makedirs(outputBaseFolder)
-    shutil.copytree(inputDataFolder, outputDataFolder, False, ignore_data)
-    shutil.copytree(inputAssembliesFolder, outputAssembliesFolder)
-    shutil.copytree(inputMonoFolder, outputMonoFolder)
-
-    for root, dirs, files in os.walk(inputLibFolder):
-        for file in files:
-            filePath = os.path.join(root, file)
-            shutil.copy(filePath, outputLibFolder)
-
-package_editor()
+#!/usr/bin/python
+
+# Packages the editor by copying all relevant files to the Builds directory.
+# This will not execute the editor build, and is implied the editor has been
+# been built before executing this script.
+
+# Usage: "package_editor $Configuration"
+# Where: $Configuration - e.g. Debug, DebugRelease
+
+import os
+import sys
+import shutil
+
+configuration = 'DebugRelease' #sys.argv[1]
+dataFoldersToIgnore = ['Examples', 'Raw']
+
+dataFolder = 'Data'
+assembliesFolder = 'Assemblies'
+monoFolder = 'Mono'
+libFolder = 'x64\\' + configuration + '\\'
+
+inputDataFolder = '..\\' + dataFolder
+inputBinFolder = '..\\bin\\'
+inputAssembliesFolder = inputBinFolder + assembliesFolder
+inputMonoFolder = inputBinFolder + monoFolder
+inputLibFolder = inputBinFolder + libFolder
+
+outputBaseFolder = '..\\Builds\Banshee Editor\\'
+outputDataFolder = outputBaseFolder + dataFolder
+outputBinFolder = outputBaseFolder + '\\bin\\'
+outputAssembliesFolder = outputBinFolder + assembliesFolder
+outputMonoFolder = outputBinFolder + monoFolder
+outputLibFolder = outputBaseFolder
+
+def ignore_data(path, entries):
+    if path != inputDataFolder:
+        return []
+
+    return list(set(dataFoldersToIgnore) & set(entries))    
+
+def package_editor():
+    if os.path.exists(outputBaseFolder):
+        shutil.rmtree(outputBaseFolder)
+
+    os.makedirs(outputBaseFolder)
+    shutil.copytree(inputDataFolder, outputDataFolder, False, ignore_data)
+    shutil.copytree(inputAssembliesFolder, outputAssembliesFolder)
+    shutil.copytree(inputMonoFolder, outputMonoFolder)
+
+    for root, dirs, files in os.walk(inputLibFolder):
+        for file in files:
+            if(file.lower().endswith(('.dll', '.exe', '.pdb', '.so'))):
+                filePath = os.path.join(root, file)
+                shutil.copy(filePath, outputLibFolder)
+
+package_editor()