284 if (argc < 0 || argv == nullptr || argc > 4096) {
285 throw std::runtime_error(
"command line contains an invalid number of arguments");
292 const bool library_build_requested = [&] {
293 for (
int index = 1; index < argc; ++index) {
294 const std::string_view argument(argv[index]);
295 if (argument ==
"--build" || argument ==
"--builddir" || argument ==
"--fix" || argument ==
"--prune" || argument ==
"--force" || argument ==
"--glslc" || argument ==
"--parallel") {
301 if (library_build_requested) {
302 for (
int index = 1; index < argc; ++index) {
303 const std::string_view option(argv[index]);
305 if (option ==
"-h" || option ==
"-v" || option ==
"--help" || option ==
"--version") {
307 }
else if (option ==
"--unbuffered") {
309 }
else if (option ==
"--build") {
311 throw std::runtime_error(
"--build may only be supplied once");
314 }
else if (option ==
"--builddir") {
316 throw std::runtime_error(
"--builddir and --fix are mutually exclusive");
319 }
else if (option ==
"--fix") {
321 throw std::runtime_error(
"--builddir and --fix are mutually exclusive");
325 }
else if (option ==
"--prune") {
327 throw std::runtime_error(
"--prune may only be supplied once");
330 }
else if (option ==
"--force") {
332 throw std::runtime_error(
"--force may only be supplied once");
335 }
else if (option ==
"--glslc") {
337 }
else if (option ==
"--parallel") {
339 throw std::runtime_error(
"--parallel may only be supplied once");
344 throw std::runtime_error(
"library build mode cannot be combined with option: " + std::string(option));
351 throw std::runtime_error(
"--parallel job count must be between 1 and 256");
354 throw std::runtime_error(
"--builddir/--fix/--prune/--force/--glslc/--parallel requires "
355 "--build <library.json>");
358 throw std::runtime_error(
"--build requires --builddir or --fix <output-directory>");
361 throw std::runtime_error(
"--prune requires --fix <output-directory>");
364 throw std::runtime_error(
"--force is only valid with --prune");
367 throw std::runtime_error(
"WARNING: --prune permanently deletes .frag and .comp "
368 "source files that fail compilation; use --force to "
374 for (
int index = 1; index < argc; ++index) {
375 const std::string_view option(argv[index]);
377 if (option ==
"-h" || option ==
"-v" || option ==
"--help" || option ==
"--version") {
379 }
else if (option ==
"--unbuffered") {
381 }
else if (option ==
"--silent" || option ==
"--headless") {
383 }
else if (option ==
"--interface-shm") {
385 }
else if (option ==
"-p" || option ==
"--path") {
388 throw std::runtime_error(
"resource path must not be empty");
390 }
else if (option ==
"-i" || option ==
"--input") {
392 }
else if (option ==
"-g" || option ==
"--graphic") {
394 }
else if (option ==
"-o" || option ==
"--output") {
396 }
else if (option ==
"-e" || option ==
"--prefix") {
399 throw std::runtime_error(
"snapshot directory must not be empty");
401 }
else if (option ==
"-d" || option ==
"--device") {
404 throw std::runtime_error(
"camera device index must be between 0 and 65535");
406 }
else if (option ==
"-c" || option ==
"--camera-res") {
408 }
else if (option ==
"--enumerate-device" || option ==
"--probe-camera") {
411 throw std::runtime_error(
"camera probe device index must be between 0 and 65535");
413 }
else if (option ==
"--list-camera-devices") {
415 }
else if (option ==
"--use-yuv") {
417 }
else if (option ==
"--maximize-fps") {
419 }
else if (option ==
"--use-source-fps") {
421 }
else if (option ==
"--use-source-audio") {
424 }
else if (option ==
"--edge") {
426 }
else if (option ==
"--human") {
428 }
else if (option ==
"--onnx") {
430 }
else if (option ==
"--background") {
432 }
else if (option ==
"--black") {
436 throw std::runtime_error(
"--black must be between 0.0 and 1.0");
438 }
else if (option ==
"--white") {
442 throw std::runtime_error(
"--white must be between 0.0 and 1.0");
444 }
else if (option ==
"--check-dnn") {
446 }
else if (option ==
"--check-deep-dream") {
448 }
else if (option ==
"--check-stable-diffusion") {
450 }
else if (option ==
"--dream-model") {
452 }
else if (option ==
"--dream-layer") {
454 }
else if (option ==
"--dream-iterations") {
458 throw std::runtime_error(
"--dream-iterations must be between 1 and 100");
460 }
else if (option ==
"--dream-strength") {
464 throw std::runtime_error(
"--dream-strength must be greater than 0 and no more than 10");
466 }
else if (option ==
"--dream-feedback") {
470 throw std::runtime_error(
"--dream-feedback must be between 0 and 0.99");
472 }
else if (option ==
"--dream-zoom") {
476 throw std::runtime_error(
"--dream-zoom must be between 0.9 and 1.1");
478 }
else if (option ==
"--dream-rotation") {
482 throw std::runtime_error(
"--dream-rotation must be between -5 and 5 degrees");
484 }
else if (option ==
"--dream-size") {
488 throw std::runtime_error(
"--dream-size must be 0 or between 64 and 4096");
490 }
else if (option ==
"--dream-fp16") {
492 }
else if (option ==
"--dream-channel") {
493 const std::string value =
optionValue(index, argc, argv, option);
495 if (value ==
"all") {
500 throw std::runtime_error(
"--dream-channel must be 'all' or between 0 and 65535");
503 }
else if (option ==
"--dream-octaves") {
507 throw std::runtime_error(
"--dream-octaves must be between 1 and 8");
509 }
else if (option ==
"--dream-octave-scale") {
513 throw std::runtime_error(
"--dream-octave-scale must be between 1.1 and 3.0");
515 }
else if (option ==
"--dream-jitter") {
519 throw std::runtime_error(
"--dream-jitter must be between 0 and 64");
521 }
else if (option ==
"--dream-smoothing") {
525 throw std::runtime_error(
"--dream-smoothing must be between 0 and 16");
527 }
else if (option ==
"--random-dream" || option ==
"--random_dream") {
531 throw std::runtime_error(std::string(option) +
" must be greater than 0");
533 }
else if (option ==
"--dream-headless") {
535 }
else if (option ==
"--deep-orig") {
537 }
else if (option ==
"--gpu-filter-before-dream") {
539 }
else if (option ==
"--sd-model") {
542 }
else if (option ==
"--sd-prompt") {
545 }
else if (option ==
"--sd-negative-prompt") {
548 }
else if (option ==
"--sd-lora") {
551 }
else if (option ==
"--sd-lora-strength") {
554 throw std::runtime_error(
"--sd-lora-strength must follow --sd-lora");
557 if (multiplier < -10.0 || multiplier > 10.0) {
558 throw std::runtime_error(
"--sd-lora-strength must be between -10 and 10");
561 }
else if (option ==
"--sd-server") {
564 }
else if (option ==
"--sd-server-arg") {
566 constexpr std::size_t MAX_SERVER_ARGUMENTS = 256U;
568 throw std::runtime_error(
"--sd-server-arg may be repeated at most 256 times");
571 }
else if (option ==
"--sd-server-port") {
575 throw std::runtime_error(
"--sd-server-port must be between 1024 and 65535");
577 }
else if (option ==
"--sd-size") {
581 throw std::runtime_error(
"--sd-size dimensions must be multiples of 64 and no "
584 }
else if (option ==
"--sd-upscale-size") {
588 throw std::runtime_error(
"--sd-upscale-size dimensions must be between 64 and 4096");
590 }
else if (option ==
"--sd-steps") {
594 throw std::runtime_error(
"--sd-steps must be between 1 and 150");
596 }
else if (option ==
"--sd-strength") {
600 throw std::runtime_error(
"--sd-strength must be greater than 0 and no more than 1");
602 }
else if (option ==
"--sd-cfg-scale") {
606 throw std::runtime_error(
"--sd-cfg-scale must be between 0 and 50");
608 }
else if (option ==
"--sd-seed") {
611 }
else if (option ==
"--sd-sampler") {
614 }
else if (option ==
"--sd-scheduler") {
617 }
else if (option ==
"--sd-after-shaders") {
620 }
else if (option ==
"--sd-upscale") {
623 }
else if (option ==
"--sd-upscale-only") {
626 }
else if (option ==
"--upscale-model") {
629 }
else if (option ==
"--sd-quiet") {
632 }
else if (option ==
"--probe-hdr") {
634 }
else if (option ==
"-r" || option ==
"--resolution") {
637 }
else if (option ==
"-s" || option ==
"--shaders") {
639 }
else if (option ==
"-f" || option ==
"--fragment") {
641 }
else if (option ==
"--compute") {
643 }
else if (option ==
"--enable-3d") {
645 }
else if (option ==
"--model") {
648 }
else if (option ==
"-H" || option ==
"--shader-index") {
651 throw std::runtime_error(
"shader index is outside the supported range");
653 }
else if (option ==
"--shader-file") {
655 }
else if (option ==
"--uniform") {
658 throw std::runtime_error(
"too many --uniform overrides were supplied");
660 }
else if (option ==
"--shader-pass") {
661 const std::string values =
optionValue(index, argc, argv, option);
662 std::size_t start = 0;
663 while (start <= values.size()) {
664 const std::size_t separator = values.find(
',', start);
665 const std::string_view value(values.data() + start, (separator == std::string::npos ? values.size() : separator) - start);
667 throw std::runtime_error(
"shader pass list contains an empty entry");
671 throw std::runtime_error(
"shader pass list is outside the supported range");
673 if (separator == std::string::npos) {
676 start = separator + 1;
678 }
else if (option ==
"--shader-pass-files") {
679 const std::string payload =
optionValue(index, argc, argv, option);
680 std::size_t start = 0;
681 while (start < payload.size()) {
682 const std::size_t separator = payload.find(
':', start);
683 if (separator == std::string::npos) {
684 throw std::runtime_error(
"invalid --shader-pass-files payload");
686 const int length =
parseInteger(std::string_view(payload).substr(start, separator - start), option);
687 const std::size_t name_start = separator + 1;
688 if (
length < 0 ||
static_cast<std::size_t
>(
length) > payload.size() - name_start) {
689 throw std::runtime_error(
"invalid --shader-pass-files payload");
693 throw std::runtime_error(
"shader pass file list contains too many entries");
695 start = name_start +
static_cast<std::size_t
>(
length);
697 }
else if (option ==
"--playlist") {
699 }
else if (option ==
"--enable-playlist") {
701 }
else if (option ==
"--cross-fade") {
704 throw std::runtime_error(
"crossfade duration must be between 0 and 60 seconds");
706 }
else if (option ==
"--time-speed") {
709 throw std::runtime_error(
"time speed must be between -1000 and 1000");
711 }
else if (option ==
"--normalized") {
713 }
else if (option ==
"--enable-random-autopilot") {
715 }
else if (option ==
"--autopilot-frames" || option ==
"--autopilot-timeout") {
718 throw std::runtime_error(
"autopilot frame interval must be between 4 and 1000000000");
720 }
else if (option ==
"--autopilot-random" || option ==
"--autiopilot-random") {
723 throw std::runtime_error(
"autopilot random interval must be between 4 and 1000000000");
725 }
else if (option ==
"-u" || option ==
"--fps") {
728 throw std::runtime_error(
"FPS must be between 0 and 1000");
730 }
else if (option ==
"-w" || option ==
"--enable-audio") {
732 }
else if (option ==
"--audio-time" || option ==
"--enable-audio-time") {
734 }
else if (option ==
"-l" || option ==
"--channels") {
737 throw std::runtime_error(
"audio channels must be between 1 and 32");
739 }
else if (option ==
"-q" || option ==
"--sense") {
742 throw std::runtime_error(
"audio sensitivity must be between 0.1 and 5.0");
744 }
else if (option ==
"--audio-warm-rate") {
748 throw std::runtime_error(
"audio warmup rate must be between 0 and 1000");
750 }
else if (option ==
"--audio-input") {
751 const std::string value =
optionValue(index, argc, argv, option);
755 throw std::runtime_error(
"audio input must be default or a non-negative device index");
757 }
else if (option ==
"--audio-file") {
760 }
else if (option ==
"-y" || option ==
"--pass-through") {
763 }
else if (option ==
"--audio-output") {
764 const std::string value =
optionValue(index, argc, argv, option);
768 throw std::runtime_error(
"audio output must be default or a non-negative device index");
770 }
else if (option ==
"--pass-through-gain") {
774 throw std::runtime_error(
"pass-through gain must be between 0.0 and 4.0");
776 }
else if (option ==
"--record-gain") {
780 throw std::runtime_error(
"recording gain must be between 0.0 and 2.0");
782 }
else if (option ==
"--record-audio") {
785 }
else if (option ==
"--audio-repeat") {
787 }
else if (option ==
"--audio-trunc") {
789 }
else if (option ==
"--enable-audio-buffers" || option ==
"--audio-buffers") {
792 throw std::runtime_error(
"audio history buffers must be between 0 and 64");
794 }
else if (option ==
"--list-devices") {
796 }
else if (option ==
"--check-audio") {
798 }
else if (option ==
"--midi-device") {
802 throw std::runtime_error(
"MIDI device index must be between 0 and 65535");
804 }
else if (option ==
"--midi-monitor") {
806 }
else if (option ==
"--midi-map") {
808 }
else if (option ==
"--midi-cc") {
810 if (options.
midi_cc_mappings.size() > mxvk::VK_Sprite::MAX_CUSTOM_UNIFORMS) {
811 throw std::runtime_error(
"too many --midi-cc mappings were supplied");
813 }
else if (option ==
"--list-midi") {
815 }
else if (option ==
"--check-midi") {
817 }
else if (option ==
"--gpu-filter") {
820 throw std::runtime_error(
"GPU filter list is outside the supported range");
822 }
else if (option ==
"--gpu-buffer") {
826 throw std::runtime_error(
"GPU frame buffer must be between 4 and 32");
828 }
else if (option ==
"-m" || option ==
"--cuda-device") {
832 throw std::runtime_error(
"CUDA device index must be between 0 and 1024");
834 }
else if (option ==
"--list-filters") {
836 }
else if (option ==
"--list-cuda-devices") {
838 }
else if (option ==
"--check-cuda") {
840 }
else if (option ==
"--duration") {
843 throw std::runtime_error(
"duration must be between 0 and 604800 seconds");
845 }
else if (option ==
"--max-size") {
848 throw std::runtime_error(
"maximum output size must be between 0 and 1048576 MB");
850 }
else if (option ==
"--png") {
853 }
else if (option ==
"--png-level") {
856 throw std::runtime_error(
"--png-level must be between 1 and 9");
858 }
else if (option ==
"--generate") {
861 throw std::runtime_error(
"--generate requires a positive frame interval");
863 }
else if (option ==
"-b" || option ==
"--encode-crf") {
866 throw std::runtime_error(
"encoder CRF must be between 0 and 51");
868 }
else if (option ==
"--bitrate" || option ==
"--video-bitrate" || option ==
"--encode-bitrate") {
870 }
else if (option ==
"--encode-preset") {
872 }
else if (option ==
"--encode-tune") {
874 }
else if (option ==
"--encode-codec") {
876 }
else if (option ==
"--encode-params") {
878 }
else if (option ==
"--list-encoders") {
880 }
else if (option ==
"--list-encoder-options") {
882 }
else if (option ==
"--encode-realtime") {
884 }
else if (option ==
"--no-drop") {
886 }
else if (option ==
"--constant-frame-rate" || option ==
"--sequential-encode") {
888 }
else if (option ==
"--fill-pts-gaps") {
890 }
else if (option ==
"--display-filter") {
892 }
else if (option ==
"--disable-counter") {
894 }
else if (option ==
"--use-watermark") {
897 throw std::runtime_error(
"--use-watermark requires non-empty text");
899 }
else if (option ==
"--use-watermark-color") {
901 }
else if (option ==
"--copy-audio") {
903 }
else if (option ==
"--mute-output") {
905 }
else if (option ==
"-n" || option ==
"--fullscreen") {
907 }
else if (option ==
"-a" || option ==
"--repeat") {
909 }
else if (option ==
"--enable-vsync") {
911 }
else if (option ==
"--enable-screenshot") {
913 }
else if (option ==
"--history-test") {
916 }
else if (option ==
"--texture-cache" || option ==
"--texture-cache-array") {
918 }
else if (option ==
"--cache-delay") {
921 throw std::runtime_error(
"--cache-delay must be between 0 and 1000000");
923 }
else if (option ==
"--texture-cache-size") {
926 throw std::runtime_error(
"--texture-cache-size must be between 1 and 64");
928 }
else if (option ==
"--flip") {
930 }
else if (option ==
"--rotate") {
933 throw std::runtime_error(
"unknown option: " + std::string(option));
947 throw std::runtime_error(
"--pass-through cannot be used with --headless or --silent");
952 throw std::runtime_error(
"--use-source-audio requires --input <video>");
956 throw std::runtime_error(
"--use-source-audio requires --use-source-fps or "
957 "--headless --constant-frame-rate");
960 throw std::runtime_error(
"--use-source-audio cannot be combined with --audio-file");
970 throw std::runtime_error(
"--input and --graphic cannot be used together");
973 throw std::runtime_error(
"--dream-layer requires --dream-model");
976 throw std::runtime_error(
"Deep Dream processing options require --dream-model");
980 throw std::runtime_error(
"Stable Diffusion upscale-only processing requires --input <video>");
983 throw std::runtime_error(
"--sd-upscale-only requires --upscale-model <file>");
986 throw std::runtime_error(
"--sd-upscale-only cannot be combined with image-to-image model, prompt, or LoRA options");
989 throw std::runtime_error(
"--sd-upscale-only cannot be combined with --sd-after-shaders or --sd-upscale");
992 throw std::runtime_error(
"Stable Diffusion processing cannot be combined with --fill-pts-gaps");
996 throw std::runtime_error(
"Stable Diffusion processing requires --input <video>");
999 throw std::runtime_error(
"--sd-model requires --sd-prompt <text>");
1002 throw std::runtime_error(
"Stable Diffusion processing cannot be combined with --fill-pts-gaps");
1005 throw std::runtime_error(
"--sd-after-shaders requires --headless and encoded --output <video>");
1008 throw std::runtime_error(
"--sd-upscale cannot be combined with --sd-after-shaders");
1011 throw std::runtime_error(
"--sd-upscale and --upscale-model are mutually exclusive");
1014 throw std::runtime_error(
"Stable Diffusion options require --sd-model");
1018 throw std::runtime_error(
"--dream-headless requires --headless or --silent");
1021 throw std::runtime_error(
"--dream-headless and --deep-orig are mutually exclusive");
1024 throw std::runtime_error(
"independent-frame Deep Dream mode requires --input <video>");
1027 throw std::runtime_error(
"independent-frame Deep Dream mode requires --output <file>");
1030 throw std::runtime_error(
"independent-frame Deep Dream mode cannot be combined with --random-dream");
1038 if (shader_source_count > 1) {
1039 throw std::runtime_error(
"--shaders, --fragment, and --compute are mutually exclusive");
1042 throw std::runtime_error(
"--uniform requires --shaders <directory>");
1045 throw std::runtime_error(
"--midi-cc requires --shaders <directory>");
1048 throw std::runtime_error(
"--gpu-buffer requires --gpu-filter <list>");
1051 throw std::runtime_error(
"--gpu-filter-before-dream requires --gpu-filter <list>");
1054 throw std::runtime_error(
"--gpu-filter-before-dream cannot be combined with --maximize-fps");
1057 throw std::runtime_error(
"--gpu-filter-before-dream currently supports camera and video input");
1060 throw std::runtime_error(
"--gpu-filter-before-dream cannot be combined with DNN input effects");
1063 throw std::runtime_error(
"shader passes and playlists require --shaders <directory>");
1066 throw std::runtime_error(
"--enable-playlist requires --playlist <file>");
1069 throw std::runtime_error(
"--enable-random-autopilot requires --enable-playlist");
1072 throw std::runtime_error(
"autopilot requires --playlist <file>");
1075 throw std::runtime_error(
"output file must differ from the input file");
1078 throw std::runtime_error(
"--duration requires --output <file> or --png <directory>");
1081 throw std::runtime_error(
"--png requires video --input");
1084 throw std::runtime_error(
"--png <directory> cannot be combined with --output <file>");
1087 throw std::runtime_error(
"--max-size requires encoded video output");
1090 throw std::runtime_error(
"--constant-frame-rate requires video input and encoded output");
1093 throw std::runtime_error(
"--fill-pts-gaps requires encoded video output");
1096 throw std::runtime_error(
"--fill-pts-gaps and --constant-frame-rate are mutually exclusive");
1099 throw std::runtime_error(
"--copy-audio requires non-repeating video input and encoded output");
1102 throw std::runtime_error(
"--copy-audio and --audio-file select different audio sources");
1105 throw std::runtime_error(
"--record-audio records live input and cannot be used with --audio-file");
1108 const fs::path recording_path = fs::absolute(options.
record_audio_file).lexically_normal();
1109 const auto conflicts_with = [&](
const std::string &filename) {
return !filename.empty() && recording_path == fs::absolute(filename).lexically_normal(); };
1111 throw std::runtime_error(
"--record-audio output must differ from media input and video output");
1115 throw std::runtime_error(
"graphic recording requires --duration <seconds> or "
1116 "--audio-file <media> --audio-trunc");
1120 throw std::runtime_error(
"--headless/--silent requires --input <video> or "
1121 "--graphic <image>; camera input is not supported");
1124 throw std::runtime_error(
"--headless/--silent requires --output <file> or --png <directory>");
1127 throw std::runtime_error(
"headless graphic processing requires --duration "
1131 throw std::runtime_error(
"--headless/--silent with --repeat requires --duration "
1135 throw std::runtime_error(
"--headless/--silent cannot be combined with "
1136 "--fullscreen, --enable-vsync, or --enable-screenshot");
1140 throw std::runtime_error(
"--enable-audio-buffers requires --enable-audio");
1143 throw std::runtime_error(
"--audio-time requires an enabled audio source");
1146 throw std::runtime_error(
"--audio-warm-rate requires an enabled audio source");
1149 throw std::runtime_error(
"--audio-file and --audio-input cannot be used together");
1152 throw std::runtime_error(
"--audio-repeat requires --audio-file <media>");
1155 throw std::runtime_error(
"--audio-trunc requires --audio-file <media>");
1158 throw std::runtime_error(
"--audio-output requires --pass-through");
1161 throw std::runtime_error(
"--pass-through-gain requires --pass-through");
1164 throw std::runtime_error(
"--record-gain requires live audio recording or encoded output");
1168 throw std::runtime_error(
"--maximize-fps is available only for camera input");
1171 throw std::runtime_error(
"--maximize-fps requires a target set with --fps");
1176 throw std::runtime_error(
"--use-source-fps requires --input <video>");
1179 throw std::runtime_error(
"--use-source-fps cannot be combined with --fps");
1183 throw std::runtime_error(
"--background, --black, and --white require --human <model.onnx>");
1186 throw std::runtime_error(
"--black must be less than --white for human segmentation");
1189 throw std::runtime_error(
"--background is currently available only in 2D mode");