diff --git a/CMake/Common.cmake b/CMake/Common.cmake index 5a9c1ca1a..7e0f4ef0a 100644 --- a/CMake/Common.cmake +++ b/CMake/Common.cmake @@ -15,6 +15,9 @@ else () set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/Lib) endif () +add_definitions(-DENGINE_CMAKE_SOURCE_DIRECTORY="${CMAKE_SOURCE_DIR}") +add_definitions(-DENGINE_CMAKE_BINARY_DIRECTORY="${CMAKE_BINARY_DIR}") + add_definitions(-DBUILD_CONFIG_DEBUG=$,1,0>) add_definitions(-DPLATFORM_WINDOWS=$,1,0>) diff --git a/CMake/Target.cmake b/CMake/Target.cmake index 4a9c33b5e..9b1914bac 100644 --- a/CMake/Target.cmake +++ b/CMake/Target.cmake @@ -2,7 +2,6 @@ option(BUILD_TEST "Build unit tests" ON) option(BUILD_SAMPLE "Build sample" ON) set(API_HEADER_DIR ${CMAKE_BINARY_DIR}/Generated/Api CACHE PATH "" FORCE) -set(META_HEADER_DIR ${CMAKE_BINARY_DIR}/Generated/Meta CACHE PATH "" FORCE) set(BASIC_LIBS Common CACHE STRING "" FORCE) set(BASIC_TEST_LIBS Test CACHE STRING "" FORCE) @@ -127,14 +126,21 @@ function(AddRuntimeDependenciesCopyCommand) string(REPLACE "->" ";" TEMP ${R}) list(GET TEMP 0 SRC) list(GET TEMP 1 DST) + set(COPY_COMMAND ${SRC} $/${DST}) + else () + set(SRC ${R}) + set(COPY_COMMAND ${SRC} $) + endif () + + if (IS_DIRECTORY ${SRC}) add_custom_command( TARGET ${PARAMS_NAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SRC} $/${DST} + COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different ${COPY_COMMAND} ) else () add_custom_command( TARGET ${PARAMS_NAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${R} $ + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${COPY_COMMAND} ) endif () endforeach() @@ -214,7 +220,7 @@ function(GetTargetIncludeDirectoriesRecurse) endfunction() function(AddMirrorInfoSourceGenerationTarget) - cmake_parse_arguments(PARAMS "" "NAME;OUTPUT_SRC;OUTPUT_TARGET_NAME" "SEARCH_DIR;PUBLIC_INC;PRIVATE_INC;LIB" ${ARGN}) + cmake_parse_arguments(PARAMS "DYNAMIC" "NAME;OUTPUT_SRC;OUTPUT_TARGET_NAME" "SEARCH_DIR;PUBLIC_INC;PRIVATE_INC;LIB" ${ARGN}) if (DEFINED PARAMS_PUBLIC_INC) list(APPEND INC ${PARAMS_PUBLIC_INC}) @@ -242,6 +248,10 @@ function(AddMirrorInfoSourceGenerationTarget) list(APPEND INC_ARGS ${ABSOLUTE_I}) endforeach() + if (${PARAMS_DYNAMIC}) + list(APPEND DYNAMIC_ARG "-d") + endif () + foreach (SEARCH_DIR ${PARAMS_SEARCH_DIR}) file(GLOB_RECURSE INPUT_HEADER_FILES "${SEARCH_DIR}/*.h") foreach (INPUT_HEADER_FILE ${INPUT_HEADER_FILES}) @@ -254,7 +264,7 @@ function(AddMirrorInfoSourceGenerationTarget) add_custom_command( OUTPUT ${OUTPUT_SOURCE} - COMMAND "$" "-i" ${INPUT_HEADER_FILE} "-o" ${OUTPUT_SOURCE} ${INC_ARGS} + COMMAND "$" ${DYNAMIC_ARG} "-i" ${INPUT_HEADER_FILE} "-o" ${OUTPUT_SOURCE} ${INC_ARGS} DEPENDS MirrorTool ${INPUT_HEADER_FILE} ) endforeach() @@ -274,7 +284,7 @@ function(AddMirrorInfoSourceGenerationTarget) endfunction() function(AddExecutable) - cmake_parse_arguments(PARAMS "SAMPLE;META" "NAME" "SRC;INC;LINK;LIB;DEP_TARGET;RES;REFLECT" ${ARGN}) + cmake_parse_arguments(PARAMS "SAMPLE" "NAME" "SRC;INC;LINK;LIB;DEP_TARGET;RES;REFLECT" ${ARGN}) if (${PARAMS_SAMPLE} AND (NOT ${BUILD_SAMPLE})) return() @@ -332,13 +342,17 @@ function(AddExecutable) endfunction() function(AddLibrary) - cmake_parse_arguments(PARAMS "META" "NAME;TYPE" "SRC;PRIVATE_INC;PUBLIC_INC;PRIVATE_LINK;LIB;REFLECT" ${ARGN}) + cmake_parse_arguments(PARAMS "" "NAME;TYPE" "SRC;PRIVATE_INC;PUBLIC_INC;PRIVATE_LINK;LIB;REFLECT" ${ARGN}) if ("${PARAMS_TYPE}" STREQUAL "SHARED") list(APPEND PARAMS_PUBLIC_INC ${API_HEADER_DIR}/${PARAMS_NAME}) endif () if (DEFINED PARAMS_REFLECT) + if ("${PARAMS_TYPE}" STREQUAL "SHARED") + list(APPEND EXTRA_PARAMS DYNAMIC) + endif () + AddMirrorInfoSourceGenerationTarget( NAME ${PARAMS_NAME} OUTPUT_SRC GENERATED_SRC @@ -347,6 +361,7 @@ function(AddLibrary) PUBLIC_INC ${PARAMS_PUBLIC_INC} PRIVATE_INC ${PARAMS_PRIVATE_INC} LIB ${PARAMS_LIB} + ${EXTRA_PARAMS} ) endif() diff --git a/Editor/CMakeLists.txt b/Editor/CMakeLists.txt index c3023b612..c96d31166 100644 --- a/Editor/CMakeLists.txt +++ b/Editor/CMakeLists.txt @@ -12,20 +12,54 @@ qt_add_executable(Editor ${SOURCES}) target_include_directories(Editor PRIVATE Include) target_link_libraries(Editor PRIVATE Core RHI Runtime Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Quick) -# TODO config runtime dependencies in Windows +if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows") + set(PLATFORM_DEP_TARGET RHI-DirectX12 RHI-Vulkan) +else() + set(PLATFORM_DEP_TARGET RHI-Vulkan) +endif() +add_dependencies(Editor ${PLATFORM_DEP_TARGET}) -file(GLOB_RECURSE QML_SOURCES QML/*.qml) -file(GLOB_RECURSE RESOURCES Resource/*) +if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows") + set(QT_WIN_DEPLOY_EXECUTABLE ${QT_LIB_PREFIX}/bin/windeployqt.exe) + set(QT_BUILDIN_QML_DIRECTORY ${QT_LIB_PREFIX}/qml) + add_custom_command( + TARGET Editor POST_BUILD + COMMAND ${QT_WIN_DEPLOY_EXECUTABLE} $ --qmldir ${QT_BUILDIN_QML_DIRECTORY} --verbose 0 + ) +endif () -list(APPEND RESOURCES_PENDING_SET_ALIAS ${QML_SOURCES} ${RESOURCES}) -foreach (RESOURCE ${RESOURCES_PENDING_SET_ALIAS}) - get_filename_component(FILENAME ${RESOURCE} NAME) - set_source_files_properties(${RESOURCE} PROPERTIES QT_RESOURCE_ALIAS ${FILENAME}) +set(EDITOR_QML_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/Qml) +set(EDITOR_RESOURCE_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/Qml/Resource) +get_filename_component(EDITOR_RESOURCE_ROOT_ABSOLUTE ${EDITOR_RESOURCE_ROOT} ABSOLUTE) + +file(GLOB QML_SOURCES ${EDITOR_QML_ROOT}/*.qml) +file(GLOB_RECURSE RESOURCES ${EDITOR_RESOURCE_ROOT}/*) + +list( + APPEND SINGLETON_QML_SOURCES + ETheme.qml) +target_compile_definitions(Editor PRIVATE -DSINGLETON_QML_SOURCES="${SINGLETON_QML_SOURCES}") + +# QML only support placed in root dir +foreach (QML_SOURCE ${QML_SOURCES}) + get_filename_component(ALIAS ${QML_SOURCE} NAME) + set_source_files_properties(${QML_SOURCE} PROPERTIES QT_RESOURCE_ALIAS ${ALIAS}) + if (${ALIAS} IN_LIST SINGLETON_QML_SOURCES) + set_source_files_properties(${QML_SOURCE} PROPERTIES QT_QML_SINGLETON_TYPE true) + endif () +endforeach () + +foreach (RESOURCE ${RESOURCES}) + string(REPLACE ${EDITOR_RESOURCE_ROOT_ABSOLUTE} "Resource" ALIAS ${RESOURCE}) + set_source_files_properties(${RESOURCE} PROPERTIES QT_RESOURCE_ALIAS ${ALIAS}) endforeach () qt_add_qml_module( Editor + NO_CACHEGEN + NO_GENERATE_QMLDIR URI editor QML_FILES ${QML_SOURCES} RESOURCES ${RESOURCES} + OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/Generated/QmlModule ) diff --git a/Editor/Include/Editor/QmlEngine.h b/Editor/Include/Editor/QmlEngine.h new file mode 100644 index 000000000..2be9529e2 --- /dev/null +++ b/Editor/Include/Editor/QmlEngine.h @@ -0,0 +1,36 @@ +// +// Created by Kindem on 2024/12/31. +// + +#pragma once + +#include + +#include + +#include +#include + +namespace Editor { + class QmlEngine { + public: + static QmlEngine& Get(); + + ~QmlEngine(); + + QUrl GetUrlFromShort(const std::string& inQmlShortFileName) const; + void Start(); + void Stop(); + void Register(QmlWidget* inWidget); + void Unregister(QmlWidget* inWidget); + + private: + QmlEngine(); + + void ReloadSingletonTypes() const; + + Common::Path qmlSourceDir; + std::unordered_set widgets; + Common::UniqueRef watcher; + }; +} diff --git a/Editor/Include/Editor/QmlHotReload.h b/Editor/Include/Editor/QmlHotReload.h deleted file mode 100644 index 57b52e435..000000000 --- a/Editor/Include/Editor/QmlHotReload.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// Created by Kindem on 2024/12/31. -// - -#pragma once - -#include - -#include - -namespace Editor { - class QmlHotReloadEngine { - public: - static QmlHotReloadEngine& Get(); - - ~QmlHotReloadEngine(); - - void Start(); - void Stop(); - void Register(QQuickView* inView); - void Unregister(QQuickView* inView); - - private: - QmlHotReloadEngine(); - - std::unordered_set liveQuickViews; - }; -} diff --git a/Editor/Include/Editor/Widget/QmlWidget.h b/Editor/Include/Editor/Widget/QmlWidget.h index 9fed2f42b..3e986d88f 100644 --- a/Editor/Include/Editor/Widget/QmlWidget.h +++ b/Editor/Include/Editor/Widget/QmlWidget.h @@ -12,13 +12,18 @@ namespace Editor { Q_OBJECT public: - explicit QmlWidget(const std::string& qmlFileName, QWidget* parent = nullptr); + explicit QmlWidget(std::string inShortQmlFileName, QWidget* inParent = nullptr); + ~QmlWidget() override; + const std::string& GetShotQmlFileName() const; + const QUrl& GetUrl() const; QQuickView* GetQuickView(); - const QUrl& GetQmlUrl() const; + QWidget* GetQuickViewContainer() const; private: + std::string shortQmlFileName; QUrl url; QQuickView* quickView; + QWidget* quickViewContainer; }; } diff --git a/Editor/Include/Editor/Widget/WidgetSamples.h b/Editor/Include/Editor/Widget/WidgetSamples.h new file mode 100644 index 000000000..830a75b1e --- /dev/null +++ b/Editor/Include/Editor/Widget/WidgetSamples.h @@ -0,0 +1,16 @@ +// +// Created by Kindem on 2025/1/5. +// + +#pragma once + +#include + +namespace Editor { + class WidgetSamples final : public QmlWidget { + Q_OBJECT + + public: + WidgetSamples(); + }; +} diff --git a/Editor/QML/launcher.qml b/Editor/QML/launcher.qml deleted file mode 100644 index f8c63e1ff..000000000 --- a/Editor/QML/launcher.qml +++ /dev/null @@ -1,5 +0,0 @@ -import QtQuick - -Rectangle { - color: 'red' -} diff --git a/Editor/Qml/EButton.qml b/Editor/Qml/EButton.qml new file mode 100644 index 000000000..810bd9448 --- /dev/null +++ b/Editor/Qml/EButton.qml @@ -0,0 +1,21 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Controls.Basic + +Button { + contentItem: Text { + text: parent.text + font.pixelSize: ETheme.contentFontSize + font.family: ETheme.fontFamily + color: ETheme.fontColor + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + elide: Text.ElideNone + } + + background: Rectangle { + implicitWidth: 50 + color: parent.down ? ETheme.focusColor : ETheme.primaryColor + radius: 10 + } +} diff --git a/Editor/Qml/ELauncher.qml b/Editor/Qml/ELauncher.qml new file mode 100644 index 000000000..118f38887 --- /dev/null +++ b/Editor/Qml/ELauncher.qml @@ -0,0 +1,6 @@ +import QtQuick +import QtQuick.Controls + +Rectangle { + color: ETheme.bgColor +} diff --git a/Editor/Qml/EText.qml b/Editor/Qml/EText.qml new file mode 100644 index 000000000..61c244d8b --- /dev/null +++ b/Editor/Qml/EText.qml @@ -0,0 +1,36 @@ +import QtQuick +import QtQuick.Controls + +Text { + enum Style { + Title1, + Title2, + Title3, + Content + } + + function getFontBold(style) + { + return style !== EText.Style.Content + } + + function getFontPixelSize(style) + { + if (style === EText.Style.Title1) { + return ETheme.tiele1FontSize; + } else if (style === EText.Style.Title2) { + return ETheme.title2FontSize; + } else if (style === EText.Style.Title3) { + return ETheme.title3FontSize; + } else { + return ETheme.contentFontSize; + } + } + + property int style: EText.Style.Content + + font.bold: getFontBold(style) + font.pixelSize: getFontPixelSize(style) + font.family: ETheme.fontFamily + color: ETheme.fontColor +} diff --git a/Editor/Qml/ETheme.qml b/Editor/Qml/ETheme.qml new file mode 100644 index 000000000..ab3f73d83 --- /dev/null +++ b/Editor/Qml/ETheme.qml @@ -0,0 +1,19 @@ +pragma Singleton + +import QtQuick + +QtObject { + property color bgColor: Qt.color('#212121') + property color primaryColor: Qt.color('#e74c3c') + property color focusColor: Qt.color('#c0392b') + property color secondaryColor: Qt.color('#f1c40f') + property color fontColor: Qt.color('#ecf0f1') + + property FontLoader normalFont: FontLoader { source: Qt.url('Resource/Font/MiSans-Normal.ttf') } + property FontLoader boldFont: FontLoader { source: Qt.url('Resource/Font/MiSans-Bold.ttf') } + property int tiele1FontSize: 20 + property int title2FontSize: 18 + property int title3FontSize: 16 + property int contentFontSize: 14 + property string fontFamily: 'MiSans' +} diff --git a/Editor/Qml/EWidgetSamples.qml b/Editor/Qml/EWidgetSamples.qml new file mode 100644 index 000000000..bd899e4d8 --- /dev/null +++ b/Editor/Qml/EWidgetSamples.qml @@ -0,0 +1,99 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +Rectangle { + color: ETheme.bgColor + + ScrollView { + anchors.fill: parent + + ColumnLayout { + anchors.fill: parent + anchors.margins: 20 + + RowLayout { + Layout.leftMargin: 5 + + EText { + text: 'Buttons' + style: EText.Style.Title1 + } + } + + RowLayout { + Layout.margins: 5 + + EButton { + text: 'Basic' + } + + EButton { + text: 'TODO Secondary Button' + } + + EButton { + text: 'TODO Disabled Button' + } + + EButton { + text: 'TODO Icon Button' + } + + EButton { + text: 'TODO Outline Button' + } + } + + RowLayout { + Layout.margins: 5 + + EButton { + text: 'TODO Dashed Button' + } + + EButton { + text: 'TODO Dashed Button' + } + + EButton { + text: 'TODO Text Button' + } + } + + RowLayout { + Layout.leftMargin: 5 + Layout.topMargin: 10 + + EText { + text: 'Texts' + style: EText.Style.Title1 + } + } + + RowLayout { + Layout.leftMargin: 5 + + EText { + text: 'Title1' + style: EText.Style.Title1 + } + + EText { + text: 'Title2' + style: EText.Style.Title2 + } + + EText { + text: 'Title3' + style: EText.Style.Title3 + } + + EText { + text: 'Content' + style: EText.Style.Content + } + } + } + } +} diff --git a/Editor/Qml/Resource/Font/MiSans-Bold.ttf b/Editor/Qml/Resource/Font/MiSans-Bold.ttf new file mode 100644 index 000000000..eda4bea1e Binary files /dev/null and b/Editor/Qml/Resource/Font/MiSans-Bold.ttf differ diff --git a/Editor/Qml/Resource/Font/MiSans-Normal.ttf b/Editor/Qml/Resource/Font/MiSans-Normal.ttf new file mode 100644 index 000000000..efaedf2ad Binary files /dev/null and b/Editor/Qml/Resource/Font/MiSans-Normal.ttf differ diff --git a/Editor/Src/Core.cpp b/Editor/Src/Core.cpp index e0760c60e..ab23d06bc 100644 --- a/Editor/Src/Core.cpp +++ b/Editor/Src/Core.cpp @@ -5,13 +5,15 @@ #include #include -Core::CmdlineArgValue caRhiType( - "rhiType", "-rhi", RHI::GetPlatformDefaultRHIAbbrString(), - "rhi abbr string, can be 'dx12' or 'vulkan'"); +namespace Editor { + static ::Core::CmdlineArgValue caRhiType( + "rhiType", "-rhi", RHI::GetPlatformDefaultRHIAbbrString(), + "rhi abbr string, can be 'dx12' or 'vulkan'"); -Core::CmdlineArgValue caProjectFile( - "projectFile", "-project", "", - "project file path"); + static ::Core::CmdlineArgValue caProjectFile( + "projectFile", "-project", "", + "project file path"); +} namespace Editor { Core& Core::Get() @@ -58,6 +60,7 @@ namespace Editor { void Core::InitializeRuntime() { Runtime::EngineInitParams params {}; + params.logToFile = true; params.projectFile = caProjectFile.GetValue(); params.rhiType = caRhiType.GetValue(); diff --git a/Editor/Src/Main.cpp b/Editor/Src/Main.cpp index 038ea78f9..ebacd61b4 100644 --- a/Editor/Src/Main.cpp +++ b/Editor/Src/Main.cpp @@ -4,8 +4,15 @@ #include #include -#include +#include #include +#include + +#if BUILD_CONFIG_DEBUG +static ::Core::CmdlineArgValue caRunSample( + "widgetSamples", "-widgetSamples", false, + "Whether to run widget samples instead of editor"); +#endif int main(int argc, char* argv[]) { @@ -13,20 +20,26 @@ int main(int argc, char* argv[]) QApplication qtApplication(argc, argv); - Common::UniqueRef mainWindow; - if (!Editor::Core::Get().ProjectHasSet()) { - mainWindow = new Editor::Launcher(); + auto& qmlEngine = Editor::QmlEngine::Get(); + qmlEngine.Start(); + + Common::UniqueRef mainWidget; +#if BUILD_CONFIG_DEBUG + if (caRunSample.GetValue()) { + mainWidget = new Editor::WidgetSamples(); + } else +#endif + if (!Editor::Core::Get().ProjectHasSet()) { // NOLINT + mainWidget = new Editor::Launcher(); } else { // TODO editor main } - mainWindow->show(); + mainWidget->show(); - auto& qmlHotReloadEngine = Editor::QmlHotReloadEngine::Get(); - qmlHotReloadEngine.Start(); const int execRes = QApplication::exec(); - qmlHotReloadEngine.Stop(); + qmlEngine.Stop(); - mainWindow = nullptr; + mainWidget = nullptr; Editor::Core::Get().Cleanup(); return execRes; } diff --git a/Editor/Src/QmlEngine.cpp b/Editor/Src/QmlEngine.cpp new file mode 100644 index 000000000..048db827d --- /dev/null +++ b/Editor/Src/QmlEngine.cpp @@ -0,0 +1,100 @@ +// +// Created by Kindem on 2024/12/31. +// + +#include + +#include +#include +#include + +namespace Editor { + static ::Core::CmdlineArgValue caQmlHotReload( + "qmlHotReload", "-qmlHotReload", false, + "Whether to enable qml hot reload (for editor development)"); +} + +namespace Editor { + QmlEngine& QmlEngine::Get() + { + static QmlEngine engine; + return engine; + } + + QmlEngine::QmlEngine() + : qmlSourceDir(::Core::Paths::EngineCMakeSourceDir() / "Editor" / "Qml") + { + } + + QmlEngine::~QmlEngine() = default; + + QUrl QmlEngine::GetUrlFromShort(const std::string& inQmlShortFileName) const + { + if (caQmlHotReload.GetValue()) { + const Common::Path qmlSourcePath = qmlSourceDir / inQmlShortFileName; + return QUrl::fromLocalFile(QString::fromStdString(qmlSourcePath.String())); + } else { // NOLINT + const auto urlString = std::format("qrc:/qt/qml/editor/{}", inQmlShortFileName); + return QString::fromStdString(urlString); + } + } + + void QmlEngine::Start() + { + ReloadSingletonTypes(); + if (!caQmlHotReload.GetValue()) { + return; + } + + watcher = new QFileSystemWatcher(); + watcher->addPath(QString::fromStdString(qmlSourceDir.String())); + qmlSourceDir.TraverseRecurse([&](const Common::Path& path) -> bool { + if (path.IsDirectory()) { + watcher->addPath(QString::fromStdString(path.String())); + } + return true; + }); + + QObject::connect(watcher.Get(), &QFileSystemWatcher::directoryChanged, [this](const QString&) -> void { + ReloadSingletonTypes(); + + for (auto* widget : widgets) { + QQuickView* quickView = widget->GetQuickView(); + quickView->source().clear(); + quickView->engine()->clearComponentCache(); + quickView->setSource(widget->GetUrl()); + } + }); + } + + void QmlEngine::Stop() + { + widgets.clear(); + if (watcher == nullptr) { + return; + } + + QObject::disconnect(watcher.Get(), nullptr, nullptr, nullptr); + watcher.Reset(); + } + + void QmlEngine::Register(QmlWidget* inWidget) + { + widgets.emplace(inWidget); + } + + void QmlEngine::Unregister(QmlWidget* inWidget) + { + widgets.erase(inWidget); + } + + void QmlEngine::ReloadSingletonTypes() const + { + const std::vector singletonSources = Common::StringUtils::Split(SINGLETON_QML_SOURCES, ";"); + for (const auto& singletonSource : singletonSources) { + const std::string name = Common::Path(singletonSource).FileNameWithoutExtension(); + const QUrl url = GetUrlFromShort(singletonSource); + qmlRegisterSingletonType(url, name.c_str(), 1, 0, name.c_str()); + } + } +} // namespace Editor diff --git a/Editor/Src/QmlHotReload.cpp b/Editor/Src/QmlHotReload.cpp deleted file mode 100644 index 170c37cdb..000000000 --- a/Editor/Src/QmlHotReload.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// -// Created by Kindem on 2024/12/31. -// - -#include - -namespace Editor { - QmlHotReloadEngine& QmlHotReloadEngine::Get() - { - static QmlHotReloadEngine engine; - return engine; - } - - QmlHotReloadEngine::QmlHotReloadEngine() = default; - - QmlHotReloadEngine::~QmlHotReloadEngine() = default; - - void QmlHotReloadEngine::Start() - { - // TODO - } - - void QmlHotReloadEngine::Stop() - { - // TODO - } - - void QmlHotReloadEngine::Register(QQuickView* inView) - { - liveQuickViews.emplace(inView); - } - - void QmlHotReloadEngine::Unregister(QQuickView* inView) - { - liveQuickViews.erase(inView); - } -} // namespace Editor diff --git a/Editor/Src/Widget/Launcher.cpp b/Editor/Src/Widget/Launcher.cpp index 8dab65c08..581518fa3 100644 --- a/Editor/Src/Widget/Launcher.cpp +++ b/Editor/Src/Widget/Launcher.cpp @@ -7,7 +7,9 @@ namespace Editor { Launcher::Launcher() - : QmlWidget("launcher.qml") + : QmlWidget("ELauncher.qml") { + setWindowFlags(windowFlags() & ~Qt::WindowMaximizeButtonHint); + setFixedSize(1024, 768); } } diff --git a/Editor/Src/Widget/QmlWidget.cpp b/Editor/Src/Widget/QmlWidget.cpp index 9df364566..840d44c6b 100644 --- a/Editor/Src/Widget/QmlWidget.cpp +++ b/Editor/Src/Widget/QmlWidget.cpp @@ -5,21 +5,22 @@ #include #include +#include #include // NOLINT namespace Editor { - QmlWidget::QmlWidget(const std::string& qmlFileName, QWidget* parent) - : QWidget(parent) - , url(QUrl(QString(std::format("qrc:/qt/qml/editor/{}", qmlFileName).c_str()))) + QmlWidget::QmlWidget(std::string inShortQmlFileName, QWidget* inParent) + : QWidget(inParent) + , shortQmlFileName(std::move(inShortQmlFileName)) + , url(QmlEngine::Get().GetUrlFromShort(shortQmlFileName)) , quickView(new QQuickView) { quickView->setResizeMode(QQuickView::SizeRootObjectToView); quickView->setSource(url); - quickView->show(); - QWidget* quickViewContainer = createWindowContainer(quickView); - quickViewContainer->setMinimumSize(quickView->size()); - quickViewContainer->setFocusPolicy(Qt::TabFocus); + quickViewContainer = createWindowContainer(quickView); + quickViewContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + quickViewContainer->show(); QVBoxLayout* layout = new QVBoxLayout(); // NOLINT setLayout(layout); @@ -27,7 +28,22 @@ namespace Editor { layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(quickViewContainer); - // TODO support hot reload + QmlEngine::Get().Register(this); + } + + QmlWidget::~QmlWidget() + { + QmlEngine::Get().Unregister(this); + } + + const std::string& QmlWidget::GetShotQmlFileName() const + { + return shortQmlFileName; + } + + const QUrl& QmlWidget::GetUrl() const + { + return url; } QQuickView* QmlWidget::GetQuickView() // NOLINT @@ -35,8 +51,8 @@ namespace Editor { return quickView; } - const QUrl& QmlWidget::GetQmlUrl() const + QWidget* QmlWidget::GetQuickViewContainer() const { - return url; + return quickViewContainer; } } // namespace Editor diff --git a/Editor/Src/Widget/WidgetSamples.cpp b/Editor/Src/Widget/WidgetSamples.cpp new file mode 100644 index 000000000..2da8a9f24 --- /dev/null +++ b/Editor/Src/Widget/WidgetSamples.cpp @@ -0,0 +1,15 @@ +// +// Created by Kindem on 2025/1/5. +// + +#include +#include // NOLINT + +namespace Editor { + WidgetSamples::WidgetSamples() + : QmlWidget("EWidgetSamples.qml") + { + setWindowFlags(windowFlags() & ~Qt::WindowMaximizeButtonHint); + setFixedSize(1024, 768); + } +} diff --git a/Engine/Source/Common/Include/Common/Concepts.h b/Engine/Source/Common/Include/Common/Concepts.h index 688065c22..3e82037d3 100644 --- a/Engine/Source/Common/Include/Common/Concepts.h +++ b/Engine/Source/Common/Include/Common/Concepts.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include #include @@ -51,6 +52,7 @@ namespace Common { template concept CppMoveConstructible = std::is_move_constructible_v; template concept CppCopyAssignable = std::is_copy_assignable_v; template concept CppMoveAssignable = std::is_move_assignable_v; + template concept CppStdString = std::is_same_v; template concept ArgsNumEqual = sizeof...(T) == N; template concept ArgsNumLess = sizeof...(T) < N; template concept ArgsNumGreater = sizeof...(T) > N; diff --git a/Engine/Source/Common/Include/Common/Container.h b/Engine/Source/Common/Include/Common/Container.h index 814dbdfa8..2bd6e970e 100644 --- a/Engine/Source/Common/Include/Common/Container.h +++ b/Engine/Source/Common/Include/Common/Container.h @@ -43,11 +43,6 @@ namespace Common { explicit InplaceVectorIter(T* inPtr); - InplaceVectorIter(const InplaceVectorIter& inOther); - InplaceVectorIter(InplaceVectorIter&& inOther) noexcept; - InplaceVectorIter& operator=(const InplaceVectorIter& inOther); - InplaceVectorIter& operator=(InplaceVectorIter&& inOther) noexcept; - template T2> Offset operator-(const T2& inOther) const; template T2> bool operator==(const T2& inOther) const; template T2> bool operator!=(const T2& inOther) const; @@ -212,8 +207,8 @@ namespace Common { ConstHandle(const TrunkList* inOwner, const Trunk* inTrunk, size_t inIndex); explicit operator bool() const; - T* operator->() const; - T& operator*() const; + const T* operator->() const; + const T& operator*() const; private: friend class TrunkList; @@ -231,8 +226,8 @@ namespace Common { ConstHandle Const() const; explicit operator bool() const; - const T* operator->() const; - const T& operator*() const; + T* operator->() const; + T& operator*() const; private: friend class TrunkList; @@ -247,6 +242,10 @@ namespace Common { TrunkList(); ~TrunkList(); + // move and copy will cause handle invalid + NonCopyable(TrunkList) + NonMovable(TrunkList) + template Handle Emplace(Args&&... inArgs); void Erase(const Handle& inHandle); void Erase(const ConstHandle& inHandle); @@ -262,9 +261,39 @@ namespace Common { private: std::list> trunks; }; + + template , typename EqualTo = std::equal_to> + class StableUnorderedMap { + public: + using TraverseFunc = std::function; + using ConstTraverseFunc = std::function; + + StableUnorderedMap(); + ~StableUnorderedMap(); + + NonCopyable(StableUnorderedMap) + NonMovable(StableUnorderedMap) + + template V& Emplace(const K& inKey, Args&&... inValueArgs); + void Each(const TraverseFunc& inFunc); + void Each(const ConstTraverseFunc& inFunc) const; + V& At(const K& inKey); + const V& At(const K& inKey) const; + bool Contains(const K& inKey) const; + void Erase(const K& inKey); + void Reserve(size_t inCapacity); + size_t Size() const; + size_t Capacity() const; + + private: + using ValuesContainer = TrunkList; + + std::unordered_map handleMap; + ValuesContainer values; + }; } -namespace Common { // NOLINT +namespace Common { template struct EqualComparableTest> { static constexpr bool value = BaseEqualComparable; @@ -348,32 +377,6 @@ namespace Common { { } - template - InplaceVectorIter::InplaceVectorIter(const InplaceVectorIter& inOther) - : ptr(inOther.ptr) - { - } - - template - InplaceVectorIter::InplaceVectorIter(InplaceVectorIter&& inOther) noexcept - : ptr(inOther.ptr) - { - } - - template - InplaceVectorIter& InplaceVectorIter::operator=(const InplaceVectorIter& inOther) - { - ptr = inOther.ptr; - return *this; - } - - template - InplaceVectorIter& InplaceVectorIter::operator=(InplaceVectorIter&& inOther) noexcept - { - ptr = inOther.ptr; - return *this; - } - template template T2> typename InplaceVectorIter::Offset InplaceVectorIter::operator-(const T2& inOther) const @@ -1314,13 +1317,13 @@ namespace Common { } template - T* TrunkList::ConstHandle::operator->() const + const T* TrunkList::ConstHandle::operator->() const { return trunk->Try(index); } template - T& TrunkList::ConstHandle::operator*() const + const T& TrunkList::ConstHandle::operator*() const { return trunk->At(index); } @@ -1352,13 +1355,13 @@ namespace Common { } template - const T* TrunkList::Handle::operator->() const + T* TrunkList::Handle::operator->() const { return trunk->Try(index); } template - const T& TrunkList::Handle::operator*() const + T& TrunkList::Handle::operator*() const { return trunk->At(index); } @@ -1469,4 +1472,83 @@ namespace Common { { return Empty(); } + + template + StableUnorderedMap::StableUnorderedMap() = default; + + template + StableUnorderedMap::~StableUnorderedMap() = default; + + template + template + V& StableUnorderedMap::Emplace(const K& inKey, Args&&... inValueArgs) + { + Assert(!Contains(inKey)); + const auto handle = values.Emplace(std::forward(inValueArgs)...); + handleMap.emplace(inKey, handle); + return *handle; + } + + template + void StableUnorderedMap::Each(const TraverseFunc& inFunc) + { + for (const auto& [key, handle] : handleMap) { + inFunc(key, *handle); + } + } + + template + void StableUnorderedMap::Each(const ConstTraverseFunc& inFunc) const + { + for (const auto& [key, handle] : handleMap) { + inFunc(key, *handle); + } + } + + template + V& StableUnorderedMap::At(const K& inKey) + { + const auto& handle = handleMap.at(inKey); + return *handle; + } + + template + const V& StableUnorderedMap::At(const K& inKey) const + { + const auto& handle = handleMap.at(inKey); + return *handle; + } + + template + bool StableUnorderedMap::Contains(const K& inKey) const + { + return handleMap.contains(inKey); + } + + template + void StableUnorderedMap::Erase(const K& inKey) + { + const auto& handle = handleMap.at(inKey); + values.Erase(handle); + handleMap.erase(inKey); + } + + template + void StableUnorderedMap::Reserve(size_t inCapacity) + { + values.Reserve(inCapacity); + handleMap.reserve(values.Capacity()); + } + + template + size_t StableUnorderedMap::Size() const + { + return handleMap.size(); + } + + template + size_t StableUnorderedMap::Capacity() const + { + return values.Capacity(); + } } diff --git a/Engine/Source/Common/Include/Common/FileSystem.h b/Engine/Source/Common/Include/Common/FileSystem.h new file mode 100644 index 000000000..7f39864cc --- /dev/null +++ b/Engine/Source/Common/Include/Common/FileSystem.h @@ -0,0 +1,51 @@ +// +// Created by johnk on 2025/1/2. +// + +#pragma once + +#include + +namespace Common { + class Path { + public: + using TraverseFunc = std::function; + + static Path WorkingDirectory(); + + Path(); + Path(const std::filesystem::path& inPath, bool inNeedFixedUp = true); // NOLINT + Path(const std::string& inPath, bool inNeedFixedUp = true); // NOLINT + Path(const char* inPath, bool inNeedFixedUp = true); // NOLINT + + std::string String() const; + Path Parent() const; + std::string FileName() const; + std::string FileNameWithoutExtension() const; + bool Empty() const; + bool Exists() const; + bool IsFile() const; + bool IsDirectory() const; + bool HasExtension() const; + std::string Extension() const; + bool IsAbsolute() const; + bool IsRelative() const; + Path Absolute() const; + Path Relative(const Path& inRelative) const; + Path Canonical() const; + size_t Traverse(const TraverseFunc& inFunc) const; + size_t TraverseRecurse(const TraverseFunc& inFunc) const; + void CopyTo(const Path& inPath) const; + void MakeDir() const; + + Path operator/(const Path& inPath) const; + Path operator/(const std::string& inPath) const; + Path operator/(const char* inPath) const; + Path operator+(const Path& inPath) const; + Path operator+(const std::string& inPath) const; + Path operator+(const char* inPath) const; + + private: + std::filesystem::path path; + }; +} diff --git a/Engine/Source/Common/Include/Common/Math/Common.h b/Engine/Source/Common/Include/Common/Math/Common.h index e745da464..f65a85b63 100644 --- a/Engine/Source/Common/Include/Common/Math/Common.h +++ b/Engine/Source/Common/Include/Common/Math/Common.h @@ -16,13 +16,13 @@ namespace Common { } namespace Common { - template T CompareNumber(T lhs, T rhs); + template bool CompareNumber(T lhs, T rhs); template T DivideAndRoundUp(T lhs, T rhs); } namespace Common { template - T CompareNumber(T lhs, T rhs) + bool CompareNumber(T lhs, T rhs) { if constexpr (std::is_floating_point_v) { return std::abs(lhs - rhs) < epsilon; diff --git a/Engine/Source/Common/Include/Common/Time.h b/Engine/Source/Common/Include/Common/Time.h new file mode 100644 index 000000000..efdd222eb --- /dev/null +++ b/Engine/Source/Common/Include/Common/Time.h @@ -0,0 +1,75 @@ +// +// Created by johnk on 2025/1/14. +// + +#pragma once + +#include + +namespace Common { + class Time; + class AccurateTime; + + class TimePoint { + public: + static TimePoint Now(); + + explicit TimePoint(const std::chrono::system_clock::time_point& inTimePoint); + + Time ToTime() const; + AccurateTime ToAccurateTime() const; + float ToSeconds() const; + float ToMilliseconds() const; + uint64_t ToMicroseconds() const; + const std::chrono::system_clock::time_point& GetNative() const; + + private: + std::chrono::system_clock::time_point timePoint; + }; + + class Time { + public: + explicit Time(const TimePoint& inTimePoint); + + uint16_t Year() const; + uint8_t Month() const; + uint8_t Day() const; + uint8_t Hour() const; + uint8_t Minute() const; + uint8_t Second() const; + std::string ToString(const std::string& inFormat = "YYYY-MM-DD-hh-mm-ss") const; + + private: + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; + }; + + class AccurateTime { + public: + explicit AccurateTime(const TimePoint& inTimePoint); + + uint16_t Year() const; + uint8_t Month() const; + uint8_t Day() const; + uint8_t Hour() const; + uint8_t Minute() const; + uint8_t Second() const; + uint16_t Milliseconds() const; + uint64_t Microseconds() const; + std::string ToString(const std::string& inFormat = "YYYY-MM-DD-hh-mm-ss:mss:uss") const; + + private: + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; + uint16_t millisecond; + uint16_t microsecond; + }; +} diff --git a/Engine/Source/Common/Include/Common/Utility.h b/Engine/Source/Common/Include/Common/Utility.h index 4ebc2cebd..9da719e7c 100644 --- a/Engine/Source/Common/Include/Common/Utility.h +++ b/Engine/Source/Common/Include/Common/Utility.h @@ -4,11 +4,14 @@ #pragma once +#include #include #include #include +#include #include +#include #define DefaultCopyCtor(clz) \ clz(clz&) = default; \ @@ -48,8 +51,56 @@ namespace Common { template struct IsAllSame {}; + + template + T ToArithmetic(const std::string& inStr); } +// ------------------------ FlagAndBits Begin ------------------------ +#define DECLARE_FLAG_BITS_OP(FlagsType, BitsType) \ + inline FlagsType operator&(BitsType a, BitsType b) { return FlagsType(static_cast(a) & static_cast(b)); } \ + inline FlagsType operator|(BitsType a, BitsType b) { return FlagsType(static_cast(a) | static_cast(b)); } \ + +namespace Common { + template + using BitsForEachFunc = std::function; + + template + void ForEachBits(BitsForEachFunc&& func); + + template + class Flags { + public: + static Flags null; + + using UnderlyingType = std::underlying_type_t; + + Flags(); + ~Flags(); + Flags(UnderlyingType inValue); // NOLINT + Flags(E e); // NOLINT + + UnderlyingType Value() const; + explicit operator bool(); + bool operator==(Flags other) const; + bool operator!=(Flags other) const; + bool operator==(UnderlyingType inValue) const; + bool operator!=(UnderlyingType inValue) const; + bool operator==(E e) const; + bool operator!=(E e) const; + Flags operator&(Flags other) const; + Flags operator|(Flags other) const; + Flags operator&(UnderlyingType inValue) const; + Flags operator|(UnderlyingType inValue) const; + Flags operator&(E e) const; + Flags operator|(E e) const; + + private: + UnderlyingType value; + }; +} +// ------------------------ FlagAndBits End -------------------------- + namespace Common { template T AlignUp(T value) @@ -66,4 +117,149 @@ namespace Common { struct IsAllSame { static constexpr bool value = std::is_same_v, std::remove_cvref_t>; }; + + template + T ToArithmetic(const std::string& inStr) + { + if constexpr (CppIntegral) { + return std::stoi(inStr); + } else if constexpr (CppFloatingPoint) { + return std::stof(inStr); + } else if constexpr (CppBool) { + if (inStr == "true") { + return true; + } else if (inStr == "false") { + return false; + } else { + return QuickFail(), false; + } + } else { + return QuickFail(), T(); + } + } + + template + void ForEachBits(BitsForEachFunc&& func) + { + using UBitsType = std::underlying_type_t; + for (UBitsType i = 0x1; i < static_cast(E::max); i = i << 1) { + func(static_cast(i)); + } + } + + template + Flags::Flags() = default; + + template + Flags::~Flags() = default; + + template + Flags::Flags(UnderlyingType inValue) + : value(inValue) + { + } + + template + Flags::Flags(E e) + : value(static_cast(e)) + { + } + + template + typename Flags::UnderlyingType Flags::Value() const + { + return value; + } + + template + Flags::operator bool() + { + return value; + } + + template + bool Flags::operator==(Flags other) const + { + return value == other.value; + } + + template + bool Flags::operator!=(Flags other) const + { + return value != other.value; + } + + template + bool Flags::operator==(UnderlyingType inValue) const + { + return value == inValue; + } + + template + bool Flags::operator!=(UnderlyingType inValue) const + { + return value != inValue; + } + + template + bool Flags::operator==(E e) const + { + return value == static_cast(e); + } + + template + bool Flags::operator!=(E e) const + { + return value != static_cast(e); + } + + template + Flags Flags::null = Flags(0); + + template + Flags Flags::operator&(Flags other) const + { + return Flags(Value() & other.Value()); + } + + template + Flags Flags::operator|(Flags other) const + { + return Flags(Value() | other.Value()); + } + + template + Flags Flags::operator&(UnderlyingType inValue) const + { + return Flags(Value() & inValue); + } + + template + Flags Flags::operator|(UnderlyingType inValue) const + { + return Flags(Value() | inValue); + } + + template + Flags Flags::operator&(E e) const + { + return Flags(Value() & static_cast(e)); + } + + template + Flags Flags::operator|(E e) const + { + return Flags(Value() | static_cast(e)); + } +} + +namespace std { // NOLINT + template + struct hash> + { + size_t operator()(Common::Flags flags) const + { + return hash::UnderlyingType>()(flags.Value()); + } + }; } diff --git a/Engine/Source/Common/Src/FileSystem.cpp b/Engine/Source/Common/Src/FileSystem.cpp new file mode 100644 index 000000000..5d8c7a72c --- /dev/null +++ b/Engine/Source/Common/Src/FileSystem.cpp @@ -0,0 +1,186 @@ +// +// Created by johnk on 2025/1/2. +// + +#include +#include + +namespace Common::Internal { + std::filesystem::path GetUnixStylePath(const std::string& inPath) + { + return Common::StringUtils::Replace(std::filesystem::weakly_canonical(inPath).string(), "\\", "/"); + } + + std::filesystem::path GetUnixStylePath(const std::filesystem::path& inPath) + { + return GetUnixStylePath(inPath.string()); + } +} + +namespace Common { + Path Path::WorkingDirectory() + { + return { std::filesystem::current_path() }; + } + + Path::Path() = default; + + Path::Path(const std::filesystem::path& inPath, bool inNeedFixedUp) + : path(inNeedFixedUp ? Internal::GetUnixStylePath(inPath) : inPath) + { + } + + Path::Path(const std::string& inPath, bool inNeedFixedUp) + : path(inNeedFixedUp ? Internal::GetUnixStylePath(inPath) : std::filesystem::path(inPath)) + { + } + + Path::Path(const char* inPath, bool inNeedFixedUp) + : path(inNeedFixedUp ? Internal::GetUnixStylePath(std::string(inPath)) : std::filesystem::path(inPath)) + { + } + + std::string Path::String() const + { + return path.string(); + } + + Path Path::operator/(const Path& inPath) const + { + return { String() + "/" + inPath.String(), false }; + } + + Path Path::operator/(const std::string& inPath) const + { + return { String() + "/" + inPath, false }; + } + + Path Path::operator/(const char* inPath) const + { + return { String() + "/" + inPath, false }; + } + + Path Path::operator+(const Path& inPath) const + { + return { String() + inPath.String(), false }; + } + + Path Path::operator+(const std::string& inPath) const + { + return { String() + inPath, false }; + } + + Path Path::operator+(const char* inPath) const + { + return { String() + inPath, false }; + } + + Path Path::Parent() const + { + return { path.parent_path(), false }; + } + + std::string Path::FileName() const + { + Assert(IsFile()); + return { path.filename().string() }; + } + + std::string Path::FileNameWithoutExtension() const + { + const auto extension = Extension(); + return extension.empty() ? FileName() : Common::StringUtils::Replace(FileName(), Extension(), ""); + } + + bool Path::Empty() const + { + return path.empty(); + } + + bool Path::Exists() const + { + return std::filesystem::exists(path); + } + + bool Path::IsFile() const + { + return !std::filesystem::is_directory(path); + } + + bool Path::IsDirectory() const + { + return std::filesystem::is_directory(path); + } + + bool Path::HasExtension() const + { + return path.has_extension(); + } + + std::string Path::Extension() const + { + Assert(IsFile()); + return path.extension().string(); + } + + bool Path::IsAbsolute() const + { + return path.is_absolute(); + } + + bool Path::IsRelative() const + { + return path.is_relative(); + } + + Path Path::Absolute() const + { + return { std::filesystem::absolute(path) }; + } + + Path Path::Relative(const Path& inRelative) const + { + return { std::filesystem::relative(path, inRelative.path) }; + } + + Path Path::Canonical() const + { + return { std::filesystem::weakly_canonical(path) }; + } + + size_t Path::Traverse(const TraverseFunc& inFunc) const + { + Assert(IsDirectory()); + size_t count = 0; + for (const auto& entry : std::filesystem::directory_iterator(path)) { + count++; + if (!inFunc(entry.path())) { + break; + } + } + return count; + } + + size_t Path::TraverseRecurse(const TraverseFunc& inFunc) const + { + Assert(IsDirectory()); + size_t count = 0; + for (const auto& entry : std::filesystem::recursive_directory_iterator(path)) { + count++; + if (!inFunc(entry.path())) { + break; + } + } + return count; + } + + void Path::CopyTo(const Path& inPath) const + { + std::filesystem::copy(path, inPath.path, std::filesystem::copy_options::update_existing | std::filesystem::copy_options::recursive); + } + + void Path::MakeDir() const + { + std::filesystem::create_directories(path); + } +} // namespace Common diff --git a/Engine/Source/Common/Src/Time.cpp b/Engine/Source/Common/Src/Time.cpp new file mode 100644 index 000000000..398ad2f3a --- /dev/null +++ b/Engine/Source/Common/Src/Time.cpp @@ -0,0 +1,189 @@ +// +// Created by johnk on 2025/1/14. +// + +#include + +#include +#include +#include + +namespace Common::Internal { + template + static std::string GetNumStringFillZeros(T inNum, uint8_t inWidth) + { + const std::string numStr = std::to_string(inNum); + std::stringstream stream; + auto count = inWidth - numStr.length(); + while (count > 0) { + stream << "0"; + --count; + } + stream << numStr; + return stream.str(); + } +} + +namespace Common { + TimePoint TimePoint::Now() + { + return TimePoint { std::chrono::system_clock::now() }; + } + + TimePoint::TimePoint(const std::chrono::system_clock::time_point& inTimePoint) + : timePoint(inTimePoint) + { + } + + Time TimePoint::ToTime() const + { + return Time { *this }; + } + + AccurateTime TimePoint::ToAccurateTime() const + { + return AccurateTime { *this }; + } + + float TimePoint::ToSeconds() const + { + return ToMilliseconds() / 1000.0f; + } + + float TimePoint::ToMilliseconds() const + { + return static_cast(ToMicroseconds()) / 1000.0f; + } + + uint64_t TimePoint::ToMicroseconds() const + { + return std::chrono::duration_cast(timePoint.time_since_epoch()).count(); + } + + const std::chrono::system_clock::time_point& TimePoint::GetNative() const + { + return timePoint; + } + + Time::Time(const TimePoint& inTimePoint) + { + const auto time = std::chrono::system_clock::to_time_t(inTimePoint.GetNative()); + const auto tm = std::localtime(&time); // NOLINT + year = tm->tm_year + 1900; + month = tm->tm_mon + 1; + day = tm->tm_mday; + hour = tm->tm_hour; + minute = tm->tm_min; + second = tm->tm_sec; + } + + uint16_t Time::Year() const + { + return year; + } + + uint8_t Time::Month() const + { + return month; + } + + uint8_t Time::Day() const + { + return day; + } + + uint8_t Time::Hour() const + { + return hour; + } + + uint8_t Time::Minute() const + { + return minute; + } + + uint8_t Time::Second() const + { + return second; + } + + std::string Time::ToString(const std::string& inFormat) const + { + auto temp = StringUtils::Replace(inFormat, "YYYY", Internal::GetNumStringFillZeros(year, 4)); + temp = StringUtils::Replace(temp, "MM", Internal::GetNumStringFillZeros(month, 2)); + temp = StringUtils::Replace(temp, "DD", Internal::GetNumStringFillZeros(day, 2)); + temp = StringUtils::Replace(temp, "hh", Internal::GetNumStringFillZeros(hour, 2)); + temp = StringUtils::Replace(temp, "mm", Internal::GetNumStringFillZeros(minute, 2)); + temp = StringUtils::Replace(temp, "ss", Internal::GetNumStringFillZeros(second, 2)); + return temp; + } + + AccurateTime::AccurateTime(const TimePoint& inTimePoint) + { + const auto time = std::chrono::system_clock::to_time_t(inTimePoint.GetNative()); + const auto* tm = std::localtime(&time); // NOLINT + year = tm->tm_year + 1900; + month = tm->tm_mon + 1; + day = tm->tm_mday; + hour = tm->tm_hour; + minute = tm->tm_min; + second = tm->tm_sec; + + const auto us = inTimePoint.ToMicroseconds() % 1000000; + millisecond = us / 1000; + microsecond = us % 1000; + } + + uint16_t AccurateTime::Year() const + { + return year; + } + + uint8_t AccurateTime::Month() const + { + return month; + } + + uint8_t AccurateTime::Day() const + { + return day; + } + + uint8_t AccurateTime::Hour() const + { + return hour; + } + + uint8_t AccurateTime::Minute() const + { + return minute; + } + + uint8_t AccurateTime::Second() const + { + return second; + } + + uint16_t AccurateTime::Milliseconds() const + { + return millisecond; + } + + uint64_t AccurateTime::Microseconds() const + { + return microsecond; + } + + std::string AccurateTime::ToString(const std::string& inFormat) const + { + auto temp = StringUtils::Replace(inFormat, "mss", Internal::GetNumStringFillZeros(millisecond, 3)); + temp = StringUtils::Replace(temp, "uss", Internal::GetNumStringFillZeros(microsecond, 3)); + temp = StringUtils::Replace(temp, "YYYY", Internal::GetNumStringFillZeros(year, 4)); + temp = StringUtils::Replace(temp, "MM", Internal::GetNumStringFillZeros(month, 2)); + temp = StringUtils::Replace(temp, "DD", Internal::GetNumStringFillZeros(day, 2)); + temp = StringUtils::Replace(temp, "hh", Internal::GetNumStringFillZeros(hour, 2)); + temp = StringUtils::Replace(temp, "mm", Internal::GetNumStringFillZeros(minute, 2)); + temp = StringUtils::Replace(temp, "ss", Internal::GetNumStringFillZeros(second, 2)); + return temp; + } +}; diff --git a/Engine/Source/Common/Test/ContainerTest.cpp b/Engine/Source/Common/Test/ContainerTest.cpp index 36d12fad1..5ded7288f 100644 --- a/Engine/Source/Common/Test/ContainerTest.cpp +++ b/Engine/Source/Common/Test/ContainerTest.cpp @@ -442,3 +442,32 @@ TEST(ContainerTest, TrunkListBasic) ASSERT_EQ(elem, count++); }); } + +TEST(ContainerTest, StableUnorderedMapTest) +{ + StableUnorderedMap t0; + bool* a0 = &t0.Emplace(1, true); + t0.Emplace(2, true); + t0.Emplace(3, false); + t0.Emplace(4, true); + t0.Emplace(5, false); + ASSERT_EQ(a0, &t0.At(1)); + + ASSERT_TRUE(t0.Contains(1)); + ASSERT_TRUE(t0.Contains(3)); + t0.Erase(1); + ASSERT_FALSE(t0.Contains(1)); + + ASSERT_EQ(t0.At(2), true); + ASSERT_EQ(t0.At(3), false); + ASSERT_EQ(t0.At(4), true); + ASSERT_EQ(t0.At(5), false); + + uint32_t count = 0; + t0.Each([&](const int&, const bool&) -> void { + count++; + }); + ASSERT_EQ(count, 4); + ASSERT_EQ(t0.Size(), 4); + ASSERT_EQ(t0.Capacity(), 8); +} diff --git a/Engine/Source/Common/Test/DelegateTest.cpp b/Engine/Source/Common/Test/DelegateTest.cpp index 97d6a948a..91403895b 100644 --- a/Engine/Source/Common/Test/DelegateTest.cpp +++ b/Engine/Source/Common/Test/DelegateTest.cpp @@ -25,7 +25,7 @@ class Receiver { } }; -TEST(EventTest, BasicTest) +TEST(DelegateTest, BasicTest) { Receiver receiver; diff --git a/Engine/Source/Common/Test/FileSystemTest.cpp b/Engine/Source/Common/Test/FileSystemTest.cpp new file mode 100644 index 000000000..18b30368f --- /dev/null +++ b/Engine/Source/Common/Test/FileSystemTest.cpp @@ -0,0 +1,10 @@ +// +// Created by johnk on 2025/1/3. +// + +#include + +TEST(FileSystemTest, PathTest) +{ + // TODO +} diff --git a/Engine/Source/Core/Include/Core/Console.h b/Engine/Source/Core/Include/Core/Console.h new file mode 100644 index 000000000..68622c362 --- /dev/null +++ b/Engine/Source/Core/Include/Core/Console.h @@ -0,0 +1,497 @@ +// +// Created by johnk on 2025/1/16. +// + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace Core { + enum class CSFlagBits { + // TODO + }; + using CSFlags = Common::Flags; + DECLARE_FLAG_BITS_OP(CSFlags, CSFlagBits) + + template + concept ConsoleSettingBasicType = Common::CppArithmetic || Common::CppStdString; + + class CORE_API ConsoleSetting { + public: + virtual ~ConsoleSetting(); + + const std::string& Name() const; + const std::string& Description() const; + CSFlags Flags() const; + + // any-thread + virtual int8_t GetI8() const = 0; + virtual uint8_t GetU8() const = 0; + virtual int16_t GetI16() const = 0; + virtual uint16_t GetU16() const = 0; + virtual int32_t GetI32() const = 0; + virtual uint32_t GetU32() const = 0; + virtual int64_t GetI64() const = 0; + virtual uint64_t GetU64() const = 0; + virtual bool GetBool() const = 0; + virtual float GetFloat() const = 0; + virtual double GetDouble() const = 0; + virtual std::string GetString() const = 0; + + // game-thread + virtual void SetI8(int8_t inValue) = 0; + virtual void SetU8(uint8_t inValue) = 0; + virtual void SetI16(int16_t inValue) = 0; + virtual void SetU16(uint16_t inValue) = 0; + virtual void SetI32(int32_t inValue) = 0; + virtual void SetU32(uint32_t inValue) = 0; + virtual void SetI64(int64_t inValue) = 0; + virtual void SetU64(uint64_t inValue) = 0; + virtual void SetBool(bool inValue) = 0; + virtual void SetFloat(float inValue) = 0; + virtual void SetDouble(double inValue) = 0; + virtual void SetString(const std::string& inValue) = 0; + + protected: + ConsoleSetting(const std::string& inName, const std::string& inDescription, const CSFlags& inFlags = CSFlags::null); + + virtual void PerformRenderThreadCopy() = 0; + + private: + friend class Console; + + std::string name; + std::string description; + CSFlags flags; + }; + + template + class ConsoleSettingValue final : public ConsoleSetting { + public: + ConsoleSettingValue(const std::string& inName, const std::string& inDescription, const T& inDefaultValue, const CSFlags& inFlags = CSFlags::null); + ~ConsoleSettingValue() override; + + // any-thread + const T& Get() const; + // game-thread + const T& GetGT() const; + // render-thread + const T& GetRT() const; + // game-thread + void Set(const T& inValue); + + // any-thread + int8_t GetI8() const override; + uint8_t GetU8() const override; + int16_t GetI16() const override; + uint16_t GetU16() const override; + int32_t GetI32() const override; + uint32_t GetU32() const override; + int64_t GetI64() const override; + uint64_t GetU64() const override; + bool GetBool() const override; + float GetFloat() const override; + double GetDouble() const override; + std::string GetString() const override; + + // game-thread + void SetI8(int8_t inValue) override; + void SetU8(uint8_t inValue) override; + void SetI16(int16_t inValue) override; + void SetU16(uint16_t inValue) override; + void SetI32(int32_t inValue) override; + void SetU32(uint32_t inValue) override; + void SetI64(int64_t inValue) override; + void SetU64(uint64_t inValue) override; + void SetBool(bool inValue) override; + void SetFloat(float inValue) override; + void SetDouble(double inValue) override; + void SetString(const std::string& inValue) override; + + protected: + void PerformRenderThreadCopy() override; + + private: + // 0: game/gameWorker + // 1: render/renderWorker + T value[2]; + }; + + class CORE_API Console { + public: + static Console& Get(); + ~Console(); + + bool HasSetting(const std::string& inName) const; + ConsoleSetting* FindSetting(const std::string& inName) const; + ConsoleSetting& GetSetting(const std::string& inName) const; + template ConsoleSettingValue* FindSettingValue(const std::string& inName) const; + template ConsoleSettingValue& GetSettingValue(const std::string& inName) const; + void PerformRenderThreadCopy() const; + + private: + friend class ConsoleSetting; + + Console(); + + void RegisterConsoleSetting(ConsoleSetting& inSetting); + + std::unordered_map settings; + }; +} + +namespace Core { + template + ConsoleSettingValue::ConsoleSettingValue(const std::string& inName, const std::string& inDescription, const T& inDefaultValue, const CSFlags& inFlags) + : ConsoleSetting(inName, inDescription, inFlags) + { + value[0] = inDefaultValue; + value[1] = inDefaultValue; + } + + template + ConsoleSettingValue::~ConsoleSettingValue() = default; + + template + const T& ConsoleSettingValue::Get() const + { + if (ThreadContext::IsGameOrWorkerThread()) { + return value[0]; + } + if (ThreadContext::IsRenderOrWorkerThread()) { + return value[1]; + } + return QuickFail(), value[0]; + } + + template + const T& ConsoleSettingValue::GetGT() const + { + Assert(ThreadContext::IsGameOrWorkerThread()); + return value[0]; + } + + template + const T& ConsoleSettingValue::GetRT() const + { + Assert(ThreadContext::IsRenderOrWorkerThread()); + return value[1]; + } + + template + void ConsoleSettingValue::Set(const T& inValue) + { + value[0] = inValue; + } + + template + void ConsoleSettingValue::PerformRenderThreadCopy() + { + value[1] = value[0]; + } + + template + int8_t ConsoleSettingValue::GetI8() const + { + if constexpr (Common::CppStdString) { + return Common::ToArithmetic(Get()); + } else if constexpr (Common::CppBool) { + return Get() ? 1 : 0; + } else { + return static_cast(Get()); + } + } + + template + uint8_t ConsoleSettingValue::GetU8() const + { + if constexpr (Common::CppStdString) { + return Common::ToArithmetic(Get()); + } else if constexpr (Common::CppBool) { + return Get() ? 1 : 0; + } else { + return static_cast(Get()); + } + } + + template + int16_t ConsoleSettingValue::GetI16() const + { + if constexpr (Common::CppStdString) { + return Common::ToArithmetic(Get()); + } else if constexpr (Common::CppBool) { + return Get() ? 1 : 0; + } else { + return static_cast(Get()); + } + } + + template + uint16_t ConsoleSettingValue::GetU16() const + { + if constexpr (Common::CppStdString) { + return Common::ToArithmetic(Get()); + } else if constexpr (Common::CppBool) { + return Get() ? 1 : 0; + } else { + return static_cast(Get()); + } + } + + template + int32_t ConsoleSettingValue::GetI32() const + { + if constexpr (Common::CppStdString) { + return Common::ToArithmetic(Get()); + } else if constexpr (Common::CppBool) { + return Get() ? 1 : 0; + } else { + return static_cast(Get()); + } + } + + template + uint32_t ConsoleSettingValue::GetU32() const + { + if constexpr (Common::CppStdString) { + return Common::ToArithmetic(Get()); + } else if constexpr (Common::CppBool) { + return Get() ? 1 : 0; + } else { + return static_cast(Get()); + } + } + + template + int64_t ConsoleSettingValue::GetI64() const + { + if constexpr (Common::CppStdString) { + return Common::ToArithmetic(Get()); + } else if constexpr (Common::CppBool) { + return Get() ? 1 : 0; + } else { + return static_cast(Get()); + } + } + + template + uint64_t ConsoleSettingValue::GetU64() const + { + if constexpr (Common::CppStdString) { + return Common::ToArithmetic(Get()); + } else if constexpr (Common::CppBool) { + return Get() ? 1 : 0; + } else { + return static_cast(Get()); + } + } + + template + bool ConsoleSettingValue::GetBool() const + { + if constexpr (Common::CppStdString) { + return Common::ToArithmetic(Get()); + } else if constexpr (Common::CppBool) { + return Get(); + } else { + return Get() != 0; + } + } + + template + float ConsoleSettingValue::GetFloat() const + { + if constexpr (Common::CppStdString) { + return Common::ToArithmetic(Get()); + } else if constexpr (Common::CppBool) { + return Get() ? 1.0f : 0.0f; + } else { + return static_cast(Get()); + } + } + + template + double ConsoleSettingValue::GetDouble() const + { + if constexpr (Common::CppStdString) { + return Common::ToArithmetic(Get()); + } else if constexpr (Common::CppBool) { + return Get() ? 1.0 : 0.0; + } else { + return static_cast(Get()); + } + } + + template + std::string ConsoleSettingValue::GetString() const + { + if constexpr (Common::CppStdString) { + return Get(); + } else if constexpr (Common::CppBool) { + return Get() ? "true" : "false"; + } else { + return Common::ToString(Get()); + } + } + + template + void ConsoleSettingValue::SetI8(int8_t inValue) + { + if constexpr (Common::CppStdString) { + Set(Common::ToString(inValue)); + } else if constexpr (Common::CppBool) { + Set(inValue != 0); + } else { + Set(static_cast(inValue)); + } + } + + template + void ConsoleSettingValue::SetU8(uint8_t inValue) + { + if constexpr (Common::CppStdString) { + Set(Common::ToString(inValue)); + } else if constexpr (Common::CppBool) { + Set(inValue != 0); + } else { + Set(static_cast(inValue)); + } + } + + template + void ConsoleSettingValue::SetI16(int16_t inValue) + { + if constexpr (Common::CppStdString) { + Set(Common::ToString(inValue)); + } else if constexpr (Common::CppBool) { + Set(inValue != 0); + } else { + Set(static_cast(inValue)); + } + } + + template + void ConsoleSettingValue::SetU16(uint16_t inValue) + { + if constexpr (Common::CppStdString) { + Set(Common::ToString(inValue)); + } else if constexpr (Common::CppBool) { + Set(inValue != 0); + } else { + Set(static_cast(inValue)); + } + } + + template + void ConsoleSettingValue::SetI32(int32_t inValue) + { + if constexpr (Common::CppStdString) { + Set(Common::ToString(inValue)); + } else if constexpr (Common::CppBool) { + Set(inValue != 0); + } else { + Set(static_cast(inValue)); + } + } + + template + void ConsoleSettingValue::SetU32(uint32_t inValue) + { + if constexpr (Common::CppStdString) { + Set(Common::ToString(inValue)); + } else if constexpr (Common::CppBool) { + Set(inValue != 0); + } else { + Set(static_cast(inValue)); + } + } + + template + void ConsoleSettingValue::SetI64(int64_t inValue) + { + if constexpr (Common::CppStdString) { + Set(Common::ToString(inValue)); + } else if constexpr (Common::CppBool) { + Set(inValue != 0); + } else { + Set(static_cast(inValue)); + } + } + + template + void ConsoleSettingValue::SetU64(uint64_t inValue) + { + if constexpr (Common::CppStdString) { + Set(Common::ToString(inValue)); + } else if constexpr (Common::CppBool) { + Set(inValue != 0); + } else { + Set(static_cast(inValue)); + } + } + + template + void ConsoleSettingValue::SetBool(bool inValue) + { + if constexpr (Common::CppStdString) { + Set(Common::ToString(inValue)); + } else if constexpr (Common::CppBool) { + Set(inValue); + } else { + Set(inValue ? 1 : 0); + } + } + + template + void ConsoleSettingValue::SetFloat(float inValue) + { + if constexpr (Common::CppStdString) { + Set(Common::ToString(inValue)); + } else if constexpr (Common::CppBool) { + Set(!Common::CompareNumber(inValue, 0.0f)); + } else { + Set(static_cast(inValue)); + } + } + + template + void ConsoleSettingValue::SetDouble(double inValue) + { + if constexpr (Common::CppStdString) { + Set(Common::ToString(inValue)); + } else if constexpr (Common::CppBool) { + Set(!Common::CompareNumber(inValue, 0.0)); + } else { + Set(static_cast(inValue)); + } + } + + template + void ConsoleSettingValue::SetString(const std::string& inValue) + { + if constexpr (Common::CppStdString) { + Set(inValue); + } else { + Set(Common::ToArithmetic(inValue)); + } + } + + template + ConsoleSettingValue* Console::FindSettingValue(const std::string& inName) const + { + return dynamic_cast*>(FindSettingValue(inName)); + } + + template + ConsoleSettingValue& Console::GetSettingValue(const std::string& inName) const + { + auto* result = FindSettingValue(inName); + Assert(result != nullptr); + return *result; + } +} diff --git a/Engine/Source/Core/Include/Core/Log.h b/Engine/Source/Core/Include/Core/Log.h new file mode 100644 index 000000000..c70a54e45 --- /dev/null +++ b/Engine/Source/Core/Include/Core/Log.h @@ -0,0 +1,88 @@ +// +// Created by johnk on 2025/1/13. +// + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +#define LogVerbose(tag, ...) Core::Logger::Get().Log(#tag, "Verbose", std::format(__VA_ARGS__)) +#define LogDebug(tag, ...) Core::Logger::Get().Log(#tag, "Debug", std::format(__VA_ARGS__)) +#define LogHint(tag, ...) Core::Logger::Get().Log(#tag, "Hint", std::format(__VA_ARGS__)) +#define LogInfo(tag, ...) Core::Logger::Get().Log(#tag, "Info", std::format(__VA_ARGS__)) +#define LogWarning(tag, ...) Core::Logger::Get().Log(#tag, "Warning", std::format(__VA_ARGS__)) +#define LogError(tag, ...) Core::Logger::Get().Log(#tag, "Error", std::format(__VA_ARGS__)) + +namespace Core { + class LogStream { + public: + virtual ~LogStream() = default; + virtual void Write(const std::string& inString) = 0; + virtual void Flush() = 0; + }; + + class CORE_API COutLogStream final : public LogStream { + public: + COutLogStream(); + ~COutLogStream() override; + + NonCopyable(COutLogStream); + NonMovable(COutLogStream); + + void Write(const std::string& inString) override; + void Flush() override; + }; + + class CORE_API FileLogStream final : public LogStream { + public: + explicit FileLogStream(const std::string& inFilePath); + ~FileLogStream() override; + + NonCopyable(FileLogStream) + NonMovable(FileLogStream) + + void Write(const std::string& inString) override; + void Flush() override; + + private: + std::ofstream file; + }; + + enum class LogLevel : uint8_t { + verbose, + debug, + hint, + info, + warning, + error, + max + }; + + class CORE_API Logger { + public: + static Logger& Get(); + + ~Logger(); + NonCopyable(Logger) + NonMovable(Logger) + + void Log(const std::string& inTag, const std::string& inLevel, const std::string& inContent); + void Attach(Common::UniqueRef&& inStream); + void Flush(); + + private: + Logger(); + + void LogInternal(const std::string& inString); + + float lastFlushTimeSec; + std::vector> streams; + }; +} diff --git a/Engine/Source/Core/Include/Core/Paths.h b/Engine/Source/Core/Include/Core/Paths.h index 1346302dd..850ec487b 100644 --- a/Engine/Source/Core/Include/Core/Paths.h +++ b/Engine/Source/Core/Include/Core/Paths.h @@ -4,38 +4,50 @@ #pragma once -#include #include #include +#include namespace Core { class CORE_API Paths { public: Paths() = delete; - static std::filesystem::path WorkingDir(); - static std::filesystem::path EngineRoot(); - static std::filesystem::path EngineRes(); - static std::filesystem::path EngineShader(); - static std::filesystem::path EngineAsset(); - static std::filesystem::path EngineBin(); - static std::filesystem::path EnginePlugin(); - static std::filesystem::path EnginePluginAsset(const std::string& pluginName); - static std::filesystem::path ProjectFile(); - static void SetCurrentProjectFile(std::filesystem::path inFile); - static std::filesystem::path ProjectRoot(); - static std::filesystem::path ProjectAsset(); - static std::filesystem::path ProjectBin(); - static std::filesystem::path ProjectPlugin(); - static std::filesystem::path ProjectPluginAsset(const std::string& pluginName); + static void SetExecutableDir(const Common::Path& inPath); + static void SetCurrentProjectFile(const Common::Path& inFile); + + static bool HasSetProjectFile(); + static bool HasSetExecutableDir(); + static Common::Path WorkingDir(); + static Common::Path ExecutablePath(); + static Common::Path EngineRootDir(); + static Common::Path EngineResDir(); + static Common::Path EngineShaderDir(); + static Common::Path EngineAssetDir(); + static Common::Path EngineBinDir(); + static Common::Path EngineCacheDir(); + static Common::Path EngineLogDir(); + static Common::Path EnginePluginDir(); + static Common::Path EnginePluginAssetDir(const std::string& pluginName); + static Common::Path ProjectFile(); + static Common::Path ProjectRootDir(); + static Common::Path ProjectAssetDir(); + static Common::Path ProjectBinDir(); + static Common::Path ProjectCacheDir(); + static Common::Path ProjectLogDir(); + static Common::Path ProjectPluginDir(); + static Common::Path ProjectPluginAssetDir(const std::string& pluginName); + static Common::Path EngineCMakeSourceDir(); + static Common::Path EngineCMakeBinaryDir(); #if BUILD_TEST - static std::filesystem::path EngineTest(); + static Common::Path EngineTest(); #endif private: - static std::filesystem::path workdingDir; - static std::filesystem::path currentProjectFile; + static Common::Path executablePath; + static Common::Path workingDir; + static Common::Path currentProjectFile; }; } diff --git a/Engine/Source/Core/Include/Core/Thread.h b/Engine/Source/Core/Include/Core/Thread.h new file mode 100644 index 000000000..b4cf64f7c --- /dev/null +++ b/Engine/Source/Core/Include/Core/Thread.h @@ -0,0 +1,42 @@ +// +// Created by johnk on 2025/1/17. +// + +#pragma once + +#include + +#include + +namespace Core { + enum class ThreadTag : uint8_t { + unknown, + game, + render, + gameWorker, + renderWorker, + max + }; + + class CORE_API ThreadContext { + public: + static void SetTag(ThreadTag inTag); + static ThreadTag GetTag(); + static bool IsUnknownThread(); + static bool IsGameThread(); + static bool IsRenderThread(); + static bool IsGameWorkerThread(); + static bool IsRenderWorkerThread(); + static bool IsGameOrWorkerThread(); + static bool IsRenderOrWorkerThread(); + }; + + class CORE_API ScopedThreadTag { + public: + explicit ScopedThreadTag(ThreadTag inTag); + ~ScopedThreadTag(); + + private: + ThreadTag tagToRestore; + }; +} diff --git a/Engine/Source/Core/Include/Core/Uri.h b/Engine/Source/Core/Include/Core/Uri.h index 65685920e..b5255b1a8 100644 --- a/Engine/Source/Core/Include/Core/Uri.h +++ b/Engine/Source/Core/Include/Core/Uri.h @@ -41,7 +41,7 @@ namespace Core { bool IsProjectAsset() const; bool IsEnginePluginAsset() const; bool IsProjectPluginAsset() const; - std::filesystem::path AbsoluteFilePath() const; + Common::Path AbsoluteFilePath() const; #if BUILD_TEST bool IsEngineTestAsset() const; diff --git a/Engine/Source/Core/Src/Cmdline.cpp b/Engine/Source/Core/Src/Cmdline.cpp index 5ef9713ed..dc3717cd7 100644 --- a/Engine/Source/Core/Src/Cmdline.cpp +++ b/Engine/Source/Core/Src/Cmdline.cpp @@ -5,6 +5,7 @@ #include #include +#include #include namespace Core { @@ -28,6 +29,8 @@ namespace Core { Assert(!parsed); parsed = true; + Core::Paths::SetExecutableDir(argv[0]); + clipp::group cli; for (const auto& [name, arg] : args) { cli.push_back(arg->CreateClippParameter()); diff --git a/Engine/Source/Core/Src/Console.cpp b/Engine/Source/Core/Src/Console.cpp new file mode 100644 index 000000000..94a402bfc --- /dev/null +++ b/Engine/Source/Core/Src/Console.cpp @@ -0,0 +1,72 @@ +// +// Created by johnk on 2025/1/16. +// + +#include + +#include + +namespace Core { + ConsoleSetting::ConsoleSetting(const std::string& inName, const std::string& inDescription, const CSFlags& inFlags) + : name(inName) + , description(inDescription) + , flags(inFlags) + { + Console::Get().RegisterConsoleSetting(*this); + } + + ConsoleSetting::~ConsoleSetting() = default; + + const std::string& ConsoleSetting::Name() const + { + return name; + } + + const std::string& ConsoleSetting::Description() const + { + return description; + } + + CSFlags ConsoleSetting::Flags() const + { + return flags; + } + + Console& Console::Get() + { + static Console instance; + return instance; + } + + Console::~Console() = default; + + bool Console::HasSetting(const std::string& inName) const + { + return settings.contains(inName); + } + + ConsoleSetting* Console::FindSetting(const std::string& inName) const + { + const auto iter = settings.find(inName); + return iter == settings.end() ? nullptr : iter->second; + } + + ConsoleSetting& Console::GetSetting(const std::string& inName) const + { + return *settings.at(inName); + } + + void Console::PerformRenderThreadCopy() const + { + for (const auto& setting : settings | std::views::values) { + setting->PerformRenderThreadCopy(); + } + } + + Console::Console() = default; + + void Console::RegisterConsoleSetting(ConsoleSetting& inSetting) + { + settings.emplace(inSetting.Name(), &inSetting); + } +} diff --git a/Engine/Source/Core/Src/Log.cpp b/Engine/Source/Core/Src/Log.cpp new file mode 100644 index 000000000..2ea179a22 --- /dev/null +++ b/Engine/Source/Core/Src/Log.cpp @@ -0,0 +1,102 @@ +// +// Created by johnk on 2025/1/13. +// + +#include +#include + +namespace Core { + COutLogStream::COutLogStream() = default; + + COutLogStream::~COutLogStream() + { + Flush(); + } + + void COutLogStream::Write(const std::string& inString) + { + std::cout << inString << Common::newline; + } + + void COutLogStream::Flush() + { + std::cout << std::flush; + } + + FileLogStream::FileLogStream(const std::string& inFilePath) + { + if (const auto parentPath = Common::Path(inFilePath).Parent(); + !parentPath.Exists()) { + parentPath.MakeDir(); + } + file = std::ofstream(inFilePath); + } + + FileLogStream::~FileLogStream() + { + Flush(); + } + + void FileLogStream::Write(const std::string& inString) + { + file << inString << Common::newline; + } + + void FileLogStream::Flush() + { + file << std::flush; + } + + Logger& Logger::Get() + { + static Logger logger; + return logger; + } + + Logger::~Logger() + { + Flush(); + } + + void Logger::Log(const std::string& inTag, const std::string& inLevel, const std::string& inContent) + { + const auto time = Common::AccurateTime(Common::TimePoint::Now()); + LogInternal(std::format("[{}][{}][{}] {}", time.ToString(), inTag, inLevel, inContent)); + } + + void Logger::Attach(Common::UniqueRef&& inStream) + { + streams.emplace_back(std::move(inStream)); + } + + void Logger::Flush() // NOLINT + { + for (const auto& stream : streams) { + stream->Flush(); + } + } + + Logger::Logger() + : lastFlushTimeSec(Common::TimePoint::Now().ToSeconds()) + { + Attach(new COutLogStream()); + } + + void Logger::LogInternal(const std::string& inString) // NOLINT + { +#if BUILD_CONFIG_DEBUG + const bool needFlush = true; // NOLINT +#else + const auto timeNowSec = Common::TimePoint::Now().ToSeconds(); + const bool needFlush = timeNowSec - lastFlushTimeSec > 5.0f; + lastFlushTimeSec = timeNowSec; +#endif + + for (const auto& stream : streams) { + stream->Write(inString); + if (needFlush) { + stream->Flush(); + } + } + } +} diff --git a/Engine/Source/Core/Src/Module.cpp b/Engine/Source/Core/Src/Module.cpp index 19d706063..8585a55bd 100644 --- a/Engine/Source/Core/Src/Module.cpp +++ b/Engine/Source/Core/Src/Module.cpp @@ -131,32 +131,41 @@ namespace Core { std::optional ModuleManager::SearchModule(const std::string& moduleName) { - const std::vector searchPaths = { - Paths::EngineBin(), - Paths::ProjectBin(), - Paths::EnginePlugin(), - Paths::ProjectPlugin() + std::vector searchPaths = { + Paths::WorkingDir(), }; - const std::filesystem::path workingDir = Paths::WorkingDir(); + + if (Paths::HasSetExecutableDir()) { + searchPaths.emplace_back(Paths::EngineBinDir()); + searchPaths.emplace_back(Paths::EnginePluginDir()); + } + + if (Paths::HasSetProjectFile()) { + searchPaths.emplace_back(Paths::ProjectBinDir()); + searchPaths.emplace_back(Paths::ProjectPluginDir()); + } for (const auto& searchPath : searchPaths) { - try { - for (const std::filesystem::directory_entry& entry : std::filesystem::recursive_directory_iterator(searchPath)) { - if (entry.is_directory()) { - continue; - } - - const auto& path = entry.path(); - auto fileName = path.filename().string(); - - if (auto extension = path.extension().string(); - (extension == ".dll" || extension == ".so" || extension == ".dylib") - && (fileName == std::format("{}{}", moduleName, extension) || fileName == std::format("lib{}{}", moduleName, extension))) - { - return path.string(); - } + std::optional result = std::nullopt; + (void) searchPath.TraverseRecurse([&](const Common::Path& path) -> bool { + if (path.IsDirectory()) { + return true; } - } catch (const std::exception&) {} // NOLINT + + const auto fileName = path.FileName(); + const auto extension = path.Extension(); // NOLINT + + if ((extension == ".dll" || extension == ".so" || extension == ".dylib") + && (fileName == std::format("{}{}", moduleName, extension) || fileName == std::format("lib{}{}", moduleName, extension))) { + result = path.String(); + return false; + } + return true; + }); + + if (result.has_value()) { + return result; + } } return {}; } diff --git a/Engine/Source/Core/Src/Paths.cpp b/Engine/Source/Core/Src/Paths.cpp index dd6fb64c6..5a9bfd301 100644 --- a/Engine/Source/Core/Src/Paths.cpp +++ b/Engine/Source/Core/Src/Paths.cpp @@ -5,92 +5,152 @@ #include namespace Core { - std::filesystem::path Paths::workdingDir = std::filesystem::path(); - std::filesystem::path Paths::currentProjectFile = std::filesystem::path(); + Common::Path Paths::executablePath = Common::Path(); + Common::Path Paths::workingDir = Common::Path(); + Common::Path Paths::currentProjectFile = Common::Path(); - std::filesystem::path Paths::WorkingDir() + void Paths::SetExecutableDir(const Common::Path& inPath) + { + executablePath = inPath; + } + + void Paths::SetCurrentProjectFile(const Common::Path& inFile) + { + currentProjectFile = inFile; + } + + bool Paths::HasSetProjectFile() + { + return !currentProjectFile.Empty(); + } + + bool Paths::HasSetExecutableDir() + { + return !executablePath.Empty(); + } + + Common::Path Paths::WorkingDir() { // working directory is set to engine binaries directory as default - if (workdingDir.empty()) { - workdingDir = std::filesystem::current_path(); + if (workingDir.Empty()) { + workingDir = Common::Path::WorkingDirectory(); } - return workdingDir; + return workingDir; } - std::filesystem::path Paths::EngineRoot() + Common::Path Paths::ExecutablePath() { - return WorkingDir().parent_path(); + Assert(!executablePath.Empty()); + return executablePath; } - std::filesystem::path Paths::EngineRes() + Common::Path Paths::EngineRootDir() { - return EngineRoot() / "Resource"; +#if BUILD_TEST + if (HasSetExecutableDir()) { + return ExecutablePath().Parent().Parent(); + } + return WorkingDir().Parent(); +#else + Assert(HasSetExecutableDir()); + return ExecutableDir().Parent().Parent(); +#endif } - std::filesystem::path Paths::EngineShader() + Common::Path Paths::EngineResDir() { - return EngineRoot() / "Shader"; + return EngineRootDir() / "Resource"; } - std::filesystem::path Paths::EngineAsset() + Common::Path Paths::EngineShaderDir() { - return EngineRoot() / "Asset"; + return EngineRootDir() / "Shader"; } - std::filesystem::path Paths::EngineBin() + Common::Path Paths::EngineAssetDir() { - return EngineRoot() / "Binaries"; + return EngineRootDir() / "Asset"; } - std::filesystem::path Paths::EnginePlugin() + Common::Path Paths::EngineBinDir() { - return EngineRoot() / "Plugin"; + return EngineRootDir() / "Binaries"; } - std::filesystem::path Paths::EnginePluginAsset(const std::string& pluginName) + Common::Path Paths::EngineCacheDir() { - return EnginePlugin() / pluginName / "Asset"; + return EngineRootDir() / "Cache"; } - void Paths::SetCurrentProjectFile(std::filesystem::path inFile) + Common::Path Paths::EngineLogDir() { - currentProjectFile = std::move(inFile); + return EngineCacheDir() / "Log"; } - std::filesystem::path Paths::ProjectFile() + Common::Path Paths::EnginePluginDir() + { + return EngineRootDir() / "Plugin"; + } + + Common::Path Paths::EnginePluginAssetDir(const std::string& pluginName) + { + return EnginePluginDir() / pluginName / "Asset"; + } + + Common::Path Paths::ProjectFile() { return currentProjectFile; } - std::filesystem::path Paths::ProjectRoot() + Common::Path Paths::ProjectRootDir() + { + return currentProjectFile.Parent(); + } + + Common::Path Paths::ProjectAssetDir() + { + return ProjectRootDir() / "Asset"; + } + + Common::Path Paths::ProjectBinDir() + { + return ProjectRootDir() / "Binaries"; + } + + Common::Path Paths::ProjectCacheDir() + { + return ProjectRootDir() / "Cache"; + } + + Common::Path Paths::ProjectLogDir() { - return currentProjectFile.parent_path(); + return ProjectCacheDir() / "Log"; } - std::filesystem::path Paths::ProjectAsset() + Common::Path Paths::ProjectPluginDir() { - return ProjectRoot() / "Asset"; + return ProjectRootDir() / "Plugin"; } - std::filesystem::path Paths::ProjectBin() + Common::Path Paths::ProjectPluginAssetDir(const std::string& pluginName) { - return ProjectRoot() / "Binaries"; + return ProjectPluginDir() / pluginName / "Asset"; } - std::filesystem::path Paths::ProjectPlugin() + Common::Path Paths::EngineCMakeSourceDir() { - return ProjectRoot() / "Plugin"; + return ENGINE_CMAKE_SOURCE_DIRECTORY; } - std::filesystem::path Paths::ProjectPluginAsset(const std::string& pluginName) + Common::Path Paths::EngineCMakeBinaryDir() { - return ProjectPlugin() / pluginName / "Asset"; + return ENGINE_CMAKE_BINARY_DIRECTORY; } #if BUILD_TEST - std::filesystem::path Paths::EngineTest() + Common::Path Paths::EngineTest() { - return EngineRoot() / "Test"; + return EngineRootDir() / "Test"; } #endif } diff --git a/Engine/Source/Core/Src/Thread.cpp b/Engine/Source/Core/Src/Thread.cpp new file mode 100644 index 000000000..05fde0d8b --- /dev/null +++ b/Engine/Source/Core/Src/Thread.cpp @@ -0,0 +1,65 @@ +// +// Created by johnk on 2025/1/17. +// + +#include + +namespace Core { + static thread_local auto currentTag = ThreadTag::unknown; + + void ThreadContext::SetTag(ThreadTag inTag) + { + currentTag = inTag; + } + + ThreadTag ThreadContext::GetTag() + { + return currentTag; + } + + bool ThreadContext::IsUnknownThread() + { + return currentTag == ThreadTag::unknown; + } + + bool ThreadContext::IsGameThread() + { + return currentTag == ThreadTag::game; + } + + bool ThreadContext::IsRenderThread() + { + return currentTag == ThreadTag::render; + } + + bool ThreadContext::IsGameWorkerThread() + { + return currentTag == ThreadTag::gameWorker; + } + + bool ThreadContext::IsRenderWorkerThread() + { + return currentTag == ThreadTag::renderWorker; + } + + bool ThreadContext::IsGameOrWorkerThread() + { + return currentTag == ThreadTag::game || currentTag == ThreadTag::gameWorker; + } + + bool ThreadContext::IsRenderOrWorkerThread() + { + return currentTag == ThreadTag::render || currentTag == ThreadTag::renderWorker; + } + + ScopedThreadTag::ScopedThreadTag(ThreadTag inTag) + { + tagToRestore = ThreadContext::GetTag(); + ThreadContext::SetTag(inTag); + } + + ScopedThreadTag::~ScopedThreadTag() + { + ThreadContext::SetTag(tagToRestore); + } +} diff --git a/Engine/Source/Core/Src/Uri.cpp b/Engine/Source/Core/Src/Uri.cpp index bb10cad23..ab8a55c28 100644 --- a/Engine/Source/Core/Src/Uri.cpp +++ b/Engine/Source/Core/Src/Uri.cpp @@ -105,9 +105,9 @@ namespace Core { return Common::StringUtils::RegexMatch(content, R"(Project/Plugin/.*)"); } - std::filesystem::path AssetUriParser::AbsoluteFilePath() const + Common::Path AssetUriParser::AbsoluteFilePath() const { - std::filesystem::path path; + Common::Path path; #if BUILD_TEST if (IsEngineTestAsset()) { path = Paths::EngineTest() / Common::StringUtils::AfterFirst(content, "Engine/Test/"); @@ -116,18 +116,19 @@ namespace Core { if (IsEnginePluginAsset()) { #endif const std::string pathWithPluginName = Common::StringUtils::AfterFirst(content, "Engine/Plugin/"); - path = Paths::EnginePluginAsset(Common::StringUtils::BeforeFirst(pathWithPluginName, "/")) / Common::StringUtils::AfterFirst(pathWithPluginName, "/"); + path = Paths::EnginePluginAssetDir(Common::StringUtils::BeforeFirst(pathWithPluginName, "/")) / Common::StringUtils::AfterFirst(pathWithPluginName, "/"); } else if (IsProjectPluginAsset()) { const std::string pathWithPluginName = Common::StringUtils::AfterFirst(content, "Project/Plugin/"); - path = Paths::ProjectPluginAsset(Common::StringUtils::BeforeFirst(pathWithPluginName, "/")) / Common::StringUtils::AfterFirst(pathWithPluginName, "/"); + path = Paths::ProjectPluginAssetDir(Common::StringUtils::BeforeFirst(pathWithPluginName, "/")) / Common::StringUtils::AfterFirst(pathWithPluginName, "/"); } else if (IsEngineAsset()) { - path = Paths::EngineAsset() / Common::StringUtils::AfterFirst(content, "Engine/"); + path = Paths::EngineAssetDir() / Common::StringUtils::AfterFirst(content, "Engine/"); } else if (IsProjectAsset()) { - path = Paths::ProjectAsset() / Common::StringUtils::AfterFirst(content, "Project/"); + path = Paths::ProjectAssetDir() / Common::StringUtils::AfterFirst(content, "Project/"); } else { AssertWithReason(false, "bad asset uri"); } - return std::filesystem::absolute(path.concat(".expa")); + path = path + ".expa"; + return path.Absolute(); } #if BUILD_TEST diff --git a/Engine/Source/Mirror/Include/Mirror/Meta.h b/Engine/Source/Mirror/Include/Mirror/Meta.h index ebfbad28a..932355632 100644 --- a/Engine/Source/Mirror/Include/Mirror/Meta.h +++ b/Engine/Source/Mirror/Include/Mirror/Meta.h @@ -18,20 +18,23 @@ #define EMeta(...) #endif +namespace Mirror::Internal { + class ScopedReleaser; +} namespace Mirror { class Class; } #define EClassBody(className) \ private: \ - static int _mirrorRegistry; \ + static Mirror::Internal::ScopedReleaser _mirrorRegistry; \ public: \ static const Mirror::Class& GetStaticClass(); \ const Mirror::Class& GetClass(); \ #define EPolyClassBody(className) \ private: \ - static int _mirrorRegistry; \ + static Mirror::Internal::ScopedReleaser _mirrorRegistry; \ public: \ static const Mirror::Class& GetStaticClass(); \ virtual const Mirror::Class& GetClass(); \ diff --git a/Engine/Source/Mirror/Include/Mirror/Mirror.h b/Engine/Source/Mirror/Include/Mirror/Mirror.h index d8829f6cb..a7451b19e 100644 --- a/Engine/Source/Mirror/Include/Mirror/Mirror.h +++ b/Engine/Source/Mirror/Include/Mirror/Mirror.h @@ -227,13 +227,16 @@ namespace Mirror { Any& CopyAssign(Any& inOther); Any& CopyAssign(const Any& inOther); + Any& CopyAssign(Any&& inOther); Any& MoveAssign(Any& inOther) noexcept; Any& MoveAssign(const Any& inOther) noexcept; + Any& MoveAssign(Any&& inOther) noexcept; const Any& CopyAssign(Any& inOther) const; const Any& CopyAssign(const Any& inOther) const; + const Any& CopyAssign(Any&& inOther) const; const Any& MoveAssign(Any& inOther) const noexcept; const Any& MoveAssign(const Any& inOther) const noexcept; - // TODO rvalue version CopyAssign/MoveAssign + const Any& MoveAssign(Any&& inOther) const noexcept; template bool Convertible(); template bool Convertible() const; @@ -337,6 +340,8 @@ namespace Mirror { void PerformMoveConstruct(Any&& inOther) noexcept; void PerformCopyAssign(const Any& inOther) const; void PerformMoveAssign(const Any& inOther) const noexcept; + void PerformCopyAssign(Any&& inOther) const; + void PerformMoveAssign(Any&& inOther) const noexcept; uint32_t ElementNum() const; uint32_t arrayLength; @@ -374,8 +379,6 @@ namespace Mirror { const TypeInfo* AddPointerType() const; const TypeInfo* RemovePointerType() const; - // TODO more proxy func - private: template decltype(auto) Delegate(F&& inFunc) const; @@ -744,8 +747,8 @@ namespace Mirror { GlobalScope(); - std::unordered_map variables; - std::unordered_map functions; + Common::StableUnorderedMap variables; + Common::StableUnorderedMap functions; }; class MIRROR_API Class final : public ReflNode { @@ -835,7 +838,6 @@ namespace Mirror { std::optional defaultConstructorParams; std::optional moveConstructorParams; std::optional copyConstructorParams; - // TODO assign operator ? }; explicit Class(ConstructParams&& params); @@ -1815,9 +1817,9 @@ namespace Common { // NOLINT } metaEnumValue->Set(value); } else { - std::underlying_type_t unlderlyingValue; - deserialized += Serializer>::Deserialize(stream, unlderlyingValue); - value = static_cast(unlderlyingValue); + std::underlying_type_t underlyingValue; + deserialized += Serializer>::Deserialize(stream, underlyingValue); + value = static_cast(underlyingValue); } return deserialized; } @@ -2112,7 +2114,9 @@ namespace Common { // NOLINT } if (baseClass != nullptr) { - JsonSerializeDyn(outJsonValue, inAllocator, *baseClass, inObj); + rapidjson::Value baseContentValue; + JsonSerializeDyn(baseContentValue, inAllocator, *baseClass, inObj); + outJsonValue.AddMember("_base", baseContentValue, inAllocator); } for (const auto& memberVariable : memberVariables | std::views::values) { @@ -2145,8 +2149,8 @@ namespace Common { // NOLINT return; } - if (baseClass != nullptr) { - JsonDeserializeDyn(inJsonValue, *baseClass, outObj); + if (baseClass != nullptr && inJsonValue.HasMember("_base")) { + JsonDeserializeDyn(inJsonValue["_base"], *baseClass, outObj); } for (const auto& memberVariable : clazz.GetMemberVariables() | std::views::values) { @@ -2514,9 +2518,14 @@ namespace Common { // NOLINT static std::string ToStringDyn(const Mirror::Class& clazz, const Mirror::Argument& argument) { const auto& memberVariables = clazz.GetMemberVariables(); + const Mirror::Class* baseClass = clazz.GetBaseClass(); std::stringstream stream; stream << "{ "; + if (baseClass != nullptr) { + stream << std::format("_base: {}", ToStringDyn(*baseClass, argument)); + } + auto count = 0; for (const auto& [id, var] : memberVariables) { stream << std::format("{}: {}", id.name, var.GetDyn(argument).ToString()); diff --git a/Engine/Source/Mirror/Include/Mirror/Registry.h b/Engine/Source/Mirror/Include/Mirror/Registry.h index 2a77f57ec..cd29f8573 100644 --- a/Engine/Source/Mirror/Include/Mirror/Registry.h +++ b/Engine/Source/Mirror/Include/Mirror/Registry.h @@ -4,9 +4,8 @@ #pragma once -#include - #include +#include #include #include @@ -22,6 +21,16 @@ namespace Mirror::Internal { template decltype(auto) InvokeConstructorStack(const ArgumentList& args, std::index_sequence); template decltype(auto) InvokeConstructorNew(const ArgumentList& args, std::index_sequence); template decltype(auto) InvokeConstructorInplace(void* ptr, const ArgumentList& args, std::index_sequence); + + class MIRROR_API ScopedReleaser { + public: + using ReleaseFunc = std::function; + explicit ScopedReleaser(ReleaseFunc inReleaseFunc = {}); + ~ScopedReleaser(); + + private: + ReleaseFunc releaseFunc; + }; } namespace Mirror { @@ -50,8 +59,6 @@ namespace Mirror { template ClassRegistry& StaticVariable(const Id& inId); template ClassRegistry& StaticFunction(const Id& inId); template ClassRegistry& MemberVariable(const Id& inId); - // TODO support overload - // TODO virtual function support template ClassRegistry& MemberFunction(const Id& inId); private: @@ -68,7 +75,8 @@ namespace Mirror { template GlobalRegistry& Variable(const Id& inId); template GlobalRegistry& Function(const Id& inId); - // TODO overload support + void UnloadVariable(const Id& inId); + void UnloadFunction(const Id& inId); private: friend class Registry; @@ -103,14 +111,10 @@ namespace Mirror { GlobalRegistry Global(); - template < - Common::CppClass C, CppBaseClassOrVoid B = void, - FieldAccess DefaultCtorAccess = FieldAccess::faPublic, - FieldAccess DestructorAccess = FieldAccess::faPublic> - ClassRegistry Class(const Id& inId); - - template EnumRegistry - Enum(const Id& inId); + template B = void, FieldAccess DefaultCtorAccess = FieldAccess::faPublic, FieldAccess DestructorAccess = FieldAccess::faPublic> ClassRegistry Class(const Id& inId); + template EnumRegistry Enum(const Id& inId); + void UnloadClass(const Id& inId); + void UnloadEnum(const Id& inId); private: friend class GlobalScope; @@ -123,8 +127,8 @@ namespace Mirror { Mirror::Enum& EmplaceEnum(const Id& inId, Enum::ConstructParams&& inParams); GlobalScope globalScope; - std::unordered_map classes; - std::unordered_map enums; + Common::StableUnorderedMap classes; + Common::StableUnorderedMap enums; }; } @@ -252,8 +256,13 @@ namespace Mirror { params.argRemoveRefTypeInfos = { GetTypeInfo>()... }; params.argRemovePointerTypeInfos = { GetTypeInfo>()... }; params.stackConstructor = [](const ArgumentList& args) -> Any { - Assert(argsTupleSize == args.size()); - return ForwardAsAny(Internal::InvokeConstructorStack(args, std::make_index_sequence {})); + if constexpr (std::is_copy_constructible_v || std::is_move_constructible_v) { + Assert(argsTupleSize == args.size()); + return ForwardAsAny(Internal::InvokeConstructorStack(args, std::make_index_sequence {})); + } else { + QuickFail(); + return {}; + } }; params.heapConstructor = [](const ArgumentList& args) -> Any { Assert(argsTupleSize == args.size()); @@ -261,7 +270,7 @@ namespace Mirror { }; params.inplaceConstructor = [](void* ptr, const ArgumentList& args) -> Any { Assert(argsTupleSize == args.size()); - return ForwardAsAny(Internal::InvokeConstructorInplace(ptr, args, std::make_index_sequence {})); + return ForwardAsAny(std::ref(Internal::InvokeConstructorInplace(ptr, args, std::make_index_sequence {}))); }; return MetaDataRegistry::SetContext(&clazz.EmplaceConstructor(inId, std::move(params))); @@ -398,8 +407,7 @@ namespace Mirror { { using ValueType = typename Internal::VariableTraits::ValueType; - const auto iter = globalScope.variables.find(inId); - Assert(iter == globalScope.variables.end()); + Assert(!globalScope.variables.Contains(inId)); Variable::ConstructParams params; params.id = inId; @@ -425,12 +433,10 @@ namespace Mirror { { using ArgsTupleType = typename Internal::FunctionTraits::ArgsTupleType; using RetType = typename Internal::FunctionTraits::RetType; - - const auto iter = globalScope.functions.find(inId); - Assert(iter == globalScope.functions.end()); - constexpr size_t argsTupleSize = std::tuple_size_v; + Assert(!globalScope.functions.Contains(inId)); + Function::ConstructParams params; params.id = inId; params.owner = Id::null; @@ -492,7 +498,7 @@ namespace Mirror { { const auto typeId = GetTypeInfo()->id; Assert(!Class::typeToIdMap.contains(typeId)); - Assert(!classes.contains(inId)); + Assert(!classes.Contains(inId)); Class::ConstructParams params; params.id = inId; @@ -535,8 +541,13 @@ namespace Mirror { ctorParams.argRemoveRefTypeInfos = {}; ctorParams.argRemovePointerTypeInfos = {}; ctorParams.stackConstructor = [](const ArgumentList& args) -> Any { - Assert(args.empty()); - return { C() }; + if constexpr (std::is_copy_constructible_v || std::is_move_constructible_v) { + Assert(args.empty()); + return { C() }; + } else { + QuickFail(); + return {}; + } }; ctorParams.heapConstructor = [](const ArgumentList& args) -> Any { Assert(args.empty()); @@ -559,8 +570,13 @@ namespace Mirror { copyCtorParams.argRemoveRefTypeInfos = { GetTypeInfo>() }; copyCtorParams.argRemovePointerTypeInfos = { GetTypeInfo>() }; copyCtorParams.stackConstructor = [](const ArgumentList& args) -> Any { - Assert(args.size() == 1); - return { C(args[0].As()) }; + if constexpr (std::is_copy_constructible_v || std::is_move_constructible_v) { + Assert(args.size() == 1); + return { C(args[0].As()) }; + } else { + QuickFail(); + return {}; + } }; copyCtorParams.heapConstructor = [](const ArgumentList& args) -> Any { Assert(args.size() == 1); @@ -583,8 +599,13 @@ namespace Mirror { moveCtorParams.argRemoveRefTypeInfos = { GetTypeInfo>() }; moveCtorParams.argRemovePointerTypeInfos = { GetTypeInfo>() }; moveCtorParams.stackConstructor = [](const ArgumentList& args) -> Any { - Assert(args.size() == 1); - return { C(args[0].As()) }; + if constexpr (std::is_copy_constructible_v || std::is_move_constructible_v) { + Assert(args.size() == 1); + return { C(args[0].As()) }; + } else { + QuickFail(); + return {}; + } }; moveCtorParams.heapConstructor = [](const ArgumentList& args) -> Any { Assert(args.size() == 1); @@ -607,7 +628,7 @@ namespace Mirror { { const auto typeId = GetTypeInfo()->id; Assert(!Enum::typeToIdMap.contains(typeId)); - Assert(!enums.contains(inId)); + Assert(!enums.Contains(inId)); Enum::ConstructParams params; params.id = inId; diff --git a/Engine/Source/Mirror/Src/Mirror.cpp b/Engine/Source/Mirror/Src/Mirror.cpp index 45921617d..1debbab1b 100644 --- a/Engine/Source/Mirror/Src/Mirror.cpp +++ b/Engine/Source/Mirror/Src/Mirror.cpp @@ -144,6 +144,13 @@ namespace Mirror { return *this; } + Any& Any::CopyAssign(Any&& inOther) + { + Assert(!IsConstRef()); + PerformCopyAssign(std::move(inOther)); + return *this; + } + Any& Any::MoveAssign(Any& inOther) noexcept { Assert(!IsConstRef()); @@ -160,6 +167,14 @@ namespace Mirror { return *this; } + Any& Any::MoveAssign(Any&& inOther) noexcept + { + Assert(!IsConstRef()); + Assert(!inOther.IsRef() || inOther.IsNonConstRef()); + PerformMoveAssign(std::move(inOther)); + return *this; + } + const Any& Any::CopyAssign(Any& inOther) const { Assert(IsNonConstRef()); @@ -174,6 +189,13 @@ namespace Mirror { return *this; } + const Any& Any::CopyAssign(Any&& inOther) const + { + Assert(IsNonConstRef()); + PerformCopyAssign(std::move(inOther)); + return *this; + } + const Any& Any::MoveAssign(Any& inOther) const noexcept { Assert(IsNonConstRef()); @@ -190,6 +212,14 @@ namespace Mirror { return *this; } + const Any& Any::MoveAssign(Any&& inOther) const noexcept + { + Assert(IsNonConstRef()); + Assert(!inOther.IsRef() || inOther.IsNonConstRef()); + PerformMoveAssign(std::move(inOther)); + return *this; + } + void Any::PerformCopyConstruct(const Any& inOther) { arrayLength = inOther.arrayLength; @@ -288,6 +318,24 @@ namespace Mirror { } } + void Any::PerformCopyAssign(Any&& inOther) const + { + Assert(!Empty() && !inOther.Empty() && arrayLength == inOther.arrayLength && rtti == inOther.rtti); + + for (auto i = 0; i < ElementNum(); i++) { + rtti->copyAssign(Data(i), inOther.Data(i)); + } + } + + void Any::PerformMoveAssign(Any&& inOther) const noexcept + { + Assert(!Empty() && !inOther.Empty() && arrayLength == inOther.arrayLength && rtti == inOther.rtti); + + for (auto i = 0; i < ElementNum(); i++) { + rtti->copyAssign(Data(i), inOther.Data(i)); + } + } + uint32_t Any::ElementNum() const { return std::max(1u, arrayLength); @@ -1260,16 +1308,14 @@ namespace Mirror { Variable& GlobalScope::EmplaceVariable(const Id& inId, Variable::ConstructParams&& inParams) { - Assert(!variables.contains(inId)); - variables.emplace(inId, Variable(std::move(inParams))); - return variables.at(inId); + variables.Emplace(inId, Variable(std::move(inParams))); + return variables.At(inId); } Function& GlobalScope::EmplaceFunction(const Id& inId, Function::ConstructParams&& inParams) { - Assert(!functions.contains(inId)); - functions.emplace(inId, Function(std::move(inParams))); - return functions.at(inId); + functions.Emplace(inId, Function(std::move(inParams))); + return functions.At(inId); } const GlobalScope& GlobalScope::Get() @@ -1279,52 +1325,46 @@ namespace Mirror { void GlobalScope::ForEachVariable(const VariableTraverser& func) const { - for (const auto& [id, variable] : variables) { + variables.Each([&](const Id&, const Variable& variable) -> void { func(variable); - } + }); } void GlobalScope::ForEachFunction(const FunctionTraverser& func) const { - for (const auto& [id, function] : functions) { + functions.Each([&](const Id&, const Function& function) -> void { func(function); - } + }); } bool GlobalScope::HasVariable(const Id& inId) const { - return variables.contains(inId); + return variables.Contains(inId); } const Variable* GlobalScope::FindVariable(const Id& inId) const { - const auto iter = variables.find(inId); - return iter == variables.end() ? nullptr : &iter->second; + return variables.Contains(inId) ? &variables.At(inId) : nullptr; } const Variable& GlobalScope::GetVariable(const Id& inId) const { - const auto iter = variables.find(inId); - Assert(iter != variables.end()); - return iter->second; + return variables.At(inId); } bool GlobalScope::HasFunction(const Id& inId) const { - return functions.contains(inId); + return functions.Contains(inId); } const Function* GlobalScope::FindFunction(const Id& inId) const { - const auto iter = functions.find(inId); - return iter == functions.end() ? nullptr : &iter->second; + return functions.Contains(inId) ? &functions.At(inId) : nullptr; } const Function& GlobalScope::GetFunction(const Id& inId) const { - const auto iter = functions.find(inId); - Assert(iter != functions.end()); - return iter->second; + return functions.At(inId); } std::unordered_map Class::typeToIdMap = {}; @@ -1356,22 +1396,19 @@ namespace Mirror { bool Class::Has(const Id& inId) { const auto& classes = Registry::Get().classes; - return classes.contains(inId); + return classes.Contains(inId); } const Class* Class::Find(const Id& inId) { const auto& classes = Registry::Get().classes; - const auto iter = classes.find(inId); - return iter == classes.end() ? nullptr : &iter->second; + return classes.Contains(inId) ? &classes.At(inId) : nullptr; } const Class& Class::Get(const Id& inId) { const auto& classes = Registry::Get().classes; - const auto iter = classes.find(inId); - Assert(iter != classes.end()); - return iter->second; + return classes.At(inId); } bool Class::Has(const TypeInfo* typeInfo) @@ -1421,10 +1458,10 @@ namespace Mirror { { const auto& classes = Registry::Get().classes; std::vector result; - result.reserve(classes.size()); - for (const auto& [id, clazz] : classes) { + result.reserve(classes.Size()); + classes.Each([&](const Id& id, const Class& clazz) -> void { result.emplace_back(&clazz); - } + }); return result; } @@ -1432,12 +1469,12 @@ namespace Mirror { { const auto& classes = Registry::Get().classes; std::vector result; - result.reserve(classes.size()); - for (const auto& [id, clazz] : classes) { + result.reserve(classes.Size()); + classes.Each([&](const Id& id, const Class& clazz) -> void { if (clazz.HasMeta("category") && clazz.GetMeta("category") == category) { result.emplace_back(&clazz); } - } + }); return result; } @@ -1816,16 +1853,13 @@ namespace Mirror { const Enum* Enum::Find(const Id& inId) { const auto& enums = Registry::Get().enums; - const auto iter = enums.find(inId); - return iter == enums.end() ? nullptr : &iter->second; + return enums.Contains(inId) ? &enums.At(inId) : nullptr; } const Enum& Enum::Get(const Id& inId) { const auto& enums = Registry::Get().enums; - const auto iter = enums.find(inId); - Assert(iter != enums.end()); - return iter->second; + return enums.At(inId); } std::unordered_map Enum::typeToIdMap = {}; diff --git a/Engine/Source/Mirror/Src/Registry.cpp b/Engine/Source/Mirror/Src/Registry.cpp index c3aae8dfe..cf17586f4 100644 --- a/Engine/Source/Mirror/Src/Registry.cpp +++ b/Engine/Source/Mirror/Src/Registry.cpp @@ -2,8 +2,24 @@ // Created by johnk on 2023/10/31. // +#include + #include +namespace Mirror::Internal { + ScopedReleaser::ScopedReleaser(ReleaseFunc inReleaseFunc) + : releaseFunc(std::move(inReleaseFunc)) + { + } + + ScopedReleaser::~ScopedReleaser() + { + if (releaseFunc) { + releaseFunc(); + } + } +} // namespace Mirror::Internal + namespace Mirror { GlobalRegistry::GlobalRegistry(GlobalScope& inGlobalScope) : MetaDataRegistry(&inGlobalScope) @@ -13,6 +29,16 @@ namespace Mirror { GlobalRegistry::~GlobalRegistry() = default; + void GlobalRegistry::UnloadVariable(const Id& inId) // NOLINT + { + globalScope.variables.Erase(inId); + } + + void GlobalRegistry::UnloadFunction(const Id& inId) // NOLINT + { + globalScope.functions.Erase(inId); + } + Registry& Registry::Get() { static Registry instance; @@ -30,15 +56,23 @@ namespace Mirror { Class& Registry::EmplaceClass(const Id& inId, Class::ConstructParams&& inParams) { - Assert(!classes.contains(inId)); - classes.emplace(inId, Mirror::Class(std::move(inParams))); - return classes.at(inId); + classes.Emplace(inId, Mirror::Class(std::move(inParams))); + return classes.At(inId); } Enum& Registry::EmplaceEnum(const Id& inId, Enum::ConstructParams&& inParams) { - Assert(!enums.contains(inId)); - enums.emplace(inId, Mirror::Enum(std::move(inParams))); - return enums.at(inId); + enums.Emplace(inId, Mirror::Enum(std::move(inParams))); + return enums.At(inId); + } + + void Registry::UnloadClass(const Id& inId) // NOLINT + { + classes.Erase(inId); + } + + void Registry::UnloadEnum(const Id& inId) // NOLINT + { + enums.Erase(inId); } } diff --git a/Engine/Source/Mirror/Test/RegistryTest.cpp b/Engine/Source/Mirror/Test/RegistryTest.cpp index 17afdce1d..b070c90e0 100644 --- a/Engine/Source/Mirror/Test/RegistryTest.cpp +++ b/Engine/Source/Mirror/Test/RegistryTest.cpp @@ -31,6 +31,16 @@ int F3(int&& inValue) return std::move(inValue); } +int F4(int inValue) +{ + return inValue; +} + +float F4(int inValue, float inRet) +{ + return inRet; +} + int C0::v0 = 0; int& C0::F0() @@ -38,6 +48,26 @@ int& C0::F0() return v0; } +int C0::F1(int inValue) +{ + return inValue; +} + +int C0::F1(int inValue0, int inValue1) +{ + return inValue0 + inValue1; +} + +int C0::F2(int inValue) // NOLINT +{ + return inValue; +} + +int C0::F2(int inValue0, int inValue1) // NOLINT +{ + return inValue0 + inValue1; +} + C1::C1(const int inV0) : v0(inV0) { @@ -117,6 +147,21 @@ TEST(RegistryTest, GlobalScopeTest) function.Invoke(std::ref(value)); ASSERT_EQ(value, 1); } + + { + const auto& function = globalScope.GetFunction("F4(int)"); + int value = 1; + auto ret = function.Invoke(value); + ASSERT_EQ(ret.As(), 1); + } + + { + const auto& function = globalScope.GetFunction("F4(int, float)"); + int value = 1; + float temp = 2.0f; + auto ret = function.Invoke(value, temp); + ASSERT_EQ(ret.As(), 2.0f); + } } TEST(RegistryTest, ClassTest) @@ -137,6 +182,36 @@ TEST(RegistryTest, ClassTest) auto result = function.Invoke(); ASSERT_EQ(result.As(), 1); } + + { + const auto& function = clazz.GetStaticFunction("F1(int)"); + ASSERT_EQ(function.GetMeta("testKey"), "F1"); + auto result = function.Invoke(1); + ASSERT_EQ(result.As(), 1); + } + + { + const auto& function = clazz.GetStaticFunction("F1(int, int)"); + ASSERT_EQ(function.GetMeta("testKey"), "F1"); + auto result = function.Invoke(1, 2); + ASSERT_EQ(result.As(), 3); + } + + { + C0 object = {}; // NOLINT + const auto& function = clazz.GetMemberFunction("F2(int)"); + ASSERT_EQ(function.GetMeta("testKey"), "F2"); + auto result = function.Invoke(object, 1); + ASSERT_EQ(result.As(), 1); + } + + { + C0 object = {}; // NOLINT + const auto& function = clazz.GetMemberFunction("F2(int, int)"); + ASSERT_EQ(function.GetMeta("testKey"), "F2"); + auto result = function.Invoke(object, 1, 2); + ASSERT_EQ(result.As(), 3); + } } { diff --git a/Engine/Source/Mirror/Test/RegistryTest.h b/Engine/Source/Mirror/Test/RegistryTest.h index 2dce6c809..25f930d18 100644 --- a/Engine/Source/Mirror/Test/RegistryTest.h +++ b/Engine/Source/Mirror/Test/RegistryTest.h @@ -12,11 +12,18 @@ EFunc() int F0(const int a, const int b); EFunc() int& F1(); EFunc() void F2(int& outValue); EFunc() int F3(int&& inValue); +EFunc() int F4(int inValue); +EFunc() float F4(int inValue, float inRet); struct EClass(testKey=C0) C0 { EClassBody(C0) EFunc(testKey=F0) static int& F0(); + EFunc(testKey=F1) static int F1(int inValue); + EFunc(testKey=F1) static int F1(int inValue0, int inValue1); + EFunc(testKey=F2) int F2(int inValue); + EFunc(testKey=F2) int F2(int inValue0, int inValue1); + EProperty(testKey=v0) static int v0; }; diff --git a/Engine/Source/RHI-DirectX12/Include/RHI/DirectX12/SwapChain.h b/Engine/Source/RHI-DirectX12/Include/RHI/DirectX12/SwapChain.h index e7914e1e2..209a937bb 100644 --- a/Engine/Source/RHI-DirectX12/Include/RHI/DirectX12/SwapChain.h +++ b/Engine/Source/RHI-DirectX12/Include/RHI/DirectX12/SwapChain.h @@ -20,6 +20,7 @@ namespace RHI::DirectX12 { explicit DX12SwapChain(DX12Device& inDevice, const SwapChainCreateInfo& inCreateInfo); ~DX12SwapChain() override; + uint8_t GetTextureNum() override; Texture* GetTexture(uint8_t inIndex) override; uint8_t AcquireBackTexture(Semaphore* inSignalSemaphore) override; void Present(RHI::Semaphore* inWaitSemaphore) override; diff --git a/Engine/Source/RHI-DirectX12/Src/BindGroupLayout.cpp b/Engine/Source/RHI-DirectX12/Src/BindGroupLayout.cpp index c552de69a..32fc2a9cf 100644 --- a/Engine/Source/RHI-DirectX12/Src/BindGroupLayout.cpp +++ b/Engine/Source/RHI-DirectX12/Src/BindGroupLayout.cpp @@ -9,7 +9,7 @@ namespace RHI::DirectX12 { D3D12_SHADER_VISIBILITY GetShaderVisibility(const ShaderStageFlags shaderStageFlags) { uint8_t count = 0; - ForEachBitsType([&](ShaderStageBits shaderStage) -> void { + Common::ForEachBits([&](ShaderStageBits shaderStage) -> void { if (shaderStageFlags & shaderStage) { count++; } diff --git a/Engine/Source/RHI-DirectX12/Src/CommandRecorder.cpp b/Engine/Source/RHI-DirectX12/Src/CommandRecorder.cpp index 4f875cd10..f7b6c06a5 100644 --- a/Engine/Source/RHI-DirectX12/Src/CommandRecorder.cpp +++ b/Engine/Source/RHI-DirectX12/Src/CommandRecorder.cpp @@ -33,16 +33,42 @@ namespace RHI::DirectX12 { return D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL; } - size_t GetNativeSubResourceIndex(const DX12Texture& texture, const TextureSubResourceInfo& subResource) + static size_t GetNativeSubResourceIndex(const DX12Texture& texture, const TextureSubResourceInfo& subResource) { const auto& createInfo = texture.GetCreateInfo(); return D3D12CalcSubresource(subResource.mipLevel, subResource.arrayLayer, 0, createInfo.mipLevels, createInfo.dimension == TextureDimension::t3D ? 1 : createInfo.depthOrArraySize); } - CD3DX12_TEXTURE_COPY_LOCATION GetNativeTextureCopyLocation(const DX12Texture& texture, const TextureSubResourceInfo& subResource) + static CD3DX12_TEXTURE_COPY_LOCATION GetNativeTextureCopyLocation(const DX12Texture& texture, const TextureSubResourceInfo& subResource) { return { texture.GetNative(), static_cast(GetNativeSubResourceIndex(texture, subResource)) }; } + + static CD3DX12_TEXTURE_COPY_LOCATION GetNativeBufferCopyLocationFromTextureLayout(DX12Device& device, const DX12Buffer& buffer, const DX12Texture& texture, const BufferTextureCopyInfo& copyInfo) + { + const auto aspectLayout = device.GetTextureSubResourceCopyFootprint(texture, copyInfo.textureSubResource); // NOLINT + + D3D12_PLACED_SUBRESOURCE_FOOTPRINT bufferLayout; + bufferLayout.Offset = copyInfo.bufferOffset; + bufferLayout.Footprint.Format = texture.GetNative()->GetDesc().Format; + bufferLayout.Footprint.Width = copyInfo.copyRegion.x; + bufferLayout.Footprint.Height = copyInfo.copyRegion.y; + bufferLayout.Footprint.Depth = copyInfo.copyRegion.z; + bufferLayout.Footprint.RowPitch = aspectLayout.rowPitch; + return { buffer.GetNative(), bufferLayout }; + } + + static D3D12_BOX GetNativeBox(const Common::UVec3& origin, const Common::UVec3& extent) + { + D3D12_BOX result; + result.left = origin.x; + result.right = origin.x + extent.x; + result.top = origin.y; + result.bottom = origin.y + extent.y; + result.front = origin.z; + result.back = origin.z + extent.z; + return result; + } } namespace RHI::DirectX12 { @@ -76,26 +102,9 @@ namespace RHI::DirectX12 { const auto* srcBuffer = static_cast(src); const auto* dstTexture = static_cast(dst); - const auto aspectLayout = device.GetTextureSubResourceCopyFootprint(*dst, copyInfo.textureSubResource); // NOLINT - - D3D12_PLACED_SUBRESOURCE_FOOTPRINT srcLayout; - srcLayout.Offset = copyInfo.bufferOffset; - srcLayout.Footprint.Format = dstTexture->GetNative()->GetDesc().Format; - srcLayout.Footprint.Width = copyInfo.copyRegion.x; - srcLayout.Footprint.Height = copyInfo.copyRegion.y; - srcLayout.Footprint.Depth = copyInfo.copyRegion.z; - srcLayout.Footprint.RowPitch = aspectLayout.rowPitch; - - const CD3DX12_TEXTURE_COPY_LOCATION srcCopyRegion(srcBuffer->GetNative(), srcLayout); + const CD3DX12_TEXTURE_COPY_LOCATION srcCopyRegion = GetNativeBufferCopyLocationFromTextureLayout(device, *srcBuffer, *dstTexture, copyInfo); const CD3DX12_TEXTURE_COPY_LOCATION dstCopyRegion = GetNativeTextureCopyLocation(*dstTexture, copyInfo.textureSubResource); - - D3D12_BOX srcBox; - srcBox.left = 0; - srcBox.right = copyInfo.copyRegion.x; - srcBox.top = 0; - srcBox.bottom = copyInfo.copyRegion.y; - srcBox.front = 0; - srcBox.back = copyInfo.copyRegion.z; + const D3D12_BOX srcBox = GetNativeBox(Common::UVec3Consts::zero, copyInfo.copyRegion); commandBuffer.GetNative()->CopyTextureRegion( &dstCopyRegion, @@ -108,7 +117,18 @@ namespace RHI::DirectX12 { void DX12CopyPassCommandRecorder::CopyTextureToBuffer(Texture* src, Buffer* dst, const BufferTextureCopyInfo& copyInfo) { - // TODO + const auto* srcTexture = static_cast(src); + const auto* dstBuffer = static_cast(dst); + + const CD3DX12_TEXTURE_COPY_LOCATION srcCopyRegion = GetNativeTextureCopyLocation(*srcTexture, copyInfo.textureSubResource); + const CD3DX12_TEXTURE_COPY_LOCATION dstCopyRegion = GetNativeBufferCopyLocationFromTextureLayout(device, *dstBuffer, *srcTexture, copyInfo); + const D3D12_BOX srcBox = GetNativeBox(copyInfo.textureOrigin, copyInfo.copyRegion); + + commandBuffer.GetNative()->CopyTextureRegion( + &dstCopyRegion, + 0, 0, 0, + &srcCopyRegion, + &srcBox); } void DX12CopyPassCommandRecorder::CopyTextureToTexture(Texture* src, Texture* dst, const TextureCopyInfo& copyInfo) @@ -116,16 +136,9 @@ namespace RHI::DirectX12 { const auto* srcTexture = static_cast(src); const auto* dstTexture = static_cast(dst); - D3D12_BOX srcBox; - srcBox.left = copyInfo.srcOrigin.x; - srcBox.right = copyInfo.srcOrigin.x + copyInfo.copyRegion.x; - srcBox.top = copyInfo.srcOrigin.y; - srcBox.bottom = copyInfo.srcOrigin.y + copyInfo.copyRegion.y; - srcBox.front = copyInfo.srcOrigin.z; - srcBox.back = copyInfo.srcOrigin.z + copyInfo.copyRegion.z; - const CD3DX12_TEXTURE_COPY_LOCATION srcCopyRegion = GetNativeTextureCopyLocation(*srcTexture, copyInfo.srcSubResource); - const CD3DX12_TEXTURE_COPY_LOCATION dstCopyRegion = GetNativeTextureCopyLocation(*dstTexture, copyInfo.dstSubResource); + const CD3DX12_TEXTURE_COPY_LOCATION dstCopyRegion = GetNativeTextureCopyLocation(*dstTexture, copyInfo.dstSubResource); + const D3D12_BOX srcBox = GetNativeBox(copyInfo.srcOrigin, copyInfo.copyRegion); commandBuffer.GetNative()->CopyTextureRegion( &dstCopyRegion, diff --git a/Engine/Source/RHI-DirectX12/Src/Pipeline.cpp b/Engine/Source/RHI-DirectX12/Src/Pipeline.cpp index 285f8372f..43083881d 100644 --- a/Engine/Source/RHI-DirectX12/Src/Pipeline.cpp +++ b/Engine/Source/RHI-DirectX12/Src/Pipeline.cpp @@ -70,7 +70,6 @@ namespace RHI::DirectX12 { CD3DX12_DEPTH_STENCIL_DESC GetDX12DepthStencilDesc(const RasterPipelineCreateInfo& createInfo) { CD3DX12_DEPTH_STENCIL_DESC desc(D3D12_DEFAULT); - // TODO check this desc.DepthEnable = createInfo.depthStencilState.depthEnabled; desc.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; desc.DepthFunc = EnumCast(createInfo.depthStencilState.depthCompareFunc); @@ -86,7 +85,6 @@ namespace RHI::DirectX12 { { DXGI_SAMPLE_DESC desc {}; desc.Count = createInfo.multiSampleState.count; - // TODO https://docs.microsoft.com/en-us/windows/win32/api/dxgicommon/ns-dxgicommon-dxgi_sample_desc desc.Quality = 0; return desc; } diff --git a/Engine/Source/RHI-DirectX12/Src/SwapChain.cpp b/Engine/Source/RHI-DirectX12/Src/SwapChain.cpp index 9f5730a0d..2c20adbe5 100644 --- a/Engine/Source/RHI-DirectX12/Src/SwapChain.cpp +++ b/Engine/Source/RHI-DirectX12/Src/SwapChain.cpp @@ -35,6 +35,11 @@ namespace RHI::DirectX12 { DX12SwapChain::~DX12SwapChain() = default; + uint8_t DX12SwapChain::GetTextureNum() + { + return textureNum; + } + Texture* DX12SwapChain::GetTexture(uint8_t inIndex) { return textures[inIndex].Get(); @@ -79,7 +84,7 @@ namespace RHI::DirectX12 { dx12Queue->GetNative(), dx12Surface->GetNative(), &desc, - /* TODO fullscreen */ nullptr, + nullptr, nullptr, &dx12SwapChain1)); Assert(success); diff --git a/Engine/Source/RHI-DirectX12/Src/Texture.cpp b/Engine/Source/RHI-DirectX12/Src/Texture.cpp index f3506144b..39d6398d5 100644 --- a/Engine/Source/RHI-DirectX12/Src/Texture.cpp +++ b/Engine/Source/RHI-DirectX12/Src/Texture.cpp @@ -47,7 +47,6 @@ namespace RHI::DirectX12 { textureDesc.Flags = FlagsCast(inCreateInfo.usages); textureDesc.DepthOrArraySize = inCreateInfo.depthOrArraySize; textureDesc.SampleDesc.Count = inCreateInfo.samples; - // TODO https://docs.microsoft.com/en-us/windows/win32/api/dxgicommon/ns-dxgicommon-dxgi_sample_desc textureDesc.SampleDesc.Quality = 0; textureDesc.Dimension = EnumCast(inCreateInfo.dimension); diff --git a/Engine/Source/RHI-Dummy/Include/RHI/Dummy/SwapChain.h b/Engine/Source/RHI-Dummy/Include/RHI/Dummy/SwapChain.h index a9c192b49..8e33b4e9b 100644 --- a/Engine/Source/RHI-Dummy/Include/RHI/Dummy/SwapChain.h +++ b/Engine/Source/RHI-Dummy/Include/RHI/Dummy/SwapChain.h @@ -17,6 +17,7 @@ namespace RHI::Dummy { explicit DummySwapChain(const SwapChainCreateInfo& createInfo); ~DummySwapChain() override; + uint8_t GetTextureNum() override; Texture* GetTexture(uint8_t index) override; uint8_t AcquireBackTexture(Semaphore* signalSemaphore) override; void Present(Semaphore* waitSemaphore) override; diff --git a/Engine/Source/RHI-Dummy/Src/SwapChain.cpp b/Engine/Source/RHI-Dummy/Src/SwapChain.cpp index 4c9eed612..bbba71c98 100644 --- a/Engine/Source/RHI-Dummy/Src/SwapChain.cpp +++ b/Engine/Source/RHI-Dummy/Src/SwapChain.cpp @@ -18,6 +18,11 @@ namespace RHI::Dummy { DummySwapChain::~DummySwapChain() = default; + uint8_t DummySwapChain::GetTextureNum() + { + return dummyTextures.size(); + } + Texture* DummySwapChain::GetTexture(uint8_t index) { Assert(index < dummyTextures.size()); diff --git a/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/SwapChain.h b/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/SwapChain.h index 5b2c14d98..af9e9b259 100644 --- a/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/SwapChain.h +++ b/Engine/Source/RHI-Vulkan/Include/RHI/Vulkan/SwapChain.h @@ -20,6 +20,7 @@ namespace RHI::Vulkan { explicit VulkanSwapChain(VulkanDevice& inDevice, const SwapChainCreateInfo& inCreateInfo); ~VulkanSwapChain() override; + uint8_t GetTextureNum() override; Texture* GetTexture(uint8_t inIndex) override; uint8_t AcquireBackTexture(Semaphore* inSignalSemaphore) override; void Present(Semaphore* inWaitSemaphore) override; diff --git a/Engine/Source/RHI-Vulkan/Src/CommandRecorder.cpp b/Engine/Source/RHI-Vulkan/Src/CommandRecorder.cpp index 14770f35f..2e690d908 100644 --- a/Engine/Source/RHI-Vulkan/Src/CommandRecorder.cpp +++ b/Engine/Source/RHI-Vulkan/Src/CommandRecorder.cpp @@ -135,7 +135,7 @@ namespace RHI::Vulkan { return result; } - static VkBufferImageCopy GetNativeBufferImageCopy(Device& device, Texture& texture, const BufferTextureCopyInfo& copyInfo) + static VkBufferImageCopy GetNativeBufferImageCopy(Device& device, const Texture& texture, const BufferTextureCopyInfo& copyInfo) { const auto aspectLayout = device.GetTextureSubResourceCopyFootprint(texture, copyInfo.textureSubResource); // NOLINT const auto createInfo = texture.GetCreateInfo(); @@ -371,7 +371,7 @@ namespace RHI::Vulkan { if (inBeginInfo.depthStencilAttachment.has_value()) { - auto* depthStencilTextureView = static_cast(inBeginInfo.depthStencilAttachment->view); + const auto* depthStencilTextureView = static_cast(inBeginInfo.depthStencilAttachment->view); VkRenderingAttachmentInfo depthAttachmentInfo = {}; depthAttachmentInfo.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; diff --git a/Engine/Source/RHI-Vulkan/Src/SwapChain.cpp b/Engine/Source/RHI-Vulkan/Src/SwapChain.cpp index 6ecef2049..c3788c514 100644 --- a/Engine/Source/RHI-Vulkan/Src/SwapChain.cpp +++ b/Engine/Source/RHI-Vulkan/Src/SwapChain.cpp @@ -37,6 +37,11 @@ namespace RHI::Vulkan { } } + uint8_t VulkanSwapChain::GetTextureNum() + { + return swapChainImageCount; + } + Texture* VulkanSwapChain::GetTexture(const uint8_t inIndex) { return textures[inIndex]; diff --git a/Engine/Source/RHI/Include/RHI/Common.h b/Engine/Source/RHI/Include/RHI/Common.h index c36a10417..9518b4baf 100644 --- a/Engine/Source/RHI/Include/RHI/Common.h +++ b/Engine/Source/RHI/Include/RHI/Common.h @@ -4,13 +4,11 @@ #pragma once -#include #include #include -#include +#include #include -#include #include #include @@ -25,49 +23,33 @@ #define FCIMPL_END(B) return result; }; namespace RHI { - template - using BitsTypeForEachFunc = std::function; - - template - void ForEachBitsType(BitsTypeForEachFunc&& func) - { - using UBitsType = std::underlying_type_t; - for (UBitsType i = 0x1; i < static_cast(E::max); i = i << 1) { - func(static_cast(i)); - } - } -} - -namespace RHI { - using EnumType = uint32_t; - - enum class RHIType : EnumType { + enum class RHIType : uint8_t { directX12, vulkan, dummy, max }; - enum class GpuType : EnumType { + enum class GpuType : uint8_t { hardware, software, max }; - enum class QueueType : EnumType { + enum class QueueType : uint8_t { graphics, compute, transfer, max }; - enum class MapMode : EnumType { + enum class MapMode : uint8_t { read, write, max }; - enum class PixelFormat : EnumType { + enum class PixelFormat : uint8_t { // 8-Bits begin8Bits, r8Unorm, @@ -121,7 +103,7 @@ namespace RHI { max }; - enum class VertexFormat : EnumType { + enum class VertexFormat : uint8_t { // 8-Bits Channel uint8X2, uint8X4, @@ -158,14 +140,14 @@ namespace RHI { max }; - enum class TextureDimension : EnumType { + enum class TextureDimension : uint8_t { t1D, t2D, t3D, max }; - enum class TextureViewDimension : EnumType { + enum class TextureViewDimension : uint8_t { tv1D, tv2D, tv2DArray, @@ -175,7 +157,7 @@ namespace RHI { max }; - enum class TextureAspect : EnumType { + enum class TextureAspect : uint8_t { color, depth, stencil, @@ -183,7 +165,7 @@ namespace RHI { max }; - enum class TextureViewType : EnumType { + enum class TextureViewType : uint8_t { textureBinding, storageBinding, colorAttachment, @@ -191,7 +173,7 @@ namespace RHI { max }; - enum class BufferViewType : EnumType { + enum class BufferViewType : uint8_t { vertex, index, uniformBinding, @@ -200,20 +182,20 @@ namespace RHI { max }; - enum class AddressMode : EnumType { + enum class AddressMode : uint8_t { clampToEdge, repeat, mirrorRepeat, max }; - enum class FilterMode : EnumType { + enum class FilterMode : uint8_t { nearest, linear, max }; - enum class CompareFunc : EnumType { + enum class CompareFunc : uint8_t { never, less, equal, @@ -225,7 +207,7 @@ namespace RHI { max }; - enum class HlslBindingRangeType : EnumType { + enum class HlslBindingRangeType : uint8_t { constantBuffer, texture, sampler, @@ -233,7 +215,7 @@ namespace RHI { max }; - enum class BindingType : EnumType { + enum class BindingType : uint8_t { uniformBuffer, storageBuffer, rwStorageBuffer, @@ -243,14 +225,14 @@ namespace RHI { max }; - enum class SamplerBindingType : EnumType { + enum class SamplerBindingType : uint8_t { filtering, nonFiltering, comparison, max }; - enum class TextureSampleType : EnumType { + enum class TextureSampleType : uint8_t { filterableFloat, nonFilterableFloat, depth, @@ -259,25 +241,25 @@ namespace RHI { max }; - enum class StorageTextureAccess : EnumType { + enum class StorageTextureAccess : uint8_t { writeOnly, max }; - enum class VertexStepMode : EnumType { + enum class VertexStepMode : uint8_t { perVertex, perInstance, max }; - enum class PrimitiveTopologyType : EnumType { + enum class PrimitiveTopologyType : uint8_t { point, line, triangle, max }; - enum class PrimitiveTopology : EnumType { + enum class PrimitiveTopology : uint8_t { pointList, lineList, lineStrip, @@ -290,32 +272,32 @@ namespace RHI { max }; - enum class IndexFormat : EnumType { + enum class IndexFormat : uint8_t { uint16, uint32, max }; - enum class FrontFace : EnumType { + enum class FrontFace : uint8_t { ccw, cw, max }; - enum class FillMode : EnumType { + enum class FillMode : uint8_t { wireframe, solid, max }; - enum class CullMode : EnumType { + enum class CullMode : uint8_t { none, front, back, max }; - enum class StencilOp : EnumType { + enum class StencilOp : uint8_t { keep, zero, replace, @@ -327,7 +309,7 @@ namespace RHI { max }; - enum class BlendFactor : EnumType { + enum class BlendFactor : uint8_t { zero, one, src, @@ -341,7 +323,7 @@ namespace RHI { max }; - enum class BlendOp : EnumType { + enum class BlendOp : uint8_t { opAdd, opSubstract, opReverseSubstract, @@ -350,19 +332,19 @@ namespace RHI { max }; - enum class LoadOp : EnumType { + enum class LoadOp : uint8_t { load, clear, max }; - enum class StoreOp : EnumType { + enum class StoreOp : uint8_t { store, discard, max }; - enum class PresentMode : EnumType { + enum class PresentMode : uint8_t { // TODO check this // 1. DirectX SwapEffect #see https://docs.microsoft.com/en-us/windows/win32/api/dxgi/ne-dxgi-dxgi_swap_effect // 2. Vulkan VkPresentModeKHR #see https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkPresentModeKHR.html @@ -371,13 +353,13 @@ namespace RHI { max }; - enum class ResourceType : EnumType { + enum class ResourceType : uint8_t { buffer, texture, max }; - enum class BufferState : EnumType { + enum class BufferState : uint8_t { undefined, staging, copySrc, @@ -388,7 +370,7 @@ namespace RHI { max }; - enum class TextureState : EnumType { + enum class TextureState : uint8_t { undefined, copySrc, copyDst, @@ -402,87 +384,8 @@ namespace RHI { }; } -#define RHI_FLAGS_DECLARE(FlagsType, BitsType) \ - FlagsType operator&(BitsType a, BitsType b); \ - FlagsType operator&(FlagsType a, BitsType b); \ - FlagsType operator|(BitsType a, BitsType b); \ - FlagsType operator|(FlagsType a, BitsType b); \ - namespace RHI { - using FlagBitsType = uint32_t; - - template - class Flags { - public: - static Flags null; - - using UnderlyingType = std::underlying_type_t; - - Flags() = default; - ~Flags() = default; - Flags(UnderlyingType inValue) : value(inValue) {} // NOLINT - Flags(E e) : value(static_cast(e)) {} // NOLINT - - UnderlyingType Value() const - { - return value; - } - - explicit operator bool() - { - return value; - } - - bool operator==(Flags other) const - { - return value == other.value; - } - - bool operator!=(Flags other) const - { - return value != other.value; - } - - bool operator==(UnderlyingType inValue) const - { - return value == inValue; - } - - bool operator!=(UnderlyingType inValue) const - { - return value != inValue; - } - - bool operator==(E e) const - { - return value == static_cast(e); - } - - bool operator!=(E e) const - { - return value != static_cast(e); - } - - private: - UnderlyingType value; - }; - - template - Flags Flags::null = Flags(0); - - template - Flags operator&(Flags a, Flags b) - { - return Flags(a.Value() & b.Value()); - } - - template - Flags operator|(Flags a, Flags b) - { - return Flags(a.Value() | b.Value()); - } - - enum class BufferUsageBits : FlagBitsType { + enum class BufferUsageBits : uint16_t { mapRead = 0x1, mapWrite = 0x2, copySrc = 0x4, @@ -494,59 +397,48 @@ namespace RHI { rwStorage = 0x100, indirect = 0x200, queryResolve = 0x400, - max + max = 0x800 }; - using BufferUsageFlags = Flags; - RHI_FLAGS_DECLARE(BufferUsageFlags, BufferUsageBits) + using BufferUsageFlags = Common::Flags; + DECLARE_FLAG_BITS_OP(BufferUsageFlags, BufferUsageBits) - enum class TextureUsageBits : FlagBitsType { + enum class TextureUsageBits : uint8_t { copySrc = 0x1, copyDst = 0x2, textureBinding = 0x4, storageBinding = 0x8, renderAttachment = 0x10, depthStencilAttachment = 0x20, - max + max = 0x40 }; - using TextureUsageFlags = Flags; - RHI_FLAGS_DECLARE(TextureUsageFlags, TextureUsageBits) + using TextureUsageFlags = Common::Flags; + DECLARE_FLAG_BITS_OP(TextureUsageFlags, TextureUsageBits) - enum class ShaderStageBits : FlagBitsType { + enum class ShaderStageBits : uint8_t { sVertex = 0x1, sPixel = 0x2, sCompute = 0x4, sGeometry = 0x8, sHull = 0x10, sDomain = 0x20, - max + max = 0x40, }; - using ShaderStageFlags = Flags; - RHI_FLAGS_DECLARE(ShaderStageFlags, ShaderStageBits) + using ShaderStageFlags = Common::Flags; + DECLARE_FLAG_BITS_OP(ShaderStageFlags, ShaderStageBits) - enum class ColorWriteBits : FlagBitsType { + enum class ColorWriteBits : uint8_t { red = 0x1, green = 0x2, blue = 0x4, alpha = 0x8, - max, + max = 0x10, rgb = red | green | blue, all = red | green | blue | alpha }; - using ColorWriteFlags = Flags; - RHI_FLAGS_DECLARE(ColorWriteFlags, ColorWriteBits) + using ColorWriteFlags = Common::Flags; + DECLARE_FLAG_BITS_OP(ColorWriteFlags, ColorWriteBits) } namespace RHI { size_t GetBytesPerPixel(PixelFormat format); -} - -namespace std { - template - struct hash> - { - size_t operator()(RHI::Flags flags) const - { - return hash::UnderlyingType>()(flags.Value()); - } - }; } \ No newline at end of file diff --git a/Engine/Source/RHI/Include/RHI/SwapChain.h b/Engine/Source/RHI/Include/RHI/SwapChain.h index 50eef9003..8da93789d 100644 --- a/Engine/Source/RHI/Include/RHI/SwapChain.h +++ b/Engine/Source/RHI/Include/RHI/SwapChain.h @@ -40,6 +40,7 @@ namespace RHI { NonCopyable(SwapChain) virtual ~SwapChain(); + virtual uint8_t GetTextureNum() = 0; virtual Texture* GetTexture(uint8_t index) = 0; virtual uint8_t AcquireBackTexture(Semaphore* signalSemaphore) = 0; virtual void Present(Semaphore* waitSemaphore) = 0; diff --git a/Engine/Source/RHI/Src/Common.cpp b/Engine/Source/RHI/Src/Common.cpp index a6776e31f..48887d3e6 100644 --- a/Engine/Source/RHI/Src/Common.cpp +++ b/Engine/Source/RHI/Src/Common.cpp @@ -4,19 +4,6 @@ #include -#define RHI_FLAGS_IMPL(FlagsType, BitsType) \ - FlagsType operator&(BitsType a, BitsType b) { return FlagsType(static_cast(a) & static_cast(b)); } \ - FlagsType operator&(FlagsType a, BitsType b) { return FlagsType(a.Value() & static_cast(b)); } \ - FlagsType operator|(BitsType a, BitsType b) { return FlagsType(static_cast(a) | static_cast(b)); } \ - FlagsType operator|(FlagsType a, BitsType b) { return FlagsType(a.Value() | static_cast(b)); } \ - -namespace RHI { - RHI_FLAGS_IMPL(BufferUsageFlags, BufferUsageBits) - RHI_FLAGS_IMPL(TextureUsageFlags, TextureUsageBits) - RHI_FLAGS_IMPL(ShaderStageFlags, ShaderStageBits) - RHI_FLAGS_IMPL(ColorWriteFlags, ColorWriteBits) -} - namespace RHI { size_t GetBytesPerPixel(PixelFormat format) { diff --git a/Engine/Source/Render/Include/Render/RenderingCache.h b/Engine/Source/Render/Include/Render/RenderCache.h similarity index 100% rename from Engine/Source/Render/Include/Render/RenderingCache.h rename to Engine/Source/Render/Include/Render/RenderCache.h diff --git a/Engine/Source/Render/Include/Render/RenderGraph.h b/Engine/Source/Render/Include/Render/RenderGraph.h index afd71db11..9b0f09bf1 100644 --- a/Engine/Source/Render/Include/Render/RenderGraph.h +++ b/Engine/Source/Render/Include/Render/RenderGraph.h @@ -11,7 +11,7 @@ #include #include #include -#include +#include namespace Render { class RGBuilder; diff --git a/Engine/Source/Render/Include/Render/RenderModule.h b/Engine/Source/Render/Include/Render/RenderModule.h index c7890183d..e36b29765 100644 --- a/Engine/Source/Render/Include/Render/RenderModule.h +++ b/Engine/Source/Render/Include/Render/RenderModule.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -29,7 +30,8 @@ namespace Render { void Initialize(const RenderModuleInitParams& inParams); void DeInitialize(); RHI::Device* GetDevice() const; - Scene* AllocateScene(); + Common::UniqueRef NewScene(); + Common::UniqueRef NewView(); void ShutdownRenderingThread(); void FlushAllRenderingCommands() const; diff --git a/Engine/Source/Render/Include/Render/Renderer.h b/Engine/Source/Render/Include/Render/Renderer.h index b7873aab1..da5b6dbe0 100644 --- a/Engine/Source/Render/Include/Render/Renderer.h +++ b/Engine/Source/Render/Include/Render/Renderer.h @@ -4,9 +4,44 @@ #pragma once -#include -#include +#include +#include namespace Render { - // TODO + class Renderer { + public: + struct Params { + const Scene* scene; + const RHI::Texture* surface; + std::vector views; + RHI::Semaphore* waitSemaphore; + RHI::Semaphore* signalSemaphore; + RHI::Fence* signalFence; + }; + + explicit Renderer(const Params& inParams); + virtual ~Renderer(); + + virtual void Render(float inDeltaTimeSeconds) = 0; + + protected: + const Scene* scene; + const RHI::Texture* surface; + std::vector views; + RHI::Semaphore* waitSemaphore; + RHI::Semaphore* signalSemaphore; + RHI::Fence* signalFence; + }; + + class StandardRenderer final : public Renderer { + public: + explicit StandardRenderer(const Params& inParams); + ~StandardRenderer() override; + + void Render(float inDeltaTimeSeconds) override; + + private: + }; + + // TODO ScriptableRenderer } diff --git a/Engine/Source/Render/Include/Render/Scene.h b/Engine/Source/Render/Include/Render/Scene.h index d34a69f26..8a8b59f20 100644 --- a/Engine/Source/Render/Include/Render/Scene.h +++ b/Engine/Source/Render/Include/Render/Scene.h @@ -10,20 +10,20 @@ namespace Render { template using SPPool = Common::TrunkList; template using SPHandle = typename SPPool::Handle; - template using SPPatcher = std::function; using LightSPPool = SPPool; using LightSPH = SPHandle; - using LightSPPatcher = SPPatcher; class Scene final { public: Scene(); ~Scene(); - LightSPH AddLight(const LightSceneProxy& inLight); + NonCopyable(Scene) + NonMovable(Scene) + + LightSPH AddLight(LightSceneProxy&& inLight); void RemoveLight(const LightSPH& inHandle); - void PatchLight(const LightSPH& inHandle, const LightSPPatcher& inPatcher); private: LightSPPool lights; diff --git a/Engine/Source/Render/Include/Render/Shader.h b/Engine/Source/Render/Include/Render/Shader.h index b99ef61a8..84c142eeb 100644 --- a/Engine/Source/Render/Include/Render/Shader.h +++ b/Engine/Source/Render/Include/Render/Shader.h @@ -372,7 +372,7 @@ namespace Render { void GlobalShaderType::ReadCode() { static std::unordered_map pathMap = { - { "/Engine/Shader", Core::Paths::EngineShader().string() } + { "/Engine/Shader", Core::Paths::EngineShaderDir().String() } }; const std::string sourceFile = Shader::sourceFile; diff --git a/Engine/Source/Render/Include/Render/View.h b/Engine/Source/Render/Include/Render/View.h new file mode 100644 index 000000000..acff73c6e --- /dev/null +++ b/Engine/Source/Render/Include/Render/View.h @@ -0,0 +1,29 @@ +// +// Created by johnk on 2025/1/8. +// + +#pragma once + +#include +#include +#include +#include + +namespace Render { + class View { + public: + View(); + ~View(); + + NonCopyable(View) + NonMovable(View) + + void Update(const Common::FMat4x4& inViewMatrix, const Common::FMat4x4& inProjectionMatrix, const Common::FRect& inViewport); + + private: + Common::FMat4x4 viewMatrix; + Common::FMat4x4 projectionMatrix; + Common::FRect viewport; + // TODO viewOrigin, etc... + }; +} diff --git a/Engine/Source/Render/SharedSrc/RenderModule.cpp b/Engine/Source/Render/SharedSrc/RenderModule.cpp index 1d4cfe5b2..f446befc6 100644 --- a/Engine/Source/Render/SharedSrc/RenderModule.cpp +++ b/Engine/Source/Render/SharedSrc/RenderModule.cpp @@ -2,6 +2,7 @@ // Created by johnk on 2023/8/4. // +#include #include #include @@ -34,6 +35,7 @@ namespace Render { Assert(!initialized); renderingThread = Common::MakeUnique("RenderingThread"); + renderingThread->EmplaceTask([]() -> void { Core::ThreadContext::SetTag(Core::ThreadTag::render); }); rhiInstance = RHI::Instance::GetByType(inParams.rhiType); rhiDevice = rhiInstance->GetGpu(0)->RequestDevice( @@ -58,11 +60,16 @@ namespace Render { return rhiDevice.Get(); } - Render::Scene* RenderModule::AllocateScene() // NOLINT + Common::UniqueRef RenderModule::NewScene() // NOLINT { return new Scene(); } + Common::UniqueRef RenderModule::NewView() // NOLINT + { + return new View(); + } + void RenderModule::ShutdownRenderingThread() { renderingThread = nullptr; diff --git a/Engine/Source/Render/Src/RenderingCache.cpp b/Engine/Source/Render/Src/RenderCache.cpp similarity index 99% rename from Engine/Source/Render/Src/RenderingCache.cpp rename to Engine/Source/Render/Src/RenderCache.cpp index 272ebbac5..8ef3de5f5 100644 --- a/Engine/Source/Render/Src/RenderingCache.cpp +++ b/Engine/Source/Render/Src/RenderCache.cpp @@ -2,7 +2,7 @@ // Created by johnk on 2023/3/11. // -#include +#include #include diff --git a/Engine/Source/Render/Src/RenderGraph.cpp b/Engine/Source/Render/Src/RenderGraph.cpp index bb421d4e0..7937ba3de 100644 --- a/Engine/Source/Render/Src/RenderGraph.cpp +++ b/Engine/Source/Render/Src/RenderGraph.cpp @@ -5,7 +5,6 @@ #include #include -#include #include namespace Render::Internal { diff --git a/Engine/Source/Render/Src/Renderer.cpp b/Engine/Source/Render/Src/Renderer.cpp index 0472ce3b9..031b20546 100644 --- a/Engine/Source/Render/Src/Renderer.cpp +++ b/Engine/Source/Render/Src/Renderer.cpp @@ -5,5 +5,27 @@ #include namespace Render { + Renderer::Renderer(const Params& inParams) + : scene(inParams.scene) + , surface(inParams.surface) + , views(inParams.views) + , waitSemaphore(inParams.waitSemaphore) + , signalSemaphore(inParams.signalSemaphore) + , signalFence(inParams.signalFence) + { + } + Renderer::~Renderer() = default; + + StandardRenderer::StandardRenderer(const Params& inParams) + : Renderer(inParams) + { + } + + StandardRenderer::~StandardRenderer() = default; + + void StandardRenderer::Render(float inDeltaTimeSeconds) + { + // TODO + } } diff --git a/Engine/Source/Render/Src/Scene.cpp b/Engine/Source/Render/Src/Scene.cpp index 62f2bf6f3..2d5455b5b 100644 --- a/Engine/Source/Render/Src/Scene.cpp +++ b/Engine/Source/Render/Src/Scene.cpp @@ -2,11 +2,20 @@ // Created by johnk on 2023/8/17. // -#include #include namespace Render { Scene::Scene() = default; Scene::~Scene() = default; + + LightSPH Scene::AddLight(LightSceneProxy&& inLight) + { + return lights.Emplace(std::move(inLight)); + } + + void Scene::RemoveLight(const LightSPH& inHandle) + { + lights.Erase(inHandle); + } } diff --git a/Engine/Source/Render/Src/View.cpp b/Engine/Source/Render/Src/View.cpp new file mode 100644 index 000000000..b4373612b --- /dev/null +++ b/Engine/Source/Render/Src/View.cpp @@ -0,0 +1,18 @@ +// +// Created by johnk on 2025/1/8. +// + +#include + +namespace Render { + View::View() = default; + + View::~View() = default; + + void View::Update(const Common::FMat4x4& inViewMatrix, const Common::FMat4x4& inProjectionMatrix, const Common::FRect& inViewport) + { + viewMatrix = inViewMatrix; + projectionMatrix = inProjectionMatrix; + viewport = inViewport; + } +} diff --git a/Engine/Source/Runtime/Include/Runtime/Asset.h b/Engine/Source/Runtime/Include/Runtime/Asset.h index 488f9c98a..6cdcc4c00 100644 --- a/Engine/Source/Runtime/Include/Runtime/Asset.h +++ b/Engine/Source/Runtime/Include/Runtime/Asset.h @@ -369,7 +369,7 @@ namespace Runtime { } Core::AssetUriParser parser(assetRef.Uri()); - auto pathString = parser.AbsoluteFilePath().string(); + auto pathString = parser.AbsoluteFilePath().String(); Common::BinaryFileSerializeStream stream(pathString); Mirror::Any ref = std::ref(*assetRef.Get()); @@ -387,7 +387,7 @@ namespace Runtime { AssetRef LoadInternal(const Core::Uri& uri) { Core::AssetUriParser parser(uri); - auto pathString = parser.AbsoluteFilePath().string(); + auto pathString = parser.AbsoluteFilePath().String(); Common::BinaryFileDeserializeStream stream(pathString); AssetRef result = Common::SharedRef(new A()); diff --git a/Engine/Source/Runtime/Include/Runtime/Component/Transform.h b/Engine/Source/Runtime/Include/Runtime/Component/Transform.h index 8f5bf2bb0..6a56881cd 100644 --- a/Engine/Source/Runtime/Include/Runtime/Component/Transform.h +++ b/Engine/Source/Runtime/Include/Runtime/Component/Transform.h @@ -6,6 +6,7 @@ #include #include +#include #include namespace Runtime { @@ -17,4 +18,23 @@ namespace Runtime { EProperty() Common::FTransform localToWorld; }; + + struct RUNTIME_API EClass() ChildOfConstraint final { + EClassBody(ChildOfConstraint) + + ChildOfConstraint(); + explicit ChildOfConstraint(Entity inParent, const Common::FTransform& inLocalToParent); + + EProperty() Entity parent; + EProperty() Common::FTransform localToParent; + }; + + struct RUNTIME_API EClass() CopyConstraint final { + EClassBody(CopyConstraint) + + CopyConstraint(); + explicit CopyConstraint(Entity inTarget); + + EProperty() Entity target; + }; } diff --git a/Engine/Source/Runtime/Include/Runtime/ECS.h b/Engine/Source/Runtime/Include/Runtime/ECS.h index 51f6b0fa0..65eeaa476 100644 --- a/Engine/Source/Runtime/Include/Runtime/ECS.h +++ b/Engine/Source/Runtime/Include/Runtime/ECS.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include @@ -29,7 +30,7 @@ namespace Runtime { class RUNTIME_API EClass() System { public: - EClassBody(System) + EPolyClassBody(System) explicit System(ECRegistry& inRegistry); virtual ~System(); @@ -145,6 +146,7 @@ namespace Runtime::Internal { Common::UniqueRef Build(ECRegistry& inRegistry) const; std::unordered_map GetArguments(); const std::unordered_map& GetArguments() const; + SystemClass GetClass() const; private: void BuildArgumentLists(); @@ -316,8 +318,10 @@ namespace Runtime { template Observer& ObConstructed(); template Observer& ObUpdated(); + template Observer& ObRemoved(); Observer& ObConstructedDyn(CompClass inClass); Observer& ObUpdatedDyn(CompClass inClass); + Observer& ObRemoved(CompClass inClass); size_t Size() const; void Each(const EntityTraverseFunc& inFunc) const; void Clear(); @@ -336,6 +340,65 @@ namespace Runtime { std::vector entities; }; + template + class EventsObserver { + public: + using EntityTraverseFunc = Observer::EntityTraverseFunc; + + explicit EventsObserver(ECRegistry& inRegistry); + ~EventsObserver(); + NonCopyable(EventsObserver) + NonMovable(EventsObserver) + + size_t ConstructedSize() const; + size_t UpdatedSize() const; + size_t RemovedSize() const; + void EachConstructed(const EntityTraverseFunc& inFunc) const; + void EachUpdated(const EntityTraverseFunc& inFunc) const; + void EachRemoved(const EntityTraverseFunc& inFunc) const; + void ClearConstructed(); + void ClearUpdated(); + void ClearRemoved(); + void Clear(); + const auto& Constructed() const; + const auto& Updated() const; + const auto& Removed() const; + + private: + Observer constructedObserver; + Observer updatedObserver; + Observer removedObserver; + }; + + class RUNTIME_API EventsObserverDyn { + public: + using EntityTraverseFunc = Observer::EntityTraverseFunc; + + explicit EventsObserverDyn(ECRegistry& inRegistry, CompClass inClass); + ~EventsObserverDyn(); + NonCopyable(EventsObserverDyn) + NonMovable(EventsObserverDyn) + + size_t ConstructedSize() const; + size_t UpdatedSize() const; + size_t RemovedSize() const; + void EachConstructed(const EntityTraverseFunc& inFunc) const; + void EachUpdated(const EntityTraverseFunc& inFunc) const; + void EachRemoved(const EntityTraverseFunc& inFunc) const; + void ClearConstructed(); + void ClearUpdated(); + void ClearRemoved(); + void Clear(); + const auto& Constructed() const; + const auto& Updated() const; + const auto& Removed() const; + + private: + Observer constructedObserver; + Observer updatedObserver; + Observer removedObserver; + }; + class RUNTIME_API ECRegistry { public: using EntityTraverseFunc = Internal::EntityPool::EntityTraverseFunc; @@ -393,7 +456,7 @@ namespace Runtime { template Runtime::ConstView, C...> View(Exclude = {}) const; template Runtime::ConstView, C...> ConstView(Exclude = {}) const; template CompEvents& Events(); - Observer Observer(); + template EventsObserver EventsObserver(); // component dynamic Mirror::Any EmplaceDyn(CompClass inClass, Entity inEntity, const Mirror::ArgumentList& inArgs); @@ -410,6 +473,7 @@ namespace Runtime { Runtime::RuntimeView RuntimeView(const RuntimeFilter& inFilter); Runtime::ConstRuntimeView RuntimeView(const RuntimeFilter& inFilter) const; Runtime::ConstRuntimeView ConstRuntimeView(const RuntimeFilter& inFilter) const; + EventsObserverDyn EventsObserverDyn(CompClass inClass); // global component static template G& GEmplace(Args&&... inArgs); @@ -437,6 +501,8 @@ namespace Runtime { Mirror::Any GGetDyn(GCompClass inClass) const; GCompEvents& GEventsDyn(GCompClass inClass); + Observer Observer(); + private: template friend class BasicView; template friend class BasicRuntimeView; @@ -473,21 +539,27 @@ namespace Runtime { template bool HasSystem() const; template Internal::SystemFactory& GetSystem(); template const Internal::SystemFactory& GetSystem() const; + template const Internal::SystemFactory& MoveSystemTo(); Internal::SystemFactory& EmplaceSystemDyn(SystemClass inClass); void RemoveSystemDyn(SystemClass inClass); bool HasSystemDyn(SystemClass inClass) const; Internal::SystemFactory& GetSystemDyn(SystemClass inClass); const Internal::SystemFactory& GetSystemDyn(SystemClass inClass) const; - auto GetSystems(); - auto GetSystems() const; + Internal::SystemFactory& MoveSystemToDyn(SystemClass inSrcClass, SystemClass inDstClass); + + const std::vector& GetSystems(); + const std::vector& GetSystems() const; const std::string& GetName() const; SystemExecuteStrategy GetStrategy() const; private: + std::vector::iterator FindSystem(SystemClass inClass); + std::vector::const_iterator FindSystem(SystemClass inClass) const; + std::string name; SystemExecuteStrategy strategy; - std::unordered_map systems; + std::vector systems; }; class RUNTIME_API SystemGraph { @@ -500,8 +572,12 @@ namespace Runtime { SystemGroup& GetGroup(const std::string& inName); const SystemGroup& GetGroup(const std::string& inName) const; const std::vector& GetGroups() const; + SystemGroup& MoveGroupTo(const std::string& inSrcName, const std::string& inDstName); private: + std::vector::iterator FindGroup(const std::string& inName); + std::vector::const_iterator FindGroup(const std::string& inName) const; + std::vector systemGroups; }; @@ -784,7 +860,7 @@ namespace Runtime { for (const auto entity : archetype.All()) { std::vector comps; comps.reserve(includes.size()); - for (const auto clazz : includes) { + for (const auto* clazz : includes) { comps.emplace_back(archetype.GetComp(entity, clazz)); } @@ -824,6 +900,106 @@ namespace Runtime { return OnEvent(registry.Events().onUpdated); } + template + Observer& Observer::ObRemoved() + { + return OnEvent(registry.Events().onRemove); + } + + template + EventsObserver::EventsObserver(ECRegistry& inRegistry) + : constructedObserver(inRegistry.Observer()) + , updatedObserver(inRegistry.Observer()) + , removedObserver(inRegistry.Observer()) + { + constructedObserver.ObConstructed(); + constructedObserver.ObUpdated(); + constructedObserver.ObRemoved(); + } + + template + EventsObserver::~EventsObserver() = default; + + template + size_t EventsObserver::ConstructedSize() const + { + return constructedObserver.Size(); + } + + template + size_t EventsObserver::UpdatedSize() const + { + return updatedObserver.Size(); + } + + template + size_t EventsObserver::RemovedSize() const + { + return removedObserver.Size(); + } + + template + void EventsObserver::EachConstructed(const EntityTraverseFunc& inFunc) const + { + constructedObserver.Each(inFunc); + } + + template + void EventsObserver::EachUpdated(const EntityTraverseFunc& inFunc) const + { + updatedObserver.Each(inFunc); + } + + template + void EventsObserver::EachRemoved(const EntityTraverseFunc& inFunc) const + { + removedObserver.Each(inFunc); + } + + template + void EventsObserver::ClearConstructed() + { + constructedObserver.Clear(); + } + + template + void EventsObserver::ClearUpdated() + { + updatedObserver.Clear(); + } + + template + void EventsObserver::ClearRemoved() + { + removedObserver.Clear(); + } + + template + void EventsObserver::Clear() + { + ClearConstructed(); + ClearUpdated(); + ClearRemoved(); + } + + template + const auto& EventsObserver::Constructed() const + { + return constructedObserver; + } + + template + const auto& EventsObserver::Updated() const + { + return updatedObserver; + } + + template + const auto& EventsObserver::Removed() const + { + return removedObserver; + } + template C& ECRegistry::Emplace(Entity inEntity, Args&&... inArgs) { @@ -905,6 +1081,12 @@ namespace Runtime { return EventsDyn(Internal::GetClass()); } + template + EventsObserver ECRegistry::EventsObserver() + { + return Runtime::EventsObserver { *this }; + } + template void ECRegistry::NotifyUpdated(Entity inEntity) { @@ -1033,4 +1215,10 @@ namespace Runtime { { return GetSystemDyn(Internal::GetClass()); } + + template + const Internal::SystemFactory& SystemGroup::MoveSystemTo() + { + return MoveSystemToDyn(Internal::GetClass(), Internal::GetClass()); + } } // namespace Runtime diff --git a/Engine/Source/Runtime/Include/Runtime/Engine.h b/Engine/Source/Runtime/Include/Runtime/Engine.h index c72380abf..1655818b6 100644 --- a/Engine/Source/Runtime/Include/Runtime/Engine.h +++ b/Engine/Source/Runtime/Include/Runtime/Engine.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include @@ -14,6 +15,7 @@ namespace Runtime { class World; struct EngineInitParams { + bool logToFile; std::string projectFile; std::string rhiType; }; @@ -33,6 +35,9 @@ namespace Runtime { protected: explicit Engine(const EngineInitParams& inParams); + void AttachLogFile(); + void InitRender(const std::string& inRhiTypeStr); + std::unordered_set worlds; Render::RenderModule* renderModule; }; diff --git a/Engine/Source/Runtime/Include/Runtime/System/Scene.h b/Engine/Source/Runtime/Include/Runtime/System/Scene.h new file mode 100644 index 000000000..00e7c3e9e --- /dev/null +++ b/Engine/Source/Runtime/Include/Runtime/System/Scene.h @@ -0,0 +1,76 @@ +// +// Created by johnk on 2025/1/9. +// + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace Runtime { + class RUNTIME_API EClass() SceneSystem final : public System { + EPolyClassBody(SceneSystem) + + public: + explicit SceneSystem(ECRegistry& inRegistry); + ~SceneSystem() override; + + NonCopyable(SceneSystem) + NonMovable(SceneSystem) + + void Tick(float inDeltaTimeMs) override; + + private: + template using SPMap = std::unordered_map>; + + static Render::LightSceneProxy MakeLightSceneProxy(const DirectionalLight& inDirectionalLight, const Transform* inTransform); + static Render::LightSceneProxy MakeLightSceneProxy(const PointLight& inPointLight, const Transform* inTransform); + static Render::LightSceneProxy MakeLightSceneProxy(const SpotLight& inSpotLight, const Transform* inTransform); + template void EmplaceLightSceneProxy(Entity inEntity); + template void UpdateLightSceneProxy(Entity inEntity); + template void UpdateTransformForSceneProxy(SPMap& inSceneProxyMap, Entity inEntity, bool inWithScale = true); + void RemoveLightSceneProxy(Entity e); + + Render::RenderModule& renderModule; + Common::UniqueRef scene; + Observer transformUpdatedObserver; + EventsObserver directionalLightsObserver; + EventsObserver pointLightsObserver; + EventsObserver spotLightsObserver; + SPMap lightSceneProxies; + }; +} + +namespace Runtime { + template + void SceneSystem::EmplaceLightSceneProxy(Entity inEntity) + { + lightSceneProxies.emplace(inEntity, scene->AddLight(MakeLightSceneProxy(registry.Get(inEntity), registry.Find(inEntity)))); + } + + template + void SceneSystem::UpdateLightSceneProxy(Entity inEntity) + { + *lightSceneProxies.at(inEntity) = MakeLightSceneProxy(registry.Get(inEntity), registry.Find(inEntity)); + } + + template + void SceneSystem::UpdateTransformForSceneProxy(SPMap& inSceneProxyMap, Entity inEntity, bool inWithScale) + { + const auto iter = inSceneProxyMap.find(inEntity); + if (iter == inSceneProxyMap.end()) { + return; + } + + if (const Transform* transform = registry.Find(inEntity); + transform == nullptr) { + iter->second->localToWorld = Common::FMat4x4Consts::identity; + } else { + iter->second->localToWorld = inWithScale ? transform->localToWorld.GetTransformMatrix() : transform->localToWorld.GetTransformMatrixNoScale();; + } + } +} diff --git a/Engine/Source/Runtime/Src/Component/Transform.cpp b/Engine/Source/Runtime/Src/Component/Transform.cpp index cc9ff5a4a..f5a6e731f 100644 --- a/Engine/Source/Runtime/Src/Component/Transform.cpp +++ b/Engine/Source/Runtime/Src/Component/Transform.cpp @@ -11,4 +11,19 @@ namespace Runtime { : localToWorld(inLocalToWorld) { } + + ChildOfConstraint::ChildOfConstraint() = default; + + ChildOfConstraint::ChildOfConstraint(Entity inParent, const Common::FTransform& inLocalToParent) + : parent(inParent) + , localToParent(inLocalToParent) + { + } + + CopyConstraint::CopyConstraint() = default; + + CopyConstraint::CopyConstraint(Entity inTarget) + : target(inTarget) + { + } } diff --git a/Engine/Source/Runtime/Src/ECS.cpp b/Engine/Source/Runtime/Src/ECS.cpp index 707968410..a9b5dc59d 100644 --- a/Engine/Source/Runtime/Src/ECS.cpp +++ b/Engine/Source/Runtime/Src/ECS.cpp @@ -368,6 +368,11 @@ namespace Runtime::Internal { return arguments; } + SystemClass SystemFactory::GetClass() const + { + return clazz; + } + void SystemFactory::BuildArgumentLists() { const auto& memberVariables = clazz->GetMemberVariables(); @@ -438,6 +443,11 @@ namespace Runtime { return OnEvent(registry.EventsDyn(inClass).onUpdated); } + Observer& Observer::ObRemoved(CompClass inClass) + { + return OnEvent(registry.EventsDyn(inClass).onRemove); + } + size_t Observer::Size() const { return entities.size(); @@ -499,6 +509,85 @@ namespace Runtime { return *this; } + EventsObserverDyn::EventsObserverDyn(ECRegistry& inRegistry, CompClass inClass) + : constructedObserver(inRegistry.Observer()) + , updatedObserver(inRegistry.Observer()) + , removedObserver(inRegistry.Observer()) + { + constructedObserver.ObConstructedDyn(inClass); + updatedObserver.ObUpdatedDyn(inClass); + removedObserver.ObRemoved(inClass); + } + + EventsObserverDyn::~EventsObserverDyn() = default; + + size_t EventsObserverDyn::ConstructedSize() const + { + return constructedObserver.Size(); + } + + size_t EventsObserverDyn::UpdatedSize() const + { + return updatedObserver.Size(); + } + + size_t EventsObserverDyn::RemovedSize() const + { + return removedObserver.Size(); + } + + void EventsObserverDyn::EachConstructed(const EntityTraverseFunc& inFunc) const + { + constructedObserver.Each(inFunc); + } + + void EventsObserverDyn::EachUpdated(const EntityTraverseFunc& inFunc) const + { + updatedObserver.Each(inFunc); + } + + void EventsObserverDyn::EachRemoved(const EntityTraverseFunc& inFunc) const + { + removedObserver.Each(inFunc); + } + + void EventsObserverDyn::ClearConstructed() + { + constructedObserver.Clear(); + } + + void EventsObserverDyn::ClearUpdated() + { + updatedObserver.Clear(); + } + + void EventsObserverDyn::ClearRemoved() + { + removedObserver.Clear(); + } + + void EventsObserverDyn::Clear() + { + ClearConstructed(); + ClearUpdated(); + ClearRemoved(); + } + + const auto& EventsObserverDyn::Constructed() const + { + return constructedObserver; + } + + const auto& EventsObserverDyn::Updated() const + { + return updatedObserver; + } + + const auto& EventsObserverDyn::Removed() const + { + return removedObserver; + } + ECRegistry::ECRegistry() { archetypes.emplace(0, Internal::Archetype({})); @@ -617,6 +706,11 @@ namespace Runtime { return Runtime::ConstRuntimeView { *this, inFilter }; } + EventsObserverDyn ECRegistry::EventsObserverDyn(CompClass inClass) + { + return Runtime::EventsObserverDyn { *this, inClass }; + } + void ECRegistry::NotifyUpdatedDyn(CompClass inClass, Entity inEntity) { const auto iter = compEvents.find(inClass); @@ -838,38 +932,57 @@ namespace Runtime { Internal::SystemFactory& SystemGroup::EmplaceSystemDyn(SystemClass inClass) { - systems.emplace(inClass, Internal::SystemFactory(inClass)); - return systems.at(inClass); + Assert(!HasSystemDyn(inClass)); + return systems.emplace_back(inClass); } void SystemGroup::RemoveSystemDyn(SystemClass inClass) { - systems.erase(inClass); + const auto iter = FindSystem(inClass); + Assert(iter != systems.end()); + systems.erase(iter); } bool SystemGroup::HasSystemDyn(SystemClass inClass) const { - return systems.contains(inClass); + const auto iter = FindSystem(inClass); + return iter != systems.end(); } Internal::SystemFactory& SystemGroup::GetSystemDyn(SystemClass inClass) { - return systems.at(inClass); + const auto iter = FindSystem(inClass); + Assert(iter != systems.end()); + return *iter; } const Internal::SystemFactory& SystemGroup::GetSystemDyn(SystemClass inClass) const { - return systems.at(inClass); + const auto iter = FindSystem(inClass); + Assert(iter != systems.end()); + return *iter; } - auto SystemGroup::GetSystems() + Internal::SystemFactory& SystemGroup::MoveSystemToDyn(SystemClass inSrcClass, SystemClass inDstClass) { - return systems | std::views::values; + const auto srcIter = FindSystem(inSrcClass); + Assert(srcIter != systems.end()); + const auto tempGroup = std::move(*srcIter); + systems.erase(srcIter); + + const auto dstIter = FindSystem(inDstClass); + Assert(dstIter != systems.end()); + return *systems.emplace(dstIter, std::move(tempGroup)); + } + + const std::vector& SystemGroup::GetSystems() + { + return systems; } - auto SystemGroup::GetSystems() const + const std::vector& SystemGroup::GetSystems() const { - return systems | std::views::values; + return systems; } const std::string& SystemGroup::GetName() const @@ -882,52 +995,53 @@ namespace Runtime { return strategy; } + std::vector::iterator SystemGroup::FindSystem(SystemClass inClass) + { + return std::ranges::find_if(systems, [inClass](const Internal::SystemFactory& inFactory) -> bool { + return inFactory.GetClass() == inClass; + }); + } + + std::vector::const_iterator SystemGroup::FindSystem(SystemClass inClass) const + { + return std::ranges::find_if(systems, [inClass](const Internal::SystemFactory& inFactory) -> bool { + return inFactory.GetClass() == inClass; + }); + } + SystemGraph::SystemGraph() = default; SystemGroup& SystemGraph::AddGroup(const std::string& inName, SystemExecuteStrategy inStrategy) { + Assert(!HasGroup(inName)); return systemGroups.emplace_back(inName, inStrategy); } void SystemGraph::RemoveGroup(const std::string& inName) { - const auto iter = std::ranges::find_if(systemGroups, [&](const SystemGroup& group) -> bool { - return group.GetName() == inName; - }); + const auto iter = FindGroup(inName); Assert(iter != systemGroups.end()); systemGroups.erase(iter); } bool SystemGraph::HasGroup(const std::string& inName) const { - for (const auto& group : systemGroups) { - if (group.GetName() == inName) { - return true; - } - } - return false; + const auto iter = FindGroup(inName); + return iter != systemGroups.end(); } SystemGroup& SystemGraph::GetGroup(const std::string& inName) { - for (auto& group : systemGroups) { - if (group.GetName() == inName) { - return group; - } - } - Assert(false); - return systemGroups.back(); + const auto iter = FindGroup(inName); + Assert(iter != systemGroups.end()); + return *iter; } const SystemGroup& SystemGraph::GetGroup(const std::string& inName) const { - for (const auto& group : systemGroups) { - if (group.GetName() == inName) { - return group; - } - } - Assert(false); - return systemGroups.back(); + const auto iter = FindGroup(inName); + Assert(iter != systemGroups.end()); + return *iter; } const std::vector& SystemGraph::GetGroups() const @@ -935,6 +1049,32 @@ namespace Runtime { return systemGroups; } + SystemGroup& SystemGraph::MoveGroupTo(const std::string& inSrcName, const std::string& inDstName) + { + const auto srcIter = FindGroup(inSrcName); + Assert(srcIter != systemGroups.end()); + const auto tempGroup = std::move(*srcIter); + systemGroups.erase(srcIter); + + const auto dstIter = FindGroup(inDstName); + Assert(dstIter != systemGroups.end()); + return *systemGroups.emplace(dstIter, std::move(tempGroup)); + } + + std::vector::iterator SystemGraph::FindGroup(const std::string& inName) + { + return std::ranges::find_if(systemGroups, [&](const SystemGroup& group) -> bool { + return group.GetName() == inName; + }); + } + + std::vector::const_iterator SystemGraph::FindGroup(const std::string& inName) const + { + return std::ranges::find_if(systemGroups, [&](const SystemGroup& group) -> bool { + return group.GetName() == inName; + }); + } + SystemPipeline::SystemPipeline(const SystemGraph& inGraph) { const auto& systemGroups = inGraph.GetGroups(); diff --git a/Engine/Source/Runtime/Src/Engine.cpp b/Engine/Source/Runtime/Src/Engine.cpp index 1526c1141..079da93ed 100644 --- a/Engine/Source/Runtime/Src/Engine.cpp +++ b/Engine/Source/Runtime/Src/Engine.cpp @@ -4,23 +4,25 @@ #include #include +#include #include #include +#include +#include #include namespace Runtime { Engine::Engine(const EngineInitParams& inParams) { + Core::ScopedThreadTag tag(Core::ThreadTag::game); if (!inParams.projectFile.empty()) { Core::Paths::SetCurrentProjectFile(inParams.projectFile); } - renderModule = ::Core::ModuleManager::Get().FindOrLoadTyped("Render"); - Assert(renderModule != nullptr); - - Render::RenderModuleInitParams initParams; - initParams.rhiType = RHI::GetRHITypeByAbbrString(inParams.rhiType); - renderModule->Initialize(initParams); + if (inParams.logToFile) { + AttachLogFile(); + } + InitRender(inParams.rhiType); } Engine::~Engine() @@ -52,6 +54,8 @@ namespace Runtime { } world->Tick(inTimeSeconds); } + + // TODO emplace render thread task, like wait fence, console command copy } Common::UniqueRef Engine::CreateWorld(const std::string& inName) const // NOLINT @@ -59,6 +63,27 @@ namespace Runtime { return new World(inName); } + void Engine::AttachLogFile() // NOLINT + { + const auto time = Common::Time(Common::TimePoint::Now()); + const auto logName = Core::Paths::ExecutablePath().FileNameWithoutExtension() + "-" + time.ToString() + ".log"; + const auto logFile = ((Core::Paths::HasSetProjectFile() ? Core::Paths::ProjectLogDir() : Core::Paths::EngineLogDir()) / logName).String(); + + Core::Logger::Get().Attach(new Core::FileLogStream(logFile)); + LogInfo(Core, "logger attached to file {}", logFile); + } + + void Engine::InitRender(const std::string& inRhiTypeStr) + { + renderModule = ::Core::ModuleManager::Get().FindOrLoadTyped("Render"); + Assert(renderModule != nullptr); + + Render::RenderModuleInitParams initParams; + initParams.rhiType = RHI::GetRHITypeByAbbrString(inRhiTypeStr); + renderModule->Initialize(initParams); + LogInfo(Render, "RHI type: {}", inRhiTypeStr); + } + Common::UniqueRef EngineHolder::engine = nullptr; MinEngine::MinEngine(const EngineInitParams& inParams) diff --git a/Engine/Source/Runtime/Src/System/Scene.cpp b/Engine/Source/Runtime/Src/System/Scene.cpp new file mode 100644 index 000000000..8009b3106 --- /dev/null +++ b/Engine/Source/Runtime/Src/System/Scene.cpp @@ -0,0 +1,84 @@ +// +// Created by johnk on 2025/1/9. +// + +#include +#include +#include + +namespace Runtime { + SceneSystem::SceneSystem(ECRegistry& inRegistry) + : System(inRegistry) + , renderModule(Core::ModuleManager::Get().GetTyped("Render")) + , scene(renderModule.NewScene()) + , transformUpdatedObserver(inRegistry.Observer()) + , directionalLightsObserver(inRegistry.EventsObserver()) + , pointLightsObserver(inRegistry.EventsObserver()) + , spotLightsObserver(inRegistry.EventsObserver()) + { + transformUpdatedObserver + .ObConstructed() + .ObUpdated(); + } + + SceneSystem::~SceneSystem() = default; + + void SceneSystem::Tick(float inDeltaTimeMs) + { + directionalLightsObserver.Constructed().Each([this](Entity e) -> void { EmplaceLightSceneProxy(e); }); + pointLightsObserver.Constructed().Each([this](Entity e) -> void { EmplaceLightSceneProxy(e); }); + spotLightsObserver.Constructed().Each([this](Entity e) -> void { EmplaceLightSceneProxy(e); }); + directionalLightsObserver.Updated().Each([this](Entity e) -> void { UpdateLightSceneProxy(e); }); + pointLightsObserver.Updated().Each([this](Entity e) -> void { UpdateLightSceneProxy(e); }); + spotLightsObserver.Updated().Each([this](Entity e) -> void { UpdateLightSceneProxy(e); }); + directionalLightsObserver.Removed().Each([this](Entity e) -> void { RemoveLightSceneProxy(e); }); + pointLightsObserver.Removed().Each([this](Entity e) -> void { RemoveLightSceneProxy(e); }); + spotLightsObserver.Removed().Each([this](Entity e) -> void { RemoveLightSceneProxy(e); }); + transformUpdatedObserver.Each([this](Entity e) -> void { UpdateTransformForSceneProxy(lightSceneProxies, e); }); + + transformUpdatedObserver.Clear(); + directionalLightsObserver.Clear(); + pointLightsObserver.Clear(); + spotLightsObserver.Clear(); + } + + Render::LightSceneProxy SceneSystem::MakeLightSceneProxy(const DirectionalLight& inDirectionalLight, const Transform* inTransform) + { + Render::LightSceneProxy result {}; + result.type = Render::LightType::directional; + result.localToWorld = inTransform == nullptr ? Common::FMat4x4Consts::identity : inTransform->localToWorld.GetTransformMatrixNoScale(); + result.color = inDirectionalLight.color; + result.intensity = inDirectionalLight.intensity; + result.typedPart.emplace(); + return result; + } + + Render::LightSceneProxy SceneSystem::MakeLightSceneProxy(const PointLight& inPointLight, const Transform* inTransform) + { + Render::LightSceneProxy result {}; + result.type = Render::LightType::point; + result.localToWorld = inTransform == nullptr ? Common::FMat4x4Consts::identity : inTransform->localToWorld.GetTransformMatrixNoScale(); + result.color = inPointLight.color; + result.intensity = inPointLight.intensity; + + auto& typedPart = result.typedPart.emplace(); // NOLINT + typedPart.radius = inPointLight.radius; + return result; + } + + Render::LightSceneProxy SceneSystem::MakeLightSceneProxy(const SpotLight& inSpotLight, const Transform* inTransform) + { + Render::LightSceneProxy result {}; + result.type = Render::LightType::spot; + result.localToWorld = inTransform == nullptr ? Common::FMat4x4Consts::identity : inTransform->localToWorld.GetTransformMatrixNoScale(); + result.color = inSpotLight.color; + result.intensity = inSpotLight.intensity; + result.typedPart.emplace(); + return result; + } + + void SceneSystem::RemoveLightSceneProxy(Entity e) + { + lightSceneProxies.erase(e); + } +} diff --git a/Engine/Source/Runtime/Test/WorldTest.cpp b/Engine/Source/Runtime/Test/WorldTest.cpp index a4dba8da1..e50cfdbeb 100644 --- a/Engine/Source/Runtime/Test/WorldTest.cpp +++ b/Engine/Source/Runtime/Test/WorldTest.cpp @@ -12,7 +12,7 @@ using namespace Runtime; struct WorldTest : testing::Test { void SetUp() override { - EngineInitParams engineInitParams; + EngineInitParams engineInitParams {}; engineInitParams.rhiType = RHI::GetAbbrStringByType(RHI::RHIType::dummy); EngineHolder::Load("RuntimeTest", engineInitParams); diff --git a/Sample/Rendering-Triangle/Triangle.cpp b/Sample/Rendering-Triangle/Triangle.cpp index 7ecc2b1fd..a8285a360 100644 --- a/Sample/Rendering-Triangle/Triangle.cpp +++ b/Sample/Rendering-Triangle/Triangle.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include using namespace Common; diff --git a/Tool/MirrorTool/ExeSrc/Main.cpp b/Tool/MirrorTool/ExeSrc/Main.cpp index 3c863f52c..16138869f 100644 --- a/Tool/MirrorTool/ExeSrc/Main.cpp +++ b/Tool/MirrorTool/ExeSrc/Main.cpp @@ -3,17 +3,11 @@ // #include -#include #include #include #include -#include - -static std::string GetUnixStylePath(const std::string& inPath) -{ - return Common::StringUtils::Replace(std::filesystem::weakly_canonical(inPath).string(), "\\", "/"); -} +#include int main(int argc, char* argv[]) // NOLINT { @@ -22,20 +16,22 @@ int main(int argc, char* argv[]) // NOLINT std::string inputFile; std::string outputFile; std::vector headerDirs; + bool dynamic = false; if (const auto cli = ( clipp::required("-i").doc("input header file") & clipp::value("input header file", inputFile), clipp::required("-o").doc("output file") & clipp::value("output file", outputFile), - clipp::option("-I").doc("header search dirs") & clipp::values("header search dirs", headerDirs)); + clipp::option("-I").doc("header search dirs") & clipp::values("header search dirs", headerDirs), + clipp::option("-d").set(dynamic).doc("used for dynamic library (auto unload some metas)")); !clipp::parse(argc, argv, cli)) { std::cout << clipp::make_man_page(cli, argv[0]); return 1; } - inputFile = GetUnixStylePath(inputFile); - outputFile = GetUnixStylePath(outputFile); + inputFile = Common::Path(inputFile).String(); + outputFile = Common::Path(outputFile).String(); for (auto& headerDir : headerDirs) { - headerDir = GetUnixStylePath(headerDir); + headerDir = Common::Path(headerDir).String(); } if (!inputFile.ends_with(".h")) { @@ -55,7 +51,7 @@ int main(int argc, char* argv[]) // NOLINT return 1; } - MirrorTool::Generator generator(inputFile, outputFile, headerDirs, std::get(parseResultOrError)); + MirrorTool::Generator generator(inputFile, outputFile, headerDirs, std::get(parseResultOrError), dynamic); if (auto [generateSuccess, generateError] = generator.Generate(); !generateSuccess) { std::cout << generateError << Common::newline; diff --git a/Tool/MirrorTool/Include/MirrorTool/Generator.h b/Tool/MirrorTool/Include/MirrorTool/Generator.h index edb804603..f0f3b1557 100644 --- a/Tool/MirrorTool/Include/MirrorTool/Generator.h +++ b/Tool/MirrorTool/Include/MirrorTool/Generator.h @@ -18,7 +18,7 @@ namespace MirrorTool { using Result = std::pair; NonCopyable(Generator) - explicit Generator(std::string inInputFile, std::string inOutputFile, std::vector inHeaderDirs, const MetaInfo& inMetaInfo); + explicit Generator(std::string inInputFile, std::string inOutputFile, std::vector inHeaderDirs, const MetaInfo& inMetaInfo, bool inDynamic); ~Generator(); Result Generate() const; @@ -30,5 +30,6 @@ namespace MirrorTool { std::string inputFile; std::string outputFile; std::vector headerDirs; + bool dynamic; }; } diff --git a/Tool/MirrorTool/Src/Generator.cpp b/Tool/MirrorTool/Src/Generator.cpp index c5b245b3e..526506a04 100644 --- a/Tool/MirrorTool/Src/Generator.cpp +++ b/Tool/MirrorTool/Src/Generator.cpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include #include @@ -81,18 +83,49 @@ namespace MirrorTool { return stream.str(); } - static std::string GetEnumsCode(const MetaInfo& metaInfo, size_t uniqueId) + static std::string GetEnumUnloadCode(const EnumInfo& enumInfo) { + const auto fullName = GetFullName(enumInfo); + std::stringstream stream; stream << Common::newline; - stream << std::format("int _mirrorEnumRegistry_{} = []() -> int", uniqueId) << Common::newline; + stream << Common::tab<2> << std::format(R"("Mirror::Registry::Get().UnloadEnum("{}");")", fullName) << Common::newline; + return stream.str(); + } + + static std::string GetNamespaceEnumsUnloadCode(const NamespaceInfo& ns) // NOLINT + { + std::stringstream stream; + for (const auto& e : ns.enums) { + stream << GetEnumUnloadCode(e); + } + for (const auto& cns : ns.namespaces) { + stream << GetNamespaceEnumsUnloadCode(cns); + } + return stream.str(); + } + + static std::string GetEnumsCode(const MetaInfo& metaInfo, size_t uniqueId, bool dynamic) + { + std::stringstream stream; + stream << Common::newline; + stream << std::format("Mirror::Internal::ScopedReleaser _mirrorEnumRegistry_{} = []() -> Mirror::Internal::ScopedReleaser", uniqueId) << Common::newline; stream << "{"; stream << GetNamespaceEnumsCode(metaInfo.global); for (const auto& ns : metaInfo.namespaces) { stream << GetNamespaceEnumsCode(ns); } stream << Common::newline; - stream << Common::tab<1> << "return 0;" << Common::newline; + if (dynamic) { + stream << Common::tab<1> << "return Mirror::Internal::ScopedReleaser([]() -> void {" << Common::newline; + stream << GetNamespaceEnumsUnloadCode(metaInfo.global); + for (const auto& ns : metaInfo.namespaces) { + stream << GetNamespaceEnumsUnloadCode(ns); + } + stream << Common::tab<1> << "});" << Common::newline; + } else { + stream << Common::tab<1> << "return Mirror::Internal::ScopedReleaser();" << Common::newline; + } stream << "}();" << Common::newline; return stream.str(); } @@ -107,7 +140,53 @@ namespace MirrorTool { return map.at(access); } - static std::string GetClassCode(const ClassInfo& clazz) // NOLINT + template + static std::unordered_map> GetFunctionOverloadMap(const std::vector& infos) + { + std::unordered_map> result; + result.reserve(infos.size()); + + for (const auto& info : infos) { + result[info.name].emplace_back(&info); + } + return result; + } + + template + static std::string GetOverloadFunctionFullNameWithParams(const FuncInfo& info, const std::string& name) + { + std::stringstream stream; + stream << name; + stream << "("; + for (auto i = 0; i < info.parameters.size(); i++) { + const auto& paramNameAndType = info.parameters[i]; + stream << paramNameAndType.second; + if (i != info.parameters.size() - 1) { + stream << ", "; + } + } + stream << ")"; + return stream.str(); + } + + template + static std::string GetOverloadFunctionPtrType(const FuncInfo& info, const std::optional& className = std::nullopt) + { + std::stringstream stream; + stream << info.retType; + stream << std::format("({}*)(", className.has_value() ? className.value() + "::" : ""); + for (auto i = 0; i < info.parameters.size(); i++) { + const auto& paramNameAndType = info.parameters[i]; + stream << paramNameAndType.second; + if (i != info.parameters.size() - 1) { + stream << ", "; + } + } + stream << ")"; + return stream.str(); + } + + static std::string GetClassCode(const ClassInfo& clazz, bool dynamic) // NOLINT { const std::string fullName = GetFullName(clazz); auto defaultCtorFieldAccess = FieldAccess::pub; @@ -124,7 +203,7 @@ namespace MirrorTool { std::stringstream stream; stream << Common::newline; - stream << std::format("int {}::_mirrorRegistry = []() -> int ", fullName) << Common::newline; + stream << std::format("Mirror::Internal::ScopedReleaser {}::_mirrorRegistry = []() -> Mirror::Internal::ScopedReleaser ", fullName) << Common::newline; stream << "{" << Common::newline; stream << Common::tab<1> << "Mirror::Registry::Get()"; if (clazz.baseClassName.empty()) { @@ -144,29 +223,66 @@ namespace MirrorTool { stream << Common::newline << Common::tab<3> << std::format(R"(.StaticVariable<&{}{}>("{}"))", variableName, fieldAccessStr, staticVariable.name); stream << GetMetaDataCode<4>(staticVariable); } - for (const auto& staticFunction : clazz.staticFunctions) { - const std::string functionName = GetFullName(staticFunction); - const std::string fieldAccessStr = staticFunction.fieldAccess != FieldAccess::pub ? std::format(", {}", GetFieldAccessStr(staticFunction.fieldAccess)) : ""; - stream << Common::newline << Common::tab<3> << std::format(R"(.StaticFunction<&{}{}>("{}"))", functionName, fieldAccessStr, staticFunction.name); - stream << GetMetaDataCode<4>(staticFunction); + + for (const auto staticFunctionOverloadMap = GetFunctionOverloadMap(clazz.staticFunctions); + const auto& overloads : staticFunctionOverloadMap | std::views::values) { + if (overloads.size() > 1) { + for (const auto& overload : overloads) { + const ClassFunctionInfo& staticFunction = *overload; + const std::string functionName = GetFullName(staticFunction); + const std::string shortFunctionNameWithParams = GetOverloadFunctionFullNameWithParams(staticFunction, staticFunction.name); + const std::string ptrType = GetOverloadFunctionPtrType(staticFunction); + const std::string fieldAccessStr = staticFunction.fieldAccess != FieldAccess::pub ? std::format(", {}", GetFieldAccessStr(staticFunction.fieldAccess)) : ""; + stream << Common::newline << Common::tab<3> << std::format(R"(.StaticFunction(&{}){}>("{}"))", ptrType, functionName, fieldAccessStr, shortFunctionNameWithParams); + stream << GetMetaDataCode<4>(staticFunction); + } + } else { + const ClassFunctionInfo& staticFunction = *overloads[0]; + const std::string functionName = GetFullName(staticFunction); + const std::string fieldAccessStr = staticFunction.fieldAccess != FieldAccess::pub ? std::format(", {}", GetFieldAccessStr(staticFunction.fieldAccess)) : ""; + stream << Common::newline << Common::tab<3> << std::format(R"(.StaticFunction<&{}{}>("{}"))", functionName, fieldAccessStr, staticFunction.name); + stream << GetMetaDataCode<4>(staticFunction); + } } - // TODO overload support + for (const auto& variable : clazz.variables) { const std::string variableName = GetFullName(variable); const std::string fieldAccessStr = variable.fieldAccess != FieldAccess::pub ? std::format(", {}", GetFieldAccessStr(variable.fieldAccess)) : ""; stream << Common::newline << Common::tab<3> << std::format(R"(.MemberVariable<&{}{}>("{}"))", variableName, fieldAccessStr, variable.name); stream << GetMetaDataCode<4>(variable); } - for (const auto& function : clazz.functions) { - const std::string functionName = GetFullName(function); - const std::string fieldAccessStr = function.fieldAccess != FieldAccess::pub ? std::format(", {}", GetFieldAccessStr(function.fieldAccess)) : ""; - stream << Common::newline << Common::tab<3> << std::format(R"(.MemberFunction<&{}{}>("{}"))", functionName, fieldAccessStr, function.name); - stream << GetMetaDataCode<4>(function); + + for (const auto memberFunctionOverloadMap = GetFunctionOverloadMap(clazz.functions); + const auto& overloads : memberFunctionOverloadMap | std::views::values) { + if (overloads.size() > 1) { + for (const auto& overload : overloads) { + const ClassFunctionInfo& function = *overload; + const std::string functionName = GetFullName(function); + const std::string shortFunctionNameWithParams = GetOverloadFunctionFullNameWithParams(function, function.name); + const std::string ptrType = GetOverloadFunctionPtrType(function, fullName); + const std::string fieldAccessStr = function.fieldAccess != FieldAccess::pub ? std::format(", {}", GetFieldAccessStr(function.fieldAccess)) : ""; + stream << Common::newline << Common::tab<3> << std::format(R"(.MemberFunction(&{}){}>("{}"))", ptrType, functionName, fieldAccessStr, shortFunctionNameWithParams); + stream << GetMetaDataCode<4>(function); + } + } else { + const ClassFunctionInfo& function = *overloads[0]; + const std::string functionName = GetFullName(function); + const std::string fieldAccessStr = function.fieldAccess != FieldAccess::pub ? std::format(", {}", GetFieldAccessStr(function.fieldAccess)) : ""; + stream << Common::newline << Common::tab<3> << std::format(R"(.MemberFunction<&{}{}>("{}"))", functionName, fieldAccessStr, function.name); + stream << GetMetaDataCode<4>(function); + } } - // TODO overload support + stream << ";" << Common::newline; - stream << Common::tab<1> << "return 0;" << Common::newline; + if (dynamic) { + stream << Common::tab<1> << "return Mirror::Internal::ScopedReleaser([]() -> void {" << Common::newline; + stream << Common::tab<2> << std::format(R"(Mirror::Registry::Get().UnloadClass("{}");)", fullName) << Common::newline; + stream << Common::tab<1> << "});" << Common::newline; + } else { + stream << Common::tab<1> << "return Mirror::Internal::ScopedReleaser();" << Common::newline; + } stream << "}();" << Common::newline; + stream << Common::newline; stream << std::format("const Mirror::Class& {}::GetStaticClass()", fullName) << Common::newline; stream << "{" << Common::newline; @@ -178,32 +294,31 @@ namespace MirrorTool { stream << Common::tab<1> << std::format("static const Mirror::Class& clazz = Mirror::Class::Get<{}>();", fullName) << Common::newline; stream << Common::tab<1> << "return clazz;" << Common::newline; stream << "}" << Common::newline; - stream << Common::newline; for (const auto& internalClass : clazz.classes) { - stream << GetClassCode(internalClass); + stream << GetClassCode(internalClass, dynamic); } return stream.str(); } - static std::string GetNamespaceClassesCode(const NamespaceInfo& ns) // NOLINT + static std::string GetNamespaceClassesCode(const NamespaceInfo& ns, bool dynamic) // NOLINT { std::stringstream stream; for (const auto& clazz : ns.classes) { - stream << GetClassCode(clazz); + stream << GetClassCode(clazz, dynamic); } for (const auto& cns : ns.namespaces) { - stream << GetNamespaceClassesCode(cns); + stream << GetNamespaceClassesCode(cns, dynamic); } return stream.str(); } - static std::string GetClassesCode(const MetaInfo& metaInfo) + static std::string GetClassesCode(const MetaInfo& metaInfo, bool dynamic) { std::stringstream stream; - stream << GetNamespaceClassesCode(metaInfo.global); + stream << GetNamespaceClassesCode(metaInfo.global, dynamic); for (const auto& ns : metaInfo.namespaces) { - stream << GetNamespaceClassesCode(ns); + stream << GetNamespaceClassesCode(ns, dynamic); } return stream.str(); } @@ -221,17 +336,69 @@ namespace MirrorTool { stream << GetMetaDataCode<4>(var); stream << ";" << Common::newline; } - for (const auto& func : ns.functions) { - const auto fullName = GetFullName(func); + for (const auto funcOverloadMap = GetFunctionOverloadMap(ns.functions); + const auto& overloads : funcOverloadMap | std::views::values) { + if (overloads.size() > 1) { + for (const auto& overload : overloads) { + const FunctionInfo& func = *overload; + const auto fullName = GetFullName(func); + const auto fullNameWithParams = GetOverloadFunctionFullNameWithParams(func, GetFullName(func));; + const auto ptrType = GetOverloadFunctionPtrType(func); + + stream << Common::newline; + stream << Common::tab<1> << "Mirror::Registry::Get()" << Common::newline; + stream << Common::tab<2> << ".Global()" << Common::newline; + stream << Common::tab<3> << std::format(R"(.Function(&{})>("{}"))", ptrType, fullName, fullNameWithParams); + stream << GetMetaDataCode<4>(func); + stream << ";" << Common::newline; + } + } else { + const FunctionInfo& func = *overloads[0]; + const auto fullName = GetFullName(func); + + stream << Common::newline; + stream << Common::tab<1> << "Mirror::Registry::Get()" << Common::newline; + stream << Common::tab<2> << ".Global()" << Common::newline; + stream << Common::tab<3> << std::format(R"(.Function<&{}>("{}"))", fullName, fullName); + stream << GetMetaDataCode<4>(func); + stream << ";" << Common::newline; + } + } + + for (const auto& cns : ns.namespaces) { + stream << GetNamespaceGlobalCode(cns); + } + return stream.str(); + } + + static std::string GetNamespaceGlobalUnloadCode(const NamespaceInfo& ns) + { + std::stringstream stream; + for (const auto& var : ns.variables) { + const auto fullName = GetFullName(var); stream << Common::newline; - stream << Common::tab<1> << "Mirror::Registry::Get()" << Common::newline; - stream << Common::tab<2> << ".Global()" << Common::newline; - stream << Common::tab<3> << std::format(R"(.Function<&{}>("{}"))", fullName, fullName); - stream << GetMetaDataCode<4>(func); - stream << ";" << Common::newline; + stream << Common::tab<2> << std::format(R"(Mirror::Registry::Get().Global().UnloadVariable("{}");)", fullName) << Common::newline; + } + + for (const auto funcOverloadMap = GetFunctionOverloadMap(ns.functions); + const auto& overloads : funcOverloadMap | std::views::values) { + if (overloads.size() > 1) { + for (const auto& overload : overloads) { + const FunctionInfo& func = *overload; + const auto fullNameWithParams = GetOverloadFunctionFullNameWithParams(func, GetFullName(func));; + + stream << Common::newline; + stream << Common::tab<2> << std::format(R"("Mirror::Registry::Get().Global().UnloadFunction("{}");")", fullNameWithParams) << Common::newline; + } + } else { + const FunctionInfo& func = *overloads[0]; + const auto fullName = GetFullName(func); + + stream << Common::newline; + stream << Common::tab<2> << std::format(R"(Mirror::Registry::Get().Global().UnloadFunction<&{}>("{}"))", fullName, fullName) << Common::newline; + } } - // TODO overload support for (const auto& cns : ns.namespaces) { stream << GetNamespaceGlobalCode(cns); @@ -239,29 +406,39 @@ namespace MirrorTool { return stream.str(); } - static std::string GetGlobalCode(const MetaInfo& metaInfo, size_t uniqueId) + static std::string GetGlobalCode(const MetaInfo& metaInfo, size_t uniqueId, bool dynamic) { std::stringstream stream; stream << Common::newline; - stream << std::format("int _globalRegistry_{} = []() -> int", uniqueId) << Common::newline; + stream << std::format("Mirror::Internal::ScopedReleaser _globalRegistry_{} = []() -> Mirror::Internal::ScopedReleaser", uniqueId) << Common::newline; stream << "{"; stream << GetNamespaceGlobalCode(metaInfo.global); for (const auto& ns : metaInfo.namespaces) { stream << GetNamespaceGlobalCode(ns); } stream << Common::newline; - stream << Common::tab<1> << "return 0;" << Common::newline; + if (dynamic) { + stream << Common::tab<1> << "return Mirror::Internal::ScopedReleaser([]() -> void {" << Common::newline; + stream << GetNamespaceGlobalCode(metaInfo.global); + for (const auto& ns : metaInfo.namespaces) { + stream << GetNamespaceGlobalCode(ns); + } + stream << Common::tab<1> << "});" << Common::newline; + } else { + stream << Common::tab<1> << "return Mirror::Internal::ScopedReleaser();" << Common::newline; + } stream << "}();" << Common::newline; return stream.str(); } } namespace MirrorTool { - Generator::Generator(std::string inInputFile, std::string inOutputFile, std::vector inHeaderDirs, const MetaInfo& inMetaInfo) + Generator::Generator(std::string inInputFile, std::string inOutputFile, std::vector inHeaderDirs, const MetaInfo& inMetaInfo, bool inDynamic) : metaInfo(inMetaInfo) , inputFile(std::move(inInputFile)) , outputFile(std::move(inOutputFile)) , headerDirs(std::move(inHeaderDirs)) + , dynamic(inDynamic) { } @@ -299,9 +476,9 @@ namespace MirrorTool { outFile << GetHeaderNote() << Common::newline; outFile << std::format("#include <{}>", bestMatchHeaderPath) << Common::newline; outFile << "#include " << Common::newline; - outFile << GetGlobalCode(metaInfo, uniqueId); - outFile << GetEnumsCode(metaInfo, uniqueId); - outFile << GetClassesCode(metaInfo); + outFile << GetGlobalCode(metaInfo, uniqueId, dynamic); + outFile << GetEnumsCode(metaInfo, uniqueId, dynamic); + outFile << GetClassesCode(metaInfo, dynamic); return std::make_pair(true, ""); } } diff --git a/Tool/MirrorTool/Src/Parser.cpp b/Tool/MirrorTool/Src/Parser.cpp index 942af0055..082b5d3dc 100644 --- a/Tool/MirrorTool/Src/Parser.cpp +++ b/Tool/MirrorTool/Src/Parser.cpp @@ -159,6 +159,20 @@ namespace MirrorTool { info.name = stream.str(); } + static bool NeedProcessClassFunctionCursor(const CXCursor& cursor) + { + return !clang_CXXMethod_isDeleted(cursor); + } + + static bool NeedProcessConstructorCursor(const CXCursor& cursor) + { + return NeedProcessClassFunctionCursor(cursor) + && !clang_CXXConstructor_isDefaultConstructor(cursor) + && !clang_CXXConstructor_isCopyConstructor(cursor) + && !clang_CXXConstructor_isMoveConstructor(cursor) + && !clang_CXXConstructor_isConvertingConstructor(cursor); + } + DeclareVisitor(GlobalVariableVisitor, VariableInfo) { FetchCursorInfo(GlobalVariableVisitor, cursor); @@ -218,7 +232,7 @@ namespace MirrorTool { ApplyMetaFilter(variables, propertyMetaTag); clang_disposeString(typeSpelling); - } else if (kind == CXCursor_CXXMethod) { + } else if (kind == CXCursor_CXXMethod && NeedProcessClassFunctionCursor(cursor)) { bool isStatic = static_cast(clang_CXXMethod_isStatic(cursor)); CXType retType = clang_getCursorResultType(cursor); CXString retTypeSpelling = clang_getTypeSpelling(retType); @@ -234,7 +248,7 @@ namespace MirrorTool { ApplyMetaFilter(functions, functionMetaTag); clang_disposeString(retTypeSpelling); - } else if (kind == CXCursor_Constructor) { + } else if (kind == CXCursor_Constructor && NeedProcessConstructorCursor(cursor)) { ClassConstructorInfo constructorInfo; constructorInfo.outerName = GetOuterName(context.outerName, context.name); constructorInfo.fieldAccess = context.lastFieldAccess; diff --git a/Tool/MirrorTool/Test/Main.cpp b/Tool/MirrorTool/Test/Main.cpp index 314b0eee4..5d4bb9d2f 100644 --- a/Tool/MirrorTool/Test/Main.cpp +++ b/Tool/MirrorTool/Test/Main.cpp @@ -164,7 +164,7 @@ TEST(MirrorTest, GeneratorTest) auto [parseSuccess, parseResultOrError] = parser.Parse(); ASSERT_TRUE(parseSuccess); - const Generator generator("../Test/Resource/Mirror/MirrorToolInput.h", "../Test/Generated/Mirror/MirrorToolTest.generated.cpp", { "../" }, std::get(parseResultOrError)); + const Generator generator("../Test/Resource/Mirror/MirrorToolInput.h", "../Test/Generated/Mirror/MirrorToolTest.generated.cpp", { "../" }, std::get(parseResultOrError), false); auto [generateSuccess, generateResultOrError] = generator.Generate(); ASSERT_EQ(generateSuccess, true); }