Skip to content

Commit 2725358

Browse files
committed
Apply PR Reviews
1 parent 4b651dc commit 2725358

File tree

4 files changed

+187
-185
lines changed

4 files changed

+187
-185
lines changed

include/nbl/asset/utils/CGLSLCompiler.h

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,16 @@ namespace nbl::asset
1313

1414
class NBL_API2 CGLSLCompiler final : public IShaderCompiler
1515
{
16-
public:
16+
public:
17+
//string to be replaced with all "#" except those in "#include"
18+
static constexpr const char* PREPROC_DIRECTIVE_DISABLER = "_this_is_a_hash_";
19+
static constexpr const char* PREPROC_DIRECTIVE_ENABLER = PREPROC_DIRECTIVE_DISABLER;
20+
static constexpr const char* PREPROC_GL__DISABLER = "_this_is_a_GL__prefix_";
21+
static constexpr const char* PREPROC_GL__ENABLER = PREPROC_GL__DISABLER;
22+
static constexpr const char* PREPROC_LINE_CONTINUATION_DISABLER = "_this_is_a_line_continuation_\n";
23+
static constexpr const char* PREPROC_LINE_CONTINUATION_ENABLER = "_this_is_a_line_continuation_";
24+
25+
1726
IShader::E_CONTENT_TYPE getCodeContentType() const override { return IShader::E_CONTENT_TYPE::ECT_GLSL; };
1827

1928
CGLSLCompiler(core::smart_refctd_ptr<system::ISystem>&& system);
@@ -125,6 +134,70 @@ class NBL_API2 CGLSLCompiler final : public IShaderCompiler
125134
}
126135
}
127136

137+
static void disableAllDirectivesExceptIncludes(std::string& _code)
138+
{
139+
// TODO: replace this with a proper-ish proprocessor and includer one day
140+
std::regex directive("#(?!(include|version|pragma shader_stage|line))");//all # not followed by "include" nor "version" nor "pragma shader_stage"
141+
//`#pragma shader_stage(...)` is needed for determining shader stage when `_stage` param of IShaderCompiler functions is set to ESS_UNKNOWN
142+
auto result = std::regex_replace(_code, directive, PREPROC_DIRECTIVE_DISABLER);
143+
std::regex glMacro("[ \t\r\n\v\f]GL_");
144+
result = std::regex_replace(result, glMacro, PREPROC_GL__DISABLER);
145+
std::regex lineContinuation("\\\\[ \t\r\n\v\f]*\n");
146+
_code = std::regex_replace(result, lineContinuation, PREPROC_LINE_CONTINUATION_DISABLER);
147+
}
148+
static void reenableDirectives(std::string& _code)
149+
{
150+
std::regex lineContinuation(PREPROC_LINE_CONTINUATION_ENABLER);
151+
auto result = std::regex_replace(_code, lineContinuation, " \\");
152+
std::regex glMacro(PREPROC_GL__ENABLER);
153+
result = std::regex_replace(result, glMacro, " GL_");
154+
std::regex directive(PREPROC_DIRECTIVE_ENABLER);
155+
_code = std::regex_replace(result, directive, "#");
156+
}
157+
static std::string encloseWithinExtraInclGuards(std::string&& _code, uint32_t _maxInclusions, const char* _identifier)
158+
{
159+
assert(_maxInclusions != 0u);
160+
161+
using namespace std::string_literals;
162+
std::string defBase_ = "_GENERATED_INCLUDE_GUARD_"s + _identifier + "_";
163+
std::replace_if(defBase_.begin(), defBase_.end(), [](char c) ->bool { return !::isalpha(c) && !::isdigit(c); }, '_');
164+
165+
auto genDefs = [&defBase_, _maxInclusions, _identifier] {
166+
auto defBase = [&defBase_](uint32_t n) { return defBase_ + std::to_string(n); };
167+
std::string defs = "#ifndef " + defBase(0) + "\n\t#define " + defBase(0) + "\n";
168+
for (uint32_t i = 1u; i <= _maxInclusions; ++i) {
169+
const std::string defname = defBase(i);
170+
defs += "#elif !defined(" + defname + ")\n\t#define " + defname + "\n";
171+
}
172+
defs += "#endif\n";
173+
return defs;
174+
};
175+
auto genUndefs = [&defBase_, _maxInclusions, _identifier] {
176+
auto defBase = [&defBase_](int32_t n) { return defBase_ + std::to_string(n); };
177+
std::string undefs = "#ifdef " + defBase(_maxInclusions) + "\n\t#undef " + defBase(_maxInclusions) + "\n";
178+
for (int32_t i = _maxInclusions - 1; i >= 0; --i) {
179+
const std::string defname = defBase(i);
180+
undefs += "#elif defined(" + defname + ")\n\t#undef " + defname + "\n";
181+
}
182+
undefs += "#endif\n";
183+
return undefs;
184+
};
185+
186+
return
187+
genDefs() +
188+
"\n"
189+
"#ifndef " + defBase_ + std::to_string(_maxInclusions) +
190+
"\n" +
191+
"#line 1 \"" + _identifier + "\"\n" +
192+
_code +
193+
"\n"
194+
"#endif"
195+
"\n\n" +
196+
genUndefs();
197+
}
198+
199+
std::string preprocessShader(std::string&& code, IShader::E_SHADER_STAGE& stage, const SPreprocessorOptions& preprocessOptions) const override;
200+
128201
protected:
129202

130203
void insertIntoStart(std::string& code, std::ostringstream&& ins) const override;

include/nbl/asset/utils/IShaderCompiler.h

Lines changed: 1 addition & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ class NBL_API2 IShaderCompiler : public core::IReferenceCounted
178178
179179
@returns Shader containing logically same High Level code as input but with #include directives resolved.
180180
*/
181-
virtual std::string preprocessShader(std::string&& code, IShader::E_SHADER_STAGE& stage, const SPreprocessorOptions& preprocessOptions) const;
181+
virtual std::string preprocessShader(std::string&& code, IShader::E_SHADER_STAGE& stage, const SPreprocessorOptions& preprocessOptions) const = 0;
182182

183183
std::string preprocessShader(system::IFile* sourcefile, IShader::E_SHADER_STAGE stage, const SPreprocessorOptions& preprocessOptions) const;
184184

@@ -260,76 +260,6 @@ class NBL_API2 IShaderCompiler : public core::IReferenceCounted
260260
CIncludeFinder* getDefaultIncludeFinder() { return m_defaultIncludeFinder.get(); }
261261

262262
const CIncludeFinder* getDefaultIncludeFinder() const { return m_defaultIncludeFinder.get(); }
263-
264-
//string to be replaced with all "#" except those in "#include"
265-
static constexpr const char* PREPROC_DIRECTIVE_DISABLER = "_this_is_a_hash_";
266-
static constexpr const char* PREPROC_DIRECTIVE_ENABLER = PREPROC_DIRECTIVE_DISABLER;
267-
static constexpr const char* PREPROC_GL__DISABLER = "_this_is_a_GL__prefix_";
268-
static constexpr const char* PREPROC_GL__ENABLER = PREPROC_GL__DISABLER;
269-
static constexpr const char* PREPROC_LINE_CONTINUATION_DISABLER = "_this_is_a_line_continuation_\n";
270-
static constexpr const char* PREPROC_LINE_CONTINUATION_ENABLER = "_this_is_a_line_continuation_";
271-
272-
static void disableAllDirectivesExceptIncludes(std::string& _glslCode)
273-
{
274-
// TODO: replace this with a proper-ish proprocessor and includer one day
275-
std::regex directive("#(?!(include|version|pragma shader_stage|line))");//all # not followed by "include" nor "version" nor "pragma shader_stage"
276-
//`#pragma shader_stage(...)` is needed for determining shader stage when `_stage` param of IShaderCompiler functions is set to ESS_UNKNOWN
277-
auto result = std::regex_replace(_glslCode, directive, PREPROC_DIRECTIVE_DISABLER);
278-
std::regex glMacro("[ \t\r\n\v\f]GL_");
279-
result = std::regex_replace(result, glMacro, PREPROC_GL__DISABLER);
280-
std::regex lineContinuation("\\\\[ \t\r\n\v\f]*\n");
281-
_glslCode = std::regex_replace(result, lineContinuation, PREPROC_LINE_CONTINUATION_DISABLER);
282-
}
283-
static void reenableDirectives(std::string& _glslCode)
284-
{
285-
std::regex lineContinuation(PREPROC_LINE_CONTINUATION_ENABLER);
286-
auto result = std::regex_replace(_glslCode, lineContinuation, " \\");
287-
std::regex glMacro(PREPROC_GL__ENABLER);
288-
result = std::regex_replace(result, glMacro, " GL_");
289-
std::regex directive(PREPROC_DIRECTIVE_ENABLER);
290-
_glslCode = std::regex_replace(result, directive, "#");
291-
}
292-
static std::string encloseWithinExtraInclGuards(std::string&& _glslCode, uint32_t _maxInclusions, const char* _identifier)
293-
{
294-
assert(_maxInclusions != 0u);
295-
296-
using namespace std::string_literals;
297-
std::string defBase_ = "_GENERATED_INCLUDE_GUARD_"s + _identifier + "_";
298-
std::replace_if(defBase_.begin(), defBase_.end(), [](char c) ->bool { return !::isalpha(c) && !::isdigit(c); }, '_');
299-
300-
auto genDefs = [&defBase_, _maxInclusions, _identifier] {
301-
auto defBase = [&defBase_](uint32_t n) { return defBase_ + std::to_string(n); };
302-
std::string defs = "#ifndef " + defBase(0) + "\n\t#define " + defBase(0) + "\n";
303-
for (uint32_t i = 1u; i <= _maxInclusions; ++i) {
304-
const std::string defname = defBase(i);
305-
defs += "#elif !defined(" + defname + ")\n\t#define " + defname + "\n";
306-
}
307-
defs += "#endif\n";
308-
return defs;
309-
};
310-
auto genUndefs = [&defBase_, _maxInclusions, _identifier] {
311-
auto defBase = [&defBase_](int32_t n) { return defBase_ + std::to_string(n); };
312-
std::string undefs = "#ifdef " + defBase(_maxInclusions) + "\n\t#undef " + defBase(_maxInclusions) + "\n";
313-
for (int32_t i = _maxInclusions - 1; i >= 0; --i) {
314-
const std::string defname = defBase(i);
315-
undefs += "#elif defined(" + defname + ")\n\t#undef " + defname + "\n";
316-
}
317-
undefs += "#endif\n";
318-
return undefs;
319-
};
320-
321-
return
322-
genDefs() +
323-
"\n"
324-
"#ifndef " + defBase_ + std::to_string(_maxInclusions) +
325-
"\n" +
326-
"#line 1 \"" + _identifier + "\"\n" +
327-
_glslCode +
328-
"\n"
329-
"#endif"
330-
"\n\n" +
331-
genUndefs();
332-
}
333263
protected:
334264

335265
virtual void insertIntoStart(std::string& code, std::ostringstream&& ins) const = 0;

src/nbl/asset/utils/CGLSLCompiler.cpp

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,123 @@ using namespace nbl;
1313
using namespace nbl::asset;
1414

1515

16+
namespace nbl::asset::impl
17+
{
18+
class Includer : public shaderc::CompileOptions::IncluderInterface
19+
{
20+
const IShaderCompiler::CIncludeFinder* m_defaultIncludeFinder;
21+
const system::ISystem* m_system;
22+
const uint32_t m_maxInclCnt;
23+
24+
public:
25+
Includer(const IShaderCompiler::CIncludeFinder* _inclFinder, const system::ISystem* _fs, uint32_t _maxInclCnt) : m_defaultIncludeFinder(_inclFinder), m_system(_fs), m_maxInclCnt{ _maxInclCnt } {}
26+
27+
//_requesting_source in top level #include's is what shaderc::Compiler's compiling functions get as `input_file_name` parameter
28+
//so in order for properly working relative #include's (""-type) `input_file_name` has to be path to file from which the GLSL source really come from
29+
//or at least path to not necessarily existing file whose directory will be base for ""-type #include's resolution
30+
shaderc_include_result* GetInclude(const char* _requested_source,
31+
shaderc_include_type _type,
32+
const char* _requesting_source,
33+
size_t _include_depth) override
34+
{
35+
shaderc_include_result* res = new shaderc_include_result;
36+
std::string res_str;
37+
38+
std::filesystem::path relDir;
39+
const bool reqFromBuiltin = builtin::hasPathPrefix(_requesting_source);
40+
const bool reqBuiltin = builtin::hasPathPrefix(_requested_source);
41+
if (!reqFromBuiltin && !reqBuiltin)
42+
{
43+
//While #includ'ing a builtin, one must specify its full path (starting with "nbl/builtin" or "/nbl/builtin").
44+
// This rule applies also while a builtin is #includ`ing another builtin.
45+
//While including a filesystem file it must be either absolute path (or relative to any search dir added to asset::iIncludeHandler; <>-type),
46+
// or path relative to executable's working directory (""-type).
47+
relDir = std::filesystem::path(_requesting_source).parent_path();
48+
}
49+
std::filesystem::path name = (_type == shaderc_include_type_relative) ? (relDir / _requested_source) : (_requested_source);
50+
51+
if (std::filesystem::exists(name) && !reqBuiltin)
52+
name = std::filesystem::absolute(name);
53+
54+
if (_type == shaderc_include_type_relative)
55+
res_str = m_defaultIncludeFinder->getIncludeRelative(relDir, _requested_source);
56+
else //shaderc_include_type_standard
57+
res_str = m_defaultIncludeFinder->getIncludeStandard(relDir, _requested_source);
58+
59+
if (!res_str.size()) {
60+
const char* error_str = "Could not open file";
61+
res->content_length = strlen(error_str);
62+
res->content = new char[res->content_length + 1u];
63+
strcpy(const_cast<char*>(res->content), error_str);
64+
res->source_name_length = 0u;
65+
res->source_name = "";
66+
}
67+
else {
68+
//employ encloseWithinExtraInclGuards() in order to prevent infinite loop of (not necesarilly direct) self-inclusions while other # directives (incl guards among them) are disabled
69+
CGLSLCompiler::disableAllDirectivesExceptIncludes(res_str);
70+
res_str = CGLSLCompiler::encloseWithinExtraInclGuards(std::move(res_str), m_maxInclCnt, name.string().c_str());
71+
72+
res->content_length = res_str.size();
73+
res->content = new char[res_str.size() + 1u];
74+
strcpy(const_cast<char*>(res->content), res_str.c_str());
75+
res->source_name_length = name.native().size();
76+
res->source_name = new char[name.native().size() + 1u];
77+
strcpy(const_cast<char*>(res->source_name), name.string().c_str());
78+
}
79+
80+
return res;
81+
}
82+
83+
void ReleaseInclude(shaderc_include_result* data) override
84+
{
85+
if (data->content_length > 0u)
86+
delete[] data->content;
87+
if (data->source_name_length > 0u)
88+
delete[] data->source_name;
89+
delete data;
90+
}
91+
};
92+
}
93+
1694
CGLSLCompiler::CGLSLCompiler(core::smart_refctd_ptr<system::ISystem>&& system)
1795
: IShaderCompiler(std::move(system))
1896
{
1997
}
2098

99+
100+
101+
std::string CGLSLCompiler::preprocessShader(std::string&& code, IShader::E_SHADER_STAGE& stage, const SPreprocessorOptions& preprocessOptions) const
102+
{
103+
if (preprocessOptions.extraDefines.size())
104+
{
105+
insertExtraDefines(code, preprocessOptions.extraDefines);
106+
}
107+
if (preprocessOptions.includeFinder != nullptr)
108+
{
109+
CGLSLCompiler::disableAllDirectivesExceptIncludes(code);//all "#", except those in "#include"/"#version"/"#pragma shader_stage(...)", replaced with `PREPROC_DIRECTIVE_DISABLER`
110+
shaderc::Compiler comp;
111+
shaderc::CompileOptions options;
112+
options.SetTargetSpirv(shaderc_spirv_version_1_6);
113+
114+
options.SetIncluder(std::make_unique<impl::Includer>(preprocessOptions.includeFinder, m_system.get(), preprocessOptions.maxSelfInclusionCount + 1u));//custom #include handler
115+
const shaderc_shader_kind scstage = stage == IShader::ESS_UNKNOWN ? shaderc_glsl_infer_from_source : ESStoShadercEnum(stage);
116+
auto res = comp.PreprocessGlsl(code, scstage, preprocessOptions.sourceIdentifier.data(), options);
117+
118+
if (res.GetCompilationStatus() != shaderc_compilation_status_success) {
119+
preprocessOptions.logger.log(res.GetErrorMessage(), system::ILogger::ELL_ERROR);
120+
return nullptr;
121+
}
122+
123+
auto resolvedString = std::string(res.cbegin(), std::distance(res.cbegin(), res.cend()));
124+
CGLSLCompiler::reenableDirectives(resolvedString);
125+
return resolvedString;
126+
}
127+
else
128+
{
129+
return code;
130+
}
131+
}
132+
21133
core::smart_refctd_ptr<ICPUShader> CGLSLCompiler::compileToSPIRV(const char* code, const IShaderCompiler::SCompilerOptions& options) const
22134
{
23135
auto glslOptions = option_cast(options);

0 commit comments

Comments
 (0)