botw/src/KingSystem/Resource/resResourceMgrTask.cpp

690 lines
20 KiB
C++

#include "KingSystem/Resource/resResourceMgrTask.h"
#include <framework/seadHeapPolicies.h>
#include <framework/seadTaskID.h>
#include <resource/seadResourceMgr.h>
#include <resource/seadSZSDecompressor.h>
#include <thread/seadThreadUtil.h>
#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<void>(_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<void>((*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<void>(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