WwiseFileCache.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. /*******************************************************************************
  2. The content of this file includes portions of the proprietary AUDIOKINETIC Wwise
  3. Technology released in source code form as part of the game integration package.
  4. The content of this file may not be used without valid licenses to the
  5. AUDIOKINETIC Wwise Technology.
  6. Note that the use of the game engine is subject to the Unreal(R) Engine End User
  7. License Agreement at https://www.unrealengine.com/en-US/eula/unreal
  8. License Usage
  9. Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use
  10. this file in accordance with the end user license agreement provided with the
  11. software or, alternatively, in accordance with the terms contained
  12. in a written agreement between you and Audiokinetic Inc.
  13. Copyright (c) 2023 Audiokinetic Inc.
  14. *******************************************************************************/
  15. #include "Wwise/WwiseFileCache.h"
  16. #include "Wwise/WwiseExecutionQueue.h"
  17. #include "Wwise/WwiseFileHandlerModule.h"
  18. #include "Wwise/Stats/AsyncStats.h"
  19. #include "Wwise/Stats/FileHandler.h"
  20. #include "WwiseDefines.h"
  21. #include "Async/Async.h"
  22. #include "Async/AsyncFileHandle.h"
  23. #if UE_5_0_OR_LATER
  24. #include "HAL/PlatformFileManager.h"
  25. #else
  26. #include "HAL/PlatformFilemanager.h"
  27. #endif
  28. #include <inttypes.h>
  29. FWwiseFileCache* FWwiseFileCache::Get()
  30. {
  31. if (auto* Module = IWwiseFileHandlerModule::GetModule())
  32. {
  33. if (auto* FileCache = Module->GetFileCache())
  34. {
  35. return FileCache;
  36. }
  37. }
  38. return nullptr;
  39. }
  40. FWwiseFileCache::FWwiseFileCache()
  41. {
  42. }
  43. FWwiseFileCache::~FWwiseFileCache()
  44. {
  45. }
  46. void FWwiseFileCache::CreateFileCacheHandle(
  47. FWwiseFileCacheHandle*& OutHandle,
  48. const FString& Pathname,
  49. FWwiseFileOperationDone&& OnDone)
  50. {
  51. OutHandle = new FWwiseFileCacheHandle(Pathname);
  52. if (UNLIKELY(!OutHandle))
  53. {
  54. OnDone(false);
  55. }
  56. OutHandle->Open(MoveTemp(OnDone));
  57. }
  58. FWwiseFileCacheHandle::FWwiseFileCacheHandle(const FString& InPathname) :
  59. Pathname { InPathname },
  60. FileHandle { nullptr },
  61. FileSize { 0 },
  62. InitializationStat { nullptr }
  63. {
  64. }
  65. FWwiseFileCacheHandle::~FWwiseFileCacheHandle()
  66. {
  67. SCOPED_WWISEFILEHANDLER_EVENT_3(TEXT("FWwiseFileCacheHandle::~FWwiseFileCacheHandle"));
  68. const auto* FileHandleToDestroy = FileHandle; FileHandle = nullptr;
  69. if (UNLIKELY(RequestsInFlight.Load() > 0))
  70. {
  71. UE_LOG(LogWwiseFileHandler, Verbose, TEXT("FWwiseFileCacheHandle: Closing %s with %" PRIi32 " operations left to process."), *Pathname, RequestsInFlight.Load());
  72. auto* CanDestroyEvent = FPlatformProcess::GetSynchEventFromPool(false);
  73. CanDestroy.Store(CanDestroyEvent, EMemoryOrder::SequentiallyConsistent);
  74. while (RequestsInFlight.Load(EMemoryOrder::SequentiallyConsistent) > 0)
  75. {
  76. CanDestroyEvent->Wait(FTimespan::FromMilliseconds(1));
  77. }
  78. CanDestroy.Store(nullptr);
  79. FFunctionGraphTask::CreateAndDispatchWhenReady([CanDestroyEvent]
  80. {
  81. FPlatformProcess::ReturnSynchEventToPool(CanDestroyEvent);
  82. });
  83. }
  84. UE_LOG(LogWwiseFileHandler, Verbose, TEXT("FWwiseFileCacheHandle: Closing %s."), *Pathname);
  85. delete FileHandleToDestroy;
  86. DEC_DWORD_STAT(STAT_WwiseFileHandlerOpenedStreams);
  87. }
  88. void FWwiseFileCacheHandle::Open(FWwiseFileOperationDone&& OnDone)
  89. {
  90. SCOPED_WWISEFILEHANDLER_EVENT_3(TEXT("FWwiseFileCacheHandle::Open"));
  91. check(!InitializationStat);
  92. check(!FileHandle);
  93. InitializationStat = new FWwiseAsyncCycleCounter(GET_STATID(STAT_WwiseFileHandlerFileOperationLatency));
  94. InitializationDone = MoveTemp(OnDone);
  95. FWwiseAsyncCycleCounter Stat(GET_STATID(STAT_WwiseFileHandlerFileOperationLatency));
  96. const auto FileCache = FWwiseFileCache::Get();
  97. if (UNLIKELY(!FileCache))
  98. {
  99. UE_LOG(LogWwiseFileHandler, Verbose, TEXT("FWwiseFileCacheHandle: FileCache not available while opening %s."), *Pathname);
  100. delete InitializationStat; InitializationStat = nullptr;
  101. CallDone(false, MoveTemp(InitializationDone));
  102. return;
  103. }
  104. ++RequestsInFlight;
  105. FileCache->OpenQueue.Async([this, OnDone = MoveTemp(OnDone)]() mutable
  106. {
  107. SCOPED_WWISEFILEHANDLER_EVENT_3(TEXT("FWwiseFileCacheHandle::Open Async"));
  108. check(!FileHandle);
  109. UE_LOG(LogWwiseFileHandler, Verbose, TEXT("FWwiseFileCacheHandle: Opening %s."), *Pathname);
  110. IAsyncReadFileHandle* CurrentFileHandle;
  111. ASYNC_INC_DWORD_STAT(STAT_WwiseFileHandlerOpenedStreams);
  112. {
  113. SCOPED_WWISEFILEHANDLER_EVENT_3(TEXT("FWwiseFileCacheHandle::Open OpenAsyncRead"));
  114. CurrentFileHandle = FileHandle = FPlatformFileManager::Get().GetPlatformFile().OpenAsyncRead(*Pathname);
  115. }
  116. if (UNLIKELY(!CurrentFileHandle))
  117. {
  118. UE_LOG(LogWwiseFileHandler, Verbose, TEXT("FWwiseFileCacheHandle: OpenAsyncRead %s failed instantiating."), *Pathname);
  119. delete InitializationStat; InitializationStat = nullptr;
  120. CallDone(false, MoveTemp(InitializationDone));
  121. RemoveRequestInFlight();
  122. return;
  123. }
  124. FAsyncFileCallBack SizeCallbackFunction = [this](bool bWasCancelled, IAsyncReadRequest* Request) mutable
  125. {
  126. OnSizeRequestDone(bWasCancelled, Request);
  127. };
  128. IAsyncReadRequest* Request;
  129. {
  130. SCOPED_WWISEFILEHANDLER_EVENT_3(TEXT("FWwiseFileCacheHandle::Open SizeRequest"));
  131. // ++RequestsInFlight; already done
  132. Request = CurrentFileHandle->SizeRequest(&SizeCallbackFunction);
  133. }
  134. if (UNLIKELY(!Request))
  135. {
  136. UE_LOG(LogWwiseFileHandler, Verbose, TEXT("FWwiseFileCacheHandle: SizeRequest %s failed instantiating."), *Pathname);
  137. delete InitializationStat; InitializationStat = nullptr;
  138. CallDone(false, MoveTemp(InitializationDone));
  139. RemoveRequestInFlight();
  140. }
  141. });
  142. }
  143. void FWwiseFileCacheHandle::OnSizeRequestDone(bool bWasCancelled, IAsyncReadRequest* Request)
  144. {
  145. SCOPED_WWISEFILEHANDLER_EVENT_3(TEXT("FWwiseFileCacheHandle::OnSizeRequestDone"));
  146. FileSize = Request->GetSizeResults();
  147. const bool bSizeOpSuccess = LIKELY(FileSize > 0);
  148. UE_CLOG(!bSizeOpSuccess, LogWwiseFileHandler, Log, TEXT("FWwiseFileCacheHandle: Streamed file \"%s\" could not be opened."), *Pathname);
  149. UE_CLOG(bSizeOpSuccess, LogWwiseFileHandler, VeryVerbose, TEXT("FWwiseFileCacheHandle: Initializing %s succeeded."), *Pathname);
  150. delete InitializationStat; InitializationStat = nullptr;
  151. CallDone(bSizeOpSuccess, MoveTemp(InitializationDone));
  152. DeleteRequest(Request);
  153. }
  154. void FWwiseFileCacheHandle::CallDone(bool bResult, FWwiseFileOperationDone&& OnDone)
  155. {
  156. SCOPED_WWISEFILEHANDLER_EVENT_3(TEXT("FWwiseFileCacheHandle::CallDone Callback"));
  157. OnDone(bResult);
  158. }
  159. void FWwiseFileCacheHandle::RemoveRequestInFlight()
  160. {
  161. auto* CanDestroyEvent = CanDestroy.Load(EMemoryOrder::SequentiallyConsistent);
  162. --RequestsInFlight;
  163. if (CanDestroyEvent)
  164. {
  165. CanDestroyEvent->Trigger();
  166. }
  167. }
  168. void FWwiseFileCacheHandle::DeleteRequest(IAsyncReadRequest* Request)
  169. {
  170. if (!Request || Request->PollCompletion())
  171. {
  172. SCOPED_WWISEFILEHANDLER_EVENT_4(TEXT("FWwiseFileCacheHandle::DeleteRequest"));
  173. delete Request;
  174. RemoveRequestInFlight();
  175. }
  176. else
  177. {
  178. const auto FileCache = FWwiseFileCache::Get();
  179. if (LIKELY(FileCache))
  180. {
  181. FileCache->DeleteRequestQueue.AsyncAlways([this, Request]() mutable
  182. {
  183. DeleteRequest(Request);
  184. });
  185. }
  186. else
  187. {
  188. FFunctionGraphTask::CreateAndDispatchWhenReady([this, Request]() mutable
  189. {
  190. DeleteRequest(Request);
  191. });
  192. }
  193. }
  194. };
  195. void FWwiseFileCacheHandle::ReadData(uint8* OutBuffer, int64 Offset, int64 BytesToRead,
  196. EAsyncIOPriorityAndFlags Priority, FWwiseFileOperationDone&& OnDone)
  197. {
  198. SCOPED_WWISEFILEHANDLER_EVENT_3(TEXT("FWwiseFileCacheHandle::ReadData"));
  199. FWwiseAsyncCycleCounter Stat(GET_STATID(STAT_WwiseFileHandlerFileOperationLatency));
  200. ++RequestsInFlight;
  201. IAsyncReadFileHandle* CurrentFileHandle = FileHandle;
  202. if (UNLIKELY(!CurrentFileHandle))
  203. {
  204. UE_LOG(LogWwiseFileHandler, Error, TEXT("FWwiseFileCacheHandle::ReadData: Trying to read in file %s while it was not properly initialized."), *Pathname);
  205. OnReadDataDone(false, MoveTemp(OnDone));
  206. return;
  207. }
  208. UE_LOG(LogWwiseFileHandler, VeryVerbose, TEXT("FWwiseFileCacheHandle::ReadData: %" PRIi64 "@%" PRIi64 " in %s"), BytesToRead, Offset, *Pathname);
  209. FAsyncFileCallBack ReadCallbackFunction = [this, OnDone = new FWwiseFileOperationDone(MoveTemp(OnDone)), BytesToRead, Stat = MoveTemp(Stat)](bool bWasCancelled, IAsyncReadRequest* Request) mutable
  210. {
  211. SCOPED_WWISEFILEHANDLER_EVENT_3(TEXT("FWwiseFileCacheHandle::ReadData Callback"));
  212. if (!bWasCancelled && Request) // Do not add Request->GetReadResults() since it will break subsequent results retrievals.
  213. {
  214. ASYNC_INC_FLOAT_STAT_BY(STAT_WwiseFileHandlerTotalStreamedMB, static_cast<float>(BytesToRead) / 1024 / 1024);
  215. }
  216. OnReadDataDone(bWasCancelled, Request, MoveTemp(*OnDone));
  217. delete OnDone;
  218. DeleteRequest(Request);
  219. };
  220. ASYNC_INC_FLOAT_STAT_BY(STAT_WwiseFileHandlerStreamingKB, static_cast<float>(BytesToRead) / 1024);
  221. check(BytesToRead > 0);
  222. #if STATS
  223. switch (Priority & EAsyncIOPriorityAndFlags::AIOP_PRIORITY_MASK)
  224. {
  225. case EAsyncIOPriorityAndFlags::AIOP_CriticalPath: INC_DWORD_STAT(STAT_WwiseFileHandlerCriticalPriority); break;
  226. case EAsyncIOPriorityAndFlags::AIOP_High: INC_DWORD_STAT(STAT_WwiseFileHandlerHighPriority); break;
  227. case EAsyncIOPriorityAndFlags::AIOP_BelowNormal: INC_DWORD_STAT(STAT_WwiseFileHandlerBelowNormalPriority); break;
  228. case EAsyncIOPriorityAndFlags::AIOP_Low: INC_DWORD_STAT(STAT_WwiseFileHandlerLowPriority); break;
  229. case EAsyncIOPriorityAndFlags::AIOP_MIN: INC_DWORD_STAT(STAT_WwiseFileHandlerBackgroundPriority); break;
  230. default:
  231. case EAsyncIOPriorityAndFlags::AIOP_Normal: INC_DWORD_STAT(STAT_WwiseFileHandlerNormalPriority); break;
  232. }
  233. #endif
  234. SCOPED_WWISEFILEHANDLER_EVENT_3(TEXT("FWwiseFileCacheHandle::ReadData Async ReadRequest"));
  235. const auto* Request = CurrentFileHandle->ReadRequest(Offset, BytesToRead, Priority, &ReadCallbackFunction, OutBuffer);
  236. if (UNLIKELY(!Request))
  237. {
  238. UE_LOG(LogWwiseFileHandler, Verbose, TEXT("FWwiseFileCacheHandle::ReadData: ReadRequest %s failed instantiating."), *Pathname);
  239. ReadCallbackFunction(true, nullptr);
  240. }
  241. }
  242. void FWwiseFileCacheHandle::ReadAkData(uint8* OutBuffer, int64 Offset, int64 BytesToRead, int8 AkPriority, FWwiseFileOperationDone&& OnDone)
  243. {
  244. // Wwise priority is what we expect our priority to be. Audio will skip if "our normal" is not met.
  245. constexpr const auto bHigherAudioPriority = true;
  246. EAsyncIOPriorityAndFlags Priority;
  247. if (LIKELY(AkPriority == AK_DEFAULT_PRIORITY))
  248. {
  249. Priority = bHigherAudioPriority ? AIOP_High : AIOP_Normal;
  250. }
  251. else if (AkPriority <= AK_MIN_PRIORITY)
  252. {
  253. Priority = bHigherAudioPriority ? AIOP_BelowNormal : AIOP_Low;
  254. }
  255. else if (AkPriority >= AK_MAX_PRIORITY)
  256. {
  257. Priority = AIOP_CriticalPath;
  258. }
  259. else if (AkPriority < AK_DEFAULT_PRIORITY)
  260. {
  261. Priority = bHigherAudioPriority ? AIOP_Normal : AIOP_Low;
  262. }
  263. else
  264. {
  265. Priority = bHigherAudioPriority ? AIOP_CriticalPath : AIOP_High;
  266. }
  267. ReadData(OutBuffer, Offset, BytesToRead, Priority, MoveTemp(OnDone));
  268. }
  269. void FWwiseFileCacheHandle::ReadAkData(const AkIoHeuristics& Heuristics, AkAsyncIOTransferInfo& TransferInfo,
  270. FWwiseAkFileOperationDone&& Callback)
  271. {
  272. ReadAkData(
  273. static_cast<uint8*>(TransferInfo.pBuffer),
  274. static_cast<int64>(TransferInfo.uFilePosition),
  275. static_cast<int64>(TransferInfo.uRequestedSize),
  276. Heuristics.priority,
  277. [TransferInfo = &TransferInfo, FileOpDoneCallback = MoveTemp(Callback)](bool bResult)
  278. {
  279. FileOpDoneCallback(TransferInfo, bResult ? AK_Success : AK_UnknownFileError);
  280. });
  281. }
  282. void FWwiseFileCacheHandle::OnReadDataDone(bool bWasCancelled, IAsyncReadRequest* Request,
  283. FWwiseFileOperationDone&& OnDone)
  284. {
  285. OnReadDataDone(!bWasCancelled && Request && Request->GetReadResults(), MoveTemp(OnDone));
  286. }
  287. void FWwiseFileCacheHandle::OnReadDataDone(bool bResult, FWwiseFileOperationDone&& OnDone)
  288. {
  289. --RequestsInFlight;
  290. CallDone(bResult, MoveTemp(OnDone));
  291. }