Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

306 multithread asset importing #308

Merged
merged 21 commits into from
Aug 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ void GUI_Window_ImportModelDirectory::Draw() {
for (const auto &Entry : std::filesystem::recursive_directory_iterator(Path)) {
std::string FilePath{Entry.path().u8string()};
if (EndsWith(FilePath, ".fbx") || EndsWith(FilePath, ".dae") || EndsWith(FilePath, ".obj") || EndsWith(FilePath, ".gltf") || EndsWith(FilePath, ".glb")) {
SystemUtils_->Logger_->Log(std::string("Adding Model '") + FilePath + "' To Import Queue", 4);
SystemUtils_->Logger_->Log(std::string("Adding Model '") + FilePath + "' To Import Queue", 5);
FilePaths.push_back(FilePath);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
struct ERS_STRUCT_ModelWriterData {


const aiScene* ModelScene; /**<Pointer to assimp scene decoded by the importer*/
aiScene* ModelScene; /**<Pointer to assimp scene decoded by the importer*/
Assimp::Importer ModelImporter; /**<This owns the scene, so we keep it around*/
ERS_STRUCT_Model* Model; /**<Pointer to the model to be saved by the writer*/
long ModelAssetID; /**<Asset ID of model geometry*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ void ERS_CLASS_ExternalModelLoader::MergeTextures(ERS_STRUCT_Model* Model, std::
void ERS_CLASS_ExternalModelLoader::ProcessModelTextures(ERS_STRUCT_ModelWriterData &Data) {

// Create List Of Texture Files To Be Copied
std::vector<std::pair<std::string, FIBITMAP*>> ImageBytes;
std::vector<std::pair<std::string, std::future<FIBITMAP*>>> ImageFutures;
for (int i = 0; (long)i < (long)Data.TextureList.size(); i++) {

ERS_STRUCT_IOData IOData;
Expand Down Expand Up @@ -271,21 +271,36 @@ void ERS_CLASS_ExternalModelLoader::ProcessModelTextures(ERS_STRUCT_ModelWriterD
}

if (Success || SecondTryStatus) {
SystemUtils_->Logger_->Log(std::string("Loading Texture Image '") + TexturePath + "'", 4);
SystemUtils_->Logger_->Log(std::string("Starting Thread To Load Texture Image '") + TexturePath + "'", 3);

FIMEMORY* FIImageData = FreeImage_OpenMemory(IOData.Data.get(), IOData.Size_B);
FREE_IMAGE_FORMAT Format = FreeImage_GetFileTypeFromMemory(FIImageData);
FIBITMAP* RawImage = FreeImage_LoadFromMemory(Format, FIImageData);
FreeImage_CloseMemory(FIImageData);

FIBITMAP* Image = FreeImage_ConvertTo32Bits(RawImage);
FreeImage_Unload(RawImage);
SystemUtils_->Logger_->Log(std::string("Loaded Texture Image"), 3);
ImageFutures.push_back(std::make_pair(TexturePath, std::async(std::launch::async,
[&IOData, StartArg = 1]() {
FIMEMORY* FIImageData = FreeImage_OpenMemory(IOData.Data.get(), IOData.Size_B);
FREE_IMAGE_FORMAT Format = FreeImage_GetFileTypeFromMemory(FIImageData);
FIBITMAP* RawImage = FreeImage_LoadFromMemory(Format, FIImageData);
FreeImage_CloseMemory(FIImageData);

FIBITMAP* Image = FreeImage_ConvertTo32Bits(RawImage);
FreeImage_Unload(RawImage);
return Image;
}
)));
SystemUtils_->Logger_->Log(std::string("Created Loader Thread For Image"), 2);

ImageBytes.push_back(std::make_pair(TexturePath, Image));
//ImageFutures.push_back(std::make_pair(TexturePath, Image));
}
}

// Get Futures
std::vector<std::pair<std::string, FIBITMAP*>> ImageBytes;
for (unsigned int i = 0; i < ImageFutures.size(); i++) {
std::string ImagePath = ImageFutures[i].first;
SystemUtils_->Logger_->Log(std::string("Getting Image Bitmap From Thread For Texture '") + ImagePath + "'", 4);
FIBITMAP* Image = ImageFutures[i].second.get();
ImageBytes.push_back(std::make_pair(ImagePath, Image));
}


// Remove Duplicate Stuff (Like Alpha Maps), Just Generally Consolidate Stuff
MergeTextures(Data.Model, &ImageBytes);

Expand Down Expand Up @@ -468,6 +483,29 @@ bool ERS_CLASS_ExternalModelLoader::ReadFile(std::string FilePath, ERS_STRUCT_IO

}

bool ERS_CLASS_ExternalModelLoader::PerformModelSanityChecks(ERS_STRUCT_Model &Model) {

// Check For Meshes
if (Model.Meshes.size() == 0) {
SystemUtils_->Logger_->Log(std::string("Model Has No Meshes, Aborting"), 8);
return false;
}

// Check Meshes For Verts
for (unsigned int i = 0; i < Model.Meshes.size(); i++) {
if (Model.Meshes[i].Vertices.size() == 0) {
SystemUtils_->Logger_->Log(std::string("Warning, Mesh '") + std::to_string(i) + "' Has No Verts", 7);
return false;
}
if (Model.Meshes[i].Indices.size() == 0) {
SystemUtils_->Logger_->Log(std::string("Warning, Mesh '") + std::to_string(i) + "' Has No Indices", 7);
return false;
}
}

// Passed Check
return true;
}

// Load Model From File
bool ERS_CLASS_ExternalModelLoader::LoadModel(std::string ModelPath, ERS_STRUCT_ModelWriterData &Data) {
Expand All @@ -482,18 +520,22 @@ bool ERS_CLASS_ExternalModelLoader::LoadModel(std::string ModelPath, ERS_STRUCT_
const aiScene* Scene = Data.ModelImporter.ReadFile(ModelPath, aiProcess_Triangulate | aiProcess_GenSmoothNormals | aiProcess_FlipUVs | aiProcess_CalcTangentSpace | aiProcess_PreTransformVertices | aiProcess_JoinIdenticalVertices);
if (!Scene || Scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !Scene->mRootNode) {
SystemUtils_->Logger_->Log(std::string(std::string("External Model Loading Error: ") + std::string(Data.ModelImporter.GetErrorString())).c_str(), 10);
Data.ModelScene = nullptr;
return false;
}
SystemUtils_->Logger_->Log("Finished Loading External Model, Processing Geometry/Textures", 3);

// Process Geometry, Identify Textures
ProcessNode(Data, Data.Model, Scene->mRootNode, Scene, ModelDirectory);
if (!PerformModelSanityChecks(*Data.Model)) {
return false;
}
DetectBoundingBox(Data.Model);
CalculateTotalVertsIndices(Data.Model);

// Update Struct
Data.ModelOriginDirectoryPath = ModelPath;
Data.ModelScene = Scene;
Data.ModelScene = (aiScene*)Scene;
Data.ModelFileName = ModelFileName;

// Load Textures
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,16 @@ class ERS_CLASS_ExternalModelLoader {
*/
void MergeTextures(ERS_STRUCT_Model* Model, std::vector<std::pair<std::string, FIBITMAP*>>* LoadedTextures);

/**
* @brief Just makes sure the model has valid sata, etc.
* returns false if there's an issue so we can abort it
*
* @param Model
* @return true
* @return false
*/
bool PerformModelSanityChecks(ERS_STRUCT_Model &Model);

public:


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ void ERS_CLASS_ModelImporter::ImportThread() {
}
BlockThread_.unlock();

// Check Queue, Import As Needed
// Check Queue, Import As Needed, Empty Processing Items That Are Done
LockAssetImportQueue_.lock();
if (AssetImportQueue_.size() > 0) {

Expand All @@ -59,11 +59,12 @@ void ERS_CLASS_ModelImporter::ImportThread() {
LockAssetImportQueue_.unlock();


ERS_STRUCT_Model Model;
ERS_STRUCT_ModelWriterData ModelData;
ModelData.Model = &Model;
ModelLoader_->LoadModel(AssetPath, ModelData);
ModelWriter_->WriteModel(ModelData);
ERS_STRUCT_Model Model;
ERS_STRUCT_ModelWriterData ModelData;
ModelData.Model = &Model;
ModelLoader_->LoadModel(AssetPath, ModelData);
ModelWriter_->WriteModel(ModelData);



LockAssetImportQueue_.lock();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <string>
#include <vector>
#include <mutex>
#include <future>
#include <thread>

// Third-Party Libraries (BG convention: use <> instead of "")
Expand Down Expand Up @@ -37,6 +38,7 @@ class ERS_CLASS_ModelImporter {
bool HasJobFinished_ = false; /**<Indicate If A Job Has Finished*/
std::thread ImportThread_; /**<Import Processor Thread*/
std::vector<std::string> AssetImportQueue_; /**<List of assets to be imported, accessed by other threads so use mutex to control access*/
std::vector<std::future<void>> ProcessingItems_; /**<List of items currently being worked on by the threads*/

std::unique_ptr<ERS_CLASS_ModelWriter> ModelWriter_; /**<Instance of the model writer, used to save models to the ERS project*/
std::unique_ptr<ERS_CLASS_ExternalModelLoader> ModelLoader_; /**<Used to load models from outside the ERS project*/
Expand Down
47 changes: 37 additions & 10 deletions Source/Core/Writers/ERS_ModelWriter/ERS_ModelWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ bool ERS_CLASS_ModelWriter::WriteModelGeometry(ERS_STRUCT_ModelWriterData &Data,
// Export Model File
Logger_->Log(std::string("Exporting Model Geometry To Blob With Encoding '") + ExportFormat + "'", 4);

if (!Data.ModelScene) {
Logger_->Log("Error Exporting Scene, Scene Is Not Valid", 7);
return false;
}
if (!(bool)Data.ModelScene->HasMeshes()) {
Logger_->Log("Error Exporting Scene, Scene Is Not Valid", 7);
return false;
}
Assimp::Exporter Exporter;
const aiExportDataBlob* Blob = Exporter.ExportToBlob(Data.ModelScene, ExportFormat);

Expand Down Expand Up @@ -247,6 +255,7 @@ void ERS_CLASS_ModelWriter::WriteTextures(ERS_STRUCT_ModelWriterData &Data, std:
}
std::string ERS_CLASS_ModelWriter::GenerateModelMetadata(ERS_STRUCT_ModelWriterData &Data) {


// Generate Metadata
YAML::Emitter MetadataEmitter;
MetadataEmitter<<YAML::BeginMap;
Expand All @@ -267,19 +276,24 @@ std::string ERS_CLASS_ModelWriter::GenerateModelMetadata(ERS_STRUCT_ModelWriterD

// Set Path For Each Texture, Iterate OVer All Levels Of This Texture
std::string TexturePath = Data.TextureNames[i];//TextureList_[i].substr(TextureList_[i].find_last_of("/")+1, TextureList_[i].size()-(TextureList_[i].find_last_of("/")+1));
Logger_->Log(std::string("Saving Information For Texture '") + TexturePath + "'", 3);
MetadataEmitter<<YAML::Key<<TexturePath<<YAML::Value<<YAML::BeginMap;
if (TexturePath.find('*') != std::string::npos) {
Logger_->Log(std::string("Skipping Texture '") + TexturePath + "' Due To Invalid Char", 7);
} else {

for (unsigned int TextureLevel = 0; TextureLevel < Data.TextureMemorySizes[i].size(); TextureLevel++) {
MetadataEmitter<<YAML::Key<<(Data.TextureMemorySizes[i].size() - 1) - TextureLevel<<YAML::Value<<YAML::BeginMap;
Logger_->Log(std::string("Saving Information For Texture '") + TexturePath + "'", 3);
MetadataEmitter<<YAML::Key<<TexturePath<<YAML::Value<<YAML::BeginMap;

MetadataEmitter<<YAML::Key<<"TextureLevelAssetID"<<YAML::Value<<Data.ImageAssetIDs[i][TextureLevel];
MetadataEmitter<<YAML::Key<<"TextureLevelMemorySizeBytes"<<YAML::Value<<Data.TextureMemorySizes[i][TextureLevel];
MetadataEmitter<<YAML::Key<<"TextureLevelResolutionX"<<YAML::Value<<Data.ImageResolutions[i][TextureLevel].first;
MetadataEmitter<<YAML::Key<<"TextureLevelResolutionY"<<YAML::Value<<Data.ImageResolutions[i][TextureLevel].second;
MetadataEmitter<<YAML::Key<<"TextureLevelNumberChannels"<<YAML::Value<<Data.ImageChannels[i][TextureLevel];
for (unsigned int TextureLevel = 0; TextureLevel < Data.TextureMemorySizes[i].size(); TextureLevel++) {
MetadataEmitter<<YAML::Key<<(Data.TextureMemorySizes[i].size() - 1) - TextureLevel<<YAML::Value<<YAML::BeginMap;

MetadataEmitter<<YAML::EndMap;
MetadataEmitter<<YAML::Key<<"TextureLevelAssetID"<<YAML::Value<<Data.ImageAssetIDs[i][TextureLevel];
MetadataEmitter<<YAML::Key<<"TextureLevelMemorySizeBytes"<<YAML::Value<<Data.TextureMemorySizes[i][TextureLevel];
MetadataEmitter<<YAML::Key<<"TextureLevelResolutionX"<<YAML::Value<<Data.ImageResolutions[i][TextureLevel].first;
MetadataEmitter<<YAML::Key<<"TextureLevelResolutionY"<<YAML::Value<<Data.ImageResolutions[i][TextureLevel].second;
MetadataEmitter<<YAML::Key<<"TextureLevelNumberChannels"<<YAML::Value<<Data.ImageChannels[i][TextureLevel];

MetadataEmitter<<YAML::EndMap;
}
}
MetadataEmitter<<YAML::EndMap;
}
Expand Down Expand Up @@ -311,6 +325,19 @@ void ERS_CLASS_ModelWriter::WriteModel(ERS_STRUCT_ModelWriterData &Data) {
// Write
WriteModelGeometry(Data);
WriteTextures(Data, &Data.TextureMemorySizes, &Data.ImageAssetIDs, &Data.ImageResolutions, &Data.ImageChannels);

// Perform List Length Sanity Check
unsigned int CheckSize = Data.TextureList.size();
bool CheckFail = CheckSize != Data.ImageChannels.size();
CheckFail |= CheckSize != Data.ImageAssetIDs.size();
CheckFail |= CheckSize != Data.TextureMemorySizes.size();
CheckFail |= CheckSize != Data.ImageResolutions.size();

if (CheckFail) {
Logger_->Log("Model Failed List Length Sanity Check, Not All Lists Are Of Same Len, Aborting", 8);
return;
}

std::string Metadata = GenerateModelMetadata(Data);

// Write Metadata
Expand Down