Merge pull request #32 from teamfisk/AsyncResourceLoad

Async resource load
This commit is contained in:
2016-01-15 16:20:36 +01:00
21 changed files with 371 additions and 212 deletions
+1 -1
Submodule assets updated: 6cbf2365d4...a3c92ac876
+4 -4
View File
@@ -7,10 +7,10 @@
#include <vector> #include <vector>
#include "Core/Ray.h" #include "../Core/Ray.h"
#include "Core/AABB.h" #include "../Core/AABB.h"
#include "Engine/Rendering/RawModel.h" #include "../Rendering/RawModel.h"
#include "Core/Entity.h" #include "../Core/Entity.h"
class World; class World;
struct ComponentWrapper; struct ComponentWrapper;
+4 -4
View File
@@ -4,10 +4,10 @@
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include <glm/common.hpp> #include <glm/common.hpp>
#include "Common.h" #include "../Common.h"
#include "Core/System.h" #include "../Core/System.h"
#include "Core/EventBroker.h" #include "../Core/EventBroker.h"
#include "Core/EKeyUp.h" #include "../Core/EKeyUp.h"
class CollisionSystem : public PureSystem class CollisionSystem : public PureSystem
{ {
+2 -2
View File
@@ -4,8 +4,8 @@
#include <glm/common.hpp> #include <glm/common.hpp>
#include <unordered_set> #include <unordered_set>
#include "Core/System.h" #include "../Core/System.h"
#include "Core/EventBroker.h" #include "../Core/EventBroker.h"
#include "ETrigger.h" #include "ETrigger.h"
class AABB; class AABB;
+1 -1
View File
@@ -2,7 +2,7 @@
#define Ray_h__ #define Ray_h__
#include "../GLM.h" #include "../GLM.h"
#include "Common.h" #include "../Common.h"
class Ray class Ray
{ {
+132 -39
View File
@@ -12,7 +12,6 @@
/** Base Resource class. /** Base Resource class.
Implement this class for every resource to be handled by the resource manager. Implement this class for every resource to be handled by the resource manager.
Implement Create() to return a new object of that type.
*/ */
class Resource class Resource
{ {
@@ -22,6 +21,23 @@ protected:
Resource() { } Resource() { }
public: public:
//Should be thrown in a Resource's constructor if it cannot complete because another resource is still loading.
//Not actually an error, just a message to the ResourceManager.
struct StillLoadingException : public std::exception
{
virtual const char* what() const throw()
{
return "Resource is still loading.";
}
};
struct FailedLoadingException : public std::exception
{
virtual const char* what() const throw()
{
return "Resource is failed to load.";
}
};
// Pretend that this is a pure virtual function that you have to implement // Pretend that this is a pure virtual function that you have to implement
// FIXME: Why did we do this again instead of just using the constructor? // FIXME: Why did we do this again instead of just using the constructor?
// static Resource* Create(std::string resourceName); // static Resource* Create(std::string resourceName);
@@ -33,6 +49,15 @@ public:
unsigned int ResourceID; unsigned int ResourceID;
}; };
//Any class inheriting from this class will always be loaded on the master thread, not on a parallel worker thread.
//This is important in case some instructions must be executed on the main thread, e.g. OpenGL commands, like glBindBuffer.
//This resource can still be loaded asyncronously, but it will not be loaded in a thread, instead it's constructor will
//be called once on every ResourceManager::Load, just throw StillLoadingException in the constructor if it is not done yet.
class ThreadUnsafeResource : public Resource
{
friend class ResourceManager;
};
/** Singleton resource manager to keep track of and cache any external engine assets */ /** Singleton resource manager to keep track of and cache any external engine assets */
class ResourceManager class ResourceManager
{ {
@@ -40,6 +65,7 @@ private:
ResourceManager(); ResourceManager();
public: public:
static bool UseThreading;
/*static ResourceManager& Instance() /*static ResourceManager& Instance()
{ {
static ResourceManager s; static ResourceManager s;
@@ -49,15 +75,6 @@ public:
template <typename T> template <typename T>
static void RegisterType(std::string typeName); static void RegisterType(std::string typeName);
/** Preloads a resource and caches it for future use
@tparam T Resource type.
@param resourceName Fully qualified name of the resource to preload.
*/
template <typename T>
static void Preload(std::string resourceName);
static void Preload(std::string resourceType, std::string resourceName);
/** Checks if a resource is in cache /** Checks if a resource is in cache
@param resourceType Resource type as string. @param resourceType Resource type as string.
@@ -65,15 +82,20 @@ public:
*/ */
// TODO: Templateify // TODO: Templateify
static bool IsResourceLoaded(std::string resourceType, std::string resourceName); static bool IsResourceLoaded(std::string resourceType, std::string resourceName);
/** Return value should always be a valid pointer, will throw an exception on error.
If the resource has been loaded already, returns a pointer to it.
/** Hot-loads a resource and caches it for future use If Async is false: Hot-loads a resource, caches it for future use, and returns a pointer to it.
If Async is true: If the resource is not loaded yet, starts loading the resource
in the background and throws Resource::StillLoadingException immediately.
@tparam T Resource type. @tparam T Resource type.
@tparam async Set this to true if the resource should be loaded asyncronously.
@param resourceName Fully qualified name of the resource to load. @param resourceName Fully qualified name of the resource to load.
*/ */
template <typename T> template <typename T, bool async = false>
static T* Load(std::string resourceName, Resource* parent = nullptr); static T* Load(const std::string& resourceName, Resource* parent = nullptr);
static Resource* Load(std::string resourceType, std::string resourceName, Resource* parent = nullptr);
/** Reloads an already loaded resource, keeping its resource ID intact. /** Reloads an already loaded resource, keeping its resource ID intact.
@@ -87,19 +109,31 @@ public:
static void Update(); static void Update();
private: private:
//This is a haxy way to make sure that IsMainThread is run when the program starts, so the master thread id is set.
struct MasterThreadChecker
{
MasterThreadChecker()
{
ResourceManager::IsMainThread();
}
};
const static MasterThreadChecker m_Checker;
static std::unordered_map<std::string, std::string> m_CompilerTypenameToResourceType; static std::unordered_map<std::string, std::string> m_CompilerTypenameToResourceType;
static std::unordered_map<std::string, std::function<Resource*(std::string)>> m_FactoryFunctions; // type -> factory function static std::unordered_map<std::string, std::function<Resource*(std::string)>> m_FactoryFunctions; // type -> factory function
static std::unordered_map<std::pair<std::string, std::string>, Resource*> m_ResourceCache; // (type, name) -> resource static std::unordered_map<std::pair<std::string, std::string>, Resource*> m_ResourceCache; // (type, name) -> resource
static std::unordered_map<std::string, Resource*> m_ResourceFromName; // name -> resource static std::unordered_map<std::string, Resource*> m_ResourceFromName; // name -> resource
static std::unordered_map<Resource*, Resource*> m_ResourceParents; // resource -> parent resource static std::unordered_map<Resource*, Resource*> m_ResourceParents; // resource -> parent resource
static std::unordered_map<std::pair<std::string, std::string>, boost::thread> m_LoadingThreads; // (type, name) -> loading thread
static std::unordered_map<std::pair<std::string, std::string>, std::exception_ptr> m_LoadingThreadExceptions; // (type, name) -> exceptions
static boost::recursive_mutex m_Mutex;
// TODO: Getters for IDs // TODO: Getters for IDs
static unsigned int m_CurrentResourceTypeID; static unsigned int m_CurrentResourceTypeID;
static std::unordered_map<std::string, unsigned int> m_ResourceTypeIDs; static std::unordered_map<std::string, unsigned int> m_ResourceTypeIDs;
// Number of resources of a type. Doubles as local ID. // Number of resources of a type. Doubles as local ID.
static std::unordered_map<unsigned int, unsigned int> m_ResourceCount; static std::unordered_map<unsigned int, unsigned int> m_ResourceCount;
// Flag to suppress hot-load warnings when a preloading resource chain loads another resource
static bool m_Preloading;
static FileWatcher m_FileWatcher; static FileWatcher m_FileWatcher;
static void fileWatcherCallback(std::string path, FileWatcher::FileEventFlags flags); static void fileWatcherCallback(std::string path, FileWatcher::FileEventFlags flags);
@@ -108,22 +142,13 @@ private:
static unsigned int GetNewResourceID(unsigned int typeID); static unsigned int GetNewResourceID(unsigned int typeID);
// Internal: Create a resource and cache it // Internal: Create a resource and cache it
static Resource* CreateResource(std::string resourceType, std::string resourceName, Resource* parent); static Resource* createResourceThrowing(const std::string& resourceType, const std::string& resourceName, Resource* parent);
static Resource* createResource(const std::string& resourceType, const std::string& resourceName, Resource* parent, std::exception_ptr& exception);
static Resource* cacheResource(Resource* resource, const std::string& resourceType, const std::string& resourceName, Resource* parent);
static bool IsMainThread();
}; };
template <typename T>
T* ResourceManager::Load(std::string resourceName, Resource* parent /* = nullptr */)
{
auto resourceTypename = typeid(T).name();
auto it = m_CompilerTypenameToResourceType.find(resourceTypename);
if (it == m_CompilerTypenameToResourceType.end()) {
LOG_ERROR("Failed to load resource \"%s\" of type \"%s\": type not registered", resourceName.c_str(), resourceTypename);
return nullptr;
}
return static_cast<T*>(Load(it->second, resourceName, parent));
}
template <typename T> template <typename T>
void ResourceManager::RegisterType(std::string typeName) void ResourceManager::RegisterType(std::string typeName)
{ {
@@ -131,17 +156,85 @@ void ResourceManager::RegisterType(std::string typeName)
m_FactoryFunctions[typeName] = [](std::string resourceName) { return new T(resourceName); }; m_FactoryFunctions[typeName] = [](std::string resourceName) { return new T(resourceName); };
} }
template <typename T> template <typename T, bool async>
void ResourceManager::Preload(std::string resourceName) static T* ResourceManager::Load(const std::string& resourceName, Resource* parent /* = nullptr */)
{ {
auto resourceTypename = typeid(T).name(); auto resourceTypename = typeid(T).name();
auto it = m_CompilerTypenameToResourceType.find(resourceTypename); auto iter = m_CompilerTypenameToResourceType.find(resourceTypename);
if (it == m_CompilerTypenameToResourceType.end()) { if (iter == m_CompilerTypenameToResourceType.end()) {
LOG_ERROR("Failed to load resource \"%s\" of type \"%s\": type not registered", resourceName.c_str(), resourceTypename); LOG_ERROR("Failed to load resource \"%s\" of type \"%s\": type not registered", resourceName.c_str(), resourceTypename);
return; throw Resource::FailedLoadingException();
} }
Preload(it->second, resourceName); std::string resourceType = iter->second;
constexpr bool mustNotLoadInThread = std::is_base_of<ThreadUnsafeResource, T>::value;
if (mustNotLoadInThread && !IsMainThread()) {
LOG_ERROR("Failed to Load \"%s\": ThreadUnsafeResource type \"%s\" load in the constructor of another resource that is loaded asyncronously.", resourceName.c_str(), iter->second.c_str());
throw Resource::FailedLoadingException();
}
auto cacheKey = std::make_pair(resourceType, resourceName);
decltype(m_ResourceCache)::iterator it;
//If a thread has already been launched to load this resource.
auto tIt = m_LoadingThreads.find(cacheKey);
if (UseThreading && tIt != m_LoadingThreads.end()) {
if (async) {
//Throw StillLoadingException if the thread is still working.
if (!tIt->second.try_join_for(boost::chrono::nanoseconds(1))) {
throw Resource::StillLoadingException();
}
//Else we know the thread has completed.
} else {
//Wait for the thread to finish loading.
tIt->second.join();
}
//When the thread is done, delete the thread.
m_LoadingThreads.erase(tIt);
//Rethrow the thread exception if it threw any.
auto excIt = m_LoadingThreadExceptions.find(cacheKey);
std::exception_ptr exception = excIt->second;
m_LoadingThreadExceptions.erase(excIt);
if (exception) {
std::rethrow_exception(exception);
}
}
//If resource has already been cached and completely loaded.
it = m_ResourceCache.find(cacheKey);
if (it != m_ResourceCache.end()) {
if (it->second != nullptr) {
return static_cast<T*>(it->second);
} else {
//Don't return null on failure, exception instead.
throw Resource::FailedLoadingException();
}
}
//If resource is not cached..
if (UseThreading && async) {
if (mustNotLoadInThread) {
try {
return static_cast<T*>(createResourceThrowing(resourceType, resourceName, parent));
} catch (const Resource::StillLoadingException&) {
throw;
}
} else {
//Create a thread that loads the resource into cache.
m_LoadingThreads[cacheKey] = boost::thread(createResource, resourceType, resourceName, parent, m_LoadingThreadExceptions[cacheKey]);
throw Resource::StillLoadingException();
}
} else {
//load and return the resource.
while (true) {
try {
return static_cast<T*>(createResourceThrowing(resourceType, resourceName, parent));
} catch (const Resource::StillLoadingException&) {
continue;
} catch (const std::exception&) {
throw;
}
}
}
} }
#endif #endif
+1 -1
View File
@@ -3,7 +3,7 @@
#include "../Core/ResourceManager.h" #include "../Core/ResourceManager.h"
class BaseTexture : public Resource class BaseTexture : public ThreadUnsafeResource
{ {
friend class ResourceManager; friend class ResourceManager;
+5 -1
View File
@@ -4,7 +4,7 @@
#include "RawModel.h" #include "RawModel.h"
#include "../OpenGL.h" #include "../OpenGL.h"
class Model : public RawModel class Model : public ThreadUnsafeResource
{ {
friend class ResourceManager; friend class ResourceManager;
@@ -13,11 +13,15 @@ private:
public: public:
~Model(); ~Model();
const std::vector<RawModel::MaterialGroup>& MaterialGroups() const { return m_RawModel->MaterialGroups; }
const glm::mat4& Matrix() const { return m_RawModel->m_Matrix; }
const std::vector<RawModel::Vertex>& Vertices() const { return m_RawModel->m_Vertices; }
GLuint VAO; GLuint VAO;
GLuint ElementBuffer; GLuint ElementBuffer;
private: private:
RawModel* m_RawModel;
GLuint VertexBuffer; GLuint VertexBuffer;
GLuint DiffuseVertexColorBuffer; GLuint DiffuseVertexColorBuffer;
GLuint SpecularVertexColorBuffer; GLuint SpecularVertexColorBuffer;
+7 -7
View File
@@ -15,16 +15,16 @@
struct ModelJob : RenderJob struct ModelJob : RenderJob
{ {
ModelJob(Model* model, Camera* camera, glm::mat4 matrix, ::Model::MaterialGroup texGroup, ComponentWrapper modelComponent, World* world) ModelJob(Model* model, Camera* camera, glm::mat4 matrix, ::RawModel::MaterialGroup matGroup, ComponentWrapper modelComponent, World* world)
: RenderJob() : RenderJob()
{ {
Model = model; Model = model;
TextureID = (texGroup.Texture) ? texGroup.Texture->ResourceID : 0; TextureID = (matGroup.Texture) ? matGroup.Texture->ResourceID : 0;
DiffuseTexture = texGroup.Texture.get(); DiffuseTexture = matGroup.Texture.get();
NormalTexture = texGroup.NormalMap.get(); NormalTexture = matGroup.NormalMap.get();
SpecularTexture = texGroup.SpecularMap.get(); SpecularTexture = matGroup.SpecularMap.get();
StartIndex = texGroup.StartIndex; StartIndex = matGroup.StartIndex;
EndIndex = texGroup.EndIndex; EndIndex = matGroup.EndIndex;
Matrix = matrix; Matrix = matrix;
Color = modelComponent["Color"]; Color = modelComponent["Color"];
Entity = modelComponent.EntityID; Entity = modelComponent.EntityID;
+4 -1
View File
@@ -47,14 +47,17 @@ public:
{ {
float Shininess; float Shininess;
float Transparency; float Transparency;
std::string TexturePath;
std::shared_ptr<::Texture> Texture; std::shared_ptr<::Texture> Texture;
std::string NormalMapPath;
std::shared_ptr<::Texture> NormalMap; std::shared_ptr<::Texture> NormalMap;
std::string SpecularMapPath;
std::shared_ptr<::Texture> SpecularMap; std::shared_ptr<::Texture> SpecularMap;
unsigned int StartIndex; unsigned int StartIndex;
unsigned int EndIndex; unsigned int EndIndex;
}; };
std::vector<MaterialGroup> TextureGroups; std::vector<MaterialGroup> MaterialGroups;
std::vector<Vertex> m_Vertices; std::vector<Vertex> m_Vertices;
std::vector<unsigned int> m_Indices; std::vector<unsigned int> m_Indices;
+4 -1
View File
@@ -16,4 +16,7 @@ StartNetwork=false
IsServer=false IsServer=false
Name=Bob Name=Bob
Address=127.0.0.1 Address=127.0.0.1
Port=13 Port=13
[Multithreading]
ResourceLoading=true
@@ -17,7 +17,7 @@
<Scale X="1" Y="1" Z="1"/> <Scale X="1" Y="1" Z="1"/>
</c:Transform> </c:Transform>
<c:Model> <c:Model>
<Resource>Models/ScaleWidget.obj</Resource> <Resource>Models/Core/UnitSphere.obj</Resource>
</c:Model> </c:Model>
</Components> </Components>
</Entity> </Entity>
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<Entity xmlns:c="components" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../Types/Entity.xsd">
<Components>
<c:Transform/>
</Components>
<Children>
<Entity>
<Components>
<c:Camera/>
<c:Transform>
<Position X="-8.58846378" Y="9.3929615" Z="14.3944101"/>
<Orientation X="-0.645772398" Y="-0.314115167" Z="-4.700399e-08"/>
</c:Transform>
</Components>
<Children/>
</Entity>
<Entity>
<Components>
<c:Model>
<Resource></Resource>
</c:Model>
<c:Transform/>
</Components>
<Children/>
</Entity>
</Children>
</Entity>
+3 -3
View File
@@ -225,11 +225,11 @@ bool attachAABBComponentFromModel(World* world, EntityID id)
return false; return false;
} }
glm::mat4 modelMatrix = modelRes->m_Matrix; glm::mat4 modelMatrix = modelRes->Matrix();
glm::vec3 mini = glm::vec3(INFINITY, INFINITY, INFINITY); glm::vec3 mini = glm::vec3(INFINITY, INFINITY, INFINITY);
glm::vec3 maxi = glm::vec3(-INFINITY, -INFINITY, -INFINITY); glm::vec3 maxi = glm::vec3(-INFINITY, -INFINITY, -INFINITY);
for (const auto& v : modelRes->m_Vertices) { for (const auto& v : modelRes->Vertices()) {
const auto& wPos = modelMatrix * glm::vec4(v.Position.x, v.Position.y, v.Position.z, 1); const auto& wPos = modelMatrix * glm::vec4(v.Position.x, v.Position.y, v.Position.z, 1);
maxi.x = std::max(wPos.x, maxi.x); maxi.x = std::max(wPos.x, maxi.x);
maxi.y = std::max(wPos.y, maxi.y); maxi.y = std::max(wPos.y, maxi.y);
@@ -255,7 +255,7 @@ bool GetEntityBox(World* world, ComponentWrapper& AABBComponent, AABB& outBox)
if (modelRes == nullptr) { if (modelRes == nullptr) {
return false; return false;
} }
glm::mat4 modelMatrix = modelRes->m_Matrix * glm::mat4 modelMatrix = modelRes->Matrix() *
glm::translate(glm::mat4(), (glm::vec3)cTrans["Position"]) * glm::translate(glm::mat4(), (glm::vec3)cTrans["Position"]) *
glm::scale((glm::vec3)cTrans["Scale"]); glm::scale((glm::vec3)cTrans["Scale"]);
+56 -48
View File
@@ -1,4 +1,7 @@
#include "Core/ResourceManager.h" #include "Core/ResourceManager.h"
#include "boost/thread/thread.hpp"
#include "boost/thread/mutex.hpp"
#include "boost/thread/lock_guard.hpp"
std::unordered_map<std::string, std::string> ResourceManager::m_CompilerTypenameToResourceType; std::unordered_map<std::string, std::string> ResourceManager::m_CompilerTypenameToResourceType;
std::unordered_map<std::string, std::function<Resource*(std::string)>> ResourceManager::m_FactoryFunctions; std::unordered_map<std::string, std::function<Resource*(std::string)>> ResourceManager::m_FactoryFunctions;
@@ -6,10 +9,13 @@ std::unordered_map<std::pair<std::string, std::string>, Resource*> ResourceManag
std::unordered_map<std::string, Resource*> ResourceManager::m_ResourceFromName; std::unordered_map<std::string, Resource*> ResourceManager::m_ResourceFromName;
std::unordered_map<Resource*, Resource*> ResourceManager::m_ResourceParents; std::unordered_map<Resource*, Resource*> ResourceManager::m_ResourceParents;
unsigned int ResourceManager::m_CurrentResourceTypeID = 0; unsigned int ResourceManager::m_CurrentResourceTypeID = 0;
bool ResourceManager::UseThreading = false;
std::unordered_map<std::string, unsigned int> ResourceManager::m_ResourceTypeIDs; std::unordered_map<std::string, unsigned int> ResourceManager::m_ResourceTypeIDs;
std::unordered_map<unsigned int, unsigned int> ResourceManager::m_ResourceCount; std::unordered_map<unsigned int, unsigned int> ResourceManager::m_ResourceCount;
bool ResourceManager::m_Preloading = false;
FileWatcher ResourceManager::m_FileWatcher; FileWatcher ResourceManager::m_FileWatcher;
std::unordered_map<std::pair<std::string, std::string>, boost::thread> ResourceManager::m_LoadingThreads;
std::unordered_map<std::pair<std::string, std::string>, std::exception_ptr> ResourceManager::m_LoadingThreadExceptions;
boost::recursive_mutex ResourceManager::m_Mutex;
unsigned int ResourceManager::GetTypeID(std::string resourceType) unsigned int ResourceManager::GetTypeID(std::string resourceType)
{ {
@@ -74,63 +80,65 @@ void ResourceManager::Update()
m_FileWatcher.Check(); m_FileWatcher.Check();
} }
void ResourceManager::Preload(std::string resourceType, std::string resourceName) Resource* ResourceManager::createResource(const std::string& resourceType, const std::string& resourceName, Resource* parent, std::exception_ptr& exception)
{
if (IsResourceLoaded(resourceType, resourceName)) {
//LOG_WARNING("Attempted to preload resource \"%s\" multiple times!", resourceName.c_str());
return;
}
m_Preloading = true;
LOG_INFO("Preloading resource \"%s\"", resourceName.c_str());
CreateResource(resourceType, resourceName, nullptr);
m_Preloading = false;
}
Resource* ResourceManager::Load(std::string resourceType, std::string resourceName, Resource* parent /*= nullptr*/)
{
auto it = m_ResourceCache.find(std::make_pair(resourceType, resourceName));
if (it != m_ResourceCache.end()) {
return it->second;
}
if (m_Preloading) {
LOG_INFO("Preloading resource \"%s\"", resourceName.c_str());
} else {
LOG_WARNING("Hot-loading resource \"%s\"", resourceName.c_str());
}
return CreateResource(resourceType, resourceName, parent);
}
Resource* ResourceManager::CreateResource(std::string resourceType, std::string resourceName, Resource* parent)
{ {
auto facIt = m_FactoryFunctions.find(resourceType); auto facIt = m_FactoryFunctions.find(resourceType);
if (facIt == m_FactoryFunctions.end()) { if (facIt == m_FactoryFunctions.end()) {
LOG_ERROR("Failed to load resource \"%s\" of type \"%s\": type not registered", resourceName.c_str(), resourceType.c_str()); LOG_ERROR("Failed to load resource \"%s\" of type \"%s\": type not registered", resourceName.c_str(), resourceType.c_str());
return nullptr; cacheResource(nullptr, resourceType, resourceName, parent);
//This basically throws an exception.
exception = std::make_exception_ptr(Resource::FailedLoadingException()); return nullptr;
} }
// Call the factory function // Call the factory function
Resource* resource;
try { try {
resource = facIt->second(resourceName); return cacheResource(facIt->second(resourceName), resourceType, resourceName, parent);
} catch (const Resource::StillLoadingException&) {
exception = std::current_exception(); return nullptr;
} catch (const std::exception& e) {
LOG_ERROR("Failed to load resource \"%s\" of type \"%s\": %s", resourceName.c_str(), resourceType.c_str(), e.what());
cacheResource(nullptr, resourceType, resourceName, parent);
exception = std::current_exception(); return nullptr;
}
}
Resource* ResourceManager::createResourceThrowing(const std::string& resourceType, const std::string& resourceName, Resource* parent)
{
std::exception_ptr exception;
Resource* res = createResource(resourceType, resourceName, parent, exception);
if (exception) {
std::rethrow_exception(exception);
}
return res;
}
Resource* ResourceManager::cacheResource(Resource* resource, const std::string& resourceType, const std::string& resourceName, Resource* parent)
{
//Lock the mutex immediately, and unlock it when leaving the code block.
boost::lock_guard<decltype(m_Mutex)> guard(m_Mutex);
if (resource != nullptr) {
// Store IDs // Store IDs
resource->TypeID = GetTypeID(resourceType); resource->TypeID = GetTypeID(resourceType);
resource->ResourceID = GetNewResourceID(resource->TypeID); resource->ResourceID = GetNewResourceID(resource->TypeID);
} catch (const std::exception& e) {
resource = nullptr;
LOG_ERROR("Failed to load resource \"%s\" of type \"%s\": %s", resourceName.c_str(), resourceType.c_str(), e.what());
} }
// Cache
m_ResourceCache[std::make_pair(resourceType, resourceName)] = resource; // Cache
m_ResourceFromName[resourceName] = resource; m_ResourceCache[std::make_pair(resourceType, resourceName)] = resource;
if (parent != nullptr) { m_ResourceFromName[resourceName] = resource;
m_ResourceParents[resource] = parent; if (parent != nullptr) {
} m_ResourceParents[resource] = parent;
if (!boost::filesystem::is_directory(resourceName)) { }
LOG_DEBUG("Adding watch for %s", resourceName.c_str());
m_FileWatcher.AddWatch(resourceName, fileWatcherCallback); //if (!boost::filesystem::is_directory(resourceName)) {
} // LOG_DEBUG("Adding watch for %s", resourceName.c_str());
return resource; // m_FileWatcher.AddWatch(resourceName, fileWatcherCallback);
//}
return resource;
}
bool ResourceManager::IsMainThread()
{
static boost::thread::id MainThreadId = boost::this_thread::get_id();
return boost::this_thread::get_id() == MainThreadId;
} }
+62 -48
View File
@@ -1,60 +1,74 @@
#include "Rendering/Model.h" #include "Rendering/Model.h"
Model::Model(std::string fileName) Model::Model(std::string fileName)
: RawModel(fileName)
{ {
// Generate GL buffers //Try loading the model asyncronously, if it throws any exceptions then let it propagate back to caller.
GLuint buffer; m_RawModel = ResourceManager::Load<RawModel, true>(fileName);
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, m_Vertices.size() * sizeof(Vertex), &m_Vertices[0], GL_STATIC_DRAW);
glGenBuffers(1, &ElementBuffer); for (auto& group : m_RawModel->MaterialGroups) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ElementBuffer); if (!group.TexturePath.empty()) {
glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_Indices.size() * sizeof(unsigned int), &m_Indices[0], GL_STATIC_DRAW); group.Texture = std::shared_ptr<Texture>(ResourceManager::Load<Texture>(group.TexturePath));
}
if (!group.NormalMapPath.empty()) {
group.NormalMap = std::shared_ptr<Texture>(ResourceManager::Load<Texture>(group.NormalMapPath));
}
if (!group.SpecularMapPath.empty()) {
group.SpecularMap = std::shared_ptr<Texture>(ResourceManager::Load<Texture>(group.SpecularMapPath));
}
}
glGenVertexArrays(1, &VAO); // Generate GL buffers
glBindVertexArray(VAO); GLuint buffer;
GLERROR("GLEW: BufferFail4"); glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, m_RawModel->m_Vertices.size() * sizeof(RawModel::Vertex), &m_RawModel->m_Vertices[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, buffer); glGenBuffers(1, &ElementBuffer);
std::vector<int> structSizes = { 3, 3, 3, 3, 2, 4, 4, 4, 4, 4, 4 }; glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ElementBuffer);
int stride = 0; glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_RawModel->m_Indices.size() * sizeof(unsigned int), &m_RawModel->m_Indices[0], GL_STATIC_DRAW);
for (int size : structSizes) {
stride += size;
}
stride *= sizeof(GLfloat);
int offset = 0;
{
int element = 0;
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * offset)); element++;
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
}
GLERROR("GLEW: BufferFail5");
glEnableVertexAttribArray(0); glGenVertexArrays(1, &VAO);
glEnableVertexAttribArray(1); glBindVertexArray(VAO);
glEnableVertexAttribArray(2); GLERROR("GLEW: BufferFail4");
glEnableVertexAttribArray(3);
glEnableVertexAttribArray(4);
glEnableVertexAttribArray(5);
glEnableVertexAttribArray(6);
glEnableVertexAttribArray(7);
glEnableVertexAttribArray(8);
glEnableVertexAttribArray(9);
glEnableVertexAttribArray(10);
GLERROR("GLEW: BufferFail5");
//CreateBuffers(); glBindBuffer(GL_ARRAY_BUFFER, buffer);
std::vector<int> structSizes = { 3, 3, 3, 3, 2, 4, 4, 4, 4, 4, 4 };
int stride = 0;
for (int size : structSizes) {
stride += size;
}
stride *= sizeof(GLfloat);
int offset = 0;
{
int element = 0;
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * offset)); element++;
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
}
GLERROR("GLEW: BufferFail5");
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glEnableVertexAttribArray(3);
glEnableVertexAttribArray(4);
glEnableVertexAttribArray(5);
glEnableVertexAttribArray(6);
glEnableVertexAttribArray(7);
glEnableVertexAttribArray(8);
glEnableVertexAttribArray(9);
glEnableVertexAttribArray(10);
GLERROR("GLEW: BufferFail5");
//CreateBuffers();
} }
Model::~Model() Model::~Model()
+4 -10
View File
@@ -145,9 +145,7 @@ RawModel::RawModel(std::string fileName)
aiString path; aiString path;
aiTextureMapping mapping; aiTextureMapping mapping;
material->GetTexture(aiTextureType_DIFFUSE, 0, &path, &mapping); material->GetTexture(aiTextureType_DIFFUSE, 0, &path, &mapping);
std::string absolutePath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string(); matGroup.TexturePath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string();
//LOG_DEBUG("Diffuse texture: %s", absolutePath.c_str());
matGroup.Texture = std::shared_ptr<Texture>(ResourceManager::Load<Texture>(absolutePath));
} }
// Normal map // Normal map
//LOG_DEBUG("%i normal maps found", material->GetTextureCount(aiTextureType_HEIGHT)); //LOG_DEBUG("%i normal maps found", material->GetTextureCount(aiTextureType_HEIGHT));
@@ -155,9 +153,7 @@ RawModel::RawModel(std::string fileName)
aiString path; aiString path;
aiTextureMapping mapping; aiTextureMapping mapping;
material->GetTexture(aiTextureType_HEIGHT, 0, &path, &mapping); material->GetTexture(aiTextureType_HEIGHT, 0, &path, &mapping);
std::string absolutePath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string(); matGroup.NormalMapPath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string();
//LOG_DEBUG("Normal map: %s", absolutePath.c_str());
matGroup.NormalMap = std::shared_ptr<Texture>(ResourceManager::Load<Texture>(absolutePath));
} }
// Specular map // Specular map
//LOG_DEBUG("%i specular maps found", material->GetTextureCount(aiTextureType_SPECULAR)); //LOG_DEBUG("%i specular maps found", material->GetTextureCount(aiTextureType_SPECULAR));
@@ -165,11 +161,9 @@ RawModel::RawModel(std::string fileName)
aiString path; aiString path;
aiTextureMapping mapping; aiTextureMapping mapping;
material->GetTexture(aiTextureType_SPECULAR, 0, &path, &mapping); material->GetTexture(aiTextureType_SPECULAR, 0, &path, &mapping);
std::string absolutePath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string(); matGroup.SpecularMapPath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string();
//LOG_DEBUG("Specular map: %s", absolutePath.c_str());
matGroup.SpecularMap = std::shared_ptr<Texture>(ResourceManager::Load<Texture>(absolutePath));
} }
TextureGroups.push_back(matGroup); MaterialGroups.push_back(matGroup);
// Bones // Bones
std::map<int, std::vector<std::tuple<int, float>>> vertexWeights; std::map<int, std::vector<std::tuple<int, float>>> vertexWeights;
+14 -6
View File
@@ -84,15 +84,23 @@ void RenderSystem::fillModels(std::list<std::shared_ptr<RenderJob>>& jobs, World
continue; continue;
} }
Model* model = ResourceManager::Load<::Model>(resource); Model* model;
if (model == nullptr) { try {
model = ResourceManager::Load<::Model>("Models/Core/Error.obj"); model = ResourceManager::Load<::Model, true>(resource);
} catch (const Resource::StillLoadingException&) {
//continue;
model = ResourceManager::Load<::Model>("Models/Core/UnitRaptor.obj");
} catch (const std::exception&) {
try {
model = ResourceManager::Load<::Model>("Models/Core/Error.obj");
} catch (const std::exception&) {
continue;
}
} }
glm::mat4 modelMatrix = Transform::ModelMatrix(modelComponent.EntityID, world); glm::mat4 modelMatrix = Transform::ModelMatrix(modelComponent.EntityID, world);
for (auto matGroup : model->MaterialGroups()) {
for (auto texGroup : model->TextureGroups) { std::shared_ptr<ModelJob> modelJob = std::shared_ptr<ModelJob>(new ModelJob(model, m_Camera, modelMatrix, matGroup, modelComponent, world));
std::shared_ptr<ModelJob> modelJob = std::shared_ptr<ModelJob>(new ModelJob(model, m_Camera, modelMatrix, texGroup, modelComponent, world));
jobs.push_back(modelJob); jobs.push_back(modelJob);
} }
} }
+2 -2
View File
@@ -131,8 +131,8 @@ void Renderer::DrawScreenQuad(GLuint textureToDraw)
glBindVertexArray(m_ScreenQuad->VAO); glBindVertexArray(m_ScreenQuad->VAO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ScreenQuad->ElementBuffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ScreenQuad->ElementBuffer);
glDrawElementsBaseVertex(GL_TRIANGLES, m_ScreenQuad->TextureGroups[0].EndIndex - m_ScreenQuad->TextureGroups[0].StartIndex +1 glDrawElementsBaseVertex(GL_TRIANGLES, m_ScreenQuad->MaterialGroups()[0].EndIndex - m_ScreenQuad->MaterialGroups()[0].StartIndex +1
, GL_UNSIGNED_INT, 0, m_ScreenQuad->TextureGroups[0].StartIndex); , GL_UNSIGNED_INT, 0, m_ScreenQuad->MaterialGroups()[0].StartIndex);
} }
void Renderer::InitializeTextures() void Renderer::InitializeTextures()
+32 -32
View File
@@ -2,48 +2,48 @@
Texture::Texture(std::string path) Texture::Texture(std::string path)
{ {
PNG image(path); PNG image(path);
if (image.Width == 0 && image.Height == 0 || image.Format == Image::ImageFormat::Unknown) { if (image.Width == 0 && image.Height == 0 || image.Format == Image::ImageFormat::Unknown) {
image = PNG("Textures/Core/ErrorTexture.png"); image = PNG("Textures/Core/ErrorTexture.png");
if (image.Width == 0 && image.Height == 0 || image.Format == Image::ImageFormat::Unknown) { if (image.Width == 0 && image.Height == 0 || image.Format == Image::ImageFormat::Unknown) {
LOG_ERROR("Couldn't even load the error texture. This is a dark day indeed."); LOG_ERROR("Couldn't even load the error texture. This is a dark day indeed.");
return; return;
} }
} }
this->Width = image.Width; this->Width = image.Width;
this->Height = image.Height; this->Height = image.Height;
GLint format; GLint format;
switch (image.Format) { switch (image.Format) {
case Image::ImageFormat::RGB: case Image::ImageFormat::RGB:
format = GL_RGB; format = GL_RGB;
break; break;
case Image::ImageFormat::RGBA: case Image::ImageFormat::RGBA:
format = GL_RGBA; format = GL_RGBA;
break; break;
} }
// Construct the OpenGL texture // Construct the OpenGL texture
glGenTextures(1, &m_Texture); glGenTextures(1, &m_Texture);
glBindTexture(GL_TEXTURE_2D, m_Texture); glBindTexture(GL_TEXTURE_2D, m_Texture);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, format, image.Width, image.Height, 0, format, GL_UNSIGNED_BYTE, image.Data); glTexImage2D(GL_TEXTURE_2D, 0, format, image.Width, image.Height, 0, format, GL_UNSIGNED_BYTE, image.Data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
GLERROR("Texture load"); GLERROR("Texture load");
} }
Texture::~Texture() Texture::~Texture()
{ {
glDeleteTextures(1, &m_Texture); glDeleteTextures(1, &m_Texture);
} }
void Texture::Bind(GLenum textureUnit /* = GL_TEXTURE0 */) void Texture::Bind(GLenum textureUnit /* = GL_TEXTURE0 */)
{ {
glActiveTexture(textureUnit); glActiveTexture(textureUnit);
glBindTexture(GL_TEXTURE_2D, m_Texture); glBindTexture(GL_TEXTURE_2D, m_Texture);
} }
+2
View File
@@ -8,11 +8,13 @@ Game::Game(int argc, char* argv[])
{ {
ResourceManager::RegisterType<ConfigFile>("ConfigFile"); ResourceManager::RegisterType<ConfigFile>("ConfigFile");
ResourceManager::RegisterType<Model>("Model"); ResourceManager::RegisterType<Model>("Model");
ResourceManager::RegisterType<RawModel>("RawModel");
ResourceManager::RegisterType<Texture>("Texture"); ResourceManager::RegisterType<Texture>("Texture");
ResourceManager::RegisterType<ShaderProgram>("ShaderProgram"); ResourceManager::RegisterType<ShaderProgram>("ShaderProgram");
ResourceManager::RegisterType<EntityFile>("EntityFile"); ResourceManager::RegisterType<EntityFile>("EntityFile");
m_Config = ResourceManager::Load<ConfigFile>("Config.ini"); m_Config = ResourceManager::Load<ConfigFile>("Config.ini");
ResourceManager::UseThreading = m_Config->Get<bool>("Multithreading.ResourceLoading", true);
LOG_LEVEL = static_cast<_LOG_LEVEL>(m_Config->Get<int>("Debug.LogLevel", 1)); LOG_LEVEL = static_cast<_LOG_LEVEL>(m_Config->Get<int>("Debug.LogLevel", 1));
// Create the core event broker // Create the core event broker