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:
2025-07-14 00:35:10 -05:00
parent fd956a64d6
commit a2106817f6
2 changed files with 104 additions and 81 deletions

9
.idea/editor.xml generated
View File

@@ -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" />

View File

@@ -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