2016-03-26 00:51:59 +00:00
|
|
|
#include "ProjectResourceFactoryBase.hpp"
|
|
|
|
#include "Runtime/IObj.hpp"
|
|
|
|
|
2017-12-30 01:09:45 +00:00
|
|
|
#undef min
|
|
|
|
#undef max
|
|
|
|
|
2016-03-26 00:51:59 +00:00
|
|
|
namespace urde
|
|
|
|
{
|
|
|
|
static logvisor::Module Log("urde::ProjectResourceFactoryBase");
|
|
|
|
|
|
|
|
void ProjectResourceFactoryBase::BeginBackgroundIndex
|
2016-03-28 04:36:55 +00:00
|
|
|
(hecl::Database::Project& proj,
|
2016-03-26 00:51:59 +00:00
|
|
|
const hecl::Database::DataSpecEntry& origSpec,
|
|
|
|
const hecl::Database::DataSpecEntry& pcSpec)
|
|
|
|
{
|
|
|
|
CancelBackgroundIndex();
|
|
|
|
m_proj = &proj;
|
|
|
|
m_origSpec = &origSpec;
|
|
|
|
m_pcSpec = &pcSpec;
|
2016-03-28 04:36:55 +00:00
|
|
|
m_cookSpec.reset(pcSpec.m_factory(proj, hecl::Database::DataSpecTool::Cook));
|
2017-10-25 07:47:49 +00:00
|
|
|
return static_cast<DataSpec::SpecBase&>(*m_cookSpec).beginBackgroundIndex();
|
2016-03-28 04:36:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ProjectResourceFactoryBase::SyncCook(const hecl::ProjectPath& working)
|
|
|
|
{
|
2017-11-13 06:19:18 +00:00
|
|
|
Log.report(logvisor::Warning, _S("sync-cooking %s"), working.getRelativePath().data());
|
2017-12-29 08:08:12 +00:00
|
|
|
return m_clientProc.syncCook(working, m_cookSpec.get(), hecl::blender::SharedBlenderToken);
|
2016-03-28 04:36:55 +00:00
|
|
|
}
|
|
|
|
|
2016-03-29 23:14:14 +00:00
|
|
|
CFactoryFnReturn ProjectResourceFactoryBase::BuildSync(const SObjectTag& tag,
|
|
|
|
const hecl::ProjectPath& path,
|
2016-09-02 19:32:57 +00:00
|
|
|
const CVParamTransfer& paramXfer,
|
|
|
|
CObjectReference* selfRef)
|
2016-03-26 00:51:59 +00:00
|
|
|
{
|
|
|
|
/* Ensure cooked rep is on the filesystem */
|
2016-07-23 21:41:18 +00:00
|
|
|
std::experimental::optional<athena::io::FileReader> fr;
|
|
|
|
if (!PrepForReadSync(tag, path, fr))
|
2016-03-26 00:51:59 +00:00
|
|
|
return {};
|
|
|
|
|
|
|
|
/* All good, build resource */
|
2016-03-29 23:14:14 +00:00
|
|
|
if (m_factoryMgr.CanMakeMemory(tag))
|
|
|
|
{
|
2016-07-23 21:41:18 +00:00
|
|
|
u32 length = fr->length();
|
|
|
|
std::unique_ptr<u8[]> memBuf = fr->readUBytes(length);
|
2017-02-15 08:29:23 +00:00
|
|
|
CFactoryFnReturn ret =
|
|
|
|
m_factoryMgr.MakeObjectFromMemory(tag, std::move(memBuf), length, false, paramXfer, selfRef);
|
|
|
|
Log.report(logvisor::Info, "sync-built %.4s %08X",
|
2017-11-13 06:19:18 +00:00
|
|
|
tag.type.getChars(), u32(tag.id.Value()));
|
2017-02-15 08:29:23 +00:00
|
|
|
return ret;
|
2016-03-29 23:14:14 +00:00
|
|
|
}
|
|
|
|
|
2017-02-15 08:29:23 +00:00
|
|
|
CFactoryFnReturn ret = m_factoryMgr.MakeObject(tag, *fr, paramXfer, selfRef);
|
|
|
|
Log.report(logvisor::Info, "sync-built %.4s %08X",
|
2017-11-13 06:19:18 +00:00
|
|
|
tag.type.getChars(), u32(tag.id.Value()));
|
2017-02-15 08:29:23 +00:00
|
|
|
return ret;
|
2016-03-26 00:51:59 +00:00
|
|
|
}
|
|
|
|
|
2016-03-31 21:06:41 +00:00
|
|
|
void ProjectResourceFactoryBase::AsyncTask::EnsurePath(const urde::SObjectTag& tag,
|
|
|
|
const hecl::ProjectPath& path)
|
2016-03-28 04:36:55 +00:00
|
|
|
{
|
|
|
|
if (!m_workingPath)
|
|
|
|
{
|
|
|
|
m_workingPath = path;
|
|
|
|
|
|
|
|
/* Ensure requested resource is on the filesystem */
|
2016-09-21 05:41:51 +00:00
|
|
|
if (!path.isFileOrGlob())
|
2016-03-28 04:36:55 +00:00
|
|
|
{
|
|
|
|
Log.report(logvisor::Error, _S("unable to find resource path '%s'"),
|
2017-11-13 06:19:18 +00:00
|
|
|
path.getRelativePath().data());
|
2016-03-31 21:06:41 +00:00
|
|
|
m_failed = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-02-15 06:02:30 +00:00
|
|
|
/* Cached resolve (try PC first, then original) */
|
|
|
|
m_cookedPath = path.getCookedPath(*m_parent.m_pcSpec);
|
|
|
|
if (!m_cookedPath.isFile())
|
|
|
|
m_cookedPath = path.getCookedPath(*m_parent.m_origSpec);
|
2016-09-18 23:47:48 +00:00
|
|
|
if (!m_cookedPath.isFile() ||
|
2016-03-28 04:36:55 +00:00
|
|
|
m_cookedPath.getModtime() < path.getModtime())
|
|
|
|
{
|
2017-02-15 06:02:30 +00:00
|
|
|
/* Last chance type validation */
|
2017-12-29 08:08:12 +00:00
|
|
|
urde::SObjectTag verifyTag = m_parent.TagFromPath(path, hecl::blender::SharedBlenderToken);
|
2017-02-15 06:02:30 +00:00
|
|
|
if (verifyTag.type != tag.type)
|
|
|
|
{
|
|
|
|
Log.report(logvisor::Error, _S("%s: expected type '%.4s', found '%.4s'"),
|
2017-11-13 06:19:18 +00:00
|
|
|
path.getRelativePath().data(),
|
|
|
|
tag.type.getChars(), verifyTag.type.getChars());
|
2017-02-15 06:02:30 +00:00
|
|
|
m_failed = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get cooked representation path */
|
|
|
|
m_cookedPath = m_parent.GetCookedPath(path, true);
|
|
|
|
|
|
|
|
/* Perform mod-time comparison */
|
|
|
|
if (!m_cookedPath.isFile() ||
|
|
|
|
m_cookedPath.getModtime() < path.getModtime())
|
|
|
|
{
|
|
|
|
/* Start a background cook here */
|
|
|
|
m_cookTransaction = m_parent.m_clientProc.addCookTransaction(path, m_parent.m_cookSpec.get());
|
|
|
|
return;
|
|
|
|
}
|
2016-03-28 04:36:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CookComplete();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProjectResourceFactoryBase::AsyncTask::CookComplete()
|
|
|
|
{
|
|
|
|
/* Ensure cooked rep is on the filesystem */
|
|
|
|
athena::io::FileReader fr(m_cookedPath.getAbsolutePath(), 32 * 1024, false);
|
|
|
|
if (fr.hasError())
|
|
|
|
{
|
|
|
|
Log.report(logvisor::Error, _S("unable to open cooked resource path '%s'"),
|
2017-11-13 06:19:18 +00:00
|
|
|
m_cookedPath.getAbsolutePath().data());
|
2016-03-28 04:36:55 +00:00
|
|
|
m_failed = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Ready for buffer transaction at this point */
|
2016-07-30 03:53:45 +00:00
|
|
|
u32 availSz = std::max(0, s32(fr.length()) - s32(x14_resOffset));
|
2016-08-31 23:31:12 +00:00
|
|
|
x14_resSize = std::min(x14_resSize, availSz);
|
2017-02-27 05:25:14 +00:00
|
|
|
if (xc_targetDataRawPtr)
|
|
|
|
{
|
|
|
|
m_bufTransaction = m_parent.m_clientProc.addBufferTransaction(m_cookedPath,
|
|
|
|
xc_targetDataRawPtr,
|
|
|
|
x14_resSize, x14_resOffset);
|
|
|
|
}
|
2017-10-22 06:11:22 +00:00
|
|
|
else if (xc_targetDataPtr || xc_targetObjPtr)
|
2017-02-27 05:25:14 +00:00
|
|
|
{
|
|
|
|
x10_loadBuffer.reset(new u8[x14_resSize]);
|
|
|
|
m_bufTransaction = m_parent.m_clientProc.addBufferTransaction(m_cookedPath,
|
|
|
|
x10_loadBuffer.get(),
|
|
|
|
x14_resSize, x14_resOffset);
|
|
|
|
}
|
2017-10-22 06:11:22 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Skip buffer transaction if no target pointers set */
|
|
|
|
m_complete = true;
|
|
|
|
}
|
2016-03-28 04:36:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ProjectResourceFactoryBase::AsyncTask::AsyncPump()
|
|
|
|
{
|
2017-10-22 06:11:22 +00:00
|
|
|
if (m_failed || m_complete)
|
2016-03-28 04:36:55 +00:00
|
|
|
return true;
|
|
|
|
|
|
|
|
if (m_bufTransaction)
|
|
|
|
{
|
|
|
|
if (m_bufTransaction->m_complete)
|
2016-03-28 21:38:48 +00:00
|
|
|
{
|
|
|
|
m_complete = true;
|
2016-03-28 04:36:55 +00:00
|
|
|
return true;
|
2016-03-28 21:38:48 +00:00
|
|
|
}
|
2016-03-28 04:36:55 +00:00
|
|
|
}
|
|
|
|
else if (m_cookTransaction)
|
|
|
|
{
|
|
|
|
if (m_cookTransaction->m_complete)
|
|
|
|
CookComplete();
|
|
|
|
}
|
|
|
|
|
|
|
|
return m_failed;
|
|
|
|
}
|
|
|
|
|
2017-10-27 10:10:32 +00:00
|
|
|
void ProjectResourceFactoryBase::AsyncTask::WaitUntilComplete()
|
2017-02-19 09:27:01 +00:00
|
|
|
{
|
2017-10-22 06:11:22 +00:00
|
|
|
using ItType = std::unordered_map<SObjectTag,
|
|
|
|
std::list<std::shared_ptr<AsyncTask>>::iterator>::iterator;
|
|
|
|
ItType search = m_parent.m_asyncLoadMap.find(x0_tag);
|
|
|
|
if (search == m_parent.m_asyncLoadMap.end())
|
2017-02-19 09:27:01 +00:00
|
|
|
return;
|
|
|
|
for (ItType tmp = search ; !m_parent.AsyncPumpTask(tmp) ; tmp = search)
|
|
|
|
{std::this_thread::sleep_for(std::chrono::milliseconds(2));}
|
|
|
|
}
|
|
|
|
|
2017-10-22 06:11:22 +00:00
|
|
|
using AsyncTask = ProjectResourceFactoryBase::AsyncTask;
|
|
|
|
|
|
|
|
std::shared_ptr<AsyncTask>
|
|
|
|
ProjectResourceFactoryBase::_AddTask(const std::shared_ptr<AsyncTask>& ptr)
|
|
|
|
{
|
|
|
|
m_asyncLoadMap.insert({ptr->x0_tag, m_asyncLoadList.insert(m_asyncLoadList.end(), ptr)});
|
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::list<std::shared_ptr<AsyncTask>>::iterator
|
|
|
|
ProjectResourceFactoryBase::_RemoveTask(std::list<std::shared_ptr<AsyncTask>>::iterator it)
|
|
|
|
{
|
|
|
|
m_asyncLoadMap.erase((*it)->x0_tag);
|
|
|
|
return m_asyncLoadList.erase(it);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unordered_map<SObjectTag, std::list<std::shared_ptr<AsyncTask>>::iterator>::iterator
|
|
|
|
ProjectResourceFactoryBase::_RemoveTask(std::unordered_map<SObjectTag,
|
|
|
|
std::list<std::shared_ptr<AsyncTask>>::iterator>::iterator it)
|
|
|
|
{
|
|
|
|
if (it != m_asyncLoadMap.end())
|
|
|
|
{
|
|
|
|
m_asyncLoadList.erase(it->second);
|
|
|
|
return m_asyncLoadMap.erase(it);
|
|
|
|
}
|
|
|
|
return it;
|
|
|
|
};
|
|
|
|
|
|
|
|
std::unordered_map<SObjectTag, std::list<std::shared_ptr<AsyncTask>>::iterator>::iterator
|
|
|
|
ProjectResourceFactoryBase::_RemoveTask(const SObjectTag& tag)
|
|
|
|
{
|
|
|
|
return _RemoveTask(m_asyncLoadMap.find(tag));
|
|
|
|
};
|
|
|
|
|
2016-07-23 21:41:18 +00:00
|
|
|
bool
|
|
|
|
ProjectResourceFactoryBase::PrepForReadSync(const SObjectTag& tag,
|
|
|
|
const hecl::ProjectPath& path,
|
|
|
|
std::experimental::optional<athena::io::FileReader>& fr)
|
|
|
|
{
|
|
|
|
/* Ensure requested resource is on the filesystem */
|
2016-09-21 05:41:51 +00:00
|
|
|
if (!path.isFileOrGlob())
|
2016-07-23 21:41:18 +00:00
|
|
|
{
|
|
|
|
Log.report(logvisor::Error, _S("unable to find resource path '%s'"),
|
2017-11-13 06:19:18 +00:00
|
|
|
path.getAbsolutePath().data());
|
2016-07-23 21:41:18 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-02-15 06:02:30 +00:00
|
|
|
/* Cached resolve (try PC first, then original) */
|
|
|
|
hecl::ProjectPath cooked = path.getCookedPath(*m_pcSpec);
|
|
|
|
if (!cooked.isFile())
|
|
|
|
cooked = path.getCookedPath(*m_origSpec);
|
2016-09-18 23:47:48 +00:00
|
|
|
if (!cooked.isFile() ||
|
2016-07-23 21:41:18 +00:00
|
|
|
cooked.getModtime() < path.getModtime())
|
|
|
|
{
|
2017-02-15 06:02:30 +00:00
|
|
|
/* Last chance type validation */
|
2017-12-29 08:08:12 +00:00
|
|
|
urde::SObjectTag verifyTag = TagFromPath(path, hecl::blender::SharedBlenderToken);
|
2017-02-15 06:02:30 +00:00
|
|
|
if (verifyTag.type != tag.type)
|
2016-07-23 21:41:18 +00:00
|
|
|
{
|
2017-02-15 06:02:30 +00:00
|
|
|
Log.report(logvisor::Error, _S("%s: expected type '%.4s', found '%.4s'"),
|
2017-11-13 06:19:18 +00:00
|
|
|
path.getRelativePath().data(),
|
|
|
|
tag.type.getChars(), verifyTag.type.getChars());
|
2016-07-23 21:41:18 +00:00
|
|
|
return false;
|
|
|
|
}
|
2017-02-15 06:02:30 +00:00
|
|
|
|
|
|
|
/* Get cooked representation path */
|
|
|
|
cooked = GetCookedPath(path, true);
|
|
|
|
|
|
|
|
/* Perform mod-time comparison */
|
|
|
|
if (!cooked.isFile() ||
|
|
|
|
cooked.getModtime() < path.getModtime())
|
|
|
|
{
|
|
|
|
/* Do a blocking cook here */
|
|
|
|
if (!SyncCook(path))
|
|
|
|
{
|
|
|
|
Log.report(logvisor::Error, _S("unable to cook resource path '%s'"),
|
2017-11-13 06:19:18 +00:00
|
|
|
path.getAbsolutePath().data());
|
2017-02-15 06:02:30 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2016-07-23 21:41:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Ensure cooked rep is on the filesystem */
|
|
|
|
fr.emplace(cooked.getAbsolutePath(), 32 * 1024, false);
|
|
|
|
if (fr->hasError())
|
|
|
|
{
|
|
|
|
Log.report(logvisor::Error, _S("unable to open cooked resource path '%s'"),
|
2017-11-13 06:19:18 +00:00
|
|
|
cooked.getAbsolutePath().data());
|
2016-07-23 21:41:18 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<urde::IObj> ProjectResourceFactoryBase::Build(const urde::SObjectTag& tag,
|
2016-09-02 19:32:57 +00:00
|
|
|
const urde::CVParamTransfer& paramXfer,
|
|
|
|
CObjectReference* selfRef)
|
2016-07-23 21:41:18 +00:00
|
|
|
{
|
2017-08-13 05:26:14 +00:00
|
|
|
if (!tag.id.IsValid())
|
2017-11-13 06:19:18 +00:00
|
|
|
Log.report(logvisor::Fatal, "attempted to access null id on type '%.4s'", tag.type.getChars());
|
2016-10-08 20:32:36 +00:00
|
|
|
|
2016-07-23 21:41:18 +00:00
|
|
|
const hecl::ProjectPath* resPath = nullptr;
|
|
|
|
if (!WaitForTagReady(tag, resPath))
|
|
|
|
return {};
|
2017-10-22 06:11:22 +00:00
|
|
|
auto asyncSearch = m_asyncLoadMap.find(tag);
|
|
|
|
if (asyncSearch != m_asyncLoadMap.end())
|
2016-04-01 01:00:37 +00:00
|
|
|
{
|
|
|
|
/* Async spinloop */
|
2017-10-22 06:11:22 +00:00
|
|
|
AsyncTask& task = **asyncSearch->second;
|
2016-07-23 21:41:18 +00:00
|
|
|
task.EnsurePath(task.x0_tag, *resPath);
|
2016-04-01 01:00:37 +00:00
|
|
|
|
|
|
|
/* Pump load pipeline (cooking if needed) */
|
|
|
|
while (!task.AsyncPump()) {std::this_thread::sleep_for(std::chrono::milliseconds(2));}
|
|
|
|
|
2017-10-22 06:11:22 +00:00
|
|
|
if (task.m_complete && task.x10_loadBuffer)
|
2016-04-01 01:00:37 +00:00
|
|
|
{
|
|
|
|
/* Load complete, build resource */
|
|
|
|
std::unique_ptr<IObj> newObj;
|
|
|
|
if (m_factoryMgr.CanMakeMemory(task.x0_tag))
|
|
|
|
{
|
|
|
|
newObj = m_factoryMgr.MakeObjectFromMemory(tag, std::move(task.x10_loadBuffer),
|
2016-09-02 19:32:57 +00:00
|
|
|
task.x14_resSize, false, task.x18_cvXfer, selfRef);
|
2016-04-01 01:00:37 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
athena::io::MemoryReader mr(task.x10_loadBuffer.get(), task.x14_resSize);
|
2016-09-02 19:32:57 +00:00
|
|
|
newObj = m_factoryMgr.MakeObject(task.x0_tag, mr, task.x18_cvXfer, selfRef);
|
2016-04-01 01:00:37 +00:00
|
|
|
}
|
|
|
|
|
2017-10-26 10:09:51 +00:00
|
|
|
//*task.xc_targetObjPtr = newObj.get();
|
2016-04-01 01:00:37 +00:00
|
|
|
Log.report(logvisor::Warning, "spin-built %.4s %08X",
|
2017-11-13 06:19:18 +00:00
|
|
|
task.x0_tag.type.getChars(), u32(task.x0_tag.id.Value()));
|
2016-07-23 21:41:18 +00:00
|
|
|
|
2017-10-22 06:11:22 +00:00
|
|
|
_RemoveTask(asyncSearch);
|
2016-04-01 01:00:37 +00:00
|
|
|
return newObj;
|
|
|
|
}
|
2017-10-22 06:11:22 +00:00
|
|
|
else if (task.m_complete)
|
|
|
|
{
|
|
|
|
Log.report(logvisor::Error, "unable to spin-build %.4s %08X; Resource requested as cook-only",
|
2017-11-13 06:19:18 +00:00
|
|
|
task.x0_tag.type.getChars(), u32(task.x0_tag.id.Value()));
|
2017-10-22 06:11:22 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Log.report(logvisor::Error, "unable to spin-build %.4s %08X",
|
2017-11-13 06:19:18 +00:00
|
|
|
task.x0_tag.type.getChars(), u32(task.x0_tag.id.Value()));
|
2017-10-22 06:11:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_RemoveTask(asyncSearch);
|
2016-04-01 01:00:37 +00:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Fall-back to sync build */
|
2016-09-02 19:32:57 +00:00
|
|
|
return BuildSync(tag, *resPath, paramXfer, selfRef);
|
2016-03-26 00:51:59 +00:00
|
|
|
}
|
|
|
|
|
2017-10-22 06:11:22 +00:00
|
|
|
std::shared_ptr<AsyncTask>
|
2016-08-08 04:48:18 +00:00
|
|
|
ProjectResourceFactoryBase::BuildAsyncInternal(const urde::SObjectTag& tag,
|
|
|
|
const urde::CVParamTransfer& paramXfer,
|
2017-10-26 10:09:51 +00:00
|
|
|
std::unique_ptr<urde::IObj>* objOut,
|
2016-09-02 19:32:57 +00:00
|
|
|
CObjectReference* selfRef)
|
2016-08-08 04:48:18 +00:00
|
|
|
{
|
2017-10-22 06:11:22 +00:00
|
|
|
if (m_asyncLoadMap.find(tag) != m_asyncLoadMap.end())
|
2016-08-08 04:48:18 +00:00
|
|
|
return {};
|
2017-10-22 06:11:22 +00:00
|
|
|
return _AddTask(std::make_unique<AsyncTask>(*this, tag, objOut, paramXfer, selfRef));
|
2016-08-08 04:48:18 +00:00
|
|
|
}
|
|
|
|
|
2016-03-26 00:51:59 +00:00
|
|
|
void ProjectResourceFactoryBase::BuildAsync(const urde::SObjectTag& tag,
|
|
|
|
const urde::CVParamTransfer& paramXfer,
|
2017-10-26 10:09:51 +00:00
|
|
|
std::unique_ptr<urde::IObj>* objOut,
|
2016-09-02 19:32:57 +00:00
|
|
|
CObjectReference* selfRef)
|
2016-03-26 00:51:59 +00:00
|
|
|
{
|
2017-08-13 05:26:14 +00:00
|
|
|
if (!tag.id.IsValid())
|
2017-11-13 06:19:18 +00:00
|
|
|
Log.report(logvisor::Fatal, "attempted to access null id on type '%.4s'", tag.type.getChars());
|
2016-10-08 20:32:36 +00:00
|
|
|
|
2016-09-02 19:32:57 +00:00
|
|
|
BuildAsyncInternal(tag, paramXfer, objOut, selfRef);
|
2016-03-26 00:51:59 +00:00
|
|
|
}
|
|
|
|
|
2016-07-23 21:41:18 +00:00
|
|
|
u32 ProjectResourceFactoryBase::ResourceSize(const SObjectTag& tag)
|
|
|
|
{
|
2017-08-13 05:26:14 +00:00
|
|
|
if (!tag.id.IsValid())
|
2017-11-13 06:19:18 +00:00
|
|
|
Log.report(logvisor::Fatal, "attempted to access null id on type '%.4s'", tag.type.getChars());
|
2016-10-08 20:32:36 +00:00
|
|
|
|
2016-07-23 21:41:18 +00:00
|
|
|
/* Ensure resource at requested path is indexed and not cooking */
|
|
|
|
const hecl::ProjectPath* resPath = nullptr;
|
|
|
|
if (!WaitForTagReady(tag, resPath))
|
|
|
|
return {};
|
|
|
|
|
|
|
|
/* Ensure cooked rep is on the filesystem */
|
|
|
|
std::experimental::optional<athena::io::FileReader> fr;
|
|
|
|
if (!PrepForReadSync(tag, *resPath, fr))
|
|
|
|
return {};
|
|
|
|
|
|
|
|
return fr->length();
|
|
|
|
}
|
|
|
|
|
2017-10-27 10:10:32 +00:00
|
|
|
std::shared_ptr<urde::IDvdRequest>
|
|
|
|
ProjectResourceFactoryBase::LoadResourceAsync(const urde::SObjectTag& tag, void* target)
|
2016-07-23 21:41:18 +00:00
|
|
|
{
|
2017-08-13 05:26:14 +00:00
|
|
|
if (!tag.id.IsValid())
|
2016-10-08 20:32:36 +00:00
|
|
|
Log.report(logvisor::Fatal, "attempted to access null id");
|
2017-10-22 06:11:22 +00:00
|
|
|
if (m_asyncLoadMap.find(tag) != m_asyncLoadMap.end())
|
2016-08-08 04:48:18 +00:00
|
|
|
return {};
|
2017-10-27 10:10:32 +00:00
|
|
|
return std::static_pointer_cast<urde::IDvdRequest>(_AddTask(std::make_shared<AsyncTask>(*this, tag, reinterpret_cast<u8*>(target))));
|
2016-07-23 21:41:18 +00:00
|
|
|
}
|
|
|
|
|
2017-10-27 10:10:32 +00:00
|
|
|
std::shared_ptr<urde::IDvdRequest>
|
2016-08-08 04:48:18 +00:00
|
|
|
ProjectResourceFactoryBase::LoadResourcePartAsync(const urde::SObjectTag& tag,
|
2017-11-05 02:08:05 +00:00
|
|
|
u32 off, u32 size, void* target)
|
2017-02-27 05:25:14 +00:00
|
|
|
{
|
2017-08-13 05:26:14 +00:00
|
|
|
if (!tag.id.IsValid())
|
2017-02-27 05:25:14 +00:00
|
|
|
Log.report(logvisor::Fatal, "attempted to access null id");
|
2017-10-22 06:11:22 +00:00
|
|
|
if (m_asyncLoadMap.find(tag) != m_asyncLoadMap.end())
|
2017-02-27 05:25:14 +00:00
|
|
|
return {};
|
2017-10-27 10:10:32 +00:00
|
|
|
return std::static_pointer_cast<urde::IDvdRequest>(_AddTask(std::make_shared<AsyncTask>(*this, tag, reinterpret_cast<u8*>(target), size, off)));
|
2017-02-27 05:25:14 +00:00
|
|
|
}
|
|
|
|
|
2016-07-23 21:41:18 +00:00
|
|
|
std::unique_ptr<u8[]> ProjectResourceFactoryBase::LoadResourceSync(const urde::SObjectTag& tag)
|
|
|
|
{
|
2017-08-13 05:26:14 +00:00
|
|
|
if (!tag.id.IsValid())
|
2016-10-08 20:32:36 +00:00
|
|
|
Log.report(logvisor::Fatal, "attempted to access null id");
|
|
|
|
|
2016-07-23 21:41:18 +00:00
|
|
|
/* Ensure resource at requested path is indexed and not cooking */
|
|
|
|
const hecl::ProjectPath* resPath = nullptr;
|
|
|
|
if (!WaitForTagReady(tag, resPath))
|
|
|
|
return {};
|
|
|
|
|
|
|
|
/* Ensure cooked rep is on the filesystem */
|
|
|
|
std::experimental::optional<athena::io::FileReader> fr;
|
|
|
|
if (!PrepForReadSync(tag, *resPath, fr))
|
|
|
|
return {};
|
|
|
|
|
|
|
|
return fr->readUBytes(fr->length());
|
|
|
|
}
|
|
|
|
|
2017-11-05 02:08:05 +00:00
|
|
|
std::unique_ptr<u8[]> ProjectResourceFactoryBase::LoadNewResourcePartSync(const urde::SObjectTag& tag,
|
|
|
|
u32 off, u32 size)
|
2016-07-23 21:41:18 +00:00
|
|
|
{
|
2017-08-13 05:26:14 +00:00
|
|
|
if (!tag.id.IsValid())
|
2016-10-08 20:32:36 +00:00
|
|
|
Log.report(logvisor::Fatal, "attempted to access null id");
|
|
|
|
|
2016-07-23 21:41:18 +00:00
|
|
|
/* Ensure resource at requested path is indexed and not cooking */
|
|
|
|
const hecl::ProjectPath* resPath = nullptr;
|
|
|
|
if (!WaitForTagReady(tag, resPath))
|
|
|
|
return {};
|
|
|
|
|
|
|
|
/* Ensure cooked rep is on the filesystem */
|
|
|
|
std::experimental::optional<athena::io::FileReader> fr;
|
|
|
|
if (!PrepForReadSync(tag, *resPath, fr))
|
|
|
|
return {};
|
|
|
|
|
|
|
|
s32 sz = std::min(s32(size), std::max(0, s32(fr->length()) - s32(off)));
|
|
|
|
fr->seek(off, athena::SeekOrigin::Begin);
|
|
|
|
return fr->readUBytes(sz);
|
|
|
|
}
|
|
|
|
|
2017-10-22 06:11:22 +00:00
|
|
|
std::shared_ptr<AsyncTask>
|
|
|
|
ProjectResourceFactoryBase::CookResourceAsync(const urde::SObjectTag& tag)
|
|
|
|
{
|
|
|
|
if (!tag.id.IsValid())
|
|
|
|
Log.report(logvisor::Fatal, "attempted to access null id");
|
|
|
|
if (m_asyncLoadMap.find(tag) != m_asyncLoadMap.end())
|
|
|
|
return {};
|
|
|
|
return _AddTask(std::make_shared<AsyncTask>(*this, tag));
|
|
|
|
}
|
|
|
|
|
2016-03-26 00:51:59 +00:00
|
|
|
void ProjectResourceFactoryBase::CancelBuild(const urde::SObjectTag& tag)
|
|
|
|
{
|
2017-10-22 06:11:22 +00:00
|
|
|
_RemoveTask(tag);
|
2016-03-26 00:51:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ProjectResourceFactoryBase::CanBuild(const urde::SObjectTag& tag)
|
|
|
|
{
|
2017-08-13 05:26:14 +00:00
|
|
|
if (!tag.id.IsValid())
|
2016-10-08 20:32:36 +00:00
|
|
|
Log.report(logvisor::Fatal, "attempted to access null id");
|
|
|
|
|
2016-07-23 21:41:18 +00:00
|
|
|
const hecl::ProjectPath* resPath = nullptr;
|
|
|
|
if (!WaitForTagReady(tag, resPath))
|
|
|
|
return false;
|
2016-03-26 00:51:59 +00:00
|
|
|
|
2016-09-18 23:47:48 +00:00
|
|
|
if (resPath->isFile())
|
2016-03-26 00:51:59 +00:00
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-11-13 06:19:18 +00:00
|
|
|
const urde::SObjectTag* ProjectResourceFactoryBase::GetResourceIdByName(std::string_view name) const
|
2016-03-26 00:51:59 +00:00
|
|
|
{
|
2017-10-25 07:47:49 +00:00
|
|
|
return static_cast<DataSpec::SpecBase&>(*m_cookSpec).getResourceIdByName(name);
|
2016-03-26 00:51:59 +00:00
|
|
|
}
|
|
|
|
|
2017-08-13 05:26:14 +00:00
|
|
|
FourCC ProjectResourceFactoryBase::GetResourceTypeById(CAssetId id) const
|
2016-04-14 21:42:47 +00:00
|
|
|
{
|
2017-10-25 07:47:49 +00:00
|
|
|
return static_cast<DataSpec::SpecBase&>(*m_cookSpec).getResourceTypeById(id);
|
2016-04-14 21:42:47 +00:00
|
|
|
}
|
|
|
|
|
2016-09-25 01:58:20 +00:00
|
|
|
void ProjectResourceFactoryBase::EnumerateResources(const std::function<bool(const SObjectTag&)>& lambda) const
|
|
|
|
{
|
2017-10-25 07:47:49 +00:00
|
|
|
return static_cast<DataSpec::SpecBase&>(*m_cookSpec).enumerateResources(lambda);
|
2016-09-25 01:58:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ProjectResourceFactoryBase::EnumerateNamedResources(
|
2017-11-13 06:19:18 +00:00
|
|
|
const std::function<bool(std::string_view, const SObjectTag&)>& lambda) const
|
2016-09-25 01:58:20 +00:00
|
|
|
{
|
2017-10-25 07:47:49 +00:00
|
|
|
return static_cast<DataSpec::SpecBase&>(*m_cookSpec).enumerateNamedResources(lambda);
|
2016-09-25 01:58:20 +00:00
|
|
|
}
|
|
|
|
|
2017-10-22 06:11:22 +00:00
|
|
|
template <typename ItType>
|
|
|
|
bool ProjectResourceFactoryBase::AsyncPumpTask(ItType& it)
|
2017-02-19 09:27:01 +00:00
|
|
|
{
|
|
|
|
/* Ensure requested resource is in the index */
|
2017-10-22 06:11:22 +00:00
|
|
|
AsyncTask& task = _GetAsyncTask(it);
|
2017-10-25 07:47:49 +00:00
|
|
|
hecl::ProjectPath path = static_cast<DataSpec::SpecBase&>(*m_cookSpec).pathFromTag(task.x0_tag);
|
|
|
|
if (!path)
|
2017-02-19 09:27:01 +00:00
|
|
|
{
|
2017-10-25 07:47:49 +00:00
|
|
|
if (!static_cast<DataSpec::SpecBase&>(*m_cookSpec).backgroundIndexRunning())
|
2017-02-19 09:27:01 +00:00
|
|
|
{
|
2017-11-13 06:19:18 +00:00
|
|
|
Log.report(logvisor::Error, _S("unable to find async load resource (%.4s, %08X)"),
|
|
|
|
task.x0_tag.type.getChars(), task.x0_tag.id);
|
2017-10-22 06:11:22 +00:00
|
|
|
it = _RemoveTask(it);
|
2017-02-19 09:27:01 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2017-10-25 07:47:49 +00:00
|
|
|
task.EnsurePath(task.x0_tag, path);
|
2017-02-19 09:27:01 +00:00
|
|
|
|
|
|
|
/* Pump load pipeline (cooking if needed) */
|
|
|
|
if (task.AsyncPump())
|
|
|
|
{
|
|
|
|
if (task.m_complete)
|
|
|
|
{
|
|
|
|
/* Load complete, build resource */
|
|
|
|
if (task.xc_targetObjPtr)
|
|
|
|
{
|
|
|
|
/* Factory build */
|
|
|
|
std::unique_ptr<IObj> newObj;
|
|
|
|
if (m_factoryMgr.CanMakeMemory(task.x0_tag))
|
|
|
|
{
|
|
|
|
newObj = m_factoryMgr.MakeObjectFromMemory(task.x0_tag, std::move(task.x10_loadBuffer),
|
|
|
|
task.x14_resSize, false, task.x18_cvXfer,
|
|
|
|
task.m_selfRef);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
athena::io::MemoryReader mr(task.x10_loadBuffer.get(), task.x14_resSize);
|
|
|
|
newObj = m_factoryMgr.MakeObject(task.x0_tag, mr, task.x18_cvXfer, task.m_selfRef);
|
|
|
|
}
|
|
|
|
|
2017-10-26 10:09:51 +00:00
|
|
|
*task.xc_targetObjPtr = std::move(newObj);
|
2017-02-19 09:27:01 +00:00
|
|
|
Log.report(logvisor::Info, "async-built %.4s %08X",
|
2017-11-13 06:19:18 +00:00
|
|
|
task.x0_tag.type.getChars(),
|
2017-08-13 05:26:14 +00:00
|
|
|
u32(task.x0_tag.id.Value()));
|
2017-02-19 09:27:01 +00:00
|
|
|
}
|
|
|
|
else if (task.xc_targetDataPtr)
|
|
|
|
{
|
|
|
|
/* Buffer only */
|
|
|
|
*task.xc_targetDataPtr = std::move(task.x10_loadBuffer);
|
|
|
|
Log.report(logvisor::Info, "async-loaded %.4s %08X",
|
2017-11-13 06:19:18 +00:00
|
|
|
task.x0_tag.type.getChars(),
|
2017-08-13 05:26:14 +00:00
|
|
|
u32(task.x0_tag.id.Value()));
|
2017-02-27 05:25:14 +00:00
|
|
|
}
|
|
|
|
else if (task.xc_targetDataRawPtr)
|
|
|
|
{
|
|
|
|
/* Buffer only raw */
|
|
|
|
Log.report(logvisor::Info, "async-loaded %.4s %08X",
|
2017-11-13 06:19:18 +00:00
|
|
|
task.x0_tag.type.getChars(),
|
2017-08-13 05:26:14 +00:00
|
|
|
u32(task.x0_tag.id.Value()));
|
2017-02-19 09:27:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-22 06:11:22 +00:00
|
|
|
it = _RemoveTask(it);
|
2017-02-19 09:27:01 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
++it;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-10-22 06:11:22 +00:00
|
|
|
template bool ProjectResourceFactoryBase::AsyncPumpTask<std::list<std::shared_ptr<AsyncTask>>::iterator>(
|
|
|
|
std::list<std::shared_ptr<AsyncTask>>::iterator& it);
|
|
|
|
template bool ProjectResourceFactoryBase::AsyncPumpTask<std::unordered_map<SObjectTag,
|
|
|
|
std::list<std::shared_ptr<AsyncTask>>::iterator>::iterator>(
|
|
|
|
std::unordered_map<SObjectTag, std::list<std::shared_ptr<AsyncTask>>::iterator>::iterator& it);
|
|
|
|
|
2016-03-26 00:51:59 +00:00
|
|
|
void ProjectResourceFactoryBase::AsyncIdle()
|
|
|
|
{
|
2016-03-28 04:36:55 +00:00
|
|
|
/* Consume completed transactions, they will be processed this cycle at the latest */
|
2016-04-19 00:17:49 +00:00
|
|
|
std::list<std::shared_ptr<hecl::ClientProcess::Transaction>> completed;
|
2016-03-28 04:36:55 +00:00
|
|
|
m_clientProc.swapCompletedQueue(completed);
|
|
|
|
|
|
|
|
/* Begin self-profiling loop */
|
2016-03-26 00:51:59 +00:00
|
|
|
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
|
2016-03-28 04:36:55 +00:00
|
|
|
for (auto it=m_asyncLoadList.begin() ; it != m_asyncLoadList.end() ;)
|
2016-03-26 00:51:59 +00:00
|
|
|
{
|
|
|
|
/* Allow 8 milliseconds (roughly 1/2 frame-time) for each async build cycle */
|
2017-02-15 06:02:30 +00:00
|
|
|
std::chrono::steady_clock::time_point resStart = std::chrono::steady_clock::now();
|
|
|
|
if (std::chrono::duration_cast<std::chrono::milliseconds>(resStart - start).count() > 8)
|
2016-03-26 00:51:59 +00:00
|
|
|
break;
|
|
|
|
|
2017-02-19 09:27:01 +00:00
|
|
|
AsyncPumpTask(it);
|
2016-03-26 00:51:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|