FileManager

- Added File utilities
     * 'FileManager'
     * 'BasicFileHandle'
     * 'ImageFileHandle'
- Moved 'ResourceManager' from 'GraphicsContext' to  'Application'
- Added static 'Quit' function to 'Application'
This commit is contained in:
Light 2021-10-02 08:51:54 +03:30
parent b24c94a718
commit 33b28acd7b
15 changed files with 236 additions and 96 deletions

View file

@ -19,12 +19,17 @@
namespace Light {
Application* Application::s_Context = nullptr;
Application::Application()
: m_Instrumentor(nullptr),
m_LayerStack(nullptr),
m_Input(nullptr),
m_Window(nullptr)
{
LT_ENGINE_ASSERT(!s_Context, "Application::Application: repeated singleton construction");
s_Context = this;
m_Logger = Logger::Create();
LogDebugData();
@ -34,6 +39,8 @@ namespace Light {
m_LayerStack = LayerStack::Create();
m_Input = Input::Create();
m_ResourceManager = ResourceManager::Create();
m_Window = Window::Create(std::bind(&Application::OnEvent, this, std::placeholders::_1));
}
@ -107,6 +114,12 @@ namespace Light {
m_Instrumentor->BeginSession("Logs/ProfileResults_Termination.json");
}
/* static */
void Application::Quit()
{
s_Context->m_Window->Close();
}
void Application::OnEvent(const Event& event)
{
// window

View file

@ -8,6 +8,8 @@
#include "Layer/LayerStack.h"
#include "Utility/ResourceManager.h"
namespace Light {
class Window;
@ -17,11 +19,15 @@ namespace Light {
class Application
{
private:
static Application* s_Context;
private:
Scope<Logger> m_Logger;
Scope<Instrumentor> m_Instrumentor;
Scope<LayerStack> m_LayerStack;
Scope<Input> m_Input;
Scope<ResourceManager> m_ResourceManager;
protected:
Scope<Window> m_Window;
@ -36,6 +42,8 @@ namespace Light {
// To be defined in client project
static void Quit();
protected:
Application();

View file

@ -43,7 +43,7 @@ namespace Light {
#elif defined(LIGHT_PLATFORM_LINUX)
api = GraphicsAPI::OpenGL;
#elif defined(LIGHT_PLATFORM_MAC)
// #todo:
api = GraphicsAPI::OpenGL;
#endif
}
@ -68,12 +68,10 @@ namespace Light {
}
// create 'GraphicsContext' dependent classes
s_Context->m_ResourceManager = ResourceManager::Create(s_Context->m_SharedContext);
s_Context->m_UserInterface = UserInterface::Create(windowHandle, s_Context->m_SharedContext);
s_Context->m_Renderer = Renderer::Create(windowHandle, s_Context->m_SharedContext);
// check
LT_ENGINE_ASSERT(s_Context->m_ResourceManager, "GraphicsContext::Create: failed to create ResourceManager");
LT_ENGINE_ASSERT(s_Context->m_UserInterface, "GraphicsContext::Create: failed to create UserInterface");
LT_ENGINE_ASSERT(s_Context->m_Renderer, "GraphicsContext::Create: failed to create Renderer");

View file

@ -12,7 +12,6 @@ namespace Light {
class UserInterface;
class WindowResizedEvent;
enum class GraphicsAPI
@ -29,7 +28,6 @@ namespace Light {
private:
static GraphicsContext* s_Context;
Scope<ResourceManager> m_ResourceManager;
Scope<UserInterface> m_UserInterface;
Scope<Renderer> m_Renderer;

View file

@ -11,16 +11,16 @@
namespace Light {
Ref<Shader> Shader::Create(const std::vector<uint8_t>& vertexBlob, const std::vector<uint8_t>& pixelBlob, const std::string& vertexFileName, const std::string& pixelFileName, Ref<SharedContext> sharedContext)
Ref<Shader> Shader::Create(BasicFileHandle vertexFile, BasicFileHandle pixelFile, Ref<SharedContext> sharedContext)
{
// load shader source
switch (GraphicsContext::GetGraphicsAPI())
{
case GraphicsAPI::OpenGL:
return CreateRef<glShader>(vertexBlob, pixelBlob, vertexFileName, pixelFileName);
return CreateRef<glShader>(vertexFile, pixelFile);
case GraphicsAPI::DirectX: LT_WIN(
return CreateRef<dxShader>(vertexBlob, pixelBlob, std::static_pointer_cast<dxSharedContext>(sharedContext));)
return CreateRef<dxShader>(vertexFile, pixelFile, std::static_pointer_cast<dxSharedContext>(sharedContext));)
default:
LT_ENGINE_ASSERT(false, "Shader::Create: invalid/unsupported 'GraphicsAPI' {}", GraphicsContext::GetGraphicsAPI());

View file

@ -2,6 +2,8 @@
#include "Base/Base.h"
#include "Utility/FileManager.h"
#include <glm/glm.hpp>
namespace Light {
@ -20,7 +22,7 @@ namespace Light {
};
public:
static Ref<Shader> Create(const std::vector<uint8_t>& vertexBlob, const std::vector<uint8_t>& pixelBlob, const std::string& vertexFileName, const std::string& pixelFileName, Ref<SharedContext> sharedContext);
static Ref<Shader> Create(BasicFileHandle vertexFile, BasicFileHandle pixelFile, Ref<SharedContext> sharedContext);
virtual ~Shader() = default;

View file

@ -0,0 +1,60 @@
#include <ltpch.h>
#include "FileManager.h"
#include <stb_image.h>
namespace Light {
BasicFileHandle FileManager::ReadTextFile(const std::string& path)
{
// parse path info
std::string name = path.substr(0, path.find('.') + -1);
std::string extension = path.substr(path.find('.') + 1);
// open file
std::ifstream file(path.c_str(), std::ios_base::in | std::ios_base::binary);
// check
if (!file)
{
LT_ENGINE_WARN("FileManager::ReadTextFile: failed to load text file: {}", path);
file.close();
return NULL;
}
// fetch file size
file.seekg(0, std::ios::end);
uint32_t size = file.tellg();
file.seekg(0, std::ios::beg);
if (!size)
LT_ENGINE_WARN("FileManager::ReadTextFile: empty text file: {}", path);
// read file
uint8_t* data = new uint8_t[size];
file.read(reinterpret_cast<char*>(data), size);
file.close();
return BasicFileHandle(data, size, path, name, extension);
}
ImageFileHandle FileManager::ReadImageFile(const std::string& path, int32_t desiredComponents)
{
// parse path info
std::string name = path.substr(0, path.find('.') + -1);
std::string extension = path.substr(path.find('.') + 1);
// load image
int32_t width = 0, height = 0, fetchedComponents = 0;
uint8_t* pixels = stbi_load(path.c_str(), &width, &height, &fetchedComponents, desiredComponents);
// check
if(!pixels)
LT_ENGINE_WARN("FileManager::LoadImageFile: failed to load image file: <{}>", path);
else if (fetchedComponents != desiredComponents)
LT_ENGINE_WARN("FileManager::LoadImageFile: mismatch of fetched/desired components: <{}> ({}/{})", name + '.' + extension, fetchedComponents, desiredComponents);
return ImageFileHandle(pixels, width * height, path, name, extension, width, height, fetchedComponents, desiredComponents);
}
}

View file

@ -0,0 +1,86 @@
#pragma once
#include "Base/Base.h"
#include <stb_image.h>
namespace Light {
class BasicFileHandle
{
public:
BasicFileHandle(uint8_t* data = nullptr, uint32_t size = 0ull, const std::string& path = "", const std::string& name = "", const std::string& extension = "") :
m_Data(data),
m_Size(size),
m_Path(path),
m_Name(name),
m_Extension(extension)
{
}
virtual void Release()
{
delete m_Data;
m_Data = nullptr;
m_Size = 0ull;
}
// getters
inline uint8_t* GetData() { return m_Data; }
inline uint32_t GetSize() { return m_Size; }
inline const std::string& GetPath() { return m_Path; }
inline const std::string& GetName() { return m_Name; }
inline const std::string& GetExtension() { return m_Extension; }
inline const std::string& GetNameWithExtension() { return m_Name + '.' + m_Extension; }
inline bool IsValid() const { return !!m_Data; }
// operators
inline operator bool() const { return IsValid(); }
protected:
// made protected for custom Free():
uint8_t* m_Data;
uint32_t m_Size;
private:
const std::string m_Path, m_Name, m_Extension;
};
class ImageFileHandle : public BasicFileHandle
{
public:
ImageFileHandle(uint8_t* data, uint32_t size, const std::string& path, const std::string& name, const std::string& extension,
uint32_t width, uint32_t height, uint32_t components, uint32_t desiredComponents):
BasicFileHandle(data, size, path, name, extension),
m_Width(width), m_Height(height), m_Components(components), m_DesiredComponents(desiredComponents)
{
}
void Release() override
{
stbi_image_free(reinterpret_cast<void*>(m_Data));
m_Data = nullptr;
m_Size = 0ull;
}
// getters
inline uint32_t GetWidth() const { return m_Width; }
inline uint32_t GetHeight() const { return m_Height; }
inline uint32_t GetComponents() const { return m_Components; }
inline uint32_t GetDesiredComponents() const { return m_DesiredComponents; }
private:
uint32_t m_Width, m_Height, m_Components, m_DesiredComponents;
};
class FileManager
{
public:
static BasicFileHandle ReadTextFile(const std::string& path);
static ImageFileHandle ReadImageFile(const std::string& path, int32_t desiredComponents);
};
}

View file

@ -1,85 +1,64 @@
#include "ltpch.h"
#include "ResourceManager.h"
#include "FileManager.h"
#include "Graphics/GraphicsContext.h"
#include "Graphics/Shader.h"
#include "Graphics/Texture.h"
#include <stb_image.h>
namespace Light {
ResourceManager* ResourceManager::s_Context = nullptr;
Scope<ResourceManager> ResourceManager::Create(Ref<SharedContext> sharedContext)
Scope<ResourceManager> ResourceManager::Create()
{
return MakeScope(new ResourceManager(sharedContext));
return MakeScope(new ResourceManager());
}
ResourceManager::ResourceManager(Ref<SharedContext> sharedContext)
: m_SharedGraphicsContext(sharedContext),
ResourceManager::ResourceManager():
m_Shaders{},
m_Textures{}
{
LT_ENGINE_ASSERT(!s_Context, "ResourceManager::ResourceManager: an instance of 'ResourceManager' already exists, do not construct this class!");
LT_ENGINE_ASSERT(!s_Context, "ResourceManager::ResourceManager: repeated singleton construction");
s_Context = this;
stbi_set_flip_vertically_on_load(true);
}
void ResourceManager::LoadShaderImpl(const std::string& name, const std::string& vertexPath, const std::string& pixelPath)
{
std::vector<uint8_t> vertexFile, pixelFile;
// check
LT_ENGINE_ASSERT(s_Context, "ResourceManager::LoadShaderImpl: uninitliazed singleton");
LT_ENGINE_ASSERT(!vertexPath.empty(), "ResourceManager::LoadShaderImpl: empty 'vertexPath'");
LT_ENGINE_ASSERT(!pixelPath.empty(), "ResourceManager::LoadShaderImpl: empty 'pixelPath'");
std::ifstream file;
std::ios_base::openmode mode = std::ios_base::in;
// read vertex shader file
file.open(vertexPath.c_str(), mode);
LT_ENGINE_ASSERT(file, "ResourceManager::LoadShaderImpl: failed to load vertex shader at path: {}", vertexPath);
if (file)
{
file.seekg(0, std::ios::end);
vertexFile.resize(static_cast<size_t>(file.tellg()));
file.seekg(0, std::ios::beg);
file.read(reinterpret_cast<char*>(vertexFile.data()), vertexFile.size());
vertexFile.resize(static_cast<size_t>(file.gcount()));
file.close();
}
// load files
BasicFileHandle vertexFile = FileManager::ReadTextFile(vertexPath);
BasicFileHandle pixelFile = FileManager::ReadTextFile(pixelPath);
// read pixel shader file
file.open(pixelPath.c_str(), mode);
LT_ENGINE_ASSERT(file, "ResourceManager::LoadShaderImpl: failed to load vertex shader at path: {}", vertexPath);
if (file)
{
file.seekg(0, std::ios::end);
pixelFile.resize(static_cast<size_t>(file.tellg()));
file.seekg(0, std::ios::beg);
file.read(reinterpret_cast<char*>(pixelFile.data()), pixelFile.size());
pixelFile.resize(static_cast<size_t>(file.gcount()));
file.close();
}
// check
LT_ENGINE_ASSERT(vertexFile.IsValid(), "ResourceManager::LoadShaderImpl: failed to read vertex file: {}", vertexPath);
LT_ENGINE_ASSERT(pixelFile.IsValid(), "ResourceManager::LoadShaderImpl: failed to read vertex file: {}", pixelPath);
// create shader
m_Shaders[name] = Ref<Shader>(Shader::Create(vertexFile, pixelFile, vertexPath, pixelPath, m_SharedGraphicsContext));
m_Shaders[name] = Ref<Shader>(Shader::Create(vertexFile, pixelFile, GraphicsContext::GetSharedContext()));
// free file
vertexFile.Release();
pixelFile.Release();
}
void ResourceManager::LoadTextureImpl(const std::string& name, const std::string& path, unsigned int desiredComponents /* = 4u */)
{
// load image
int width, height, components;
unsigned char* pixels = stbi_load(path.c_str(), &width, &height, &components, desiredComponents);
// check
LT_ENGINE_ASSERT(pixels, "ResourceManager::LoadTextureImpl: failed to load texture <{}>, 'path': {}", name, path);
if (components != desiredComponents)
{
LT_ENGINE_WARN("ResourceManager::LoadTextureImpl: image file compoenents != 'desiredComponents' ({} - {})", components, desiredComponents);
LT_ENGINE_WARN("ResourceManager::LoadTextureImpl: <{}> 'path': {}", name, path);
}
LT_ENGINE_ASSERT(s_Context, "ResourceManager::LoadShaderImpl: uninitliazed singleton");
// load file
ImageFileHandle imgFile = FileManager::ReadImageFile(path, desiredComponents);
// create texture
m_Textures[name] = Ref<Texture>(Texture::Create(width, height, components, pixels, m_SharedGraphicsContext));
m_Textures[name] = Ref<Texture>(Texture::Create(imgFile.GetWidth(), imgFile.GetHeight(), imgFile.GetComponents(), imgFile.GetData(), GraphicsContext::GetSharedContext()));
// free file
imgFile.Release();
}
void ResourceManager::ReleaseTextureImpl(const std::string& name)

View file

@ -9,19 +9,16 @@ namespace Light {
class SharedContext;
// #todo: optimize
class ResourceManager
{
private:
static ResourceManager* s_Context;
Ref<SharedContext> m_SharedGraphicsContext;
std::unordered_map<std::string, Ref<Shader>> m_Shaders;
std::unordered_map<std::string, Ref<Texture>> m_Textures;
public:
static Scope<ResourceManager> Create(Ref<SharedContext> sharedContext);
static Scope<ResourceManager> Create();
// #todo: add geometry shader support
static inline void LoadShader(const std::string& name, const std::string& vertexPath, const std::string& pixelPath) { s_Context->LoadShaderImpl(name, vertexPath, pixelPath); }
@ -34,7 +31,7 @@ namespace Light {
static inline Ref<Texture> GetTexture(const std::string& name) { return s_Context->m_Textures[name]; }
private:
ResourceManager(Ref<SharedContext> sharedContext);
ResourceManager();
void LoadShaderImpl(const std::string& name, const std::string& vertexPath, const std::string& pixelPath);

View file

@ -6,7 +6,7 @@
namespace Light {
dxShader::dxShader(const std::vector<uint8_t>& vertexBlob, const std::vector<uint8_t> pixelBlob, Ref<dxSharedContext> sharedContext)
dxShader::dxShader(BasicFileHandle vertexFile, BasicFileHandle pixelFile, Ref<dxSharedContext> sharedContext)
: m_Context(sharedContext),
m_VertexShader(nullptr),
m_PixelShader(nullptr),
@ -15,8 +15,8 @@ namespace Light {
Microsoft::WRL::ComPtr<ID3DBlob> ps = nullptr, vsErr = nullptr, psErr = nullptr;
// compile shaders (we don't use DXC here because if D3DCompile fails it throws a dxException without logging the vsErr/psErr
D3DCompile(vertexBlob.data(), vertexBlob.size(), NULL, nullptr, nullptr, "main", "vs_4_0", NULL, NULL, &m_VertexBlob, &vsErr);
D3DCompile(pixelBlob.data(), pixelBlob.size(), NULL, nullptr, nullptr, "main", "ps_4_0", NULL, NULL, &ps, &psErr);
D3DCompile(vertexFile.GetData(), vertexFile.GetSize(), NULL, nullptr, nullptr, "main", "vs_4_0", NULL, NULL, &m_VertexBlob, &vsErr);
D3DCompile(pixelFile.GetData(), pixelFile.GetSize(), NULL, nullptr, nullptr, "main", "ps_4_0", NULL, NULL, &ps, &psErr);
// check
LT_ENGINE_ASSERT(!vsErr.Get(), "dxShader::dxShader: vertex shader compile error: {}", (char*)vsErr->GetBufferPointer());

View file

@ -4,6 +4,8 @@
#include "Base/Base.h"
#include "Utility/FileManager.h"
#include <d3d11.h>
#include <wrl.h>
@ -22,7 +24,7 @@ namespace Light {
Microsoft::WRL::ComPtr<ID3DBlob> m_VertexBlob;
public:
dxShader(const std::vector<uint8_t>& vertexBlob, const std::vector<uint8_t> pixelBlob, Ref<dxSharedContext> sharedContext);
dxShader(BasicFileHandle vertexFile, BasicFileHandle pixelFile, Ref<dxSharedContext> sharedContext);
~dxShader();
void Bind() override;

View file

@ -10,18 +10,18 @@
namespace Light {
glShader::glShader(const std::vector<uint8_t>& vertexBlob, const std::vector<uint8_t>& pixelBlob, const std::string& vertexFileName, const std::string& pixelFileName)
: m_ShaderID(NULL)
glShader::glShader(BasicFileHandle vertexFile, BasicFileHandle pixelFile):
m_ShaderID(NULL)
{
// create
m_ShaderID = glCreateProgram();
// compile hlsl to glsl
ShaderConductor::Compiler::ResultDesc vertexResult = CompileHLSL(vertexBlob, vertexFileName, Shader::Stage::VERTEX);
ShaderConductor::Compiler::ResultDesc pixelResult = CompileHLSL(pixelBlob, pixelFileName, Shader::Stage::PIXEL);
ShaderConductor::Compiler::ResultDesc vertexResult = CompileHLSL(vertexFile, Shader::Stage::VERTEX);
ShaderConductor::Compiler::ResultDesc pixelResult = CompileHLSL(pixelFile, Shader::Stage::PIXEL);
LT_ENGINE_ASSERT(!vertexResult.hasError, "glShader::glShader: failed to compile hlsl vertex shader: {}", vertexFileName);
LT_ENGINE_ASSERT(!pixelResult.hasError, "glShader::glShader: failed to compile hlsl pixel shader: {}", pixelFileName);
LT_ENGINE_ASSERT(!vertexResult.hasError, "glShader::glShader: failed to compile hlsl vertex shader: {}", vertexFile.GetPath());
LT_ENGINE_ASSERT(!pixelResult.hasError, "glShader::glShader: failed to compile hlsl pixel shader: {}", pixelFile.GetPath());
// extract glsl source
std::string vertexSource = std::string(reinterpret_cast<const char*>(vertexResult.target.Data()));
@ -61,12 +61,11 @@ namespace Light {
glUseProgram(NULL);
}
ShaderConductor::Compiler::ResultDesc glShader::CompileHLSL(std::vector<uint8_t> blob, std::string fileName, Shader::Stage stage)
ShaderConductor::Compiler::ResultDesc glShader::CompileHLSL(BasicFileHandle file, Shader::Stage stage)
{
// check
LT_ENGINE_ASSERT(!blob.empty(), "glShader::CompileHLSL: 'blob' is empty");
LT_ENGINE_ASSERT(!fileName.empty(), "glShader::CompileHLSL: 'fileName' is empty");
LT_ENGINE_ASSERT(stage, "glShader::CompileHLSL: 'stage' is invalid: None");
LT_ENGINE_ASSERT(file.IsValid(), "glShader::CompileHLSL: invalid 'file'");
LT_ENGINE_ASSERT(stage, "glShader::CompileHLSL: invalid 'stage': None");
// compiler options
ShaderConductor::Compiler::Options options = {};
@ -84,9 +83,9 @@ namespace Light {
// compiler source descriptor
ShaderConductor::Compiler::SourceDesc sourceDesc = {};
std::string source = std::string(reinterpret_cast<char*>(blob.data()), blob.size());
std::string source = std::string(reinterpret_cast<char*>(file.GetData()), file.GetSize());
sourceDesc.source = source.c_str();
sourceDesc.fileName = fileName.c_str();
sourceDesc.fileName = file.GetName().c_str();
sourceDesc.entryPoint = "main";
sourceDesc.stage = stage == Shader::Stage::VERTEX ? ShaderConductor::ShaderStage::VertexShader :
stage == Shader::Stage::PIXEL ? ShaderConductor::ShaderStage::PixelShader :
@ -98,23 +97,16 @@ namespace Light {
// compilation result
ShaderConductor::Compiler::ResultDesc result = ShaderConductor::Compiler::Compile(sourceDesc, options, targetDesc);
// log info
LT_ENGINE_INFO("_______________________________________");
LT_ENGINE_INFO("Compiled with ShaderConductor:");
LT_ENGINE_INFO(" hasError: {}", result.hasError);
LT_ENGINE_INFO(" isText: {}", result.isText);
// log error/warning
if (result.errorWarningMsg.Size() != 0u)
{
const char* errorStr = reinterpret_cast<const char*>(result.errorWarningMsg.Data());
LT_ENGINE_ERROR(" errorWarningMsg: \n{}", errorStr);
const char* errorWarningStr = reinterpret_cast<const char*>(result.errorWarningMsg.Data());
if(result.hasError)
LT_ENGINE_ERROR("glShader::CompileHLSL: ShaderConductor error:'{}': {}", file.GetName(), errorWarningStr);
else
LT_ENGINE_WARN("glShader::CompileHLSL: ShaderConductor warning:'{}': {}", errorWarningStr);
}
if (result.target.Size() != 0u)
{
const char* targetStr = reinterpret_cast<const char*>(result.target.Data());
LT_ENGINE_INFO(" target: \n{}", targetStr);
}
LT_ENGINE_INFO("_______________________________________");
return result;
}

View file

@ -4,6 +4,8 @@
#include "Base/Base.h"
#include "Utility/FileManager.h"
#include <ShaderConductor/ShaderConductor.hpp>
namespace Light {
@ -14,14 +16,14 @@ namespace Light {
unsigned int m_ShaderID;
public:
glShader(const std::vector<uint8_t>& vertexBlob, const std::vector<uint8_t>& pixelBlob, const std::string& vertexFileName, const std::string& pixelFileName);
glShader(BasicFileHandle vertexFile, BasicFileHandle pixelFile);
~glShader();
void Bind() override;
void UnBind() override;
private:
ShaderConductor::Compiler::ResultDesc CompileHLSL(std::vector<uint8_t> blob, std::string fileName, Shader::Stage stage);
ShaderConductor::Compiler::ResultDesc CompileHLSL(BasicFileHandle file, Shader::Stage stage);
unsigned int CompileShader(const std::string& source, Shader::Stage stage);
};

View file

@ -33,6 +33,9 @@ namespace Light {
auto& cameraTranslation = m_CameraEntity.GetComponent<TransformComponent>().translation;
cameraTranslation += glm::vec3(m_Direction * m_Speed * deltaTime, 0.0f);
if (Input::GetKeyboardKey(Key::Escape))
Application::Quit();
}
void EditorLayer::OnRender()