77
88#include < iostream>
99#include < cstdlib>
10+ #include < string>
11+ #include < fstream>
12+ #include < streambuf>
1013
14+ using namespace nbl ;
15+ using namespace nbl ::system;
16+ using namespace nbl ::core;
17+ using namespace nbl ::asset;
1118
1219
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- }
3720
38- std::string command = " dxc.exe" ;
39- for (std::string arg : arguments)
40- {
41- command.append (" " ).append (arg);
42- }
4321
44- int execute = std::system (command.c_str ());
4522
46- std::cout << " -no-nbl-builtins - " << no_nbl_builtins;
4723
4824
49- return true ;
50- }
51-
52-
53- void workLoopBody () override {}
25+ class ShaderCompiler final : public system::IApplicationFramework
26+ {
27+ using base_t = system::IApplicationFramework;
5428
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 ; }
56102
57103
58104private:
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