diff --git a/CMake/Common.cmake b/CMake/Common.cmake index 7e0f4ef0a..e9c8d680a 100644 --- a/CMake/Common.cmake +++ b/CMake/Common.cmake @@ -1,8 +1,9 @@ -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_UNITY_BUILD ON) - option(BUILD_EDITOR "Build Explosion editor" ON) option(CI "Build in CI" OFF) +option(USE_UNITY_BUILD "Use unity build" OFF) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_UNITY_BUILD ${USE_UNITY_BUILD}) get_cmake_property(GENERATOR_IS_MULTI_CONFIG GENERATOR_IS_MULTI_CONFIG) if (${GENERATOR_IS_MULTI_CONFIG}) diff --git a/Editor/Qml/EFloatInput.qml b/Editor/Qml/EFloatInput.qml new file mode 100644 index 000000000..eb4251054 --- /dev/null +++ b/Editor/Qml/EFloatInput.qml @@ -0,0 +1,16 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Controls.Basic + +ETextField { + property double from: 0.0 + property double to: 1.0 + + id: root + implicitWidth: 100 + + validator: DoubleValidator { + bottom: root.from + top: root.to + } +} diff --git a/Editor/Qml/ENumberInput.qml b/Editor/Qml/EIntegerInput.qml similarity index 82% rename from Editor/Qml/ENumberInput.qml rename to Editor/Qml/EIntegerInput.qml index 368158367..146815399 100644 --- a/Editor/Qml/ENumberInput.qml +++ b/Editor/Qml/EIntegerInput.qml @@ -24,7 +24,11 @@ Item { id: textField implicitWidth: 40 text: spinBox.textFromValue(spinBox.value) + readOnly: !root.editable validator: spinBox.validator + onAccepted: { + root.value = spinBox.valueFromText(displayText, Qt.locale()) + } } down.indicator: Rectangle { @@ -33,7 +37,7 @@ Item { implicitWidth: 25 implicitHeight: textField.implicitHeight radius: 5 - color: spinBox.down.hovered ? ETheme.secondaryBgColor : ETheme.primaryBgColor + color: spinBox.down.hovered ? ETheme.primaryColor : ETheme.primaryBgColor EIcon { name: 'minus' @@ -47,7 +51,7 @@ Item { implicitWidth: 25 implicitHeight: textField.implicitHeight radius: 5 - color: spinBox.up.hovered ? ETheme.secondaryBgColor : ETheme.primaryBgColor + color: spinBox.up.hovered ? ETheme.primaryColor : ETheme.primaryBgColor EIcon { name: 'add' diff --git a/Editor/Qml/ETextField.qml b/Editor/Qml/ETextField.qml index d87de0f92..72412fbad 100644 --- a/Editor/Qml/ETextField.qml +++ b/Editor/Qml/ETextField.qml @@ -3,9 +3,11 @@ import QtQuick.Controls import QtQuick.Controls.Basic Item { + readonly property string displayText: textField.displayText property string placeHolderText: '' property int wrapMode: TextInput.NoWrap property string text: textField.text + property bool readOnly: textField.readOnly property var validator: null signal accepted() @@ -27,6 +29,7 @@ Item { font.pixelSize: ETheme.contentFontSize font.family: ETheme.fontFamily wrapMode: root.wrapMode + readOnly: root.readOnly validator: root.validator onAccepted: root.accepted() diff --git a/Editor/Qml/ETheme.qml b/Editor/Qml/ETheme.qml index eea6eea61..6bcba1614 100644 --- a/Editor/Qml/ETheme.qml +++ b/Editor/Qml/ETheme.qml @@ -6,11 +6,11 @@ QtObject { property color bgColor: Qt.color('#212121') property color primaryBgColor: Qt.color('#3a3939') property color primaryColor: Qt.color('#e74c3c') - property color primaryHoverColor: Qt.color('#ce4d40') + property color primaryHoverColor: Qt.color('#f55b4b') property color primaryFocusColor: Qt.color('#c0392b') property color primaryDisabledColor: Qt.color('#4b4a49') property color secondaryColor: Qt.color('#d58845') - property color secondaryHoverColor: Qt.color('#d58845') + property color secondaryHoverColor: Qt.color('#ec9d58') property color secondaryFocusColor: Qt.color('#9b6a40') property color secondaryBgColor: Qt.color('#8e8e8e') property color disabledColor: Qt.color('#676563') diff --git a/Editor/Qml/EWidgetSamples.qml b/Editor/Qml/EWidgetSamples.qml index b8d56034d..24014e122 100644 --- a/Editor/Qml/EWidgetSamples.qml +++ b/Editor/Qml/EWidgetSamples.qml @@ -147,7 +147,7 @@ Rectangle { RowLayout { Layout.leftMargin: 5 - Layout.topMargin: 35 + Layout.topMargin: 15 EText { text: 'Texts' @@ -196,7 +196,7 @@ Rectangle { RowLayout { Layout.leftMargin: 5 - Layout.topMargin: 35 + Layout.topMargin: 15 EText { text: 'Icons' @@ -276,7 +276,7 @@ Rectangle { RowLayout { Layout.leftMargin: 5 - Layout.topMargin: 35 + Layout.topMargin: 15 EText { text: 'Switches' style: EText.Style.Title1 @@ -317,7 +317,7 @@ Rectangle { RowLayout { Layout.leftMargin: 5 - Layout.topMargin: 35 + Layout.topMargin: 15 EText { text: 'TextInput' style: EText.Style.Title1 @@ -393,9 +393,9 @@ Rectangle { RowLayout { Layout.leftMargin: 5 - Layout.topMargin: 35 + Layout.topMargin: 15 EText { - text: 'NumberInput' + text: 'IntegerInput' style: EText.Style.Title1 } } @@ -404,7 +404,7 @@ Rectangle { Layout.margins: 5 RowLayout { - ENumberInput {} + EIntegerInput {} EText { Layout.leftMargin: 5 @@ -413,7 +413,7 @@ Rectangle { } RowLayout { - ENumberInput { + EIntegerInput { from: 0 to: 10 } @@ -423,6 +423,43 @@ Rectangle { text: 'Limit 0-10' } } + + RowLayout { + EIntegerInput { + editable: true + } + + EText { + Layout.leftMargin: 5 + text: 'Editable' + } + } + } + + RowLayout { + Layout.leftMargin: 5 + Layout.topMargin: 15 + EText { + text: 'FloatInput' + style: EText.Style.Title1 + } + } + + ColumnLayout { + Layout.margins: 5 + + RowLayout { + EFloatInput { + onAccepted: { + console.log('value accepted, value=' + text) + } + } + + EText { + Layout.leftMargin: 5 + text: 'Default' + } + } } } } diff --git a/Editor/Src/Main.cpp b/Editor/Src/Main.cpp index 86efc0782..0cf2d353f 100644 --- a/Editor/Src/Main.cpp +++ b/Editor/Src/Main.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include diff --git a/Editor/Src/Widget/GraphicsSampleWidget.cpp b/Editor/Src/Widget/GraphicsSampleWidget.cpp index 20e02f8ec..fe0785606 100644 --- a/Editor/Src/Widget/GraphicsSampleWidget.cpp +++ b/Editor/Src/Widget/GraphicsSampleWidget.cpp @@ -2,6 +2,8 @@ // Created by Kindem on 2025/3/16. // +#include + #include #include #include // NOLINT @@ -26,7 +28,7 @@ namespace Editor { RecreateSwapChain(width(), height()); Render::ShaderCompileOptions shaderCompileOptions; - shaderCompileOptions.includePaths = {"../Shader/Engine"}; + shaderCompileOptions.includeDirectories = {"../Shader/Engine"}; shaderCompileOptions.byteCodeType = GetDevice().GetGpu().GetInstance().GetRHIType() == RHI::RHIType::directX12 ? Render::ShaderByteCodeType::dxil : Render::ShaderByteCodeType::spirv; shaderCompileOptions.withDebugInfo = static_cast(BUILD_CONFIG_DEBUG); // NOLINT diff --git a/Editor/Src/Widget/GraphicsWidget.cpp b/Editor/Src/Widget/GraphicsWidget.cpp index d9674caea..e1a7820c9 100644 --- a/Editor/Src/Widget/GraphicsWidget.cpp +++ b/Editor/Src/Widget/GraphicsWidget.cpp @@ -4,6 +4,7 @@ #include #include // NOLINT +#include namespace Editor { GraphicsWidget::GraphicsWidget(QWidget* inParent) diff --git a/Engine/Source/Common/Include/Common/Container.h b/Engine/Source/Common/Include/Common/Container.h index 7a1301808..0a91eebda 100644 --- a/Engine/Source/Common/Include/Common/Container.h +++ b/Engine/Source/Common/Include/Common/Container.h @@ -26,6 +26,7 @@ namespace Common { template static size_t SwapWithLastAndDelete(std::vector& vector, size_t index); template static std::vector GetIntersection(const std::vector& lhs, const std::vector& rhs); template static std::vector GetDifferences(const std::vector& lhs, const std::vector& rhs); + template static std::vector Combine(const std::vector& lhs, const std::vector& rhs); }; class SetUtils { @@ -335,6 +336,15 @@ namespace Common { return result; } + template + std::vector VectorUtils::Combine(const std::vector& lhs, const std::vector& rhs) + { + std::vector result; + result.insert(result.end(), lhs.begin(), lhs.end()); + result.insert(result.end(), rhs.begin(), rhs.end()); + return result; + } + template std::unordered_set SetUtils::GetIntersection(const std::unordered_set& lhs, const std::unordered_set& rhs) { @@ -669,7 +679,7 @@ namespace Common { } else if constexpr (std::is_copy_assignable_v) { TypedMemory(i) = TypedMemory(i + 1); } else { - Assert(false); + QuickFail(); } } InplaceDestruct(size - 1); @@ -699,7 +709,7 @@ namespace Common { } else if constexpr (std::is_copy_assignable_v) { TypedMemory(inIndex) = TypedMemory(size - 1); } else { - Assert(false); + QuickFail(); } InplaceDestruct(size - 1); size--; @@ -934,7 +944,7 @@ namespace Common { } else if constexpr (std::is_copy_constructible_v) { InplaceConstruct(i, TypedMemory(i - 1)); } else { - Assert(false); + QuickFail(); } } else { if constexpr (std::is_move_assignable_v) { @@ -942,7 +952,7 @@ namespace Common { } else if constexpr (std::is_copy_assignable_v) { TypedMemory(i) = TypedMemory(i - 1); } else { - Assert(false); + QuickFail(); } } } diff --git a/Engine/Source/Common/Include/Common/FileSystem.h b/Engine/Source/Common/Include/Common/FileSystem.h index fbd9f0275..57d09b989 100644 --- a/Engine/Source/Common/Include/Common/FileSystem.h +++ b/Engine/Source/Common/Include/Common/FileSystem.h @@ -38,6 +38,7 @@ namespace Common { size_t TraverseRecurse(const TraverseFunc& inFunc) const; void CopyTo(const Path& inPath) const; void MakeDir() const; + void Fixup(); Path operator/(const Path& inPath) const; Path operator/(const std::string& inPath) const; diff --git a/Engine/Source/Common/Src/FileSystem.cpp b/Engine/Source/Common/Src/FileSystem.cpp index 01f511470..d34643122 100644 --- a/Engine/Source/Common/Src/FileSystem.cpp +++ b/Engine/Source/Common/Src/FileSystem.cpp @@ -8,7 +8,7 @@ namespace Common::Internal { std::filesystem::path GetUnixStylePath(const std::string& inPath) { - return Common::StringUtils::Replace(std::filesystem::weakly_canonical(inPath).string(), "\\", "/"); + return StringUtils::Replace(inPath, "\\", "/"); } std::filesystem::path GetUnixStylePath(const std::filesystem::path& inPath) @@ -188,4 +188,9 @@ namespace Common { { std::filesystem::create_directories(path); } + + void Path::Fixup() + { + path = Internal::GetUnixStylePath(path); + } } // namespace Common diff --git a/Engine/Source/Core/Include/Core/Paths.h b/Engine/Source/Core/Include/Core/Paths.h index f012f0f58..6f0d48445 100644 --- a/Engine/Source/Core/Include/Core/Paths.h +++ b/Engine/Source/Core/Include/Core/Paths.h @@ -30,6 +30,7 @@ namespace Core { static Common::Path EngineCacheDir(); static Common::Path EngineLogDir(); static Common::Path EnginePluginDir(); + static Common::Path EnginePluginDir(const std::string& pluginName); static Common::Path EnginePluginAssetDir(const std::string& pluginName); static Common::Path GameRootDir(); static Common::Path GameAssetDir(); @@ -38,13 +39,23 @@ namespace Core { static Common::Path GameCacheDir(); static Common::Path GameLogDir(); static Common::Path GamePluginDir(); + static Common::Path GamePluginDir(const std::string& pluginName); static Common::Path GamePluginAssetDir(const std::string& pluginName); static Common::Path EngineCMakeSourceDir(); static Common::Path EngineCMakeBinaryDir(); +#if BUILD_TEST + static Common::Path EngineTestDir(); +#endif + static bool IsEnginePath(const Common::Path& inPath); + static bool IsGamePath(const Common::Path& inPath); + static bool IsEnginePluginPath(const Common::Path& inPath); + static bool IsGamePluginPath(const Common::Path& inPath); #if BUILD_TEST - static Common::Path EngineTest(); + static bool IsEngineTestPath(const Common::Path& inPath); #endif + static Common::Path Translate(const Common::Path& inPath); + static Common::Path TranslateAsset(const Common::Path& inPath); private: static Common::Path executablePath; diff --git a/Engine/Source/Core/Include/Core/Uri.h b/Engine/Source/Core/Include/Core/Uri.h index d58a44a16..0641f8c97 100644 --- a/Engine/Source/Core/Include/Core/Uri.h +++ b/Engine/Source/Core/Include/Core/Uri.h @@ -32,9 +32,6 @@ namespace Core { class CORE_API FileUriParser { public: explicit FileUriParser(const Uri& inUri); - bool IsEngineFile() const; - bool IsGameFile() const; - bool IsRegularFile() const; Common::Path Parse() const; private: @@ -44,16 +41,8 @@ namespace Core { class CORE_API AssetUriParser { public: explicit AssetUriParser(const Uri& inUri); - bool IsEngineAsset() const; - bool IsGameAsset() const; - bool IsEnginePluginAsset() const; - bool IsGamePluginAsset() const; Common::Path Parse() const; -#if BUILD_TEST - bool IsEngineTestAsset() const; -#endif - private: std::string content; }; diff --git a/Engine/Source/Core/Src/Paths.cpp b/Engine/Source/Core/Src/Paths.cpp index 8bd794025..23b16931f 100644 --- a/Engine/Source/Core/Src/Paths.cpp +++ b/Engine/Source/Core/Src/Paths.cpp @@ -3,6 +3,7 @@ // #include +#include #include namespace Core { @@ -90,9 +91,14 @@ namespace Core { return EngineRootDir() / "Plugin"; } + Common::Path Paths::EnginePluginDir(const std::string& pluginName) + { + return EngineRootDir() / pluginName; + } + Common::Path Paths::EnginePluginAssetDir(const std::string& pluginName) { - return EnginePluginDir() / pluginName / "Asset"; + return EnginePluginDir(pluginName) / "Asset"; } Common::Path Paths::GameRootDir() @@ -130,6 +136,11 @@ namespace Core { return GameRootDir() / "Plugin"; } + Common::Path Paths::GamePluginDir(const std::string& pluginName) + { + return GameRootDir() / pluginName; + } + Common::Path Paths::GamePluginAssetDir(const std::string& pluginName) { return GamePluginDir() / pluginName / "Asset"; @@ -145,8 +156,83 @@ namespace Core { return ENGINE_CMAKE_BINARY_DIRECTORY; } + bool Paths::IsEnginePath(const Common::Path& inPath) + { + return Common::StringUtils::RegexMatch(inPath.String(), R"(Engine/.*)"); + } + + bool Paths::IsGamePath(const Common::Path& inPath) + { + return Common::StringUtils::RegexMatch(inPath.String(), R"(Game/.*)"); + } + + bool Paths::IsEnginePluginPath(const Common::Path& inPath) + { + return Common::StringUtils::RegexMatch(inPath.String(), R"(Engine/Plugin/.*)"); + } + + bool Paths::IsGamePluginPath(const Common::Path& inPath) + { + return Common::StringUtils::RegexMatch(inPath.String(), R"(Game/Plugin/.*)"); + } + +#if BUILD_TEST + bool Paths::IsEngineTestPath(const Common::Path& inPath) + { + return Common::StringUtils::RegexMatch(inPath.String(), R"(Engine/Test/.*)"); + } +#endif + + Common::Path Paths::Translate(const Common::Path& inPath) + { + Common::Path result = inPath; +#if BUILD_TEST + if (IsEngineTestPath(inPath)) { + result = EngineTestDir() / Common::StringUtils::AfterFirst(inPath.String(), "Engine/Test/"); + } else if (IsEnginePluginPath(inPath)) { +#else + if (IsEnginePluginPath(inPath)) { +#endif + const std::string pathWithPluginName = Common::StringUtils::AfterFirst(inPath.String(), "Engine/Plugin/"); + result = EnginePluginDir(Common::StringUtils::BeforeFirst(pathWithPluginName, "/")) / Common::StringUtils::AfterFirst(pathWithPluginName, "/"); + } else if (IsGamePluginPath(inPath)) { + const std::string pathWithPluginName = Common::StringUtils::AfterFirst(inPath.String(), "Game/Plugin/"); + result = GamePluginDir(Common::StringUtils::BeforeFirst(pathWithPluginName, "/")) / Common::StringUtils::AfterFirst(pathWithPluginName, "/"); + } else if (IsEnginePath(inPath)) { + result = EngineRootDir() / Common::StringUtils::AfterFirst(inPath.String(), "Engine/"); + } else if (IsGamePath(inPath)) { + result = GameRootDir() / Common::StringUtils::AfterFirst(inPath.String(), "Game/"); + } + return result; + } + + Common::Path Paths::TranslateAsset(const Common::Path& inPath) + { + Common::Path result; +#if BUILD_TEST + if (IsEngineTestPath(inPath)) { + result = EngineTestDir() / Common::StringUtils::AfterFirst(inPath.String(), "Engine/Test/"); + } else if (IsEnginePluginPath(inPath)) { +#else + if (IsEnginePluginPath(inPath)) { +#endif + const std::string pathWithPluginName = Common::StringUtils::AfterFirst(inPath.String(), "Engine/Plugin/"); + result = EnginePluginAssetDir(Common::StringUtils::BeforeFirst(pathWithPluginName, "/")) / Common::StringUtils::AfterFirst(pathWithPluginName, "/"); + } else if (IsGamePluginPath(inPath)) { + const std::string pathWithPluginName = Common::StringUtils::AfterFirst(inPath.String(), "Game/Plugin/"); + result = GamePluginAssetDir(Common::StringUtils::BeforeFirst(pathWithPluginName, "/")) / Common::StringUtils::AfterFirst(pathWithPluginName, "/"); + } else if (IsEnginePath(inPath)) { + result = EngineAssetDir() / Common::StringUtils::AfterFirst(inPath.String(), "Engine/"); + } else if (IsGamePath(inPath)) { + result = GameAssetDir() / Common::StringUtils::AfterFirst(inPath.String(), "Game/"); + } else { + QuickFail(); + } + return result; + } + #if BUILD_TEST - Common::Path Paths::EngineTest() + Common::Path Paths::EngineTestDir() { return EngineRootDir() / "Test"; } diff --git a/Engine/Source/Core/Src/Thread.cpp b/Engine/Source/Core/Src/Thread.cpp index e3a56b3a1..0acc6019e 100644 --- a/Engine/Source/Core/Src/Thread.cpp +++ b/Engine/Source/Core/Src/Thread.cpp @@ -2,10 +2,13 @@ // Created by johnk on 2025/1/17. // +#include + #include namespace Core { - static thread_local auto currentTag = ThreadTag::unknown; + static auto mainThreadId = std::this_thread::get_id(); + static thread_local auto currentTag = std::this_thread::get_id() == mainThreadId ? ThreadTag::game : ThreadTag::unknown; static thread_local uint64_t frameNumber = 0; void ThreadContext::SetTag(ThreadTag inTag) diff --git a/Engine/Source/Core/Src/Uri.cpp b/Engine/Source/Core/Src/Uri.cpp index 9ed6ed011..b8bfe663f 100644 --- a/Engine/Source/Core/Src/Uri.cpp +++ b/Engine/Source/Core/Src/Uri.cpp @@ -56,32 +56,9 @@ namespace Core { Assert(inUri.Protocol() == UriProtocol::file); } - bool FileUriParser::IsEngineFile() const - { - return Common::StringUtils::RegexMatch(content, R"(Engine/.*)"); - } - - bool FileUriParser::IsGameFile() const - { - return Common::StringUtils::RegexMatch(content, R"(Game/.*)"); - } - - bool FileUriParser::IsRegularFile() const - { - return !IsEngineFile() && !IsGameFile(); - } - Common::Path FileUriParser::Parse() const { - Common::Path path; - if (IsEngineFile()) { - path = Paths::EngineRootDir() / Common::StringUtils::AfterFirst(content, "Engine/"); - } else if (IsGameFile()) { - path = Paths::GameRootDir() / Common::StringUtils::AfterFirst(content, "Game/"); - } else { - path = content; - } - return path; + return Paths::Translate(content); } AssetUriParser::AssetUriParser(const Uri& inUri) @@ -90,55 +67,8 @@ namespace Core { Assert(inUri.Protocol() == UriProtocol::asset); } - bool AssetUriParser::IsEngineAsset() const - { - return Common::StringUtils::RegexMatch(content, R"(Engine/.*)"); - } - - bool AssetUriParser::IsGameAsset() const - { - return Common::StringUtils::RegexMatch(content, R"(Game/.*)"); - } - - bool AssetUriParser::IsEnginePluginAsset() const - { - return Common::StringUtils::RegexMatch(content, R"(Engine/Plugin/.*)"); - } - - bool AssetUriParser::IsGamePluginAsset() const - { - return Common::StringUtils::RegexMatch(content, R"(Game/Plugin/.*)"); - } - Common::Path AssetUriParser::Parse() const { - Common::Path path; -#if BUILD_TEST - if (IsEngineTestAsset()) { - path = Paths::EngineTest() / Common::StringUtils::AfterFirst(content, "Engine/Test/"); - } else if (IsEnginePluginAsset()) { -#else - if (IsEnginePluginAsset()) { -#endif - const std::string pathWithPluginName = Common::StringUtils::AfterFirst(content, "Engine/Plugin/"); - path = Paths::EnginePluginAssetDir(Common::StringUtils::BeforeFirst(pathWithPluginName, "/")) / Common::StringUtils::AfterFirst(pathWithPluginName, "/"); - } else if (IsGamePluginAsset()) { - const std::string pathWithPluginName = Common::StringUtils::AfterFirst(content, "Game/Plugin/"); - path = Paths::GamePluginAssetDir(Common::StringUtils::BeforeFirst(pathWithPluginName, "/")) / Common::StringUtils::AfterFirst(pathWithPluginName, "/"); - } else if (IsEngineAsset()) { - path = Paths::EngineAssetDir() / Common::StringUtils::AfterFirst(content, "Engine/"); - } else if (IsGameAsset()) { - path = Paths::GameAssetDir() / Common::StringUtils::AfterFirst(content, "Game/"); - } else { - AssertWithReason(false, "bad asset uri"); - } - return path + ".expa"; - } - -#if BUILD_TEST - bool AssetUriParser::IsEngineTestAsset() const - { - return Common::StringUtils::RegexMatch(content, R"(Engine/Test/.*)"); + return Paths::TranslateAsset(content) + ".expa"; } -#endif } diff --git a/Engine/Source/RHI/Include/RHI/Common.h b/Engine/Source/RHI/Include/RHI/Common.h index 9518b4baf..7c6f9824f 100644 --- a/Engine/Source/RHI/Include/RHI/Common.h +++ b/Engine/Source/RHI/Include/RHI/Common.h @@ -22,6 +22,8 @@ #define FCIMPL_ITEM(A, B) if (flags & A) { result |= B; } #define FCIMPL_END(B) return result; }; +#define ALIGN_AS_GPU alignas(16) + namespace RHI { enum class RHIType : uint8_t { directX12, diff --git a/Engine/Source/Render/Include/Render/RenderCache.h b/Engine/Source/Render/Include/Render/RenderCache.h index 03ef601e7..eab7e27dd 100644 --- a/Engine/Source/Render/Include/Render/RenderCache.h +++ b/Engine/Source/Render/Include/Render/RenderCache.h @@ -74,17 +74,17 @@ namespace Render { }; struct ComputePipelineShaderSet { - Render::ShaderInstance computeShader; + ShaderInstance computeShader; uint64_t Hash() const; }; struct RasterPipelineShaderSet { - Render::ShaderInstance vertexShader; - Render::ShaderInstance pixelShader; - Render::ShaderInstance geometryShader; - Render::ShaderInstance domainShader; - Render::ShaderInstance hullShader; + ShaderInstance vertexShader; + ShaderInstance pixelShader; + ShaderInstance geometryShader; + ShaderInstance domainShader; + ShaderInstance hullShader; uint64_t Hash() const; }; @@ -173,7 +173,7 @@ namespace Render { private: struct ShaderInstancePack { RHI::ShaderStageBits stage; - const Render::ShaderInstance* instance; + const ShaderInstance* instance; }; friend class PipelineLayoutCache; @@ -233,6 +233,8 @@ namespace Render { Sampler* GetOrCreate(const RSamplerDesc& desc); private: + static std::mutex mutex; + explicit SamplerCache(RHI::Device& inDevice); RHI::Device& device; @@ -250,6 +252,8 @@ namespace Render { RasterPipelineState* GetOrCreate(const RasterPipelineStateDesc& desc); private: + static std::mutex mutex; + explicit PipelineCache(RHI::Device& inDevice); RHI::Device& device; @@ -269,6 +273,8 @@ namespace Render { void Forfeit(); private: + static std::mutex mutex; + explicit ResourceViewCache(RHI::Device& inDevice); struct BufferViewCache { @@ -300,6 +306,8 @@ namespace Render { private: using AllocateFrameNumber = uint64_t; + static std::mutex mutex; + explicit BindGroupCache(RHI::Device& inDevice); RHI::Device& device; diff --git a/Engine/Source/Render/Include/Render/Shader.h b/Engine/Source/Render/Include/Render/Shader.h index 65ec149ed..888453299 100644 --- a/Engine/Source/Render/Include/Render/Shader.h +++ b/Engine/Source/Render/Include/Render/Shader.h @@ -4,587 +4,522 @@ #pragma once +#include #include -#include #include #include +#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include -namespace Render { - class Shader {}; +#define ShaderTypeInfo(inClass, inStage, inSourceFile, inEntryPoint) \ + public: \ + \ + static const inClass& Get(); \ + \ + static constexpr const char* name = #inClass; \ + static constexpr RHI::ShaderStageBits stage = inStage; \ + static constexpr const char* sourceFile = inSourceFile; \ + static constexpr const char* entryPoint = inEntryPoint; \ + +#define DeclBoolVariantField(inName, inMacro, inDefaultValue) \ + struct inName { \ + static constexpr Render::VariantFieldType type = Render::VariantFieldType::vfBool; \ + static constexpr const char* macro = #inMacro; \ + static constexpr bool defaultValue = inDefaultValue; \ + }; \ - using ShaderTypeKey = uint64_t; - using VariantKey = uint64_t; +#define DeclRangedIntVariantField(inName, inMacro, inDefaultValue, inFrom, inTo) \ + struct inName { \ + static constexpr Render::VariantFieldType type = Render::VariantFieldType::vfRangedInt; \ + static constexpr const char* macro = #inMacro; \ + static constexpr int32_t defaultValue = inDefaultValue; \ + static constexpr int32_t from = inFrom; \ + static constexpr int32_t to = inTo; \ + }; \ - struct ShaderReflectionData { - using VertexSemantic = std::string; - using ResourceBindingName = std::string; - using LayoutIndex = uint8_t; - using LayoutAndResourceBinding = std::pair; +#define MakeVariantFieldVec(...) \ + using VariantFieldVec = std::tuple<__VA_ARGS__>; \ - ShaderReflectionData(); - ShaderReflectionData(const ShaderReflectionData& inOther); - ShaderReflectionData(ShaderReflectionData&& inOther) noexcept; - ShaderReflectionData& operator=(const ShaderReflectionData& inOther); +#define EmptyVariantFieldVec \ + using VariantFieldVec = std::tuple<>; \ - const RHI::PlatformVertexBinding& QueryVertexBindingChecked(const VertexSemantic& inSemantic) const; - const LayoutAndResourceBinding& QueryResourceBindingChecked(const ResourceBindingName& inName) const; +#define BeginIncludeDirectories \ + static constexpr auto includeDirectories = std::to_array({ \ - std::unordered_map vertexBindings; - std::unordered_map resourceBindings; - }; +#define EndIncludeDirectories \ + }); \ - struct ShaderArchive { - std::vector byteCode; - ShaderReflectionData reflectionData; - }; +#define EmptyIncludeDirectories \ + static constexpr auto includeDirectories = std::array {}; \ - class IShaderType { - public: - virtual ~IShaderType(); - virtual const std::string& GetName() = 0; - virtual ShaderTypeKey GetKey() = 0; - virtual RHI::ShaderStageBits GetStage() = 0; - virtual const std::string& GetEntryPoint() = 0; - virtual const std::string& GetCode() = 0; - virtual uint32_t GetVariantNum() = 0; - virtual const std::vector& GetVariants() = 0; - virtual const std::vector& GetDefinitions(VariantKey variantKey) = 0; - virtual void Reload() = 0; - virtual void Invalidate() = 0; - }; +#define ImplementStaticShaderType(inClass) \ + static inClass __staticShaderTypeInstance_##inClass = inClass(); \ + \ + const inClass& inClass::Get() { \ + return __staticShaderTypeInstance_##inClass; \ + } \ - struct ShaderInstance { - RHI::ShaderModule* rhiHandle = nullptr; - ShaderTypeKey typeKey = 0; - VariantKey variantKey = 0; - const ShaderReflectionData* reflectionData = nullptr; +#define VertexFactoryTypeInfo(inClass, inSourceFile) \ + public: \ + \ + static const inClass& Get(); \ + \ + static constexpr const char* name = #inClass; \ + static constexpr const char* sourceFile = inSourceFile; \ - bool IsValid() const; - uint64_t Hash() const; - }; +#define DeclVertexInput(inType, inName, inFormat, inOffset) \ + struct inType { \ + static constexpr const char* name = #inName; \ + static constexpr RHI::VertexFormat format = inFormat; \ + static constexpr uint32_t offset = inOffset; \ + }; \ - using ShaderArchivePackage = std::unordered_map; +#define MakeVertexInputVec(...) \ + using VertexFactoryInputVec = std::tuple<__VA_ARGS__>; \ - class ShaderArchiveStorage { - public: - static ShaderArchiveStorage& Get(); - ShaderArchiveStorage(); - ~ShaderArchiveStorage(); - NonCopyable(ShaderArchiveStorage) +#define BeginSupportedMaterialTypes \ + static constexpr auto supportedMaterialTypes = std::to_array({ \ - void UpdateShaderArchivePackage(ShaderTypeKey shaderTypeKey, ShaderArchivePackage&& shaderArchivePackage); - const ShaderArchivePackage& GetShaderArchivePackage(ShaderTypeKey shaderTypeKey); - void InvalidateAll(); - void Invalidate(ShaderTypeKey shaderTypeKey); +#define EndSupportedMaterialTypes \ + }); \ - private: - std::unordered_map shaderArchivePackages; - }; +#define ImplementStaticVertexFactoryType(inClass) \ + static inClass __staticVertexFactoryTypeInstance_##inClass = inClass(); \ + \ + const inClass& inClass::Get() { \ + return __staticVertexFactoryTypeInstance_##inClass; \ + } \ + +namespace Render::Internal { + static uint64_t MakeTypeKeyFromName(const std::string& inName); } namespace Render { - class GlobalShader : public Shader {}; + enum class VariantFieldType : uint8_t { + vfBool, + vfRangedInt, + max + }; - template - class GlobalShaderType final : public IShaderType { - public: - static GlobalShaderType& Get(); + struct ShaderBoolVariantField { + std::string macro; + bool defaultValue; - GlobalShaderType(); + bool operator==(const ShaderBoolVariantField& inRhs) const; + }; - ~GlobalShaderType() override; - NonCopyable(GlobalShaderType) + struct ShaderRangedIntVariantField { + std::string macro; + int32_t defaultValue; + std::pair range; - const std::string& GetName() override; - ShaderTypeKey GetKey() override; - RHI::ShaderStageBits GetStage() override; - const std::string& GetEntryPoint() override; - const std::string& GetCode() override; - uint32_t GetVariantNum() override; - const std::vector& GetVariants() override; - const std::vector& GetDefinitions(VariantKey variantKey) override; - void Reload() override; - void Invalidate() override; + bool operator==(const ShaderRangedIntVariantField& inRhs) const; + }; - private: - void ReloadInternal(); - void ReadCode(); - void ComputeVariants(); - void ComputeVariantDefinitions(); + using ShaderTypeKey = uint64_t; + using ShaderVariantKey = uint64_t; + using ShaderSourceHash = uint64_t; + using ShaderVariantField = std::variant; + using ShaderVariantFieldVec = std::vector; + using ShaderVariantValue = std::variant; + using ShaderVariantValueMap = std::unordered_map; - std::string name; - ShaderTypeKey key; - RHI::ShaderStageBits stage; - std::string entryPoint; - std::string code; - std::vector variants; - std::unordered_map> variantDefinitions; - }; + constexpr ShaderSourceHash shaderSourceHashNotCompiled = 0; - template - class GlobalShaderMap { + class ShaderUtils { public: - static GlobalShaderMap& Get(); + static std::vector GetAllVariants(const ShaderVariantFieldVec& inFields); + static ShaderVariantKey ComputeVariantKey(const ShaderVariantFieldVec& inFields, const ShaderVariantValueMap& inVariantSet); + static std::vector ComputeVariantDefinitions(const ShaderVariantFieldVec& inFields, const ShaderVariantValueMap& inVariantSet); + static ShaderSourceHash ComputeShaderSourceHash(const std::string& inSourceFile, const std::vector& inIncludeDirectories); - ~GlobalShaderMap(); - NonCopyable(GlobalShaderMap) + private: + static std::string GetAbsoluteIncludeFile(const std::string& inPath, const std::vector& inIncludeDirectories); + static void GatherShaderSources(std::unordered_map& outFileAndSource, const std::string& inSourceFile, const std::vector& inIncludeDirectories); + }; - void Invalidate(); - ShaderInstance GetShaderInstance(RHI::Device& device, const typename T::VariantSet& variantSet); + struct VertexFactoryInput { + std::string name; + RHI::VertexFormat format; + uint32_t offset; - private: - GlobalShaderMap(); + bool operator==(const VertexFactoryInput& inRhs) const; + }; - [[nodiscard]] const ShaderArchive& GetArchive(const typename T::VariantSet& variantSet) const; + using VertexFactoryInputVec = std::vector; + using VertexFactoryTypeKey = uint64_t; - std::unordered_map>> shaderModules; + enum class MaterialType : uint8_t { + surface, + volume, + postProcess, + max }; - class GlobalShaderRegistry { + class VertexFactoryType { public: - static GlobalShaderRegistry& Get(); + virtual ~VertexFactoryType(); - GlobalShaderRegistry(); - ~GlobalShaderRegistry(); - NonCopyable(GlobalShaderRegistry) + virtual const std::string& GetName() const = 0; + virtual const std::string& GetSourceFile() const = 0; + virtual const VertexFactoryInputVec& GetVertexInputs() const = 0; + virtual const ShaderVariantFieldVec& GetVariantFields() const = 0; + virtual bool SupportMaterialType(MaterialType inType) const = 0; - template - void Register(); + VertexFactoryTypeKey GetKey() const; - const std::vector& GetShaderTypes() const; - // call this func before device release - void Invalidate() const; - // call this func after shader reloaded - void ReloadAll() const; + protected: + explicit VertexFactoryType(VertexFactoryTypeKey inKey); private: - std::vector shaderTypes; + VertexFactoryTypeKey key; }; - class BoolShaderVariantFieldImpl { + template + class StaticVertexFactoryType : public VertexFactoryType { public: - using ValueType = bool; + StaticVertexFactoryType(); + ~StaticVertexFactoryType() override; + + const std::string& GetName() const override; + const std::string& GetSourceFile() const override; + const VertexFactoryInputVec& GetVertexInputs() const override; + const ShaderVariantFieldVec& GetVariantFields() const override; + bool SupportMaterialType(MaterialType inType) const override; - static constexpr std::pair valueRange = { 0, 1 }; - static constexpr ValueType defaultValue = false; + private: + std::string name; + std::string sourceFile; + VertexFactoryInputVec inputs; + ShaderVariantFieldVec variantFields; + std::unordered_set supportedMaterialTypes; + }; + + class VertexFactoryTypeRegistry { + public: + static VertexFactoryTypeRegistry& Get(); - BoolShaderVariantFieldImpl(); - BoolShaderVariantFieldImpl(BoolShaderVariantFieldImpl&& other) noexcept; - ~BoolShaderVariantFieldImpl(); + ~VertexFactoryTypeRegistry(); - void Set(ValueType inValue); - [[nodiscard]] ValueType Get() const; - [[nodiscard]] uint32_t GetNumberValue() const; + // game thread + void RegisterType(const VertexFactoryType& inType); + void UnregisterType(const VertexFactoryType& inType); + std::vector AllTypes() const; private: - uint32_t value; + VertexFactoryTypeRegistry(); + + std::unordered_map types; }; - template - struct RangedIntShaderVariantFieldImpl { + class ShaderType { public: - using ValueType = uint32_t; + NonCopyable(ShaderType) + NonMovable(ShaderType) - static constexpr std::pair valueRange = { From, To }; - static constexpr ValueType defaultValue = From; + virtual ~ShaderType(); + virtual const std::string& GetName() const = 0; + virtual RHI::ShaderStageBits GetStage() const = 0; + virtual const std::string& GetSourceFile() const = 0; + virtual const std::string& GetEntryPoint() const = 0; + virtual const std::vector& GetIncludeDirectories() const = 0; + virtual const ShaderVariantFieldVec& GetVariantFields() const = 0; - RangedIntShaderVariantFieldImpl(); - RangedIntShaderVariantFieldImpl(RangedIntShaderVariantFieldImpl&& other) noexcept; - ~RangedIntShaderVariantFieldImpl(); // NOLINT + ShaderTypeKey GetKey() const; - void Set(ValueType inValue); - [[nodiscard]] ValueType Get() const; - [[nodiscard]] uint32_t GetNumberValue() const; + protected: + explicit ShaderType(ShaderTypeKey inKey); private: - uint32_t value; + ShaderTypeKey key; }; - template - class VariantSetImpl { + template + class StaticShaderType : public ShaderType { public: - static uint32_t VariantNum(); + StaticShaderType(); + ~StaticShaderType() override; - VariantSetImpl(); - VariantSetImpl(const VariantSetImpl& other); - VariantSetImpl(VariantSetImpl&& other) noexcept; - ~VariantSetImpl(); + const std::string& GetName() const override; + RHI::ShaderStageBits GetStage() const override; + const std::string& GetSourceFile() const override; + const std::string& GetEntryPoint() const override; + const std::vector& GetIncludeDirectories() const override; + const ShaderVariantFieldVec& GetVariantFields() const override; - template - static void TraverseAll(F&& func); + private: + std::string name; + std::string sourceFile; + std::string entryPoint; + std::vector includeDirectories; + ShaderVariantFieldVec variantFields; + }; - template - void Set(typename Variant::ValueType value); + class MaterialShaderType final : public ShaderType { + public: + MaterialShaderType( + const VertexFactoryType& inVertexFactory, + std::string inName, + RHI::ShaderStageBits inStage, + std::string inSourceFile, + std::string inEntryPoint, + const std::vector& inIncludeDirectories, + const ShaderVariantFieldVec& inShaderVariantFields); + ~MaterialShaderType() override; + + const std::string& GetName() const override; + RHI::ShaderStageBits GetStage() const override; + const std::string& GetSourceFile() const override; + const std::string& GetEntryPoint() const override; + const std::vector& GetIncludeDirectories() const override; + const ShaderVariantFieldVec& GetVariantFields() const override; - template - typename Variant::ValueType Get(); + private: + const VertexFactoryType& vertexFactory; + std::string name; + RHI::ShaderStageBits stage; + std::string sourceFile; + std::string entryPoint; + std::vector includeDirectories; + ShaderVariantFieldVec shaderVariantFields; + }; - [[nodiscard]] std::vector ComputeDefinitions() const; - [[nodiscard]] VariantKey Hash() const; + struct ShaderReflectionData { + using VertexSemantic = std::string; + using ResourceBindingName = std::string; + using LayoutIndex = uint8_t; + using LayoutAndResourceBinding = std::pair; - private: - std::tuple variants; + ShaderReflectionData(); + ShaderReflectionData(const ShaderReflectionData& inOther); + ShaderReflectionData(ShaderReflectionData&& inOther) noexcept; + ShaderReflectionData& operator=(const ShaderReflectionData& inOther); + + const RHI::PlatformVertexBinding& QueryVertexBindingChecked(const VertexSemantic& inSemantic) const; + const LayoutAndResourceBinding& QueryResourceBindingChecked(const ResourceBindingName& inName) const; + + std::unordered_map vertexBindings; + std::unordered_map resourceBindings; }; -} -#define ShaderInfo(inName, inSourceFile, inEntryPoint, inStage) \ - static constexpr const char* name = inName; \ - static constexpr const char* sourceFile = inSourceFile; \ - static constexpr const char* entryPoint = inEntryPoint; \ - static constexpr RHI::ShaderStageBits stage = inStage; \ + struct ShaderVariantArtifact { + std::string entryPoint; + std::vector byteCode; + ShaderReflectionData reflectionData; + }; -#define DefaultVariantFilter \ - static bool VariantFilter(const VariantSet& variantSet) { return true; } \ + struct ShaderTypeArtifact { + ShaderSourceHash sourceHash; + std::unordered_map variantArtifacts; + }; -#define BoolShaderVariantField(inClass, inMacro) \ - struct inClass : public Render::BoolShaderVariantFieldImpl { \ - static constexpr const char* name = #inClass; \ - static constexpr const char* macro = inMacro; \ - }; \ + struct ShaderInstance { + ShaderTypeKey typeKey; + ShaderVariantKey variantKey; + RHI::ShaderModule* rhiHandle; + const ShaderReflectionData* reflectionData; -#define RangedIntShaderVariantField(inClass, inMacro, inRangeFrom, inRangeTo) \ - struct inClass : public Render::RangedIntShaderVariantFieldImpl { \ - static constexpr const char* name = #inClass; \ - static constexpr const char* macro = inMacro; \ - } + ShaderInstance(); -#define VariantSet(...) \ - class VariantSet : public Render::VariantSetImpl<__VA_ARGS__> {}; + bool Valid() const; + uint64_t Hash() const; + }; -#define NonVariant \ - RangedIntShaderVariantField(_PlaceholderVariantField, "__PLACEHOLDER_VARIANT", 0, 0); /* NOLINT */ \ - VariantSet(_PlaceholderVariantField); + class ShaderTypeRegistry { + public: + static ShaderTypeRegistry& Get(); -#define RegisterGlobalShader(inClass) \ - static uint8_t _globalShaderRegister_##inClass = []() -> uint8_t { \ - Render::GlobalShaderRegistry::Get().Register(); \ - return 0; \ - }(); \ + ~ShaderTypeRegistry(); -#define ALIGN_AS_GPU alignas(16) + // game thread + void RegisterType(const ShaderType& inShaderType); + void UnregisterType(const ShaderType& inShaderType); + const ShaderType& GetType(ShaderTypeKey inKey) const; + std::vector AllTypes() const; -namespace Render { - class MaterialShader : public Shader {}; -} + private: + ShaderTypeRegistry(); -namespace Render { - template - GlobalShaderType& GlobalShaderType::Get() - { - static GlobalShaderType instance; - return instance; - } + std::unordered_map types; + }; - template - GlobalShaderType::GlobalShaderType() - { - ReloadInternal(); - } + class ShaderArtifactRegistry { + public: + static ShaderArtifactRegistry& Get(); - template - GlobalShaderType::~GlobalShaderType() = default; + ~ShaderArtifactRegistry(); - template - const std::string& GlobalShaderType::GetName() - { - return name; - } + void PerformThreadCopy(); - template - ShaderTypeKey GlobalShaderType::GetKey() - { - return key; - } + private: + friend class ShaderMap; + friend class ShaderTypeCompiler; - template - RHI::ShaderStageBits GlobalShaderType::GetStage() - { - return stage; - } + ShaderArtifactRegistry(); - template - const std::string& GlobalShaderType::GetEntryPoint() - { - return entryPoint; - } + std::mutex mutexGT; + std::unordered_map typeArtifactsGT; + std::unordered_map typeArtifactsRT; + }; - template - const std::string& GlobalShaderType::GetCode() - { - return code; - } + class ShaderMap { + public: + static ShaderMap& Get(RHI::Device& inDevice); - template - uint32_t GlobalShaderType::GetVariantNum() - { - return variants.size(); - } + ~ShaderMap(); - template - const std::vector & GlobalShaderType::GetVariants() - { - return variants; - } + // render thread + ShaderInstance GetShaderInstance(const ShaderType& inShaderType, const ShaderVariantValueMap& inShaderVariants); - template - const std::vector& GlobalShaderType::GetDefinitions(VariantKey variantKey) - { - return variantDefinitions.at(variantKey); - } + private: + using VariantsShaderModules = std::unordered_map>; - template - void GlobalShaderType::Reload() - { - Invalidate(); - ReloadInternal(); - } + explicit ShaderMap(RHI::Device& inDevice); - template - void GlobalShaderType::Invalidate() - { - GlobalShaderMap::Get().Invalidate(); - } + RHI::Device& device; + std::unordered_map shaderModules; + }; +} - template - void GlobalShaderType::ReloadInternal() +namespace Render::Internal { + static uint64_t MakeTypeKeyFromName(const std::string& inName) { - name = Shader::name; - const std::string keySource = std::string("Global-") + name; - key = Common::HashUtils::CityHash(keySource.data(), keySource.size()); - stage = Shader::stage; - entryPoint = Shader::entryPoint; - - ReadCode(); - ComputeVariants(); - ComputeVariantDefinitions(); + return Common::HashUtils::CityHash(inName.data(), inName.size()); } - template - void GlobalShaderType::ReadCode() + template + static ShaderVariantFieldVec BuildVariantFieldVecFromStatic(std::index_sequence) { - const std::string sourceFile = Shader::sourceFile; - const Core::Uri uri(std::format("file://{}", sourceFile)); - const Core::FileUriParser parser(uri); - code = Common::FileUtils::ReadTextFile(parser.Parse().String()); - } + ShaderVariantFieldVec result; + result.reserve(sizeof...(I)); + (void) std::initializer_list { ([&]() -> void { + using StaticVariantField = std::tuple_element_t; + constexpr VariantFieldType type = StaticVariantField::type; // NOLINT - template - void GlobalShaderType::ComputeVariants() - { - variants.reserve(Shader::VariantSet::VariantNum()); - Shader::VariantSet::TraverseAll([this](auto&& variantSetImpl) -> void { - const auto* variantSet = static_cast(&variantSetImpl); - if (!Shader::VariantFilter(*variantSet)) { - return; + if constexpr (type == VariantFieldType::vfBool) { + result.emplace_back(ShaderBoolVariantField { StaticVariantField::macro, StaticVariantField::defaultValue }); + } else if constexpr (type == VariantFieldType::vfRangedInt) { + result.emplace_back(ShaderRangedIntVariantField { StaticVariantField::macro, StaticVariantField::defaultValue, { StaticVariantField::from, StaticVariantField::to } }); + } else { + QuickFail(); } - variants.emplace_back(variantSet->Hash()); - }); + }(), 0)... }; + return result; } - template - void GlobalShaderType::ComputeVariantDefinitions() + template + static VertexFactoryInputVec BuildVertexFactoryInputVecFromStatic(std::index_sequence) { - variantDefinitions.reserve(Shader::VariantSet::VariantNum()); - Shader::VariantSet::TraverseAll([this](auto&& variantSetImpl) -> void { - const auto* variantSet = static_cast(&variantSetImpl); - if (!Shader::VariantFilter(*variantSet)) { - return; - } - variantDefinitions[variantSet->Hash()] = variantSet->ComputeDefinitions(); - }); + VertexFactoryInputVec result; + result.reserve(sizeof...(I)); + (void) std::initializer_list { ([&]() -> void { + using VertexFactoryInputStruct = std::tuple_element_t; + result.emplace_back(VertexFactoryInput { VertexFactoryInputStruct::name, VertexFactoryInputStruct::format, VertexFactoryInputStruct::offset }); + }(), 0)... }; + return result; } +} +namespace Render { template - GlobalShaderMap& GlobalShaderMap::Get() + StaticVertexFactoryType::StaticVertexFactoryType() + : VertexFactoryType(Internal::MakeTypeKeyFromName(T::name)) + , name(T::name) + , sourceFile(T::sourceFile) { - static GlobalShaderMap instance; - return instance; + inputs = Internal::BuildVertexFactoryInputVecFromStatic(std::make_index_sequence> {}); + variantFields = Internal::BuildVariantFieldVecFromStatic(std::make_index_sequence> {}); + + for (const auto& materialType : T::supportedMaterialTypes) { + supportedMaterialTypes.emplace(materialType); + } } template - GlobalShaderMap::~GlobalShaderMap() = default; + StaticVertexFactoryType::~StaticVertexFactoryType() = default; template - void GlobalShaderMap::Invalidate() + const std::string& StaticVertexFactoryType::GetName() const { - shaderModules.clear(); + return name; } template - ShaderInstance GlobalShaderMap::GetShaderInstance(RHI::Device& device, const typename T::VariantSet& variantSet) + const std::string& StaticVertexFactoryType::GetSourceFile() const { - auto variantKey = variantSet.Hash(); - const auto& archive = GetArchive(variantSet); - auto& deviceShaderModules = shaderModules[&device]; - - auto iter = deviceShaderModules.find(variantKey); - if (iter == deviceShaderModules.end()) { - deviceShaderModules[variantKey] = device.CreateShaderModule(RHI::ShaderModuleCreateInfo(T::entryPoint, archive.byteCode)); - } - - ShaderInstance result; - result.rhiHandle = deviceShaderModules.at(variantKey).Get(); - result.typeKey = GlobalShaderType::Get().GetKey(); - result.variantKey = variantKey; - result.reflectionData = &archive.reflectionData; - return result; + return sourceFile; } template - GlobalShaderMap::GlobalShaderMap() = default; - - template - const ShaderArchive& GlobalShaderMap::GetArchive(const typename T::VariantSet& variantSet) const + const VertexFactoryInputVec& StaticVertexFactoryType::GetVertexInputs() const { - const auto& package = ShaderArchiveStorage::Get().GetShaderArchivePackage(GlobalShaderType::Get().GetKey()); - - auto iter = package.find(variantSet.Hash()); - Assert(iter != package.end()); - return iter->second; - } - - template - void GlobalShaderRegistry::Register() - { - shaderTypes.emplace_back(&GlobalShaderType::Get()); + return inputs; } - template - RangedIntShaderVariantFieldImpl::RangedIntShaderVariantFieldImpl() - : value(defaultValue) - { - } - - template - RangedIntShaderVariantFieldImpl::RangedIntShaderVariantFieldImpl(RangedIntShaderVariantFieldImpl&& other) noexcept - : value(other.value) - { - } - - template - RangedIntShaderVariantFieldImpl::~RangedIntShaderVariantFieldImpl() = default; - - template - void RangedIntShaderVariantFieldImpl::Set(const ValueType inValue) - { - Assert(From <= value && value <= To); - value = inValue; - } - - template - typename RangedIntShaderVariantFieldImpl::ValueType RangedIntShaderVariantFieldImpl::Get() const + template + const ShaderVariantFieldVec& StaticVertexFactoryType::GetVariantFields() const { - return value; + return variantFields; } - template - uint32_t RangedIntShaderVariantFieldImpl::GetNumberValue() const + template + bool StaticVertexFactoryType::SupportMaterialType(MaterialType inType) const { - return value; + return supportedMaterialTypes.contains(inType); } - template - uint32_t VariantSetImpl::VariantNum() - { - uint32_t result = 1; - (void) std::initializer_list { ([&result]() -> void { - auto valueRange = Variants::valueRange; - Assert(valueRange.first <= valueRange.second); - result *= valueRange.second - valueRange.first + 1; - }(), 0)... }; - return result; - } + template + StaticShaderType::StaticShaderType() + : ShaderType(Internal::MakeTypeKeyFromName(T::name)) + , name(T::name) + , sourceFile(T::sourceFile) + , entryPoint(T::entryPoint) + { + includeDirectories.reserve(T::includeDirectories.size()); + for (const auto& includeDir : T::includeDirectories) { + includeDirectories.emplace_back(includeDir.data(), includeDir.size()); + } - template - VariantSetImpl::VariantSetImpl() - { - (void) std::initializer_list { ([this]() -> void { std::get(variants).Set(Variants::defaultValue); }(), 0)... }; + variantFields = Internal::BuildVariantFieldVecFromStatic(std::make_index_sequence> {}); } - template - VariantSetImpl::VariantSetImpl(const VariantSetImpl& other) - { - (void) std::initializer_list { ([this, &other]() -> void { std::get(variants).Set(std::get(other.variants).Get()); }(), 0)... }; - } + template + StaticShaderType::~StaticShaderType() = default; - template - VariantSetImpl::VariantSetImpl(VariantSetImpl&& other) noexcept - : variants(std::move(other.variants)) + template + const std::string& StaticShaderType::GetName() const { + return name; } - template - VariantSetImpl::~VariantSetImpl() = default; - - template - template - void VariantSetImpl::TraverseAll(F&& func) + template + RHI::ShaderStageBits StaticShaderType::GetStage() const { - std::vector variantSets; - variantSets.reserve(VariantNum()); - variantSets.emplace_back(VariantSetImpl()); - - (void) std::initializer_list { ([&variantSets]() -> void { - auto valueRange = Variants::valueRange; - auto variantSetsSize = variantSets.size(); - for (auto i = valueRange.first; i <= valueRange.second; ++i) { - if (i == valueRange.first) { - for (auto j = 0; j < variantSetsSize; j++) { - variantSets[j].template Set(static_cast(i)); - } - } else { - for (auto j = 0; j < variantSetsSize; j++) { - variantSets.emplace_back(variantSets[j]); - variantSets.back().template Set(static_cast(i)); - } - } - } - }(), 0)... }; - - for (auto& variantSet : variantSets) { - func(variantSet); - } + return T::stage; } - template - template - void VariantSetImpl::Set(typename Variant::ValueType value) + template + const std::string& StaticShaderType::GetSourceFile() const { - std::get(variants).Set(value); + return sourceFile; } - template - template - typename Variant::ValueType VariantSetImpl::Get() + template + const std::string& StaticShaderType::GetEntryPoint() const { - return std::get(variants).Get(); + return entryPoint; } - template - std::vector VariantSetImpl::ComputeDefinitions() const + template + const std::vector& StaticShaderType::GetIncludeDirectories() const { - std::vector result; - result.reserve(sizeof...(Variants)); - (void) std::initializer_list { ([&result, this]() -> void { - result.emplace_back(std::string(Variants::macro) + "=" + std::to_string(std::get(variants).GetNumberValue())); - }(), 0)... }; - return result; + return includeDirectories; } - template - VariantKey VariantSetImpl::Hash() const + template + const ShaderVariantFieldVec& StaticShaderType::GetVariantFields() const { - return Common::HashUtils::CityHash(&variants, sizeof(std::tuple)); + return variantFields; } -} +} // namespace Render diff --git a/Engine/Source/Render/Include/Render/ShaderCompiler.h b/Engine/Source/Render/Include/Render/ShaderCompiler.h index 784324280..83b9076aa 100644 --- a/Engine/Source/Render/Include/Render/ShaderCompiler.h +++ b/Engine/Source/Render/Include/Render/ShaderCompiler.h @@ -21,30 +21,32 @@ namespace Render { struct ShaderCompileInput { std::string source; std::string entryPoint; - RHI::ShaderStageBits stage; + RHI::ShaderStageBits stage = RHI::ShaderStageBits::max; std::vector definitions; + std::vector includeDirectories; }; struct ShaderCompileOptions { + std::vector includeDirectories; ShaderByteCodeType byteCodeType = ShaderByteCodeType::max; bool withDebugInfo = false; - std::vector includePaths; }; struct ShaderCompileOutput { bool success; + std::string entryPoint; std::vector byteCode; ShaderReflectionData reflectionData; std::string errorInfo; }; struct ShaderTypeAndVariantHashProvider { - size_t operator()(const std::pair& value) const; + size_t operator()(const std::pair& value) const; }; struct ShaderTypeCompileResult { bool success; - std::unordered_map, std::string, ShaderTypeAndVariantHashProvider> errorInfos; + std::unordered_map, std::string, ShaderTypeAndVariantHashProvider> errorInfos; }; class ShaderCompiler { @@ -64,8 +66,8 @@ namespace Render { static ShaderTypeCompiler& Get(); ~ShaderTypeCompiler(); - std::future Compile(const std::vector& inShaderTypes, const ShaderCompileOptions& inOptions); - std::future CompileGlobalShaderTypes(const ShaderCompileOptions& inOptions); + std::future Compile(const std::vector& inShaderTypes, const ShaderCompileOptions& inOptions); + std::future CompileAll(const ShaderCompileOptions& inOptions); private: ShaderTypeCompiler(); diff --git a/Engine/Source/Render/Include/Render/View.h b/Engine/Source/Render/Include/Render/View.h index 720c27abc..71c55a931 100644 --- a/Engine/Source/Render/Include/Render/View.h +++ b/Engine/Source/Render/Include/Render/View.h @@ -6,7 +6,7 @@ #include #include -#include +#include namespace Render { struct ViewData { diff --git a/Engine/Source/Render/Src/RenderCache.cpp b/Engine/Source/Render/Src/RenderCache.cpp index 6fc805569..040f58a69 100644 --- a/Engine/Source/Render/Src/RenderCache.cpp +++ b/Engine/Source/Render/Src/RenderCache.cpp @@ -374,7 +374,7 @@ namespace Render { { std::unordered_map layoutBindingMaps; for (const auto& [stage, instance] : shaderInstancePack) { - if (!instance->IsValid()) { + if (!instance->Valid()) { continue; } Assert(instance->reflectionData != nullptr); @@ -504,10 +504,13 @@ namespace Render { return hash; } + std::mutex SamplerCache::mutex = std::mutex(); + SamplerCache& SamplerCache::Get(RHI::Device& device) { static std::unordered_map> map; + std::unique_lock lock(mutex); const auto iter = map.find(&device); if (iter == map.end()) { map[&device] = Common::UniquePtr(new SamplerCache(device)); @@ -532,10 +535,13 @@ namespace Render { return samplers[hash].Get(); } + std::mutex PipelineCache::mutex = std::mutex(); + PipelineCache& PipelineCache::Get(RHI::Device& device) { static std::unordered_map> map; + std::unique_lock lock(mutex); if (const auto iter = map.find(&device); iter == map.end()) { map[&device] = Common::UniquePtr(new PipelineCache(device)); @@ -577,10 +583,13 @@ namespace Render { return rasterPipelines[hash].Get(); } + std::mutex ResourceViewCache::mutex = std::mutex(); + ResourceViewCache& ResourceViewCache::Get(RHI::Device& device) { static std::unordered_map> map; + std::unique_lock lock(mutex); if (!map.contains(&device)) { map.emplace(std::make_pair(&device, Common::UniquePtr(new ResourceViewCache(device)))); } @@ -661,10 +670,13 @@ namespace Render { forfeitCaches(textureViewCaches); } + std::mutex BindGroupCache::mutex = std::mutex(); + BindGroupCache& BindGroupCache::Get(RHI::Device& device) { static std::unordered_map> map; + std::unique_lock lock(mutex); if (!map.contains(&device)) { map.emplace(std::make_pair(&device, Common::UniquePtr(new BindGroupCache(device)))); } diff --git a/Engine/Source/Render/Src/Shader.cpp b/Engine/Source/Render/Src/Shader.cpp index 0eae46085..ca8260dc5 100644 --- a/Engine/Source/Render/Src/Shader.cpp +++ b/Engine/Source/Render/Src/Shader.cpp @@ -2,9 +2,298 @@ // Created by johnk on 2022/7/24. // +#include + #include +#include +#include +#include namespace Render { + bool ShaderBoolVariantField::operator==(const ShaderBoolVariantField& inRhs) const + { + return macro == inRhs.macro + && defaultValue == inRhs.defaultValue; + } + + bool ShaderRangedIntVariantField::operator==(const ShaderRangedIntVariantField& inRhs) const + { + return macro == inRhs.macro + && defaultValue == inRhs.defaultValue + && range == inRhs.range; + } + + std::vector ShaderUtils::GetAllVariants(const ShaderVariantFieldVec& inFields) + { + std::vector result; + + ShaderVariantValueMap baseVariant; + for (const auto& field : inFields) { + std::visit([&](auto&& typedField) -> void { + baseVariant.emplace(typedField.macro, typedField.defaultValue); + }, field); + } + result.emplace_back(baseVariant); + + for (const auto& field : inFields) { + int32_t variantFieldRange = 0; + if (field.index() == 0) { + variantFieldRange = 2; + } else if (field.index() == 1) { + const auto& [macro, defaultValue, range] = std::get(field); + variantFieldRange = range.second - range.first + 1; + } else { + Unimplement(); + } + + const auto fork = result; + result.clear(); + result.reserve(fork.size() * variantFieldRange); + + if (field.index() == 0) { + const auto& [macro, defaultValue] = std::get(field); + for (const std::vector candidateValues = { false, true }; + auto candidateValue : candidateValues) { + for (const auto& valueMap : fork) { + auto& last = result.emplace_back(valueMap); + last.at(macro) = candidateValue; + } + } + } else if (field.index() == 1) { + const auto& [macro, defaultValue, range] = std::get(field); + for (auto i = range.first; i <= range.second; i++) { + for (const auto& valueMap : fork) { + auto& last = result.emplace_back(valueMap); + last.at(macro) = i; + } + } + } else { + Unimplement(); + } + } + return result; + } + + ShaderVariantKey ShaderUtils::ComputeVariantKey(const ShaderVariantFieldVec& inFields, const ShaderVariantValueMap& inVariantSet) + { + uint64_t result = 0; + uint64_t variantKeyMultipy = 1; + for (const auto& field : inFields) { + uint64_t variantFieldValue = 0; + uint64_t variantFieldRange = 1; + + if (field.index() == 0) { + const auto& [macro, defaultValue] = std::get(field); + const bool value = inVariantSet.contains(macro) ? std::get(inVariantSet.at(macro)) : defaultValue; + variantFieldValue = value ? 1 : 0; + variantFieldRange = 2; + } else if (field.index() == 1) { + const auto& [macro, defaultValue, range] = std::get(field); + const int32_t value = inVariantSet.contains(macro) ? std::get(inVariantSet.at(macro)) : defaultValue; + variantFieldValue = value - range.first; + variantFieldRange = range.second - range.first + 1; + } else { + Unimplement(); + } + + result *= variantKeyMultipy; + result += variantFieldValue; + variantKeyMultipy *= variantFieldRange; + } + return result; + } + + std::vector ShaderUtils::ComputeVariantDefinitions(const ShaderVariantFieldVec& inFields, const ShaderVariantValueMap& inVariantSet) + { + std::vector result; + result.reserve(inFields.size()); + for (const auto& field : inFields) { + if (field.index() == 0) { + const auto& [macro, defaultValue] = std::get(field); + const bool value = inVariantSet.contains(macro) ? std::get(inVariantSet.at(macro)) : defaultValue; + result.emplace_back(std::format("{}={}", macro, value ? 1 : 0)); + } else if (field.index() == 1) { + const auto& [macro, defaultValue, range] = std::get(field); + const int32_t value = inVariantSet.contains(macro) ? std::get(inVariantSet.at(macro)) : defaultValue; + result.emplace_back(std::format("{}={}", macro, value)); + } else { + Unimplement(); + } + } + return result; + } + + ShaderSourceHash ShaderUtils::ComputeShaderSourceHash(const std::string& inSourceFile, const std::vector& inIncludeDirectories) + { + std::unordered_map relativeFileAndSources; + GatherShaderSources(relativeFileAndSources, inSourceFile, inIncludeDirectories); + + std::string finalString; + for (const auto& source : relativeFileAndSources | std::views::values) { + finalString += source; + } + return Common::HashUtils::CityHash(finalString.data(), finalString.size()); + } + + std::string ShaderUtils::GetAbsoluteIncludeFile(const std::string& inPath, const std::vector& inIncludeDirectories) + { + for (const auto& includeDirectory : inIncludeDirectories) { + const Common::Path absoluteIncludeDirectory = Core::Paths::Translate(includeDirectory); + auto testPath = absoluteIncludeDirectory / inPath; + testPath.Fixup(); + + if (testPath.Exists()) { + return testPath.String(); + } + } + QuickFail(); + return ""; + } + + void ShaderUtils::GatherShaderSources(std::unordered_map& outFileAndSource, const std::string& inSourceFile, const std::vector& inIncludeDirectories) + { + const std::string text = Common::FileUtils::ReadTextFile(inSourceFile); + outFileAndSource.emplace(inSourceFile, text); + + for (const auto includes = Common::StringUtils::RegexSearch(text, "#include \\<.*\\>"); + const auto& include : includes) { + auto pureInclude = Common::StringUtils::Replace(include, "#include <", ""); + pureInclude = Common::StringUtils::Replace(pureInclude, ">", ""); + const std::string absoluteInclude = GetAbsoluteIncludeFile(pureInclude, inIncludeDirectories); + GatherShaderSources(outFileAndSource, absoluteInclude, inIncludeDirectories); + } + } + + bool VertexFactoryInput::operator==(const VertexFactoryInput& inRhs) const + { + return name == inRhs.name + && format == inRhs.format + && offset == inRhs.offset; + } + + VertexFactoryType::VertexFactoryType(VertexFactoryTypeKey inKey) + : key(inKey) + { + VertexFactoryTypeRegistry::Get().RegisterType(*this); + } + + VertexFactoryType::~VertexFactoryType() + { + VertexFactoryTypeRegistry::Get().UnregisterType(*this); + } + + VertexFactoryTypeKey VertexFactoryType::GetKey() const + { + return key; + } + + VertexFactoryTypeRegistry& VertexFactoryTypeRegistry::Get() + { + static VertexFactoryTypeRegistry instance; + return instance; + } + + VertexFactoryTypeRegistry::VertexFactoryTypeRegistry() = default; + + VertexFactoryTypeRegistry::~VertexFactoryTypeRegistry() = default; + + void VertexFactoryTypeRegistry::RegisterType(const VertexFactoryType& inType) + { + Assert(Core::ThreadContext::IsGameThread()); + + const auto typeKey = inType.GetKey(); + Assert(!types.contains(typeKey)); + types.emplace(typeKey, &inType); + } + + void VertexFactoryTypeRegistry::UnregisterType(const VertexFactoryType& inType) + { + Assert(Core::ThreadContext::IsGameThread()); + + const auto typeKey = inType.GetKey(); + Assert(types.contains(typeKey)); + types.erase(typeKey); + } + + std::vector VertexFactoryTypeRegistry::AllTypes() const + { + Assert(Core::ThreadContext::IsGameThread()); + + std::vector result; + result.reserve(types.size()); + for (const auto* type : types | std::views::values) { + result.emplace_back(type); + } + return result; + } + + ShaderType::ShaderType(ShaderTypeKey inKey) + : key(inKey) + { + ShaderTypeRegistry::Get().RegisterType(*this); + } + + ShaderType::~ShaderType() + { + ShaderTypeRegistry::Get().UnregisterType(*this); + } + + ShaderTypeKey ShaderType::GetKey() const + { + return key; + } + + MaterialShaderType::MaterialShaderType( + const VertexFactoryType& inVertexFactory, + std::string inName, + RHI::ShaderStageBits inStage, + std::string inSourceFile, + std::string inEntryPoint, + const std::vector& inIncludeDirectories, + const ShaderVariantFieldVec& inShaderVariantFields) + : ShaderType(Internal::MakeTypeKeyFromName(inName)) + , vertexFactory(inVertexFactory) + , name(std::move(inName)) + , stage(inStage) + , sourceFile(std::move(inSourceFile)) + , entryPoint(std::move(inEntryPoint)) + , includeDirectories(inIncludeDirectories) + , shaderVariantFields(Common::VectorUtils::Combine(inVertexFactory.GetVariantFields(), inShaderVariantFields)) + { + } + + MaterialShaderType::~MaterialShaderType() = default; + + const std::string& MaterialShaderType::GetName() const + { + return name; + } + + RHI::ShaderStageBits MaterialShaderType::GetStage() const + { + return stage; + } + + const std::string& MaterialShaderType::GetSourceFile() const + { + return sourceFile; + } + + const std::string& MaterialShaderType::GetEntryPoint() const + { + return entryPoint; + } + + const std::vector& MaterialShaderType::GetIncludeDirectories() const + { + return includeDirectories; + } + + const ShaderVariantFieldVec& MaterialShaderType::GetVariantFields() const + { + return shaderVariantFields; + } + ShaderReflectionData::ShaderReflectionData() = default; ShaderReflectionData::ShaderReflectionData(const ShaderReflectionData& inOther) // NOLINT @@ -40,114 +329,129 @@ namespace Render { return iter->second; } - IShaderType::~IShaderType() {} - - ShaderArchiveStorage& ShaderArchiveStorage::Get() + ShaderInstance::ShaderInstance() + : typeKey(0) + , variantKey(0) + , rhiHandle(nullptr) + , reflectionData(nullptr) { - static ShaderArchiveStorage instance; - return instance; } - ShaderArchiveStorage::ShaderArchiveStorage() = default; - - ShaderArchiveStorage::~ShaderArchiveStorage() = default; + bool ShaderInstance::Valid() const + { + return rhiHandle != nullptr; + } - void ShaderArchiveStorage::UpdateShaderArchivePackage(ShaderTypeKey shaderTypeKey, ShaderArchivePackage&& shaderArchivePackage) + uint64_t ShaderInstance::Hash() const { - Assert(!shaderArchivePackages.contains(shaderTypeKey)); - shaderArchivePackages.emplace(std::make_pair(shaderTypeKey, std::move(shaderArchivePackage))); + const std::vector values = { + typeKey, + variantKey + }; + return Common::HashUtils::CityHash(values.data(), values.size() * sizeof(uint64_t)); } - const ShaderArchivePackage& ShaderArchiveStorage::GetShaderArchivePackage(ShaderTypeKey shaderTypeKey) + ShaderTypeRegistry& ShaderTypeRegistry::Get() { - const auto iter = shaderArchivePackages.find(shaderTypeKey); - Assert(iter != shaderArchivePackages.end()); - return iter->second; + static ShaderTypeRegistry registry; + return registry; } - void ShaderArchiveStorage::InvalidateAll() + ShaderTypeRegistry::ShaderTypeRegistry() = default; + + ShaderTypeRegistry::~ShaderTypeRegistry() = default; + + void ShaderTypeRegistry::RegisterType(const ShaderType& inShaderType) { - shaderArchivePackages.clear(); + Assert(Core::ThreadContext::IsGameThread()); + + const auto key = inShaderType.GetKey(); + Assert(!types.contains(key)); + types.emplace(key, &inShaderType); } - void ShaderArchiveStorage::Invalidate(ShaderTypeKey shaderTypeKey) + void ShaderTypeRegistry::UnregisterType(const ShaderType& inShaderType) { - shaderArchivePackages.erase(shaderTypeKey); + Assert(Core::ThreadContext::IsGameThread()); + + const auto key = inShaderType.GetKey(); + Assert(types.contains(key)); + types.erase(key); } - bool ShaderInstance::IsValid() const + const ShaderType& ShaderTypeRegistry::GetType(ShaderTypeKey inKey) const { - return rhiHandle != nullptr; + Assert(Core::ThreadContext::IsGameThread()); + return *types.at(inKey); } - uint64_t ShaderInstance::Hash() const + std::vector ShaderTypeRegistry::AllTypes() const { - if (!IsValid()) { - return 0; - } + Assert(Core::ThreadContext::IsGameThread()); - const std::vector values = { - typeKey, - variantKey - }; - return Common::HashUtils::CityHash(values.data(), values.size() * sizeof(uint64_t)); + std::vector result; + result.reserve(types.size()); + for (const auto* type : types | std::views::values) { + result.emplace_back(type); + } + return result; } - GlobalShaderRegistry& GlobalShaderRegistry::Get() + ShaderArtifactRegistry& ShaderArtifactRegistry::Get() { - static GlobalShaderRegistry instance; + static ShaderArtifactRegistry instance; return instance; } - GlobalShaderRegistry::GlobalShaderRegistry() = default; + ShaderArtifactRegistry::ShaderArtifactRegistry() = default; - GlobalShaderRegistry::~GlobalShaderRegistry() = default; + ShaderArtifactRegistry::~ShaderArtifactRegistry() = default; - const std::vector& GlobalShaderRegistry::GetShaderTypes() const + void ShaderArtifactRegistry::PerformThreadCopy() { - return shaderTypes; + typeArtifactsRT = typeArtifactsGT; } - void GlobalShaderRegistry::Invalidate() const // NOLINT + ShaderMap& ShaderMap::Get(RHI::Device& inDevice) { - ShaderArchiveStorage::Get().InvalidateAll(); - for (auto* shaderType : shaderTypes) { - shaderType->Invalidate(); + static std::unordered_map> instances; + if (!instances.contains(&inDevice)) { + instances.emplace(&inDevice, Common::UniquePtr(new ShaderMap(inDevice))); } + return *instances.at(&inDevice); } - void GlobalShaderRegistry::ReloadAll() const + ShaderMap::ShaderMap(RHI::Device& inDevice) + : device(inDevice) { - Invalidate(); - for (auto* shaderType : shaderTypes) { - shaderType->Reload(); - } } - BoolShaderVariantFieldImpl::BoolShaderVariantFieldImpl() - : value(static_cast(defaultValue)) - { - } + ShaderMap::~ShaderMap() = default; - BoolShaderVariantFieldImpl::BoolShaderVariantFieldImpl(BoolShaderVariantFieldImpl&& other) noexcept - : value(other.value) + ShaderInstance ShaderMap::GetShaderInstance(const ShaderType& inShaderType, const ShaderVariantValueMap& inShaderVariants) { - } + Assert(Core::ThreadContext::IsRenderThread()); - BoolShaderVariantFieldImpl::~BoolShaderVariantFieldImpl() = default; + const ShaderArtifactRegistry& registry = ShaderArtifactRegistry::Get(); + const auto typeKey = inShaderType.GetKey(); - void BoolShaderVariantFieldImpl::Set(const ValueType inValue) - { - value = inValue ? 1 : 0; - } + const ShaderTypeArtifact& typeArtifact = registry.typeArtifactsRT.at(typeKey); // NOLINT + const ShaderVariantKey variantKey = ShaderUtils::ComputeVariantKey(inShaderType.GetVariantFields(), inShaderVariants); + const auto& [entryPoint, byteCode, reflectionData] = typeArtifact.variantArtifacts.at(variantKey); - BoolShaderVariantFieldImpl::ValueType BoolShaderVariantFieldImpl::Get() const - { - return value == 1; - } + if (!shaderModules.contains(typeKey)) { + shaderModules.emplace(typeKey, VariantsShaderModules {}); + } + VariantsShaderModules& variantShaderModules = shaderModules.at(typeKey); + if (!variantShaderModules.contains(variantKey)) { + variantShaderModules.emplace(variantKey, device.CreateShaderModule(RHI::ShaderModuleCreateInfo(entryPoint, byteCode))); + } - uint32_t BoolShaderVariantFieldImpl::GetNumberValue() const - { - return value; + ShaderInstance result; + result.typeKey = typeKey; + result.variantKey = variantKey; + result.rhiHandle = variantShaderModules.at(variantKey).Get(); + result.reflectionData = &reflectionData; + return result; } -} +} // namespace Render diff --git a/Engine/Source/Render/Src/ShaderCompiler.cpp b/Engine/Source/Render/Src/ShaderCompiler.cpp index a3afb8fa6..d3097b6cb 100644 --- a/Engine/Source/Render/Src/ShaderCompiler.cpp +++ b/Engine/Source/Render/Src/ShaderCompiler.cpp @@ -13,6 +13,8 @@ #else #define __EMULATE_UUID 1 #endif +#include "Core/Thread.h" + #include #if PLATFORM_WINDOWS @@ -30,7 +32,26 @@ using namespace Microsoft::WRL; #include #include +#include #include +#include + +namespace Render::Internal { + static std::vector GetPresetIncludeDirectories() + { + std::vector result; + return result; + } + + static std::vector TranslateIncludeDirectories(const std::vector& inDir) + { + std::vector result; + for (const auto& dir : inDir) { + result.emplace_back(Core::Paths::Translate(dir).String()); + } + return result; + } +} namespace Render { #if PLATFORM_WINDOWS @@ -108,10 +129,10 @@ namespace Render { }; } - static std::vector GetIncludePathArguments(const ShaderCompileOptions& options) + static std::vector GetIncludePathArguments(const ShaderCompileInput& input) { std::vector result; - for (const auto& includePath : options.includePaths) { + for (const auto& includePath : input.includeDirectories) { result.emplace_back(L"-I"); result.emplace_back(Common::StringUtils::ToWideString(includePath)); } @@ -209,19 +230,19 @@ namespace Render { std::vector> resourceBindings; for (const spirv_cross::Resource& uniformBuffer : shaderResources.uniform_buffers) { - resourceBindings.emplace_back(std::make_pair(&uniformBuffer, RHI::BindingType::uniformBuffer)); + resourceBindings.emplace_back(&uniformBuffer, RHI::BindingType::uniformBuffer); } for (const spirv_cross::Resource& image : shaderResources.separate_images) { - resourceBindings.emplace_back(std::make_pair(&image, RHI::BindingType::texture)); + resourceBindings.emplace_back(&image, RHI::BindingType::texture); } for (const spirv_cross::Resource& sampler : shaderResources.separate_samplers) { - resourceBindings.emplace_back(std::make_pair(&sampler, RHI::BindingType::sampler)); + resourceBindings.emplace_back(&sampler, RHI::BindingType::sampler); } for (const spirv_cross::Resource& buffer : shaderResources.storage_buffers) { - resourceBindings.emplace_back(std::make_pair(&buffer, RHI::BindingType::storageBuffer)); + resourceBindings.emplace_back(&buffer, RHI::BindingType::storageBuffer); } for (const spirv_cross::Resource& image : shaderResources.storage_images) { - resourceBindings.emplace_back(std::make_pair(&image, RHI::BindingType::storageTexture)); + resourceBindings.emplace_back(&image, RHI::BindingType::storageTexture); } for (const auto& iter : resourceBindings) { // NOLINT @@ -261,7 +282,7 @@ namespace Render { std::vector arguments = GetDXCBaseArguments(options); const auto entryPointArgs = GetEntryPointArguments(input); const auto targetProfileArgs = GetTargetProfileArguments(input); - const auto includePathArgs = GetIncludePathArguments(options); + const auto includePathArgs = GetIncludePathArguments(input); const auto definitionArgs = GetDefinitionArguments(input, options); FillArguments(arguments, entryPointArgs); FillArguments(arguments, targetProfileArgs); @@ -297,6 +318,7 @@ namespace Render { output.success = true; const auto* codeStart = static_cast(codeBlob->GetBufferPointer()); const auto* codeEnd = codeStart + codeBlob->GetBufferSize(); + output.entryPoint = input.entryPoint; output.byteCode = std::vector(codeStart, codeEnd); if (options.byteCodeType == ShaderByteCodeType::dxil) { @@ -321,9 +343,9 @@ namespace Render { } namespace Render { - size_t ShaderTypeAndVariantHashProvider::operator()(const std::pair& value) const + size_t ShaderTypeAndVariantHashProvider::operator()(const std::pair& value) const { - return Common::HashUtils::CityHash(&value, sizeof(std::pair)); + return Common::HashUtils::CityHash(&value, sizeof(std::pair)); } ShaderCompiler& ShaderCompiler::Get() @@ -360,58 +382,81 @@ namespace Render { ShaderTypeCompiler::~ShaderTypeCompiler() = default; - std::future ShaderTypeCompiler::Compile(const std::vector& inShaderTypes, const ShaderCompileOptions& inOptions) + std::future ShaderTypeCompiler::Compile(const std::vector& inShaderTypes, const ShaderCompileOptions& inOptions) { + Assert(Core::ThreadContext::IsGameThread()); + return threadPool.EmplaceTask([inShaderTypes, inOptions]() -> ShaderTypeCompileResult { - std::unordered_map>> compileOutputs; + ShaderArtifactRegistry& artifactRegistry = ShaderArtifactRegistry::Get(); + + std::unique_lock lock(artifactRegistry.mutexGT); + auto& typeArtifactGT = artifactRegistry.typeArtifactsGT; + + std::unordered_map>> compileOutputs; compileOutputs.reserve(inShaderTypes.size()); - for (auto* shaderType : inShaderTypes) { + + for (const auto* shaderType : inShaderTypes) { auto typeKey = shaderType->GetKey(); - auto stage = shaderType->GetStage(); + auto sourceFile = Core::Paths::Translate(shaderType->GetSourceFile()).String(); + + auto includeDirectories = Common::VectorUtils::Combine(Internal::GetPresetIncludeDirectories(), shaderType->GetIncludeDirectories()); + includeDirectories = Common::VectorUtils::Combine(includeDirectories, inOptions.includeDirectories); + includeDirectories = Internal::TranslateIncludeDirectories(includeDirectories); + const auto oldHash = typeArtifactGT.contains(typeKey) ? typeArtifactGT.at(typeKey).sourceHash : shaderSourceHashNotCompiled; + const auto newHash = ShaderUtils::ComputeShaderSourceHash(sourceFile, includeDirectories); // NOLINT + + if (oldHash != shaderSourceHashNotCompiled && oldHash == newHash) { + continue; + } + typeArtifactGT[typeKey].sourceHash = newHash; + typeArtifactGT[typeKey].variantArtifacts.clear(); + + const auto stage = shaderType->GetStage(); const auto& entryPoint = shaderType->GetEntryPoint(); - const auto& code = shaderType->GetCode(); + const auto& variantFields = shaderType->GetVariantFields(); + const auto source = Common::FileUtils::ReadTextFile(sourceFile); Assert(!compileOutputs.contains(typeKey)); - compileOutputs.emplace(std::make_pair(typeKey, std::unordered_map> {})); + compileOutputs.emplace(std::make_pair(typeKey, std::unordered_map> {})); auto& variantCompileOutputs = compileOutputs.at(typeKey); - for ( const auto& variants = shaderType->GetVariants(); - const auto& variantKey : variants) { + for (const auto& variantSet : ShaderUtils::GetAllVariants(variantFields)) { + const auto variantKey = ShaderUtils::ComputeVariantKey(variantFields, variantSet); + ShaderCompileInput input {}; - input.source = code; + input.source = source; input.entryPoint = entryPoint; input.stage = stage; - input.definitions = shaderType->GetDefinitions(variantKey); + input.definitions = ShaderUtils::ComputeVariantDefinitions(variantFields, variantSet); + input.includeDirectories = includeDirectories; - variantCompileOutputs.emplace(std::make_pair(variantKey, ShaderCompiler::Get().Compile(input, inOptions))); + variantCompileOutputs.emplace(variantKey, ShaderCompiler::Get().Compile(input, inOptions)); } } ShaderTypeCompileResult result; for (auto& [typeKey, variantCompileOutputs] : compileOutputs) { - ShaderArchivePackage archivePackage; - + auto& typeArtifact = typeArtifactGT.at(typeKey); for (auto& [variantKey, compileFuture] : variantCompileOutputs) { ShaderCompileOutput output = compileFuture.get(); // NOLINT if (output.success) { - ShaderArchive archive; - archive.byteCode = std::move(output.byteCode); - archive.reflectionData = std::move(output.reflectionData); - - archivePackage.emplace(std::make_pair(variantKey, std::move(archive))); + ShaderVariantArtifact variantArtifact; + variantArtifact.entryPoint = output.entryPoint; + variantArtifact.byteCode = std::move(output.byteCode); + variantArtifact.reflectionData = std::move(output.reflectionData); + typeArtifact.variantArtifacts.emplace(variantKey, std::move(variantArtifact)); } else { result.errorInfos.emplace(std::make_pair(std::make_pair(typeKey, variantKey), output.errorInfo)); } } - ShaderArchiveStorage::Get().UpdateShaderArchivePackage(typeKey, std::move(archivePackage)); } result.success = result.errorInfos.empty(); return result; }); } - std::future ShaderTypeCompiler::CompileGlobalShaderTypes(const ShaderCompileOptions& inOptions) + std::future ShaderTypeCompiler::CompileAll(const ShaderCompileOptions& inOptions) { - return Compile(GlobalShaderRegistry::Get().GetShaderTypes(), inOptions); + return Compile(ShaderTypeRegistry::Get().AllTypes(), inOptions); } } diff --git a/Engine/Source/Render/Test/ShaderTest.cpp b/Engine/Source/Render/Test/ShaderTest.cpp index bdec663c5..7eed2e8ad 100644 --- a/Engine/Source/Render/Test/ShaderTest.cpp +++ b/Engine/Source/Render/Test/ShaderTest.cpp @@ -6,46 +6,131 @@ #include -class TestGlobalShaderVS : public Render::GlobalShader { -public: - ShaderInfo( - "TestGlobalShader", +class TestGlobalShaderVS final : public Render::StaticShaderType { + ShaderTypeInfo( + TestGlobalShaderVS, + RHI::ShaderStageBits::sVertex, "Engine/Shader/Test/TestGlobalShader.esl", - "VSMain", - RHI::ShaderStageBits::sVertex); + "VSMain") - BoolShaderVariantField(TestBoolVariant, "TEST_BOOL"); - RangedIntShaderVariantField(TestRangedIntVariant, "TEST_RANGED_INT", 0, 3); - VariantSet(TestBoolVariant, TestRangedIntVariant); + DeclBoolVariantField(TestBool, TEST_BOOL, false) + DeclRangedIntVariantField(TestRangedInt, TEST_RANGED_INT, 0, 0, 3) + MakeVariantFieldVec(TestBool, TestRangedInt) - DefaultVariantFilter + BeginIncludeDirectories + "Engine/Shader/Test" + EndIncludeDirectories }; -//RegisterGlobalShader(TestGlobalShaderVS); -TEST(ShaderTest, StaticVariantSetTest) +ImplementStaticShaderType(TestGlobalShaderVS) + +class TestVertexFactory final : public Render::StaticVertexFactoryType { + VertexFactoryTypeInfo( + TestVertexFactory, + "Engine/Shader/Test/VertexFactory.esh") + + DeclBoolVariantField(TestBool, TEST_BOOL, false) + MakeVariantFieldVec(TestBool) + + DeclVertexInput(BaseColorInput, BaseColor, RHI::VertexFormat::float32X3, 0) + MakeVertexInputVec(BaseColorInput) + + BeginSupportedMaterialTypes + Render::MaterialType::surface + EndSupportedMaterialTypes +}; + +ImplementStaticVertexFactoryType(TestVertexFactory) + +TEST(ShaderTest, StaticShaderTypeTest) +{ + const auto& testGlobalShaderVS = TestGlobalShaderVS::Get(); + ASSERT_EQ(testGlobalShaderVS.GetName(), "TestGlobalShaderVS"); + ASSERT_EQ(testGlobalShaderVS.GetStage(), RHI::ShaderStageBits::sVertex); + ASSERT_EQ(testGlobalShaderVS.GetSourceFile(), "Engine/Shader/Test/TestGlobalShader.esl"); + ASSERT_EQ(testGlobalShaderVS.GetEntryPoint(), "VSMain"); + + const auto& resultIncludeDirectories = testGlobalShaderVS.GetIncludeDirectories(); + const std::vector aspectIncludes = { "Engine/Shader/Test" }; + ASSERT_EQ(resultIncludeDirectories, aspectIncludes); + + const auto& resultVariantFields = testGlobalShaderVS.GetVariantFields(); + Render::ShaderVariantFieldVec aspectVariantFields; + aspectVariantFields.emplace_back(Render::ShaderBoolVariantField { "TEST_BOOL", false }); + aspectVariantFields.emplace_back(Render::ShaderRangedIntVariantField { "TEST_RANGED_INT", 0, { 0, 3 } }); + ASSERT_EQ(resultVariantFields, aspectVariantFields); +} + +TEST(ShaderTest, ComputeVariantKeyTest) { - ASSERT_EQ(TestGlobalShaderVS::VariantSet::VariantNum(), 8); - - const std::set> expectVariants = { - { false, 0 }, - { false, 1 }, - { false, 2 }, - { false, 3 }, - { true, 0 }, - { true, 1 }, - { true, 2 }, - { true, 3 } + const Render::ShaderVariantFieldVec variantFields = { + Render::ShaderBoolVariantField { "TEST_BOOL", false }, + Render::ShaderRangedIntVariantField { "TEST_RANGED_INT", 1, { 1, 4 } } }; - std::vector> actualVariants; - TestGlobalShaderVS::VariantSet::TraverseAll([&actualVariants](auto&& variantSet) -> void { - actualVariants.emplace_back( - variantSet.template Get(), - variantSet.template Get() - ); - }); - - ASSERT_EQ(expectVariants.size(), actualVariants.size()); - for (const auto& variant : actualVariants) { - ASSERT_EQ(expectVariants.contains(variant), true); + const Render::ShaderVariantValueMap variantSet = { + { "TEST_BOOL", true }, + { "TEST_RANGED_INT", 2 } + }; + ASSERT_EQ(Render::ShaderUtils::ComputeVariantKey(variantFields, variantSet), 3); +} + +TEST(ShaderTest, GetAllVariantsTest) +{ + const Render::ShaderVariantFieldVec variantFields = { + Render::ShaderBoolVariantField { "TEST_BOOL", false }, + Render::ShaderRangedIntVariantField { "TEST_RANGED_INT", 1, { 1, 4 } } + }; + + const auto resultVariants = Render::ShaderUtils::GetAllVariants(variantFields); + const std::vector aspectVariants = { + { { "TEST_BOOL", false }, { "TEST_RANGED_INT", 1 } }, + { { "TEST_BOOL", true }, { "TEST_RANGED_INT", 1 } }, + { { "TEST_BOOL", false }, { "TEST_RANGED_INT", 2 } }, + { { "TEST_BOOL", true }, { "TEST_RANGED_INT", 2 } }, + { { "TEST_BOOL", false }, { "TEST_RANGED_INT", 3 } }, + { { "TEST_BOOL", true }, { "TEST_RANGED_INT", 3 } }, + { { "TEST_BOOL", false }, { "TEST_RANGED_INT", 4 } }, + { { "TEST_BOOL", true }, { "TEST_RANGED_INT", 4 } }, + }; + ASSERT_EQ(resultVariants, aspectVariants); +} + +TEST(ShaderTest, ComputeVariantDefinitionsTest) +{ + const Render::ShaderVariantFieldVec variantFields = { + Render::ShaderBoolVariantField { "TEST_BOOL", false }, + Render::ShaderRangedIntVariantField { "TEST_RANGED_INT", 1, { 1, 4 } } + }; + const Render::ShaderVariantValueMap variantSet = { + { "TEST_BOOL", true }, + { "TEST_RANGED_INT", 2 } + }; + const std::vector definitions = { + "TEST_BOOL=1", + "TEST_RANGED_INT=2" + }; + ASSERT_EQ(Render::ShaderUtils::ComputeVariantDefinitions(variantFields, variantSet), definitions); +} + +TEST(ShaderTest, VertexFactoryTest) +{ + const auto& testVertexFactory = TestVertexFactory::Get(); + ASSERT_EQ(testVertexFactory.GetName(), "TestVertexFactory"); + ASSERT_EQ(testVertexFactory.GetSourceFile(), "Engine/Shader/Test/VertexFactory.esh"); + + const auto& resultVariantFields = testVertexFactory.GetVariantFields(); + Render::ShaderVariantFieldVec aspectVariantFields; + aspectVariantFields.emplace_back(Render::ShaderBoolVariantField { "TEST_BOOL", false }); + ASSERT_EQ(resultVariantFields, aspectVariantFields); + + const auto& resultInputs = testVertexFactory.GetVertexInputs(); + Render::VertexFactoryInputVec aspectInputs; + aspectInputs.emplace_back(Render::VertexFactoryInput { "BaseColor", RHI::VertexFormat::float32X3, 0 }); + ASSERT_EQ(resultInputs, aspectInputs); + + const auto aspectSupportedMaterialTypes = std::unordered_set { Render::MaterialType::surface }; + for (auto i = 0; i < static_cast(Render::MaterialType::max); i++) { + const auto type = static_cast(i); + ASSERT_EQ(testVertexFactory.SupportMaterialType(type), aspectSupportedMaterialTypes.contains(type)); } } diff --git a/Engine/Source/Runtime/Src/Engine.cpp b/Engine/Source/Runtime/Src/Engine.cpp index c5c76013c..612713b6e 100644 --- a/Engine/Source/Runtime/Src/Engine.cpp +++ b/Engine/Source/Runtime/Src/Engine.cpp @@ -69,6 +69,7 @@ namespace Runtime { renderThread.EmplaceTask([]() -> void { Core::ThreadContext::IncFrameNumber(); Core::Console::Get().PerformRenderThreadSettingsCopy(); + Render::ShaderArtifactRegistry::Get().PerformThreadCopy(); }); for (auto* world : worlds) { diff --git a/Engine/Source/Runtime/Test/WorldTest.cpp b/Engine/Source/Runtime/Test/WorldTest.cpp index e5051b1e1..39b64d59a 100644 --- a/Engine/Source/Runtime/Test/WorldTest.cpp +++ b/Engine/Source/Runtime/Test/WorldTest.cpp @@ -5,6 +5,7 @@ #include #include #include +#include using namespace Runtime; struct WorldTest : testing::Test { diff --git a/Sample/Base/Application.cpp b/Sample/Base/Application.cpp index f90af8f05..b28f44981 100644 --- a/Sample/Base/Application.cpp +++ b/Sample/Base/Application.cpp @@ -262,27 +262,25 @@ Application::ShaderCompileOutput Application::CompileShader(const std::string& f { std::string shaderSource = FileUtils::ReadTextFile(fileName); - Render::ShaderCompileInput info; - info.source = shaderSource; - info.entryPoint = entryPoint; - info.stage = shaderStage; + Render::ShaderCompileInput input; + input.source = shaderSource; + input.entryPoint = entryPoint; + input.stage = shaderStage; + input.includeDirectories.emplace_back("../Test/Sample/ShaderInclude"); Render::ShaderCompileOptions options; - options.includePaths.emplace_back("../Test/Sample/ShaderInclude"); - options.includePaths.insert(options.includePaths.end(), includePaths.begin(), includePaths.end()); - if (rhiType == RHI::RHIType::directX12) { options.byteCodeType = Render::ShaderByteCodeType::dxil; } else { options.byteCodeType = Render::ShaderByteCodeType::spirv; } options.withDebugInfo = false; - auto future = Render::ShaderCompiler::Get().Compile(info, options); + auto future = Render::ShaderCompiler::Get().Compile(input, options); future.wait(); auto compileOutput = future.get(); if (!compileOutput.success) { - std::cout << "failed to compiler shader (" << fileName << ", " << info.entryPoint << ")" << '\n' << compileOutput.errorInfo << std::endl; + std::cout << "failed to compiler shader (" << fileName << ", " << input.entryPoint << ")" << '\n' << compileOutput.errorInfo << std::endl; } Assert(compileOutput.success); diff --git a/Sample/Rendering-Triangle/Triangle.cpp b/Sample/Rendering-Triangle/Triangle.cpp index 49bc77b80..07109fa69 100644 --- a/Sample/Rendering-Triangle/Triangle.cpp +++ b/Sample/Rendering-Triangle/Triangle.cpp @@ -16,32 +16,31 @@ struct Vertex { FVec3 position; }; -class TriangleVS : public GlobalShader { -public: - ShaderInfo( - "TriangleVS", +class TriangleVS final : public StaticShaderType { + ShaderTypeInfo( + TriangleVS, + RHI::ShaderStageBits::sVertex, "Engine/Test/Sample/Rendering-Triangle/Triangle.esl", - "VSMain", - RHI::ShaderStageBits::sVertex); + "VSMain") - NonVariant; - DefaultVariantFilter; + EmptyIncludeDirectories + EmptyVariantFieldVec }; -class TrianglePS : public GlobalShader { +class TrianglePS final : public StaticShaderType { public: - ShaderInfo( - "TrianglePS", + ShaderTypeInfo( + TrianglePS, + RHI::ShaderStageBits::sPixel, "Engine/Test/Sample/Rendering-Triangle/Triangle.esl", - "PSMain", - RHI::ShaderStageBits::sPixel); + "PSMain") - NonVariant; - DefaultVariantFilter; + EmptyIncludeDirectories + EmptyVariantFieldVec }; -RegisterGlobalShader(TriangleVS); -RegisterGlobalShader(TrianglePS); +ImplementStaticShaderType(TriangleVS); +ImplementStaticShaderType(TrianglePS); struct PsUniform { FVec3 pixelColor; @@ -60,7 +59,8 @@ class TriangleApplication final : public Application { static constexpr size_t backBufferCount = 2; void CreateDevice(); - void CompileAllShaders(); + void CompileAllShaders() const; + void FetchShaderInstances(); void CreateSwapChain(); void CreateTriangleVertexBuffer(); void CreateSyncObjects(); @@ -89,100 +89,114 @@ TriangleApplication::~TriangleApplication() = default; void TriangleApplication::OnCreate() { - RenderWorkerThreads::Get().Start(); - CreateDevice(); CompileAllShaders(); - CreateSwapChain(); - CreateTriangleVertexBuffer(); - CreateSyncObjects(); + RenderThread::Get().Start(); + RenderWorkerThreads::Get().Start(); + + RenderThread::Get().EmplaceTask([this]() -> void { + CreateDevice(); + FetchShaderInstances(); + CreateSwapChain(); + CreateTriangleVertexBuffer(); + CreateSyncObjects(); + }); } void TriangleApplication::OnDrawFrame() { - frameFence->Reset(); - const auto backTextureIndex = swapChain->AcquireBackTexture(imageReadySemaphore.Get()); - - auto* pso = PipelineCache::Get(*device).GetOrCreate( - RasterPipelineStateDesc() - .SetVertexShader(triangleVS) - .SetPixelShader(trianglePS) - .SetVertexState( - RVertexState() - .AddVertexBufferLayout( - RVertexBufferLayout(VertexStepMode::perVertex, sizeof(Vertex)) - .AddAttribute(RVertexAttribute(RVertexBinding("POSITION", 0), VertexFormat::float32X3, offsetof(Vertex, position))))) - .SetFragmentState( - RFragmentState() - .AddColorTarget(ColorTargetState(swapChainFormat, ColorWriteBits::all, false)))); - - RGBuilder builder(*device); - auto* backTexture = builder.ImportTexture(swapChainTextures[backTextureIndex], TextureState::present); - auto* backTextureView = builder.CreateTextureView(backTexture, RGTextureViewDesc(TextureViewType::colorAttachment, TextureViewDimension::tv2D)); - auto* vertexBuffer = builder.ImportBuffer(triangleVertexBuffer.Get(), BufferState::shaderReadOnly); - auto* vertexBufferView = builder.CreateBufferView(vertexBuffer, RGBufferViewDesc(BufferViewType::vertex, vertexBuffer->GetDesc().size, 0, VertexBufferViewInfo(sizeof(Vertex)))); - auto* psUniformBuffer = builder.CreateBuffer(RGBufferDesc(sizeof(PsUniform), BufferUsageBits::uniform | BufferUsageBits::mapWrite, BufferState::staging, "psUniform")); - auto* psUniformBufferView = builder.CreateBufferView(psUniformBuffer, RGBufferViewDesc(BufferViewType::uniformBinding, sizeof(PsUniform))); - - auto* bindGroup = builder.AllocateBindGroup( - RGBindGroupDesc::Create(pso->GetPipelineLayout()->GetBindGroupLayout(0)) - .UniformBuffer("psUniform", psUniformBufferView)); - - PsUniform psUniform {}; - psUniform.pixelColor = FVec3( - (std::sin(GetCurrentTimeSeconds()) + 1) / 2, - (std::cos(GetCurrentTimeSeconds()) + 1) / 2, - std::abs(std::sin(GetCurrentTimeSeconds()))); - - builder.QueueBufferUpload( - psUniformBuffer, - RGBufferUploadInfo(&psUniform, sizeof(PsUniform))); - - builder.AddRasterPass( - "BasePass", - RGRasterPassDesc() - .AddColorAttachment(RGColorAttachment(backTextureView, LoadOp::clear, StoreOp::store)), - { bindGroup }, - [pso, vertexBufferView, bindGroup, viewportWidth = GetWindowWidth(), viewportHeight = GetWindowHeight()](const RGBuilder& rg, RasterPassCommandRecorder& recorder) -> void { - recorder.SetPipeline(pso->GetRHI()); - recorder.SetScissor(0, 0, viewportWidth, viewportHeight); - recorder.SetViewport(0, 0, static_cast(viewportWidth), static_cast(viewportHeight), 0, 1); - recorder.SetVertexBuffer(0, rg.GetRHI(vertexBufferView)); - recorder.SetPrimitiveTopology(PrimitiveTopology::triangleList); - recorder.SetBindGroup(0, rg.GetRHI(bindGroup)); - recorder.Draw(3, 1, 0, 0); - }, - {}, - [backTexture](const RGBuilder& rg, CommandRecorder& recorder) -> void { - recorder.ResourceBarrier(Barrier::Transition(rg.GetRHI(backTexture), TextureState::renderTarget, TextureState::present)); - }); - - RGExecuteInfo executeInfo; - executeInfo.semaphoresToWait = { imageReadySemaphore.Get() }; - executeInfo.semaphoresToSignal = { renderFinishedSemaphore.Get() }; - executeInfo.inFenceToSignal = frameFence.Get(); - builder.Execute(executeInfo); - swapChain->Present(renderFinishedSemaphore.Get()); - frameFence->Wait(); - - Core::ThreadContext::IncFrameNumber(); - BufferPool::Get(*device).Forfeit(); - TexturePool::Get(*device).Forfeit(); - ResourceViewCache::Get(*device).Forfeit(); - BindGroupCache::Get(*device).Forfeit(); + RenderThread::Get().EmplaceTask([this]() -> void { + frameFence->Reset(); + const auto backTextureIndex = swapChain->AcquireBackTexture(imageReadySemaphore.Get()); + + auto* pso = PipelineCache::Get(*device).GetOrCreate( + RasterPipelineStateDesc() + .SetVertexShader(triangleVS) + .SetPixelShader(trianglePS) + .SetVertexState( + RVertexState() + .AddVertexBufferLayout( + RVertexBufferLayout(VertexStepMode::perVertex, sizeof(Vertex)) + .AddAttribute(RVertexAttribute(RVertexBinding("POSITION", 0), VertexFormat::float32X3, offsetof(Vertex, position))))) + .SetFragmentState( + RFragmentState() + .AddColorTarget(ColorTargetState(swapChainFormat, ColorWriteBits::all, false)))); + + RGBuilder builder(*device); + auto* backTexture = builder.ImportTexture(swapChainTextures[backTextureIndex], TextureState::present); + auto* backTextureView = builder.CreateTextureView(backTexture, RGTextureViewDesc(TextureViewType::colorAttachment, TextureViewDimension::tv2D)); + auto* vertexBuffer = builder.ImportBuffer(triangleVertexBuffer.Get(), BufferState::shaderReadOnly); + auto* vertexBufferView = builder.CreateBufferView(vertexBuffer, RGBufferViewDesc(BufferViewType::vertex, vertexBuffer->GetDesc().size, 0, VertexBufferViewInfo(sizeof(Vertex)))); + auto* psUniformBuffer = builder.CreateBuffer(RGBufferDesc(sizeof(PsUniform), BufferUsageBits::uniform | BufferUsageBits::mapWrite, BufferState::staging, "psUniform")); + auto* psUniformBufferView = builder.CreateBufferView(psUniformBuffer, RGBufferViewDesc(BufferViewType::uniformBinding, sizeof(PsUniform))); + + auto* bindGroup = builder.AllocateBindGroup( + RGBindGroupDesc::Create(pso->GetPipelineLayout()->GetBindGroupLayout(0)) + .UniformBuffer("psUniform", psUniformBufferView)); + + PsUniform psUniform {}; + psUniform.pixelColor = FVec3( + (std::sin(GetCurrentTimeSeconds()) + 1) / 2, + (std::cos(GetCurrentTimeSeconds()) + 1) / 2, + std::abs(std::sin(GetCurrentTimeSeconds()))); + + builder.QueueBufferUpload( + psUniformBuffer, + RGBufferUploadInfo(&psUniform, sizeof(PsUniform))); + + builder.AddRasterPass( + "BasePass", + RGRasterPassDesc() + .AddColorAttachment(RGColorAttachment(backTextureView, LoadOp::clear, StoreOp::store)), + { bindGroup }, + [pso, vertexBufferView, bindGroup, viewportWidth = GetWindowWidth(), viewportHeight = GetWindowHeight()](const RGBuilder& rg, RasterPassCommandRecorder& recorder) -> void { + recorder.SetPipeline(pso->GetRHI()); + recorder.SetScissor(0, 0, viewportWidth, viewportHeight); + recorder.SetViewport(0, 0, static_cast(viewportWidth), static_cast(viewportHeight), 0, 1); + recorder.SetVertexBuffer(0, rg.GetRHI(vertexBufferView)); + recorder.SetPrimitiveTopology(PrimitiveTopology::triangleList); + recorder.SetBindGroup(0, rg.GetRHI(bindGroup)); + recorder.Draw(3, 1, 0, 0); + }, + {}, + [backTexture](const RGBuilder& rg, CommandRecorder& recorder) -> void { + recorder.ResourceBarrier(Barrier::Transition(rg.GetRHI(backTexture), TextureState::renderTarget, TextureState::present)); + }); + + RGExecuteInfo executeInfo; + executeInfo.semaphoresToWait = { imageReadySemaphore.Get() }; + executeInfo.semaphoresToSignal = { renderFinishedSemaphore.Get() }; + executeInfo.inFenceToSignal = frameFence.Get(); + builder.Execute(executeInfo); + swapChain->Present(renderFinishedSemaphore.Get()); + frameFence->Wait(); + + Core::ThreadContext::IncFrameNumber(); + BufferPool::Get(*device).Forfeit(); + TexturePool::Get(*device).Forfeit(); + ResourceViewCache::Get(*device).Forfeit(); + BindGroupCache::Get(*device).Forfeit(); + }); + + // TODO in sample, just sync with render thread every frame, maybe later need a better render-thread based application class + RenderThread::Get().Flush(); } void TriangleApplication::OnDestroy() { - const UniquePtr fence = device->CreateFence(false); - device->GetQueue(QueueType::graphics, 0)->Flush(fence.Get()); - fence->Wait(); - - BindGroupCache::Get(*device).Invalidate(); - PipelineCache::Get(*device).Invalidate(); - BufferPool::Get(*device).Invalidate(); - TexturePool::Get(*device).Invalidate(); - GlobalShaderRegistry::Get().Invalidate(); + RenderThread::Get().EmplaceTask([this]() -> void { + const UniquePtr fence = device->CreateFence(false); + device->GetQueue(QueueType::graphics, 0)->Flush(fence.Get()); + fence->Wait(); + + BindGroupCache::Get(*device).Invalidate(); + PipelineCache::Get(*device).Invalidate(); + BufferPool::Get(*device).Invalidate(); + TexturePool::Get(*device).Invalidate(); + }); + RenderThread::Get().Flush(); + RenderWorkerThreads::Get().Stop(); + RenderThread::Get().Stop(); } void TriangleApplication::CreateDevice() @@ -194,18 +208,22 @@ void TriangleApplication::CreateDevice() .AddQueueRequest(QueueRequestInfo(QueueType::graphics, 1))); } -void TriangleApplication::CompileAllShaders() +void TriangleApplication::CompileAllShaders() const { ShaderCompileOptions options; - options.includePaths = { "../Test/Sample/ShaderInclude", "../Test/Sample/Rendering-Triangle" }; + options.includeDirectories = {"../Test/Sample/ShaderInclude", "../Test/Sample/Rendering-Triangle"}; options.byteCodeType = GetRHIType() == RHI::RHIType::directX12 ? ShaderByteCodeType::dxil : ShaderByteCodeType::spirv; options.withDebugInfo = false; - auto result = ShaderTypeCompiler::Get().CompileGlobalShaderTypes(options); + auto result = ShaderTypeCompiler::Get().CompileAll(options); const auto& [success, errorInfo] = result.get(); Assert(success); +} - triangleVS = GlobalShaderMap::Get().GetShaderInstance(*device, {}); - trianglePS = GlobalShaderMap::Get().GetShaderInstance(*device, {}); +void TriangleApplication::FetchShaderInstances() +{ + ShaderArtifactRegistry::Get().PerformThreadCopy(); + triangleVS = ShaderMap::Get(*device).GetShaderInstance(TriangleVS::Get(), {}); + trianglePS = ShaderMap::Get(*device).GetShaderInstance(TrianglePS::Get(), {}); } void TriangleApplication::CreateSwapChain()