#include "KingSystem/Resource/resResourceMgrTask.h" #include #include #include #include #include #include "KingSystem/Resource/resCache.h" #include "KingSystem/Resource/resCompactedHeap.h" #include "KingSystem/Resource/resEntryFactory.h" #include "KingSystem/Resource/resMemoryTask.h" #include "KingSystem/Resource/resSystem.h" #include "KingSystem/Resource/resTextureHandleList.h" #include "KingSystem/Resource/resTextureHandleMgr.h" #include "KingSystem/System/OverlayArenaSystem.h" #include "KingSystem/Utils/SafeDelete.h" #include "KingSystem/Utils/Thread/GameTaskThread.h" #include "KingSystem/Utils/Thread/TaskMgr.h" #include "KingSystem/Utils/Thread/TaskQueueBase.h" #include "KingSystem/Utils/Thread/TaskQueueLock.h" #include "KingSystem/Utils/Thread/TaskThread.h" namespace ksys::res { namespace { class ClearCachesTaskData : public util::TaskData { SEAD_RTTI_OVERRIDE(ClearCachesTaskData, util::TaskData) public: virtual ~ClearCachesTaskData() = default; bool _8; s32 _c; sead::SafeString mStr; }; KSYS_CHECK_SIZE_NX150(ClearCachesTaskData, 0x20); } // namespace void ResourceMgrTask::setInstance(ResourceMgrTask* task) { if (!sInstance) { sInstance = task; task->mInstancePtrClearer.mClearOnDestruction = true; } } ResourceMgrTask::ResourceMgrTask(const sead::TaskConstructArg& arg) : sead::CalculateTask(arg, "res::ResourceMgrTask"), mSzsDecompressorCS(arg.heap_array->getPrimaryHeap()), mCounter(arg.heap_array->getPrimaryHeap()), mTask(arg.heap_array->getPrimaryHeap()) { mArenas.initOffset(OverlayArena::getListNodeOffset()); mUnits.initOffset(ResourceUnit::getResMgrUnitListNodeOffset()); mSomeList.initOffset(0x188); // TODO: replace this with a "get offset" call mSystemCalcFn.bind(this, &ResourceMgrTask::callSystemCalc_); mFileDevicePrefixes.initOffset(FileDevicePrefix::getListNodeOffset()); } ResourceMgrTask::~ResourceMgrTask() { util::safeDelete(mTexHandleList); util::safeDelete(mTexHandleMgr); util::safeDeleteThread(mCompactionThread); if (mCompactedHeapMip0) { mCompactedHeapMip0->destroy(); util::safeDeleteArray(mCompactedHeapMip0Buffer); } if (mCompactedHeapMain) { mCompactedHeapMain->destroy(); util::safeDeleteArray(mCompactedHeapMainBuffer); mCompactedHeapMainSeadHeap->destroy(); } util::safeDeleteArray(mCompactedHeapMainBuffer2); util::safeDelete(mOffsetReadBuf); mExtensions2.freeBuffer(); mExtensions1.freeBuffer(); util::safeDeleteThread(mMovableMemoryThread); util::safeDeleteThread(mResourceMemoryThread); util::safeDeleteThread(mResourceControlThread); util::safeDeleteThread(mResourceLoadingThread); util::safeDelete(mTask3); util::safeDelete(mTask2); util::safeDelete(mTask1); util::safeDelete(mControlTask); if (mResourceMemoryTaskMgr) { mResourceMemoryTaskMgr->finalize(); util::safeDelete(mResourceMemoryTaskMgr); } if (mResourceControlTaskMgr) { mResourceControlTaskMgr->finalize(); util::safeDelete(mResourceControlTaskMgr); } util::safeDelete(mEntryFactoryBase); mResSystemHeap->destroy(); } void ResourceMgrTask::insertOverlayArena(OverlayArena* arena) { auto lock = sead::makeScopedLock(mArenasCS); if (!mArenas.isNodeLinked(arena)) { mArenas.pushBack(arena); stubbedLogFunction(); } } util::TaskThread* ResourceMgrTask::makeResourceLoadingThread(sead::Heap* heap, bool use_game_task_thread) { if (use_game_task_thread) { return new (heap) util::GameTaskThread( "Resource Loading", heap, sead::ThreadUtil::ConvertPrioritySeadToPlatform(19), sead::MessageQueue::BlockType::Blocking, 0x7fffffff, 0xfa000, 32); } return new (heap) util::TaskThread( "Resource Loading", heap, sead::ThreadUtil::ConvertPrioritySeadToPlatform(19), sead::MessageQueue::BlockType::Blocking, 0x7fffffff, 0xa000, 32); } void ResourceMgrTask::clearAllCaches(OverlayArena* arena) { mResourceControlThread->getTaskQueue()->waitForLaneToEmpty(u8(LaneId::_5)); mResourceControlThread->getTaskQueue()->waitForLaneToEmpty(u8(LaneId::_4)); mResourceControlThread->getTaskQueue()->waitForLaneToEmpty(u8(LaneId::_3)); MemoryTaskRequest req; req.mLaneId = u8(LaneId::_9); req.mHasHandle = true; req.mSynchronous = false; req.mThread = mResourceMemoryThread; req.mDelegate = &mClearAllCachesFn; req.mName = "ClearAllCaches"; req.mData_8 = false; req.mData_c = -1; req.mData_mStr = arena->getHeap()->getName(); util::TaskMgrRequest task_mgr_request; task_mgr_request.request = &req; mResourceMemoryTaskMgr->submitRequest(task_mgr_request); } bool ResourceMgrTask::isDefragDone() const { return mTask2->getStatus() == util::Task::Status::PostFinishCallback; } f32 ResourceMgrTask::getDefragProgress() const { auto lock = sead::makeScopedLock(mArenasCS); if (_4c8 == -1) return 0; return f32(_4cc) / f32(_4c8); } void ResourceMgrTask::cancelTasks() { stubbedLogFunction(); stubbedLogFunction(); mMovableMemoryThread->cancelTasks(u8(LaneId::_1)); mMovableMemoryThread->cancelTasks(u8(LaneId::_0)); mMovableMemoryThread->cancelTasks(u8(LaneId::_3)); mMovableMemoryThread->cancelTasks(u8(LaneId::_4)); stubbedLogFunction(); stubbedLogFunction(); mResourceControlThread->cancelTasks(u8(LaneId::_0)); mResourceControlThread->cancelTasks(u8(LaneId::_1)); mResourceControlThread->cancelTasks(u8(LaneId::_2)); stubbedLogFunction(); stubbedLogFunction(); mResourceMemoryThread->cancelTasks(u8(LaneId::_1)); mResourceMemoryThread->cancelTasks(u8(LaneId::_2)); mResourceMemoryThread->cancelTasks(u8(LaneId::_3)); mResourceMemoryThread->cancelTasks(u8(LaneId::_4)); mResourceMemoryThread->cancelTasks(u8(LaneId::_5)); mResourceMemoryThread->cancelTasks(u8(LaneId::_6)); stubbedLogFunction(); stubbedLogFunction(); mResourceLoadingThread->cancelTasks(u8(LaneId::_0)); mResourceLoadingThread->cancelTasks(u8(LaneId::_1)); mResourceLoadingThread->cancelTasks(u8(LaneId::_2)); stubbedLogFunction(); stubbedLogFunction(); } void ResourceMgrTask::waitForTaskQueuesToEmpty() { if (mResourceControlThread->isPaused() || mResourceMemoryThread->isPaused() || mResourceLoadingThread->isPaused()) { return; } mResourceControlThread->waitForQueueToEmpty(); mResourceMemoryThread->waitForQueueToEmpty(); mResourceLoadingThread->waitForQueueToEmpty(); } s32 ResourceMgrTask::getNumActiveTasksOnResLoadingThread() const { return mResourceLoadingThread->getNumActiveTasks(); } OffsetReadFileDevice* ResourceMgrTask::getOffsetReadFileDevice() const { return mOffsetReadFileDevice; } sead::ArchiveFileDevice* ResourceMgrTask::getArchiveFileDev1() { return &mArchiveFileDev1; } void ResourceMgrTask::controlField9c0d88(bool off) { if (off) { _9c0d88.decrement(); static_cast(_9c0d88.load()); } else { _9c0d88.increment(); } mFlags.change(Flag::_400, _9c0d88 <= 0); } void ResourceMgrTask::setFlag2000Or5000(s32 type) { if (type != 0) { mFlags.set(Flag::_1000); mFlags.set(Flag::_4000); stubbedLogFunction(); } else { mFlags.set(Flag::_2000); } } bool ResourceMgrTask::isFlag4Set() const { return mFlags.isOn(Flag::_4); } void ResourceMgrTask::registerUnit(ResourceUnit* unit) { auto lock = sead::makeScopedLock(mUnitsCS); mUnits.pushBack(unit); if (res::returnFalse()) stubbedLogFunction(); } void ResourceMgrTask::deregisterUnit(ResourceUnit* unit) { auto lock = sead::makeScopedLock(mUnitsCS); if (unit->isLinkedToResourceMgr()) { mUnits.erase(unit); if (res::returnFalse()) stubbedLogFunction(); } } void ResourceMgrTask::requestClearCache(ResourceUnit** p_unit, util::Task* task) { if (!p_unit || !*p_unit || !(*p_unit)->isStatusFlag8000Set()) { stubbedLogFunction(); return; } if ((*p_unit)->getRefCount() > 0 || (*p_unit)->isStatus1() || (*p_unit)->isStatusFlag10000Set()) return; (*p_unit)->setStatusFlag10000(); { ControlTaskRequest req; req.mHasHandle = true; req.mSynchronous = false; req.mLaneId = u8(LaneId::_4); req.mThread = mResourceControlThread; req.mDelegate = &mUnitClearCacheForSyncFn.fn; req.mPostRunCallback = &mUnitClearCacheForSyncFn.cb; req.mUserData = *p_unit; req.mName = "ClearCache"; if (task) task->submitRequest(req); else (*p_unit)->mTask3.submitRequest(req); } *p_unit = nullptr; } void ResourceMgrTask::requestClearCacheForSync(ResourceUnit** p_unit, bool clear_immediately, bool delete_immediately) { if (!p_unit || !*p_unit || !(*p_unit)->isStatusFlag8000Set()) { goto fail; } if ((*p_unit)->isStatus1() || !(*p_unit)->mCache || (*p_unit)->getRefCount() > 0) return; if ((*p_unit)->isStatusFlag10000Set()) { fail: stubbedLogFunction(); return; } (*p_unit)->setStatusFlag10000(); if (clear_immediately) { (*p_unit)->removeTask3FromQueue(); (*p_unit)->clearCacheForSync(true); (*p_unit)->clearCache(nullptr); static_cast((*p_unit)->getStatus()); ResourceUnit* ptr = *p_unit; if (delete_immediately) deleteUnit(ptr, true); else requestDeleteUnit(&ptr); } else { ControlTaskRequest req; req.mHasHandle = true; req.mSynchronous = true; req.mLaneId = u8(LaneId::_4); req.mThread = mResourceControlThread; req.mDelegate = &mUnitClearCacheForSyncFn.fn; req.mUserData = *p_unit; req.mName = "ClearCache(ForSync)"; (*p_unit)->mTask3.submitRequest(req); } *p_unit = nullptr; } #ifdef MATCHING_HACK_NX_CLANG [[gnu::noinline]] #endif void ResourceMgrTask::deleteUnit(ResourceUnit*& unit, bool sync) { if (!unit) return; const bool immediate = mResourceControlThread->isPaused() || sync; if (!immediate && res::returnFalse()) stubbedLogFunction(); unit->unloadArchiveRes(); if (immediate) { mUnitPool.freeForSync(unit); unit = nullptr; } else { mUnitPool.free(unit); unit = nullptr; if (res::returnFalse()) stubbedLogFunction(); } } void ResourceMgrTask::requestDeleteUnit(ResourceUnit** p_unit) { if (!p_unit || !*p_unit) { stubbedLogFunction(); return; } if (mResourceControlThread->isPaused()) { if (res::returnFalse()) stubbedLogFunction(); deleteUnit(*p_unit, false); } else { ControlTaskRequest req; req.mHasHandle = false; req.mSynchronous = false; req.mLaneId = u8(LaneId::_5); req.mThread = mResourceControlThread; req.mDelegate = &mUnitDeleteFn; req.mName = "DeleteUnit"; req.mUserData = *p_unit; (*p_unit)->mTask1.submitRequest(req); } *p_unit = nullptr; } bool ResourceMgrTask::canUseSdCard() const { return false; } bool ResourceMgrTask::isHostPath(const sead::SafeString&) const { return false; } bool ResourceMgrTask::calc_(void*) { if (mCacheControlFlags.testAndClear(CacheControlFlag::ClearAllCachesRequested)) { MemoryTaskRequest req; req.mLaneId = u8(LaneId::_9); req.mHasHandle = true; req.mSynchronous = true; req.mThread = mResourceMemoryThread; req.mDelegate = &mClearAllCachesFn; req.mName = "ClearAllCaches"; req.mData_8 = false; req.mData_c = -1; util::TaskMgrRequest request; request.request = &req; mResourceMemoryTaskMgr->submitRequest(request); mTexHandleMgr->clearAllCache(); stubbedLogFunction(); } clearUnits_(); return true; } bool ResourceMgrTask::dropSFromExtensionIfNeeded(const sead::SafeString& path, sead::BufferedSafeString& new_path, s32 dot_idx, const sead::SafeString& extension) const { if (extension == "sbfevfl" || extension == "sbcamanim" || extension == "sbarslist") { new_path.copyAtWithTerminate(0, path, dot_idx); new_path.appendWithFormat(".%s", &extension.at(1)); return true; } return mExtensions2.binarySearch(&extension) != -1; } void ResourceMgrTask::unloadSeadResource(sead::Resource* resource) { if (res::returnFalse()) stubbedLogFunction(); sead::ResourceMgr::instance()->unload(resource); stubbedLogFunction(); if (res::returnFalse()) stubbedLogFunction(); } u32 ResourceMgrTask::getResourceSize(const sead::SafeString& name, void* userdata) const { if (!userdata) return mResourceInfoContainer.getResourceSize(name); mFileDevicePrefixesLock.readLock(); for (const auto& entry : mFileDevicePrefixes) { if (entry.getUserData() == userdata) { const u32 size = mResourceInfoContainer.getResourceSize(entry.getPrefix(), name); if (size == 0 && entry.getField28()) break; mFileDevicePrefixesLock.readUnlock(); return size; } } mFileDevicePrefixesLock.readUnlock(); return mResourceInfoContainer.getResourceSize(name); } void ResourceMgrTask::registerFileDevicePrefix(FileDevicePrefix& prefix) { mFileDevicePrefixesLock.writeLock(); mFileDevicePrefixes.pushBack(&prefix); mFileDevicePrefixesLock.writeUnlock(); } void ResourceMgrTask::deregisterFileDevicePrefix(FileDevicePrefix& prefix) { mFileDevicePrefixesLock.writeLock(); mFileDevicePrefixes.erase(&prefix); mFileDevicePrefixesLock.writeUnlock(); } void ResourceMgrTask::callStubbedFunctionOnArenas() { auto lock = sead::makeScopedLock(mArenasCS); for (OverlayArena& arena : mArenas) { if (arena.isFlag8Set()) arena.stubbed(); } } void ResourceMgrTask::updateResourceArenasFlag8() { mArenaForResourceS.updateFlag8(false); mArenaForResourceL.updateFlag8(false); } // NON_MATCHING: branching sead::Heap* ResourceMgrTask::makeHeapForUnit(const MakeHeapArg& arg) { const auto heap_size = arg.heap_size; const auto path = arg.path; OverlayArena* arena = arg.arena; if (!arena) { if (heap_size > 0x80000) arena = &mArenaForResourceL; else arena = &mArenaForResourceS; } sead::Heap* const heap = arena->makeDualHeap(heap_size, path, sead::Heap::cHeapDirection_Forward, arg.unit, false); if (!heap) { static_cast(arena->isFlag10Set()); *arg.out_arena1 = arena; return nullptr; } if (arg.out_arena2 == nullptr) return heap; *arg.out_arena1 = arena; *arg.out_arena2 = arena; return heap; } ResourceUnit* ResourceMgrTask::clearCachesAndGetUnit(const GetUnitArg& arg) { auto* unit = mUnitPool.tryAlloc(); if (!unit) { util::TaskQueueLock lock; auto* queue = mResourceControlThread->getTaskQueue(); auto it = queue->activeTasksRobustBegin(&lock); const auto end = queue->activeTasksRobustEnd(); while (it != end && it->getLaneId() >= u8(ResourceMgrTask::LaneId::_5)) { it->removeFromQueue2(); it->processOnCurrentThreadDirectly(mResourceControlThread); ++it; } unit = mUnitPool.tryAlloc(); } if (!unit) { ClearCachesTaskData data; data._8 = true; data._c = 100; util::TaskRequest req; req.mLaneId = u8(LaneId::_8); req.mHasHandle = true; req.mSynchronous = true; req.mThread = mResourceMemoryThread; req.mDelegate = &mClearCachesFn; req.mUserData = &data; req.mName = "ClearCaches"; mTask3->submitRequest(req); unit = mUnitPool.tryAlloc(); } if (!unit) { util::TaskQueueLock lock; auto* queue = mResourceControlThread->getTaskQueue(); auto it = queue->activeTasksRobustBegin(&lock); const auto end = queue->activeTasksRobustEnd(); while (it != end && it->getLaneId() >= u8(ResourceMgrTask::LaneId::_5)) { it->removeFromQueue2(); it->processOnCurrentThreadDirectly(mResourceControlThread); ++it; } unit = mUnitPool.alloc(); } if (!unit->init(*arg.unit_init_arg)) return nullptr; return unit; } void ResourceMgrTask::setActorCreateInitializerThreads( const SetActorCreateInitializerThreadsArg& arg) { mFlags.set(Flag::_8); mActorCreateInitializerThreads = arg.threads; stubbedLogFunction(); } void ResourceMgrTask::clearActorCreateInitializerThreads() { mFlags.reset(Flag::_8); mActorCreateInitializerThreads = nullptr; stubbedLogFunction(); } void ResourceMgrTask::pauseThreads() { stubbedLogFunction(); mMovableMemoryThread->pauseAndWaitForAck(); mMovableMemoryThread->cancelTasks(3); mResourceControlThread->pauseAndWaitForAck(); mResourceMemoryThread->pauseAndWaitForAck(); mResourceLoadingThread->pauseAndWaitForAck(); stubbedLogFunction(); } void ResourceMgrTask::resumeThreads() { stubbedLogFunction(); mResourceLoadingThread->resume(); mResourceMemoryThread->resume(); mResourceControlThread->resume(); mMovableMemoryThread->resume(); } sead::SZSDecompressor* ResourceMgrTask::getSzsDecompressor() { mSzsDecompressorCS.lock(); sead::SZSDecompressor* ptr = nullptr; OverlayArenaSystem::instance()->getSzsDecompressor(&ptr); return ptr; } void ResourceMgrTask::unlockSzsDecompressorCS() { mSzsDecompressorCS.unlock(); } bool ResourceMgrTask::getUncompressedSize(u32* size, const sead::SafeString& path, sead::FileDevice* device) const { auto lock = sead::makeScopedLock(mSzsDecompressorCS); if (!device) device = mSeadMainFileDevice; sead::FileHandle handle; if (!device->tryOpen(&handle, path, sead::FileDevice::cFileOpenFlag_ReadOnly)) { stubbedLogFunction(); return false; } u32 read_size = 0; handle.tryRead(&read_size, mOffsetReadBuf, 0x10); *size = sead::Mathu::roundUpPow2(sead::SZSDecompressor::getDecompSize(mOffsetReadBuf), 32); return true; } // NON_MATCHING: reordering void ResourceMgrTask::setCompactionStopped(bool stopped) { u32 old_counter; if (stopped) old_counter = mCompactionCounter.decrement(); else old_counter = mCompactionCounter.increment(); stubbedLogFunction(); if (mCompactionCounter == 0 || old_counter == 0) stubbedLogFunction(); } bool ResourceMgrTask::isCompactionStopped() const { return mCompactionCounter == 0; } bool ResourceMgrTask::initTempResourceLoader(TempResourceLoader* loader, TempResourceLoader::InitArg& arg) { arg.work = mTexHandleMgr->getArchiveWork(); return loader->init(arg); } bool ResourceMgrTask::returnTrue1() { return true; } void ResourceMgrTask::clearCacheWithFileExtension(const sead::SafeString& extension) { stubbedLogFunction(); const s32 idx = getCacheIdx(extension); stubbedLogFunction(); mCaches[idx]->eraseUnits(); } void ResourceMgrTask::clearAllCachesSynchronously(OverlayArena* arena) { mResourceControlThread->getTaskQueue()->waitForLaneToEmpty(u8(LaneId::_5)); mResourceControlThread->getTaskQueue()->waitForLaneToEmpty(u8(LaneId::_4)); mResourceControlThread->getTaskQueue()->waitForLaneToEmpty(u8(LaneId::_3)); MemoryTaskRequest req; req.mLaneId = u8(LaneId::_9); req.mHasHandle = true; req.mSynchronous = true; req.mThread = mResourceMemoryThread; req.mDelegate = &mClearAllCachesFn; req.mName = "ClearAllCaches"; req.mData_8 = false; req.mData_c = -1; req.mData_mStr = arena->getHeap()->getName(); util::TaskMgrRequest task_mgr_request; task_mgr_request.request = &req; mResourceMemoryTaskMgr->submitRequest(task_mgr_request); } bool ResourceMgrTask::returnTrue() { return true; } void ResourceMgrTask::removeOverlayArena(OverlayArena* arena) { mTask.removeFromQueue(); auto lock = sead::makeScopedLock(mArenasCS); mArenaIdx = 0; if (mArenas.isNodeLinked(arena)) { mArenas.erase(arena); stubbedLogFunction(); } } } // namespace ksys::res