Async resource build fixes

This commit is contained in:
Jack Andersen 2016-03-31 15:00:37 -10:00
parent bc6ba1141d
commit 9fcce94a7a
10 changed files with 169 additions and 99 deletions

View File

@ -436,6 +436,46 @@ std::unique_ptr<urde::IObj> ProjectResourceFactoryBase::Build(const urde::SObjec
} }
lk.unlock(); lk.unlock();
auto asyncSearch = m_asyncLoadList.find(tag);
if (asyncSearch != m_asyncLoadList.end())
{
/* Async spinloop */
AsyncTask& task = asyncSearch->second;
task.EnsurePath(task.x0_tag, search->second);
/* Pump load pipeline (cooking if needed) */
while (!task.AsyncPump()) {std::this_thread::sleep_for(std::chrono::milliseconds(2));}
if (task.m_complete)
{
/* 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),
task.x14_resSize, false, task.x18_cvXfer);
}
else
{
athena::io::MemoryReader mr(task.x10_loadBuffer.get(), task.x14_resSize);
newObj = m_factoryMgr.MakeObject(task.x0_tag, mr, task.x18_cvXfer);
}
*task.xc_targetPtr = newObj.get();
Log.report(logvisor::Warning, "spin-built %.4s %08X",
task.x0_tag.type.toString().c_str(),
u32(task.x0_tag.id));
m_asyncLoadList.erase(asyncSearch);
return newObj;
}
Log.report(logvisor::Error, "unable to spin-build %.4s %08X",
task.x0_tag.type.toString().c_str(),
u32(task.x0_tag.id));
m_asyncLoadList.erase(asyncSearch);
return {};
}
/* Fall-back to sync build */
return BuildSync(tag, search->second, paramXfer); return BuildSync(tag, search->second, paramXfer);
} }
@ -545,8 +585,22 @@ void ProjectResourceFactoryBase::AsyncIdle()
if (task.m_complete) if (task.m_complete)
{ {
/* Load complete, build resource */ /* Load complete, build resource */
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);
}
else
{
athena::io::MemoryReader mr(task.x10_loadBuffer.get(), task.x14_resSize); athena::io::MemoryReader mr(task.x10_loadBuffer.get(), task.x14_resSize);
*task.xc_targetPtr = m_factoryMgr.MakeObject(task.x0_tag, mr, task.x18_cvXfer).release(); newObj = m_factoryMgr.MakeObject(task.x0_tag, mr, task.x18_cvXfer);
}
*task.xc_targetPtr = newObj.release();
Log.report(logvisor::Info, "async-built %.4s %08X",
task.x0_tag.type.toString().c_str(),
u32(task.x0_tag.id));
} }
it = m_asyncLoadList.erase(it); it = m_asyncLoadList.erase(it);
continue; continue;

View File

@ -24,7 +24,7 @@ namespace urde
ProjectResourceFactoryMP1::ProjectResourceFactoryMP1(hecl::ClientProcess& clientProc) ProjectResourceFactoryMP1::ProjectResourceFactoryMP1(hecl::ClientProcess& clientProc)
: ProjectResourceFactoryBase(clientProc) : ProjectResourceFactoryBase(clientProc)
{ {
m_factoryMgr.AddFactory(FOURCC('TXTR'), FFactoryFunc(FTextureFactory)); m_factoryMgr.AddFactory(FOURCC('TXTR'), FMemFactoryFunc(FTextureFactory));
m_factoryMgr.AddFactory(FOURCC('PART'), FFactoryFunc(FParticleFactory)); m_factoryMgr.AddFactory(FOURCC('PART'), FFactoryFunc(FParticleFactory));
m_factoryMgr.AddFactory(FOURCC('FRME'), FFactoryFunc(RGuiFrameFactoryInGame)); m_factoryMgr.AddFactory(FOURCC('FRME'), FFactoryFunc(RGuiFrameFactoryInGame));
m_factoryMgr.AddFactory(FOURCC('FONT'), FFactoryFunc(FRasterFontFactory)); m_factoryMgr.AddFactory(FOURCC('FONT'), FFactoryFunc(FRasterFontFactory));

View File

@ -21,6 +21,7 @@ namespace urde
void ViewManager::BuildTestPART(urde::IObjectStore& objStore) void ViewManager::BuildTestPART(urde::IObjectStore& objStore)
{ {
m_modelTest = objStore.GetObj("CMDL_GameCube"); m_modelTest = objStore.GetObj("CMDL_GameCube");
m_modelTest.Lock();
//m_partGenDesc = objStore.GetObj({hecl::FOURCC('PART'), 0x972A5CD2}); //m_partGenDesc = objStore.GetObj({hecl::FOURCC('PART'), 0x972A5CD2});
m_partGenDesc = objStore.GetObj("BusterSparks"); m_partGenDesc = objStore.GetObj("BusterSparks");
@ -58,9 +59,16 @@ void ViewManager::ParticleView::resized(const boo::SWindowRect& root, const boo:
void ViewManager::ParticleView::draw(boo::IGraphicsCommandQueue *gfxQ) void ViewManager::ParticleView::draw(boo::IGraphicsCommandQueue *gfxQ)
{ {
if (m_vm.m_modelTest) if (m_vm.m_modelTest.IsLoaded())
{ {
CModelFlags flags; CModelFlags flags;
CGraphics::SetModelMatrix(zeus::CTransform::Identity());
CGraphics::SetViewPointMatrix(zeus::CTransform::Identity() + zeus::CVector3f(0.f, -10.f, 0.f));
boo::SWindowRect windowRect = m_vm.m_mainWindow->getWindowFrame();
float aspect = windowRect.size[0] / float(windowRect.size[1]);
CGraphics::SetPerspective(55.0, aspect, 0.001f, 1000.f);
m_vm.m_modelTest->Draw(flags); m_vm.m_modelTest->Draw(flags);
} }
if (m_vm.m_partGen) if (m_vm.m_partGen)
@ -70,11 +78,11 @@ void ViewManager::ParticleView::draw(boo::IGraphicsCommandQueue *gfxQ)
if (m_vm.m_partGen->IsSystemDeletable()) if (m_vm.m_partGen->IsSystemDeletable())
m_vm.m_partGen->Reset(); m_vm.m_partGen->Reset();
urde::CGraphics::SetModelMatrix(zeus::CTransform::Identity()); CGraphics::SetModelMatrix(zeus::CTransform::Identity());
urde::CGraphics::SetViewPointMatrix(zeus::CTransform::Identity() + zeus::CVector3f(0.f, -10.f, 0.f)); CGraphics::SetViewPointMatrix(zeus::CTransform::Identity() + zeus::CVector3f(0.f, -10.f, 0.f));
boo::SWindowRect windowRect = m_vm.m_mainWindow->getWindowFrame(); boo::SWindowRect windowRect = m_vm.m_mainWindow->getWindowFrame();
float aspect = windowRect.size[0] / float(windowRect.size[1]); float aspect = windowRect.size[0] / float(windowRect.size[1]);
urde::CGraphics::SetPerspective(55.0, aspect, 0.001f, 1000.f); CGraphics::SetPerspective(55.0, aspect, 0.001f, 1000.f);
//gfxQ->clearTarget(false, true); //gfxQ->clearTarget(false, true);
m_vm.m_partGen->Render(); m_vm.m_partGen->Render();

View File

@ -48,8 +48,8 @@ class ViewManager : public specter::IViewManager
void draw(boo::IGraphicsCommandQueue* gfxQ); void draw(boo::IGraphicsCommandQueue* gfxQ);
}; };
std::unique_ptr<ParticleView> m_particleView; std::unique_ptr<ParticleView> m_particleView;
urde::TLockedToken<CModel> m_modelTest; urde::TCachedToken<CModel> m_modelTest;
urde::TLockedToken<CGenDescription> m_partGenDesc; urde::TCachedToken<CGenDescription> m_partGenDesc;
std::unique_ptr<CElementGen> m_partGen; std::unique_ptr<CElementGen> m_partGen;
std::unique_ptr<CLineRenderer> m_lineRenderer; std::unique_ptr<CLineRenderer> m_lineRenderer;
std::unique_ptr<CMoviePlayer> m_moviePlayer; std::unique_ptr<CMoviePlayer> m_moviePlayer;

View File

@ -19,18 +19,12 @@ CToken CSimplePool::GetObj(const SObjectTag& tag, const CVParamTransfer& paramXf
if (iter != x4_resources.end()) if (iter != x4_resources.end())
return CToken(iter->second); return CToken(iter->second);
// TODO: There is some logic missing here, need to figure out what it's doing
CObjectReference* ret = new CObjectReference(*this, x30_factory.Build(tag, paramXfer), tag, paramXfer); CObjectReference* ret = new CObjectReference(*this, std::unique_ptr<IObj>(), tag, paramXfer);
if (ret->GetObject())
{
x4_resources.push_back(std::make_pair<SObjectTag, CObjectReference*>((SObjectTag)tag, std::move(ret))); x4_resources.push_back(std::make_pair<SObjectTag, CObjectReference*>((SObjectTag)tag, std::move(ret)));
return CToken(ret); return CToken(ret);
} }
delete ret;
return CToken();
}
CToken CSimplePool::GetObj(const SObjectTag& tag) CToken CSimplePool::GetObj(const SObjectTag& tag)
{ {
return GetObj(tag, x34_paramXfer); return GetObj(tag, x34_paramXfer);

View File

@ -16,6 +16,7 @@ class IObjectStore;
class CObjectReference class CObjectReference
{ {
friend class CToken; friend class CToken;
friend class CSimplePool;
u16 x0_refCount = 0; u16 x0_refCount = 0;
u16 x2_lockCount = 0; u16 x2_lockCount = 0;
bool x3_loading = false; /* Rightmost bit of lockCount */ bool x3_loading = false; /* Rightmost bit of lockCount */
@ -23,7 +24,23 @@ class CObjectReference
IObjectStore* xC_objectStore = nullptr; IObjectStore* xC_objectStore = nullptr;
IObj* x10_object = nullptr; IObj* x10_object = nullptr;
CVParamTransfer x14_params; CVParamTransfer x14_params;
public:
/** Mechanism by which CToken decrements 1st ref-count, indicating CToken invalidation or reset.
* Reaching 0 indicates the CToken should delete the CObjectReference */
u16 RemoveReference()
{
--x0_refCount;
if (x0_refCount == 0)
{
if (x10_object)
Unload();
if (IsLoading())
CancelLoad();
xC_objectStore->ObjectUnreferenced(x4_objTag);
}
return x0_refCount;
}
CObjectReference(IObjectStore& objStore, std::unique_ptr<IObj>&& obj, CObjectReference(IObjectStore& objStore, std::unique_ptr<IObj>&& obj,
const SObjectTag& objTag, CVParamTransfer buildParams) const SObjectTag& objTag, CVParamTransfer buildParams)
: x4_objTag(objTag), xC_objectStore(&objStore), : x4_objTag(objTag), xC_objectStore(&objStore),
@ -61,21 +78,6 @@ public:
} }
} }
/** Mechanism by which CToken decrements 1st ref-count, indicating CToken invalidation or reset.
* Reaching 0 indicates the CToken should delete the CObjectReference */
u16 RemoveReference()
{
--x0_refCount;
if (x0_refCount == 0)
{
if (x10_object)
Unload();
if (IsLoading())
CancelLoad();
xC_objectStore->ObjectUnreferenced(x4_objTag);
}
return x0_refCount;
}
void CancelLoad() void CancelLoad()
{ {
if (xC_objectStore && IsLoading()) if (xC_objectStore && IsLoading())
@ -108,6 +110,8 @@ public:
{ {
return x4_objTag; return x4_objTag;
} }
public:
~CObjectReference() ~CObjectReference()
{ {
if (x10_object) if (x10_object)
@ -122,8 +126,25 @@ public:
* (default/empty constructor, move constructor/assign) */ * (default/empty constructor, move constructor/assign) */
class CToken class CToken
{ {
friend class CSimplePool;
CObjectReference* x0_objRef = nullptr; CObjectReference* x0_objRef = nullptr;
bool x4_lockHeld = false; bool x4_lockHeld = false;
void RemoveRef()
{
if (x0_objRef && x0_objRef->RemoveReference() == 0)
{
std::default_delete<CObjectReference>()(x0_objRef);
x0_objRef = nullptr;
}
}
CToken(CObjectReference* obj)
{
x0_objRef = obj;
++x0_objRef->x0_refCount;
}
public: public:
/* Added to test for non-null state */ /* Added to test for non-null state */
operator bool() const {return x0_objRef != nullptr;} operator bool() const {return x0_objRef != nullptr;}
@ -150,14 +171,6 @@ public:
return false; return false;
return x0_objRef->IsLoaded(); return x0_objRef->IsLoaded();
} }
void RemoveRef()
{
if (x0_objRef && x0_objRef->RemoveReference() == 0)
{
std::default_delete<CObjectReference>()(x0_objRef);
x0_objRef = nullptr;
}
}
IObj* GetObj() IObj* GetObj()
{ {
if (!x0_objRef) if (!x0_objRef)
@ -217,11 +230,6 @@ public:
++x0_objRef->x0_refCount; ++x0_objRef->x0_refCount;
Lock(); Lock();
} }
CToken(CObjectReference* obj)
{
x0_objRef = obj;
++x0_objRef->x0_refCount;
}
const SObjectTag* GetObjectTag() const const SObjectTag* GetObjectTag() const
{ {
if (!x0_objRef) if (!x0_objRef)

View File

@ -156,6 +156,7 @@ zeus::CMatrix4f CGraphics::CalculatePerspectiveMatrix(float fovy, float aspect,
zeus::CMatrix4f CGraphics::GetPerspectiveProjectionMatrix() zeus::CMatrix4f CGraphics::GetPerspectiveProjectionMatrix()
{ {
CProjectionState& ref = g_Proj;
float rml = g_Proj.x8_right - g_Proj.x4_left; float rml = g_Proj.x8_right - g_Proj.x4_left;
float rpl = g_Proj.x8_right + g_Proj.x4_left; float rpl = g_Proj.x8_right + g_Proj.x4_left;
float tmb = g_Proj.xc_top - g_Proj.x10_bottom; float tmb = g_Proj.xc_top - g_Proj.x10_bottom;

View File

@ -116,8 +116,6 @@ void CBooModel::UnlockTextures() const
} }
void CBooModel::DrawAlphaSurfaces(const CModelFlags& flags) const void CBooModel::DrawAlphaSurfaces(const CModelFlags& flags) const
{
if (TryLockTextures())
{ {
const CBooSurface* surf = x3c_firstSortedSurface; const CBooSurface* surf = x3c_firstSortedSurface;
while (surf) while (surf)
@ -126,11 +124,8 @@ void CBooModel::DrawAlphaSurfaces(const CModelFlags& flags) const
surf = surf->m_next; surf = surf->m_next;
} }
} }
}
void CBooModel::DrawNormalSurfaces(const CModelFlags& flags) const void CBooModel::DrawNormalSurfaces(const CModelFlags& flags) const
{
if (TryLockTextures())
{ {
const CBooSurface* surf = x38_firstUnsortedSurface; const CBooSurface* surf = x38_firstUnsortedSurface;
while (surf) while (surf)
@ -139,14 +134,9 @@ void CBooModel::DrawNormalSurfaces(const CModelFlags& flags) const
surf = surf->m_next; surf = surf->m_next;
} }
} }
}
void CBooModel::DrawSurfaces(const CModelFlags& flags) const void CBooModel::DrawSurfaces(const CModelFlags& flags) const
{ {
if (!(flags.m_flags & 0x4))
if (!TryLockTextures())
return;
const CBooSurface* surf = x38_firstUnsortedSurface; const CBooSurface* surf = x38_firstUnsortedSurface;
while (surf) while (surf)
{ {
@ -299,22 +289,31 @@ void CBooModel::UpdateUniformData() const
} }
void CBooModel::DrawAlpha(const CModelFlags& flags) const void CBooModel::DrawAlpha(const CModelFlags& flags) const
{
if (TryLockTextures())
{ {
UpdateUniformData(); UpdateUniformData();
DrawAlphaSurfaces(flags); DrawAlphaSurfaces(flags);
} }
}
void CBooModel::DrawNormal(const CModelFlags& flags) const void CBooModel::DrawNormal(const CModelFlags& flags) const
{
if (TryLockTextures())
{ {
UpdateUniformData(); UpdateUniformData();
DrawNormalSurfaces(flags); DrawNormalSurfaces(flags);
} }
}
void CBooModel::Draw(const CModelFlags& flags) const void CBooModel::Draw(const CModelFlags& flags) const
{
if (TryLockTextures())
{ {
UpdateUniformData(); UpdateUniformData();
DrawSurfaces(flags); DrawSurfaces(flags);
} }
}
static const u8* MemoryFromPartData(const u8*& dataCur, const s32*& secSizeCur) static const u8* MemoryFromPartData(const u8*& dataCur, const s32*& secSizeCur)
{ {
@ -378,7 +377,7 @@ CModel::CModel(std::unique_ptr<u8[]>&& in, u32 dataLen, IObjectStore* store)
{ {
hecl::Runtime::ShaderTag tag(mat.heclIr, hecl::Runtime::ShaderTag tag(mat.heclIr,
hmdlMeta.colorCount, hmdlMeta.uvCount, hmdlMeta.weightCount, hmdlMeta.colorCount, hmdlMeta.uvCount, hmdlMeta.weightCount,
0, mat.uvAnims.size(), true, true, true); 0, mat.uvAnims.size(), false, false, true);
matSet.m_shaders.push_back(CGraphics::g_ShaderCacheMgr->buildShader(tag, mat.heclIr, "CMDL", ctx)); matSet.m_shaders.push_back(CGraphics::g_ShaderCacheMgr->buildShader(tag, mat.heclIr, "CMDL", ctx));
} }
} }
@ -403,7 +402,7 @@ CModel::CModel(std::unique_ptr<u8[]>&& in, u32 dataLen, IObjectStore* store)
hecl::SBig(aabbPtr[3]), hecl::SBig(aabbPtr[4]), hecl::SBig(aabbPtr[5])); hecl::SBig(aabbPtr[3]), hecl::SBig(aabbPtr[4]), hecl::SBig(aabbPtr[5]));
x28_modelInst = std::make_unique<CBooModel>(&x8_surfaces, x18_matSets[0], x28_modelInst = std::make_unique<CBooModel>(&x8_surfaces, x18_matSets[0],
m_vtxFmt, m_vbo, m_ibo, m_vtxFmt, m_vbo, m_ibo,
aabb, flags & 0x2, true); aabb, flags & 0x2, false);
} }
void CBooModel::SShader::UnlockTextures() void CBooModel::SShader::UnlockTextures()
@ -465,7 +464,8 @@ CFactoryFnReturn FModelFactory(const urde::SObjectTag& tag,
const urde::CVParamTransfer& vparms) const urde::CVParamTransfer& vparms)
{ {
IObjectStore* store = static_cast<TObjOwnerParam<IObjectStore*>*>(vparms.GetObj())->GetParam(); IObjectStore* store = static_cast<TObjOwnerParam<IObjectStore*>*>(vparms.GetObj())->GetParam();
return TToken<CModel>::GetIObjObjectFor(std::make_unique<CModel>(std::move(in), len, store)); CFactoryFnReturn ret = TToken<CModel>::GetIObjObjectFor(std::make_unique<CModel>(std::move(in), len, store));
return ret;
} }
} }

View File

@ -34,10 +34,10 @@ class CTexture
void BuildRGB5A3FromGCN(CInputStream& in); void BuildRGB5A3FromGCN(CInputStream& in);
void BuildRGBA8FromGCN(CInputStream& in); void BuildRGBA8FromGCN(CInputStream& in);
void BuildDXT1FromGCN(CInputStream& in); void BuildDXT1FromGCN(CInputStream& in);
void BuildRGBA8(CInputStream& in); void BuildRGBA8(const void* data);
public: public:
CTexture(CInputStream& in); CTexture(std::unique_ptr<u8[]>&& in, u32 length);
enum class EClampMode enum class EClampMode
{ {
None, None,
@ -50,7 +50,9 @@ public:
boo::ITexture* GetBooTexture() {return m_booTex;} boo::ITexture* GetBooTexture() {return m_booTex;}
}; };
CFactoryFnReturn FTextureFactory(const SObjectTag& tag, CInputStream& in, const CVParamTransfer& vparms); CFactoryFnReturn FTextureFactory(const urde::SObjectTag& tag,
std::unique_ptr<u8[]>&& in, u32 len,
const urde::CVParamTransfer& vparms);
} }

View File

@ -648,63 +648,64 @@ void CTexture::BuildDXT1FromGCN(CInputStream& in)
}); });
} }
void CTexture::BuildRGBA8(CInputStream& in) void CTexture::BuildRGBA8(const void* data)
{ {
size_t texelCount = ComputeMippedTexelCount(); size_t texelCount = ComputeMippedTexelCount();
std::unique_ptr<atInt8[]> buf = in.readBytes(texelCount * 4);
m_booToken = CGraphics::CommitResources([&](boo::IGraphicsDataFactory::Context& ctx) -> bool m_booToken = CGraphics::CommitResources([&](boo::IGraphicsDataFactory::Context& ctx) -> bool
{ {
m_booTex = ctx.newStaticTexture(x4_w, x6_h, x8_mips, boo::TextureFormat::RGBA8, m_booTex = ctx.newStaticTexture(x4_w, x6_h, x8_mips, boo::TextureFormat::RGBA8,
buf.get(), texelCount * 4); data, texelCount * 4);
return true; return true;
}); });
} }
CTexture::CTexture(CInputStream& in) CTexture::CTexture(std::unique_ptr<u8[]>&& in, u32 length)
{ {
x0_fmt = ETexelFormat(in.readUint32Big()); std::unique_ptr<u8[]> owned = std::move(in);
x4_w = in.readUint16Big(); athena::io::MemoryReader r(owned.get(), length);
x6_h = in.readUint16Big(); x0_fmt = ETexelFormat(r.readUint32Big());
x8_mips = in.readUint32Big(); x4_w = r.readUint16Big();
x6_h = r.readUint16Big();
x8_mips = r.readUint32Big();
switch (x0_fmt) switch (x0_fmt)
{ {
case ETexelFormat::I4: case ETexelFormat::I4:
BuildI4FromGCN(in); BuildI4FromGCN(r);
break; break;
case ETexelFormat::I8: case ETexelFormat::I8:
BuildI8FromGCN(in); BuildI8FromGCN(r);
break; break;
case ETexelFormat::IA4: case ETexelFormat::IA4:
BuildIA4FromGCN(in); BuildIA4FromGCN(r);
break; break;
case ETexelFormat::IA8: case ETexelFormat::IA8:
BuildIA8FromGCN(in); BuildIA8FromGCN(r);
break; break;
case ETexelFormat::C4: case ETexelFormat::C4:
BuildC4FromGCN(in); BuildC4FromGCN(r);
break; break;
case ETexelFormat::C8: case ETexelFormat::C8:
BuildC8FromGCN(in); BuildC8FromGCN(r);
break; break;
case ETexelFormat::C14X2: case ETexelFormat::C14X2:
BuildC14X2FromGCN(in); BuildC14X2FromGCN(r);
break; break;
case ETexelFormat::RGB565: case ETexelFormat::RGB565:
BuildRGB565FromGCN(in); BuildRGB565FromGCN(r);
break; break;
case ETexelFormat::RGB5A3: case ETexelFormat::RGB5A3:
BuildRGB5A3FromGCN(in); BuildRGB5A3FromGCN(r);
break; break;
case ETexelFormat::RGBA8: case ETexelFormat::RGBA8:
BuildRGBA8FromGCN(in); BuildRGBA8FromGCN(r);
break; break;
case ETexelFormat::CMPR: case ETexelFormat::CMPR:
BuildDXT1FromGCN(in); BuildDXT1FromGCN(r);
break; break;
case ETexelFormat::RGBA8PC: case ETexelFormat::RGBA8PC:
BuildRGBA8(in); BuildRGBA8(owned.get() + 12);
break; break;
default: default:
Log.report(logvisor::Fatal, "invalid texture type %d for boo", int(x0_fmt)); Log.report(logvisor::Fatal, "invalid texture type %d for boo", int(x0_fmt));
@ -716,9 +717,11 @@ void CTexture::Load(int slot, EClampMode clamp) const
} }
CFactoryFnReturn FTextureFactory(const SObjectTag& tag, CInputStream& in, const CVParamTransfer& vparms) CFactoryFnReturn FTextureFactory(const urde::SObjectTag& tag,
std::unique_ptr<u8[]>&& in, u32 len,
const urde::CVParamTransfer& vparms)
{ {
return TToken<CTexture>::GetIObjObjectFor(std::make_unique<CTexture>(in)); return TToken<CTexture>::GetIObjObjectFor(std::make_unique<CTexture>(std::move(in), len));
} }
} }