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);