mirror of
https://github.com/antopilo/Nuake.git
synced 2026-09-15 20:08:54 +03:00
Live reloading
This commit is contained in:
@@ -73,6 +73,8 @@ void EditorLayer::OnUpdate()
|
||||
|
||||
Nuake::Engine::EndDraw();
|
||||
Nuake::Input::Update();
|
||||
|
||||
RID::ExecuteRemaps();
|
||||
}
|
||||
|
||||
void EditorLayer::OnDetach()
|
||||
|
||||
@@ -79,10 +79,10 @@ FileType File::GetFileType() const
|
||||
return FileType::Mesh;
|
||||
}
|
||||
|
||||
if (ext == ".glb" || ext == ".gltf" || ext == ".fbx")
|
||||
{
|
||||
return FileType::MeshAsset;
|
||||
}
|
||||
//if (ext == ".glb" || ext == ".gltf" || ext == ".fbx")
|
||||
//{
|
||||
// return FileType::MeshAsset;
|
||||
//}
|
||||
|
||||
if (ext == ".html")
|
||||
{
|
||||
|
||||
@@ -7,9 +7,16 @@
|
||||
#include "Directory.h"
|
||||
#include "File.h"
|
||||
|
||||
#include "Nuake/Resource/Bakers/AssetBakerManager.h"
|
||||
#include "Nuake/Resource/Resolvers/ResolverManager.h"
|
||||
|
||||
#include "filewatch/FileWatch.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <Nuake/Resource/ResourceManifest.h>
|
||||
#include <Nuake/Resource/ResourceManager.h>
|
||||
#include "Nuake/Resource/RID.h"
|
||||
|
||||
|
||||
using namespace Nuake;
|
||||
|
||||
@@ -113,8 +120,11 @@ std::vector<Ref<File>> FileSystem::GetAllFiles(const FileType fileType)
|
||||
void FileSystem::SetRootDirectory(const std::string path)
|
||||
{
|
||||
Root = path;
|
||||
RootFileWatch = CreateRef<filewatch::FileWatch<std::string>>(
|
||||
path, [&](const std::string& path, const filewatch::Event& event)
|
||||
|
||||
if (!RootFileWatch)
|
||||
{
|
||||
RootFileWatch = CreateRef<filewatch::FileWatch<std::string>>(
|
||||
path, [&](const std::string& path, const filewatch::Event& event)
|
||||
{
|
||||
std::string normalizedPath = String::ReplaceSlash(path);
|
||||
|
||||
@@ -133,23 +143,59 @@ void FileSystem::SetRootDirectory(const std::string path)
|
||||
std::string extension = filePath.extension().string();
|
||||
Ref<File> newImportedFile = CreateRef<File>(parentDirectory, FileSystem::RelativeToAbsolute(normalizedPath), name, extension);
|
||||
parentDirectory->Files.push_back(newImportedFile);
|
||||
|
||||
AssetBakerManager::Get().OnNewAssetDetected(newImportedFile);
|
||||
}
|
||||
|
||||
if(Ref<File> file = GetFile(normalizedPath); file)
|
||||
if (Ref<File> file = GetFile(normalizedPath); file)
|
||||
{
|
||||
if (file->GetFileType() == FileType::Unknown)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (event == filewatch::Event::modified)
|
||||
{
|
||||
file->SetHasBeenModified(true);
|
||||
|
||||
AssetBakerManager::Get().OnNewAssetDetected(file);
|
||||
|
||||
if (file->GetExtension() != ".nkmesh")
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ResourceManifest& manifest = Nuake::ResourceManager::Manifest;
|
||||
UUID oldUUID = manifest.GetResourceUUID(file->GetRelativePath());
|
||||
|
||||
if(oldUUID == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Logger::Log("Old UUID" + std::to_string(oldUUID), "UUID", VERBOSE);
|
||||
|
||||
//Ref<File> newFile = AssetBakerManager::Get().Bake(file);
|
||||
auto& resolver = ResourceResolverManager::Get();
|
||||
if (resolver.IsFileTypeResolvable(file->GetExtension()))
|
||||
{
|
||||
// Read uuid from file
|
||||
const UUID& uuid = resolver.ResolveUUID(file);
|
||||
Logger::Log("new UUID" + std::to_string(uuid), "UUID", VERBOSE);
|
||||
manifest.RegisterResource(uuid, file->GetRelativePath());
|
||||
RID::QueueRemap(oldUUID, uuid);
|
||||
}
|
||||
|
||||
// manifest.RegisterResource(uuid, file->GetRelativePath());
|
||||
//
|
||||
// ResourceManager::RemapResource(oldUUID, uuid);
|
||||
//
|
||||
// if (ResourceManager::IsResourceLoaded(uuid))
|
||||
// {
|
||||
// ResourceManager::ReloadResource(uuid);
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
Scan();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "FileTypes.h"
|
||||
|
||||
#include <functional>
|
||||
#include <queue>
|
||||
|
||||
namespace filewatch
|
||||
{
|
||||
@@ -17,8 +18,15 @@ namespace Nuake
|
||||
|
||||
using OnFileFunc = std::function<void(Ref<File>)>;
|
||||
|
||||
struct FileProcessRequest
|
||||
{
|
||||
Ref<File> FileToProcess;
|
||||
};
|
||||
|
||||
class FileSystem
|
||||
{
|
||||
private:
|
||||
|
||||
public:
|
||||
static std::string Root;
|
||||
static Ref<Directory> RootDirectory;
|
||||
|
||||
@@ -87,7 +87,7 @@ namespace Nuake
|
||||
|
||||
constexpr uint32_t MAX_MODEL_MATRIX = 3000;
|
||||
constexpr uint32_t MAX_MATERIAL = 3000;
|
||||
constexpr uint32_t MAX_TEXTURES = 500;
|
||||
constexpr uint32_t MAX_TEXTURES = 1000;
|
||||
constexpr uint32_t MAX_CAMERAS = 100;
|
||||
constexpr uint32_t MAX_LIGHTS = 100;
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "vk_mem_alloc.h"
|
||||
|
||||
#include <array>
|
||||
#include <mutex>
|
||||
#include <algorithm>
|
||||
|
||||
bool NKUseValidationLayer = false;
|
||||
@@ -767,9 +768,11 @@ bool VkRenderer::Draw()
|
||||
return true;
|
||||
}
|
||||
|
||||
std::mutex queueMutex;
|
||||
void VkRenderer::EndDraw()
|
||||
{
|
||||
|
||||
std::lock_guard<std::mutex> lock(queueMutex);
|
||||
if (FrameSkipped)
|
||||
{
|
||||
return;
|
||||
@@ -856,8 +859,10 @@ void VkRenderer::DrawImgui(VkCommandBuffer cmd, VkImageView targetImageView)
|
||||
vkCmdEndRendering(cmd);
|
||||
}
|
||||
|
||||
|
||||
void VkRenderer::ImmediateSubmit(std::function<void(VkCommandBuffer cmd)>&& function)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(queueMutex);
|
||||
VK_CALL(vkResetFences(Device, 1, &ImguiFence));
|
||||
VK_CALL(vkResetCommandBuffer(ImguiCommandBuffer, 0));
|
||||
|
||||
|
||||
@@ -312,16 +312,19 @@ void GPUResources::CreateBindlessLayout()
|
||||
void GPUResources::RecreateBindlessTextures()
|
||||
{
|
||||
// Ideally wed have update bit enabled ondescriptors
|
||||
vkQueueWaitIdle(VkRenderer::Get().GPUQueue);
|
||||
//vkQueueWaitIdle(VkRenderer::Get().GPUQueue);
|
||||
|
||||
if (!TexturesDescriptor)
|
||||
{
|
||||
CreateBindlessLayout();
|
||||
}
|
||||
|
||||
BindlessTextureMapping.clear();
|
||||
|
||||
std::vector<VkDescriptorImageInfo> imageInfos(Images.size());
|
||||
auto allTextures = GetAllTextures();
|
||||
for (size_t i = 0; i < Images.size(); i++) {
|
||||
for (size_t i = 0; i < Images.size(); i++)
|
||||
{
|
||||
imageInfos[i].imageView = allTextures[i]->GetImageView();
|
||||
imageInfos[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
BindlessTextureMapping[allTextures[i]->GetID()] = i;
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
#pragma once
|
||||
#include "IAssetBaker.h"
|
||||
#include "Nuake/Core/Core.h"
|
||||
#include "Nuake/Core/Logger.h"
|
||||
|
||||
#include "Nuake/FileSystem/File.h"
|
||||
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
namespace Nuake
|
||||
{
|
||||
@@ -29,13 +34,77 @@ namespace Nuake
|
||||
}
|
||||
}
|
||||
|
||||
void OnAssetReimport(const Ref<File>& file)
|
||||
{
|
||||
const std::string extension = file->GetExtension();
|
||||
if (Bakers.find(extension) == Bakers.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Bakers[extension]->Bake(file);
|
||||
Logger::Log("New file baked: " + file->GetRelativePath(), "baker", VERBOSE);
|
||||
}
|
||||
|
||||
bool WaitForFileReady(const std::string& path, int timeoutMs = 10000)
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
const int checkIntervalMs = 500;
|
||||
auto startTime = std::chrono::steady_clock::now();
|
||||
std::uintmax_t lastSize = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (!fs::exists(path))
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(checkIntervalMs));
|
||||
continue;
|
||||
}
|
||||
|
||||
auto currentSize = fs::file_size(path);
|
||||
if (currentSize == lastSize && currentSize > 0)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(checkIntervalMs));
|
||||
if (fs::file_size(path) == currentSize)
|
||||
return true;
|
||||
}
|
||||
|
||||
lastSize = currentSize;
|
||||
|
||||
if (std::chrono::steady_clock::now() - startTime > std::chrono::milliseconds(timeoutMs))
|
||||
return false;
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(checkIntervalMs));
|
||||
}
|
||||
}
|
||||
|
||||
void OnNewAssetDetected(const Ref<File>& file)
|
||||
{
|
||||
const std::string extension = file->GetExtension();
|
||||
if (Bakers.find(extension) == Bakers.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait until the file is fully copied/written
|
||||
if (!WaitForFileReady(file->GetAbsolutePath()))
|
||||
{
|
||||
Logger::Log("File was not ready in time: " + file->GetRelativePath(), "baker", WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
Bakers[extension]->Bake(file);
|
||||
Logger::Log("New file baked: " + file->GetRelativePath(), "baker", VERBOSE);
|
||||
}
|
||||
|
||||
Ref<File> Bake(const Ref<File>& file)
|
||||
{
|
||||
const std::string extension = file->GetExtension();
|
||||
if(Bakers.find(extension) == Bakers.end())
|
||||
{
|
||||
assert(false && "Baker not found for asset type");
|
||||
return nullptr;
|
||||
return file;
|
||||
}
|
||||
|
||||
return Bakers[extension]->Bake(file);
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#include "RID.h"
|
||||
|
||||
|
||||
using namespace Nuake;
|
||||
|
||||
std::vector<RID*> RID::Handles;
|
||||
std::queue<RID::RemapRequest> RID::RemapQueue;
|
||||
std::mutex RID::RemapMutex;
|
||||
@@ -10,13 +10,73 @@ namespace Nuake
|
||||
|
||||
class RID
|
||||
{
|
||||
private:
|
||||
struct RemapRequest
|
||||
{
|
||||
UUID Old;
|
||||
UUID New;
|
||||
};
|
||||
|
||||
static std::vector<RID*> Handles;
|
||||
static std::queue<RemapRequest> RemapQueue;
|
||||
static std::mutex RemapMutex;
|
||||
|
||||
public:
|
||||
UUID ID;
|
||||
Ref<Resource> Data;
|
||||
|
||||
RID() : ID(0), Data(nullptr) {}
|
||||
RID(UUID id) : ID(id) {}
|
||||
RID() : ID(0), Data(nullptr)
|
||||
{
|
||||
Handles.push_back(this);
|
||||
}
|
||||
RID(UUID id) : ID(id)
|
||||
{
|
||||
Handles.push_back(this);
|
||||
}
|
||||
|
||||
~RID()
|
||||
{
|
||||
std::remove_if(Handles.begin(), Handles.end(), [this](const RID* rid) { return rid == this; });
|
||||
}
|
||||
|
||||
static void ExecuteRemaps()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(RemapMutex);
|
||||
while (!RemapQueue.empty())
|
||||
{
|
||||
auto& request = RemapQueue.front();
|
||||
uint32_t remappedCount = 0;
|
||||
for (auto& handle : Handles)
|
||||
{
|
||||
if (handle->ID == request.Old)
|
||||
{
|
||||
handle->ID = request.New;
|
||||
|
||||
if (handle->Data)
|
||||
{
|
||||
handle->Data = nullptr;
|
||||
}
|
||||
|
||||
remappedCount++;
|
||||
}
|
||||
}
|
||||
RemapQueue.pop();
|
||||
|
||||
Logger::Log("Remapped " + std::to_string(remappedCount), "RID", VERBOSE);
|
||||
}
|
||||
}
|
||||
|
||||
static void QueueRemap(const UUID& old, const UUID& newUUID)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(RemapMutex);
|
||||
if (old == newUUID)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RemapQueue.push({ old, newUUID });
|
||||
}
|
||||
|
||||
public:
|
||||
template<typename T>
|
||||
Ref<T> Get()
|
||||
|
||||
@@ -25,6 +25,11 @@ namespace Nuake
|
||||
static ResourceManifest Manifest;
|
||||
static Ref<Resource> GetResource(const UUID& uuid);
|
||||
|
||||
static void RemapResource(const UUID& oldUUID, const UUID& newUUID)
|
||||
{
|
||||
m_Resources[oldUUID] = m_Resources[newUUID];
|
||||
}
|
||||
|
||||
static void RegisterResource(Ref<Resource> resource)
|
||||
{
|
||||
m_Resources[resource->ID] = resource;
|
||||
@@ -35,6 +40,13 @@ namespace Nuake
|
||||
return m_Resources.find(uuid) != m_Resources.end();
|
||||
}
|
||||
|
||||
static bool ReloadResource(const UUID& uuid)
|
||||
{
|
||||
m_Resources.erase(uuid);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static Ref<Resource> RegisterUnregisteredRessource(Ref<File> file)
|
||||
{
|
||||
Ref<Resource> resolvedResource = ResourceResolverManager::Get().Resolve(file);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Nuake/FileSystem/FileSystem.h"
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
namespace Nuake
|
||||
@@ -16,10 +17,11 @@ namespace Nuake
|
||||
private:
|
||||
std::map<std::string, UUID> PathToMap;
|
||||
std::map<UUID, std::string> MapToPath;
|
||||
|
||||
std::mutex Mutex;
|
||||
public:
|
||||
void RegisterResource(UUID uuid, const std::string& path)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(Mutex);
|
||||
// New resource is overwritting so we need to clear UUID -> Path map
|
||||
if (PathToMap.find(path) != PathToMap.end())
|
||||
{
|
||||
@@ -32,6 +34,7 @@ namespace Nuake
|
||||
|
||||
std::string GetResourcePath(UUID uuid)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(Mutex);
|
||||
if (MapToPath.find(uuid) != MapToPath.end())
|
||||
{
|
||||
return MapToPath[uuid];
|
||||
@@ -40,8 +43,14 @@ namespace Nuake
|
||||
return "";
|
||||
}
|
||||
|
||||
void RemapResource(UUID oldUUID, UUID newUUID)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
UUID GetResourceUUID(const std::string& path)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(Mutex);
|
||||
if (PathToMap.find(path) != PathToMap.end())
|
||||
{
|
||||
return PathToMap[path];
|
||||
@@ -52,6 +61,7 @@ namespace Nuake
|
||||
|
||||
void Serialize(const std::string& path)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(Mutex);
|
||||
json j;
|
||||
for (auto& [path, uuid] : PathToMap)
|
||||
{
|
||||
@@ -65,6 +75,7 @@ namespace Nuake
|
||||
|
||||
void Deserialize(const std::string& path)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(Mutex);
|
||||
auto content = FileSystem::ReadFile(path);
|
||||
json j = json::parse(content);
|
||||
for (auto& [path, uuid] : j.items())
|
||||
|
||||
Reference in New Issue
Block a user