Improves asset management and scanning
Enhances asset manager to improve performance and stability: - Fixes resource scanning by using a snapshot to avoid race conditions. - Adds more logging, especially for asset loading failures and filesystem errors. - Improves performance by counting textures before the scan loop. - Fixes an issue where namespace definitions were not placed on the same line. - Switches case block braces to next line
This commit is contained in:
9
.idea/editor.xml
generated
9
.idea/editor.xml
generated
@@ -255,13 +255,12 @@
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_TYPE_PARAMETER/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTIPLE_DECLARATION/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_TERNARY/@EntryValue" value="ALIGN_ALL" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ANONYMOUS_METHOD_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BLANK_LINES_AROUND_CLASS_DEFINITION/@EntryValue" value="1" type="int" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BLANK_LINES_AROUND_DECLARATIONS/@EntryValue" value="0" type="int" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BLANK_LINES_AROUND_FUNCTION_DECLARATION/@EntryValue" value="1" type="int" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BLANK_LINES_AROUND_FUNCTION_DEFINITION/@EntryValue" value="1" type="int" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BREAK_TEMPLATE_DECLARATION/@EntryValue" value="LINE_BREAK" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/CASE_BLOCK_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/CASE_BLOCK_BRACES/@EntryValue" value="NEXT_LINE" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/CONTINUOUS_LINE_INDENT/@EntryValue" value="Double" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/FREE_BLOCK_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_ACCESS_SPECIFIERS_FROM_CLASS/@EntryValue" value="false" type="bool" />
|
||||
@@ -272,18 +271,17 @@
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_STYLE/@EntryValue" value="Space" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INITIALIZER_BRACES/@EntryValue" value="END_OF_LINE_NO_SPACE" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INT_ALIGN_EQ/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INVOCABLE_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/KEEP_BLANK_LINES_IN_CODE/@EntryValue" value="2" type="int" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/KEEP_BLANK_LINES_IN_DECLARATIONS/@EntryValue" value="2" type="int" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/KEEP_USER_LINEBREAKS/@EntryValue" value="true" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/LINE_BREAK_AFTER_COLON_IN_MEMBER_INITIALIZER_LISTS/@EntryValue" value="ON_SINGLE_LINE" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/LINKAGE_SPECIFICATION_BRACES/@EntryValue" value="NEXT_LINE" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/MEMBER_INITIALIZER_LIST_STYLE/@EntryValue" value="DO_NOT_CHANGE" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/NAMESPACE_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/NAMESPACE_INDENTATION/@EntryValue" value="All" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/OTHER_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/PLACE_CATCH_ON_NEW_LINE/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/PLACE_ELSE_ON_NEW_LINE/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/PLACE_NAMESPACE_DEFINITIONS_ON_SAME_LINE/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/PLACE_NAMESPACE_DEFINITIONS_ON_SAME_LINE/@EntryValue" value="true" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/PLACE_WHILE_ON_NEW_LINE/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SIMPLE_BLOCK_STYLE/@EntryValue" value="DO_NOT_CHANGE" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_CAST_EXPRESSION_PARENTHESES/@EntryValue" value="true" type="bool" />
|
||||
@@ -328,7 +326,6 @@
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_TEMPLATE_PARAMS/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPECIAL_ELSE_IF_TREATMENT/@EntryValue" value="true" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/TAB_WIDTH/@EntryValue" value="4" type="int" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/TYPE_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_AFTER_BINARY_OPSIGN/@EntryValue" value="true" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_AFTER_DECLARATION_LPAR/@EntryValue" value="false" type="bool" />
|
||||
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_AFTER_INVOCATION_LPAR/@EntryValue" value="false" type="bool" />
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// AssetManager.cpp
|
||||
|
||||
#include "AssetManager.h"
|
||||
#include "assets/Texture2D.h"
|
||||
#include "Logger.h"
|
||||
@@ -8,6 +9,7 @@
|
||||
#include <yaml-cpp/yaml.h>
|
||||
#include <fstream>
|
||||
#include <ranges>
|
||||
#include <filesystem>
|
||||
|
||||
#include "Profiler.h"
|
||||
|
||||
@@ -19,9 +21,8 @@ namespace OX
|
||||
std::unordered_map<std::string, std::shared_ptr<Asset> > AssetManager::s_LoadedAssets;
|
||||
std::unordered_map<std::string, AssetManager::AssetMetadata> AssetManager::s_MetadataMap;
|
||||
|
||||
std::atomic<size_t> OX::AssetManager::s_TotalTexturesToLoad{0};
|
||||
std::atomic<size_t> OX::AssetManager::s_LoadedTexturesCount{0};
|
||||
|
||||
std::atomic<size_t> AssetManager::s_TotalTexturesToLoad{0};
|
||||
std::atomic<size_t> AssetManager::s_LoadedTexturesCount{0};
|
||||
|
||||
// Map from an actual file path to virtual ID
|
||||
std::unordered_map<std::string, std::string> AssetManager::s_PathToID;
|
||||
@@ -54,17 +55,16 @@ namespace OX
|
||||
void AssetManager::Shutdown()
|
||||
{
|
||||
s_Scanning = false;
|
||||
|
||||
if (s_ScanThread.joinable()) {
|
||||
if (s_ScanThread.joinable())
|
||||
s_ScanThread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void AssetManager::Rescan()
|
||||
{
|
||||
if (s_Scanning) return;
|
||||
s_Scanning = true;
|
||||
if (s_Scanning)
|
||||
return;
|
||||
|
||||
s_Scanning = true;
|
||||
s_FileTree = std::make_shared<ResourceTreeNode>();
|
||||
s_FileTree->name = "res://";
|
||||
s_FileTree->path = "res://";
|
||||
@@ -72,67 +72,92 @@ namespace OX
|
||||
s_TotalTexturesToLoad.store(0);
|
||||
s_LoadedTexturesCount.store(0);
|
||||
s_ScanThread = std::thread(BackgroundScan);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
void AssetManager::BackgroundScan()
|
||||
{
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
size_t totalTextures = 0;
|
||||
for (auto &entry: fs::recursive_directory_iterator(s_ProjectRoot)) {
|
||||
if (!s_Scanning) break;
|
||||
if (!entry.is_directory()) {
|
||||
if (DetectAssetType(entry.path()) == "texture2D")
|
||||
|
||||
// First pass: count textures
|
||||
try {
|
||||
for (auto it = fs::recursive_directory_iterator(s_ProjectRoot,
|
||||
fs::directory_options::skip_permission_denied);
|
||||
it != fs::recursive_directory_iterator(); ++it) {
|
||||
if (!s_Scanning) break;
|
||||
const auto &entry = *it;
|
||||
if (!entry.is_directory() && DetectAssetType(entry.path()) == "texture2D")
|
||||
++totalTextures;
|
||||
}
|
||||
} catch (const fs::filesystem_error &e) {
|
||||
Logger::LogError("AssetManager scan error (count): %s", e.what());
|
||||
}
|
||||
|
||||
s_TotalTexturesToLoad.store(totalTextures, std::memory_order_relaxed);
|
||||
s_LoadedTexturesCount.store(0, std::memory_order_relaxed);
|
||||
|
||||
std::vector<std::pair<std::string, AssetMetadata> > newMetadata;
|
||||
newMetadata.reserve(256);
|
||||
|
||||
// Main loop: pick up newly-added files
|
||||
while (s_Scanning) {
|
||||
std::vector<fs::directory_entry> allEntries;
|
||||
allEntries.reserve(1024);
|
||||
for (auto &entry: fs::recursive_directory_iterator(s_ProjectRoot)) {
|
||||
if (!s_Scanning) break;
|
||||
allEntries.push_back(entry);
|
||||
|
||||
// Gather directory snapshot
|
||||
try {
|
||||
for (auto it = fs::recursive_directory_iterator(s_ProjectRoot,
|
||||
fs::directory_options::skip_permission_denied);
|
||||
it != fs::recursive_directory_iterator(); ++it) {
|
||||
if (!s_Scanning) break;
|
||||
allEntries.push_back(*it);
|
||||
}
|
||||
} catch (const fs::filesystem_error &e) {
|
||||
Logger::LogError("AssetManager scan error (gather): %s", e.what());
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, AssetMetadata> > newMetadata;
|
||||
newMetadata.reserve(256);
|
||||
|
||||
newMetadata.clear();
|
||||
|
||||
for (auto &entry: allEntries) {
|
||||
if (!s_Scanning) break;
|
||||
|
||||
const auto &path = entry.path();
|
||||
std::string resPath = MakeVirtualPath(path);
|
||||
try {
|
||||
const auto &path = entry.path();
|
||||
std::string resPath = MakeVirtualPath(path);
|
||||
|
||||
if (entry.is_directory()) {
|
||||
AddToTree(resPath, true);
|
||||
continue;
|
||||
} {
|
||||
std::lock_guard<std::mutex> lock(s_AssetMutex);
|
||||
if (s_MetadataMap.count(resPath))
|
||||
if (entry.is_directory()) {
|
||||
AddToTree(resPath, true);
|
||||
continue;
|
||||
s_MetadataMap.emplace(resPath, AssetMetadata{"", ""});
|
||||
}
|
||||
AddToTree(resPath, false);
|
||||
|
||||
if (DetectAssetType(path) == "texture2D") {
|
||||
int w, h, ch;
|
||||
stbi_set_flip_vertically_on_load(1);
|
||||
unsigned char *data = stbi_load(path.string().c_str(), &w, &h, &ch, 0);
|
||||
if (!data) continue;
|
||||
|
||||
newMetadata.emplace_back(resPath, AssetMetadata{"texture2D", path.string()}); {
|
||||
std::lock_guard<std::mutex> qlock(s_QueueMutex);
|
||||
s_TextureQueue.push({resPath, w, h, ch, data});
|
||||
} {
|
||||
std::lock_guard<std::mutex> lock(s_AssetMutex);
|
||||
if (s_MetadataMap.count(resPath))
|
||||
continue;
|
||||
s_MetadataMap.emplace(resPath, AssetMetadata{"", ""});
|
||||
}
|
||||
|
||||
AddToTree(resPath, false);
|
||||
|
||||
if (DetectAssetType(path) == "texture2D") {
|
||||
int w, h, ch;
|
||||
stbi_set_flip_vertically_on_load(1);
|
||||
unsigned char *data = stbi_load(path.string().c_str(), &w, &h, &ch, 0);
|
||||
if (!data) {
|
||||
Logger::LogWarning("Failed to load texture: %s", path.string().c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
newMetadata.emplace_back(resPath, AssetMetadata{"texture2D", path.string()}); {
|
||||
std::lock_guard<std::mutex> qlock(s_QueueMutex);
|
||||
s_TextureQueue.push({resPath, w, h, ch, data});
|
||||
}
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
Logger::LogError("Error processing asset '%s': %s",
|
||||
entry.path().string().c_str(),
|
||||
e.what());
|
||||
}
|
||||
catch (...) {
|
||||
Logger::LogError("Unknown error processing asset '%s'",
|
||||
entry.path().string().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,10 +170,8 @@ namespace OX
|
||||
if (s_Scanning)
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void AssetManager::Tick()
|
||||
{
|
||||
OX_PROFILE_FUNCTION();
|
||||
@@ -158,7 +181,6 @@ namespace OX
|
||||
s_TextureQueue.pop();
|
||||
s_LoadedTexturesCount.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
|
||||
GLuint texID;
|
||||
glGenTextures(1, &texID);
|
||||
glBindTexture(GL_TEXTURE_2D, texID);
|
||||
@@ -175,55 +197,45 @@ namespace OX
|
||||
tex->SetFromGL(texID, pending.width, pending.height);
|
||||
|
||||
std::lock_guard<std::mutex> lock(s_AssetMutex);
|
||||
// Store asset by ID
|
||||
s_LoadedAssets[pending.id] = tex;
|
||||
// Also map original file path to this ID
|
||||
auto meta = s_MetadataMap[pending.id];
|
||||
s_PathToID[meta.absolutePath] = pending.id;
|
||||
}
|
||||
}
|
||||
|
||||
// Get asset by virtual ID or file path
|
||||
std::shared_ptr<Asset> AssetManager::Get(const std::string &keyOrPath)
|
||||
{
|
||||
std::string key = keyOrPath;
|
||||
// Normalize accidental triple slashes in resource path: "res:///..." -> "res://..."
|
||||
const std::string triple = "res:///";
|
||||
if (key.rfind(triple, 0) == 0) {
|
||||
if (key.rfind(triple, 0) == 0)
|
||||
key = std::string("res://") + key.substr(triple.size());
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(s_AssetMutex);
|
||||
|
||||
// 1) Direct ID lookup
|
||||
auto it = s_LoadedAssets.find(key);
|
||||
if (it != s_LoadedAssets.end()) {
|
||||
if (auto it = s_LoadedAssets.find(key); it != s_LoadedAssets.end())
|
||||
return it->second;
|
||||
}
|
||||
// 2) Path-to-ID mapping
|
||||
auto pit = s_PathToID.find(key);
|
||||
if (pit != s_PathToID.end()) {
|
||||
auto ait = s_LoadedAssets.find(pit->second);
|
||||
if (ait != s_LoadedAssets.end()) {
|
||||
|
||||
// 2) Path-to-ID
|
||||
if (auto pit = s_PathToID.find(key); pit != s_PathToID.end()) {
|
||||
if (auto ait = s_LoadedAssets.find(pit->second); ait != s_LoadedAssets.end())
|
||||
return ait->second;
|
||||
}
|
||||
}
|
||||
// 3) Convert filesystem path to virtual and lookup
|
||||
|
||||
// 3) Filesystem path -> virtual
|
||||
try {
|
||||
std::string virt = MakeVirtualPath(fs::absolute(key));
|
||||
// Normalize if virt had leading slash
|
||||
if (!virt.empty() && virt.front() == '/') virt.erase(0, 1);
|
||||
auto vit = s_LoadedAssets.find(virt);
|
||||
if (vit != s_LoadedAssets.end()) {
|
||||
if (!virt.empty() && virt.front() == '/')
|
||||
virt.erase(0, 1);
|
||||
if (auto vit = s_LoadedAssets.find(virt); vit != s_LoadedAssets.end())
|
||||
return vit->second;
|
||||
}
|
||||
} catch (...) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<ResourceTreeNode> AssetManager::GetFileTree()
|
||||
{
|
||||
return s_FileTree;
|
||||
@@ -232,7 +244,9 @@ namespace OX
|
||||
void AssetManager::SaveAssetPack(const std::string &outputPath)
|
||||
{
|
||||
YAML::Emitter out;
|
||||
out << YAML::BeginMap << YAML::Key << "assets" << YAML::Value << YAML::BeginSeq; {
|
||||
out << YAML::BeginMap
|
||||
<< YAML::Key << "assets"
|
||||
<< YAML::Value << YAML::BeginSeq; {
|
||||
std::lock_guard<std::mutex> lock(s_AssetMutex);
|
||||
for (auto &[id, meta]: s_MetadataMap) {
|
||||
fs::path rel = fs::relative(meta.absolutePath, s_ProjectRoot);
|
||||
@@ -246,7 +260,16 @@ namespace OX
|
||||
out << YAML::EndSeq << YAML::EndMap;
|
||||
|
||||
std::ofstream fout(outputPath);
|
||||
if (!fout.is_open()) {
|
||||
Logger::LogError("AssetManager: failed to open '%s' for writing", outputPath.c_str());
|
||||
return;
|
||||
}
|
||||
fout << out.c_str();
|
||||
if (!fout.good()) {
|
||||
Logger::LogError("AssetManager: error writing to '%s'", outputPath.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
Logger::LogInfo("Saved asset pack: %s", outputPath.c_str());
|
||||
}
|
||||
|
||||
@@ -261,12 +284,14 @@ namespace OX
|
||||
std::vector<std::string> parts;
|
||||
size_t pos = 0, next;
|
||||
while ((next = resPath.find('/', pos)) != std::string::npos) {
|
||||
if (next != pos) parts.push_back(resPath.substr(pos, next - pos));
|
||||
if (next != pos)
|
||||
parts.push_back(resPath.substr(pos, next - pos));
|
||||
pos = next + 1;
|
||||
}
|
||||
if (pos < resPath.size()) parts.push_back(resPath.substr(pos));
|
||||
std::lock_guard<std::mutex> lock(AssetManager::s_TreeMutex);
|
||||
if (pos < resPath.size())
|
||||
parts.push_back(resPath.substr(pos));
|
||||
|
||||
std::lock_guard<std::mutex> lock(s_TreeMutex);
|
||||
auto current = s_FileTree;
|
||||
for (size_t i = 1; i < parts.size(); ++i) {
|
||||
auto &name = parts[i];
|
||||
@@ -288,7 +313,8 @@ namespace OX
|
||||
std::string AssetManager::DetectAssetType(const fs::path &path)
|
||||
{
|
||||
auto e = path.extension().string();
|
||||
if (e == ".png" || e == ".jpg" || e == ".jpeg") return "texture2D";
|
||||
if (e == ".png" || e == ".jpg" || e == ".jpeg")
|
||||
return "texture2D";
|
||||
return "";
|
||||
}
|
||||
} // namespace OX
|
||||
|
||||
Reference in New Issue
Block a user