384 if (argc < 0 || argv == nullptr || argc > 4096) {
385 throw std::runtime_error(
386 "command line contains an invalid number of arguments");
393 const bool library_build_requested = [&] {
394 for (
int index = 1; index < argc; ++index) {
395 const std::string_view argument(argv[index]);
396 if (argument ==
"--build" || argument ==
"--builddir" ||
397 argument ==
"--fix" || argument ==
"--prune" ||
398 argument ==
"--force" || argument ==
"--glslc" ||
399 argument ==
"--parallel") {
405 if (library_build_requested) {
406 for (
int index = 1; index < argc; ++index) {
407 const std::string_view option(argv[index]);
409 "command-line option");
410 if (option ==
"-h" || option ==
"-v" ||
411 option ==
"--help" || option ==
"--version") {
413 }
else if (option ==
"--unbuffered") {
415 }
else if (option ==
"--build") {
417 throw std::runtime_error(
418 "--build may only be supplied once");
422 }
else if (option ==
"--builddir") {
424 throw std::runtime_error(
425 "--builddir and --fix are mutually exclusive");
429 }
else if (option ==
"--fix") {
431 throw std::runtime_error(
432 "--builddir and --fix are mutually exclusive");
437 }
else if (option ==
"--prune") {
439 throw std::runtime_error(
440 "--prune may only be supplied once");
443 }
else if (option ==
"--force") {
445 throw std::runtime_error(
446 "--force may only be supplied once");
449 }
else if (option ==
"--glslc") {
452 }
else if (option ==
"--parallel") {
454 throw std::runtime_error(
455 "--parallel may only be supplied once");
461 throw std::runtime_error(
462 "library build mode cannot be combined with option: " +
463 std::string(option));
470 options.
build_fix ?
"--fix" :
"--builddir",
475 throw std::runtime_error(
476 "--parallel job count must be between 1 and 256");
479 throw std::runtime_error(
480 "--builddir/--fix/--prune/--force/--glslc/--parallel requires "
481 "--build <library.json>");
484 throw std::runtime_error(
485 "--build requires --builddir or --fix <output-directory>");
489 throw std::runtime_error(
490 "--prune requires --fix <output-directory>");
494 throw std::runtime_error(
495 "--force is only valid with --prune");
499 throw std::runtime_error(
500 "WARNING: --prune permanently deletes .frag and .comp "
501 "source files that fail compilation; use --force to "
507 for (
int index = 1; index < argc; ++index) {
508 const std::string_view option(argv[index]);
510 "command-line option");
511 if (option ==
"-h" || option ==
"-v" || option ==
"--help" ||
512 option ==
"--version") {
514 }
else if (option ==
"--unbuffered") {
516 }
else if (option ==
"--silent" || option ==
"--headless") {
518 }
else if (option ==
"--interface-shm") {
520 }
else if (option ==
"-p" || option ==
"--path") {
524 throw std::runtime_error(
525 "resource path must not be empty");
527 }
else if (option ==
"-i" || option ==
"--input") {
529 }
else if (option ==
"-g" || option ==
"--graphic") {
531 }
else if (option ==
"-o" || option ==
"--output") {
533 }
else if (option ==
"-e" || option ==
"--prefix") {
537 throw std::runtime_error(
538 "snapshot directory must not be empty");
540 }
else if (option ==
"-d" || option ==
"--device") {
544 throw std::runtime_error(
545 "camera device index must be between 0 and 65535");
547 }
else if (option ==
"-c" || option ==
"--camera-res") {
550 }
else if (option ==
"--enumerate-device" ||
551 option ==
"--probe-camera") {
556 throw std::runtime_error(
557 "camera probe device index must be between 0 and 65535");
559 }
else if (option ==
"--list-camera-devices") {
561 }
else if (option ==
"--use-yuv") {
563 }
else if (option ==
"--maximize-fps") {
565 }
else if (option ==
"--use-source-fps") {
567 }
else if (option ==
"--use-source-audio") {
570 }
else if (option ==
"--edge") {
572 }
else if (option ==
"--human") {
574 }
else if (option ==
"--onnx") {
577 }
else if (option ==
"--background") {
579 }
else if (option ==
"--black") {
585 throw std::runtime_error(
586 "--black must be between 0.0 and 1.0");
588 }
else if (option ==
"--white") {
594 throw std::runtime_error(
595 "--white must be between 0.0 and 1.0");
597 }
else if (option ==
"--check-dnn") {
599 }
else if (option ==
"--check-deep-dream") {
601 }
else if (option ==
"--dream-model") {
603 }
else if (option ==
"--dream-layer") {
605 }
else if (option ==
"--dream-iterations") {
611 throw std::runtime_error(
612 "--dream-iterations must be between 1 and 100");
614 }
else if (option ==
"--dream-strength") {
620 throw std::runtime_error(
621 "--dream-strength must be greater than 0 and no more than 10");
623 }
else if (option ==
"--dream-feedback") {
629 throw std::runtime_error(
630 "--dream-feedback must be between 0 and 0.99");
632 }
else if (option ==
"--dream-zoom") {
637 throw std::runtime_error(
638 "--dream-zoom must be between 0.9 and 1.1");
640 }
else if (option ==
"--dream-rotation") {
646 throw std::runtime_error(
647 "--dream-rotation must be between -5 and 5 degrees");
649 }
else if (option ==
"--dream-size") {
655 throw std::runtime_error(
656 "--dream-size must be 0 or between 64 and 4096");
658 }
else if (option ==
"--dream-fp16") {
660 }
else if (option ==
"--dream-channel") {
661 const std::string value =
664 if (value ==
"all") {
670 throw std::runtime_error(
671 "--dream-channel must be 'all' or between 0 and 65535");
674 }
else if (option ==
"--dream-octaves") {
679 throw std::runtime_error(
680 "--dream-octaves must be between 1 and 8");
682 }
else if (option ==
"--dream-octave-scale") {
688 throw std::runtime_error(
689 "--dream-octave-scale must be between 1.1 and 3.0");
691 }
else if (option ==
"--dream-jitter") {
696 throw std::runtime_error(
697 "--dream-jitter must be between 0 and 64");
699 }
else if (option ==
"--dream-smoothing") {
705 throw std::runtime_error(
706 "--dream-smoothing must be between 0 and 16");
708 }
else if (option ==
"--random-dream" ||
709 option ==
"--random_dream") {
714 throw std::runtime_error(
715 std::string(option) +
" must be greater than 0");
717 }
else if (option ==
"--dream-headless") {
719 }
else if (option ==
"--deep-orig") {
721 }
else if (option ==
"--gpu-filter-before-dream") {
723 }
else if (option ==
"--probe-hdr") {
726 }
else if (option ==
"-r" || option ==
"--resolution") {
730 }
else if (option ==
"-s" || option ==
"--shaders") {
732 }
else if (option ==
"-f" || option ==
"--fragment") {
734 }
else if (option ==
"--compute") {
736 }
else if (option ==
"--enable-3d") {
738 }
else if (option ==
"--model") {
741 }
else if (option ==
"-H" || option ==
"--shader-index") {
747 throw std::runtime_error(
"shader index is outside the supported range");
749 }
else if (option ==
"--shader-file") {
751 }
else if (option ==
"--uniform") {
755 mxvk::VK_Sprite::MAX_CUSTOM_UNIFORMS) {
756 throw std::runtime_error(
757 "too many --uniform overrides were supplied");
759 }
else if (option ==
"--shader-pass") {
760 const std::string values =
optionValue(index, argc, argv, option);
761 std::size_t start = 0;
762 while (start <= values.size()) {
763 const std::size_t separator = values.find(
',', start);
764 const std::string_view value(
765 values.data() + start,
766 (separator == std::string::npos ? values.size() : separator) - start);
768 throw std::runtime_error(
769 "shader pass list contains an empty entry");
775 throw std::runtime_error(
776 "shader pass list is outside the supported range");
778 if (separator == std::string::npos) {
781 start = separator + 1;
783 }
else if (option ==
"--shader-pass-files") {
784 const std::string payload =
optionValue(index, argc, argv, option);
785 std::size_t start = 0;
786 while (start < payload.size()) {
787 const std::size_t separator = payload.find(
':', start);
788 if (separator == std::string::npos) {
789 throw std::runtime_error(
"invalid --shader-pass-files payload");
792 std::string_view(payload).substr(start, separator - start), option);
793 const std::size_t name_start = separator + 1;
795 payload.size() - name_start) {
796 throw std::runtime_error(
"invalid --shader-pass-files payload");
799 payload.substr(name_start,
static_cast<std::size_t
>(
length)));
802 throw std::runtime_error(
803 "shader pass file list contains too many entries");
805 start = name_start +
static_cast<std::size_t
>(
length);
807 }
else if (option ==
"--playlist") {
809 }
else if (option ==
"--enable-playlist") {
811 }
else if (option ==
"--cross-fade") {
816 throw std::runtime_error(
817 "crossfade duration must be between 0 and 60 seconds");
819 }
else if (option ==
"--time-speed") {
824 throw std::runtime_error(
825 "time speed must be between -1000 and 1000");
827 }
else if (option ==
"--normalized") {
829 }
else if (option ==
"--autopilot-frames" ||
830 option ==
"--autopilot-timeout") {
835 throw std::runtime_error(
836 "autopilot frame interval must be between 4 and 1000000000");
838 }
else if (option ==
"--autopilot-random" ||
839 option ==
"--autiopilot-random") {
844 throw std::runtime_error(
845 "autopilot random interval must be between 4 and 1000000000");
847 }
else if (option ==
"-u" || option ==
"--fps") {
852 throw std::runtime_error(
853 "FPS must be between 0 and 1000");
855 }
else if (option ==
"-w" || option ==
"--enable-audio") {
857 }
else if (option ==
"-l" || option ==
"--channels") {
862 throw std::runtime_error(
863 "audio channels must be between 1 and 32");
865 }
else if (option ==
"-q" || option ==
"--sense") {
870 throw std::runtime_error(
871 "audio sensitivity must be between 0.1 and 5.0");
873 }
else if (option ==
"--audio-warm-rate") {
879 throw std::runtime_error(
880 "audio warmup rate must be between 0 and 1000");
882 }
else if (option ==
"--audio-input") {
883 const std::string value =
optionValue(index, argc, argv, option);
889 throw std::runtime_error(
890 "audio input must be default or a non-negative device index");
892 }
else if (option ==
"--audio-file") {
895 }
else if (option ==
"-y" || option ==
"--pass-through") {
898 }
else if (option ==
"--audio-output") {
899 const std::string value =
optionValue(index, argc, argv, option);
905 throw std::runtime_error(
906 "audio output must be default or a non-negative device index");
908 }
else if (option ==
"--pass-through-gain") {
914 throw std::runtime_error(
915 "pass-through gain must be between 0.0 and 4.0");
917 }
else if (option ==
"--record-gain") {
923 throw std::runtime_error(
924 "recording gain must be between 0.0 and 2.0");
926 }
else if (option ==
"--record-audio") {
930 }
else if (option ==
"--audio-repeat") {
932 }
else if (option ==
"--audio-trunc") {
934 }
else if (option ==
"--enable-audio-buffers" ||
935 option ==
"--audio-buffers") {
939 throw std::runtime_error(
940 "audio history buffers must be between 0 and 64");
942 }
else if (option ==
"--list-devices") {
944 }
else if (option ==
"--check-audio") {
946 }
else if (option ==
"--midi-device") {
951 throw std::runtime_error(
952 "MIDI device index must be between 0 and 65535");
954 }
else if (option ==
"--midi-monitor") {
956 }
else if (option ==
"--midi-map") {
958 }
else if (option ==
"--midi-cc") {
962 mxvk::VK_Sprite::MAX_CUSTOM_UNIFORMS) {
963 throw std::runtime_error(
964 "too many --midi-cc mappings were supplied");
966 }
else if (option ==
"--list-midi") {
968 }
else if (option ==
"--check-midi") {
970 }
else if (option ==
"--gpu-filter") {
976 return value < 0 || value > 65535;
978 throw std::runtime_error(
979 "GPU filter list is outside the supported range");
981 }
else if (option ==
"--gpu-buffer") {
987 throw std::runtime_error(
988 "GPU frame buffer must be between 4 and 32");
990 }
else if (option ==
"-m" || option ==
"--cuda-device") {
995 throw std::runtime_error(
996 "CUDA device index must be between 0 and 1024");
998 }
else if (option ==
"--list-filters") {
1000 }
else if (option ==
"--list-cuda-devices") {
1002 }
else if (option ==
"--check-cuda") {
1004 }
else if (option ==
"--duration") {
1007 throw std::runtime_error(
1008 "duration must be between 0 and 604800 seconds");
1010 }
else if (option ==
"--max-size") {
1015 throw std::runtime_error(
1016 "maximum output size must be between 0 and 1048576 MB");
1018 }
else if (option ==
"--png") {
1020 }
else if (option ==
"--generate") {
1024 throw std::runtime_error(
"--generate requires a positive frame interval");
1026 }
else if (option ==
"-b" || option ==
"--encode-crf") {
1030 throw std::runtime_error(
"encoder CRF must be between 0 and 51");
1032 }
else if (option ==
"--bitrate" ||
1033 option ==
"--video-bitrate" ||
1034 option ==
"--encode-bitrate") {
1037 }
else if (option ==
"--encode-preset") {
1039 }
else if (option ==
"--encode-tune") {
1041 }
else if (option ==
"--encode-codec") {
1043 }
else if (option ==
"--encode-params") {
1045 }
else if (option ==
"--list-encoders") {
1047 }
else if (option ==
"--list-encoder-options") {
1049 }
else if (option ==
"--encode-realtime") {
1051 }
else if (option ==
"--no-drop") {
1053 }
else if (option ==
"--display-filter") {
1055 }
else if (option ==
"--disable-counter") {
1057 }
else if (option ==
"--use-watermark") {
1061 throw std::runtime_error(
1062 "--use-watermark requires non-empty text");
1064 }
else if (option ==
"--use-watermark-color") {
1067 }
else if (option ==
"--copy-audio") {
1069 }
else if (option ==
"--mute-output") {
1071 }
else if (option ==
"-n" || option ==
"--fullscreen") {
1073 }
else if (option ==
"-a" || option ==
"--repeat") {
1075 }
else if (option ==
"--enable-vsync") {
1077 }
else if (option ==
"--enable-screenshot") {
1079 }
else if (option ==
"--history-test") {
1082 }
else if (option ==
"--texture-cache" ||
1083 option ==
"--texture-cache-array") {
1085 }
else if (option ==
"--cache-delay") {
1089 throw std::runtime_error(
1090 "--cache-delay must be between 0 and 1000000");
1092 }
else if (option ==
"--texture-cache-size") {
1096 throw std::runtime_error(
1097 "--texture-cache-size must be between 1 and 64");
1099 }
else if (option ==
"--flip") {
1101 }
else if (option ==
"--rotate") {
1105 throw std::runtime_error(
"unknown option: " + std::string(option));
1111 throw std::runtime_error(
1112 "--use-source-audio requires --input <video>");
1115 throw std::runtime_error(
1116 "--use-source-audio requires --use-source-fps");
1119 throw std::runtime_error(
1120 "--use-source-audio cannot be combined with --audio-file");
1130 throw std::runtime_error(
"--input and --graphic cannot be used together");
1133 throw std::runtime_error(
"--dream-layer requires --dream-model");
1149 throw std::runtime_error(
1150 "Deep Dream processing options require --dream-model");
1154 throw std::runtime_error(
1155 "--dream-headless requires --headless or --silent");
1158 throw std::runtime_error(
1159 "--dream-headless and --deep-orig are mutually exclusive");
1162 throw std::runtime_error(
1163 "independent-frame Deep Dream mode requires --input <video>");
1166 throw std::runtime_error(
1167 "independent-frame Deep Dream mode requires --output <file>");
1170 throw std::runtime_error(
1171 "independent-frame Deep Dream mode cannot be combined with --random-dream");
1178 const int shader_source_count =
1182 if (shader_source_count > 1) {
1183 throw std::runtime_error(
1184 "--shaders, --fragment, and --compute are mutually exclusive");
1188 throw std::runtime_error(
"--uniform requires --shaders <directory>");
1192 throw std::runtime_error(
"--midi-cc requires --shaders <directory>");
1195 throw std::runtime_error(
"--gpu-buffer requires --gpu-filter <list>");
1199 throw std::runtime_error(
1200 "--gpu-filter-before-dream requires --gpu-filter <list>");
1203 throw std::runtime_error(
1204 "--gpu-filter-before-dream cannot be combined with --maximize-fps");
1207 throw std::runtime_error(
1208 "--gpu-filter-before-dream currently supports camera and video input");
1213 throw std::runtime_error(
1214 "--gpu-filter-before-dream cannot be combined with DNN input effects");
1219 throw std::runtime_error(
1220 "shader passes and playlists require --shaders <directory>");
1223 throw std::runtime_error(
"--enable-playlist requires --playlist <file>");
1227 throw std::runtime_error(
"autopilot requires --playlist <file>");
1230 fs::absolute(options.
output_file).lexically_normal() ==
1231 fs::absolute(options.
input_file).lexically_normal()) {
1232 throw std::runtime_error(
"output file must differ from the input file");
1235 throw std::runtime_error(
"--duration requires --output <file>");
1239 throw std::runtime_error(
"--png requires video --input and --output");
1243 throw std::runtime_error(
"--max-size requires encoded video output");
1248 throw std::runtime_error(
1249 "--copy-audio requires non-repeating video input and encoded output");
1252 throw std::runtime_error(
1253 "--copy-audio and --audio-file select different audio sources");
1256 throw std::runtime_error(
1257 "--record-audio records live input and cannot be used with --audio-file");
1260 const fs::path recording_path =
1262 const auto conflicts_with = [&](
const std::string &filename) {
1263 return !filename.empty() &&
1264 recording_path == fs::absolute(filename).lexically_normal();
1269 throw std::runtime_error(
1270 "--record-audio output must differ from media input and video output");
1276 throw std::runtime_error(
1277 "graphic recording requires --duration <seconds> or "
1278 "--audio-file <media> --audio-trunc");
1282 throw std::runtime_error(
1283 "--headless/--silent requires --input <video> or "
1284 "--graphic <image>; camera input is not supported");
1287 throw std::runtime_error(
1288 "--headless/--silent requires --output <file>");
1291 throw std::runtime_error(
1292 "headless graphic processing requires --duration "
1296 throw std::runtime_error(
1297 "--headless/--silent with --repeat requires --duration "
1302 throw std::runtime_error(
1303 "--headless/--silent cannot be combined with "
1304 "--fullscreen, --enable-vsync, or --enable-screenshot");
1308 throw std::runtime_error(
1309 "--enable-audio-buffers requires --enable-audio");
1312 throw std::runtime_error(
1313 "--audio-warm-rate requires an enabled audio source");
1316 throw std::runtime_error(
1317 "--audio-file and --audio-input cannot be used together");
1320 throw std::runtime_error(
1321 "--audio-repeat requires --audio-file <media>");
1324 throw std::runtime_error(
1325 "--audio-trunc requires --audio-file <media>");
1328 throw std::runtime_error(
1329 "--audio-output requires --pass-through");
1333 throw std::runtime_error(
1334 "--pass-through-gain requires --pass-through");
1341 throw std::runtime_error(
1342 "--record-gain requires live audio recording or encoded output");
1346 throw std::runtime_error(
1347 "--maximize-fps is available only for camera input");
1350 throw std::runtime_error(
1351 "--maximize-fps requires a target set with --fps");
1356 throw std::runtime_error(
1357 "--use-source-fps requires --input <video>");
1360 throw std::runtime_error(
1361 "--use-source-fps cannot be combined with --fps");
1367 throw std::runtime_error(
1368 "--background, --black, and --white require --human <model.onnx>");
1372 throw std::runtime_error(
1373 "--black must be less than --white for human segmentation");
1376 throw std::runtime_error(
1377 "--background is currently available only in 2D mode");