diff --git a/.gitmodules b/.gitmodules index 6ebf41b2..00e0e463 100644 --- a/.gitmodules +++ b/.gitmodules @@ -45,3 +45,6 @@ [submodule "Engine/Source/ThirdParty/imgui-node-editor"] path = Engine/Source/ThirdParty/imgui-node-editor url = https://github.com/thedmd/imgui-node-editor +[submodule "Engine/Source/ThirdParty/dmon"] + path = Engine/Source/ThirdParty/dmon + url = https://github.com/CatDogEngine/dmon diff --git a/Engine/Auto/Scripts/editor.lua b/Engine/Auto/Scripts/editor.lua index 14185ee1..121581fc 100644 --- a/Engine/Auto/Scripts/editor.lua +++ b/Engine/Auto/Scripts/editor.lua @@ -13,6 +13,7 @@ project("Editor") path.join(ThirdPartySourcePath, "imguizmo/*.cpp"), path.join(ThirdPartySourcePath, "imgui-node-editor/*.h"), path.join(ThirdPartySourcePath, "imgui-node-editor/*.cpp"), + path.join(ThirdPartySourcePath, "dmon/*.h"), } vpaths { @@ -55,6 +56,7 @@ project("Editor") path.join(ThirdPartySourcePath, "imgui"), path.join(ThirdPartySourcePath, "imguizmo"), path.join(ThirdPartySourcePath, "imgui-node-editor"), + path.join(ThirdPartySourcePath, "dmon"), ThirdPartySourcePath, } diff --git a/Engine/Source/Editor/EditorApp.cpp b/Engine/Source/Editor/EditorApp.cpp index e49ebe5e..8741c0f6 100644 --- a/Engine/Source/Editor/EditorApp.cpp +++ b/Engine/Source/Editor/EditorApp.cpp @@ -31,7 +31,7 @@ #include "Rendering/TerrainRenderer.h" #include "Rendering/WorldRenderer.h" #include "Rendering/ParticleRenderer.h" -#include "Resources/FileWatcher.h" +#include "Resources/FileWatcher.hpp" #include "Resources/ResourceBuilder.h" #include "Resources/ShaderBuilder.h" #include "Scene/SceneDatabase.h" @@ -113,9 +113,39 @@ void EditorApp::Init(engine::EngineInitArgs initArgs) }); resourceThread.detach(); - // TODO : Shader hot compile and load. - m_pFileWatcher = std::make_unique(CDENGINE_BUILTIN_SHADER_PATH); - m_pFileWatcher->EnableWatchSubTree(); +#if 0 + // Test code, will be removed in the next PR. + auto FileWatchCallback = []( + WatchID id, WatchAction action, + const char* rootDir, const char* filePath, + const char* oldFilePath, void* user) + { + switch (action) + { + case DMON_ACTION_CREATE: + CD_FATAL("Create"); + break; + + case DMON_ACTION_DELETE: + CD_FATAL("Delete"); + break; + + case DMON_ACTION_MODIFY: + CD_FATAL("Modify"); + break; + + case DMON_ACTION_MOVE: + CD_FATAL("Move"); + break; + + default: + CD_FATAL("Default"); + } + }; + + m_pFileWatcher = std::make_unique(); + m_pFileWatcher->Watch(CDENGINE_BUILTIN_SHADER_PATH, FileWatchCallback, DMON_WATCHFLAGS_RECURSIVE, nullptr); +#endif } void EditorApp::Shutdown() diff --git a/Engine/Source/Editor/Resources/FileWatcher.cpp b/Engine/Source/Editor/Resources/FileWatcher.cpp deleted file mode 100644 index f0230f85..00000000 --- a/Engine/Source/Editor/Resources/FileWatcher.cpp +++ /dev/null @@ -1,228 +0,0 @@ -#include "FileWatcher.h" - -#include "Base/Template.h" -#include "Log/Log.h" - -#include - -#ifdef _WIN32 - #include -#endif - -namespace editor -{ - -namespace -{ - -constexpr size_t BUFFER_SIZE = 1024; - -} - -FileWatcher::FileWatcher(std::string path) - : m_fileAddedCallback(nullptr), m_fileModifiedCallback(nullptr), m_fileRemovedCallback(nullptr), - m_fileRenamedOldCallBack(nullptr), m_fileRenamedNewCallBack(nullptr), - m_path(cd::MoveTemp(path)), m_isRunning(false), m_isWatchSubTree(false) {} - -FileWatcher::~FileWatcher() -{ - Stop(); -} - -void FileWatcher::SetFileAddedCallback(FileWatcherCallback callback) -{ - m_fileAddedCallback = callback; -} - -void FileWatcher::SetFileModifiedCallback(FileWatcherCallback callback) -{ - m_fileModifiedCallback = callback; -} - -void FileWatcher::SetFileRemovedCallback(FileWatcherCallback callback) -{ - m_fileRemovedCallback = callback; -} - -void FileWatcher::SetFileRenamedOldCallback(FileWatcherCallback callback) -{ - m_fileRenamedOldCallBack = callback; -} - -void FileWatcher::SetFileRenamedNewCallback(FileWatcherCallback callback) -{ - m_fileRenamedNewCallBack = callback; -} - -void FileWatcher::Start() -{ - if (!std::filesystem::exists(m_path) || !std::filesystem::is_directory(m_path)) - { - CD_ERROR("Unknown file watcher path!"); - return; - } - - if (m_isRunning) - { - CD_ERROR("File watcher is already running!"); - return; - } - - CD_INFO("Start watching \"{0}\", subtree {1}.", m_path, (m_isWatchSubTree ? "included" : "ignored")); - - m_isRunning = true; - m_thread = std::thread(&FileWatcher::Watch, this); -} - -void FileWatcher::Stop() -{ - if (m_isRunning) - { - m_isRunning = false; - if (m_thread.joinable()) - { - m_thread.join(); - } - } -} - -void FileWatcher::Watch() -{ -#ifdef _WIN32 - HANDLE pathHandle = CreateFileA(m_path.c_str(), FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, nullptr); - if (INVALID_HANDLE_VALUE == pathHandle) - { - CD_ERROR("FileWatcher create path handle failed with error code {0}", GetLastError()); - return; - } - - char buffer[BUFFER_SIZE]; - DWORD bytesReturned = { 0 }; - - OVERLAPPED overlapped = { 0 }; - overlapped.hEvent = CreateEvent(nullptr, true, false, nullptr); - if (nullptr == overlapped.hEvent) - { - CD_ERROR("Filewatcher create event failed with error code {0}", GetLastError()); - CloseHandle(pathHandle); - return; - } - - while (m_isRunning) - { - if (!ResetEvent(overlapped.hEvent)) - { - CD_ERROR("FileWatcher reset event failed with error code {0}", GetLastError()); - break; - } - - constexpr uint32_t WatchStatus = FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_SECURITY; - - if (!ReadDirectoryChangesW(pathHandle, buffer, BUFFER_SIZE, m_isWatchSubTree, WatchStatus, &bytesReturned, &overlapped, nullptr)) - { - CD_ERROR("FileWatcher read directory changes failed with error code {0}", GetLastError()); - break; - } - - // Wait for ReadDirectoryChangesW for 100 milliseconds. - // If it times out then the next ReadDirectoryChangesW will be called. - DWORD waitResault = WaitForSingleObject(overlapped.hEvent, 100); - - if (WAIT_TIMEOUT == waitResault) - { - continue; - } - else if (WAIT_OBJECT_0 != waitResault) - { - CD_ERROR("FileWatcher wait for IO failed with error code {0}", GetLastError()); - break; - } - - DWORD overlappedReadByteCount; - if (!GetOverlappedResult(pathHandle, &overlapped, &overlappedReadByteCount, false)) - { - CD_ERROR("FileWatcher get overlapped result failed with error code {0}", GetLastError()); - break; - } - - FILE_NOTIFY_INFORMATION* pFileNotifyInfo = reinterpret_cast(buffer); - - while (true) - { - int fileNameSize = pFileNotifyInfo->FileNameLength / sizeof(WCHAR); - int utf8Size = WideCharToMultiByte(CP_UTF8, 0, pFileNotifyInfo->FileName, fileNameSize, nullptr, 0, nullptr, nullptr); - std::string fileName(utf8Size, 0); - WideCharToMultiByte(CP_UTF8, 0, pFileNotifyInfo->FileName, fileNameSize, fileName.data(), utf8Size, nullptr, nullptr); - std::string fullFilePath = (std::filesystem::path(m_path) / cd::MoveTemp(fileName)).generic_string(); - - DWORD fileAction = pFileNotifyInfo->Action; - - switch (fileAction) - { - case FILE_ACTION_ADDED: - if (m_fileAddedCallback) - { - CD_INFO("File added {0}", fullFilePath); - m_fileAddedCallback(fullFilePath); - } - break; - - case FILE_ACTION_MODIFIED: - if (m_fileModifiedCallback) - { - CD_INFO("File modified {0}", fullFilePath); - m_fileModifiedCallback(fullFilePath); - } - break; - - case FILE_ACTION_REMOVED: - if (m_fileRemovedCallback) - { - CD_INFO("File removed {0}", fullFilePath); - m_fileRemovedCallback(fullFilePath); - } - break; - - case FILE_ACTION_RENAMED_OLD_NAME: - if (m_fileRenamedOldCallBack) - { - CD_INFO("File renamed old {0}", fullFilePath); - m_fileRenamedOldCallBack(fullFilePath); - } - break; - - case FILE_ACTION_RENAMED_NEW_NAME: - if (m_fileRenamedNewCallBack) - { - CD_INFO("File renamed new {0}", fullFilePath); - m_fileRenamedNewCallBack(fullFilePath); - } - break; - - default: - CD_ERROR("{0} unknown file whatcher status!", fullFilePath); - break; - } - - if (0 == pFileNotifyInfo->NextEntryOffset) - { - break; - } - - pFileNotifyInfo = reinterpret_cast(reinterpret_cast(pFileNotifyInfo) + pFileNotifyInfo->NextEntryOffset); - } - - memset(buffer, 0, bytesReturned); - } - - CloseHandle(pathHandle); - CloseHandle(overlapped.hEvent); - -#else - // TODO : Remaining cross-platform code. -#endif - -} - -} \ No newline at end of file diff --git a/Engine/Source/Editor/Resources/FileWatcher.h b/Engine/Source/Editor/Resources/FileWatcher.h deleted file mode 100644 index 585f4ee8..00000000 --- a/Engine/Source/Editor/Resources/FileWatcher.h +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace editor -{ - -class FileWatcher -{ -private: - typedef std::function FileWatcherCallback; - -public: - FileWatcher(std::string path); - ~FileWatcher(); - - FileWatcher() = delete; - FileWatcher(const FileWatcher&) = delete; - FileWatcher& operator=(const FileWatcher&) = delete; - FileWatcher(FileWatcher&&) = delete; - FileWatcher& operator=(FileWatcher&&) = delete; - - void SetFileAddedCallback(FileWatcherCallback callback); - void SetFileModifiedCallback(FileWatcherCallback callback); - void SetFileRemovedCallback(FileWatcherCallback callback); - void SetFileRenamedOldCallback(FileWatcherCallback callback); - void SetFileRenamedNewCallback(FileWatcherCallback callback); - - void Start(); - void Stop(); - - const std::string& GetPath() const { return m_path; } - - const bool GetIsRunning() const { return m_isRunning; } - - void EnableWatchSubTree() { m_isWatchSubTree = true; } - void DisableWatchSubTree() { m_isWatchSubTree = false; } - const bool GetIsWatchSubTree() const { return m_isWatchSubTree; } - -private: - FileWatcherCallback m_fileAddedCallback; - FileWatcherCallback m_fileModifiedCallback; - FileWatcherCallback m_fileRemovedCallback; - FileWatcherCallback m_fileRenamedOldCallBack; - FileWatcherCallback m_fileRenamedNewCallBack; - - std::string m_path; - bool m_isRunning; - bool m_isWatchSubTree; - - std::thread m_thread; - - void Watch(); -}; - -} \ No newline at end of file diff --git a/Engine/Source/Editor/Resources/FileWatcher.hpp b/Engine/Source/Editor/Resources/FileWatcher.hpp new file mode 100644 index 00000000..779985f1 --- /dev/null +++ b/Engine/Source/Editor/Resources/FileWatcher.hpp @@ -0,0 +1,66 @@ +#pragma once + +#include +#include + +#include "Log/Log.h" + +#define DMON_LOG_DEBUG(s) do { CD_INFO(s); } while(0) +#define DMON_LOG_ERROR(s) do { CD_ERROR(s); assert(false); } while(0) + +#define DMON_IMPL +#include "dmon.h" + +namespace editor +{ + +using WatchID = dmon_watch_id; +using WatchAction = dmon_action; + +class FileWatcher +{ + +public: + FileWatcher(const FileWatcher&) = delete; + FileWatcher& operator=(const FileWatcher&) = delete; + FileWatcher(FileWatcher&&) = delete; + FileWatcher& operator=(FileWatcher&&) = delete; + + FileWatcher() + { + Init(); + } + + ~FileWatcher() + { + Deinit(); + } + + void Init() + { + dmon_init(); + } + + void Deinit() + { + dmon_deinit(); + } + + WatchID Watch( + const char* rootDir, + void (*callback)( + WatchID watchID, WatchAction action, + const char* dirName, const char* filename, + const char* oldName, void* user), + uint32_t flags, void* userData) + { + return dmon_watch(rootDir, callback, flags, userData); + } + + void UnWatch(WatchID watchID) + { + dmon_unwatch(watchID); + } +}; + +} \ No newline at end of file diff --git a/Engine/Source/Runtime/Rendering/ShaderCollections.cpp b/Engine/Source/Runtime/Rendering/ShaderCollections.cpp index 89fa5a9e..73b533c3 100644 --- a/Engine/Source/Runtime/Rendering/ShaderCollections.cpp +++ b/Engine/Source/Runtime/Rendering/ShaderCollections.cpp @@ -60,7 +60,7 @@ void ShaderCollections::SetShaderPrograms(std::map> combines) +void ShaderCollections::SetProgramFeatureCombines(std::map> combines) { m_programFeatureCombines = cd::MoveTemp(combines); } diff --git a/Engine/Source/Runtime/Rendering/ShaderCollections.h b/Engine/Source/Runtime/Rendering/ShaderCollections.h index 5452a682..987081c2 100644 --- a/Engine/Source/Runtime/Rendering/ShaderCollections.h +++ b/Engine/Source/Runtime/Rendering/ShaderCollections.h @@ -43,9 +43,9 @@ class ShaderCollections final std::map>& GetShaderPrograms() { return m_shaderPrograms; } const std::map>& GetShaderPrograms() const { return m_shaderPrograms; } - void SetFeatureCombinePrograms(std::map> combines); - std::map>& GetFeatureCombinePrograms() { return m_programFeatureCombines; } - const std::map>& GetFeatureCombinePrograms() const { return m_programFeatureCombines; } + void SetProgramFeatureCombines(std::map> combines); + std::map>& GetProgramFeatureCombines() { return m_programFeatureCombines; } + const std::map>& GetProgramFeatureCombiness() const { return m_programFeatureCombines; } private: // Key : StringCrc(Program name), Value : Shader names diff --git a/Engine/Source/ThirdParty/dmon b/Engine/Source/ThirdParty/dmon new file mode 160000 index 00000000..c1b63775 --- /dev/null +++ b/Engine/Source/ThirdParty/dmon @@ -0,0 +1 @@ +Subproject commit c1b6377545a85357a0e2cd8c37319cb24b45b984 diff --git a/README.md b/README.md index f519cd62..1fb1e654 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,8 @@ ## ThirdParty +* File Monitor + * [dmon](https://github.com/septag/dmon) * Font * [freetype](https://github.com/freetype/freetype) * Graphics