AkPlatformFuncs.h 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818
  1. /*******************************************************************************
  2. The content of this file includes portions of the AUDIOKINETIC Wwise Technology
  3. released in source code form as part of the SDK installer package.
  4. Commercial License Usage
  5. Licensees holding valid commercial licenses to the AUDIOKINETIC Wwise Technology
  6. may use this file in accordance with the end user license agreement provided
  7. with the software or, alternatively, in accordance with the terms contained in a
  8. written agreement between you and Audiokinetic Inc.
  9. Apache License Usage
  10. Alternatively, this file may be used under the Apache License, Version 2.0 (the
  11. "Apache License"); you may not use this file except in compliance with the
  12. Apache License. You may obtain a copy of the Apache License at
  13. http://www.apache.org/licenses/LICENSE-2.0.
  14. Unless required by applicable law or agreed to in writing, software distributed
  15. under the Apache License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
  16. OR CONDITIONS OF ANY KIND, either express or implied. See the Apache License for
  17. the specific language governing permissions and limitations under the License.
  18. Copyright (c) 2023 Audiokinetic Inc.
  19. *******************************************************************************/
  20. #ifndef _AK_PLATFORM_FUNCS_H_
  21. #define _AK_PLATFORM_FUNCS_H_
  22. #include "malloc.h"
  23. #include <AK/Tools/Common/AkAssert.h>
  24. #include <AK/SoundEngine/Common/AkAtomic.h>
  25. #include <windows.h>
  26. #include <stdio.h>
  27. #if defined(_WIN64)
  28. // on 64 bit, removes warning C4985: 'ceil': attributes not present on previous declaration.
  29. // see http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=294649
  30. #include <math.h>
  31. #endif // _WIN64
  32. #include <intrin.h>
  33. #if defined(AK_XBOXSERIESX)
  34. #include <ammintrin.h>
  35. #endif
  36. //-----------------------------------------------------------------------------
  37. // Platform-specific thread properties definition.
  38. //-----------------------------------------------------------------------------
  39. struct AkThreadProperties
  40. {
  41. int nPriority; ///< Thread priority
  42. #ifdef AK_WIN_UNIVERSAL_APP
  43. PROCESSOR_NUMBER processorNumber;///< Ideal processor (passed to SetThreadIdealProcessorEx)
  44. #else
  45. AkUInt32 dwAffinityMask; ///< Affinity mask
  46. #endif
  47. AkUInt32 uStackSize; ///< Thread stack size.
  48. };
  49. //-----------------------------------------------------------------------------
  50. // External variables.
  51. //-----------------------------------------------------------------------------
  52. // g_fFreqRatio is used by time helpers to return time values in milliseconds.
  53. // It is declared and updated by the sound engine.
  54. namespace AK
  55. {
  56. extern AkReal32 g_fFreqRatio;
  57. }
  58. //-----------------------------------------------------------------------------
  59. // Defines for Win32.
  60. //-----------------------------------------------------------------------------
  61. #define AK_DECLARE_THREAD_ROUTINE( FuncName ) DWORD WINAPI FuncName(LPVOID lpParameter)
  62. #define AK_THREAD_RETURN( _param_ ) return (_param_);
  63. #define AK_THREAD_ROUTINE_PARAMETER lpParameter
  64. #define AK_GET_THREAD_ROUTINE_PARAMETER_PTR(type) reinterpret_cast<type*>( AK_THREAD_ROUTINE_PARAMETER )
  65. #define AK_RETURN_THREAD_OK 0x00000000
  66. #define AK_RETURN_THREAD_ERROR 0x00000001
  67. #define AK_DEFAULT_STACK_SIZE (128*1024)
  68. #define AK_THREAD_PRIORITY_NORMAL THREAD_PRIORITY_NORMAL
  69. #define AK_THREAD_PRIORITY_ABOVE_NORMAL THREAD_PRIORITY_ABOVE_NORMAL
  70. #define AK_THREAD_PRIORITY_BELOW_NORMAL THREAD_PRIORITY_BELOW_NORMAL
  71. #define AK_THREAD_PRIORITY_TIME_CRITICAL THREAD_PRIORITY_TIME_CRITICAL
  72. #define AK_THREAD_MODE_BACKGROUND_BEGIN THREAD_MODE_BACKGROUND_BEGIN
  73. // NULL objects
  74. #define AK_NULL_THREAD NULL
  75. #define AK_INFINITE INFINITE
  76. #define AkMax(x1, x2) (((x1) > (x2))? (x1): (x2))
  77. #define AkMin(x1, x2) (((x1) < (x2))? (x1): (x2))
  78. #define AkClamp(x, min, max) ((x) < (min)) ? (min) : (((x) > (max) ? (max) : (x)))
  79. namespace AKPLATFORM
  80. {
  81. // Simple automatic event API
  82. // ------------------------------------------------------------------
  83. /// Platform Independent Helper
  84. inline void AkClearEvent( AkEvent & out_event )
  85. {
  86. out_event = NULL;
  87. }
  88. /// Platform Independent Helper
  89. inline AKRESULT AkCreateEvent( AkEvent & out_event )
  90. {
  91. #ifdef AK_USE_UWP_API
  92. out_event = CreateEventEx(nullptr, nullptr, 0, STANDARD_RIGHTS_ALL|EVENT_MODIFY_STATE);
  93. #else
  94. out_event = ::CreateEvent( NULL, // No security attributes
  95. false, // Reset type: automatic
  96. false, // Initial signaled state: not signaled
  97. NULL // No name
  98. );
  99. #endif
  100. return ( out_event ) ? AK_Success : AK_Fail;
  101. }
  102. /// Platform Independent Helper
  103. inline void AkDestroyEvent( AkEvent & io_event )
  104. {
  105. if ( io_event )
  106. ::CloseHandle( io_event );
  107. io_event = NULL;
  108. }
  109. /// Platform Independent Helper
  110. inline void AkWaitForEvent( AkEvent & in_event )
  111. {
  112. #ifdef AK_USE_UWP_API
  113. DWORD dwWaitResult = ::WaitForSingleObjectEx( in_event, INFINITE, FALSE );
  114. AKASSERT( dwWaitResult == WAIT_OBJECT_0 );
  115. #else
  116. AKVERIFY( ::WaitForSingleObject( in_event, INFINITE ) == WAIT_OBJECT_0 );
  117. #endif
  118. }
  119. /// Platform Independent Helper
  120. inline void AkSignalEvent( const AkEvent & in_event )
  121. {
  122. AKVERIFY( ::SetEvent( in_event ) );
  123. }
  124. /// Platform Independent Helper
  125. AkForceInline void AkClearSemaphore(AkSemaphore& io_semaphore)
  126. {
  127. io_semaphore = NULL;
  128. }
  129. /// Platform Independent Helper
  130. inline AKRESULT AkCreateSemaphore(AkSemaphore& out_semaphore, AkUInt32 in_initialCount)
  131. {
  132. #ifdef AK_USE_UWP_API
  133. out_semaphore = ::CreateSemaphoreEx(
  134. NULL, // no security attributes
  135. in_initialCount, // initial count
  136. INT_MAX, // no maximum -- matches posix semaphore behaviour
  137. NULL, // no name
  138. 0, // reserved
  139. STANDARD_RIGHTS_ALL | SEMAPHORE_MODIFY_STATE);
  140. #else
  141. out_semaphore = ::CreateSemaphore(
  142. NULL, // no security attributes
  143. in_initialCount, // initial count
  144. INT_MAX, // no maximum -- matches posix semaphore behaviour
  145. NULL); // no name
  146. #endif
  147. return (out_semaphore) ? AK_Success : AK_Fail;
  148. }
  149. /// Platform Independent Helper
  150. inline void AkDestroySemaphore(AkSemaphore& io_semaphore)
  151. {
  152. ::CloseHandle(io_semaphore);
  153. }
  154. /// Platform Independent Helper - Semaphore wait, aka Operation P. Decrements value of semaphore, and, if the semaphore would be less than 0, waits for the semaphore to be released.
  155. inline void AkWaitForSemaphore(AkSemaphore& in_semaphore)
  156. {
  157. AKVERIFY(::WaitForSingleObject(in_semaphore, INFINITE) == WAIT_OBJECT_0);
  158. }
  159. /// Platform Independent Helper - Semaphore signal, aka Operation V. Increments value of semaphore by an arbitrary count.
  160. inline void AkReleaseSemaphore(AkSemaphore& in_semaphore, AkUInt32 in_count)
  161. {
  162. AKVERIFY(ReleaseSemaphore(in_semaphore, in_count, NULL) >= 0);
  163. }
  164. // Virtual Memory
  165. // ------------------------------------------------------------------
  166. #if defined(AK_WIN)
  167. #ifdef AK_WIN_UNIVERSAL_APP
  168. AkForceInline void* AllocVM(size_t size, size_t* /*extra*/)
  169. {
  170. return VirtualAllocFromApp(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
  171. }
  172. #else
  173. AkForceInline void* AllocVM(size_t size, size_t* /*extra*/)
  174. {
  175. return VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
  176. }
  177. #endif
  178. AkForceInline void FreeVM(void* address, size_t size, size_t /*extra*/, size_t release)
  179. {
  180. VirtualFree(address, release ? 0 : size, release ? MEM_RELEASE : MEM_DECOMMIT);
  181. }
  182. #endif
  183. // Threads
  184. // ------------------------------------------------------------------
  185. /// Platform Independent Helper
  186. inline bool AkIsValidThread( AkThread * in_pThread )
  187. {
  188. return (*in_pThread != AK_NULL_THREAD);
  189. }
  190. /// Platform Independent Helper
  191. inline void AkClearThread( AkThread * in_pThread )
  192. {
  193. *in_pThread = AK_NULL_THREAD;
  194. }
  195. /// Platform Independent Helper
  196. inline void AkCloseThread( AkThread * in_pThread )
  197. {
  198. AKASSERT( in_pThread );
  199. AKASSERT( *in_pThread );
  200. AKVERIFY( ::CloseHandle( *in_pThread ) );
  201. AkClearThread( in_pThread );
  202. }
  203. #define AkExitThread( _result ) return _result;
  204. /// Platform Independent Helper
  205. inline void AkGetDefaultThreadProperties( AkThreadProperties & out_threadProperties )
  206. {
  207. out_threadProperties.nPriority = AK_THREAD_PRIORITY_NORMAL;
  208. out_threadProperties.uStackSize= AK_DEFAULT_STACK_SIZE;
  209. #ifdef AK_WIN_UNIVERSAL_APP
  210. out_threadProperties.processorNumber.Group = 0;
  211. out_threadProperties.processorNumber.Number = MAXIMUM_PROCESSORS;
  212. out_threadProperties.processorNumber.Reserved = 0;
  213. #else
  214. out_threadProperties.dwAffinityMask = 0;
  215. #endif
  216. }
  217. /// Set the name of a thread: see http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
  218. inline void AkSetThreadName( AkThread in_threadHnd, DWORD in_dwThreadID, LPCSTR in_szThreadName )
  219. {
  220. const DWORD MS_VC_EXCEPTION=0x406D1388;
  221. #pragma pack(push,8)
  222. typedef struct tagTHREADNAME_INFO
  223. {
  224. DWORD dwType;
  225. LPCSTR szName;
  226. DWORD dwThreadID;
  227. DWORD dwFlags;
  228. } THREADNAME_INFO;
  229. #pragma pack(pop)
  230. THREADNAME_INFO info;
  231. info.dwType = 0x1000;
  232. info.szName = in_szThreadName;
  233. info.dwThreadID = in_dwThreadID;
  234. info.dwFlags = 0;
  235. __try
  236. {
  237. RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
  238. }
  239. #pragma warning(suppress: 6312 6322)
  240. __except(EXCEPTION_CONTINUE_EXECUTION)
  241. {
  242. }
  243. #if defined(AK_XBOX) || defined(_GAMING_DESKTOP) // also applicable on Windows when Win7 support is dropped. SetThreadDescription is a Win10 only API
  244. wchar_t wszThreadName[32];
  245. AkUInt32 maxStrLen = (sizeof(wszThreadName) / sizeof(wchar_t)) - 1;
  246. AkUInt32 nameStrLen = AkMin((int)strlen(in_szThreadName), maxStrLen);
  247. MultiByteToWideChar(CP_UTF8, 0, in_szThreadName, nameStrLen, wszThreadName, maxStrLen);
  248. wszThreadName[nameStrLen] = '\0';
  249. SetThreadDescription(in_threadHnd, wszThreadName);
  250. #endif
  251. }
  252. /// Platform Independent Helper
  253. inline void AkCreateThread(
  254. AkThreadRoutine pStartRoutine, // Thread routine.
  255. void * pParams, // Routine params.
  256. const AkThreadProperties & in_threadProperties, // Properties. NULL for default.
  257. AkThread * out_pThread, // Returned thread handle.
  258. const char * in_szThreadName ) // Opt thread name.
  259. {
  260. AKASSERT( out_pThread != NULL );
  261. AKASSERT( (in_threadProperties.nPriority >= THREAD_PRIORITY_LOWEST && in_threadProperties.nPriority <= THREAD_PRIORITY_HIGHEST)
  262. || ( in_threadProperties.nPriority == THREAD_PRIORITY_TIME_CRITICAL )
  263. || ( in_threadProperties.nPriority == THREAD_MODE_BACKGROUND_BEGIN ) );
  264. DWORD dwThreadID;
  265. *out_pThread = ::CreateThread( NULL, // No security attributes
  266. in_threadProperties.uStackSize, // StackSize (0 uses system default)
  267. pStartRoutine, // Thread start routine
  268. pParams, // Thread function parameter
  269. CREATE_SUSPENDED, // Creation flags: create suspended so we can set priority/affinity before starting
  270. &dwThreadID );
  271. // ::CreateThread() return NULL if it fails.
  272. if ( !*out_pThread )
  273. {
  274. AkClearThread( out_pThread );
  275. return;
  276. }
  277. // Set thread name.
  278. if (in_szThreadName)
  279. {
  280. AkSetThreadName(*out_pThread, dwThreadID, in_szThreadName);
  281. }
  282. // Set properties.
  283. if ( !::SetThreadPriority( *out_pThread, in_threadProperties.nPriority ) &&
  284. in_threadProperties.nPriority != THREAD_MODE_BACKGROUND_BEGIN )
  285. {
  286. AKASSERT( !"Failed setting thread priority" );
  287. AkCloseThread( out_pThread );
  288. AkClearThread(out_pThread);
  289. return;
  290. }
  291. #ifdef AK_WIN_UNIVERSAL_APP
  292. if ( in_threadProperties.processorNumber.Number != MAXIMUM_PROCESSORS)
  293. {
  294. if ( !SetThreadIdealProcessorEx( *out_pThread, const_cast<PPROCESSOR_NUMBER>(&in_threadProperties.processorNumber), NULL) )
  295. {
  296. AKASSERT( !"Failed setting thread ideal processor" );
  297. AkCloseThread( out_pThread );
  298. AkClearThread(out_pThread);
  299. return;
  300. }
  301. }
  302. #else
  303. if (in_threadProperties.dwAffinityMask)
  304. {
  305. AkUInt32 dwAffinityMask = in_threadProperties.dwAffinityMask;
  306. DWORD_PTR procAffinity, sysAffinity;
  307. if (::GetProcessAffinityMask(::GetCurrentProcess(), &procAffinity, &sysAffinity))
  308. {
  309. // To avoid errors in SetThreadAffinityMask, make sure the user-supplied mask is a subset of what is allowed by the process
  310. dwAffinityMask &= procAffinity;
  311. }
  312. if (!::SetThreadAffinityMask(*out_pThread, dwAffinityMask))
  313. {
  314. AKASSERT(!"Failed setting thread affinity mask");
  315. AkCloseThread(out_pThread);
  316. AkClearThread(out_pThread);
  317. return;
  318. }
  319. }
  320. #endif
  321. // Thread is ready, start it up.
  322. if (!::ResumeThread(*out_pThread))
  323. {
  324. AKASSERT(!"Failed to start the thread");
  325. AkCloseThread(out_pThread);
  326. AkClearThread(out_pThread);
  327. return;
  328. }
  329. }
  330. /// Platform Independent Helper
  331. inline void AkWaitForSingleThread( AkThread * in_pThread )
  332. {
  333. AKASSERT( in_pThread );
  334. AKASSERT( *in_pThread );
  335. #ifdef AK_USE_UWP_API
  336. ::WaitForSingleObjectEx( *in_pThread, INFINITE, FALSE );
  337. #else
  338. ::WaitForSingleObject( *in_pThread, INFINITE );
  339. #endif
  340. }
  341. /// Returns the calling thread's ID.
  342. inline AkThreadID CurrentThread()
  343. {
  344. return ::GetCurrentThreadId();
  345. }
  346. /// Platform Independent Helper
  347. inline void AkSleep( AkUInt32 in_ulMilliseconds )
  348. {
  349. ::Sleep( in_ulMilliseconds );
  350. }
  351. // Optimized memory functions
  352. // --------------------------------------------------------------------
  353. /// Platform Independent Helper
  354. inline void AkMemCpy( void * pDest, const void * pSrc, AkUInt32 uSize )
  355. {
  356. memcpy( pDest, pSrc, uSize );
  357. }
  358. /// Platform Independent Helper
  359. inline void AkMemMove( void* pDest, const void* pSrc, AkUInt32 uSize )
  360. {
  361. memmove( pDest, pSrc, uSize );
  362. }
  363. /// Platform Independent Helper
  364. inline void AkMemSet( void * pDest, AkInt32 iVal, AkUInt32 uSize )
  365. {
  366. memset( pDest, iVal, uSize );
  367. }
  368. // Time functions
  369. // ------------------------------------------------------------------
  370. /// Platform Independent Helper
  371. inline void PerformanceCounter( AkInt64 * out_piLastTime )
  372. {
  373. ::QueryPerformanceCounter( (LARGE_INTEGER*)out_piLastTime );
  374. }
  375. /// Platform Independent Helper
  376. inline void PerformanceFrequency( AkInt64 * out_piFreq )
  377. {
  378. ::QueryPerformanceFrequency( (LARGE_INTEGER*)out_piFreq );
  379. }
  380. /// Platform Independent Helper
  381. inline void UpdatePerformanceFrequency()
  382. {
  383. AkInt64 iFreq;
  384. PerformanceFrequency( &iFreq );
  385. AK::g_fFreqRatio = (AkReal32)((AkReal64)iFreq / 1000);
  386. }
  387. /// Returns a time range in milliseconds, using the sound engine's updated count->milliseconds ratio.
  388. inline AkReal32 Elapsed( const AkInt64 & in_iNow, const AkInt64 & in_iStart )
  389. {
  390. return ( in_iNow - in_iStart ) / AK::g_fFreqRatio;
  391. }
  392. #if defined(AK_XBOXSERIESX)
  393. // Waits for a limited amount of time for in_pVal to hit zero (without yielding the thread)
  394. inline void AkLimitedSpinForZero(AkAtomic32* in_pVal)
  395. {
  396. // monitorx and waitx are available on certain AMD CPUs, so we can have a custom impl of AkLimitedSpinForZero
  397. AkInt64 endSpinTime = 0;
  398. AkInt64 currentTime = 0;
  399. PerformanceCounter(&endSpinTime);
  400. endSpinTime += AkInt64(AK::g_fFreqRatio * 0.01); // only spin for about 10us
  401. while (true)
  402. {
  403. // set up monitorx on pVal
  404. _mm_monitorx((void*)in_pVal, 0U, 0U);
  405. // if pval is zero, skip out
  406. if (AkAtomicLoad32(in_pVal) == 0)
  407. {
  408. break;
  409. }
  410. // wait until a store to pVal occurs (or ~1us passes)
  411. _mm_mwaitx(2U, 0U, 1000U);
  412. // Check if we've hit the deadline for the timeout
  413. PerformanceCounter(&currentTime);
  414. if (currentTime > endSpinTime)
  415. {
  416. break;
  417. }
  418. }
  419. }
  420. #define AK_LIMITEDSPINFORZERO // mark AkLimitedSpinForZero as defined to avoid duplicate definitions
  421. #endif
  422. /// String conversion helper. If io_pszAnsiString is null, the function returns the required size.
  423. inline AkInt32 AkWideCharToChar( const wchar_t* in_pszUnicodeString,
  424. AkUInt32 in_uiOutBufferSize,
  425. char* io_pszAnsiString )
  426. {
  427. if(!io_pszAnsiString)
  428. return WideCharToMultiByte(CP_UTF8, 0, in_pszUnicodeString, -1, NULL, 0, NULL, NULL);
  429. int iWritten = ::WideCharToMultiByte(CP_UTF8, // code page
  430. 0, // performance and mapping flags
  431. in_pszUnicodeString, // wide-character string
  432. (int)AkMin( ( (AkUInt32)wcslen( in_pszUnicodeString )), in_uiOutBufferSize-1 ), // number of chars in string : -1 = NULL terminated string.
  433. io_pszAnsiString, // buffer for new string
  434. in_uiOutBufferSize, // size of buffer
  435. NULL, // default for unmappable chars
  436. NULL); // set when default char used
  437. io_pszAnsiString[iWritten] = 0;
  438. return iWritten;
  439. }
  440. /// String conversion helper
  441. inline AkInt32 AkCharToWideChar( const char* in_pszAnsiString,
  442. AkUInt32 in_uiOutBufferSize,
  443. void* io_pvUnicodeStringBuffer )
  444. {
  445. return ::MultiByteToWideChar( CP_UTF8, // code page
  446. 0, // performance and mapping flags
  447. in_pszAnsiString, // wide-character string
  448. -1, // number of chars in string : -1 = NULL terminated string.
  449. (wchar_t*)io_pvUnicodeStringBuffer, // buffer for new string
  450. in_uiOutBufferSize); // size of buffer
  451. }
  452. /// String conversion helper
  453. inline AkInt32 AkUtf8ToWideChar( const char* in_pszUtf8String,
  454. AkUInt32 in_uiOutBufferSize,
  455. void* io_pvUnicodeStringBuffer )
  456. {
  457. return ::MultiByteToWideChar( CP_UTF8, // code page
  458. 0, // performance and mapping flags
  459. in_pszUtf8String, // wide-character string
  460. -1, // number of chars in string : -1 = NULL terminated string.
  461. (wchar_t*)io_pvUnicodeStringBuffer, // buffer for new string
  462. in_uiOutBufferSize); // size of buffer
  463. }
  464. /// Safe unicode string copy.
  465. inline void SafeStrCpy( wchar_t * in_pDest, const wchar_t* in_pSrc, size_t in_uDestMaxNumChars )
  466. {
  467. size_t iSizeCopy = AkMin( in_uDestMaxNumChars - 1, wcslen( in_pSrc ) + 1 );
  468. wcsncpy_s( in_pDest, in_uDestMaxNumChars, in_pSrc, iSizeCopy );
  469. in_pDest[iSizeCopy] = '\0';
  470. }
  471. /// Safe string copy.
  472. inline void SafeStrCpy( char * in_pDest, const char* in_pSrc, size_t in_uDestMaxNumChars )
  473. {
  474. size_t iSizeCopy = AkMin( in_uDestMaxNumChars - 1, strlen( in_pSrc ) + 1 );
  475. strncpy_s( in_pDest, in_uDestMaxNumChars, in_pSrc, iSizeCopy );
  476. in_pDest[iSizeCopy] = '\0';
  477. }
  478. /// Safe unicode string concatenation.
  479. inline void SafeStrCat( wchar_t * in_pDest, const wchar_t* in_pSrc, size_t in_uDestMaxNumChars )
  480. {
  481. int iAvailableSize = (int)( in_uDestMaxNumChars - wcslen( in_pDest ) - 1 );
  482. wcsncat_s( in_pDest, in_uDestMaxNumChars, in_pSrc, AkMin( iAvailableSize, (int)wcslen( in_pSrc ) ) );
  483. }
  484. /// Safe string concatenation.
  485. inline void SafeStrCat( char * in_pDest, const char* in_pSrc, size_t in_uDestMaxNumChars )
  486. {
  487. int iAvailableSize = (int)( in_uDestMaxNumChars - strlen( in_pDest ) - 1 );
  488. strncat_s( in_pDest, in_uDestMaxNumChars, in_pSrc, AkMin( iAvailableSize, (int)strlen( in_pSrc ) ) );
  489. }
  490. inline int SafeStrFormat(wchar_t * in_pDest, size_t in_uDestMaxNumChars, const wchar_t* in_pszFmt, ...)
  491. {
  492. va_list args;
  493. va_start(args, in_pszFmt);
  494. int r = vswprintf(in_pDest, in_uDestMaxNumChars, in_pszFmt, args);
  495. va_end(args);
  496. return r;
  497. }
  498. inline int SafeStrFormat(char * in_pDest, size_t in_uDestMaxNumChars, const char* in_pszFmt, ...)
  499. {
  500. va_list args;
  501. va_start(args, in_pszFmt);
  502. int r = vsnprintf(in_pDest, in_uDestMaxNumChars, in_pszFmt, args);
  503. va_end(args);
  504. return r;
  505. }
  506. /// Stack allocations.
  507. #define AkAlloca( _size_ ) _alloca( _size_ )
  508. /// Output a debug message on the console
  509. #if ! defined(AK_OPTIMIZED)
  510. inline void OutputDebugMsg( const wchar_t* in_pszMsg )
  511. {
  512. OutputDebugStringW( in_pszMsg );
  513. }
  514. /// Output a debug message on the console
  515. inline void OutputDebugMsg( const char* in_pszMsg )
  516. {
  517. OutputDebugStringA( in_pszMsg );
  518. }
  519. /// Output a debug message on the console (variadic function).
  520. /// Use MaxSize to specify the size to reserve for the message.
  521. /// Warning: On Win32, OutputDebugMsgV with wchar_t will truncate the string
  522. /// to MaxSize.
  523. template <int MaxSize = 256>
  524. inline void OutputDebugMsgV(const wchar_t* in_pszFmt, ...)
  525. {
  526. // No equivalent of snprintf for wide string so truncate if necessary
  527. // vswprintf returns the number of characters written and not the numbers
  528. // that would be written
  529. wchar_t* msg = (wchar_t*)AkAlloca(MaxSize * sizeof(wchar_t));
  530. msg[MaxSize - 1] = '\0';
  531. va_list args;
  532. va_start(args, in_pszFmt);
  533. vswprintf(msg, MaxSize, in_pszFmt, args);
  534. va_end(args);
  535. OutputDebugMsg(msg);
  536. }
  537. /// Output a debug message on the console (variadic function).
  538. /// Use MaxSize to specify the expected size of the message.
  539. /// If the required size is superior to MaxSize, a new string
  540. /// will be allocated on the stack with the required size.
  541. template <int MaxSize = 256>
  542. inline void OutputDebugMsgV(const char* in_pszFmt, ...)
  543. {
  544. int size = 0;
  545. {
  546. // Try with a reasonable guess first
  547. char msg[MaxSize];
  548. msg[MaxSize - 1] = '\0';
  549. va_list args;
  550. va_start(args, in_pszFmt);
  551. size = vsnprintf(msg, MaxSize, in_pszFmt, args);
  552. va_end(args);
  553. // If it was enough, print to debug log
  554. if (0 <= size && size <= MaxSize)
  555. {
  556. OutputDebugMsg(msg);
  557. return;
  558. }
  559. }
  560. // Else, we need more memory to prevent truncation
  561. {
  562. // size + 1 more char for the last '\0'
  563. size++;
  564. char* msg = (char*)AkAlloca((size) * sizeof(char));
  565. msg[size - 1] = '\0';
  566. va_list args;
  567. va_start(args, in_pszFmt);
  568. vsnprintf(msg, size, in_pszFmt, args);
  569. va_end(args);
  570. OutputDebugMsg(msg);
  571. }
  572. }
  573. #else
  574. inline void OutputDebugMsg(const wchar_t*){}
  575. inline void OutputDebugMsg(const char*){}
  576. template <int MaxSize = 0>
  577. inline void OutputDebugMsgV(const wchar_t*, ...) {}
  578. template <int MaxSize = 0>
  579. inline void OutputDebugMsgV(const char*, ...) {}
  580. #endif
  581. /// Converts a wchar_t string to an AkOSChar string.
  582. /// \remark On some platforms the AkOSChar string simply points to the same string,
  583. /// on others a new buffer is allocated on the stack using AkAlloca. This means
  584. /// you must make sure that:
  585. /// - The source string stays valid and unmodified for as long as you need the
  586. /// AkOSChar string (for cases where they point to the same string)
  587. /// - The AkOSChar string is used within this scope only -- for example, do NOT
  588. /// return that string from a function (for cases where it is allocated on the stack)
  589. #define CONVERT_WIDE_TO_OSCHAR( _wstring_, _oscharstring_ ) ( _oscharstring_ ) = (AkOSChar*)( _wstring_ )
  590. /// Converts a char string to an AkOSChar string.
  591. /// \remark On some platforms the AkOSChar string simply points to the same string,
  592. /// on others a new buffer is allocated on the stack using AkAlloca. This means
  593. /// you must make sure that:
  594. /// - The source string stays valid and unmodified for as long as you need the
  595. /// AkOSChar string (for cases where they point to the same string)
  596. /// - The AkOSChar string is used within this scope only -- for example, do NOT
  597. /// return that string from a function (for cases where it is allocated on the stack)
  598. #define CONVERT_CHAR_TO_OSCHAR( _astring_, _oscharstring_ ) \
  599. _oscharstring_ = (AkOSChar*)AkAlloca( (1 + strlen( _astring_ )) * sizeof(AkOSChar)); \
  600. AKPLATFORM::AkCharToWideChar( _astring_, (AkUInt32)(1 + strlen(_astring_ )), (AkOSChar*)( _oscharstring_ ) )
  601. /// Converts a AkOSChar string into wide char string.
  602. /// \remark On some platforms the AkOSChar string simply points to the same string,
  603. /// on others a new buffer is allocated on the stack using AkAlloca. This means
  604. /// you must make sure that:
  605. /// - The source string stays valid and unmodified for as long as you need the
  606. /// AkOSChar string (for cases where they point to the same string)
  607. /// - The AkOSChar string is used within this scope only -- for example, do NOT
  608. /// return that string from a function (for cases where it is allocated on the stack)
  609. #define CONVERT_OSCHAR_TO_WIDE( _osstring_, _wstring_ ) _wstring_ = _osstring_
  610. /// Converts a AkOSChar string into char string.
  611. /// \remark On some platforms the AkOSChar string simply points to the same string,
  612. /// on others a new buffer is allocated on the stack using AkAlloca. This means
  613. /// you must make sure that:
  614. /// - The source string stays valid and unmodified for as long as you need the
  615. /// AkOSChar string (for cases where they point to the same string)
  616. /// - The AkOSChar string is used within this scope only -- for example, do NOT
  617. /// return that string from a function (for cases where it is allocated on the stack)
  618. #define CONVERT_OSCHAR_TO_CHAR( _osstring_, _astring_ ) \
  619. _astring_ = (char*)AkAlloca( 1 + AKPLATFORM::AkWideCharToChar( _osstring_, 0, NULL )); \
  620. AKPLATFORM::AkWideCharToChar( _osstring_, AkUInt32(1 + AKPLATFORM::AkWideCharToChar( _osstring_, 0, NULL )), _astring_ );
  621. /// Get the length, in characters, of a NULL-terminated AkUtf16 string
  622. /// \return The length, in characters, of the specified string (excluding terminating NULL)
  623. inline size_t AkUtf16StrLen( const AkUtf16* in_pStr )
  624. {
  625. return ( wcslen( in_pStr ) );
  626. }
  627. /// Get the length, in characters, of a NULL-terminated AkOSChar string
  628. /// \return The length, in characters, of the specified string (excluding terminating NULL)
  629. inline size_t OsStrLen( const AkOSChar* in_pszString )
  630. {
  631. return ( wcslen( in_pszString ) );
  632. }
  633. /// AkOSChar version of sprintf().
  634. #define AK_OSPRINTF swprintf_s
  635. /// Compare two NULL-terminated AkOSChar strings
  636. /// \return
  637. /// - \< 0 if in_pszString1 \< in_pszString2
  638. /// - 0 if the two strings are identical
  639. /// - \> 0 if in_pszString1 \> in_pszString2
  640. /// \remark The comparison is case-sensitive
  641. inline int OsStrCmp( const AkOSChar* in_pszString1, const AkOSChar* in_pszString2 )
  642. {
  643. return ( wcscmp( in_pszString1, in_pszString2 ) );
  644. }
  645. /// Compare two NULL-terminated AkOSChar strings up to the specified count of characters.
  646. /// \return
  647. /// - \< 0 if in_pszString1 \< in_pszString2
  648. /// - 0 if the two strings are identical
  649. /// - \> 0 if in_pszString1 \> in_pszString2
  650. /// \remark The comparison is case-sensitive
  651. inline int OsStrNCmp( const AkOSChar* in_pszString1, const AkOSChar* in_pszString2, size_t in_MaxCountSize)
  652. {
  653. return wcsncmp(in_pszString1, in_pszString2, in_MaxCountSize);
  654. }
  655. #define AK_UTF16_TO_WCHAR( in_pdDest, in_pSrc, in_MaxSize ) AKPLATFORM::SafeStrCpy( in_pdDest, in_pSrc, in_MaxSize )
  656. #define AK_WCHAR_TO_UTF16( in_pdDest, in_pSrc, in_MaxSize ) AKPLATFORM::SafeStrCpy( in_pdDest, in_pSrc, in_MaxSize )
  657. #define AK_UTF8_TO_OSCHAR( in_pdDest, in_pSrc, in_MaxSize ) AKPLATFORM::AkCharToWideChar( in_pSrc, in_MaxSize, in_pdDest )
  658. #define AK_UTF16_TO_OSCHAR( in_pdDest, in_pSrc, in_MaxSize ) AKPLATFORM::SafeStrCpy( in_pdDest, in_pSrc, in_MaxSize )
  659. #define AK_UTF16_TO_CHAR( in_pdDest, in_pSrc, in_MaxSize ) AKPLATFORM::AkWideCharToChar( in_pSrc, in_MaxSize, in_pdDest )
  660. #define AK_CHAR_TO_UTF16( in_pdDest, in_pSrc, in_MaxSize ) AKPLATFORM::AkCharToWideChar( in_pSrc, in_MaxSize, in_pdDest )
  661. #define AK_OSCHAR_TO_UTF16( in_pdDest, in_pSrc, in_MaxSize ) AKPLATFORM::SafeStrCpy( in_pdDest, in_pSrc, in_MaxSize )
  662. /// Detects whether the string represents an absolute path to a file
  663. inline bool IsAbsolutePath(const AkOSChar* in_pszPath, size_t in_pathLen)
  664. {
  665. return
  666. (in_pathLen >= 3 && in_pszPath[1] == ':' && in_pszPath[2] == '\\') || // Classic "C:\..." DOS-style path
  667. (in_pathLen >= 2 && in_pszPath[0] == '\\'); // Uncommon "\..." absolute path from current drive, or UNC "\\..."
  668. }
  669. // Use with AkOSChar.
  670. #define AK_PATH_SEPARATOR (L"\\")
  671. #define AK_LIBRARY_PREFIX (L"")
  672. #define AK_DYNAMIC_LIBRARY_EXTENSION (L".dll")
  673. #define AK_FILEHANDLE_TO_UINTPTR(_h) ((AkUIntPtr)_h)
  674. #define AK_SET_FILEHANDLE_TO_UINTPTR(_h,_u) _h = (AkFileHandle)_u
  675. #if defined(AK_ENABLE_PERF_RECORDING)
  676. static AkUInt32 g_uAkPerfRecExecCount = 0;
  677. static AkReal32 g_fAkPerfRecExecTime = 0.f;
  678. #define AK_PERF_RECORDING_RESET() \
  679. AKPLATFORM::g_uAkPerfRecExecCount = 0;\
  680. AKPLATFORM::g_fAkPerfRecExecTime = 0.f;
  681. #define AK_PERF_RECORDING_START( __StorageName__, __uExecutionCountStart__, __uExecutionCountStop__ ) \
  682. AkInt64 iAkPerfRecTimeBefore; \
  683. if ( (AKPLATFORM::g_uAkPerfRecExecCount >= (__uExecutionCountStart__)) && (AKPLATFORM::g_uAkPerfRecExecCount <= (__uExecutionCountStop__)) ) \
  684. AKPLATFORM::PerformanceCounter( &iAkPerfRecTimeBefore );
  685. #define AK_PERF_RECORDING_STOP( __StorageName__, __uExecutionCountStart__, __uExecutionCountStop__ ) \
  686. if ( (AKPLATFORM::g_uAkPerfRecExecCount >= (__uExecutionCountStart__)) && (AKPLATFORM::g_uAkPerfRecExecCount <= (__uExecutionCountStop__)) ) \
  687. { \
  688. AkInt64 iAkPerfRecTimeAfter; \
  689. AKPLATFORM::PerformanceCounter( &iAkPerfRecTimeAfter ); \
  690. AKPLATFORM::g_fAkPerfRecExecTime += AKPLATFORM::Elapsed( iAkPerfRecTimeAfter, iAkPerfRecTimeBefore ); \
  691. if ( AKPLATFORM::g_uAkPerfRecExecCount == (__uExecutionCountStop__) ) \
  692. { \
  693. AkReal32 fAverageExecutionTime = AKPLATFORM::g_fAkPerfRecExecTime/((__uExecutionCountStop__)-(__uExecutionCountStart__)); \
  694. AkOSChar str[256]; \
  695. swprintf_s(str, 256, AKTEXT("%s average execution time: %f\n"), AKTEXT(__StorageName__), fAverageExecutionTime);\
  696. AKPLATFORM::OutputDebugMsg( str ); \
  697. } \
  698. } \
  699. AKPLATFORM::g_uAkPerfRecExecCount++;
  700. #endif // AK_ENABLE_PERF_RECORDING
  701. #if (defined(AK_CPU_X86_64) || defined(AK_CPU_X86))
  702. /// Support to fetch the CPUID for the platform. Only valid for X86 targets
  703. /// \remark Note that IAkProcessorFeatures should be preferred to fetch this data
  704. /// as it will have already translated the feature bits into AK-relevant enums
  705. inline void CPUID(AkUInt32 in_uLeafOpcode, AkUInt32 in_uSubLeafOpcode, unsigned int out_uCPUFeatures[4])
  706. {
  707. __cpuidex((int*)out_uCPUFeatures, in_uLeafOpcode, in_uSubLeafOpcode);
  708. }
  709. #endif
  710. }
  711. #endif // _AK_PLATFORM_FUNCS_H_