Skip to content

Commit 029d173

Browse files
committed
Created ModelPool
1 parent ccb9c5c commit 029d173

8 files changed

Lines changed: 159 additions & 40 deletions

File tree

engine/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,7 @@ set(DORIAX_SRCS
603603
core/pool/SoundPool.cpp
604604
core/pool/TextureDataPool.cpp
605605
core/pool/TexturePool.cpp
606+
core/pool/ModelPool.cpp
606607
core/registry/EntityRegistry.cpp
607608
core/render/BufferRender.cpp
608609
core/render/CameraRender.cpp

engine/core/Doriax.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@
148148
#include "pool/SoundPool.h"
149149
#include "pool/TextureDataPool.h"
150150
#include "pool/TexturePool.h"
151+
#include "pool/ModelPool.h"
151152

152153
#include "registry/EntityRegistry.h"
153154

engine/core/Engine.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "pool/TextureDataPool.h"
1717
#include "pool/ShaderPool.h"
1818
#include "pool/FontPool.h"
19+
#include "pool/ModelPool.h"
1920
#ifndef NO_THREAD_SUPPORT
2021
#include "thread/ThreadPoolManager.h"
2122
#endif
@@ -853,6 +854,7 @@ void Engine::systemViewDestroyed(){
853854
TextureDataPool::clear();
854855
ShaderPool::clear();
855856
FontPool::clear();
857+
ModelPool::clear();
856858

857859
drawSemaphore.release();
858860

engine/core/component/ModelComponent.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@
88
#include "buffer/ExternalBuffer.h"
99
#include <vector>
1010
#include <map>
11+
#include <memory>
1112
#include <string>
1213

1314
namespace tinygltf {class Model;}
1415

1516
namespace doriax{
1617

1718
struct DORIAX_API ModelComponent{
18-
tinygltf::Model* gltfModel = NULL;
19+
std::shared_ptr<tinygltf::Model> gltfModel;
1920

2021
Matrix4 inverseDerivedTransform;
2122

engine/core/pool/ModelPool.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//
2+
// (c) 2026 Eduardo Doria.
3+
//
4+
5+
#include "ModelPool.h"
6+
7+
#include "tiny_gltf.h"
8+
9+
using namespace doriax;
10+
11+
models_t& ModelPool::getMap(){
12+
//To prevent similar problem of static init fiasco but on deinitialization
13+
//https://isocpp.org/wiki/faq/ctors#static-init-order-on-first-use
14+
static models_t* map = new models_t();
15+
return *map;
16+
}
17+
18+
std::shared_ptr<tinygltf::Model> ModelPool::get(const std::string& id){
19+
auto it = getMap().find(id);
20+
if (it == getMap().end()){
21+
return nullptr;
22+
}
23+
24+
if (it->second.use_count() > 0){
25+
return it->second;
26+
}
27+
28+
return nullptr;
29+
}
30+
31+
std::shared_ptr<tinygltf::Model> ModelPool::add(const std::string& id, std::shared_ptr<tinygltf::Model> model){
32+
auto& shared = getMap()[id];
33+
34+
if (shared.use_count() > 0){
35+
return shared;
36+
}
37+
38+
shared = model;
39+
return shared;
40+
}
41+
42+
void ModelPool::remove(const std::string& id){
43+
if (getMap().count(id)){
44+
auto& shared = getMap()[id];
45+
if (shared.use_count() <= 1){
46+
getMap().erase(id);
47+
}
48+
}
49+
}
50+
51+
void ModelPool::clear(){
52+
getMap().clear();
53+
}

engine/core/pool/ModelPool.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//
2+
// (c) 2026 Eduardo Doria.
3+
//
4+
5+
#ifndef MODELPOOL_H
6+
#define MODELPOOL_H
7+
8+
#include "Export.h"
9+
#include <map>
10+
#include <memory>
11+
#include <string>
12+
13+
namespace tinygltf { class Model; }
14+
15+
namespace doriax{
16+
17+
typedef std::map< std::string, std::shared_ptr<tinygltf::Model> > models_t;
18+
19+
class DORIAX_API ModelPool{
20+
private:
21+
static models_t& getMap();
22+
23+
public:
24+
static std::shared_ptr<tinygltf::Model> get(const std::string& id);
25+
static std::shared_ptr<tinygltf::Model> add(const std::string& id, std::shared_ptr<tinygltf::Model> model);
26+
static void remove(const std::string& id);
27+
28+
// necessary for engine shutdown
29+
static void clear();
30+
};
31+
}
32+
33+
#endif /* MODELPOOL_H */

engine/core/subsystem/MeshSystem.cpp

Lines changed: 64 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "buffer/InterleavedBuffer.h"
1010
#include "io/FileData.h"
1111
#include "io/Data.h"
12+
#include "pool/ModelPool.h"
1213
#include "thread/ResourceProgress.h"
1314
#include "thread/ThreadPoolManager.h"
1415

@@ -600,14 +601,14 @@ std::string MeshSystem::getAsyncModelLoadScenePrefix(const Scene* scene){
600601
return key.str();
601602
}
602603

603-
std::string MeshSystem::getAsyncModelLoadKey(const Scene* scene, Entity entity, const std::string& filename){
604+
std::string MeshSystem::getAsyncModelLoadKey(const Scene* scene, const std::string& filename){
604605
std::ostringstream key;
605-
key << getAsyncModelLoadScenePrefix(scene) << entity << '|' << filename;
606+
key << getAsyncModelLoadScenePrefix(scene) << getModelFilenameKey(filename);
606607
return key.str();
607608
}
608609

609-
std::string MeshSystem::getAsyncModelLoadKey(Entity entity, const std::string& filename) const{
610-
return getAsyncModelLoadKey(scene, entity, filename);
610+
std::string MeshSystem::getAsyncModelLoadKey(const std::string& filename) const{
611+
return getAsyncModelLoadKey(scene, filename);
611612
}
612613

613614
bool MeshSystem::hasPendingAsyncModelLoads() const{
@@ -656,13 +657,15 @@ void MeshSystem::cancelAllAsyncModelLoads(){
656657
}
657658

658659
bool MeshSystem::isAsyncModelLoadPending(Entity entity, const std::string& filename) const{
659-
const std::string key = getAsyncModelLoadKey(entity, filename);
660+
(void)entity;
661+
const std::string key = getAsyncModelLoadKey(filename);
660662
std::lock_guard<std::mutex> lock(asyncModelMutex);
661663
return pendingModelLoads.find(key) != pendingModelLoads.end();
662664
}
663665

664666
void MeshSystem::cancelAsyncModelLoad(Entity entity, const std::string& filename){
665-
const std::string key = getAsyncModelLoadKey(entity, filename);
667+
(void)entity;
668+
const std::string key = getAsyncModelLoadKey(filename);
666669
const uint64_t buildId = std::hash<std::string>{}(key);
667670
bool erased = false;
668671
{
@@ -715,8 +718,8 @@ std::shared_ptr<MeshSystem::AsyncModelLoadResult> MeshSystem::loadModelFileOnWor
715718
return result;
716719
}
717720

718-
std::shared_ptr<MeshSystem::AsyncModelLoadResult> MeshSystem::pollOrStartAsyncModelLoad(Entity entity, const std::string& filename, bool obj){
719-
const std::string key = getAsyncModelLoadKey(entity, filename);
721+
std::shared_ptr<MeshSystem::AsyncModelLoadResult> MeshSystem::pollOrStartAsyncModelLoad(const std::string& filename, bool obj){
722+
const std::string key = getAsyncModelLoadKey(filename);
720723
const uint64_t buildId = std::hash<std::string>{}(key);
721724

722725
std::shared_future<std::shared_ptr<AsyncModelLoadResult>> future;
@@ -2200,9 +2203,16 @@ void MeshSystem::createTorus(MeshComponent& mesh, float radius, float ringRadius
22002203
}
22012204

22022205
bool MeshSystem::loadGLTF(Entity entity, const std::string filename, bool asyncLoad, bool skipEntities, bool changeRootTransform){
2206+
const std::string poolKey = getModelFilenameKey(filename);
2207+
2208+
// If parsed data is already cached, skip the async background-thread entirely.
2209+
if (asyncLoad && ModelPool::get(poolKey)){
2210+
asyncLoad = false;
2211+
}
2212+
22032213
std::shared_ptr<AsyncModelLoadResult> asyncResult;
22042214
if (asyncLoad){
2205-
asyncResult = pollOrStartAsyncModelLoad(entity, filename, false);
2215+
asyncResult = pollOrStartAsyncModelLoad(filename, false);
22062216
if (!asyncResult || !asyncResult->success || !asyncResult->gltfModel){
22072217
return false;
22082218
}
@@ -2216,14 +2226,11 @@ bool MeshSystem::loadGLTF(Entity entity, const std::string filename, bool asyncL
22162226

22172227
model.filename = filename;
22182228

2219-
uint64_t buildId = asyncLoad ? std::hash<std::string>{}(getAsyncModelLoadKey(entity, filename)) : 0;
2229+
uint64_t buildId = asyncLoad ? std::hash<std::string>{}(getAsyncModelLoadKey(filename)) : 0;
22202230

22212231
mesh.submeshes[0].primitiveType = PrimitiveType::TRIANGLES;
22222232
mesh.numSubmeshes = 1;
22232233

2224-
if (!model.gltfModel)
2225-
model.gltfModel = new tinygltf::Model();
2226-
22272234
std::string err;
22282235
std::string warn;
22292236

@@ -2235,32 +2242,50 @@ bool MeshSystem::loadGLTF(Entity entity, const std::string filename, bool asyncL
22352242
bool res = false;
22362243

22372244
if (asyncLoad){
2238-
*model.gltfModel = std::move(*asyncResult->gltfModel);
2245+
// Async loader already produced a parsed model; reuse it (and cache it).
2246+
auto cached = ModelPool::get(poolKey);
2247+
if (cached){
2248+
model.gltfModel = cached;
2249+
}else{
2250+
model.gltfModel = asyncResult->gltfModel;
2251+
ModelPool::add(poolKey, model.gltfModel);
2252+
}
22392253
res = true;
22402254
}else{
2241-
tinygltf::TinyGLTF loader;
2242-
loader.SetFsCallbacks({&fileExists, &tinygltf::ExpandFilePath, &readWholeFile, &tinygltf::WriteWholeFile, &getFileSizeInBytes});
2255+
// Try cache before hitting disk.
2256+
auto cached = ModelPool::get(poolKey);
2257+
if (cached){
2258+
model.gltfModel = cached;
2259+
res = true;
2260+
}else{
2261+
model.gltfModel = std::make_shared<tinygltf::Model>();
22432262

2244-
std::string ext = FileData::getFilePathExtension(filename);
2263+
tinygltf::TinyGLTF loader;
2264+
loader.SetFsCallbacks({&fileExists, &tinygltf::ExpandFilePath, &readWholeFile, &tinygltf::WriteWholeFile, &getFileSizeInBytes});
22452265

2246-
if (ext.compare("glb") == 0) {
2247-
res = loader.LoadBinaryFromFile(model.gltfModel, &err, &warn, filename); // for binary glTF(.glb)
2248-
}else{
2249-
res = loader.LoadASCIIFromFile(model.gltfModel, &err, &warn, filename);
2250-
}
2266+
std::string ext = FileData::getFilePathExtension(filename);
22512267

2252-
if (!warn.empty()) {
2253-
Log::warn("Loading GLTF model (%s): %s", filename.c_str(), warn.c_str());
2254-
}
2268+
if (ext.compare("glb") == 0) {
2269+
res = loader.LoadBinaryFromFile(model.gltfModel.get(), &err, &warn, filename); // for binary glTF(.glb)
2270+
}else{
2271+
res = loader.LoadASCIIFromFile(model.gltfModel.get(), &err, &warn, filename);
2272+
}
22552273

2256-
if (!err.empty()) {
2257-
Log::error("Can't load GLTF model (%s): %s", filename.c_str(), err.c_str());
2258-
return false;
2259-
}
2274+
if (!warn.empty()) {
2275+
Log::warn("Loading GLTF model (%s): %s", filename.c_str(), warn.c_str());
2276+
}
22602277

2261-
if (!res) {
2262-
Log::verbose("Failed to load glTF: %s", filename.c_str());
2263-
return false;
2278+
if (!err.empty()) {
2279+
Log::error("Can't load GLTF model (%s): %s", filename.c_str(), err.c_str());
2280+
return false;
2281+
}
2282+
2283+
if (!res) {
2284+
Log::verbose("Failed to load glTF: %s", filename.c_str());
2285+
return false;
2286+
}
2287+
2288+
ModelPool::add(poolKey, model.gltfModel);
22642289
}
22652290
}
22662291

@@ -3016,7 +3041,7 @@ bool MeshSystem::loadGLTF(Entity entity, const std::string filename, bool asyncL
30163041
bool MeshSystem::loadOBJ(Entity entity, const std::string filename, bool asyncLoad){
30173042
std::shared_ptr<AsyncModelLoadResult> asyncResult;
30183043
if (asyncLoad){
3019-
asyncResult = pollOrStartAsyncModelLoad(entity, filename, true);
3044+
asyncResult = pollOrStartAsyncModelLoad(filename, true);
30203045
if (!asyncResult || !asyncResult->success){
30213046
return false;
30223047
}
@@ -3030,7 +3055,7 @@ bool MeshSystem::loadOBJ(Entity entity, const std::string filename, bool asyncLo
30303055

30313056
model.filename = filename;
30323057

3033-
uint64_t buildId = asyncLoad ? std::hash<std::string>{}(getAsyncModelLoadKey(entity, filename)) : 0;
3058+
uint64_t buildId = asyncLoad ? std::hash<std::string>{}(getAsyncModelLoadKey(filename)) : 0;
30343059

30353060
mesh.submeshes[0].primitiveType = PrimitiveType::TRIANGLES;
30363061
mesh.numSubmeshes = 1;
@@ -3299,8 +3324,11 @@ void MeshSystem::clearAnimationMapping(ModelComponent& model){
32993324

33003325
void MeshSystem::destroyModel(ModelComponent& model){
33013326
if (model.gltfModel){
3302-
delete model.gltfModel;
3303-
model.gltfModel = NULL;
3327+
model.gltfModel.reset();
3328+
}
3329+
3330+
if (!model.loadedFilename.empty()){
3331+
ModelPool::remove(model.loadedFilename);
33043332
}
33053333

33063334
model.morphNameMapping.clear();

engine/core/subsystem/MeshSystem.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,10 @@ namespace doriax{
5050
static bool readWholeFile(std::vector<unsigned char> *out, std::string *err, const std::string &filepath, void *);
5151
static bool getFileSizeInBytes(size_t *filesize_out, std::string *err, const std::string &filepath, void *userdata);
5252
static std::string getAsyncModelLoadScenePrefix(const Scene* scene);
53-
static std::string getAsyncModelLoadKey(const Scene* scene, Entity entity, const std::string& filename);
53+
static std::string getAsyncModelLoadKey(const Scene* scene, const std::string& filename);
5454
static std::string getModelFilenameKey(const std::string& filename);
55-
std::string getAsyncModelLoadKey(Entity entity, const std::string& filename) const;
56-
std::shared_ptr<AsyncModelLoadResult> pollOrStartAsyncModelLoad(Entity entity, const std::string& filename, bool obj);
55+
std::string getAsyncModelLoadKey(const std::string& filename) const;
56+
std::shared_ptr<AsyncModelLoadResult> pollOrStartAsyncModelLoad(const std::string& filename, bool obj);
5757
static std::shared_ptr<AsyncModelLoadResult> loadModelFileOnWorker(const std::string& filename, bool obj, uint64_t buildId);
5858
template<typename T>
5959
static bool isValidGLTFIndex(int index, const std::vector<T>& values) {

0 commit comments

Comments
 (0)