@@ -235,14 +235,60 @@ std::string basename(const std::string &path) {
235
235
return path.substr (path.find_last_of (" /\\ " ) + 1 );
236
236
}
237
237
238
+ std::stringstream make_generic_stringstream () {
239
+ std::stringstream ss;
240
+ ss.imbue (c_locale);
241
+ return ss;
242
+ }
243
+
244
+ struct compile_command {
245
+ std::string name;
246
+ std::string input_filepath;
247
+ std::string output_filepath;
248
+ std::string flags;
249
+ };
250
+
251
+ // Code for writing a ninja build file
252
+
253
+ static std::stringstream ninja_build;
254
+
255
+ std::string ninja_escape (const std::string& str) {
256
+ std::string result;
257
+ for (char c : str) {
258
+ if (c == ' ' || c == ' $' || c == ' :' ) {
259
+ result += ' $' ;
260
+ }
261
+ result += c;
262
+ }
263
+ return result;
264
+ }
265
+
266
+ void ninja_init () {
267
+ ninja_build = make_generic_stringstream ();
268
+ ninja_build << " ninja_required_version = 1.3\n\n " ;
269
+ ninja_build << " rule glslc\n " ;
270
+ ninja_build << " command = " << GLSLC << " $FLAGS -MD -MF $out.d $in -o $out\n " ;
271
+ ninja_build << " depfile = $out.d\n " ;
272
+ ninja_build << " deps = gcc\n " ;
273
+ ninja_build << " description = Building Vulkan shader ${NAME}.spv\n\n " ;
274
+ }
275
+
276
+ void ninja_add_build_command (const compile_command& cmd) {
277
+ ninja_build << " build " << ninja_escape (cmd.output_filepath ) << " : glslc " << ninja_escape (cmd.input_filepath ) << " \n " ;
278
+ ninja_build << " NAME = " << cmd.name << " \n " ;
279
+ ninja_build << " FLAGS = " << cmd.flags << " \n\n " ;
280
+ shader_fnames.push_back (std::make_pair (cmd.name , cmd.output_filepath ));
281
+ }
282
+
283
+
238
284
// variables to track number of compiles in progress
239
285
static uint32_t compile_count = 0 ;
240
286
static std::mutex compile_count_mutex;
241
287
static std::condition_variable compile_count_cond;
242
288
243
- void string_to_spv_func (const std::string& _name, const std::string& in_fname, const std::map<std::string, std::string>& defines, bool fp16 = true , bool coopmat = false , bool coopmat2 = false , bool f16acc = false ) {
289
+ compile_command create_compile_command (const std::string& _name, const std::string& in_fname, const std::map<std::string, std::string>& defines, bool fp16, bool coopmat, bool coopmat2, bool f16acc) {
244
290
std::string name = _name + (f16acc ? " _f16acc" : " " ) + (coopmat ? " _cm1" : " " ) + (coopmat2 ? " _cm2" : (fp16 ? " " : " _fp32" ));
245
- std::string out_fname = join_paths (output_dir, name + " .spv" );
291
+ std::string out_path = join_paths (output_dir, name + " .spv" );
246
292
std::string in_path = join_paths (input_dir, in_fname);
247
293
248
294
std::string target_env = (name.find (" _cm2" ) != std::string::npos) ? " --target-env=vulkan1.3" : " --target-env=vulkan1.2" ;
@@ -251,25 +297,24 @@ void string_to_spv_func(const std::string& _name, const std::string& in_fname, c
251
297
// disable spirv-opt for bf16 shaders for https://github.com/ggml-org/llama.cpp/issues/15344
252
298
std::string opt_level = (coopmat || name.find (" bf16" ) != std::string::npos) ? " " : " -O" ;
253
299
254
- #ifdef _WIN32
255
- std::vector<std::string> cmd = {GLSLC, " -fshader-stage=compute" , target_env, opt_level, " \" " + in_path + " \" " , " -o" , " \" " + out_fname + " \" " };
256
- #else
257
- std::vector<std::string> cmd = {GLSLC, " -fshader-stage=compute" , target_env, opt_level, in_path, " -o" , out_fname};
258
- #endif
300
+ std::vector<std::string> flags = {" -fshader-stage=compute" , target_env, opt_level};
259
301
260
302
#ifdef GGML_VULKAN_SHADER_DEBUG_INFO
261
- cmd .push_back (" -g" );
303
+ flags .push_back (" -g" );
262
304
#endif
263
305
264
306
for (const auto & define : defines) {
265
- cmd .push_back (" -D" + define.first + " =" + define.second );
307
+ flags .push_back (" -D" + define.first + " =" + define.second );
266
308
}
267
309
268
- std::string command ;
269
- for (const auto & part : cmd ) {
270
- command += part + " " ;
310
+ std::string flags_str ;
311
+ for (const auto & part : flags ) {
312
+ flags_str += part + " " ;
271
313
}
314
+ return {std::move (name), std::move (in_path), std::move (out_path), std::move (flags_str)};
315
+ }
272
316
317
+ void execute_compile_command (compile_command cmd) {
273
318
std::string stdout_str, stderr_str;
274
319
try {
275
320
// std::cout << "Executing command: ";
@@ -278,16 +323,22 @@ void string_to_spv_func(const std::string& _name, const std::string& in_fname, c
278
323
// }
279
324
// std::cout << std::endl;
280
325
326
+ #ifdef _WIN32
327
+ std::string command = GLSLC + " " + cmd.flags + " \" " + cmd.input_filepath + " \" -o \" " + cmd.output_filepath + " \" " ;
328
+ #else
329
+ std::string command = GLSLC + " " + cmd.flags + " " + cmd.input_filepath + " -o " + cmd.output_filepath ;
330
+ #endif
281
331
execute_command (command, stdout_str, stderr_str);
332
+
282
333
if (!stderr_str.empty ()) {
283
- std::cerr << " cannot compile " << name << " \n\n " << command << " \n\n " << stderr_str << std::endl;
334
+ std::cerr << " cannot compile " << cmd. name << " \n\n " << command << " \n\n " << stderr_str << std::endl;
284
335
return ;
285
336
}
286
337
287
338
std::lock_guard<std::mutex> guard (lock);
288
- shader_fnames.push_back (std::make_pair (name, out_fname ));
339
+ shader_fnames.push_back (std::make_pair (cmd. name , cmd. output_filepath ));
289
340
} catch (const std::exception& e) {
290
- std::cerr << " Error executing command for " << name << " : " << e.what () << std::endl;
341
+ std::cerr << " Error executing command for " << cmd. name << " : " << e.what () << std::endl;
291
342
}
292
343
{
293
344
std::lock_guard<std::mutex> guard (compile_count_mutex);
@@ -305,6 +356,12 @@ std::map<std::string, std::string> merge_maps(const std::map<std::string, std::s
305
356
306
357
static std::vector<std::future<void >> compiles;
307
358
void string_to_spv (const std::string& _name, const std::string& in_fname, const std::map<std::string, std::string>& defines, bool fp16 = true , bool coopmat = false , bool coopmat2 = false , bool f16acc = false ) {
359
+ compile_command cmd = create_compile_command (_name, in_fname, defines, fp16, coopmat, coopmat2, f16acc);
360
+
361
+ if (!ninja_build_file.empty ()) {
362
+ ninja_add_build_command (cmd);
363
+ return ;
364
+ }
308
365
{
309
366
// wait until fewer than N compiles are in progress.
310
367
// 16 is an arbitrary limit, the goal is to avoid "failed to create pipe" errors.
@@ -315,7 +372,7 @@ void string_to_spv(const std::string& _name, const std::string& in_fname, const
315
372
}
316
373
compile_count++;
317
374
}
318
- compiles.push_back (std::async (string_to_spv_func, _name, in_fname, defines, fp16, coopmat, coopmat2, f16acc ));
375
+ compiles.push_back (std::async (execute_compile_command, std::move (cmd) ));
319
376
}
320
377
321
378
void matmul_shaders (bool fp16, MatMulIdType matmul_id_type, bool coopmat, bool coopmat2, bool f16acc) {
@@ -765,12 +822,6 @@ void process_shaders() {
765
822
}
766
823
}
767
824
768
- std::stringstream make_generic_stringstream () {
769
- std::stringstream ss;
770
- ss.imbue (c_locale);
771
- return ss;
772
- }
773
-
774
825
std::vector<unsigned char > read_binary_file (const std::string& path, bool may_not_exist = false ) {
775
826
FILE* f = fopen (path.c_str (), " rb" );
776
827
if (!f) {
@@ -810,10 +861,14 @@ void write_binary_file(const std::string& path, const unsigned char * data, size
810
861
}
811
862
}
812
863
864
+ void write_binary_file (const std::string& path, const std::string& content) {
865
+ write_binary_file (path, (const unsigned char *)content.data (), content.size ());
866
+ }
867
+
813
868
void write_file_if_changed (const std::string& path, const std::string& content) {
814
869
std::vector<unsigned char > existing = read_binary_file (path, true );
815
870
if (existing.size () != content.size () || memcmp (existing.data (), content.data (), content.size ()) != 0 ) {
816
- write_binary_file (path, ( const unsigned char *) content. data (), content. size () );
871
+ write_binary_file (path, content);
817
872
}
818
873
}
819
874
@@ -944,8 +999,10 @@ void write_output_files() {
944
999
if (no_embed) {
945
1000
write_file_if_changed (target_cpp, src.str ());
946
1001
} else {
947
- std::string src_str = src.str ();
948
- write_binary_file (target_cpp, (const unsigned char *)src_str.data (), src_str.size ());
1002
+ write_binary_file (target_cpp, src.str ());
1003
+ }
1004
+ if (!ninja_build_file.empty ()) {
1005
+ write_file_if_changed (ninja_build_file, ninja_build.str ());
949
1006
}
950
1007
}
951
1008
@@ -988,6 +1045,7 @@ int main(int argc, char** argv) {
988
1045
}
989
1046
if (args.find (" --ninja" ) != args.end ()) {
990
1047
ninja_build_file = args[" --ninja" ]; // Write a ninja build file instead of invoking glslc directly
1048
+ ninja_init ();
991
1049
}
992
1050
993
1051
if (!directory_exists (input_dir)) {
0 commit comments