diff --git a/Source/Engine/Core/Types/StringView.h b/Source/Engine/Core/Types/StringView.h index 27c63c999..3e2bbd5f3 100644 --- a/Source/Engine/Core/Types/StringView.h +++ b/Source/Engine/Core/Types/StringView.h @@ -327,6 +327,18 @@ public: bool operator!=(const String& other) const; public: + using StringViewBase::StartsWith; + FORCE_INLINE bool StartsWith(const StringView& prefix, StringSearchCase searchCase = StringSearchCase::IgnoreCase) const + { + return StringViewBase::StartsWith(prefix, searchCase); + } + + using StringViewBase::EndsWith; + FORCE_INLINE bool EndsWith(const StringView& suffix, StringSearchCase searchCase = StringSearchCase::IgnoreCase) const + { + return StringViewBase::EndsWith(suffix, searchCase); + } + /// /// Gets the left most given number of characters. /// @@ -511,6 +523,18 @@ public: bool operator!=(const StringAnsi& other) const; public: + using StringViewBase::StartsWith; + FORCE_INLINE bool StartsWith(const StringAnsiView& prefix, StringSearchCase searchCase = StringSearchCase::IgnoreCase) const + { + return StringViewBase::StartsWith(prefix, searchCase); + } + + using StringViewBase::EndsWith; + FORCE_INLINE bool EndsWith(const StringAnsiView& suffix, StringSearchCase searchCase = StringSearchCase::IgnoreCase) const + { + return StringViewBase::EndsWith(suffix, searchCase); + } + /// /// Retrieves substring created from characters starting from startIndex to the String end. /// diff --git a/Source/Engine/ShadersCompilation/ShaderCompiler.cpp b/Source/Engine/ShadersCompilation/ShaderCompiler.cpp index c0fda4d6e..da67ef3b4 100644 --- a/Source/Engine/ShadersCompilation/ShaderCompiler.cpp +++ b/Source/Engine/ShadersCompilation/ShaderCompiler.cpp @@ -3,6 +3,7 @@ #if COMPILE_WITH_SHADER_COMPILER #include "ShaderCompiler.h" +#include "ShadersCompilation.h" #include "Engine/Core/Log.h" #include "Engine/Core/Collections/Dictionary.h" #include "Engine/Engine/Globals.h" @@ -14,10 +15,6 @@ #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Serialization/MemoryWriteStream.h" #include "Engine/Utilities/StringConverter.h" -#if USE_EDITOR -#include "Editor/Editor.h" -#include "Editor/ProjectInfo.h" -#endif namespace IncludedFiles { @@ -30,31 +27,6 @@ namespace IncludedFiles CriticalSection Locker; Dictionary Files; - -#if USE_EDITOR - bool FindProject(const ProjectInfo* project, HashSet& projects, const StringView& projectName, String& path) - { - if (!project || projects.Contains(project)) - return false; - projects.Add(project); - - // Check the project name - if (project->Name == projectName) - { - path = project->ProjectFolderPath; - return true; - } - - // Initialize referenced projects - for (const auto& reference : project->References) - { - if (reference.Project && FindProject(reference.Project, projects, projectName, path)) - return true; - } - - return false; - } -#endif } bool ShaderCompiler::Compile(ShaderCompilationContext* context) @@ -124,7 +96,8 @@ bool ShaderCompiler::Compile(ShaderCompilationContext* context) output->WriteInt32(context->Includes.Count()); for (auto& include : context->Includes) { - output->WriteString(include.Item, 11); + String compactPath = ShadersCompilation::CompactShaderPath(include.Item); + output->WriteString(compactPath, 11); const auto date = FileSystem::GetFileLastEditTime(include.Item); output->Write(date); } @@ -138,75 +111,17 @@ bool ShaderCompiler::GetIncludedFileSource(ShaderCompilationContext* context, co source = nullptr; sourceLength = 0; - // Skip to the last root start './' but preserve the leading one - const int32 includedFileLength = StringUtils::Length(includedFile); - for (int32 i = includedFileLength - 2; i >= 2; i--) + // Get actual file path + const String includedFileName(includedFile); + String path = ShadersCompilation::ResolveShaderPath(includedFileName); + if (!FileSystem::FileExists(path)) { - if (StringUtils::Compare(includedFile + i, "./", 2) == 0) - { - includedFile = includedFile + i; - break; - } + LOG(Error, "Unknown shader source file '{0}' included in '{1}'.{2}", includedFileName, String(sourceFile), String::Empty); + return true; } ScopeLock lock(IncludedFiles::Locker); - // Find the included file path - String path; -#if USE_EDITOR - if (StringUtils::Compare(includedFile, "./", 2) == 0) - { - int32 projectNameEnd = -1; - for (int32 i = 2; i < includedFileLength; i++) - { - if (includedFile[i] == '/') - { - projectNameEnd = i; - break; - } - } - if (projectNameEnd == -1) - { - LOG(Error, "Unknown shader source file '{0}' included in '{1}'.{2}", String(includedFile), String(sourceFile), TEXT("Missing project name after root path.")); - return true; - } - const StringAsUTF16<120> projectName(includedFile + 2, projectNameEnd - 2); - if (StringUtils::Compare(projectName.Get(), TEXT("FlaxPlatforms")) == 0) - { - // Hard-coded redirect to platform-specific includes - path /= Globals::StartupFolder / TEXT("Source/Platforms"); - } - else - { - HashSet projects; - if (!IncludedFiles::FindProject(Editor::Project, projects, StringView(projectName.Get(), projectNameEnd - 2), path)) - { - LOG(Error, "Unknown shader source file '{0}' included in '{1}'.{2}", String(includedFile), String(sourceFile), TEXT("Failed to find the project of the given name.")); - return true; - } - path /= TEXT("Source/Shaders/"); - } - const StringAsUTF16<250> localPath(includedFile + projectNameEnd + 1, includedFileLength - projectNameEnd - 1); - path /= localPath.Get(); - } -#else - if (StringUtils::Compare(includedFile, "./Flax/", 7) == 0) - { - // Engine project relative shader path - const auto includedFileStr = String(includedFile + 6); - path = Globals::StartupFolder / TEXT("Source/Shaders") / includedFileStr; - } -#endif - else if (FileSystem::FileExists(path = String(includedFile))) - { - // Absolute shader path - } - else - { - LOG(Error, "Unknown shader source file '{0}' included in '{1}'.{2}", String(includedFile), String(sourceFile), String::Empty); - return true; - } - // Try to reuse file IncludedFiles::File* result = nullptr; if (!IncludedFiles::Files.TryGet(path, result) || FileSystem::GetFileLastEditTime(path) > result->LastEditTime) diff --git a/Source/Engine/ShadersCompilation/ShadersCompilation.cpp b/Source/Engine/ShadersCompilation/ShadersCompilation.cpp index de2ba6e54..793a3c1c3 100644 --- a/Source/Engine/ShadersCompilation/ShadersCompilation.cpp +++ b/Source/Engine/ShadersCompilation/ShadersCompilation.cpp @@ -33,7 +33,6 @@ #include "Editor/Editor.h" #include "Editor/ProjectInfo.h" #endif - #if COMPILE_WITH_D3D_SHADER_COMPILER #include "DirectX/ShaderCompilerD3D.h" #endif @@ -55,6 +54,49 @@ namespace ShadersCompilationImpl CriticalSection Locker; Array Compilers; Array ReadyCompilers; + +#if USE_EDITOR + const ProjectInfo* FindProjectByName(const ProjectInfo* project, HashSet& projects, const StringView& projectName) + { + if (!project || projects.Contains(project)) + return nullptr; + projects.Add(project); + + // Check the project name + if (project->Name == projectName) + return project; + + // Search referenced projects + for (const auto& reference : project->References) + { + const ProjectInfo* result = FindProjectByName(reference.Project, projects, projectName); + if (result) + return result; + } + return nullptr; + } + + const ProjectInfo* FindProjectByPath(const ProjectInfo* project, HashSet& projects, const StringView& projectPath) + { + if (!project || projects.Contains(project)) + return nullptr; + projects.Add(project); + + // Search referenced projects (depth first to handle plugin projects first) + for (const auto& reference : project->References) + { + const ProjectInfo* result = FindProjectByPath(reference.Project, projects, projectPath); + if (result) + return result; + } + + // Check the project path + if (projectPath.StartsWith(project->ProjectFolderPath)) + return project; + + return nullptr; + } +#endif } using namespace ShadersCompilationImpl; @@ -361,11 +403,89 @@ void ShadersCompilation::ExtractShaderIncludes(byte* shaderCache, int32 shaderCa { String& include = includes.AddOne(); stream.ReadString(&include, 11); + include = ShadersCompilation::ResolveShaderPath(include); DateTime lastEditTime; stream.Read(lastEditTime); } } +String ShadersCompilation::ResolveShaderPath(StringView path) +{ + // Skip to the last root start './' but preserve the leading one + for (int32 i = path.Length() - 2; i >= 2; i--) + { + if (StringUtils::Compare(path.Get() + i, TEXT("./"), 2) == 0) + { + path = path.Substring(i); + break; + } + } + + // Find the included file path + String result; +#if USE_EDITOR + if (path.StartsWith(StringView(TEXT("./"), 2))) + { + int32 projectNameEnd = -1; + for (int32 i = 2; i < path.Length(); i++) + { + if (path[i] == '/') + { + projectNameEnd = i; + break; + } + } + if (projectNameEnd == -1) + return String::Empty; // Invalid project path + StringView projectName = path.Substring(2, projectNameEnd - 2); + if (projectName.StartsWith(StringView(TEXT("FlaxPlatforms")))) + { + // Hard-coded redirect to platform-specific includes + result = Globals::StartupFolder / TEXT("Source/Platforms"); + } + else + { + HashSet projects; + const ProjectInfo* project = FindProjectByName(Editor::Project, projects, StringView(projectName.Get(), projectNameEnd - 2)); + if (project) + result = project->ProjectFolderPath / TEXT("/Source/Shaders/"); + else + return String::Empty; + } + result /= path.Substring(projectNameEnd + 1); + } +#else + if (path.StartsWith(StringView(TEXT("./Flax/"), 7))) + { + // Engine project relative shader path + result = Globals::StartupFolder / TEXT("Source/Shaders") / path.Substring(6); + } +#endif + else + { + // Absolute shader path + result = path; + } + + return result; +} + +String ShadersCompilation::CompactShaderPath(StringView path) +{ +#if USE_EDITOR + // Try to use file path relative to the project shader sources folder + HashSet projects; + const ProjectInfo* project = FindProjectByPath(Editor::Project, projects, path); + if (project) + { + String projectSourcesPath = project->ProjectFolderPath / TEXT("/Source/Shaders/"); + if (path.StartsWith(projectSourcesPath)) + return String::Format(TEXT("./{}/{}"), project->Name, path.Substring(projectSourcesPath.Length())); + } +#endif + return String(path); +} + #if USE_EDITOR namespace diff --git a/Source/Engine/ShadersCompilation/ShadersCompilation.h b/Source/Engine/ShadersCompilation/ShadersCompilation.h index fca6a6ebe..fd907acf7 100644 --- a/Source/Engine/ShadersCompilation/ShadersCompilation.h +++ b/Source/Engine/ShadersCompilation/ShadersCompilation.h @@ -14,7 +14,6 @@ class Asset; class FLAXENGINE_API ShadersCompilation { public: - /// /// Compiles the shader. /// @@ -43,6 +42,11 @@ public: /// The output included. static void ExtractShaderIncludes(byte* shaderCache, int32 shaderCacheLength, Array& includes); + // Resolves shader path name into absolute file path. Resolves './/ShaderFile.hlsl' cases into a full path. + static String ResolveShaderPath(StringView path); + // Compacts the full shader file path into portable format with project name prefix such as './/ShaderFile.hlsl'. + static String CompactShaderPath(StringView path); + private: static ShaderCompiler* CreateCompiler(ShaderProfile profile);