| 448 | } |
| 449 | |
| 450 | bool Asset::WaitForLoaded(double timeoutInMilliseconds) const |
| 451 | { |
| 452 | // This function is used many time when some parts of the engine need to wait for asset loading end (it may fail but has to end). |
| 453 | // But it cannot be just a simple active-wait loop. |
| 454 | // Imagine following situation: |
| 455 | // Content Pool has 2 loading threads. |
| 456 | // Both start to load layered materials which need to be recompiled (Material Generator work). |
| 457 | // Every of these materials is made of a few layers. |
| 458 | // To load child layer Material Generator is requesting Content Pool to load it fully. |
| 459 | // However this cannot be done because Pool has limited threads and all of them may requesting more loads to be done. |
| 460 | // |
| 461 | // To solve that problem we have different solutions: |
| 462 | // 1) add more loading threads (bad idea) |
| 463 | // 2) content loading could use thread pool to enqueue single tasks (still risky due to many tasks queued and limited Thread Pool size (need to build graph of dependencies?)) |
| 464 | // 3) content loading could detect asset load calls from content loader threads and load requested asset without stalls |
| 465 | // 4) every asset could expose dependencies and content system could load required dependencies earlier |
| 466 | // |
| 467 | // 3) and 4) are good solutions. 4) would require more work but will be needed in future for building system to gather assets for game packages. |
| 468 | // But WaitForLoaded could detect if is called from the Loading Thead and manually load dependent asset. It's fairly easy to do and will work out. |
| 469 | |
| 470 | // Early out if asset has been already loaded |
| 471 | if (IsLoaded()) |
| 472 | { |
| 473 | // If running on a main thread we can flush asset `Loaded` event |
| 474 | if (IsInMainThread()) |
| 475 | { |
| 476 | Content::tryCallOnLoaded((Asset*)this); |
| 477 | } |
| 478 | |
| 479 | return false; |
| 480 | } |
| 481 | |
| 482 | // Check if loading failed |
| 483 | Platform::MemoryBarrier(); |
| 484 | if (LastLoadFailed()) |
| 485 | return true; |
| 486 | |
| 487 | // Check if has missing loading task |
| 488 | Platform::MemoryBarrier(); |
| 489 | const auto loadingTask = (ContentLoadTask*)Platform::AtomicRead(&_loadingTask); |
| 490 | if (loadingTask == nullptr) |
| 491 | { |
| 492 | if (IsLoaded()) |
| 493 | return false; |
| 494 | LOG(Warning, "WaitForLoaded asset \'{0}\' failed. No loading task attached and asset is not loaded.", ToString()); |
| 495 | return true; |
| 496 | } |
| 497 | |
| 498 | PROFILE_CPU(); |
| 499 | ZoneColor(TracyWaitZoneColor); |
| 500 | const StringView path(GetPath()); |
| 501 | ZoneText(*path, path.Length()); |
| 502 | |
| 503 | Content::WaitForTask(loadingTask, timeoutInMilliseconds); |
| 504 | |
| 505 | // If running on a main thread we can flush asset `Loaded` event |
| 506 | if (IsInMainThread() && IsLoaded()) |
| 507 | { |
no test coverage detected