Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Graphics/GraphicsEngineVulkan/src/ShaderVkImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ std::vector<uint32_t> CompileShaderGLSLang(const ShaderCreateInfo& Shade
GLSLangUtils::GLSLtoSPIRVAttribs Attribs;
Attribs.ShaderType = ShaderCI.Desc.ShaderType;
Attribs.ShaderSource = SourceData.Source;
Attribs.SourceName = ShaderCI.FilePath;
Attribs.SourceCodeLen = static_cast<int>(SourceData.SourceLength);
Attribs.Version = GLSLangUtils::SpirvVersion::Vk100;
Attribs.Macros = Macros;
Expand Down
1 change: 1 addition & 0 deletions Graphics/GraphicsEngineWebGPU/src/ShaderWebGPUImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ std::vector<uint32_t> CompileShaderToSPIRV(const ShaderCreateInfo& S
GLSLangUtils::GLSLtoSPIRVAttribs Attribs;
Attribs.ShaderType = ShaderCI.Desc.ShaderType;
Attribs.ShaderSource = SourceData.Source;
Attribs.SourceName = ShaderCI.FilePath;
Attribs.SourceCodeLen = static_cast<int>(SourceData.SourceLength);
Attribs.Version = GLSLangUtils::SpirvVersion::Vk100;
Attribs.Macros = Macros;
Expand Down
1 change: 1 addition & 0 deletions Graphics/ShaderTools/include/GLSLangUtils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ struct GLSLtoSPIRVAttribs
{
SHADER_TYPE ShaderType = SHADER_TYPE_UNKNOWN;
const char* ShaderSource = nullptr;
const char* SourceName = nullptr;
int SourceCodeLen = 0;
ShaderMacroArray Macros;
IShaderSourceInputStreamFactory* pShaderSourceStreamFactory = nullptr;
Expand Down
71 changes: 52 additions & 19 deletions Graphics/ShaderTools/src/GLSLangUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include "DataBlobImpl.hpp"
#include "RefCntAutoPtr.hpp"
#include "ShaderToolsCommon.hpp"
#include "BasicFileSystem.hpp"
#ifdef USE_SPIRV_TOOLS
# include "SPIRVTools.hpp"
# include "spirv-tools/libspirv.h"
Expand Down Expand Up @@ -308,37 +309,38 @@ class IncluderImpl : public ::glslang::TShader::Includer
const char* /*includerName*/,
size_t /*inclusionDepth*/)
{
DEV_CHECK_ERR(m_pInputStreamFactory != nullptr, "The shader source contains #include directives, but no input stream factory was provided");
RefCntAutoPtr<IFileStream> pSourceStream;
m_pInputStreamFactory->CreateInputStream(headerName, &pSourceStream);
if (pSourceStream == nullptr)
IncludeResult* pInclude = ReadIncludeFile(headerName, CREATE_SHADER_SOURCE_INPUT_STREAM_FLAG_NONE);
if (pInclude == nullptr)
{
LOG_ERROR("Failed to open shader include file '", headerName, "'. Check that the file exists");
return nullptr;
}

RefCntAutoPtr<DataBlobImpl> pFileData = DataBlobImpl::Create();
pSourceStream->ReadBlob(pFileData);
IncludeResult* pNewInclude =
new IncludeResult{
headerName,
pFileData->GetConstDataPtr<char>(),
pFileData->GetSize(),
nullptr};

m_IncludeRes.emplace(pNewInclude);
m_DataBlobs.emplace(pNewInclude, std::move(pFileData));
return pNewInclude;
return pInclude;
}

// For the "local"-only aspect of a "" include. Should not search in the
// "system" paths, because on returning a failure, the parser will
// call includeSystem() to look in the "system" locations.
virtual IncludeResult* includeLocal(const char* headerName,
const char* includerName,
size_t inclusionDepth)
size_t /*inclusionDepth*/)
{
return nullptr;
if (m_pInputStreamFactory == nullptr)
return nullptr;

if (BasicFileSystem::IsPathAbsolute(headerName))
return ReadIncludeFile(headerName, CREATE_SHADER_SOURCE_INPUT_STREAM_FLAG_SILENT);

String ParentDir;
BasicFileSystem::GetPathComponents(includerName, &ParentDir, nullptr);
if (ParentDir.empty())
return nullptr;

const std::string LocalPath = BasicFileSystem::SimplifyPath(
(ParentDir + BasicFileSystem::SlashSymbol + headerName).c_str(),
BasicFileSystem::SlashSymbol);
return ReadIncludeFile(LocalPath.c_str(), CREATE_SHADER_SOURCE_INPUT_STREAM_FLAG_SILENT);
}

// Signals that the parser will no longer use the contents of the
Expand All @@ -349,6 +351,29 @@ class IncluderImpl : public ::glslang::TShader::Includer
}

private:
IncludeResult* ReadIncludeFile(const char* IncludePath, CREATE_SHADER_SOURCE_INPUT_STREAM_FLAGS Flags)
{
DEV_CHECK_ERR(m_pInputStreamFactory != nullptr, "The shader source contains #include directives, but no input stream factory was provided");

RefCntAutoPtr<IFileStream> pSourceStream;
m_pInputStreamFactory->CreateInputStream2(IncludePath, Flags, &pSourceStream);
if (pSourceStream == nullptr)
return nullptr;

RefCntAutoPtr<DataBlobImpl> pFileData = DataBlobImpl::Create();
pSourceStream->ReadBlob(pFileData);
IncludeResult* pNewInclude =
new IncludeResult{
IncludePath,
pFileData->GetConstDataPtr<char>(),
pFileData->GetSize(),
nullptr};

m_IncludeRes.emplace(pNewInclude);
m_DataBlobs.emplace(pNewInclude, std::move(pFileData));
return pNewInclude;
}

IShaderSourceInputStreamFactory* const m_pInputStreamFactory;
std::unordered_set<std::unique_ptr<IncludeResult>> m_IncludeRes;
std::unordered_map<IncludeResult*, RefCntAutoPtr<IDataBlob>> m_DataBlobs;
Expand Down Expand Up @@ -531,7 +556,15 @@ std::vector<unsigned int> GLSLtoSPIRV(const GLSLtoSPIRVAttribs& Attribs)

const char* ShaderStrings[] = {Attribs.ShaderSource};
int Lengths[] = {Attribs.SourceCodeLen};
Shader.setStringsWithLengths(ShaderStrings, Lengths, 1);
const char* Names[] = {Attribs.SourceName};
if (Attribs.SourceName != nullptr)
{
Shader.setStringsWithLengthsAndNames(ShaderStrings, Lengths, Names, 1);
}
else
{
Shader.setStringsWithLengths(ShaderStrings, Lengths, 1);
}

std::string Preamble;
if (Attribs.UseRowMajorMatrices)
Expand Down
43 changes: 37 additions & 6 deletions Graphics/ShaderTools/src/ShaderToolsCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,33 @@ static void ProcessIncludeErrorHandler(const ShaderCreateInfo& ShaderCI, const s
throw std::pair<std::string, std::string>{std::move(FileInfo), Error};
}

static std::string ResolveIncludePathForPreprocess(const ShaderCreateInfo& ShaderCI, const std::string& IncludeName)
{
if (ShaderCI.FilePath == nullptr || BasicFileSystem::IsPathAbsolute(IncludeName.c_str()))
return IncludeName;

String ParentDir;
BasicFileSystem::GetPathComponents(ShaderCI.FilePath, &ParentDir, nullptr);
if (ParentDir.empty())
return IncludeName;

const std::string RelativePath = BasicFileSystem::SimplifyPath(
(ParentDir + BasicFileSystem::SlashSymbol + IncludeName).c_str(),
BasicFileSystem::SlashSymbol);

if (ShaderCI.pShaderSourceStreamFactory != nullptr)
{
RefCntAutoPtr<IFileStream> pSourceStream;
ShaderCI.pShaderSourceStreamFactory->CreateInputStream2(RelativePath.c_str(), CREATE_SHADER_SOURCE_INPUT_STREAM_FLAG_SILENT, &pSourceStream);
if (pSourceStream)
return RelativePath;

return IncludeName;
}

return RelativePath;
}

template <typename IncludeHandlerType>
void ProcessShaderIncludesImpl(const ShaderCreateInfo& ShaderCI, std::unordered_set<std::string>& Includes, IncludeHandlerType&& IncludeHandler) noexcept(false)
{
Expand All @@ -436,13 +463,14 @@ void ProcessShaderIncludesImpl(const ShaderCreateInfo& ShaderCI, std::unordered_

FindIncludes(
FileInfo.Source, FileInfo.SourceLength,
[&](const std::string& FilePath, size_t Start, size_t End) //
[&](const std::string& IncludeName, size_t /*Start*/, size_t /*End*/) //
{
if (!Includes.insert(FilePath).second)
const std::string ResolvedPath = ResolveIncludePathForPreprocess(ShaderCI, IncludeName);
if (!Includes.insert(ResolvedPath).second)
return;

ShaderCreateInfo IncludeCI{ShaderCI};
IncludeCI.FilePath = FilePath.c_str();
IncludeCI.FilePath = ResolvedPath.c_str();
IncludeCI.Source = nullptr;
IncludeCI.SourceLength = 0;
ProcessShaderIncludesImpl(IncludeCI, Includes, IncludeHandler);
Expand Down Expand Up @@ -477,6 +505,8 @@ static std::string UnrollShaderIncludesImpl(ShaderCreateInfo ShaderCI, std::unor
{
const ShaderSourceFileData SourceData = ReadShaderSourceFile(ShaderCI);

const ShaderCreateInfo IncludeContextCI{ShaderCI};

ShaderCI.Source = SourceData.Source;
ShaderCI.SourceLength = SourceData.SourceLength;
ShaderCI.FilePath = nullptr;
Expand All @@ -489,20 +519,21 @@ static std::string UnrollShaderIncludesImpl(ShaderCreateInfo ShaderCI, std::unor
// Insert text before the include start
Stream.write(ShaderCI.Source + PrevIncludeEnd, IncludeStart - PrevIncludeEnd);

if (AllIncludes.insert(Path).second)
const std::string ResolvedPath = ResolveIncludePathForPreprocess(IncludeContextCI, Path);
if (AllIncludes.insert(ResolvedPath).second)
{
// Process the #include directive
ShaderCreateInfo IncludeCI{ShaderCI};
IncludeCI.Source = nullptr;
IncludeCI.SourceLength = 0;
IncludeCI.FilePath = Path.c_str();
IncludeCI.FilePath = ResolvedPath.c_str();
std::string UnrolledInclude = UnrollShaderIncludesImpl(IncludeCI, AllIncludes);
Stream << UnrolledInclude;
}

PrevIncludeEnd = IncludeEnd;
},
std::bind(ProcessIncludeErrorHandler, ShaderCI, std::placeholders::_1));
std::bind(ProcessIncludeErrorHandler, IncludeContextCI, std::placeholders::_1));

// Insert text after the last include
Stream.write(ShaderCI.Source + PrevIncludeEnd, ShaderCI.SourceLength - PrevIncludeEnd);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#define NESTED_INCLUDE_R 0.25
#define NESTED_INCLUDE_G 0.75
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#define NESTED_INCLUDE_R 0.25
#define NESTED_INCLUDE_G 0.75
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#include "Config.glsl"

vec4 GetNestedIncludeColor()
{
return vec4(NESTED_INCLUDE_R, NESTED_INCLUDE_G, 0.0, 1.0);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#include "Config.hlsli"

float4 GetNestedIncludeColor()
{
return float4(NESTED_INCLUDE_R, NESTED_INCLUDE_G, 0.0, 1.0);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#version 450
#extension GL_GOOGLE_include_directive : enable

#include "IncludeNestedParentRelative/Headers/Types.glsl"

layout(location = 0) out vec4 FragColor;

void main()
{
FragColor = GetNestedIncludeColor();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#include "IncludeNestedParentRelative/Headers/Types.hlsli"

float4 main() : SV_Target
{
return GetNestedIncludeColor();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Start IncludeNestedParentRelativeTest.hlsl
#include "Nested/Types.hlsl"
// End IncludeNestedParentRelativeTest.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Start Nested/Config.hlsl
#define NESTED_CONFIG_VALUE 1
// End Nested/Config.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Start Nested/Types.hlsl
#include "Config.hlsl"
// End Nested/Types.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ std::vector<unsigned int> LoadSPIRVFromGLSL(const char* FilePath, SHADER_TYPE Sh
GLSLangUtils::GLSLtoSPIRVAttribs Attribs;
Attribs.ShaderType = ShaderType;
Attribs.ShaderSource = ShaderSource.data();
Attribs.SourceName = FilePath;
Attribs.SourceCodeLen = static_cast<int>(ShaderSourceSize);
Attribs.pShaderSourceStreamFactory = pShaderSourceStreamFactory;
Attribs.Version = Version;
Expand Down Expand Up @@ -646,4 +647,16 @@ TEST_F(SPIRVShaderResourcesTest, SpecializationConstants_DXC)
TestSpecializationConstants(SHADER_COMPILER_DXC);
}

TEST_F(SPIRVShaderResourcesTest, NestedParentRelativeIncludes_HLSL_GLSLang)
{
auto SPIRV = LoadSPIRVFromHLSL("IncludeNestedParentRelative/Main.psh", SHADER_TYPE_PIXEL, SHADER_COMPILER_GLSLANG);
EXPECT_FALSE(SPIRV.empty());
}

TEST_F(SPIRVShaderResourcesTest, NestedParentRelativeIncludes_GLSL_GLSLang)
{
auto SPIRV = LoadSPIRVFromGLSL("IncludeNestedParentRelative/Main.glsl");
EXPECT_FALSE(SPIRV.empty());
}

} // namespace
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2022 Diligent Graphics LLC
* Copyright 2019-2026 Diligent Graphics LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -27,6 +27,7 @@
#include <deque>

#include "ShaderToolsCommon.hpp"
#include "BasicFileSystem.hpp"
#include "DefaultShaderSourceStreamFactory.h"
#include "RenderDevice.h"
#include "TestingEnvironment.hpp"
Expand Down Expand Up @@ -135,6 +136,35 @@ TEST(ShaderPreprocessTest, Include)
}
}

TEST(ShaderPreprocessTest, IncludeNestedParentRelative)
{
RefCntAutoPtr<IShaderSourceInputStreamFactory> pShaderSourceFactory;
CreateDefaultShaderSourceStreamFactory("shaders/ShaderPreprocessor", &pShaderSourceFactory);
ASSERT_NE(pShaderSourceFactory, nullptr);

const auto NestedTypesPath = std::string{"Nested/Types.hlsl"};
const auto NestedConfigPath = std::string{"Nested"} + BasicFileSystem::SlashSymbol + "Config.hlsl";

std::deque<std::string> Includes{
NestedConfigPath,
NestedTypesPath,
"IncludeNestedParentRelativeTest.hlsl"};

ShaderCreateInfo ShaderCI{};
ShaderCI.Desc.Name = "TestShader";
ShaderCI.FilePath = "IncludeNestedParentRelativeTest.hlsl";
ShaderCI.pShaderSourceStreamFactory = pShaderSourceFactory;

const auto Result = ProcessShaderIncludes(ShaderCI, [&](const ShaderIncludePreprocessInfo& ProcessInfo) {
ASSERT_FALSE(Includes.empty());
EXPECT_EQ(ProcessInfo.FilePath, Includes.front());
Includes.pop_front();
});

EXPECT_EQ(Result, true);
EXPECT_TRUE(Includes.empty());
}

TEST(ShaderPreprocessTest, InvalidInclude)
{
RefCntAutoPtr<IShaderSourceInputStreamFactory> pShaderSourceFactory;
Expand Down Expand Up @@ -192,6 +222,27 @@ TEST(ShaderPreprocessTest, UnrollIncludes)
auto UnrolledStr = UnrollShaderIncludes(ShaderCI);
ASSERT_EQ(RefString, UnrolledStr);
}

{
ShaderCreateInfo ShaderCI{};
ShaderCI.Desc.Name = "TestShader";
ShaderCI.FilePath = "IncludeNestedParentRelativeTest.hlsl";
ShaderCI.pShaderSourceStreamFactory = pShaderSourceFactory;

constexpr char RefString[] =
"// Start IncludeNestedParentRelativeTest.hlsl\n"
"// Start Nested/Types.hlsl\n"
"// Start Nested/Config.hlsl\n"
"#define NESTED_CONFIG_VALUE 1\n"
"// End Nested/Config.hlsl\n"
"\n"
"// End Nested/Types.hlsl\n"
"\n"
"// End IncludeNestedParentRelativeTest.hlsl\n";

auto UnrolledStr = UnrollShaderIncludes(ShaderCI);
ASSERT_EQ(RefString, UnrolledStr);
}
}

TEST(ShaderPreprocessTest, ShaderSourceLanguageDefiniton)
Expand Down
Loading