7
7
8
8
#include < iostream>
9
9
#include < cstdlib>
10
+ #include < string>
11
+ #include < fstream>
12
+ #include < streambuf>
10
13
14
+ using namespace nbl ;
15
+ using namespace nbl ::system;
16
+ using namespace nbl ::core;
17
+ using namespace nbl ::asset;
11
18
12
19
13
- class ShaderCompiler final : public system::IApplicationFramework
14
- {
15
- using base_t = system::IApplicationFramework;
16
-
17
- public:
18
- using base_t ::base_t ;
19
-
20
- bool onAppInitialized (smart_refctd_ptr<ISystem>&& system) override
21
- {
22
- if (!base_t::onAppInitialized (std::move (system)))
23
- return false ;
24
-
25
- auto argc = argv.size ();
26
-
27
- core::vector<std::string> arguments (argv + 1 , argv + argc);
28
-
29
- for (auto i=0 ; i<argc; i++)
30
- {
31
- if (argv[i] == " -no-nbl-builtins" )
32
- {
33
- no_nbl_builtins = true ;
34
- break ;
35
- }
36
- }
37
20
38
- std::string command = " dxc.exe" ;
39
- for (std::string arg : arguments)
40
- {
41
- command.append (" " ).append (arg);
42
- }
43
21
44
- int execute = std::system (command.c_str ());
45
22
46
- std::cout << " -no-nbl-builtins - " << no_nbl_builtins;
47
23
48
24
49
- return true ;
50
- }
51
-
52
-
53
- void workLoopBody () override {}
25
+ class ShaderCompiler final : public system::IApplicationFramework
26
+ {
27
+ using base_t = system::IApplicationFramework;
54
28
55
- bool keepRunning () override { return false ; }
29
+ public:
30
+ using base_t ::base_t ;
31
+
32
+ bool onAppInitialized (smart_refctd_ptr<ISystem>&& system) override
33
+ {
34
+ if (system)
35
+ m_system = std::move (system);
36
+ else
37
+ m_system = system::IApplicationFramework::createSystem ();
38
+
39
+ m_logger = make_smart_refctd_ptr<CStdoutLogger>();
40
+
41
+ auto argc = argv.size ();
42
+
43
+ // expect the first argument to be
44
+ // nsc.exe
45
+ // second the filename of a shader to compile
46
+ if (argc < 2 ) {
47
+ m_logger->log (" Insufficient arguments." , ILogger::ELL_ERROR);
48
+ return false ;
49
+ }
50
+
51
+ m_arguments = std::vector<std::string>(argv.begin () + 1 , argv.end ()); // turn argv into vector for convenience
52
+ std::string file_to_compile = m_arguments[0 ];
53
+
54
+ if (!m_system->exists (file_to_compile, IFileBase::ECF_READ)) {
55
+ m_logger->log (" Incorrect arguments. Expecting second argument to be filename of the shader intended to compile." , ILogger::ELL_ERROR);
56
+ return false ;
57
+ }
58
+ std::string output_filepath;
59
+ for (auto i = 1 ; i < argc; i++)
60
+ {
61
+ if (argv[i] == " -no-nbl-builtins" )
62
+ {
63
+ no_nbl_builtins = true ;
64
+ }
65
+ else if (argv[i] == " -Fo" )
66
+ {
67
+ if (i + 1 < argc) {
68
+ i++;
69
+ output_filepath = argv[i];
70
+ m_logger->log (" Saving compiled shader code to " + output_filepath);
71
+ }
72
+ else {
73
+ m_logger->log (" Incorrect arguments. Expecting filename after -Fo." , ILogger::ELL_ERROR);
74
+ }
75
+ }
76
+ }
77
+ string shader_code = open_shader_file (file_to_compile);
78
+ auto compilation_result = compile_shader (shader_code, file_to_compile);
79
+
80
+ // writie compiled shader to file as bytes
81
+ std::fstream output_file (output_filepath, std::ios::out | std::ios::binary);
82
+ output_file.write ((const char *)compilation_result.objectBlob ->GetBufferPointer (), compilation_result.objectBlob ->GetBufferSize ());
83
+ output_file.close ();
84
+ /* std::string command = "dxc.exe";
85
+ for (std::string arg : arguments)
86
+ {
87
+ command.append(" ").append(arg);
88
+ }
89
+
90
+ int execute = std::system(command.c_str());*/
91
+
92
+ // std::cout << "-no-nbl-builtins - " << no_nbl_builtins;
93
+
94
+
95
+ return true ;
96
+ }
97
+
98
+
99
+ void workLoopBody () override {}
100
+
101
+ bool keepRunning () override { return false ; }
56
102
57
103
58
104
private:
59
- bool no_nbl_builtins{false };
60
- };
105
+
106
+ CHLSLCompiler::DxcCompilationResult compile_shader (std::string& shader_code, std::string_view sourceIdentifier) {
107
+ constexpr uint32_t WorkgroupSize = 256 ;
108
+ constexpr uint32_t WorkgroupCount = 2048 ;
109
+ const string WorkgroupSizeAsStr = std::to_string (WorkgroupSize);
110
+ const IShaderCompiler::SPreprocessorOptions::SMacroDefinition WorkgroupSizeDefine = { " WORKGROUP_SIZE" ,WorkgroupSizeAsStr };
111
+
112
+ smart_refctd_ptr<CHLSLCompiler> hlslcompiler = make_smart_refctd_ptr<CHLSLCompiler>(std::move (m_system));
113
+
114
+ CHLSLCompiler::SOptions options = {};
115
+ options.stage = asset::IShader::E_SHADER_STAGE::ESS_UNKNOWN; // probably not needed, requires guessing -T target profile
116
+ // want as much debug as possible
117
+ options.debugInfoFlags = IShaderCompiler::E_DEBUG_INFO_FLAGS::EDIF_LINE_BIT;
118
+ // this lets you source-level debug/step shaders in renderdoc
119
+ // if (physDev->getLimits().shaderNonSemanticInfo)
120
+ // options.debugInfoFlags |= IShaderCompiler::E_DEBUG_INFO_FLAGS::EDIF_NON_SEMANTIC_BIT;
121
+ // if you don't set the logger and source identifier you'll have no meaningful errors
122
+ options.preprocessorOptions .sourceIdentifier = sourceIdentifier;
123
+ options.preprocessorOptions .logger = m_logger.get ();
124
+ options.preprocessorOptions .extraDefines = { &WorkgroupSizeDefine,&WorkgroupSizeDefine + 1 };
125
+
126
+ std::vector<std::string> dxc_compile_flags_from_pragma = {};
127
+ auto preprocessed_shader_code = hlslcompiler->preprocessShader (std::move (shader_code), options.stage , dxc_compile_flags_from_pragma, options.preprocessorOptions );
128
+
129
+ // override arguments from command line to ones listed in pragma
130
+ if (dxc_compile_flags_from_pragma.size ())
131
+ m_arguments = dxc_compile_flags_from_pragma;
132
+
133
+ add_required_arguments_if_not_present ();
134
+
135
+ // convert string arguments to wstring arguments
136
+ int arg_size = m_arguments.size () - 1 ; // skip input file argument
137
+ LPCWSTR* arguments = new LPCWSTR[arg_size]; // array of pointers
138
+ std::wstring* arguments_wdata = new std::wstring[arg_size]; // array of data, prevents deallocation before shader compilation
139
+ for (size_t i = 0 ; i < arg_size; i++) {
140
+ arguments_wdata[i] = std::wstring (m_arguments[i + 1 ].begin (), m_arguments[i + 1 ].end ());
141
+ arguments[i] = arguments_wdata[i].c_str ();
142
+ }
143
+
144
+ auto compileResult = hlslcompiler->dxcCompile (preprocessed_shader_code, arguments, arg_size, options);
145
+ delete[] arguments;
146
+ delete[] arguments_wdata;
147
+ return compileResult;
148
+ }
149
+
150
+
151
+ void add_required_arguments_if_not_present () {
152
+ constexpr int required_arg_size = 8 ;
153
+ std::string required_arguments[required_arg_size] = {
154
+ " -spirv" ,
155
+ " -Zpr" , // Packs matrices in row-major order by default
156
+ " -enable-16bit-types" ,
157
+ " -fvk-use-scalar-layout" ,
158
+ " -Wno-c++11-extensions" ,
159
+ " -Wno-c++1z-extensions" ,
160
+ " -Wno-gnu-static-float-init" ,
161
+ " -fspv-target-env=vulkan1.3"
162
+ };
163
+ bool found_arg_flags[required_arg_size]{};
164
+ int argc = m_arguments.size ();
165
+ for (int i = 0 ; i < argc; i++)
166
+ {
167
+ for (int j = 0 ; j < required_arg_size; j++)
168
+ {
169
+ if (m_arguments[i] == required_arguments[j]) {
170
+ found_arg_flags[j] = true ;
171
+ break ;
172
+ }
173
+ }
174
+ }
175
+ for (int j = 0 ; j < required_arg_size; j++)
176
+ {
177
+ if (!found_arg_flags[j]) {
178
+ m_logger->log (" Required compile flag not found " + required_arguments[j] +" . This flag will be force enabled as it is required by Nabla." , ILogger::ELL_WARNING);
179
+ m_arguments.push_back (required_arguments[j]);
180
+ }
181
+ }
182
+
183
+ }
184
+
185
+
186
+ std::string open_shader_file (std::string& filepath) {
187
+ std::ifstream stream (filepath);
188
+ std::string str ((std::istreambuf_iterator<char >(stream)),
189
+ std::istreambuf_iterator<char >());
190
+ return str;
191
+ }
192
+
193
+
194
+ bool no_nbl_builtins{ false };
195
+ smart_refctd_ptr<ISystem> m_system;
196
+ smart_refctd_ptr<CStdoutLogger> m_logger;
197
+ std::vector<std::string> m_arguments;
198
+
199
+ };
200
+
201
+ NBL_MAIN_FUNC (ShaderCompiler)
0 commit comments