Browse Source

Inline the threading classes for better performance and introduce a minimal Windows.h

Panagiotis Christopoulos Charitos 5 years ago
parent
commit
81c2e0b957

+ 1 - 1
.clang-format

@@ -29,7 +29,7 @@ BraceWrapping:
   AfterObjCDeclaration: true
   AfterStruct:     true
   AfterUnion:      true
-  AfterExternBlock: true
+  AfterExternBlock: false
   BeforeCatch:     true
   BeforeElse:      true
   IndentBraces:    false

+ 2 - 2
CMakeLists.txt

@@ -191,10 +191,10 @@ if(NOT MSVC)
 	set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LINKER_FLAGS}")
 else()
 	ProcessorCount(PC)
-	add_definitions("/MP${PC}")
+	#add_definitions("/MP${PC}")
 
 	if(${CMAKE_BUILD_TYPE} STREQUAL "Release" OR ${CMAKE_BUILD_TYPE} STREQUAL "RelWithDebInfo")
-		add_definitions("/Ox")
+		#add_definitions("/Ox")
 	endif()
 endif()
 

+ 8 - 9
src/anki/gr/vulkan/DescriptorSet.cpp

@@ -352,7 +352,7 @@ public:
 	VkDescriptorPoolCreateInfo m_poolCreateInf = {};
 
 	DynamicArray<DSThreadAllocator*> m_threadAllocs;
-	SpinLock m_threadAllocsMtx;
+	RWMutex m_threadAllocsMtx;
 
 	DSLayoutCacheEntry(DescriptorSetFactory* factory)
 		: m_factory(factory)
@@ -740,8 +740,6 @@ Error DSLayoutCacheEntry::getOrCreateThreadAllocator(ThreadId tid, DSThreadAlloc
 {
 	alloc = nullptr;
 
-	LockGuard<SpinLock> lock(m_threadAllocsMtx);
-
 	class Comp
 	{
 	public:
@@ -757,17 +755,18 @@ Error DSLayoutCacheEntry::getOrCreateThreadAllocator(ThreadId tid, DSThreadAlloc
 	};
 
 	// Find using binary search
-	auto it = binarySearch(m_threadAllocs.getBegin(), m_threadAllocs.getEnd(), tid, Comp());
-
-	if(it != m_threadAllocs.getEnd())
 	{
-		ANKI_ASSERT((*it)->m_tid == tid);
-		alloc = *it;
+		RLockGuard<RWMutex> lock(m_threadAllocsMtx);
+		auto it = binarySearch(m_threadAllocs.getBegin(), m_threadAllocs.getEnd(), tid, Comp());
+		alloc = (it != m_threadAllocs.getEnd()) ? *it : nullptr;
 	}
-	else
+
+	if(alloc == nullptr)
 	{
 		// Need to create one
 
+		WLockGuard<RWMutex> lock(m_threadAllocsMtx);
+
 		alloc = m_factory->m_alloc.newInstance<DSThreadAllocator>(this, tid);
 		ANKI_CHECK(alloc->init());
 

+ 4 - 7
src/anki/gr/vulkan/GrManagerImplSdl.cpp

@@ -9,13 +9,10 @@
 #include <anki/core/NativeWindowSdl.h>
 #include <SDL_syswm.h>
 #include <SDL_vulkan.h>
-#if ANKI_OS_LINUX
-#	include <X11/Xlib-xcb.h>
-#elif ANKI_OS_WINDOWS
-#	include <Winuser.h>
-#	include <anki/util/CleanupWindows.h>
-#else
-#	error Not supported
+
+// Because some idiot includes Windows.h
+#if defined(ERROR)
+#	undef ERROR
 #endif
 
 namespace anki

+ 1 - 0
src/anki/gr/vulkan/TextureImpl.h

@@ -52,6 +52,7 @@ public:
 		for(U32 idx : m_bindlessIndices)
 		{
 			ANKI_ASSERT(idx == MAX_U32 && "Forgot to unbind the bindless");
+			(void)idx;
 		}
 	}
 

+ 1 - 1
src/anki/input/Input.h

@@ -8,7 +8,7 @@
 #include <anki/Math.h>
 #include <anki/util/Singleton.h>
 #include <anki/util/Array.h>
-#include <anki/util/StdTypes.h>
+#include <anki/util/String.h>
 #include <anki/input/KeyCode.h>
 
 namespace anki

+ 0 - 1
src/anki/math/Common.h

@@ -8,4 +8,3 @@
 #include <anki/math/Simd.h>
 #include <anki/util/Array.h>
 #include <anki/util/Assert.h>
-#include <anki/util/String.h>

+ 2 - 6
src/anki/shader_compiler/ShaderProgramCompiler.cpp

@@ -216,10 +216,7 @@ static Error compileVariant(ConstWeakArray<MutatorValue> mutation,
 Error compileShaderProgram(CString fname,
 	ShaderProgramFilesystemInterface& fsystem,
 	GenericMemoryPoolAllocator<U8> tempAllocator,
-	U32 pushConstantsSize,
-	U32 backendMinor,
-	U32 backendMajor,
-	GpuVendor gpuVendor,
+	const GpuDeviceCapabilities& gpuCapabilities,
 	const BindlessLimits& bindlessLimits,
 	ShaderProgramBinaryWrapper& binaryW)
 {
@@ -233,8 +230,7 @@ Error compileShaderProgram(CString fname,
 	memcpy(&binary.m_magic[0], SHADER_BINARY_MAGIC, 8);
 
 	// Parse source
-	ShaderProgramParser parser(
-		fname, &fsystem, tempAllocator, pushConstantsSize, backendMinor, backendMajor, gpuVendor, bindlessLimits);
+	ShaderProgramParser parser(fname, &fsystem, tempAllocator, gpuCapabilities, bindlessLimits);
 	ANKI_CHECK(parser.parse());
 
 	// Mutators

+ 2 - 8
src/anki/shader_compiler/ShaderProgramCompiler.h

@@ -22,10 +22,7 @@ class ShaderProgramBinaryWrapper : public NonCopyable
 	friend Error compileShaderProgram(CString fname,
 		ShaderProgramFilesystemInterface& fsystem,
 		GenericMemoryPoolAllocator<U8> tempAllocator,
-		U32 pushConstantsSize,
-		U32 backendMinor,
-		U32 backendMajor,
-		GpuVendor gpuVendor,
+		const GpuDeviceCapabilities& gpuCapabilities,
 		const BindlessLimits& bindlessLimits,
 		ShaderProgramBinaryWrapper& binary);
 
@@ -62,10 +59,7 @@ private:
 ANKI_USE_RESULT Error compileShaderProgram(CString fname,
 	ShaderProgramFilesystemInterface& fsystem,
 	GenericMemoryPoolAllocator<U8> tempAllocator,
-	U32 pushConstantsSize,
-	U32 backendMinor,
-	U32 backendMajor,
-	GpuVendor gpuVendor,
+	const GpuDeviceCapabilities& gpuCapabilities,
 	const BindlessLimits& bindlessLimits,
 	ShaderProgramBinaryWrapper& binary);
 

+ 5 - 11
src/anki/shader_compiler/ShaderProgramParser.cpp

@@ -95,18 +95,12 @@ static const char* SHADER_HEADER = R"(#version 450 core
 ShaderProgramParser::ShaderProgramParser(CString fname,
 	ShaderProgramFilesystemInterface* fsystem,
 	GenericMemoryPoolAllocator<U8> alloc,
-	U32 pushConstantsSize,
-	U32 backendMinor,
-	U32 backendMajor,
-	GpuVendor gpuVendor,
+	const GpuDeviceCapabilities& gpuCapabilities,
 	const BindlessLimits& bindlessLimits)
 	: m_alloc(alloc)
 	, m_fname(alloc, fname)
 	, m_fsystem(fsystem)
-	, m_pushConstSize(pushConstantsSize)
-	, m_backendMinor(backendMinor)
-	, m_backendMajor(backendMajor)
-	, m_gpuVendor(gpuVendor)
+	, m_gpuCapabilities(gpuCapabilities)
 	, m_bindlessLimits(bindlessLimits)
 {
 }
@@ -704,9 +698,9 @@ Error ShaderProgramParser::generateVariant(
 	// Create the header
 	StringAuto header(m_alloc);
 	header.sprintf(SHADER_HEADER,
-		m_backendMinor,
-		m_backendMajor,
-		GPU_VENDOR_STR[m_gpuVendor].cstr(),
+		m_gpuCapabilities.m_minorApiVersion,
+		m_gpuCapabilities.m_majorApiVersion,
+		GPU_VENDOR_STR[m_gpuCapabilities.m_gpuVendor].cstr(),
 		m_bindlessLimits.m_bindlessTextureCount,
 		m_bindlessLimits.m_bindlessImageCount);
 

+ 2 - 9
src/anki/shader_compiler/ShaderProgramParser.h

@@ -91,10 +91,7 @@ public:
 	ShaderProgramParser(CString fname,
 		ShaderProgramFilesystemInterface* fsystem,
 		GenericMemoryPoolAllocator<U8> alloc,
-		U32 pushConstantsSize,
-		U32 backendMinor,
-		U32 backendMajor,
-		GpuVendor gpuVendor,
+		const GpuDeviceCapabilities& gpuCapabilities,
 		const BindlessLimits& bindlessLimits);
 
 	~ShaderProgramParser();
@@ -162,11 +159,7 @@ private:
 
 	ShaderTypeBit m_shaderTypes = ShaderTypeBit::NONE;
 	Bool m_insideShader = false;
-	const U32 m_pushConstSize = 0;
-	const U32 m_backendMinor = 1;
-	const U32 m_backendMajor = 1;
-	const GpuVendor m_gpuVendor = GpuVendor::AMD;
-
+	GpuDeviceCapabilities m_gpuCapabilities;
 	BindlessLimits m_bindlessLimits;
 
 	ANKI_USE_RESULT Error parseFile(CString fname, U32 depth);

+ 19 - 14
src/anki/util/Atomic.h

@@ -38,19 +38,19 @@ public:
 	{
 	}
 
-	Atomic(const Value& a)
+	Atomic(Value a)
 		: m_val(a)
 	{
 	}
 
 	/// Set the value without protection.
-	void setNonAtomically(const Value& a)
+	void setNonAtomically(Value a)
 	{
 		m_val = a;
 	}
 
 	/// Get the value without protection.
-	const Value& getNonAtomically() const
+	Value getNonAtomically() const
 	{
 		return m_val;
 	}
@@ -62,35 +62,35 @@ public:
 	}
 
 	/// Store
-	void store(const Value& a, AtomicMemoryOrder memOrd = MEMORY_ORDER)
+	void store(Value a, AtomicMemoryOrder memOrd = MEMORY_ORDER)
 	{
 		m_att.store(a, static_cast<std::memory_order>(memOrd));
 	}
 
 	/// Fetch and add.
 	template<typename Y>
-	Value fetchAdd(const Y& a, AtomicMemoryOrder memOrd = MEMORY_ORDER)
+	Value fetchAdd(Y a, AtomicMemoryOrder memOrd = MEMORY_ORDER)
 	{
 		return m_att.fetch_add(a, static_cast<std::memory_order>(memOrd));
 	}
 
 	/// Fetch and subtract.
 	template<typename Y>
-	Value fetchSub(const Y& a, AtomicMemoryOrder memOrd = MEMORY_ORDER)
+	Value fetchSub(Y a, AtomicMemoryOrder memOrd = MEMORY_ORDER)
 	{
 		return m_att.fetch_sub(a, static_cast<std::memory_order>(memOrd));
 	}
 
 	/// Fetch and do bitwise or.
 	template<typename Y>
-	Value fetchOr(const Y& a, AtomicMemoryOrder memOrd = MEMORY_ORDER)
+	Value fetchOr(Y a, AtomicMemoryOrder memOrd = MEMORY_ORDER)
 	{
 		return m_att.fetch_or(a, static_cast<std::memory_order>(memOrd));
 	}
 
 	/// Fetch and do bitwise and.
 	template<typename Y>
-	Value fetchAnd(const Y& a, AtomicMemoryOrder memOrd = MEMORY_ORDER)
+	Value fetchAnd(Y a, AtomicMemoryOrder memOrd = MEMORY_ORDER)
 	{
 		return m_att.fetch_and(a, static_cast<std::memory_order>(memOrd));
 	}
@@ -104,20 +104,25 @@ public:
 	/// 	return false;
 	/// }
 	/// @endcode
-	Bool compareExchange(Value& expected, const Value& desired, AtomicMemoryOrder memOrd = MEMORY_ORDER)
+	Bool compareExchange(Value& expected,
+		Value desired,
+		AtomicMemoryOrder successMemOrd = MEMORY_ORDER,
+		AtomicMemoryOrder failMemOrd = MEMORY_ORDER)
 	{
-		return m_att.compare_exchange_weak(
-			expected, desired, static_cast<std::memory_order>(memOrd), static_cast<std::memory_order>(memOrd));
+		return m_att.compare_exchange_weak(expected,
+			desired,
+			static_cast<std::memory_order>(successMemOrd),
+			static_cast<std::memory_order>(failMemOrd));
 	}
 
 	/// Set @a a to the atomic and return the previous value.
-	Value exchange(const Value& a, AtomicMemoryOrder memOrd = MEMORY_ORDER)
+	Value exchange(Value a, AtomicMemoryOrder memOrd = MEMORY_ORDER)
 	{
 		return m_att.exchange(a, static_cast<std::memory_order>(memOrd));
 	}
 
 	/// Store the minimum using compare-and-swap.
-	void min(const Value& a)
+	void min(Value a)
 	{
 		Value prev = load();
 		while(a < prev && !compareExchange(prev, a))
@@ -126,7 +131,7 @@ public:
 	}
 
 	/// Store the maximum using compare-and-swap.
-	void max(const Value& a)
+	void max(Value a)
 	{
 		Value prev = load();
 		while(a > prev && !compareExchange(prev, a))

+ 1 - 1
src/anki/util/CMakeLists.txt

@@ -4,7 +4,7 @@ set(SOURCES Assert.cpp Functions.cpp File.cpp Filesystem.cpp Memory.cpp System.c
 if(LINUX OR ANDROID OR MACOS)
 	set(SOURCES ${SOURCES} HighRezTimerPosix.cpp FilesystemPosix.cpp ThreadPosix.cpp ProcessPosix.cpp)
 else()
-	set(SOURCES ${SOURCES} HighRezTimerWindows.cpp FilesystemWindows.cpp ThreadWindows.cpp ProcessWindows.cpp)
+	set(SOURCES ${SOURCES} HighRezTimerWindows.cpp FilesystemWindows.cpp ThreadWindows.cpp ProcessWindows.cpp Win32Minimal.cpp)
 endif()
 
 if(LINUX)

+ 4 - 0
src/anki/util/CleanupWindows.h

@@ -18,6 +18,10 @@
 #		undef far
 #	endif
 
+#	ifdef FAR
+#		undef FAR
+#	endif
+
 #	ifdef ERROR
 #		undef ERROR
 #	endif

+ 8 - 13
src/anki/util/FilesystemWindows.cpp

@@ -6,28 +6,23 @@
 #include <anki/util/Filesystem.h>
 #include <anki/util/Assert.h>
 #include <anki/util/Logger.h>
-#include <windows.h>
-#include <Shlobj.h>
-#include <anki/util/CleanupWindows.h>
+#include <anki/util/Win32Minimal.h>
 
 namespace anki
 {
 
-#if !defined(MAX_PATH)
-static const U MAX_PATH = 1024 * 4;
-#endif
 static const U MAX_PATH_LEN = MAX_PATH - 1;
 
 Bool fileExists(const CString& filename)
 {
-	DWORD dwAttrib = GetFileAttributes(&filename[0]);
+	DWORD dwAttrib = GetFileAttributesA(&filename[0]);
 
 	return dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY);
 }
 
 Bool directoryExists(const CString& filename)
 {
-	DWORD dwAttrib = GetFileAttributes(filename.cstr());
+	DWORD dwAttrib = GetFileAttributesA(filename.cstr());
 
 	return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY);
 }
@@ -62,7 +57,7 @@ Error removeDirectory(const CString& dirname, GenericMemoryPoolAllocator<U8> all
 Error createDirectory(const CString& dir)
 {
 	Error err = Error::NONE;
-	if(CreateDirectory(dir.cstr(), NULL) == 0)
+	if(CreateDirectoryA(dir.cstr(), NULL) == 0)
 	{
 		ANKI_UTIL_LOGE("Failed to create directory %s", dir.cstr());
 		err = Error::FUNCTION_FAILED;
@@ -74,7 +69,7 @@ Error createDirectory(const CString& dir)
 Error getHomeDirectory(StringAuto& out)
 {
 	char path[MAX_PATH];
-	if(SHGetFolderPath(NULL, CSIDL_PROFILE, nullptr, 0, path) != S_OK)
+	if(SHGetFolderPathA(NULL, CSIDL_PROFILE, nullptr, 0, path) != S_OK)
 	{
 		ANKI_UTIL_LOGE("SHGetFolderPath() failed");
 		return Error::FUNCTION_FAILED;
@@ -110,9 +105,9 @@ static Error walkDirectoryTreeInternal(
 
 	// Find files
 	HANDLE handle = INVALID_HANDLE_VALUE;
-	WIN32_FIND_DATA find;
+	WIN32_FIND_DATAA find;
 
-	handle = FindFirstFile(&dir2[0], &find);
+	handle = FindFirstFileA(&dir2[0], &find);
 	if(handle == INVALID_HANDLE_VALUE)
 	{
 		ANKI_UTIL_LOGE("FindFirstFile() failed");
@@ -159,7 +154,7 @@ static Error walkDirectoryTreeInternal(
 
 			dir2[oldLen] = '\0';
 		}
-	} while(FindNextFile(handle, &find) != 0);
+	} while(FindNextFileA(handle, &find) != 0);
 
 	if(GetLastError() != ERROR_NO_MORE_FILES)
 	{

+ 1 - 1
src/anki/util/HighRezTimerWindows.cpp

@@ -5,8 +5,8 @@
 
 #include <anki/util/HighRezTimer.h>
 #include <anki/util/Assert.h>
+#include <anki/util/Win32Minimal.h>
 #include <cstdio>
-#include <Windows.h>
 
 namespace anki
 {

+ 1 - 2
src/anki/util/Logger.cpp

@@ -14,8 +14,7 @@
 #	include <android/log.h>
 #endif
 #if ANKI_OS_WINDOWS
-#	include <windows.h>
-#	include <anki/util/CleanupWindows.h>
+#	include <anki/util/Win32Minimal.h>
 #endif
 
 namespace anki

+ 1 - 1
src/anki/util/System.cpp

@@ -11,7 +11,7 @@
 #	include <unistd.h>
 #	include <signal.h>
 #elif ANKI_OS_WINDOWS
-#	include <windows.h>
+#	include <anki/util/Win32Minimal.h>
 #else
 #	error "Unimplemented"
 #endif

+ 441 - 70
src/anki/util/Thread.h

@@ -7,8 +7,17 @@
 
 #include <anki/util/StdTypes.h>
 #include <anki/util/Array.h>
-#include <anki/util/NonCopyable.h>
-#include <atomic>
+#include <anki/util/Atomic.h>
+#include <thread>
+#if ANKI_SIMD_SSE
+#	include <xmmintrin.h>
+#endif
+#if ANKI_POSIX
+#	include <pthread.h>
+#	include <semaphore.h>
+#else
+#	include <anki/util/Win32Minimal.h>
+#endif
 
 namespace anki
 {
@@ -17,9 +26,11 @@ namespace anki
 /// @{
 
 /// The thread ID.
+/// @memberof Thread
 using ThreadId = U64;
 
-/// It holds some information to be passed to the thread's callback
+/// It holds some information to be passed to the thread's callback.
+/// @memberof Thread
 class ThreadCallbackInfo
 {
 public:
@@ -27,18 +38,40 @@ public:
 	const char* m_threadName;
 };
 
-/// The type of the tread callback
+/// The type of the tread callback.
+/// @memberof Thread
 using ThreadCallback = Error (*)(ThreadCallbackInfo&);
 
-/// Thread implementation
-class Thread : public NonCopyable
+/// Thread implementation.
+class Thread
 {
 public:
 	/// Create a thread with or without a name
 	/// @param[in] name The name of the new thread. Can be nullptr.
-	Thread(const char* name);
+	Thread(const char* name)
+	{
+		if(name)
+		{
+			U len = std::strlen(name);
+			len = std::min<U>(len, sizeof(m_name) - 1);
+			memcpy(&m_name[0], &name[0], len);
+			m_name[len] = '\0';
+		}
+		else
+		{
+			m_name[0] = '\0';
+		}
+	}
+
+	Thread(const Thread&) = delete;
+
+	~Thread()
+	{
+		ANKI_ASSERT(!m_started && "Thread probably not joined");
+		m_handle = {};
+	}
 
-	~Thread();
+	Thread& operator=(const Thread&) = delete;
 
 	/// Start the thread.
 	/// @param userData The user data of the thread callback
@@ -51,102 +84,426 @@ public:
 	ANKI_USE_RESULT Error join();
 
 	/// Identify the current thread
-	static ThreadId getCurrentThreadId();
-
-anki_internal:
-	const char* getName() const
-	{
-		return &m_name[0];
-	}
-
-	void* getUserData() const
-	{
-		return m_userData;
-	}
-
-	ThreadCallback getCallback() const
+	static ThreadId getCurrentThreadId()
 	{
-		return m_callback;
+#if ANKI_POSIX
+		return pthread_self();
+#else
+		return GetCurrentThreadId();
+#endif
 	}
 
 private:
-	void* m_impl = nullptr; ///< The system native type
-	Array<char, 32> m_name; ///< The name of the thread
-	ThreadCallback m_callback = nullptr; ///< The callback
-	void* m_userData = nullptr; ///< The user date to pass to the callback
+	/// The system native type.
+#if ANKI_POSIX
+	pthread_t m_handle = {};
+#else
+	HANDLE m_handle = 0; ///< The user date to pass to the callback.
+	Error m_returnCode = Error::NONE;
+#endif
+	void* m_userData = nullptr; ///< The user date to pass to the callback.
+	Array<char, 32> m_name; ///< The name of the thread.
+	ThreadCallback m_callback = nullptr; ///< The callback.
 
 #if ANKI_EXTRA_CHECKS
 	Bool m_started = false;
 #endif
+
+#if ANKI_OS_WINDOWS
+	static DWORD ANKI_WINAPI threadCallback(LPVOID ud);
+#endif
 };
 
 /// Mutual exclusion primitive.
-class Mutex : public NonCopyable
+class Mutex
 {
 	friend class ConditionVariable;
 
 public:
-	Mutex();
+	Mutex()
+	{
+#if ANKI_POSIX
+		pthread_mutexattr_t attr;
+		pthread_mutexattr_init(&attr);
+		pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE);
+		pthread_mutex_init(&m_handle, &attr);
+		pthread_mutexattr_destroy(&attr);
+#else
+		InitializeCriticalSection(&m_handle);
+#endif
+	}
+
+	Mutex(const Mutex&) = delete;
+
+	~Mutex()
+	{
+#if ANKI_POSIX
+		pthread_mutex_destroy(&m_handle);
+#else
+		DeleteCriticalSection(&m_handle);
+#endif
+	}
 
-	~Mutex();
+	Mutex& operator=(const Mutex&) = delete;
 
 	/// Lock
-	void lock();
+	void lock()
+	{
+#if ANKI_POSIX
+		pthread_mutex_lock(&m_handle);
+#else
+		EnterCriticalSection(&m_handle);
+#endif
+	}
 
 	/// Try lock
 	/// @return True if it was locked successfully
-	Bool tryLock();
+	Bool tryLock()
+	{
+#if ANKI_POSIX
+		return pthread_mutex_trylock(&m_handle) == 0;
+#else
+		const BOOL enter = TryEnterCriticalSection(&m_handle);
+		return enter != 0;
+#endif
+	}
 
 	/// Unlock
-	void unlock();
+	void unlock()
+	{
+#if ANKI_POSIX
+		pthread_mutex_unlock(&m_handle);
+#else
+		LeaveCriticalSection(&m_handle);
+#endif
+	}
 
 private:
-	void* m_impl = nullptr; ///< The system native type
+	/// The system native type.
+#if ANKI_POSIX
+	pthread_mutex_t m_handle = {};
+#else
+	CRITICAL_SECTION m_handle = {};
+#endif
+};
+
+/// Read write mutex.
+class RWMutex
+{
+public:
+	RWMutex()
+	{
+#if ANKI_POSIX
+		pthread_rwlockattr_t attr;
+		pthread_rwlockattr_init(&attr);
+		pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE);
+		pthread_rwlock_init(&m_handle, &attr);
+		pthread_rwlockattr_destroy(&attr);
+#else
+		InitializeSRWLock(&m_handle);
+#endif
+	}
+
+	RWMutex(const RWMutex&) = delete;
+
+	~RWMutex()
+	{
+#if ANKI_POSIX
+		pthread_rwlock_destroy(&m_handle);
+#else
+		// Nothing
+#endif
+	}
+
+	RWMutex& operator=(const RWMutex&) = delete;
+
+	/// Lock for reading.
+	void lockRead()
+	{
+#if ANKI_POSIX
+		pthread_rwlock_rdlock(&m_handle);
+#else
+		AcquireSRWLockShared(&m_handle);
+#endif
+	}
+
+	/// Unlock from reading.
+	void unlockRead()
+	{
+#if ANKI_POSIX
+		pthread_rwlock_unlock(&m_handle);
+#else
+		ReleaseSRWLockShared(&m_handle);
+#endif
+	}
+
+	/// Lock for writing.
+	void lockWrite()
+	{
+#if ANKI_POSIX
+		pthread_rwlock_wrlock(&m_handle);
+#else
+		AcquireSRWLockExclusive(&m_handle);
+#endif
+	}
+
+	/// Unlock from writing.
+	void unlockWrite()
+	{
+#if ANKI_POSIX
+		pthread_rwlock_unlock(&m_handle);
+#else
+		ReleaseSRWLockExclusive(&m_handle);
+#endif
+	}
+
+private:
+#if ANKI_POSIX
+	pthread_rwlock_t m_handle;
+#else
+	SRWLOCK m_handle;
+#endif
 };
 
 /// Condition variable.
-class ConditionVariable : public NonCopyable
+class ConditionVariable
 {
 public:
-	ConditionVariable();
+	ConditionVariable()
+	{
+#if ANKI_POSIX
+		pthread_condattr_t attr;
+		pthread_condattr_init(&attr);
+		pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE);
+		pthread_cond_init(&m_handle, &attr);
+		pthread_condattr_destroy(&attr);
+#else
+		InitializeConditionVariable(&m_handle);
+#endif
+	}
+
+	ConditionVariable(const ConditionVariable&) = delete;
 
-	~ConditionVariable();
+	~ConditionVariable()
+	{
+#if ANKI_POSIX
+		pthread_cond_destroy(&m_handle);
+#else
+		// Nothing
+#endif
+	}
+
+	ConditionVariable& operator=(const ConditionVariable&) = delete;
 
 	/// Signal one thread
-	void notifyOne();
+	void notifyOne()
+	{
+#if ANKI_POSIX
+		pthread_cond_signal(&m_handle);
+#else
+		WakeConditionVariable(&m_handle);
+#endif
+	}
 
 	/// Signal all threads
-	void notifyAll();
+	void notifyAll()
+	{
+#if ANKI_POSIX
+		pthread_cond_broadcast(&m_handle);
+#else
+		WakeAllConditionVariable(&m_handle);
+#endif
+	}
 
 	/// Bock until signaled.
 	/// @param mtx The mutex.
-	void wait(Mutex& mtx);
+	void wait(Mutex& mtx)
+	{
+#if ANKI_POSIX
+		pthread_cond_wait(&m_handle, &mtx.m_handle);
+#else
+		SleepConditionVariableCS(&m_handle, &mtx.m_handle, MAX_U32);
+#endif
+	}
 
 private:
-	void* m_impl = nullptr; ///< The system native type
+#if ANKI_POSIX
+	pthread_cond_t m_handle;
+#else
+	CONDITION_VARIABLE m_handle;
+#endif
 };
 
 /// Mutual exclusion primitive. Like Mutex. It's better than Mutex only if the critical section will be executed in a
 /// very short period of time.
-class SpinLock : public NonCopyable
+class SpinLock
 {
 public:
-	/// Lock
+	SpinLock() = default;
+
+	SpinLock(const SpinLock&) = delete;
+
+	SpinLock& operator=(const SpinLock&) = delete;
+
+	/// Lock.
 	void lock()
 	{
-		while(m_lock.test_and_set(std::memory_order_acquire))
+		for(U spinCount = 0; !tryLock(); ++spinCount)
 		{
+			if(spinCount < 16)
+			{
+#if ANKI_SIMD_SSE
+				_mm_pause();
+#endif
+			}
+			else
+			{
+				std::this_thread::yield();
+				spinCount = 0;
+			}
 		}
 	}
 
-	/// Unlock
+	/// Unlock.
 	void unlock()
 	{
-		m_lock.clear(std::memory_order_release);
+		m_lock.store(false, AtomicMemoryOrder::RELEASE);
+	}
+
+	/// Try to lock.
+	Bool tryLock()
+	{
+		return !m_lock.load(AtomicMemoryOrder::RELAXED) && !m_lock.exchange(true, AtomicMemoryOrder::ACQUIRE);
 	}
 
 private:
-	std::atomic_flag m_lock = ATOMIC_FLAG_INIT;
+	Atomic<Bool> m_lock = {false};
+};
+
+/// A barrier for thread synchronization. It works almost like boost::barrier.
+class Barrier
+{
+public:
+	Barrier(U32 count)
+	{
+#if ANKI_POSIX
+		pthread_barrierattr_t attr;
+		pthread_barrierattr_init(&attr);
+		pthread_barrierattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE);
+		pthread_barrier_init(&m_handle, &attr, count);
+		pthread_barrierattr_destroy(&attr);
+#else
+		InitializeCriticalSection(&m_mtx);
+		InitializeConditionVariable(&m_cvar);
+
+		m_threshold = count;
+		m_count = count;
+		m_generation = 0;
+#endif
+	}
+
+	Barrier(const Barrier&) = delete;
+
+	~Barrier()
+	{
+#if ANKI_POSIX
+		pthread_barrier_destroy(&m_handle);
+#else
+		DeleteCriticalSection(&m_mtx);
+#endif
+	}
+
+	Barrier& operator=(const Barrier&) = delete;
+
+	/// Wait until all threads call wait().
+	void wait()
+	{
+#if ANKI_POSIX
+		pthread_barrier_wait(&m_handle);
+#else
+		EnterCriticalSection(&m_mtx);
+		const U32 gen = m_generation;
+
+		if(--m_count == 0)
+		{
+			++m_generation;
+			m_count = m_threshold;
+			WakeAllConditionVariable(&m_cvar);
+		}
+		else
+		{
+			while(gen == m_generation)
+			{
+				SleepConditionVariableCS(&m_cvar, &m_mtx, MAX_U32);
+			}
+		}
+
+		LeaveCriticalSection(&m_mtx);
+#endif
+	}
+
+private:
+#if ANKI_POSIX
+	pthread_barrier_t m_handle;
+#else
+	CONDITION_VARIABLE m_cvar;
+	CRITICAL_SECTION m_mtx;
+	U32 m_threshold;
+	U32 m_count;
+	U32 m_generation;
+#endif
+};
+
+/// Semaphore for thread synchronization.
+class Semaphore
+{
+public:
+	Semaphore(I32 initialValue)
+	{
+#if ANKI_POSIX
+		sem_init(&m_handle, 0, initialValue);
+#else
+		ANKI_ASSERT(!"TODO");
+#endif
+	}
+
+	Semaphore(const Semaphore&) = delete;
+
+	~Semaphore()
+	{
+#if ANKI_POSIX
+		sem_destroy(&m_handle);
+#else
+		ANKI_ASSERT(!"TODO");
+#endif
+	}
+
+	Semaphore& operator=(const Semaphore&) = delete;
+
+	/// Same as sem_wait().
+	void wait()
+	{
+#if ANKI_POSIX
+		sem_wait(&m_handle);
+#else
+		ANKI_ASSERT(!"TODO");
+#endif
+	}
+
+	/// Same as sem_post().
+	void post()
+	{
+#if ANKI_POSIX
+		sem_post(&m_handle);
+#else
+		ANKI_ASSERT(!"TODO");
+#endif
+	}
+
+private:
+#if ANKI_POSIX
+	sem_t m_handle;
+#endif
 };
 
 /// Lock guard. When constructed it locks a TMutex and unlocks it when it gets destroyed.
@@ -185,38 +542,52 @@ private:
 	TMutex* m_mtx;
 };
 
-/// A barrier for thread synchronization. It works almost like boost::barrier.
-class Barrier : public NonCopyable
+/// Read/write lock guard. When constructed it locks a TMutex and unlocks it when it gets destroyed.
+/// @tparam TMutex Can be RWMutex.
+template<typename TMutex, Bool READER>
+class RWLockGuard
 {
 public:
-	Barrier(U32 count);
+	RWLockGuard(TMutex& mtx)
+		: m_mtx(&mtx)
+	{
+		if(READER)
+		{
+			m_mtx->lockRead();
+		}
+		else
+		{
+			m_mtx->lockWrite();
+		}
+	}
 
-	~Barrier();
+	RWLockGuard(const RWLockGuard& b) = delete;
 
-	/// Wait until all threads call wait().
-	Bool wait();
+	~RWLockGuard()
+	{
+		if(READER)
+		{
+			m_mtx->unlockRead();
+		}
+		else
+		{
+			m_mtx->unlockWrite();
+		}
+	}
+
+	RWLockGuard& operator=(const RWLockGuard& b) = delete;
 
 private:
-	void* m_impl = nullptr;
+	TMutex* m_mtx;
 };
 
-/// Semaphore for thread synchronization.
-class Semaphore : public NonCopyable
-{
-public:
-	Semaphore(I32 initialValue);
-
-	~Semaphore();
-
-	/// Same as sem_wait().
-	void wait();
-
-	/// Same as sem_post().
-	void post();
+/// Read lock guard.
+template<typename TMutex>
+using RLockGuard = RWLockGuard<TMutex, true>;
 
-private:
-	void* m_impl = nullptr;
-};
+/// Write lock guard.
+template<typename TMutex>
+using WLockGuard = RWLockGuard<TMutex, false>;
 /// @}
 
 } // end namespace anki

+ 28 - 305
src/anki/util/ThreadPosix.cpp

@@ -5,69 +5,19 @@
 
 #include <anki/util/Thread.h>
 #include <anki/util/Logger.h>
-#include <anki/util/Functions.h>
-#include <cstring>
-#include <algorithm>
-#include <pthread.h>
-#include <semaphore.h>
 
 namespace anki
 {
 
-static void* pthreadCallback(void* ud)
-{
-	ANKI_ASSERT(ud != nullptr);
-	Thread* thread = static_cast<Thread*>(ud);
-
-	// Set thread name
-	if(thread->getName()[0] != '\0')
-	{
-		pthread_setname_np(pthread_self(), &thread->getName()[0]);
-	}
-
-	// Call the callback
-	ThreadCallbackInfo info;
-	info.m_userData = thread->getUserData();
-	info.m_threadName = thread->getName();
-
-	Error err = thread->getCallback()(info);
-
-	return numberToPtr<void*>(err._getCode());
-}
-
-Thread::Thread(const char* name)
-{
-	m_impl = malloc(sizeof(pthread_t));
-	if(m_impl == nullptr)
-	{
-		ANKI_UTIL_LOGF("Out of memory");
-	}
-
-	// Init the name
-	if(name)
-	{
-		U len = std::strlen(name);
-		len = std::min<U>(len, sizeof(m_name) - 1);
-		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, ThreadCallback callback, I32 pinToCore)
 {
 	ANKI_ASSERT(!m_started);
 	ANKI_ASSERT(callback != nullptr);
+	m_callback = callback;
+	m_userData = userData;
+#if ANKI_EXTRA_CHECKS
+	m_started = true;
+#endif
 
 	pthread_attr_t attr;
 	cpu_set_t cpus;
@@ -80,32 +30,37 @@ void Thread::start(void* userData, ThreadCallback callback, I32 pinToCore)
 		pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpus);
 	}
 
-	pthread_t* thread = static_cast<pthread_t*>(m_impl);
+	auto pthreadCallback = [](void* ud) -> void* {
+		ANKI_ASSERT(ud != nullptr);
+		Thread* thread = static_cast<Thread*>(ud);
 
-	m_callback = callback;
-	m_userData = userData;
+		// Set thread name
+		if(thread->m_name[0] != '\0')
+		{
+			pthread_setname_np(pthread_self(), &thread->m_name[0]);
+		}
+
+		// Call the callback
+		ThreadCallbackInfo info;
+		info.m_userData = thread->m_userData;
+		info.m_threadName = &thread->m_name[0];
+
+		const Error err = thread->m_callback(info);
+		return numberToPtr<void*>(err._getCode());
+	};
 
-	I err = pthread_create(thread, &attr, pthreadCallback, this);
-	if(err)
+	if(ANKI_UNLIKELY(pthread_create(&m_handle, &attr, pthreadCallback, this)))
 	{
-		ANKI_UTIL_LOGF("pthread_create() failed: %d", err);
-	}
-	else
-	{
-#if ANKI_EXTRA_CHECKS
-		m_started = true;
-#endif
+		ANKI_UTIL_LOGF("pthread_create() failed");
 	}
+
+	pthread_attr_destroy(&attr);
 }
 
 Error Thread::join()
 {
-	ANKI_ASSERT(m_started);
-	pthread_t* thread = static_cast<pthread_t*>(m_impl);
-
 	void* out;
-	I err = pthread_join(*thread, &out);
-	if(err)
+	if(ANKI_UNLIKELY(pthread_join(m_handle, &out)))
 	{
 		ANKI_UTIL_LOGF("pthread_join() failed");
 	}
@@ -115,239 +70,7 @@ Error Thread::join()
 #endif
 
 	// Set return error code
-	Error code = Error(I32(ptrToNumber(out)));
-	return code;
-}
-
-ThreadId Thread::getCurrentThreadId()
-{
-	pthread_t pid = pthread_self();
-	return pid;
-}
-
-Mutex::Mutex()
-{
-	pthread_mutex_t* mtx = static_cast<pthread_mutex_t*>(malloc(sizeof(pthread_mutex_t)));
-	if(mtx == nullptr)
-	{
-		ANKI_UTIL_LOGF("Out of memory");
-	}
-
-	I err = pthread_mutex_init(mtx, nullptr);
-	if(err)
-	{
-		free(mtx);
-		ANKI_UTIL_LOGF("pthread_mutex_init() failed");
-	}
-
-	m_impl = mtx;
-}
-
-Mutex::~Mutex()
-{
-	pthread_mutex_t* mtx = static_cast<pthread_mutex_t*>(m_impl);
-	pthread_mutex_destroy(mtx);
-
-	free(m_impl);
-	m_impl = nullptr;
-}
-
-void Mutex::lock()
-{
-	ANKI_ASSERT(m_impl);
-	pthread_mutex_t* mtx = static_cast<pthread_mutex_t*>(m_impl);
-
-	I err = pthread_mutex_lock(mtx);
-	if(err)
-	{
-		ANKI_UTIL_LOGF("pthread_mutex_lock() failed");
-	}
-}
-
-Bool Mutex::tryLock()
-{
-	ANKI_ASSERT(m_impl);
-	pthread_mutex_t* mtx = static_cast<pthread_mutex_t*>(m_impl);
-
-	I err = pthread_mutex_trylock(mtx);
-	return err == 0;
-}
-
-void Mutex::unlock()
-{
-	ANKI_ASSERT(m_impl);
-	pthread_mutex_t* mtx = static_cast<pthread_mutex_t*>(m_impl);
-
-	I err = pthread_mutex_unlock(mtx);
-	if(err)
-	{
-		ANKI_UTIL_LOGF("pthread_mutex_unlock() failed");
-	}
-}
-
-ConditionVariable::ConditionVariable()
-{
-	pthread_cond_t* cond = static_cast<pthread_cond_t*>(malloc(sizeof(pthread_cond_t)));
-	if(cond == nullptr)
-	{
-		ANKI_UTIL_LOGF("Out of memory");
-	}
-
-	I err = pthread_cond_init(cond, nullptr);
-	if(err)
-	{
-		free(cond);
-		ANKI_UTIL_LOGF("pthread_cond_init() failed");
-	}
-
-	m_impl = cond;
-}
-
-ConditionVariable::~ConditionVariable()
-{
-	pthread_cond_t* cond = static_cast<pthread_cond_t*>(m_impl);
-	pthread_cond_destroy(cond);
-
-	free(m_impl);
-	m_impl = nullptr;
-}
-
-void ConditionVariable::notifyOne()
-{
-	ANKI_ASSERT(m_impl);
-	pthread_cond_t* cond = static_cast<pthread_cond_t*>(m_impl);
-	pthread_cond_signal(cond);
-}
-
-void ConditionVariable::notifyAll()
-{
-	ANKI_ASSERT(m_impl);
-	pthread_cond_t* cond = static_cast<pthread_cond_t*>(m_impl);
-	pthread_cond_broadcast(cond);
-}
-
-void ConditionVariable::wait(Mutex& amtx)
-{
-	ANKI_ASSERT(m_impl);
-	ANKI_ASSERT(amtx.m_impl);
-	pthread_cond_t* cond = static_cast<pthread_cond_t*>(m_impl);
-	pthread_mutex_t* mtx = static_cast<pthread_mutex_t*>(amtx.m_impl);
-
-	I err = pthread_cond_wait(cond, mtx);
-	if(err)
-	{
-		ANKI_UTIL_LOGF("pthread_cond_wait() failed");
-	}
-}
-
-#define ANKI_BARR_GET() (*static_cast<pthread_barrier_t*>(this->m_impl))
-
-Barrier::Barrier(U32 count)
-{
-	ANKI_ASSERT(count > 1);
-
-	m_impl = static_cast<pthread_barrier_t*>(malloc(sizeof(pthread_barrier_t)));
-	if(m_impl == nullptr)
-	{
-		ANKI_UTIL_LOGF("Out of memory");
-	}
-
-	pthread_barrierattr_t attr;
-	I err = pthread_barrierattr_init(&attr);
-	if(err)
-	{
-		ANKI_UTIL_LOGF("pthread_barrierattr_init() failed");
-	}
-
-	err = pthread_barrierattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE);
-	if(err)
-	{
-		pthread_barrierattr_destroy(&attr);
-		ANKI_UTIL_LOGF("pthread_barrierattr_setpshared() failed");
-	}
-
-	err = pthread_barrier_init(&ANKI_BARR_GET(), &attr, count);
-	if(err)
-	{
-		pthread_barrierattr_destroy(&attr);
-		ANKI_UTIL_LOGF("pthread_barrier_init() failed");
-	}
-
-	pthread_barrierattr_destroy(&attr);
-}
-
-Barrier::~Barrier()
-{
-	if(m_impl)
-	{
-		I err = pthread_barrier_destroy(&ANKI_BARR_GET());
-		if(err)
-		{
-			ANKI_UTIL_LOGE("pthread_barrier_destroy() failed");
-		}
-
-		free(m_impl);
-		m_impl = nullptr;
-	}
-}
-
-Bool Barrier::wait()
-{
-	I err = pthread_barrier_wait(&ANKI_BARR_GET());
-	if(ANKI_UNLIKELY(err != PTHREAD_BARRIER_SERIAL_THREAD && err != 0))
-	{
-		ANKI_UTIL_LOGF("pthread_barrier_wait() failed");
-	}
-
-	return true;
-}
-
-Semaphore::Semaphore(I32 val)
-{
-	sem_t* sem = static_cast<sem_t*>(malloc(sizeof(sem_t)));
-	m_impl = sem;
-	if(m_impl == nullptr)
-	{
-		ANKI_UTIL_LOGF("Out of memory");
-	}
-
-	if(sem_init(sem, 0, val))
-	{
-		ANKI_UTIL_LOGF("sem_init() failed");
-	}
-}
-
-Semaphore::~Semaphore()
-{
-	if(m_impl)
-	{
-		sem_t* sem = static_cast<sem_t*>(m_impl);
-		if(sem_destroy(sem))
-		{
-			ANKI_UTIL_LOGE("sem_destroy() failed");
-		}
-
-		free(m_impl);
-		m_impl = nullptr;
-	}
-}
-
-void Semaphore::wait()
-{
-	ANKI_ASSERT(m_impl);
-	if(ANKI_UNLIKELY(sem_wait(static_cast<sem_t*>(m_impl))))
-	{
-		ANKI_UTIL_LOGF("sem_wait() failed");
-	}
-}
-
-void Semaphore::post()
-{
-	ANKI_ASSERT(m_impl);
-	if(ANKI_UNLIKELY(sem_post(static_cast<sem_t*>(m_impl))))
-	{
-		ANKI_UTIL_LOGF("sem_post() failed");
-	}
+	return Error(I32(ptrToNumber(out)));
 }
 
 } // end namespace anki

+ 22 - 203
src/anki/util/ThreadWindows.cpp

@@ -7,77 +7,55 @@
 #define _WIN32_WINNT _WIN32_WINNT_VISTA
 
 #include <anki/util/Thread.h>
-#include <anki/util/Functions.h>
 #include <anki/util/Logger.h>
-#include <cstring>
-#include <Windows.h>
-#include <WinBase.h>
 
 namespace anki
 {
 
-static DWORD WINAPI threadCallback(LPVOID ud)
+DWORD ANKI_WINAPI Thread::threadCallback(LPVOID ud)
 {
 	ANKI_ASSERT(ud != nullptr);
 	Thread* thread = reinterpret_cast<Thread*>(ud);
 
 	// Set thread name
-	if(thread->getName()[0] != '\0')
+	if(thread->m_name[0] != '\0')
 	{
 		// TODO
 	}
 
 	// Call the callback
 	ThreadCallbackInfo info;
-	info.m_userData = thread->getUserData();
-	info.m_threadName = thread->getName();
+	info.m_userData = thread->m_userData;
+	info.m_threadName = &thread->m_name[0];
 
-	Error err = thread->getCallback()(info);
+	thread->m_returnCode = thread->m_callback(info);
 
-	return err._getCode();
-}
-
-Thread::Thread(const char* name)
-{
-	// Init the name
-	if(name)
-	{
-		U len = std::strlen(name);
-		len = min<U>(len, sizeof(m_name) - 1);
-		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");
-	ANKI_ASSERT(m_impl == nullptr);
-	m_impl = nullptr;
+	return thread->m_returnCode._getCode();
 }
 
 void Thread::start(void* userData, ThreadCallback callback, I32 pinToCore)
 {
 	ANKI_ASSERT(!m_started);
 	ANKI_ASSERT(callback != nullptr);
-
 	m_callback = callback;
 	m_userData = userData;
+#if ANKI_EXTRA_CHECKS
+	m_started = true;
+#endif
+	m_returnCode = Error::NONE;
 
-	m_impl = CreateThread(nullptr, 0, threadCallback, this, 0, nullptr);
-	if(m_impl == nullptr)
+	m_handle = CreateThread(nullptr, 0, threadCallback, this, 0, nullptr);
+	if(m_handle == nullptr)
 	{
 		ANKI_UTIL_LOGF("CreateThread() failed");
 	}
-	else
+
+	if(pinToCore >= 0)
 	{
-#if ANKI_EXTRA_CHECKS
-		m_started = true;
-#endif
+		if(SetThreadAffinityMask(m_handle, DWORD_PTR(1) << DWORD_PTR(pinToCore)) == 0)
+		{
+			ANKI_UTIL_LOGF("SetThreadAffinityMask() failed");
+		}
 	}
 }
 
@@ -86,181 +64,22 @@ Error Thread::join()
 	ANKI_ASSERT(m_started);
 
 	// Wait thread
-	WaitForSingleObject(m_impl, INFINITE);
-
-	// Get return code
-	DWORD exitCode = 0;
-	BOOL ok = GetExitCodeThread(m_impl, &exitCode);
-	if(!ok)
-	{
-		ANKI_UTIL_LOGF("GetExitCodeThread() failed");
-	}
+	WaitForSingleObject(m_handle, INFINITE);
 
 	// Delete handle
-	ok = CloseHandle(m_impl);
+	const BOOL ok = CloseHandle(m_handle);
 	if(!ok)
 	{
 		ANKI_UTIL_LOGF("CloseHandle() failed");
 	}
 
-	m_impl = nullptr;
+	m_handle = nullptr;
 
 #if ANKI_EXTRA_CHECKS
 	m_started = false;
 #endif
 
-	return static_cast<Error>(exitCode);
-}
-
-ThreadId Thread::getCurrentThreadId()
-{
-	DWORD x = GetCurrentThreadId();
-	return PtrSize(x);
-}
-
-Mutex::Mutex()
-{
-	CRITICAL_SECTION* mtx = reinterpret_cast<CRITICAL_SECTION*>(malloc(sizeof(CRITICAL_SECTION)));
-	if(mtx == nullptr)
-	{
-		ANKI_UTIL_LOGF("Out of memory");
-	}
-
-	m_impl = mtx;
-
-	InitializeCriticalSection(mtx);
-}
-
-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 = TryEnterCriticalSection(mtx);
-	return enter;
-}
-
-void Mutex::unlock()
-{
-	CRITICAL_SECTION* mtx = reinterpret_cast<CRITICAL_SECTION*>(m_impl);
-	LeaveCriticalSection(mtx);
-}
-
-ConditionVariable::ConditionVariable()
-{
-	CONDITION_VARIABLE* cond = reinterpret_cast<CONDITION_VARIABLE*>(malloc(sizeof(CONDITION_VARIABLE)));
-	if(cond == nullptr)
-	{
-		ANKI_UTIL_LOGF("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*>(amtx.m_impl);
-
-	SleepConditionVariableCS(cond, mtx, INFINITE);
-}
-
-struct BarrierImpl
-{
-	CONDITION_VARIABLE m_cvar;
-	CRITICAL_SECTION m_mtx;
-	U32 m_threshold;
-	U32 m_count;
-	U32 m_generation;
-};
-
-Barrier::Barrier(U32 count)
-{
-	ANKI_ASSERT(count > 1);
-
-	BarrierImpl* barrier = reinterpret_cast<BarrierImpl*>(malloc(sizeof(BarrierImpl)));
-	if(barrier == nullptr)
-	{
-		ANKI_UTIL_LOGF("Out of memory");
-	}
-
-	InitializeCriticalSection(&barrier->m_mtx);
-	InitializeConditionVariable(&barrier->m_cvar);
-
-	barrier->m_threshold = count;
-	barrier->m_count = count;
-	barrier->m_generation = 0;
-
-	m_impl = barrier;
-}
-
-Barrier::~Barrier()
-{
-	ANKI_ASSERT(m_impl);
-	BarrierImpl* barrier = reinterpret_cast<BarrierImpl*>(m_impl);
-
-	DeleteCriticalSection(&barrier->m_mtx);
-	free(barrier);
-	m_impl = nullptr;
-}
-
-Bool Barrier::wait()
-{
-	ANKI_ASSERT(m_impl);
-	BarrierImpl& barrier = *reinterpret_cast<BarrierImpl*>(m_impl);
-
-	EnterCriticalSection(&barrier.m_mtx);
-	U32 gen = barrier.m_generation;
-
-	if(--barrier.m_count == 0)
-	{
-		++barrier.m_generation;
-		barrier.m_count = barrier.m_threshold;
-		WakeAllConditionVariable(&barrier.m_cvar);
-		LeaveCriticalSection(&barrier.m_mtx);
-		return true;
-	}
-
-	while(gen == barrier.m_generation)
-	{
-		SleepConditionVariableCS(&barrier.m_cvar, &barrier.m_mtx, INFINITE);
-	}
-
-	LeaveCriticalSection(&barrier.m_mtx);
-	return false;
+	return m_returnCode;
 }
 
 } // end namespace anki

+ 32 - 0
src/anki/util/Win32Minimal.cpp

@@ -0,0 +1,32 @@
+// Copyright (C) 2009-2020, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+/// @file 
+/// This file is mainly used to test that Windows.h works with our own minimal version of it.
+
+#include "Win32Minimal.h"
+#include <Windows.h>
+
+#define ANKI_T_STRUCT(type) \
+	static_assert(sizeof(::type) == sizeof(anki::type), "Sizeof mismatch of the Windows.h type and AnKi's fake one"); \
+	static_assert( \
+		alignof(::type) == alignof(anki::type), "Alignment mismatch of the Windows.h type and AnKi's fake one");
+
+#define ANKI_T_OFFSETOF(type, member) \
+	static_assert(offsetof(::type, member) == offsetof(anki::type, member), "See file");
+
+ANKI_T_STRUCT(CRITICAL_SECTION)
+ANKI_T_STRUCT(SRWLOCK)
+ANKI_T_STRUCT(CONDITION_VARIABLE)
+ANKI_T_STRUCT(SHFILEOPSTRUCTA)
+ANKI_T_STRUCT(SECURITY_ATTRIBUTES)
+ANKI_T_STRUCT(WIN32_FIND_DATAA)
+ANKI_T_OFFSETOF(WIN32_FIND_DATAA, cAlternateFileName)
+ANKI_T_STRUCT(LARGE_INTEGER)
+ANKI_T_STRUCT(COORD)
+ANKI_T_STRUCT(CONSOLE_SCREEN_BUFFER_INFO)
+ANKI_T_STRUCT(SYSTEM_INFO)
+ANKI_T_STRUCT(FILETIME)
+ANKI_T_STRUCT(SMALL_RECT)

+ 438 - 0
src/anki/util/Win32Minimal.h

@@ -0,0 +1,438 @@
+// Copyright (C) 2009-2020, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+/// @file
+/// This file includes minimal declarations of Win32 API to avoid Windows.h polution.
+
+#pragma once
+
+// Minimal Windows.h declaration. Only what's needed.
+extern "C" {
+
+#define ANKI_WINAPI __stdcall
+#define ANKI_WINBASEAPI __declspec(dllimport)
+#define ANKI_DECLARE_HANDLE(name) \
+	struct name##__; \
+	typedef struct name##__* name
+
+// Types
+typedef void VOID;
+typedef int BOOL;
+typedef char CHAR;
+typedef short SHORT;
+typedef int INT;
+typedef long LONG;
+typedef unsigned char UCHAR;
+typedef unsigned short USHORT;
+typedef unsigned int UINT;
+typedef unsigned long ULONG;
+typedef unsigned char BYTE;
+typedef unsigned short WORD;
+typedef unsigned long DWORD;
+typedef unsigned __int64 ULONG_PTR;
+typedef __int64 LONG_PTR;
+typedef __int64 LONGLONG;
+typedef ULONG_PTR SIZE_T;
+typedef LONG HRESULT;
+typedef BYTE BOOLEAN;
+typedef ULONG_PTR DWORD_PTR;
+typedef DWORD* LPDWORD;
+typedef void* HANDLE;
+typedef void* PVOID;
+typedef void* LPVOID;
+typedef const CHAR *LPCSTR, *PCSTR;
+typedef const CHAR* PCZZSTR;
+typedef CHAR* LPSTR;
+ANKI_DECLARE_HANDLE(HWND);
+
+typedef struct _SECURITY_ATTRIBUTES SECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;
+typedef DWORD(ANKI_WINAPI* PTHREAD_START_ROUTINE)(LPVOID lpThreadParameter);
+typedef PTHREAD_START_ROUTINE LPTHREAD_START_ROUTINE;
+typedef struct _RTL_CRITICAL_SECTION RTL_CRITICAL_SECTION, CRITICAL_SECTION, *LPCRITICAL_SECTION, *PCRITICAL_SECTION;
+typedef struct _RTL_SRWLOCK RTL_SRWLOCK, *PSRWLOCK;
+typedef struct _RTL_CONDITION_VARIABLE RTL_CONDITION_VARIABLE, *PCONDITION_VARIABLE;
+
+typedef struct _SHFILEOPSTRUCTA* LPSHFILEOPSTRUCTA;
+typedef struct _WIN32_FIND_DATAA WIN32_FIND_DATAA, *PWIN32_FIND_DATAA, *LPWIN32_FIND_DATAA;
+
+typedef union _LARGE_INTEGER LARGE_INTEGER;
+
+typedef struct _CONSOLE_SCREEN_BUFFER_INFO CONSOLE_SCREEN_BUFFER_INFO, *PCONSOLE_SCREEN_BUFFER_INFO;
+
+typedef struct _SYSTEM_INFO SYSTEM_INFO, *LPSYSTEM_INFO;
+
+// Thread & locks
+ANKI_WINBASEAPI HANDLE ANKI_WINAPI CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,
+	SIZE_T dwStackSize,
+	LPTHREAD_START_ROUTINE lpStartAddress,
+	LPVOID lpParameter,
+	DWORD dwCreationFlags,
+	LPDWORD lpThreadId);
+ANKI_WINBASEAPI DWORD_PTR ANKI_WINAPI SetThreadAffinityMask(HANDLE hThread, DWORD_PTR dwThreadAffinityMask);
+ANKI_WINBASEAPI DWORD ANKI_WINAPI WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);
+ANKI_WINBASEAPI BOOL ANKI_WINAPI CloseHandle(HANDLE hObject);
+ANKI_WINBASEAPI DWORD ANKI_WINAPI GetCurrentThreadId(VOID);
+
+ANKI_WINBASEAPI VOID ANKI_WINAPI InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
+ANKI_WINBASEAPI VOID ANKI_WINAPI EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
+ANKI_WINBASEAPI BOOL ANKI_WINAPI TryEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
+ANKI_WINBASEAPI VOID ANKI_WINAPI LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
+ANKI_WINBASEAPI VOID ANKI_WINAPI DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
+
+ANKI_WINBASEAPI VOID ANKI_WINAPI InitializeSRWLock(PSRWLOCK SRWLock);
+ANKI_WINBASEAPI VOID ANKI_WINAPI AcquireSRWLockExclusive(PSRWLOCK SRWLock);
+ANKI_WINBASEAPI VOID ANKI_WINAPI AcquireSRWLockShared(PSRWLOCK SRWLock);
+ANKI_WINBASEAPI VOID ANKI_WINAPI ReleaseSRWLockExclusive(PSRWLOCK SRWLock);
+ANKI_WINBASEAPI VOID ANKI_WINAPI ReleaseSRWLockShared(PSRWLOCK SRWLock);
+ANKI_WINBASEAPI BOOLEAN ANKI_WINAPI TryAcquireSRWLockExclusive(PSRWLOCK SRWLock);
+ANKI_WINBASEAPI BOOLEAN ANKI_WINAPI TryAcquireSRWLockShared(PSRWLOCK SRWLock);
+
+ANKI_WINBASEAPI VOID ANKI_WINAPI InitializeConditionVariable(PCONDITION_VARIABLE ConditionVariable);
+ANKI_WINBASEAPI BOOL ANKI_WINAPI SleepConditionVariableCS(
+	PCONDITION_VARIABLE ConditionVariable, PCRITICAL_SECTION CriticalSection, DWORD dwMilliseconds);
+ANKI_WINBASEAPI VOID ANKI_WINAPI WakeAllConditionVariable(PCONDITION_VARIABLE ConditionVariable);
+ANKI_WINBASEAPI VOID ANKI_WINAPI WakeConditionVariable(PCONDITION_VARIABLE ConditionVariable);
+
+// Filesystem
+ANKI_WINBASEAPI DWORD ANKI_WINAPI GetFileAttributesA(LPCSTR lpFileName);
+ANKI_WINBASEAPI int ANKI_WINAPI SHFileOperationA(LPSHFILEOPSTRUCTA lpFileOp);
+ANKI_WINBASEAPI BOOL ANKI_WINAPI CreateDirectoryA(LPCSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes);
+ANKI_WINBASEAPI HRESULT ANKI_WINAPI SHGetFolderPathA(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPSTR pszPath);
+ANKI_WINBASEAPI HANDLE ANKI_WINAPI FindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATAA lpFindFileData);
+ANKI_WINBASEAPI BOOL ANKI_WINAPI FindClose(HANDLE hFindFile);
+ANKI_WINBASEAPI BOOL ANKI_WINAPI FindNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData);
+
+// Other
+ANKI_WINBASEAPI DWORD ANKI_WINAPI GetLastError(VOID);
+ANKI_WINBASEAPI BOOL ANKI_WINAPI QueryPerformanceFrequency(LARGE_INTEGER* lpFrequency);
+ANKI_WINBASEAPI BOOL ANKI_WINAPI QueryPerformanceCounter(LARGE_INTEGER* lpPerformanceCount);
+ANKI_WINBASEAPI VOID ANKI_WINAPI Sleep(DWORD dwMilliseconds);
+ANKI_WINBASEAPI HANDLE ANKI_WINAPI GetStdHandle(DWORD nStdHandle);
+ANKI_WINBASEAPI BOOL ANKI_WINAPI GetConsoleScreenBufferInfo(
+	HANDLE hConsoleOutput, PCONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);
+ANKI_WINBASEAPI BOOL ANKI_WINAPI SetConsoleTextAttribute(HANDLE hConsoleOutput, WORD wAttributes);
+ANKI_WINBASEAPI VOID ANKI_WINAPI GetSystemInfo(LPSYSTEM_INFO lpSystemInfo);
+
+#undef ANKI_WINBASEAPI
+#undef ANKI_DECLARE_HANDLE
+
+} // end extern "C"
+
+// AnKi's wrappers for various Windows.h structs. Required because we can't declare those structs in the global
+// namespace since there are files that might include this header and Windows.h at the same time.
+namespace anki
+{
+
+// Consts
+constexpr DWORD INVALID_FILE_ATTRIBUTES = (DWORD)-1;
+constexpr DWORD FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
+constexpr DWORD MAX_PATH = 260;
+static const HANDLE INVALID_HANDLE_VALUE = (HANDLE)(LONG_PTR)-1;
+constexpr DWORD ERROR_NO_MORE_FILES = 18L;
+constexpr WORD FO_DELETE = 0x0003;
+constexpr WORD FOF_NOCONFIRMATION = 0x0010;
+constexpr WORD FOF_NOERRORUI = 0x0400;
+constexpr WORD FOF_SILENT = 0x0004;
+constexpr WORD CSIDL_PROFILE = 0x0028;
+constexpr DWORD STD_OUTPUT_HANDLE = (DWORD)-11;
+constexpr HRESULT S_OK = 0;
+constexpr DWORD INFINITE = 0xFFFFFFFF;
+
+constexpr WORD FOREGROUND_BLUE = 0x0001;
+constexpr WORD FOREGROUND_GREEN = 0x0002;
+constexpr WORD FOREGROUND_RED = 0x0004;
+constexpr WORD FOREGROUND_INTENSITY = 0x0008;
+constexpr WORD BACKGROUND_BLUE = 0x0010;
+constexpr WORD BACKGROUND_GREEN = 0x0020;
+constexpr WORD BACKGROUND_RED = 0x0040;
+
+// Types
+typedef union _LARGE_INTEGER
+{
+	struct
+	{
+		ULONG LowPart;
+		LONG HighPart;
+	} DUMMYSTRUCTNAME;
+	struct
+	{
+		ULONG LowPart;
+		LONG HighPart;
+	} u;
+	LONGLONG QuadPart;
+} LARGE_INTEGER;
+
+typedef struct _SECURITY_ATTRIBUTES
+{
+	DWORD nLength;
+	LPVOID lpSecurityDescriptor;
+	BOOL bInheritHandle;
+} SECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;
+
+typedef struct _LIST_ENTRY
+{
+	struct _LIST_ENTRY* Flink;
+	struct _LIST_ENTRY* Blink;
+} LIST_ENTRY, *PLIST_ENTRY;
+
+typedef struct _RTL_CRITICAL_SECTION_DEBUG
+{
+	WORD Type;
+	WORD CreatorBackTraceIndex;
+	struct _RTL_CRITICAL_SECTION* CriticalSection;
+	LIST_ENTRY ProcessLocksList;
+	DWORD EntryCount;
+	DWORD ContentionCount;
+	DWORD Flags;
+	WORD CreatorBackTraceIndexHigh;
+	WORD SpareWORD;
+} RTL_CRITICAL_SECTION_DEBUG, *PRTL_CRITICAL_SECTION_DEBUG;
+
+#pragma pack(push, 8)
+typedef struct _RTL_CRITICAL_SECTION
+{
+	PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
+	LONG LockCount;
+	LONG RecursionCount;
+	HANDLE OwningThread;
+	HANDLE LockSemaphore;
+	ULONG_PTR SpinCount;
+} CRITICAL_SECTION, *LPCRITICAL_SECTION, *PCRITICAL_SECTION;
+#pragma pack(pop)
+
+typedef struct _RTL_SRWLOCK
+{
+	PVOID Ptr;
+} SRWLOCK, *PSRWLOCK;
+
+typedef struct _RTL_CONDITION_VARIABLE
+{
+	PVOID Ptr;
+} CONDITION_VARIABLE, *PRTL_CONDITION_VARIABLE, *PCONDITION_VARIABLE;
+
+typedef WORD FILEOP_FLAGS;
+
+typedef struct _SHFILEOPSTRUCTA
+{
+	HWND hwnd;
+	UINT wFunc;
+	PCZZSTR pFrom;
+	PCZZSTR pTo;
+	FILEOP_FLAGS fFlags;
+	BOOL fAnyOperationsAborted;
+	LPVOID hNameMappings;
+	PCSTR lpszProgressTitle;
+} SHFILEOPSTRUCTA, *LPSHFILEOPSTRUCTA;
+
+typedef struct _FILETIME
+{
+	DWORD dwLowDateTime;
+	DWORD dwHighDateTime;
+} FILETIME, *PFILETIME, *LPFILETIME;
+
+typedef struct _WIN32_FIND_DATAA
+{
+	DWORD dwFileAttributes;
+	FILETIME ftCreationTime;
+	FILETIME ftLastAccessTime;
+	FILETIME ftLastWriteTime;
+	DWORD nFileSizeHigh;
+	DWORD nFileSizeLow;
+	DWORD dwReserved0;
+	DWORD dwReserved1;
+	CHAR cFileName[MAX_PATH];
+	CHAR cAlternateFileName[14];
+} WIN32_FIND_DATAA, *PWIN32_FIND_DATAA, *LPWIN32_FIND_DATAA;
+
+typedef struct _COORD
+{
+	SHORT X;
+	SHORT Y;
+} COORD, *PCOORD;
+
+typedef struct _SMALL_RECT
+{
+	SHORT Left;
+	SHORT Top;
+	SHORT Right;
+	SHORT Bottom;
+} SMALL_RECT, *PSMALL_RECT;
+
+typedef struct _CONSOLE_SCREEN_BUFFER_INFO
+{
+	COORD dwSize;
+	COORD dwCursorPosition;
+	WORD wAttributes;
+	SMALL_RECT srWindow;
+	COORD dwMaximumWindowSize;
+} CONSOLE_SCREEN_BUFFER_INFO, *PCONSOLE_SCREEN_BUFFER_INFO;
+
+typedef struct _SYSTEM_INFO
+{
+	union
+	{
+		DWORD dwOemId; // Obsolete field...do not use
+		struct
+		{
+			WORD wProcessorArchitecture;
+			WORD wReserved;
+		} DUMMYSTRUCTNAME;
+	} DUMMYUNIONNAME;
+	DWORD dwPageSize;
+	LPVOID lpMinimumApplicationAddress;
+	LPVOID lpMaximumApplicationAddress;
+	DWORD_PTR dwActiveProcessorMask;
+	DWORD dwNumberOfProcessors;
+	DWORD dwProcessorType;
+	DWORD dwAllocationGranularity;
+	WORD wProcessorLevel;
+	WORD wProcessorRevision;
+} SYSTEM_INFO, *LPSYSTEM_INFO;
+
+// Critical section
+inline void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
+{
+	::InitializeCriticalSection(reinterpret_cast<::LPCRITICAL_SECTION>(lpCriticalSection));
+}
+
+inline void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
+{
+	::EnterCriticalSection(reinterpret_cast<::LPCRITICAL_SECTION>(lpCriticalSection));
+}
+
+inline BOOL TryEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
+{
+	return ::TryEnterCriticalSection(reinterpret_cast<::LPCRITICAL_SECTION>(lpCriticalSection));
+}
+
+inline void LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
+{
+	::LeaveCriticalSection(reinterpret_cast<::LPCRITICAL_SECTION>(lpCriticalSection));
+}
+
+inline void DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
+{
+	::DeleteCriticalSection(reinterpret_cast<::LPCRITICAL_SECTION>(lpCriticalSection));
+}
+
+// Shared lock
+inline void InitializeSRWLock(PSRWLOCK SRWLock)
+{
+	::InitializeSRWLock(reinterpret_cast<::PSRWLOCK>(SRWLock));
+}
+
+inline void AcquireSRWLockExclusive(PSRWLOCK SRWLock)
+{
+	::AcquireSRWLockExclusive(reinterpret_cast<::PSRWLOCK>(SRWLock));
+}
+
+inline void AcquireSRWLockShared(PSRWLOCK SRWLock)
+{
+	::AcquireSRWLockShared(reinterpret_cast<::PSRWLOCK>(SRWLock));
+}
+
+inline void ReleaseSRWLockExclusive(PSRWLOCK SRWLock)
+{
+	::ReleaseSRWLockExclusive(reinterpret_cast<::PSRWLOCK>(SRWLock));
+}
+
+inline void ReleaseSRWLockShared(PSRWLOCK SRWLock)
+{
+	::ReleaseSRWLockShared(reinterpret_cast<::PSRWLOCK>(SRWLock));
+}
+
+inline BOOL TryAcquireSRWLockExclusive(PSRWLOCK SRWLock)
+{
+	return ::TryAcquireSRWLockExclusive(reinterpret_cast<::PSRWLOCK>(SRWLock));
+}
+
+inline BOOL TryAcquireSRWLockShared(PSRWLOCK SRWLock)
+{
+	return ::TryAcquireSRWLockShared(reinterpret_cast<::PSRWLOCK>(SRWLock));
+}
+
+// Condition var
+inline void InitializeConditionVariable(PCONDITION_VARIABLE ConditionVariable)
+{
+	::InitializeConditionVariable(reinterpret_cast<::PCONDITION_VARIABLE>(ConditionVariable));
+}
+
+inline BOOL SleepConditionVariableCS(
+	PCONDITION_VARIABLE ConditionVariable, PCRITICAL_SECTION CriticalSection, DWORD dwMilliseconds)
+{
+	return ::SleepConditionVariableCS(reinterpret_cast<::PCONDITION_VARIABLE>(ConditionVariable),
+		reinterpret_cast<::PCRITICAL_SECTION>(CriticalSection),
+		dwMilliseconds);
+}
+
+inline void WakeAllConditionVariable(PCONDITION_VARIABLE ConditionVariable)
+{
+	::WakeAllConditionVariable(reinterpret_cast<::PCONDITION_VARIABLE>(ConditionVariable));
+}
+
+inline void WakeConditionVariable(PCONDITION_VARIABLE ConditionVariable)
+{
+	::WakeConditionVariable(reinterpret_cast<::PCONDITION_VARIABLE>(ConditionVariable));
+}
+
+// Filesystem
+inline DWORD GetFileAttributesA(LPCSTR lpFileName)
+{
+	return ::GetFileAttributesA(lpFileName);
+}
+
+inline int SHFileOperationA(LPSHFILEOPSTRUCTA lpFileOp)
+{
+	return ::SHFileOperationA(reinterpret_cast<::LPSHFILEOPSTRUCTA>(lpFileOp));
+}
+
+inline BOOL CreateDirectoryA(LPCSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes)
+{
+	return ::CreateDirectoryA(lpPathName, reinterpret_cast<::LPSECURITY_ATTRIBUTES>(lpSecurityAttributes));
+}
+
+inline HRESULT SHGetFolderPathA(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPSTR pszPath)
+{
+	return ::SHGetFolderPathA(hwnd, csidl, hToken, dwFlags, pszPath);
+}
+
+inline HANDLE FindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATAA lpFindFileData)
+{
+	return ::FindFirstFileA(lpFileName, reinterpret_cast<::LPWIN32_FIND_DATAA>(lpFindFileData));
+}
+
+inline BOOL FindClose(HANDLE hFindFile)
+{
+	return ::FindClose(hFindFile);
+}
+
+inline BOOL FindNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData)
+{
+	return ::FindNextFileA(hFindFile, reinterpret_cast<::LPWIN32_FIND_DATAA>(lpFindFileData));
+}
+
+// Other
+inline BOOL QueryPerformanceFrequency(LARGE_INTEGER* lpFrequency)
+{
+	return ::QueryPerformanceFrequency(reinterpret_cast<::LARGE_INTEGER*>(lpFrequency));
+}
+
+inline BOOL QueryPerformanceCounter(LARGE_INTEGER* lpPerformanceCount)
+{
+	return ::QueryPerformanceCounter(reinterpret_cast<::LARGE_INTEGER*>(lpPerformanceCount));
+}
+
+inline BOOL GetConsoleScreenBufferInfo(HANDLE hConsoleOutput, PCONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo)
+{
+	return ::GetConsoleScreenBufferInfo(
+		hConsoleOutput, reinterpret_cast<::PCONSOLE_SCREEN_BUFFER_INFO>(lpConsoleScreenBufferInfo));
+}
+
+inline VOID GetSystemInfo(LPSYSTEM_INFO lpSystemInfo)
+{
+	::GetSystemInfo(reinterpret_cast<::LPSYSTEM_INFO>(lpSystemInfo));
+}
+
+} // end namespace anki

+ 2 - 1
tests/shader_compiler/ShaderProgramCompiler.cpp

@@ -150,8 +150,9 @@ void main()
 	HeapAllocator<U8> alloc(allocAligned, nullptr);
 	ShaderProgramBinaryWrapper binary(alloc);
 	BindlessLimits bindlessLimits;
+	GpuDeviceCapabilities gpuCapabilities;
 	ANKI_TEST_EXPECT_NO_ERR(
-		compileShaderProgram("test.glslp", fsystem, alloc, 128, 1, 1, GpuVendor::AMD, bindlessLimits, binary));
+		compileShaderProgram("test.glslp", fsystem, alloc, gpuCapabilities, bindlessLimits, binary));
 
 #if 0
 	StringAuto dis(alloc);

+ 2 - 1
tests/shader_compiler/ShaderProgramParser.cpp

@@ -46,7 +46,8 @@ ANKI_TEST(ShaderCompiler, ShaderCompilerParser)
 	} interface;
 
 	BindlessLimits bindlessLimits;
-	ShaderProgramParser parser("filename0", &interface, alloc, 128, 1, 1, GpuVendor::AMD, bindlessLimits);
+	GpuDeviceCapabilities gpuCapabilities;
+	ShaderProgramParser parser("filename0", &interface, alloc, gpuCapabilities, bindlessLimits);
 	ANKI_TEST_EXPECT_NO_ERR(parser.parse());
 
 	// Test a variant