Browse Source

Adding untested windows threads. Fixing bugs

Panagiotis Christopoulos Charitos 11 years ago
parent
commit
ecbac1b3bc

+ 89 - 0
include/anki/core/Counters.h

@@ -7,6 +7,8 @@
 #include "anki/util/Singleton.h"
 #include "anki/util/File.h"
 #include "anki/util/HighRezTimer.h"
+#include "anki/util/Atomic.h"
+#include "anki/util/Thread.h"
 
 namespace anki {
 
@@ -93,4 +95,91 @@ typedef SingletonInit<CountersManager> CountersManagerSingleton;
 #	define ANKI_COUNTERS_FLUSH() ((void)0)
 #endif // ANKI_ENABLE_COUNTERS
 
+namespace detail {
+
+// Forward
+class ThreadTraceManager;
+
+/// Trace manager per process.
+class TraceManager
+{
+public:
+	Array<ThreadTraceManager*, 32> m_threadData;
+	AtomicU32 m_threadCount = {0};
+	File m_traceFile;
+	Mutex m_fileMtx;
+
+	TraceManager(HeapAllocator<U8>& alloc, const CString& storeDir);
+
+	~TraceManager();
+
+	void flushAll();
+
+	void flush(ThreadTraceManager& thread);
+};
+
+using TraceManagerSingleton = SingletonInit<TraceManager>;
+
+/// Trace manager per thread.
+class ThreadTraceManager
+{
+public:
+	static const U MAX_DEPTH = 4;
+	static const U BUFFERED_EVENTS_COUNT = 16;
+
+	class Event
+	{
+	public:
+		F64 m_startTime = 0.0;
+		F64 m_stopTime = 0.0;
+		const char* m_name;
+		U32 m_depth = 0;
+	};
+
+	TraceManager* m_master = nullptr; ///< Cache it
+	U32 m_id = 0;
+
+	Array<Event, MAX_DEPTH> m_inflightEvents; ///< Stack
+	U32 m_depth = 0;
+
+	Array<Event, BUFFERED_EVENTS_COUNT> m_bufferedEvents;
+	U32 m_bufferedEventsCount = 0;
+
+	ThreadTraceManager();
+
+	/// Begin a new event
+	void pushEvent(const char* name);
+
+	/// Stop an already started event
+	void popEvent();
+};
+
+using ThreadTraceManagerSingleton = SingletonThreadSafe<ThreadTraceManager>;
+
+} // end namespace detail
+
+/// @name Trace macros.
+/// @{
+
+#if ANKI_ENABLE_COUNTERS
+
+#	define ANKI_TRACE_PUSH_EVENT(name_) \
+	detail::ThreadTraceManagerSingleton::get().pushEvent(name_)
+
+#	define ANKI_TRACE_POP_EVENT() \
+	detail::ThreadTraceManagerSingleton::get().popEvent()
+
+#	define ANKI_TRACE_FLUSH() \
+	detail::TraceManagerSingleton::get().flushAll()
+
+#else
+
+#	define ANKI_TRACE_PUSH_EVENT(name_) ((void)0)
+#	define ANKI_TRACE_POP_EVENT() ((void)0)
+#	define ANKI_TRACE_FLUSH() ((void)0)
+
+#endif
+
+/// @}
+
 } // end namespace anki

+ 1 - 1
include/anki/gl/GlQueue.h

@@ -129,7 +129,7 @@ private:
 	GlCommandBufferHandle m_syncCommands;
 	GlClientSyncHandle m_sync;
 
-	String m_error;
+	char* m_error = nullptr;
 
 	/// The function that the thread runs
 	static I threadCallback(Thread::Info&);

+ 2 - 5
include/anki/gl/GlSync.h

@@ -19,14 +19,11 @@ class GlClientSync
 {
 public:
 	GlClientSync()
-		: m_barrier(2)
+	:	m_barrier(2)
 	{}
 
 	/// Wait 
-	void wait()
-	{
-		m_barrier.wait();
-	}
+	void wait();
 
 private:
 	Barrier m_barrier;

+ 21 - 0
include/anki/scene/Light.h

@@ -241,6 +241,11 @@ public:
 		return m_sphereW;
 	}
 
+	/// @name SceneNode virtuals
+	/// @{
+	void frameUpdate(F32 prevUpdateTime, F32 crntTime) override;
+	/// @}
+
 	/// @name MoveComponent virtuals
 	/// @{
 	void onMoveComponentUpdate(SceneNode&, F32, F32) override;
@@ -255,7 +260,23 @@ public:
 	/// @}
 
 public:
+	class ShadowData
+	{
+	public:
+		ShadowData(SceneNode* node)
+		:	m_frustumComps{{
+				{node, &m_frustums[0]}, {node, &m_frustums[1]},
+				{node, &m_frustums[2]}, {node, &m_frustums[3]},
+				{node, &m_frustums[4]}, {node, &m_frustums[5]}}}
+		{}
+
+		Array<PerspectiveFrustum, 6> m_frustums;
+		Array<FrustumComponent, 6> m_frustumComps;
+		Array<Transform, 6> m_localTrfs;
+	};
+
 	Sphere m_sphereW = Sphere(Vec4(0.0), 1.0);
+	ShadowData* m_shadowData = nullptr;
 };
 
 /// Spot light

+ 3 - 2
include/anki/util/HighRezTimer.h

@@ -18,7 +18,7 @@ class HighRezTimer
 {
 public:
 	/// The type that the timer manipulates the results
-	typedef F64 Scalar;
+	using Scalar = F64;
 
 	/// Start the timer
 	void start();
@@ -33,7 +33,8 @@ public:
 	/// Get the current date's seconds
 	static Scalar getCurrentTime();
 
-	/// Micro sleep
+	/// Micro sleep.
+	/// The resolution is in nanoseconds.
 	static void sleep(Scalar seconds);
 
 private:

+ 36 - 19
include/anki/util/Thread.h

@@ -56,20 +56,33 @@ public:
 	/// Identify the current thread
 	static Id getCurrentThreadId();
 
+	/// @privatesection
+	/// @{
+	const char* _getName() const
+	{
+		return &m_name[0];
+	}
+
+	void* _getUserData() const
+	{
+		return m_userData;
+	}
+
+	Callback _getCallback() const
+	{
+		return m_callback;
+	}
+	/// @}
+
 private:
-	static constexpr U ALIGNMENT = 8;
-	alignas(ALIGNMENT) Array<PtrSize, 1> m_impl; ///< The system native type
+	void* m_impl = nullptr; ///< The system native type
 	Array<char, 32> m_name; ///< The name of the thread
 	Callback m_callback = nullptr; ///< The callback
 	void* m_userData = nullptr; ///< The user date to pass to the callback
 
-	/// Pthreads specific function
-	static void* pthreadCallback(void* ud);
-
-	Bool initialized() const
-	{
-		return m_impl[0] != 0;
-	}
+#if ANKI_ASSERTIONS
+	Bool8 m_started = false;
+#endif
 };
 
 /// Mutex
@@ -93,8 +106,7 @@ public:
 	void unlock();
 
 private:
-	static constexpr U ALIGNMENT = 8;
-	alignas(ALIGNMENT) Array<PtrSize, 10> m_impl; ///< The system native type
+	void* m_impl = nullptr; ///< The system native type
 };
 
 /// Condition variable
@@ -111,12 +123,15 @@ public:
 	/// Signal all threads
 	void notifyAll();
 
-	/// Bock until signaled
-	void wait(Mutex& mtx);
+	/// Bock until signaled.
+	/// @param mtx The mutex.
+	/// @param timeoutSeconds Wait for the specified time. If zero it waits 
+	///                       forever.
+	/// @return On timeout it returns true.
+	Bool wait(Mutex& mtx, F64 timeoutSeconds = 0.0);
 
 private:
-	static constexpr U ALIGNMENT = 16;
-	alignas(ALIGNMENT) Array<PtrSize, 12> m_impl; ///< The system native type
+	void* m_impl = nullptr; ///< The system native type
 };
 
 /// Spin lock. Good if the critical section will be executed in a short period
@@ -162,7 +177,7 @@ private:
 	TMutex* m_mtx;
 };
 
-/// A barrier for thread synchronization. It works just like boost::barrier
+/// A barrier for thread synchronization. It works almost like boost::barrier
 class Barrier: public NonCopyable
 {
 public:
@@ -176,9 +191,11 @@ public:
 
 	~Barrier() = default;
 
-	/// Wait until all threads call wait()
-	/// @return This is implementation defined. Don't pay attention.
-	Bool wait();
+	/// Wait until all threads call wait().
+	/// @param timeoutSeconds Wait for the specified time. If zero it waits 
+	///                       forever.
+	/// @return On timeout it returns true.
+	Bool wait(F64 timeoutSeconds = 0.0);
 
 private:
 	Mutex m_mtx;

+ 30 - 26
shaders/MsCommonTessc.glsl

@@ -7,14 +7,16 @@
 
 layout(vertices = 3) out;
 
+// Defines
 #define IID gl_InvocationID
-#define IN_POS(i_) gl_in[i_].gl_Position
-#define OUT_POS(i_) gl_out[i_].gl_Position
+#define IN_POS4(i_) gl_in[i_].gl_Position
+#define IN_POS3(i_) gl_in[i_].gl_Position.xyz
+#define OUT_POS4(i_) gl_out[i_].gl_Position
 
 // In
 layout(location = 0) in vec2 inTexCoords[];
 layout(location = 1) in mediump vec3 inNormal[];
-#if PASS_COLOR
+#if PASS == COLOR
 layout(location = 2) in mediump vec4 inTangent[];
 #endif
 #if INSTANCE_ID_FRAGMENT_SHADER
@@ -24,7 +26,7 @@ layout(location = 3) flat in uint inInstanceId[];
 // Out
 layout(location = 0) out vec2 outTexCoord[];
 layout(location = 1) out vec3 outNormal[];
-#if PASS_COLOR
+#if PASS == COLOR
 layout(location = 2) out vec4 outTangent[];
 #endif
 
@@ -46,10 +48,6 @@ struct PNPatch
 	vec3 pos111;
 };
 
-#define pos030 OUT_POS(0)
-#define pos003 OUT_POS(1)
-#define pos300 OUT_POS(2)
-
 struct PhongPatch
 {
 	vec3 terms[3];
@@ -74,9 +72,13 @@ vec3 projectToPlane(vec3 point, vec3 planePoint, vec3 planeNormal)
 void calcPositions()
 {
 	// The original vertices stay the same
-	pos030 = IN_POS(0);
-	pos003 = IN_POS(1);
-	pos300 = IN_POS(2);
+	vec3 pos030 = IN_POS3(0);
+	vec3 pos003 = IN_POS3(1);
+	vec3 pos300 = IN_POS3(2);
+
+	OUT_POS4(0) = IN_POS3(0);
+	OUT_POS4(1) = IN_POS3(1);
+	OUT_POS4(2) = IN_POS3(2);
 
 	// edges are names according to the opposing vertex
 	vec3 edgeB300 = pos003 - pos030;
@@ -194,7 +196,7 @@ bool isFaceVisible(in mat4 mvp)
 	vec2 clip[3];
 	for(int i = 0 ; i < 3 ; i++) 
 	{
-		vec4 v = mvp * vec4(IN_POS(i), 1.0);
+		vec4 v = mvp * IN_POS4(i);
 		clip[i] = v.xy / abs(v.w);
 	}
 
@@ -202,9 +204,10 @@ bool isFaceVisible(in mat4 mvp)
 	return isFaceFrontFacing(clip) && !isFaceOutsideClipSpace(clip);
 }
 
-float calcPhongTerm(int ivId, int i, vec3 q)
+// Used in phong method
+float calcPhongTerm(int ivId, int i, int j)
 {
-	vec3 qMinusP = q - IN_POS(i);
+	vec3 qMinusP = IN_POS3(j) - IN_POS3(i);
 	return q[ivId] - dot(qMinusP, inNormal[i]) * inNormal[i][ivId];
 }
 
@@ -218,7 +221,7 @@ void tessellatePNPositionNormalTangentTexCoord(
 	float tessLevel = 0.0;
 
 	// Calculate the face normal in view space
-	vec3 faceNorm = calcFaceNormal(IN_POS(0), IN_POS(1), IN_POS(2));
+	vec3 faceNorm = calcFaceNormal(IN_POS3(0), IN_POS3(1), IN_POS3(2));
 	faceNorm = (normalMat * faceNorm);
 
 	if(faceNorm.z >= 0.0)
@@ -229,7 +232,7 @@ void tessellatePNPositionNormalTangentTexCoord(
 		{		
 			outTexCoord[i] = inTexCoords[i];
 			outNormal[i] = inNormal[i];
-#if PASS_COLOR
+#if PASS == COLOR
 			outTangent[i] = inTangent[i];
 #endif
 		}
@@ -266,7 +269,7 @@ void tessellatePhongPositionNormalTangentTexCoord(
 		}
 	}
 
-	OUT_POS(IID) = IN_POS(IID); // Do that here to trick the barrier
+	OUT_POS4(IID) = IN_POS4(IID); // Do that here to trick the barrier
 
 	barrier();
 
@@ -274,16 +277,16 @@ void tessellatePhongPositionNormalTangentTexCoord(
 	{
 		outTexCoord[IID] = inTexCoords[IID];
 		outNormal[IID] = inNormal[IID];
-#if PASS_COLOR
+#if PASS == COLOR
 		outTangent[IID] = inTangent[IID];
 #endif
 
-		phongPatch.terms[IID][0] = calcPhongTerm(IID, 0, IN_POS(1)) 
-			+ calcPhongTerm(IID, 1, IN_POS(0));
-		phongPatch.terms[IID][1] = calcPhongTerm(IID, 1, IN_POS(2)) 
-			+ calcPhongTerm(IID, 2, IN_POS(1));
-		phongPatch.terms[IID][2] = calcPhongTerm(IID, 2, IN_POS(0)) 
-			+ calcPhongTerm(IID, 0, IN_POS(2));
+		phongPatch.terms[IID][0] = calcPhongTerm(IID, 0, 1) 
+			+ calcPhongTerm(IID, 1, 0);
+		phongPatch.terms[IID][1] = calcPhongTerm(IID, 1, 2) 
+			+ calcPhongTerm(IID, 2, 1);
+		phongPatch.terms[IID][2] = calcPhongTerm(IID, 2, 0) 
+			+ calcPhongTerm(IID, 0, 2);
 	}
 }
 
@@ -309,10 +312,11 @@ void tessellateDispMapPositionNormalTangentTexCoord(
 		}
 	}
 
-	OUT_POS(IID) = IN_POS(IID);
+	// Passthrough
+	OUT_POS4(IID) = IN_POS4(IID);
 	outTexCoord[IID] = inTexCoords[IID];
 	outNormal[IID] = inNormal[IID];
-#if PASS_COLOR
+#if PASS == COLOR
 	outTangent[IID] = inTangent[IID];
 #endif
 }

+ 19 - 16
shaders/MsCommonTesse.glsl

@@ -5,6 +5,9 @@
 
 layout(triangles, equal_spacing, ccw) in;
 
+#define IN_POS4(i_) gl_in[i_].gl_Position
+#define IN_POS3(i_) gl_in[i_].gl_Position.xyz
+
 struct PNPatch
 {
 	vec3 pos021;
@@ -16,12 +19,6 @@ struct PNPatch
 	vec3 pos111;
 };
 
-#define IN_POS(i_) gl_in[i_].gl_Position
-
-#define pos030 IN_POS(0)
-#define pos003 IN_POS(1)
-#define pos300 IN_POS(2)
-
 struct PhongPatch
 {
 	vec3 terms[3];
@@ -47,7 +44,7 @@ layout(location = 2) in vec4 inTangent[];
 #endif
 
 // Varyings out
-layout(location = 0) out highp vec2 outTexCoords;
+layout(location = 0) out highp vec2 outTexCoord;
 #if PASS_COLOR
 layout(location = 1) out mediump vec3 outNormal;
 layout(location = 2) out mediump vec4 outTangent;
@@ -65,7 +62,7 @@ void tessellatePNPositionNormalTangentTexCoord(in mat4 mvp, in mat3 normalMat)
 	outTangent.xyz = normalize(normalMat * outTangent.xyz);
 #endif
 
-	outTexCoords = INTERPOLATE(inTexCoord);
+	outTexCoord = INTERPOLATE(inTexCoord);
 
 	float u = gl_TessCoord.x;
 	float v = gl_TessCoord.y;
@@ -78,6 +75,10 @@ void tessellatePNPositionNormalTangentTexCoord(in mat4 mvp, in mat3 normalMat)
 	float vPow2 = pow(v, 2);
 	float wPow2 = pow(w, 2);
 
+	vec3 pos030 = IN_POS3(0);
+	vec3 pos003 = IN_POS3(1);
+	vec3 pos300 = IN_POS3(2);
+
 	vec3 pos = 
 		pos300 * wPow3
 		+ pos030 * uPow3
@@ -103,10 +104,11 @@ void tessellatePhongPositionNormalTangentTexCoord(
 	outTangent.xyz = normalize(normalMat * outTangent.xyz);
 #endif
 
-	outTexCoords = INTERPOLATE(inTexCoord);
+	outTexCoord = INTERPOLATE(inTexCoord);
 
 	// interpolated position
-	vec3 barPos = INTERPOLATE(tcPosition);
+	vec3 inpos[3] = vec3[](IN_POS3(0), IN_POS3(1), IN_POS3(2));
+	vec3 barPos = INTERPOLATE(inpos);
 
 	// build terms
 	vec3 termIJ = vec3(
@@ -126,9 +128,9 @@ void tessellatePhongPositionNormalTangentTexCoord(
 
 	// phong tesselated pos
 	vec3 phongPos = 
-		tc2[0] * tcPosition[0]
-		 + tc2[1] * tcPosition[1]
-		 + tc2[2] * tcPosition[2]
+		tc2[0] * inpos[0]
+		 + tc2[1] * inpos[1]
+		 + tc2[2] * inpos[2]
 		 + gl_TessCoord[0] * gl_TessCoord[1] * termIJ
 		 + gl_TessCoord[1] * gl_TessCoord[2] * termJK
 		 + gl_TessCoord[2] * gl_TessCoord[0] * termIK;
@@ -149,12 +151,13 @@ void tessellateDispMapPositionNormalTangentTexCoord(
 	outTangent.xyz = normalize(normalMat * outTangent.xyz);
 #endif
 
-	outTexCoords = INTERPOLATE(inTexCoord);
+	outTexCoord = INTERPOLATE(inTexCoord);
 
-	float height = texture(dispMap, outTexCoords).r;
+	float height = texture(dispMap, outTexCoord).r;
 	height = height * 0.7 - 0.35;
 
-	vec3 pos = INTERPOLATE(tcPosition) + norm * height;
+	vec3 inpos[3] = vec3[](IN_POS3(0), IN_POS3(1), IN_POS3(2));
+	vec3 pos = INTERPOLATE(inpos) + norm * height;
 	gl_Position = mvp * vec4(pos, 1.0);
 }
 

+ 3 - 1
src/core/App.cpp

@@ -142,6 +142,7 @@ void App::init(const ConfigSet& config_)
 
 #if ANKI_ENABLE_COUNTERS
 	CountersManagerSingleton::init(m_heapAlloc, m_settingsDir.toCString());
+	detail::TraceManagerSingleton::init(m_heapAlloc, m_settingsDir.toCString());
 #endif
 
 	// Window
@@ -309,9 +310,10 @@ void App::mainLoop(UserMainLoopCallback callback, void* userData)
 		increaseGlobTimestamp();
 	}
 
-	// Counters end
+	// Performance ends
 	ANKI_COUNTER_STOP_TIMER_INC(FPS);
 	ANKI_COUNTERS_FLUSH();
+	ANKI_TRACE_FLUSH();
 }
 
 } // end namespace anki

+ 100 - 3
src/core/Counters.cpp

@@ -3,6 +3,10 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
+#include "anki/Config.h"
+
+#if ANKI_ENABLE_COUNTERS
+
 #include "anki/core/Counters.h"
 #include "anki/core/Timestamp.h"
 #include "anki/core/App.h"
@@ -11,8 +15,6 @@
 
 namespace anki {
 
-#if ANKI_ENABLE_COUNTERS
-
 //==============================================================================
 
 enum CounterFlag
@@ -246,6 +248,101 @@ void CountersManager::flush()
 	m_perrunFile.close();
 }
 
-#endif // ANKI_ENABLE_COUNTERS
+//==============================================================================
+// TraceManager                                                                =
+//==============================================================================
+
+namespace detail {
+
+//==============================================================================
+TraceManager::TraceManager(HeapAllocator<U8>& alloc, const CString& storeDir)
+{
+	String filename(storeDir, alloc);
+	filename += "/trace";
+	m_traceFile.open(filename.toCString(), File::OpenFlag::WRITE);
+
+	m_traceFile.writeText(
+		"thread_id, depth, event_name, start_time, stop_time\n");
+}
+
+//==============================================================================
+TraceManager::~TraceManager()
+{
+	flushAll();
+}
+
+//==============================================================================
+void TraceManager::flush(ThreadTraceManager& thread)
+{
+	ANKI_ASSERT(thread.m_depth == 0);
+
+	LockGuard<Mutex> lock(m_fileMtx);
+	for(U i = 0; i < thread.m_bufferedEventsCount; i++)
+	{
+		const ThreadTraceManager::Event& event = thread.m_bufferedEvents[i];
+
+		m_traceFile.writeText("%u, %u, %s, %f, %f\n", 
+			thread.m_id,
+			event.m_depth,
+			event.m_name,
+			event.m_startTime,
+			event.m_stopTime);
+	}
+
+	thread.m_bufferedEventsCount = 0;
+}
+
+//==============================================================================
+void TraceManager::flushAll()
+{
+	auto count = m_threadCount.load();
+	while(count-- != 0)
+	{
+		flush(*m_threadData[count]);
+	}
+
+	m_traceFile.close();
+}
+
+//==============================================================================
+ThreadTraceManager::ThreadTraceManager()
+{
+	TraceManager& master = TraceManagerSingleton::get();
+	m_master = &master;
+
+	auto index = master.m_threadCount.fetch_add(1);
+	master.m_threadData[index] = this;
+	m_id = index;
+}
+
+//==============================================================================
+void ThreadTraceManager::pushEvent(const char* name)
+{
+	Event& event = m_inflightEvents[m_depth];
+	event.m_depth = m_depth++;
+	event.m_name = name;
+	event.m_startTime = HighRezTimer::getCurrentTime();
+}
+
+//==============================================================================
+void ThreadTraceManager::popEvent()
+{
+	ANKI_ASSERT(m_depth > 0);
+	Event& event = m_inflightEvents[--m_depth];
+	event.m_stopTime = HighRezTimer::getCurrentTime();
+
+	ANKI_ASSERT(m_bufferedEventsCount < m_bufferedEvents.getSize());
+	m_bufferedEvents[m_bufferedEventsCount++] = event;
+
+	if(m_bufferedEventsCount == m_bufferedEvents.getSize())
+	{
+		m_master->flush(*this);
+	}
+}
+
+} // end namespace detail
 
 } // end namespace anki
+
+#endif // ANKI_ENABLE_COUNTERS
+

+ 26 - 11
src/gl/GlQueue.cpp

@@ -12,6 +12,14 @@
 
 namespace anki {
 
+//==============================================================================
+#define CHECK_ERROR() \
+	if(m_error != nullptr) \
+	{ \
+		throw ANKI_EXCEPTION("GL rendering thread failed with error:\n%s", \
+			&m_error[0]); \
+	}
+
 //==============================================================================
 GlQueue::GlQueue(GlDevice* device, 
 	AllocAlignedCallback allocCb, void* allocCbUserData)
@@ -28,7 +36,12 @@ GlQueue::GlQueue(GlDevice* device,
 
 //==============================================================================
 GlQueue::~GlQueue()
-{}
+{
+	if(m_error)
+	{
+		m_allocCb(m_allocCbUserData, m_error, 0, 0);
+	}
+}
 
 //==============================================================================
 void GlQueue::flushCommandBuffer(GlCommandBufferHandle& commands)
@@ -39,11 +52,7 @@ void GlQueue::flushCommandBuffer(GlCommandBufferHandle& commands)
 	{
 		LockGuard<Mutex> lock(m_mtx);
 
-		if(!m_error.isEmpty())
-		{
-			throw ANKI_EXCEPTION("GL rendering thread failed with error:\n%s",
-				&m_error[0]);
-		}
+		CHECK_ERROR();
 
 		// Set commands
 		U64 diff = m_tail - m_head;
@@ -197,7 +206,7 @@ void GlQueue::threadLoop()
 
 	while(1)
 	{
-		GlCommandBufferHandle commandc;
+		GlCommandBufferHandle cmd;
 
 		// Wait for something
 		{
@@ -216,7 +225,7 @@ void GlQueue::threadLoop()
 
 			U64 idx = m_head % m_queue.size();
 			// Pop a command
-			commandc = m_queue[idx];
+			cmd = m_queue[idx];
 			m_queue[idx] = GlCommandBufferHandle(); // Insert empty cmd buffer
 
 			++m_head;
@@ -224,13 +233,17 @@ void GlQueue::threadLoop()
 
 		try
 		{
-			// Exec commands of chain
-			commandc._executeAllCommands();
+			// Exec commands
+			cmd._executeAllCommands();
 		}
 		catch(const std::exception& e)
 		{
 			LockGuard<Mutex> lock(m_mtx);
-			m_error = e.what();
+			I len = strlen(e.what());
+			m_error = reinterpret_cast<char*>(
+				m_allocCb(m_allocCbUserData, nullptr, len + 1, 1));
+
+			strcpy(m_error, e.what());
 		}
 	}
 
@@ -243,6 +256,8 @@ void GlQueue::syncClientServer()
 #if !ANKI_QUEUE_DISABLE_ASYNC
 	flushCommandBuffer(m_syncCommands);
 	m_sync.wait();
+
+	CHECK_ERROR();
 #endif
 }
 

+ 21 - 0
src/gl/GlSync.cpp

@@ -0,0 +1,21 @@
+// Copyright (C) 2014, Panagiotis Christopoulos Charitos.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include "anki/gl/GlSync.h"
+#include "anki/core/Logger.h"
+
+namespace anki {
+
+//==============================================================================
+void GlClientSync::wait()
+{
+	Bool timeout = m_barrier.wait(2.0);
+	if(timeout)
+	{
+		ANKI_LOGW("Sync timed out. Probably because of exception");
+	}
+}
+
+} // end namespace anki

+ 28 - 0
src/scene/Light.cpp

@@ -102,6 +102,34 @@ void PointLight::onMoveComponentUpdate(SceneNode&, F32, F32)
 	onMoveComponentUpdateCommon();
 }
 
+//==============================================================================
+void PointLight::frameUpdate(F32 prevUpdateTime, F32 crntTime)
+{
+	if(getShadowEnabled() && m_shadowData == nullptr)
+	{
+		m_shadowData = getAllocator().newInstance<ShadowData>(this);
+
+		const F32 ang = toRad(90.0);
+		F32 dist = m_sphereW.getRadius();
+
+		for(U i = 0; i < 6; i++)
+		{
+			m_shadowData->m_frustums[i].setAll(ang, ang, 0.1, dist);
+			m_shadowData->m_localTrfs[i] = Transform::getIdentity();
+		}
+
+		auto& trfs = m_shadowData->m_localTrfs;
+		Vec3 axis = Vec3(0.0, 1.0, 0.0);
+		trfs[1].setRotation(Mat3x4(Mat3(Axisang(ang, axis))));
+		trfs[2].setRotation(Mat3x4(Mat3(Axisang(ang * 2.0, axis))));
+		trfs[3].setRotation(Mat3x4(Mat3(Axisang(ang * 3.0, axis))));
+
+		axis = Vec3(1.0, 0.0, 0.0);
+		trfs[4].setRotation(Mat3x4(Mat3(Axisang(ang, axis))));
+		trfs[5].setRotation(Mat3x4(Mat3(Axisang(-ang, axis))));
+	}
+}
+
 //==============================================================================
 // SpotLight                                                                   =
 //==============================================================================

+ 1 - 1
src/util/CMakeLists.txt

@@ -3,7 +3,7 @@ set(ANKI_UTIL_SOURCES Assert.cpp Exception.cpp Functions.cpp File.cpp Filesystem
 if(LINUX OR ANDROID OR MACOS)
 	set(ANKI_UTIL_SOURCES ${ANKI_UTIL_SOURCES} HighRezTimerPosix.cpp FilesystemPosix.cpp ThreadPosix.cpp)
 else()
-	set(ANKI_UTIL_SOURCES ${ANKI_UTIL_SOURCES} HighRezTimerWindows.cpp FilesystemWindows.cpp)
+	set(ANKI_UTIL_SOURCES ${ANKI_UTIL_SOURCES} HighRezTimerWindows.cpp FilesystemWindows.cpp ThreadWindows.cpp)
 endif()
 
 add_library(ankiutil ${ANKI_UTIL_SOURCES})

+ 2 - 2
src/util/Exception.cpp

@@ -58,7 +58,7 @@ Exception::Exception(const char* file, I line, const char* func,
 	}
 
 #if ANKI_DEBUG == 1
-	fprintf(stderr, "Exception thrown: %s\n", &m_err[0]);
+	fprintf(stderr, "Exception thrown\n");
 #endif
 
 #if ANKI_ABORT_ON_THROW == 1
@@ -75,7 +75,7 @@ Exception::Exception(const Exception& e) noexcept
 	std::strcpy(m_err, e.m_err);
 
 #if ANKI_DEBUG == 1
-	fprintf(stderr, "Exception copied: %s\n", &m_err[0]);
+	fprintf(stderr, "Exception copied\n");
 #endif
 
 #if ANKI_ABORT_ON_THROW == 1

+ 9 - 63
src/util/HighRezTimerPosix.cpp

@@ -6,21 +6,9 @@
 #include "anki/util/HighRezTimer.h"
 #include "anki/util/Assert.h"
 #include <sys/time.h>
-#include <signal.h>
 #include <unistd.h>
 #include <errno.h>
-
-#if !defined(HAVE_NANOSLEEP)
-#	define HAVE_NANOSLEEP 0
-#endif
-
-#if !defined(HAVE_CLOCK_GETTIME)
-#	define HAVE_CLOCK_GETTIME 0
-#endif
-
-#if HAVE_NANOSLEEP || HAVE_CLOCK_GETTIME
-#	include <time.h>
-#endif
+#include <time.h>
 
 namespace anki {
 
@@ -28,22 +16,14 @@ namespace anki {
 namespace {
 
 // The first ticks value of the application
-#if HAVE_CLOCK_GETTIME
 struct timespec gstart;
-#else
-struct timeval gstart;
-#endif
 
 /// A dummy struct that inits the timer
 struct DummyInitTimer
 {
 	DummyInitTimer()
 	{
-#if HAVE_CLOCK_GETTIME
 		clock_gettime(CLOCK_MONOTONIC, &gstart);
-#else
-		gettimeofday(&gstart, NULL);
-#endif
 	}
 };
 
@@ -52,21 +32,14 @@ DummyInitTimer dummy;
 } // end namespace anonymous 
 
 //==============================================================================
-static U32 getMs()
+static U64 getNs()
 {
 	U32 ticks;
 
-#if HAVE_CLOCK_GETTIME
 	struct timespec now;
 	clock_gettime(CLOCK_MONOTONIC, &now);
-	ticks = (now.tv_sec - gstart.tv_sec) * 1000 
-		+ (now.tv_nsec - gstart.tv_nsec) / 1000000;
-#else
-	struct timeval now;
-	gettimeofday(&now, NULL);
-	ticks = (now.tv_sec - gstart.tv_sec) * 1000 
-		+ (now.tv_usec - gstart.tv_usec) / 1000;
-#endif
+	ticks = (now.tv_sec - gstart.tv_sec) * 1000000000 
+		+ (now.tv_nsec - gstart.tv_nsec);
 
 	return ticks;
 }
@@ -75,46 +48,19 @@ static U32 getMs()
 void HighRezTimer::sleep(Scalar sec)
 {
 	int wasError;
-	U32 ms = U32(sec * 1000.0);
+	U64 ns = static_cast<U64>(sec * 1e+9);
 
-#if HAVE_NANOSLEEP
 	struct timespec elapsed, tv;
-#else
-	struct timeval tv;
-	U32 then, now, elapsed;
-#endif
-
-	// Set the timeout interval
-#if HAVE_NANOSLEEP
-	elapsed.tv_sec = ms / 1000;
-	elapsed.tv_nsec = (ms % 1000) * 1000000;
-#else
-	then = getMs();
-#endif
+
+	elapsed.tv_sec = ns / 1000000000;
+	elapsed.tv_nsec = (ns % 1000000000);
 
 	do 
 	{
 		errno = 0;
-
-#if HAVE_NANOSLEEP
 		tv.tv_sec = elapsed.tv_sec;
 		tv.tv_nsec = elapsed.tv_nsec;
 		wasError = nanosleep(&tv, &elapsed);
-#else
-		// Calculate the time interval left (in case of interrupt)
-		now = getMs();
-		elapsed = now - then;
-		then = now;
-		if(elapsed >= ms)
-		{
-			break;
-		}
-		ms -= elapsed;
-		tv.tv_sec = ms / 1000;
-		tv.tv_usec = (ms % 1000) * 1000;
-
-		wasError = select(0, NULL, NULL, NULL, &tv);
-#endif
 	} while(wasError && (errno == EINTR));
 }
 
@@ -122,7 +68,7 @@ void HighRezTimer::sleep(Scalar sec)
 HighRezTimer::Scalar HighRezTimer::getCurrentTime()
 {
 	// Scalar(ticks) / 1000.0
-	return Scalar(getMs()) * 0.001;
+	return static_cast<Scalar>(getNs()) * 1e-9;
 }
 
 } // end namespace anki

+ 8 - 7
src/util/Thread.cpp

@@ -14,27 +14,28 @@ namespace anki {
 //==============================================================================
 
 //==============================================================================
-Bool Barrier::wait()
+Bool Barrier::wait(F64 timeoutSeconds)
 {
 	m_mtx.lock();
 	U32 gen = m_generation;
+	Bool timeout = false;
 
 	if(--m_count == 0)
 	{
 		m_generation++;
 		m_count = m_threshold;
 		m_cond.notifyAll();
-		m_mtx.unlock();
-		return true;
 	}
-
-	while(gen == m_generation)
+	else
 	{
-		m_cond.wait(m_mtx);
+		while(gen == m_generation)
+		{
+			timeout = m_cond.wait(m_mtx, timeoutSeconds);
+		}
 	}
 
 	m_mtx.unlock();
-	return false;
+	return timeout;
 }
 
 //==============================================================================

+ 109 - 70
src/util/ThreadPosix.cpp

@@ -16,17 +16,37 @@ namespace anki {
 //==============================================================================
 
 //==============================================================================
-Thread::Thread(const char* name)
+static void* pthreadCallback(void* ud)
 {
-	// Do some static assertions on the private data
-	static_assert(sizeof(m_impl) >= sizeof(pthread_t), 
-		"Incorrect impl size");
+	ANKI_ASSERT(ud != nullptr);
+	Thread* thread = reinterpret_cast<Thread*>(ud);
 
-	static_assert(ALIGNMENT >= alignof(pthread_t), "Incorrect impl alignment");
+	// Set thread name
+	if(thread->_getName()[0] != '\0')
+	{
+		pthread_setname_np(pthread_self(), &thread->_getName()[0]);
+	}
 
-#if ANKI_ASSERTIONS
-	std::memset(&m_impl[0], 0, sizeof(m_impl));
-#endif
+	// Call the callback
+	Thread::Info info;
+	info.m_userData = thread->_getUserData();
+	info.m_threadName = thread->_getName();
+
+	I err = thread->_getCallback()(info);
+	void* errVoidp = nullptr;
+	std::memcpy(&errVoidp, &err, sizeof(err));
+
+	return nullptr;
+}
+
+//==============================================================================
+Thread::Thread(const char* name)
+{
+	m_impl = malloc(sizeof(pthread_t));
+	if(m_impl == nullptr)
+	{
+		throw ANKI_EXCEPTION("Out of memory");
+	}
 
 	// Init the name
 	if(name)
@@ -45,70 +65,52 @@ Thread::Thread(const char* name)
 //==============================================================================
 Thread::~Thread()
 {
-	ANKI_ASSERT(!initialized() && "Thread probably not joined");
-}
-
-//==============================================================================
-void* Thread::pthreadCallback(void* ud)
-{
-	ANKI_ASSERT(ud != nullptr);
-	Thread* thread = reinterpret_cast<Thread*>(ud);
-
-	// Set thread name
-	if(thread->m_name[0] != '\0')
-	{
-		pthread_setname_np(pthread_self(), &thread->m_name[0]);
-	}
-
-	// Call the callback
-	Info info;
-	info.m_userData = thread->m_userData;
-	info.m_threadName = &thread->m_name[0];
-
-	I err = thread->m_callback(info);
-	void* errVoidp = nullptr;
-	std::memcpy(&errVoidp, &err, sizeof(err));
-
-	return nullptr;
+	ANKI_ASSERT(!m_started && "Thread probably not joined");
+	free(m_impl);
+	m_impl = nullptr;
 }
 
 //==============================================================================
 void Thread::start(void* userData, Callback callback)
 {
+	ANKI_ASSERT(!m_started);
 	ANKI_ASSERT(callback != nullptr);
 
-	ANKI_ASSERT(!initialized());
-	pthread_t* impl = reinterpret_cast<pthread_t*>(&m_impl[0]);
+	pthread_t* impl = reinterpret_cast<pthread_t*>(m_impl);
 
 	m_callback = callback;
 	m_userData = userData;
 
 	I err = pthread_create(impl, nullptr, pthreadCallback, this);
 	if(err)
+	{
+		throw ANKI_EXCEPTION("pthread_create() failed");
+	}
+	else
 	{
 #if ANKI_ASSERTIONS
-		std::memset(&m_impl[0], 0, sizeof(m_impl));
+		m_started = true;
 #endif
-		throw ANKI_EXCEPTION("pthread_create() failed");
 	}
 }
 
 //==============================================================================
 I Thread::join()
 {
-	ANKI_ASSERT(initialized());
-	pthread_t* impl = reinterpret_cast<pthread_t*>(&m_impl[0]);
+	ANKI_ASSERT(m_started);
+	pthread_t* impl = reinterpret_cast<pthread_t*>(m_impl);
 
 	void* out;
 	U err = pthread_join(*impl, &out);
-#if ANKI_ASSERTIONS
-	std::memset(&m_impl[0], 0, sizeof(m_impl));
-#endif
 	if(err)
 	{
 		throw ANKI_EXCEPTION("pthread_join() failed");
 	}
 
+#if ANKI_ASSERTIONS
+	m_started = false;
+#endif
+
 	// Set return error code
 	I callbackErr;
 	std::memcpy(&callbackErr, &out, sizeof(callbackErr));
@@ -129,18 +131,20 @@ Thread::Id Thread::getCurrentThreadId()
 //==============================================================================
 Mutex::Mutex()
 {
-	// Do some static assertions on the private data
-	static_assert(sizeof(m_impl) >= sizeof(pthread_mutex_t), 
-		"Incorrect impl size");
-
-	static_assert(ALIGNMENT >= alignof(pthread_mutex_t), 
-		"Incorrect impl alignment");
+	pthread_mutex_t* mtx = 
+		reinterpret_cast<pthread_mutex_t*>(malloc(sizeof(pthread_mutex_t)));
+	if(mtx == nullptr)
+	{
+		throw ANKI_EXCEPTION("Out of memory");
+	}
 
-	pthread_mutex_t* mtx = reinterpret_cast<pthread_mutex_t*>(&m_impl[0]);
+	m_impl = mtx;
 
 	I err = pthread_mutex_init(mtx, nullptr);
 	if(err)
 	{
+		free(m_impl);
+		m_impl = nullptr;
 		throw ANKI_EXCEPTION("pthread_mutex_init() failed");
 	}
 }
@@ -148,14 +152,17 @@ Mutex::Mutex()
 //==============================================================================
 Mutex::~Mutex()
 {
-	pthread_mutex_t* mtx = reinterpret_cast<pthread_mutex_t*>(&m_impl[0]);
+	pthread_mutex_t* mtx = reinterpret_cast<pthread_mutex_t*>(m_impl);
 	pthread_mutex_destroy(mtx);
+
+	free(m_impl);
+	m_impl = nullptr;
 }
 
 //==============================================================================
 void Mutex::lock()
 {
-	pthread_mutex_t* mtx = reinterpret_cast<pthread_mutex_t*>(&m_impl[0]);
+	pthread_mutex_t* mtx = reinterpret_cast<pthread_mutex_t*>(m_impl);
 
 	I err = pthread_mutex_lock(mtx);
 	if(err)
@@ -167,7 +174,7 @@ void Mutex::lock()
 //==============================================================================
 Bool Mutex::tryLock()
 {
-	pthread_mutex_t* mtx = reinterpret_cast<pthread_mutex_t*>(&m_impl[0]);
+	pthread_mutex_t* mtx = reinterpret_cast<pthread_mutex_t*>(m_impl);
 
 	I err = pthread_mutex_trylock(mtx);
 	return err == 0;
@@ -176,7 +183,7 @@ Bool Mutex::tryLock()
 //==============================================================================
 void Mutex::unlock()
 {
-	pthread_mutex_t* mtx = reinterpret_cast<pthread_mutex_t*>(&m_impl[0]);
+	pthread_mutex_t* mtx = reinterpret_cast<pthread_mutex_t*>(m_impl);
 
 	I err = pthread_mutex_unlock(mtx);
 	if(err)
@@ -192,21 +199,20 @@ void Mutex::unlock()
 //==============================================================================
 ConditionVariable::ConditionVariable()
 {
-	// Do some static assertions on the private data
-	static_assert(sizeof(m_impl) >= sizeof(pthread_cond_t),
-		"Incorrect impl size");
-
-	static_assert(ALIGNMENT >= alignof(pthread_cond_t),
-		"Incorrect impl alignment");
+	pthread_cond_t* cond = 
+		reinterpret_cast<pthread_cond_t*>(malloc(sizeof(pthread_cond_t)));
+	if(cond == nullptr)
+	{
+		throw ANKI_EXCEPTION("Out of memory");
+	}
 
-	pthread_cond_t* cond = reinterpret_cast<pthread_cond_t*>(&m_impl[0]);
+	m_impl = cond;
 
 	I err = pthread_cond_init(cond, nullptr);
 	if(err)
 	{
-#if ANKI_ASSERTIONS
-		std::memset(&m_impl[0], 0, sizeof(m_impl));
-#endif
+		free(m_impl);
+		m_impl = nullptr;
 		throw ANKI_EXCEPTION("pthread_cond_init() failed");
 	}
 }
@@ -214,31 +220,64 @@ ConditionVariable::ConditionVariable()
 //==============================================================================
 ConditionVariable::~ConditionVariable()
 {
-	pthread_cond_t* cond = reinterpret_cast<pthread_cond_t*>(&m_impl[0]);
+	pthread_cond_t* cond = reinterpret_cast<pthread_cond_t*>(m_impl);
 	pthread_cond_destroy(cond);
+
+	free(m_impl);
+	m_impl = nullptr;
 }
 
 //==============================================================================
 void ConditionVariable::notifyOne()
 {
-	pthread_cond_t* cond = reinterpret_cast<pthread_cond_t*>(&m_impl[0]);
+	pthread_cond_t* cond = reinterpret_cast<pthread_cond_t*>(m_impl);
 	pthread_cond_signal(cond);
 }
 
 //==============================================================================
 void ConditionVariable::notifyAll()
 {
-	pthread_cond_t* cond = reinterpret_cast<pthread_cond_t*>(&m_impl[0]);
+	pthread_cond_t* cond = reinterpret_cast<pthread_cond_t*>(m_impl);
 	pthread_cond_broadcast(cond);
 }
 
 //==============================================================================
-void ConditionVariable::wait(Mutex& amtx)
+Bool ConditionVariable::wait(Mutex& amtx, F64 timeoutSeconds)
 {
-	pthread_cond_t* cond = reinterpret_cast<pthread_cond_t*>(&m_impl[0]);
-	pthread_mutex_t* mtx = reinterpret_cast<pthread_mutex_t*>(&amtx.m_impl[0]);
+	pthread_cond_t* cond = reinterpret_cast<pthread_cond_t*>(m_impl);
+	pthread_mutex_t* mtx = reinterpret_cast<pthread_mutex_t*>(amtx.m_impl);
+
+	Bool timeout = false;
+	I err = 0;
+
+	if(timeoutSeconds == 0.0)
+	{
+		err = pthread_cond_wait(cond, mtx);
+	}
+	else
+	{
+		U64 ns = static_cast<U64>(timeoutSeconds * 1e+9);
+		struct timespec abstime;
+		abstime.tv_sec = ns / 1000000000;
+		abstime.tv_nsec = (ns % 1000000000);
+		err = pthread_cond_timedwait(cond, mtx, &abstime);
+	}
+
+	if(err == 0)
+	{
+		// Do nothing
+	}
+	else if(err == ETIMEDOUT)
+	{
+		timeout = true;
+	}
+	else
+	{
+		throw ANKI_EXCEPTION("pthread_cond_wait() or pthread_cond_timedwait()"
+			" failed: %d", err);
+	}
 
-	pthread_cond_wait(cond, mtx);
+	return timeout;
 }
 
 } // end namespace anki

+ 227 - 0
src/util/ThreadWindows.cpp

@@ -0,0 +1,227 @@
+// Copyright (C) 2014, Panagiotis Christopoulos Charitos.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include "anki/util/Thread.h"
+#include "anki/util/Exception.h"
+#include <windows.h>
+
+namespace anki {
+
+//==============================================================================
+// Thread                                                                      =
+//==============================================================================
+
+//==============================================================================
+static DWORD WINAPI threadCallback(LPVOID ud)
+{
+	ANKI_ASSERT(ud != nullptr);
+	Thread* thread = reinterpret_cast<Thread*>(ud);
+
+	// Set thread name
+	if(thread->_getName()[0] != '\0')
+	{
+		// TODO
+	}
+
+	// Call the callback
+	Thread::Info info;
+	info.m_userData = thread->_getUserData();
+	info.m_threadName = thread->_getName();
+
+	I err = thread->_getCallback()(info);
+
+	return err;
+}
+
+//==============================================================================
+Thread::Thread(const char* name)
+{
+	// Init the name
+	if(name)
+	{
+		U len = std::strlen(name);
+		len = std::min<U>(len, sizeof(m_name) - 1);
+		std::memcpy(&m_name[0], &name[0], len);
+		m_name[len] = '\0';
+	}
+	else
+	{
+		m_name[0] = '\0';
+	}
+}
+
+//==============================================================================
+Thread::~Thread()
+{
+	ANKI_ASSERT(!m_started && "Thread probably not joined");
+	free(m_impl);
+	m_impl = nullptr;
+}
+
+//==============================================================================
+void Thread::start(void* userData, Callback callback)
+{
+	ANKI_ASSERT(!m_started);
+	ANKI_ASSERT(callback != nullptr);
+
+	m_callback = callback;
+	m_userData = userData;
+
+	m_impl = CreateThread(nullptr, 0, threadCallback, this, 0, nullptr);
+	if(m_impl == nullptr)
+	{
+		throw ANKI_EXCEPTION("CreateThread() failed");
+	}
+	else
+	{
+#if ANKI_ASSERTIONS
+		m_started = true;
+#endif
+	}
+}
+
+//==============================================================================
+I Thread::join()
+{
+	ANKI_ASSERT(m_started);
+
+	// Wait thread
+	WaitForSingleObject(m_impl, INFINITE);
+	
+	// Get return code
+	DWORD exitCode = 0;
+	ok = GetExitCodeThread(m_impl, &exitCode);
+	if(!ok)
+	{
+		throw ANKI_EXCEPTION("GetExitCodeThread() failed");
+	}
+
+	// Delete handle
+	BOOL ok = CloseHandle(m_impl);
+	if(!ok)
+	{
+		throw ANKI_EXCEPTION("CloseHandle() failed");
+	}
+
+	return exitCode;
+}
+
+//==============================================================================
+Thread::Id Thread::getCurrentThreadId()
+{
+	HANDLE x = GetCurrentThread();
+	return x;
+}
+
+//==============================================================================
+// Mutex                                                                       =
+//==============================================================================
+
+//==============================================================================
+Mutex::Mutex()
+{
+	CRITICAL_SECTION* mtx = 
+		reinterpret_cast<CRITICAL_SECTION*>(malloc(sizeof(CRITICAL_SECTION)));
+	if(mtx == nullptr)
+	{
+		throw ANKI_EXCEPTION("Out of memory");
+	}
+
+	m_impl = mtx;
+
+	BOOL ok = InitializeCriticalSectionAndSpinCount(mtx, 0x400);
+	if(!ok)
+	{
+		free(m_impl);
+		m_impl = nullptr;
+		throw ANKI_EXCEPTION("InitializeCriticalSectionAndSpinCount() failed");
+	}
+}
+
+//==============================================================================
+Mutex::~Mutex()
+{
+	CRITICAL_SECTION* mtx = reinterpret_cast<CRITICAL_SECTION*>(m_impl);
+	DeleteCriticalSection(mtx);
+
+	free(m_impl);
+	m_impl = nullptr;
+}
+
+//==============================================================================
+void Mutex::lock()
+{
+	CRITICAL_SECTION* mtx = reinterpret_cast<CRITICAL_SECTION*>(m_impl);
+	EnterCriticalSection(mtx);
+}
+
+//==============================================================================
+Bool Mutex::tryLock()
+{
+	CRITICAL_SECTION* mtx = reinterpret_cast<CRITICAL_SECTION*>(m_impl);
+
+	BOOL enter = EnterCriticalSection(mtx);
+	return enter;
+}
+
+//==============================================================================
+void Mutex::unlock()
+{
+	CRITICAL_SECTION* mtx = reinterpret_cast<CRITICAL_SECTION*>(m_impl);
+
+	LeaveCriticalSection(mtx);
+}
+
+//==============================================================================
+// ConditionVariable                                                           =
+//==============================================================================
+
+//==============================================================================
+ConditionVariable::ConditionVariable()
+{
+	CONDITION_VARIABLE* cond = reinterpret_cast<CONDITION_VARIABLE*>(
+		malloc(sizeof(CONDITION_VARIABLE)));
+	if(cond == nullptr)
+	{
+		throw ANKI_EXCEPTION("Out of memory");
+	}
+
+	m_impl = cond;
+
+	InitializeConditionVariable(cond);
+}
+
+//==============================================================================
+ConditionVariable::~ConditionVariable()
+{
+	free(m_impl);
+	m_impl = nullptr;
+}
+
+//==============================================================================
+void ConditionVariable::notifyOne()
+{
+	CONDITION_VARIABLE* cond = reinterpret_cast<CONDITION_VARIABLE*>(m_impl);
+	WakeConditionVariable(cond);
+}
+
+//==============================================================================
+void ConditionVariable::notifyAll()
+{
+	CONDITION_VARIABLE* cond = reinterpret_cast<CONDITION_VARIABLE*>(m_impl);
+	WakeAllConditionVariable(cond);
+}
+
+//==============================================================================
+void ConditionVariable::wait(Mutex& amtx)
+{
+	CONDITION_VARIABLE* cond = reinterpret_cast<CONDITION_VARIABLE*>(m_impl);
+	CRITICAL_SECTION* mtx = reinterpret_cast<CRITICAL_SECTION*>(m_impl);
+
+	SleepConditionVariableCS(cond, INFINITE);
+}
+
+} // end namespace anki
+