Skip to content

Commit dcdd405

Browse files
committed
preprocessShader function
1 parent b4a433f commit dcdd405

File tree

11 files changed

+167
-156
lines changed

11 files changed

+167
-156
lines changed

include/nbl/asset/IShader.h

Lines changed: 0 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -70,55 +70,6 @@ class NBL_API IShader : public virtual core::IReferenceCounted
7070

7171
inline const std::string& getFilepathHint() const { return m_filepathHint; }
7272

73-
static inline void insertDefines(std::string& _glsl, const core::SRange<const char* const>& _defines)
74-
{
75-
if (_defines.empty())
76-
return;
77-
78-
std::ostringstream insertion;
79-
for (auto def : _defines)
80-
{
81-
insertion << "#define "<<def<<"\n";
82-
}
83-
insertAfterVersionAndPragmaShaderStage(_glsl,std::move(insertion));
84-
}
85-
86-
// TODO: can make it protected again AFTER we get rid of `COpenGLShader::k_openGL2VulkanExtensionMap`
87-
//protected:
88-
static inline void insertAfterVersionAndPragmaShaderStage(std::string& _glsl, std::ostringstream&& _ins)
89-
{
90-
auto findLineJustAfterVersionOrPragmaShaderStageDirective = [&_glsl] () -> size_t
91-
{
92-
size_t hashPos = _glsl.find_first_of('#');
93-
if (hashPos >= _glsl.length())
94-
return _glsl.npos;
95-
if (_glsl.compare(hashPos, 8, "#version"))
96-
return _glsl.npos;
97-
98-
size_t searchPos = hashPos + 8ull;
99-
100-
size_t hashPos2 = _glsl.find_first_of('#', hashPos + 8ull);
101-
if (hashPos2 < _glsl.length())
102-
{
103-
char pragma_stage_str[] = "#pragma shader_stage";
104-
if (_glsl.compare(hashPos2, sizeof(pragma_stage_str) - 1ull, pragma_stage_str) == 0)
105-
searchPos = hashPos2 + sizeof(pragma_stage_str) - 1ull;
106-
}
107-
size_t nlPos = _glsl.find_first_of('\n', searchPos);
108-
109-
return (nlPos >= _glsl.length()) ? _glsl.npos : nlPos + 1ull;
110-
};
111-
112-
const size_t pos = findLineJustAfterVersionOrPragmaShaderStageDirective();
113-
if (pos == _glsl.npos)
114-
return;
115-
116-
const size_t ln = std::count(_glsl.begin(), _glsl.begin() + pos, '\n') + 1;//+1 to count from 1
117-
118-
_ins << "#line "<<std::to_string(ln)<<"\n";
119-
_glsl.insert(pos,_ins.str());
120-
}
121-
12273
protected:
12374
E_SHADER_STAGE m_shaderStage;
12475
std::string m_filepathHint;

include/nbl/asset/utils/CCompilerSet.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace nbl::asset
1919
, m_GLSLCompiler(core::make_smart_refctd_ptr<CGLSLCompiler>(core::smart_refctd_ptr(sys)))
2020
{}
2121

22-
core::smart_refctd_ptr<const ICPUShader> compileToSPIRV(const asset::ICPUShader* shader, const IShaderCompiler::SOptions& options);
22+
core::smart_refctd_ptr<const ICPUShader> compileToSPIRV(const asset::ICPUShader* shader, const IShaderCompiler::SCompilerOptions& options);
2323

2424
inline core::smart_refctd_ptr<IShaderCompiler> getShaderCompiler(IShader::E_CONTENT_TYPE contentType) const
2525
{

include/nbl/asset/utils/CGLSLCompiler.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class NBL_API2 CGLSLCompiler final : public IShaderCompiler
1818

1919
CGLSLCompiler(core::smart_refctd_ptr<system::ISystem>&& system);
2020

21-
struct SOptions : IShaderCompiler::SOptions
21+
struct SOptions : IShaderCompiler::SCompilerOptions
2222
{
2323
IShader::E_CONTENT_TYPE getCodeContentType() const override { return IShader::E_CONTENT_TYPE::ECT_GLSL; };
2424
};
@@ -34,16 +34,16 @@ class NBL_API2 CGLSLCompiler final : public IShaderCompiler
3434
3535
Such annotation should be placed right after #version directive.
3636
37-
This function does NOT process #include directives! Use resolveIncludeDirectives() first.
37+
This function does NOT process #include directives! Use preprocessShader() first.
3838
3939
@params code high level code
40-
@params options see IShaderCompiler::SOptions
40+
@params options see IShaderCompiler::SCompilerOptions
4141
- entryPoint Must be "main" since shaderc does not allow other entry points for GLSL. Kept with hope that shaderc will drop that requirement.
4242
4343
@returns Shader containing SPIR-V bytecode.
4444
*/
4545

46-
core::smart_refctd_ptr<ICPUShader> compileToSPIRV(const char* code, const IShaderCompiler::SOptions& options) const override;
46+
core::smart_refctd_ptr<ICPUShader> compileToSPIRV(const char* code, const IShaderCompiler::SCompilerOptions& options) const override;
4747

4848
/*
4949
If original code contains #version specifier,
@@ -127,7 +127,9 @@ class NBL_API2 CGLSLCompiler final : public IShaderCompiler
127127

128128
protected:
129129

130-
static CGLSLCompiler::SOptions option_cast(const IShaderCompiler::SOptions& options)
130+
void insertExtraDefines(std::string& code, const core::SRange<const char* const>& defines) const override;
131+
132+
static CGLSLCompiler::SOptions option_cast(const IShaderCompiler::SCompilerOptions& options)
131133
{
132134
CGLSLCompiler::SOptions ret = {};
133135
if (options.getCodeContentType() == IShader::E_CONTENT_TYPE::ECT_GLSL)

include/nbl/asset/utils/CHLSLCompiler.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ class NBL_API2 CHLSLCompiler final : public IShaderCompiler
1818

1919
CHLSLCompiler(core::smart_refctd_ptr<system::ISystem>&& system);
2020

21-
struct SOptions : IShaderCompiler::SOptions
21+
struct SOptions : IShaderCompiler::SCompilerOptions
2222
{
2323
// TODO: Add extra dxc options
2424
IShader::E_CONTENT_TYPE getCodeContentType() const override { return IShader::E_CONTENT_TYPE::ECT_HLSL; };
2525
};
2626

27-
core::smart_refctd_ptr<ICPUShader> compileToSPIRV(const char* code, const IShaderCompiler::SOptions& options) const override;
27+
core::smart_refctd_ptr<ICPUShader> compileToSPIRV(const char* code, const IShaderCompiler::SCompilerOptions& options) const override;
2828

2929
template<typename... Args>
3030
static core::smart_refctd_ptr<ICPUShader> createOverridenCopy(const ICPUShader* original, const char* fmt, Args... args)
@@ -40,7 +40,9 @@ class NBL_API2 CHLSLCompiler final : public IShaderCompiler
4040

4141
protected:
4242

43-
static CHLSLCompiler::SOptions option_cast(const IShaderCompiler::SOptions& options)
43+
void insertExtraDefines(std::string& str, const core::SRange<const char* const>& defines) const override;
44+
45+
static CHLSLCompiler::SOptions option_cast(const IShaderCompiler::SCompilerOptions& options)
4446
{
4547
CHLSLCompiler::SOptions ret = {};
4648
if (options.getCodeContentType() == IShader::E_CONTENT_TYPE::ECT_GLSL)

include/nbl/asset/utils/CSPIRVIntrospector.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ class NBL_API CSPIRVIntrospector : public core::Uncopyable
9393
CSPIRVIntrospector() = default;
9494

9595
//! params.cpuShader.contentType should be ECT_SPIRV
96-
//! the compiled SPIRV must be compiled with IShaderCompiler::SOptions::genDebugInfo in order to include names in introspection data
96+
//! the compiled SPIRV must be compiled with IShaderCompiler::SCompilerOptions::genDebugInfo in order to include names in introspection data
9797
const core::smart_refctd_ptr<CIntrospectionData> introspect(const SIntrospectionParams& params, bool insertToCache = true);
9898

9999
//

include/nbl/asset/utils/IShaderCompiler.h

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -107,43 +107,51 @@ class NBL_API2 IShaderCompiler : public core::IReferenceCounted
107107

108108
IShaderCompiler(core::smart_refctd_ptr<system::ISystem>&& system);
109109

110+
struct SPreprocessorOptions
111+
{
112+
std::string_view sourceIdentifier = "";
113+
system::logger_opt_ptr logger = nullptr;
114+
const CIncludeFinder* includeFinder = nullptr;
115+
uint32_t maxSelfInclusionCount = 4u;
116+
core::SRange<const char* const> extraDefines = {nullptr, nullptr};
117+
};
118+
110119
/*
111120
@stage shaderStage
112121
@targetSpirvVersion spirv version
113122
@entryPoint entryPoint
114-
@sourceIdentifier String that will be printed along with possible errors as source identifier, and used as include's requestingSrc
115123
@outAssembly Optional parameter; if not nullptr, SPIR-V assembly is saved in there.
116124
@spirvOptimizer Optional parameter;
117-
@logger Optional parameter; used for logging errors/info
118-
@includeFinder Optional parameter; if not nullptr, it will resolve the includes in the code
119-
@maxSelfInclusionCount used only when includeFinder is not nullptr
120125
@genDebugInfo Requests compiler to generate debug info (most importantly objects' names).
121126
Anything non-vulkan, basically you can't recover the names of original variables with CSPIRVIntrospector without debug info
122127
By variables we mean names of PC/SSBO/UBO blocks, as they're essentially instantiations of structs with custom packing.
128+
@preprocessorOptions
129+
@sourceIdentifier String that will be printed along with possible errors as source identifier, and used as include's requestingSrc
130+
@logger Optional parameter; used for logging errors/info
131+
@includeFinder Optional parameter; if not nullptr, it will resolve the includes in the code
132+
@maxSelfInclusionCount used only when includeFinder is not nullptr
133+
@extraDefines adds extra defines to the shader before compilation
123134
*/
124-
struct SOptions
135+
struct SCompilerOptions
125136
{
126137
IShader::E_SHADER_STAGE stage = IShader::E_SHADER_STAGE::ESS_UNKNOWN;
127138
E_SPIRV_VERSION targetSpirvVersion = E_SPIRV_VERSION::ESV_1_6;
128139
std::string_view entryPoint = "";
129-
std::string_view sourceIdentifier = "";
130140
const ISPIRVOptimizer* spirvOptimizer = nullptr;
131-
system::logger_opt_ptr logger = nullptr;
132-
const CIncludeFinder* includeFinder = nullptr;
133-
uint32_t maxSelfInclusionCount = 4u;
134141
bool genDebugInfo = true;
142+
SPreprocessorOptions preprocessorOptions = {};
135143

136-
void setCommonData(const SOptions& opt)
144+
void setCommonData(const SCompilerOptions& opt)
137145
{
138146
(*this) = opt;
139147
}
140148

141149
virtual IShader::E_CONTENT_TYPE getCodeContentType() const { return IShader::E_CONTENT_TYPE::ECT_UNKNOWN; };
142150
};
143151

144-
virtual core::smart_refctd_ptr<ICPUShader> compileToSPIRV(const char* code, const SOptions& options) const = 0;
152+
virtual core::smart_refctd_ptr<ICPUShader> compileToSPIRV(const char* code, const SCompilerOptions& options) const = 0;
145153

146-
inline core::smart_refctd_ptr<ICPUShader> compileToSPIRV(system::IFile* sourceFile, const SOptions& options) const
154+
inline core::smart_refctd_ptr<ICPUShader> compileToSPIRV(system::IFile* sourceFile, const SCompilerOptions& options) const
147155
{
148156
size_t fileSize = sourceFile->getSize();
149157
std::string code(fileSize, '\0');
@@ -161,28 +169,18 @@ class NBL_API2 IShaderCompiler : public core::IReferenceCounted
161169
This is done in order to support `#include` AND simultaneulsy be able to store (serialize) such ICPUShader (mostly High Level source) into ONE file which, upon loading, will compile on every hardware/driver predicted by shader's author.
162170
163171
Internally function "disables" all preprocessor directives (so that they're not processed by preprocessor) except `#include` (and also `#version` and `#pragma shader_stage`).
164-
Note that among the directives there may be include guards. Because of that, _maxSelfInclusionCnt parameter is provided.
165-
166-
@param _maxSelfInclusionCnt Max self-inclusion count of possible file being #include'd. If no self-inclusions are allowed, should be set to 0.
167-
168-
@param _originFilepath Path to not necesarilly existing file whose directory will be base for relative (""-type) top-level #include's resolution.
169-
If _originFilepath is non-path-like string (e.g. "whatever" - no slashes), the base directory is assumed to be "." (working directory of your executable). It's important for it to be unique.
172+
Note that among the directives there may be include guards. Because of that, maxSelfInclusionCount parameter is provided.
173+
174+
@param preprocessOptions
175+
@maxSelfInclusionCount Max self-inclusion count of possible file being #include'd. If no self-inclusions are allowed, should be set to 0.
176+
@sourceIdentifier Path to not necesarilly existing file whose directory will be base for relative (""-type) top-level #include's resolution.
177+
If sourceIdentifier is non-path-like string (e.g. "whatever" - no slashes), the base directory is assumed to be "." (working directory of your executable). It's important for it to be unique.
170178
171179
@returns Shader containing logically same High Level code as input but with #include directives resolved.
172180
*/
173-
core::smart_refctd_ptr<ICPUShader> resolveIncludeDirectives(
174-
std::string&& _code,
175-
IShader::E_SHADER_STAGE _stage,
176-
const char* _originFilepath,
177-
uint32_t _maxSelfInclusionCnt = 4u,
178-
system::logger_opt_ptr logger = nullptr) const;
179-
180-
core::smart_refctd_ptr<ICPUShader> resolveIncludeDirectives(
181-
system::IFile* _sourcefile,
182-
IShader::E_SHADER_STAGE _stage,
183-
const char* _originFilepath,
184-
uint32_t _maxSelfInclusionCnt = 4u,
185-
system::logger_opt_ptr logger = nullptr) const;
181+
std::string preprocessShader(std::string&& code, IShader::E_SHADER_STAGE stage, const SPreprocessorOptions& preprocessOptions) const;
182+
183+
std::string preprocessShader(system::IFile* sourcefile, IShader::E_SHADER_STAGE stage, const SPreprocessorOptions& preprocessOptions) const;
186184

187185
/*
188186
Creates a formatted copy of the original
@@ -263,6 +261,10 @@ class NBL_API2 IShaderCompiler : public core::IReferenceCounted
263261

264262
const CIncludeFinder* getDefaultIncludeFinder() const { return m_defaultIncludeFinder.get(); }
265263

264+
protected:
265+
266+
virtual void insertExtraDefines(std::string& code, const core::SRange<const char* const>& defines) const = 0;
267+
266268
private:
267269
core::smart_refctd_ptr<system::ISystem> m_system;
268270
core::smart_refctd_ptr<CIncludeFinder> m_defaultIncludeFinder;

src/nbl/asset/utils/CCompilerSet.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
using namespace nbl;
77
using namespace nbl::asset;
88

9-
core::smart_refctd_ptr<const ICPUShader> CCompilerSet::compileToSPIRV(const ICPUShader* shader, const IShaderCompiler::SOptions& options)
9+
core::smart_refctd_ptr<const ICPUShader> CCompilerSet::compileToSPIRV(const ICPUShader* shader, const IShaderCompiler::SCompilerOptions& options)
1010
{
1111
core::smart_refctd_ptr<const ICPUShader> outSpirvShader = nullptr;
1212
if (shader)

src/nbl/asset/utils/CGLSLCompiler.cpp

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,54 +18,92 @@ CGLSLCompiler::CGLSLCompiler(core::smart_refctd_ptr<system::ISystem>&& system)
1818
{
1919
}
2020

21-
core::smart_refctd_ptr<ICPUShader> CGLSLCompiler::compileToSPIRV(const char* code, const IShaderCompiler::SOptions& options) const
21+
core::smart_refctd_ptr<ICPUShader> CGLSLCompiler::compileToSPIRV(const char* code, const IShaderCompiler::SCompilerOptions& options) const
2222
{
2323
auto glslOptions = option_cast(options);
2424

2525
if (!code)
2626
{
27-
glslOptions.logger.log("code is nullptr", system::ILogger::ELL_ERROR);
27+
glslOptions.preprocessorOptions.logger.log("code is nullptr", system::ILogger::ELL_ERROR);
2828
return nullptr;
2929
}
3030

3131
if (glslOptions.entryPoint.compare("main") != 0)
3232
{
33-
glslOptions.logger.log("shaderc requires entry point to be \"main\" in GLSL", system::ILogger::ELL_ERROR);
33+
glslOptions.preprocessorOptions.logger.log("shaderc requires entry point to be \"main\" in GLSL", system::ILogger::ELL_ERROR);
3434
return nullptr;
3535
}
3636

37-
core::smart_refctd_ptr<asset::ICPUShader> cpuShader;
38-
if (glslOptions.includeFinder != nullptr)
39-
{
40-
cpuShader = resolveIncludeDirectives(code, glslOptions.stage, glslOptions.sourceIdentifier.data(), glslOptions.maxSelfInclusionCount, glslOptions.logger);
41-
if (cpuShader)
42-
{
43-
code = reinterpret_cast<const char*>(cpuShader->getContent()->getPointer());
44-
}
45-
}
37+
auto newCode = preprocessShader(code, glslOptions.stage, glslOptions.preprocessorOptions);
4638

4739
shaderc::Compiler comp;
4840
shaderc::CompileOptions shadercOptions; //default options
4941
shadercOptions.SetTargetSpirv(static_cast<shaderc_spirv_version>(glslOptions.targetSpirvVersion));
5042
const shaderc_shader_kind stage = glslOptions.stage == IShader::ESS_UNKNOWN ? shaderc_glsl_infer_from_source : ESStoShadercEnum(glslOptions.stage);
51-
const size_t glsl_len = strlen(code);
5243
if (glslOptions.genDebugInfo)
5344
shadercOptions.SetGenerateDebugInfo();
5445

55-
shaderc::SpvCompilationResult bin_res = comp.CompileGlslToSpv(code, glsl_len, stage, glslOptions.sourceIdentifier.data() ? glslOptions.sourceIdentifier.data() : "", glslOptions.entryPoint.data(), shadercOptions);
46+
shaderc::SpvCompilationResult bin_res = comp.CompileGlslToSpv(newCode.c_str(), newCode.size(), stage, glslOptions.preprocessorOptions.sourceIdentifier.data() ? glslOptions.preprocessorOptions.sourceIdentifier.data() : "", glslOptions.entryPoint.data(), shadercOptions);
5647

5748
if (bin_res.GetCompilationStatus() == shaderc_compilation_status_success)
5849
{
5950
auto outSpirv = core::make_smart_refctd_ptr<ICPUBuffer>(std::distance(bin_res.cbegin(), bin_res.cend()) * sizeof(uint32_t));
6051
memcpy(outSpirv->getPointer(), bin_res.cbegin(), outSpirv->getSize());
6152

6253
if (glslOptions.spirvOptimizer)
63-
outSpirv = glslOptions.spirvOptimizer->optimize(outSpirv.get(), glslOptions.logger);
64-
return core::make_smart_refctd_ptr<asset::ICPUShader>(std::move(outSpirv), glslOptions.stage, IShader::E_CONTENT_TYPE::ECT_SPIRV, glslOptions.sourceIdentifier.data());
54+
outSpirv = glslOptions.spirvOptimizer->optimize(outSpirv.get(), glslOptions.preprocessorOptions.logger);
55+
return core::make_smart_refctd_ptr<asset::ICPUShader>(std::move(outSpirv), glslOptions.stage, IShader::E_CONTENT_TYPE::ECT_SPIRV, glslOptions.preprocessorOptions.sourceIdentifier.data());
6556
}
6657
else
6758
{
68-
glslOptions.logger.log(bin_res.GetErrorMessage(), system::ILogger::ELL_ERROR);
59+
glslOptions.preprocessorOptions.logger.log(bin_res.GetErrorMessage(), system::ILogger::ELL_ERROR);
6960
return nullptr;
7061
}
62+
}
63+
64+
static void insertAfterVersionAndPragmaShaderStage(std::string& code, std::ostringstream&& ins)
65+
{
66+
auto findLineJustAfterVersionOrPragmaShaderStageDirective = [&code]() -> size_t
67+
{
68+
size_t hashPos = code.find_first_of('#');
69+
if (hashPos >= code.length())
70+
return code.npos;
71+
if (code.compare(hashPos, 8, "#version"))
72+
return code.npos;
73+
74+
size_t searchPos = hashPos + 8ull;
75+
76+
size_t hashPos2 = code.find_first_of('#', hashPos + 8ull);
77+
if (hashPos2 < code.length())
78+
{
79+
char pragma_stage_str[] = "#pragma shader_stage";
80+
if (code.compare(hashPos2, sizeof(pragma_stage_str) - 1ull, pragma_stage_str) == 0)
81+
searchPos = hashPos2 + sizeof(pragma_stage_str) - 1ull;
82+
}
83+
size_t nlPos = code.find_first_of('\n', searchPos);
84+
85+
return (nlPos >= code.length()) ? code.npos : nlPos + 1ull;
86+
};
87+
88+
const size_t pos = findLineJustAfterVersionOrPragmaShaderStageDirective();
89+
if (pos == code.npos)
90+
return;
91+
92+
const size_t ln = std::count(code.begin(), code.begin() + pos, '\n') + 1;//+1 to count from 1
93+
94+
ins << "#line " << std::to_string(ln) << "\n";
95+
code.insert(pos, ins.str());
96+
}
97+
98+
void CGLSLCompiler::insertExtraDefines(std::string& str, const core::SRange<const char* const>& defines) const
99+
{
100+
if (defines.empty())
101+
return;
102+
103+
std::ostringstream insertion;
104+
for (auto def : defines)
105+
{
106+
insertion << "#define " << def << "\n";
107+
}
108+
insertAfterVersionAndPragmaShaderStage(str, std::move(insertion));
71109
}

0 commit comments

Comments
 (0)