// Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors. // All rights reserved. // Code licensed under the BSD License. // http://www.anki3d.org/LICENSE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace anki { ResourceManager::ResourceManager() { } ResourceManager::~ResourceManager() { ANKI_RESOURCE_LOGI("Destroying resource manager"); AsyncLoader::freeSingleton(); ShaderProgramResourceSystem::freeSingleton(); TransferGpuAllocator::freeSingleton(); ResourceFilesystem::freeSingleton(); #define ANKI_INSTANTIATE_RESOURCE(className) \ static_cast&>(m_allTypes).m_entries.destroy(); \ static_cast&>(m_allTypes).m_map.destroy(); #include AccelerationStructureScratchAllocator::freeSingleton(); ResourceMemoryPool::freeSingleton(); } Error ResourceManager::init(AllocAlignedCallback allocCallback, void* allocCallbackData) { ANKI_RESOURCE_LOGI("Initializing resource manager"); ResourceMemoryPool::allocateSingleton(allocCallback, allocCallbackData); ResourceFilesystem::allocateSingleton(); ANKI_CHECK(ResourceFilesystem::getSingleton().init()); // Init the thread AsyncLoader::allocateSingleton(); TransferGpuAllocator::allocateSingleton(); ANKI_CHECK(TransferGpuAllocator::getSingleton().init(g_cvarRsrcTransferScratchMemorySize)); // Init the programs ShaderProgramResourceSystem::allocateSingleton(); ANKI_CHECK(ShaderProgramResourceSystem::getSingleton().init()); if(GrManager::getSingleton().getDeviceCapabilities().m_rayTracingEnabled) { AccelerationStructureScratchAllocator::allocateSingleton(); } m_trackFileUpdateTimes = g_cvarRsrcTrackFileUpdates; return Error::kNone; } template Error ResourceManager::loadResource(CString filename, ResourcePtr& out, Bool async) { ANKI_ASSERT(!out.isCreated() && "Already loaded"); TypeData& type = static_cast&>(m_allTypes); // Check if registered using EntryType = typename TypeData::Entry; EntryType* entry; { RLockGuard lock(type.m_mtx); auto it = type.m_map.find(filename); entry = (it != type.m_map.getEnd()) ? &type.m_entries[*it] : nullptr; } if(!entry) { // Resource entry doesn't exist, create one WLockGuard lock(type.m_mtx); auto it = type.m_map.find(filename); // Search again if(it != type.m_map.getEnd()) { entry = &type.m_entries[*it]; } else { auto arrit = type.m_entries.emplace(); type.m_map.emplace(filename, arrit.getArrayIndex()); entry = &(*arrit); } } ANKI_ASSERT(entry); // Try to load the resource Error err = Error::kNone; T* rsrc = nullptr; { LockGuard lock(entry->m_mtx); if(entry->m_resources.getSize() == 0 || entry->m_resources.getBack()->isObsolete()) { // Resource hasn't been loaded or it needs update, load it rsrc = newInstance(ResourceMemoryPool::getSingleton(), filename, m_uuid.fetchAdd(1)); // Increment the refcount in that case where async jobs increment it and decrement it in the scope of a load() rsrc->retain(); err = rsrc->load(filename, async); // Decrement because of the increment happened a few lines above rsrc->release(); if(err) { ANKI_RESOURCE_LOGE("Failed to load resource: %s", filename.cstr()); deleteInstance(ResourceMemoryPool::getSingleton(), rsrc); rsrc = nullptr; } else { entry->m_resources.emplaceBack(rsrc); } if(m_trackFileUpdateTimes) { entry->m_fileUpdateTime = ResourceFilesystem::getSingleton().getFileUpdateTime(filename); } } else { rsrc = entry->m_resources.getBack(); } if(!err) { ANKI_ASSERT(rsrc); out.reset(rsrc); } } return err; } template void ResourceManager::freeResource(T* ptr) { ANKI_ASSERT(ptr); ANKI_ASSERT(ptr->m_refcount.load() == 0); TypeData& type = static_cast&>(m_allTypes); typename TypeData::Entry* entry = nullptr; { RLockGuard lock(type.m_mtx); auto it = type.m_map.find(ptr->m_fname); ANKI_ASSERT(it != type.m_map.getEnd()); entry = &type.m_entries[*it]; } { LockGuard lock(entry->m_mtx); auto it = entry->m_resources.getBegin(); for(; it != entry->m_resources.getEnd(); ++it) { if(*it == ptr) { break; } } ANKI_ASSERT(it != entry->m_resources.getEnd()); deleteInstance(ResourceMemoryPool::getSingleton(), *it); entry->m_resources.erase(it); } } // Instansiate #define ANKI_INSTANTIATE_RESOURCE(className) \ template Error ResourceManager::loadResource(CString filename, ResourcePtr & out, Bool async); \ template void ResourceManager::freeResource(className * ptr); #include template void ResourceManager::refreshFileUpdateTimesInternal() { TypeData& type = static_cast&>(m_allTypes); WLockGuard lock(type.m_mtx); for(auto& entry : type.m_entries) { LockGuard lock(entry.m_mtx); if(entry.m_resources.getSize() == 0) { continue; } const U64 newTime = ResourceFilesystem::getSingleton().getFileUpdateTime(entry.m_resources[0]->getFilename()); if(newTime != entry.m_fileUpdateTime) { ANKI_RESOURCE_LOGV("File updated, loaded resource now obsolete: %s", entry.m_resources[0]->getFilename().cstr()); entry.m_fileUpdateTime = newTime; for(T* rsrc : entry.m_resources) { rsrc->m_isObsolete.store(1); } } } } void ResourceManager::refreshFileUpdateTimes() { if(!m_trackFileUpdateTimes) { return; } #define ANKI_INSTANTIATE_RESOURCE(className) refreshFileUpdateTimesInternal(); #include } } // end namespace anki