ACMX 2.136.0
Dual-Backend Real-Time GPU Video Synthesis
Loading...
Searching...
No Matches
options.cpp
Go to the documentation of this file.
1#include "options.hpp"
2
4#include "../version_info.hpp"
5#include "resource_paths.hpp"
6#include <mxvk/mxvk.hpp>
7#include <mxwrite.hpp>
8
9#include <algorithm>
10#include <cctype>
11#include <cmath>
12#include <cstdlib>
13#include <iostream>
14#include <stdexcept>
15#include <utility>
16
17namespace acmxvk {
18 namespace {
19 constexpr int MAX_FRAME_DIMENSION = 16384;
20 constexpr std::int64_t MAX_FRAME_PIXELS = 67108864;
21
22 [[nodiscard]] std::int64_t parse_video_bitrate(
23 std::string value, std::string_view option) {
24 if (value.empty()) {
25 throw std::runtime_error(std::string(option) +
26 " requires a bitrate");
27 }
28 std::int64_t multiplier = 1;
29 const char suffix = static_cast<char>(
30 std::toupper(static_cast<unsigned char>(value.back())));
31 if (suffix == 'K' || suffix == 'M' || suffix == 'G') {
32 value.pop_back();
33 multiplier = suffix == 'K' ? 1000LL
34 : suffix == 'M' ? 1000000LL
35 : 1000000000LL;
36 }
37 if (value.empty() ||
38 !std::ranges::all_of(value, [](unsigned char character) {
39 return std::isdigit(character) != 0;
40 })) {
41 throw std::runtime_error(
42 "video bitrate must be an integer with an optional K, M, "
43 "or G suffix (for example 10M)");
44 }
45 std::uint64_t amount = 0;
46 try {
47 amount = std::stoull(value);
48 } catch (const std::exception &) {
49 throw std::runtime_error("video bitrate is out of range");
50 }
51 constexpr std::uint64_t MAX_VIDEO_BITRATE = 100000000000ULL;
52 if (amount == 0U ||
53 amount > MAX_VIDEO_BITRATE /
54 static_cast<std::uint64_t>(multiplier)) {
55 throw std::runtime_error(
56 "video bitrate must be between 1 bit/s and 100G");
57 }
58 return static_cast<std::int64_t>(
59 amount * static_cast<std::uint64_t>(multiplier));
60 }
61 } // namespace
62
63 [[nodiscard]] bool dimensions_supported(int width, int height) {
64 return width > 0 && height > 0 && width <= MAX_FRAME_DIMENSION &&
65 height <= MAX_FRAME_DIMENSION &&
66 static_cast<std::int64_t>(width) * height <= MAX_FRAME_PIXELS;
67 }
68
69 [[nodiscard]] bool hasShaderManifest(const fs::path &directory) {
70 return fs::is_regular_file(directory / "library.json") ||
71 fs::is_regular_file(directory / "index.txt");
72 }
73
74 [[nodiscard]] std::string optionValue(int &index, int argc, char **argv,
75 std::string_view option) {
76 if (++index >= argc) {
77 throw std::runtime_error("missing value for " + std::string(option));
78 }
79 const std::string value(argv[index]);
81 std::string(option) + " value");
82 return value;
83 }
84
85 void validateLocator(std::string_view value, std::string_view context,
86 bool allow_empty = false) {
87 if (value.empty() && allow_empty) {
88 return;
89 }
90 const input::StringKind kind =
91 value.find("://") == std::string_view::npos
94 input::validate_string(value, kind, context, allow_empty);
95 }
96
97 void validateOptionStrings(const Options &options) {
98 validateLocator(options.input_file, "--input", true);
100 "--graphic", true);
102 "--audio-file", true);
104 "--output", true);
106 input::StringKind::Path, "--record-audio", true);
108 input::StringKind::Path, "--path", true);
110 input::StringKind::Path, "--prefix");
112 input::StringKind::Path, "--shaders", true);
114 input::StringKind::Path, "--fragment", true);
116 input::StringKind::Path, "--compute", true);
118 "--shader-file", true);
120 "--model", true);
122 "--playlist", true);
124 "--midi-map", true);
126 "--edge", true);
128 "--human", true);
130 input::StringKind::Path, "--onnx", true);
132 "--dream-model", true);
134 "--dream-layer", true);
135
136 for (const std::string &path : options.shader_pass_files) {
138 "--shader-pass-files entry");
139 }
140 for (const std::string &value : options.custom_uniform_overrides) {
142 "--uniform");
143 }
144 for (const std::string &value : options.midi_cc_mappings) {
146 "--midi-cc");
147 }
148
150 "--encode-preset");
152 "--encode-tune", true);
154 "--encode-codec");
157 "--encode-params", true);
160 "--list-encoder-options", true);
162 input::StringKind::Path, "--probe-hdr", true);
165 "--use-watermark", true);
166 }
167
168 [[nodiscard]] int parseInteger(std::string_view text, std::string_view option) {
170 option);
171 std::size_t parsed = 0;
172 int value = 0;
173 try {
174 value = std::stoi(std::string(text), &parsed);
175 } catch (const std::exception &) {
176 throw std::runtime_error("invalid integer for " + std::string(option) + ": " +
177 std::string(text));
178 }
179 if (parsed != text.size()) {
180 throw std::runtime_error("invalid integer for " + std::string(option) + ": " +
181 std::string(text));
182 }
183 return value;
184 }
185
186 [[nodiscard]] double parseNumber(std::string_view text, std::string_view option) {
188 option);
189 std::size_t parsed = 0;
190 double value = 0.0;
191 try {
192 value = std::stod(std::string(text), &parsed);
193 } catch (const std::exception &) {
194 throw std::runtime_error("invalid number for " + std::string(option) + ": " +
195 std::string(text));
196 }
197 if (parsed != text.size() || !std::isfinite(value)) {
198 throw std::runtime_error("invalid number for " + std::string(option) + ": " +
199 std::string(text));
200 }
201 return value;
202 }
203
204 [[nodiscard]] std::vector<int>
205 parseIntegerList(std::string_view text, std::string_view option) {
207 option);
208 if (text.empty()) {
209 throw std::runtime_error("empty integer list for " +
210 std::string(option));
211 }
212 std::vector<int> values;
213 std::size_t start = 0;
214 while (start <= text.size()) {
215 const std::size_t separator = text.find(',', start);
216 const std::size_t end = separator == std::string_view::npos
217 ? text.size()
218 : separator;
219 if (end == start) {
220 throw std::runtime_error("invalid integer list for " +
221 std::string(option) + ": " +
222 std::string(text));
223 }
224 values.push_back(parseInteger(text.substr(start, end - start),
225 option));
226 if (separator == std::string_view::npos) {
227 break;
228 }
229 start = separator + 1;
230 }
231 return values;
232 }
233
234 [[nodiscard]] std::array<std::uint8_t, 3>
235 parseColor(std::string_view text, std::string_view option) {
236 const std::vector<int> components = parseIntegerList(text, option);
237 if (components.size() != 3U) {
238 throw std::runtime_error(std::string(option) +
239 " requires r,g,b");
240 }
241
242 std::array<std::uint8_t, 3> color{};
243 for (std::size_t index = 0; index < color.size(); ++index) {
244 if (components[index] < 0 || components[index] > 255) {
245 throw std::runtime_error(std::string(option) +
246 " components must be between 0 and 255");
247 }
248 color[index] = static_cast<std::uint8_t>(components[index]);
249 }
250 return color;
251 }
252
253 void parseDimensions(std::string_view text, int &width, int &height,
254 std::string_view option) {
255 const std::size_t separator = text.find_first_of("xX");
256 if (separator == std::string_view::npos) {
257 throw std::runtime_error("invalid dimensions for " + std::string(option) +
258 "; expected WidthxHeight");
259 }
260
261 width = parseInteger(text.substr(0, separator), option);
262 height = parseInteger(text.substr(separator + 1), option);
263 if (!dimensions_supported(width, height)) {
264 throw std::runtime_error(
265 "dimensions are outside the supported range for " +
266 std::string(option));
267 }
268 }
269
270 [[nodiscard]] FrameRotation parseFrameRotation(std::string value) {
272 std::transform(value.begin(), value.end(), value.begin(), [](unsigned char character) {
273 return static_cast<char>(std::tolower(character));
274 });
275 if (value == "clockwise" || value == "cw" || value == "90" || value == "90cw") {
277 }
278 if (value == "180") {
280 }
281 if (value == "counterclockwise" || value == "ccw" || value == "90ccw" ||
282 value == "270") {
284 }
285 throw std::runtime_error(
286 "--rotate requires clockwise, 180, or counterclockwise");
287 }
288
289 [[nodiscard]] bool isUtilityRequest(const Options &options) {
290 return options.show_help || options.list_audio_devices ||
291 options.list_camera_devices ||
292 options.check_audio || options.list_midi_devices ||
293 options.check_midi || options.list_gpu_filters ||
294 options.list_cuda_devices || options.check_cuda ||
295 options.check_dnn || options.check_deep_dream ||
296 !options.probe_hdr_file.empty() ||
297 options.enumerate_camera_device >= 0 ||
298 options.list_encoders || !options.list_encoder_options.empty();
299 }
300
302 if (isUtilityRequest(options)) {
303 return;
304 }
305
306 std::string resource_source;
307 if (!options.resource_directory.empty()) {
308 resource_source = "--path";
309 } else if (const char *environment = std::getenv("ACMXVK_PATH");
310 environment != nullptr && environment[0] != '\0') {
312 "ACMXVK_PATH");
313 options.resource_directory = environment;
314 resource_source = "ACMXVK_PATH";
315 } else if (const char *environment = std::getenv("ACMX2_PATH");
316 environment != nullptr && environment[0] != '\0') {
318 "ACMX2_PATH");
319 const fs::path compatibility_directory =
320 fs::absolute(environment).lexically_normal();
321 if (fs::is_directory(compatibility_directory)) {
322 options.resource_directory = compatibility_directory.string();
323 resource_source = "ACMX2_PATH compatibility fallback";
324 } else {
325 std::cerr << "acmxvk: ignoring unavailable ACMX2_PATH: "
326 << compatibility_directory.string() << '\n';
327 }
328 }
329
330 if (!options.resource_directory.empty()) {
331 const fs::path directory =
332 fs::absolute(options.resource_directory).lexically_normal();
333 if (!fs::is_directory(directory)) {
334 throw std::runtime_error(resource_source +
335 " is not a readable resource directory: " +
336 directory.string());
337 }
338 options.resource_directory = directory.string();
339 std::cout << "acmxvk: resource path (" << resource_source
340 << "): " << options.resource_directory << '\n';
341 }
342
343 if (!options.shader_directory.empty() ||
344 !options.fragment_shader.empty() ||
345 !options.compute_shader.empty()) {
346 return;
347 }
348
349 if (const char *environment = std::getenv("ACMXVK_SHADER_PATH");
350 environment != nullptr && environment[0] != '\0') {
352 "ACMXVK_SHADER_PATH");
353 fs::path directory = fs::absolute(environment).lexically_normal();
354 if (fs::is_regular_file(directory) &&
355 (directory.filename() == "library.json" ||
356 directory.filename() == "index.txt")) {
357 directory = directory.parent_path();
358 }
359 if (!fs::is_directory(directory) || !hasShaderManifest(directory)) {
360 throw std::runtime_error(
361 "ACMXVK_SHADER_PATH does not contain library.json or index.txt: " +
362 directory.string());
363 }
364 options.shader_directory = directory.string();
365 std::cout << "acmxvk: shader library (ACMXVK_SHADER_PATH): "
366 << options.shader_directory << '\n';
367 return;
368 }
369
370 for (const fs::path &resource_directory :
371 resource_directories(options)) {
372 const fs::path shader_directory = resource_directory / "shaders";
373 if (hasShaderManifest(shader_directory)) {
374 options.shader_directory = shader_directory.string();
375 std::cout << "acmxvk: shader library (resource path): "
376 << options.shader_directory << '\n';
377 return;
378 }
379 }
380 }
381
382 [[nodiscard]] Options parseOptions(int argc, char **argv) {
383 Options options;
384 if (argc < 0 || argv == nullptr || argc > 4096) {
385 throw std::runtime_error(
386 "command line contains an invalid number of arguments");
387 }
388 if (argc == 1) {
389 options.show_help = true;
390 return options;
391 }
392
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") {
400 return true;
401 }
402 }
403 return false;
404 }();
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") {
412 options.show_help = true;
413 } else if (option == "--unbuffered") {
414 options.unbuffered_output = true;
415 } else if (option == "--build") {
416 if (!options.build_manifest.empty()) {
417 throw std::runtime_error(
418 "--build may only be supplied once");
419 }
420 options.build_manifest =
421 optionValue(index, argc, argv, option);
422 } else if (option == "--builddir") {
423 if (!options.build_directory.empty()) {
424 throw std::runtime_error(
425 "--builddir and --fix are mutually exclusive");
426 }
427 options.build_directory =
428 optionValue(index, argc, argv, option);
429 } else if (option == "--fix") {
430 if (!options.build_directory.empty()) {
431 throw std::runtime_error(
432 "--builddir and --fix are mutually exclusive");
433 }
434 options.build_directory =
435 optionValue(index, argc, argv, option);
436 options.build_fix = true;
437 } else if (option == "--prune") {
438 if (options.build_prune) {
439 throw std::runtime_error(
440 "--prune may only be supplied once");
441 }
442 options.build_prune = true;
443 } else if (option == "--force") {
444 if (options.build_force) {
445 throw std::runtime_error(
446 "--force may only be supplied once");
447 }
448 options.build_force = true;
449 } else if (option == "--glslc") {
450 options.glslc_executable =
451 optionValue(index, argc, argv, option);
452 } else if (option == "--parallel") {
453 if (options.build_parallel_specified) {
454 throw std::runtime_error(
455 "--parallel may only be supplied once");
456 }
458 optionValue(index, argc, argv, option), option);
459 options.build_parallel_specified = true;
460 } else {
461 throw std::runtime_error(
462 "library build mode cannot be combined with option: " +
463 std::string(option));
464 }
465 }
467 input::StringKind::Path, "--build", true);
470 options.build_fix ? "--fix" : "--builddir",
471 true);
473 input::StringKind::Path, "--glslc");
474 if (options.build_parallel < 1 || options.build_parallel > 256) {
475 throw std::runtime_error(
476 "--parallel job count must be between 1 and 256");
477 }
478 if (!options.show_help && options.build_manifest.empty()) {
479 throw std::runtime_error(
480 "--builddir/--fix/--prune/--force/--glslc/--parallel requires "
481 "--build <library.json>");
482 }
483 if (!options.show_help && options.build_directory.empty()) {
484 throw std::runtime_error(
485 "--build requires --builddir or --fix <output-directory>");
486 }
487 if (!options.show_help && options.build_prune &&
488 !options.build_fix) {
489 throw std::runtime_error(
490 "--prune requires --fix <output-directory>");
491 }
492 if (!options.show_help && options.build_force &&
493 !options.build_prune) {
494 throw std::runtime_error(
495 "--force is only valid with --prune");
496 }
497 if (!options.show_help && options.build_prune &&
498 !options.build_force) {
499 throw std::runtime_error(
500 "WARNING: --prune permanently deletes .frag and .comp "
501 "source files that fail compilation; use --force to "
502 "confirm");
503 }
504 return options;
505 }
506
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") {
513 options.show_help = true;
514 } else if (option == "--unbuffered") {
515 options.unbuffered_output = true;
516 } else if (option == "--silent" || option == "--headless") {
517 options.headless = true;
518 } else if (option == "--interface-shm") {
519 options.interface_shm = true;
520 } else if (option == "-p" || option == "--path") {
521 options.resource_directory =
522 optionValue(index, argc, argv, option);
523 if (options.resource_directory.empty()) {
524 throw std::runtime_error(
525 "resource path must not be empty");
526 }
527 } else if (option == "-i" || option == "--input") {
528 options.input_file = optionValue(index, argc, argv, option);
529 } else if (option == "-g" || option == "--graphic") {
530 options.graphic_file = optionValue(index, argc, argv, option);
531 } else if (option == "-o" || option == "--output") {
532 options.output_file = optionValue(index, argc, argv, option);
533 } else if (option == "-e" || option == "--prefix") {
534 options.snapshot_directory =
535 optionValue(index, argc, argv, option);
536 if (options.snapshot_directory.empty()) {
537 throw std::runtime_error(
538 "snapshot directory must not be empty");
539 }
540 } else if (option == "-d" || option == "--device") {
541 options.camera_device =
542 parseInteger(optionValue(index, argc, argv, option), option);
543 if (options.camera_device < 0 || options.camera_device > 65535) {
544 throw std::runtime_error(
545 "camera device index must be between 0 and 65535");
546 }
547 } else if (option == "-c" || option == "--camera-res") {
548 parseDimensions(optionValue(index, argc, argv, option),
549 options.camera_width, options.camera_height, option);
550 } else if (option == "--enumerate-device" ||
551 option == "--probe-camera") {
553 parseInteger(optionValue(index, argc, argv, option), option);
554 if (options.enumerate_camera_device < 0 ||
555 options.enumerate_camera_device > 65535) {
556 throw std::runtime_error(
557 "camera probe device index must be between 0 and 65535");
558 }
559 } else if (option == "--list-camera-devices") {
560 options.list_camera_devices = true;
561 } else if (option == "--use-yuv") {
562 options.use_yuv = true;
563 } else if (option == "--maximize-fps") {
564 options.maximize_fps = true;
565 } else if (option == "--use-source-fps") {
566 options.use_source_fps = true;
567 } else if (option == "--use-source-audio") {
568 options.use_source_audio = true;
569 options.enable_audio = true;
570 } else if (option == "--edge") {
571 options.edge_model = optionValue(index, argc, argv, option);
572 } else if (option == "--human") {
573 options.human_model = optionValue(index, argc, argv, option);
574 } else if (option == "--onnx") {
575 options.onnx_configuration =
576 optionValue(index, argc, argv, option);
577 } else if (option == "--background") {
578 options.human_background = true;
579 } else if (option == "--black") {
580 options.human_black_point =
581 parseNumber(optionValue(index, argc, argv, option), option);
582 options.human_black_specified = true;
583 if (options.human_black_point < 0.0 ||
584 options.human_black_point > 1.0) {
585 throw std::runtime_error(
586 "--black must be between 0.0 and 1.0");
587 }
588 } else if (option == "--white") {
589 options.human_white_point =
590 parseNumber(optionValue(index, argc, argv, option), option);
591 options.human_white_specified = true;
592 if (options.human_white_point < 0.0 ||
593 options.human_white_point > 1.0) {
594 throw std::runtime_error(
595 "--white must be between 0.0 and 1.0");
596 }
597 } else if (option == "--check-dnn") {
598 options.check_dnn = true;
599 } else if (option == "--check-deep-dream") {
600 options.check_deep_dream = true;
601 } else if (option == "--dream-model") {
602 options.dream_model = optionValue(index, argc, argv, option);
603 } else if (option == "--dream-layer") {
604 options.dream_layer = optionValue(index, argc, argv, option);
605 } else if (option == "--dream-iterations") {
606 options.dream_iterations =
607 parseInteger(optionValue(index, argc, argv, option), option);
608 options.dream_iterations_specified = true;
609 if (options.dream_iterations < 1 ||
610 options.dream_iterations > 100) {
611 throw std::runtime_error(
612 "--dream-iterations must be between 1 and 100");
613 }
614 } else if (option == "--dream-strength") {
615 options.dream_strength =
616 parseNumber(optionValue(index, argc, argv, option), option);
617 options.dream_strength_specified = true;
618 if (options.dream_strength <= 0.0 ||
619 options.dream_strength > 10.0) {
620 throw std::runtime_error(
621 "--dream-strength must be greater than 0 and no more than 10");
622 }
623 } else if (option == "--dream-feedback") {
624 options.dream_feedback =
625 parseNumber(optionValue(index, argc, argv, option), option);
626 options.dream_feedback_specified = true;
627 if (options.dream_feedback < 0.0 ||
628 options.dream_feedback > 0.99) {
629 throw std::runtime_error(
630 "--dream-feedback must be between 0 and 0.99");
631 }
632 } else if (option == "--dream-zoom") {
633 options.dream_zoom =
634 parseNumber(optionValue(index, argc, argv, option), option);
635 options.dream_zoom_specified = true;
636 if (options.dream_zoom < 0.9 || options.dream_zoom > 1.1) {
637 throw std::runtime_error(
638 "--dream-zoom must be between 0.9 and 1.1");
639 }
640 } else if (option == "--dream-rotation") {
641 options.dream_rotation =
642 parseNumber(optionValue(index, argc, argv, option), option);
643 options.dream_rotation_specified = true;
644 if (options.dream_rotation < -5.0 ||
645 options.dream_rotation > 5.0) {
646 throw std::runtime_error(
647 "--dream-rotation must be between -5 and 5 degrees");
648 }
649 } else if (option == "--dream-size") {
650 options.dream_size =
651 parseInteger(optionValue(index, argc, argv, option), option);
652 options.dream_size_specified = true;
653 if (options.dream_size != 0 &&
654 (options.dream_size < 64 || options.dream_size > 4096)) {
655 throw std::runtime_error(
656 "--dream-size must be 0 or between 64 and 4096");
657 }
658 } else if (option == "--dream-fp16") {
659 options.dream_fp16 = true;
660 } else if (option == "--dream-channel") {
661 const std::string value =
662 optionValue(index, argc, argv, option);
663 options.dream_channel_specified = true;
664 if (value == "all") {
665 options.dream_channel = -1;
666 } else {
667 options.dream_channel = parseInteger(value, option);
668 if (options.dream_channel < 0 ||
669 options.dream_channel > 65535) {
670 throw std::runtime_error(
671 "--dream-channel must be 'all' or between 0 and 65535");
672 }
673 }
674 } else if (option == "--dream-octaves") {
675 options.dream_octaves =
676 parseInteger(optionValue(index, argc, argv, option), option);
677 options.dream_octaves_specified = true;
678 if (options.dream_octaves < 1 || options.dream_octaves > 8) {
679 throw std::runtime_error(
680 "--dream-octaves must be between 1 and 8");
681 }
682 } else if (option == "--dream-octave-scale") {
683 options.dream_octave_scale =
684 parseNumber(optionValue(index, argc, argv, option), option);
685 options.dream_octave_scale_specified = true;
686 if (options.dream_octave_scale < 1.1 ||
687 options.dream_octave_scale > 3.0) {
688 throw std::runtime_error(
689 "--dream-octave-scale must be between 1.1 and 3.0");
690 }
691 } else if (option == "--dream-jitter") {
692 options.dream_jitter =
693 parseInteger(optionValue(index, argc, argv, option), option);
694 options.dream_jitter_specified = true;
695 if (options.dream_jitter < 0 || options.dream_jitter > 64) {
696 throw std::runtime_error(
697 "--dream-jitter must be between 0 and 64");
698 }
699 } else if (option == "--dream-smoothing") {
700 options.dream_smoothing =
701 parseInteger(optionValue(index, argc, argv, option), option);
702 options.dream_smoothing_specified = true;
703 if (options.dream_smoothing < 0 ||
704 options.dream_smoothing > 16) {
705 throw std::runtime_error(
706 "--dream-smoothing must be between 0 and 16");
707 }
708 } else if (option == "--random-dream" ||
709 option == "--random_dream") {
710 options.random_dream_interval =
711 parseNumber(optionValue(index, argc, argv, option), option);
712 options.random_dream_specified = true;
713 if (options.random_dream_interval <= 0.0) {
714 throw std::runtime_error(
715 std::string(option) + " must be greater than 0");
716 }
717 } else if (option == "--dream-headless") {
718 options.dream_headless = true;
719 } else if (option == "--deep-orig") {
720 options.deep_original = true;
721 } else if (option == "--gpu-filter-before-dream") {
722 options.gpu_filter_before_dream = true;
723 } else if (option == "--probe-hdr") {
724 options.probe_hdr_file =
725 optionValue(index, argc, argv, option);
726 } else if (option == "-r" || option == "--resolution") {
727 parseDimensions(optionValue(index, argc, argv, option), options.width,
728 options.height, option);
729 options.resolution_specified = true;
730 } else if (option == "-s" || option == "--shaders") {
731 options.shader_directory = optionValue(index, argc, argv, option);
732 } else if (option == "-f" || option == "--fragment") {
733 options.fragment_shader = optionValue(index, argc, argv, option);
734 } else if (option == "--compute") {
735 options.compute_shader = optionValue(index, argc, argv, option);
736 } else if (option == "--enable-3d") {
737 options.enable_3d = true;
738 } else if (option == "--model") {
739 options.model_file = optionValue(index, argc, argv, option);
740 options.enable_3d = true;
741 } else if (option == "-H" || option == "--shader-index") {
742 options.shader_index =
743 parseInteger(optionValue(index, argc, argv, option), option);
744 if (options.shader_index < 0 ||
745 options.shader_index >=
746 static_cast<int>(input::MAX_SHADER_ENTRIES)) {
747 throw std::runtime_error("shader index is outside the supported range");
748 }
749 } else if (option == "--shader-file") {
750 options.shader_file = optionValue(index, argc, argv, option);
751 } else if (option == "--uniform") {
752 options.custom_uniform_overrides.push_back(
753 optionValue(index, argc, argv, option));
754 if (options.custom_uniform_overrides.size() >
755 mxvk::VK_Sprite::MAX_CUSTOM_UNIFORMS) {
756 throw std::runtime_error(
757 "too many --uniform overrides were supplied");
758 }
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);
767 if (value.empty()) {
768 throw std::runtime_error(
769 "shader pass list contains an empty entry");
770 }
771 options.shader_pass_indices.push_back(parseInteger(value, option));
772 if (options.shader_pass_indices.back() < 0 ||
773 options.shader_pass_indices.size() >
775 throw std::runtime_error(
776 "shader pass list is outside the supported range");
777 }
778 if (separator == std::string::npos) {
779 break;
780 }
781 start = separator + 1;
782 }
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");
790 }
791 const int length = parseInteger(
792 std::string_view(payload).substr(start, separator - start), option);
793 const std::size_t name_start = separator + 1;
794 if (length < 0 || static_cast<std::size_t>(length) >
795 payload.size() - name_start) {
796 throw std::runtime_error("invalid --shader-pass-files payload");
797 }
798 options.shader_pass_files.push_back(
799 payload.substr(name_start, static_cast<std::size_t>(length)));
800 if (options.shader_pass_files.size() >
802 throw std::runtime_error(
803 "shader pass file list contains too many entries");
804 }
805 start = name_start + static_cast<std::size_t>(length);
806 }
807 } else if (option == "--playlist") {
808 options.playlist_file = optionValue(index, argc, argv, option);
809 } else if (option == "--enable-playlist") {
810 options.enable_playlist = true;
811 } else if (option == "--cross-fade") {
812 options.cross_fade_duration =
813 parseNumber(optionValue(index, argc, argv, option), option);
814 if (options.cross_fade_duration < 0.0 ||
815 options.cross_fade_duration > 60.0) {
816 throw std::runtime_error(
817 "crossfade duration must be between 0 and 60 seconds");
818 }
819 } else if (option == "--time-speed") {
820 options.time_speed =
821 parseNumber(optionValue(index, argc, argv, option), option);
822 if (options.time_speed < -1000.0 ||
823 options.time_speed > 1000.0) {
824 throw std::runtime_error(
825 "time speed must be between -1000 and 1000");
826 }
827 } else if (option == "--normalized") {
828 options.normalized_time = true;
829 } else if (option == "--autopilot-frames" ||
830 option == "--autopilot-timeout") {
832 optionValue(index, argc, argv, option), option);
833 if (options.autopilot_frames < 4 ||
834 options.autopilot_frames > 1000000000) {
835 throw std::runtime_error(
836 "autopilot frame interval must be between 4 and 1000000000");
837 }
838 } else if (option == "--autopilot-random" ||
839 option == "--autiopilot-random") {
841 optionValue(index, argc, argv, option), option);
842 if (options.autopilot_random_timeout < 4 ||
843 options.autopilot_random_timeout > 1000000000) {
844 throw std::runtime_error(
845 "autopilot random interval must be between 4 and 1000000000");
846 }
847 } else if (option == "-u" || option == "--fps") {
848 options.requested_fps =
849 parseNumber(optionValue(index, argc, argv, option), option);
850 if (options.requested_fps <= 0.0 ||
851 options.requested_fps > 1000.0) {
852 throw std::runtime_error(
853 "FPS must be between 0 and 1000");
854 }
855 } else if (option == "-w" || option == "--enable-audio") {
856 options.enable_audio = true;
857 } else if (option == "-l" || option == "--channels") {
858 options.audio_channels =
859 parseInteger(optionValue(index, argc, argv, option), option);
860 if (options.audio_channels < 1 ||
861 options.audio_channels > 32) {
862 throw std::runtime_error(
863 "audio channels must be between 1 and 32");
864 }
865 } else if (option == "-q" || option == "--sense") {
866 options.audio_sensitivity =
867 parseNumber(optionValue(index, argc, argv, option), option);
868 if (options.audio_sensitivity < 0.1 ||
869 options.audio_sensitivity > 5.0) {
870 throw std::runtime_error(
871 "audio sensitivity must be between 0.1 and 5.0");
872 }
873 } else if (option == "--audio-warm-rate") {
874 options.audio_warm_rate =
875 parseNumber(optionValue(index, argc, argv, option), option);
876 options.audio_warm_rate_specified = true;
877 if (options.audio_warm_rate < 0.0 ||
878 options.audio_warm_rate > 1000.0) {
879 throw std::runtime_error(
880 "audio warmup rate must be between 0 and 1000");
881 }
882 } else if (option == "--audio-input") {
883 const std::string value = optionValue(index, argc, argv, option);
884 options.audio_input_specified = true;
885 options.audio_input_device =
886 value == "default" ? -1 : parseInteger(value, option);
887 if (options.audio_input_device < -1 ||
888 options.audio_input_device > 65535) {
889 throw std::runtime_error(
890 "audio input must be default or a non-negative device index");
891 }
892 } else if (option == "--audio-file") {
893 options.audio_file = optionValue(index, argc, argv, option);
894 options.enable_audio = true;
895 } else if (option == "-y" || option == "--pass-through") {
896 options.audio_pass_through = true;
897 options.enable_audio = true;
898 } else if (option == "--audio-output") {
899 const std::string value = optionValue(index, argc, argv, option);
900 options.audio_output_specified = true;
901 options.audio_output_device =
902 value == "default" ? -1 : parseInteger(value, option);
903 if (options.audio_output_device < -1 ||
904 options.audio_output_device > 65535) {
905 throw std::runtime_error(
906 "audio output must be default or a non-negative device index");
907 }
908 } else if (option == "--pass-through-gain") {
910 parseNumber(optionValue(index, argc, argv, option), option);
912 if (options.audio_pass_through_gain < 0.0 ||
913 options.audio_pass_through_gain > 4.0) {
914 throw std::runtime_error(
915 "pass-through gain must be between 0.0 and 4.0");
916 }
917 } else if (option == "--record-gain") {
918 options.audio_recording_gain =
919 parseNumber(optionValue(index, argc, argv, option), option);
920 options.audio_recording_gain_specified = true;
921 if (options.audio_recording_gain < 0.0 ||
922 options.audio_recording_gain > 2.0) {
923 throw std::runtime_error(
924 "recording gain must be between 0.0 and 2.0");
925 }
926 } else if (option == "--record-audio") {
927 options.record_audio_file =
928 optionValue(index, argc, argv, option);
929 options.enable_audio = true;
930 } else if (option == "--audio-repeat") {
931 options.audio_repeat = true;
932 } else if (option == "--audio-trunc") {
933 options.audio_trunc = true;
934 } else if (option == "--enable-audio-buffers" ||
935 option == "--audio-buffers") {
936 options.audio_buffers = parseInteger(
937 optionValue(index, argc, argv, option), option);
938 if (options.audio_buffers < 0 || options.audio_buffers > 64) {
939 throw std::runtime_error(
940 "audio history buffers must be between 0 and 64");
941 }
942 } else if (option == "--list-devices") {
943 options.list_audio_devices = true;
944 } else if (option == "--check-audio") {
945 options.check_audio = true;
946 } else if (option == "--midi-device") {
947 options.midi_device =
948 parseInteger(optionValue(index, argc, argv, option), option);
949 options.midi_device_specified = true;
950 if (options.midi_device < 0 || options.midi_device > 65535) {
951 throw std::runtime_error(
952 "MIDI device index must be between 0 and 65535");
953 }
954 } else if (option == "--midi-monitor") {
955 options.midi_monitor = true;
956 } else if (option == "--midi-map") {
957 options.midi_map_file = optionValue(index, argc, argv, option);
958 } else if (option == "--midi-cc") {
959 options.midi_cc_mappings.push_back(
960 optionValue(index, argc, argv, option));
961 if (options.midi_cc_mappings.size() >
962 mxvk::VK_Sprite::MAX_CUSTOM_UNIFORMS) {
963 throw std::runtime_error(
964 "too many --midi-cc mappings were supplied");
965 }
966 } else if (option == "--list-midi") {
967 options.list_midi_devices = true;
968 } else if (option == "--check-midi") {
969 options.check_midi = true;
970 } else if (option == "--gpu-filter") {
972 optionValue(index, argc, argv, option), option);
973 if (options.gpu_filter_indices.size() > 256U ||
974 std::any_of(options.gpu_filter_indices.begin(),
975 options.gpu_filter_indices.end(), [](int value) {
976 return value < 0 || value > 65535;
977 })) {
978 throw std::runtime_error(
979 "GPU filter list is outside the supported range");
980 }
981 } else if (option == "--gpu-buffer") {
982 options.gpu_frame_buffer_size =
983 parseInteger(optionValue(index, argc, argv, option), option);
984 options.gpu_buffer_specified = true;
985 if (options.gpu_frame_buffer_size < 4 ||
986 options.gpu_frame_buffer_size > 32) {
987 throw std::runtime_error(
988 "GPU frame buffer must be between 4 and 32");
989 }
990 } else if (option == "-m" || option == "--cuda-device") {
991 options.cuda_device =
992 parseInteger(optionValue(index, argc, argv, option), option);
993 options.cuda_device_specified = true;
994 if (options.cuda_device < 0 || options.cuda_device > 1024) {
995 throw std::runtime_error(
996 "CUDA device index must be between 0 and 1024");
997 }
998 } else if (option == "--list-filters") {
999 options.list_gpu_filters = true;
1000 } else if (option == "--list-cuda-devices") {
1001 options.list_cuda_devices = true;
1002 } else if (option == "--check-cuda") {
1003 options.check_cuda = true;
1004 } else if (option == "--duration") {
1005 options.duration = parseNumber(optionValue(index, argc, argv, option), option);
1006 if (options.duration <= 0.0 || options.duration > 604800.0) {
1007 throw std::runtime_error(
1008 "duration must be between 0 and 604800 seconds");
1009 }
1010 } else if (option == "--max-size") {
1011 options.max_size_mb =
1012 parseNumber(optionValue(index, argc, argv, option), option);
1013 if (options.max_size_mb <= 0.0 ||
1014 options.max_size_mb > 1048576.0) {
1015 throw std::runtime_error(
1016 "maximum output size must be between 0 and 1048576 MB");
1017 }
1018 } else if (option == "--png") {
1019 options.png_output = true;
1020 } else if (option == "--generate") {
1021 options.generate_interval =
1022 parseInteger(optionValue(index, argc, argv, option), option);
1023 if (options.generate_interval <= 0) {
1024 throw std::runtime_error("--generate requires a positive frame interval");
1025 }
1026 } else if (option == "-b" || option == "--encode-crf") {
1027 options.encode_crf =
1028 parseInteger(optionValue(index, argc, argv, option), option);
1029 if (options.encode_crf < 0 || options.encode_crf > 51) {
1030 throw std::runtime_error("encoder CRF must be between 0 and 51");
1031 }
1032 } else if (option == "--bitrate" ||
1033 option == "--video-bitrate" ||
1034 option == "--encode-bitrate") {
1035 options.encode_bitrate = parse_video_bitrate(
1036 optionValue(index, argc, argv, option), option);
1037 } else if (option == "--encode-preset") {
1038 options.encode_preset = optionValue(index, argc, argv, option);
1039 } else if (option == "--encode-tune") {
1040 options.encode_tune = optionValue(index, argc, argv, option);
1041 } else if (option == "--encode-codec") {
1042 options.encode_codec = optionValue(index, argc, argv, option);
1043 } else if (option == "--encode-params") {
1044 options.encode_params = optionValue(index, argc, argv, option);
1045 } else if (option == "--list-encoders") {
1046 options.list_encoders = true;
1047 } else if (option == "--list-encoder-options") {
1048 options.list_encoder_options = optionValue(index, argc, argv, option);
1049 } else if (option == "--encode-realtime") {
1050 options.encode_realtime = true;
1051 } else if (option == "--no-drop") {
1052 options.no_drop = true;
1053 } else if (option == "--display-filter") {
1054 options.display_filter = true;
1055 } else if (option == "--disable-counter") {
1056 options.disable_counter = true;
1057 } else if (option == "--use-watermark") {
1058 options.watermark_text =
1059 optionValue(index, argc, argv, option);
1060 if (options.watermark_text.empty()) {
1061 throw std::runtime_error(
1062 "--use-watermark requires non-empty text");
1063 }
1064 } else if (option == "--use-watermark-color") {
1065 options.watermark_color = parseColor(
1066 optionValue(index, argc, argv, option), option);
1067 } else if (option == "--copy-audio") {
1068 options.copy_audio = true;
1069 } else if (option == "--mute-output") {
1070 options.mute_output = true;
1071 } else if (option == "-n" || option == "--fullscreen") {
1072 options.fullscreen = true;
1073 } else if (option == "-a" || option == "--repeat") {
1074 options.repeat = true;
1075 } else if (option == "--enable-vsync") {
1076 options.enable_vsync = true;
1077 } else if (option == "--enable-screenshot") {
1078 options.enable_screenshot = true;
1079 } else if (option == "--history-test") {
1080 options.history_test = true;
1081 options.enable_texture_cache = true;
1082 } else if (option == "--texture-cache" ||
1083 option == "--texture-cache-array") {
1084 options.enable_texture_cache = true;
1085 } else if (option == "--cache-delay") {
1086 options.cache_delay =
1087 parseInteger(optionValue(index, argc, argv, option), option);
1088 if (options.cache_delay < 0 || options.cache_delay > 1000000) {
1089 throw std::runtime_error(
1090 "--cache-delay must be between 0 and 1000000");
1091 }
1092 } else if (option == "--texture-cache-size") {
1093 options.texture_cache_size =
1094 parseInteger(optionValue(index, argc, argv, option), option);
1095 if (options.texture_cache_size < 1 || options.texture_cache_size > 64) {
1096 throw std::runtime_error(
1097 "--texture-cache-size must be between 1 and 64");
1098 }
1099 } else if (option == "--flip") {
1100 options.flip_output = true;
1101 } else if (option == "--rotate") {
1102 options.frame_rotation =
1103 parseFrameRotation(optionValue(index, argc, argv, option));
1104 } else {
1105 throw std::runtime_error("unknown option: " + std::string(option));
1106 }
1107 }
1108
1109 if (options.use_source_audio) {
1110 if (options.input_file.empty()) {
1111 throw std::runtime_error(
1112 "--use-source-audio requires --input <video>");
1113 }
1114 if (!options.use_source_fps) {
1115 throw std::runtime_error(
1116 "--use-source-audio requires --use-source-fps");
1117 }
1118 if (!options.audio_file.empty()) {
1119 throw std::runtime_error(
1120 "--use-source-audio cannot be combined with --audio-file");
1121 }
1122 options.audio_file = options.input_file;
1123 options.audio_repeat = options.audio_repeat || options.repeat;
1124 }
1125
1126 validateOptionStrings(options);
1127 applyResourceDefaults(options);
1128
1129 if (!options.input_file.empty() && !options.graphic_file.empty()) {
1130 throw std::runtime_error("--input and --graphic cannot be used together");
1131 }
1132 if (!options.dream_layer.empty() && options.dream_model.empty()) {
1133 throw std::runtime_error("--dream-layer requires --dream-model");
1134 }
1135 if ((options.dream_iterations_specified ||
1136 options.dream_strength_specified ||
1139 options.dream_fp16 || options.dream_channel_specified ||
1140 options.dream_octaves_specified ||
1142 options.dream_jitter_specified ||
1143 options.dream_smoothing_specified ||
1144 options.random_dream_specified ||
1145 options.dream_headless ||
1146 options.deep_original ||
1147 options.gpu_filter_before_dream) &&
1148 options.dream_model.empty()) {
1149 throw std::runtime_error(
1150 "Deep Dream processing options require --dream-model");
1151 }
1152 if (options.dream_headless || options.deep_original) {
1153 if (options.dream_headless && !options.headless) {
1154 throw std::runtime_error(
1155 "--dream-headless requires --headless or --silent");
1156 }
1157 if (options.dream_headless && options.deep_original) {
1158 throw std::runtime_error(
1159 "--dream-headless and --deep-orig are mutually exclusive");
1160 }
1161 if (options.input_file.empty()) {
1162 throw std::runtime_error(
1163 "independent-frame Deep Dream mode requires --input <video>");
1164 }
1165 if (options.output_file.empty()) {
1166 throw std::runtime_error(
1167 "independent-frame Deep Dream mode requires --output <file>");
1168 }
1169 if (options.random_dream_specified) {
1170 throw std::runtime_error(
1171 "independent-frame Deep Dream mode cannot be combined with --random-dream");
1172 }
1173 options.dream_feedback = 0.0;
1174 options.dream_zoom = 1.0;
1175 options.dream_rotation = 0.0;
1176 options.no_drop = true;
1177 }
1178 const int shader_source_count =
1179 static_cast<int>(!options.shader_directory.empty()) +
1180 static_cast<int>(!options.fragment_shader.empty()) +
1181 static_cast<int>(!options.compute_shader.empty());
1182 if (shader_source_count > 1) {
1183 throw std::runtime_error(
1184 "--shaders, --fragment, and --compute are mutually exclusive");
1185 }
1186 if (!options.custom_uniform_overrides.empty() &&
1187 options.shader_directory.empty()) {
1188 throw std::runtime_error("--uniform requires --shaders <directory>");
1189 }
1190 if (!options.midi_cc_mappings.empty() &&
1191 options.shader_directory.empty()) {
1192 throw std::runtime_error("--midi-cc requires --shaders <directory>");
1193 }
1194 if (options.gpu_buffer_specified && options.gpu_filter_indices.empty()) {
1195 throw std::runtime_error("--gpu-buffer requires --gpu-filter <list>");
1196 }
1197 if (options.gpu_filter_before_dream &&
1198 options.gpu_filter_indices.empty()) {
1199 throw std::runtime_error(
1200 "--gpu-filter-before-dream requires --gpu-filter <list>");
1201 }
1202 if (options.gpu_filter_before_dream && options.maximize_fps) {
1203 throw std::runtime_error(
1204 "--gpu-filter-before-dream cannot be combined with --maximize-fps");
1205 }
1206 if (options.gpu_filter_before_dream && !options.graphic_file.empty()) {
1207 throw std::runtime_error(
1208 "--gpu-filter-before-dream currently supports camera and video input");
1209 }
1210 if (options.gpu_filter_before_dream &&
1211 (!options.edge_model.empty() || !options.human_model.empty() ||
1212 !options.onnx_configuration.empty())) {
1213 throw std::runtime_error(
1214 "--gpu-filter-before-dream cannot be combined with DNN input effects");
1215 }
1216 if ((!options.shader_pass_indices.empty() || !options.shader_pass_files.empty() ||
1217 !options.playlist_file.empty() || options.enable_playlist) &&
1218 options.shader_directory.empty()) {
1219 throw std::runtime_error(
1220 "shader passes and playlists require --shaders <directory>");
1221 }
1222 if (options.enable_playlist && options.playlist_file.empty()) {
1223 throw std::runtime_error("--enable-playlist requires --playlist <file>");
1224 }
1225 if ((options.autopilot_frames > 0 || options.autopilot_random_timeout > 0) &&
1226 options.playlist_file.empty()) {
1227 throw std::runtime_error("autopilot requires --playlist <file>");
1228 }
1229 if (!options.output_file.empty() && !options.input_file.empty() &&
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");
1233 }
1234 if (options.duration > 0.0 && options.output_file.empty()) {
1235 throw std::runtime_error("--duration requires --output <file>");
1236 }
1237 if (options.png_output &&
1238 (options.input_file.empty() || options.output_file.empty())) {
1239 throw std::runtime_error("--png requires video --input and --output");
1240 }
1241 if (options.max_size_mb > 0.0 &&
1242 (options.output_file.empty() || options.png_output)) {
1243 throw std::runtime_error("--max-size requires encoded video output");
1244 }
1245 if (options.copy_audio &&
1246 (options.input_file.empty() || options.output_file.empty() ||
1247 options.png_output || options.repeat)) {
1248 throw std::runtime_error(
1249 "--copy-audio requires non-repeating video input and encoded output");
1250 }
1251 if (options.copy_audio && !options.audio_file.empty()) {
1252 throw std::runtime_error(
1253 "--copy-audio and --audio-file select different audio sources");
1254 }
1255 if (!options.record_audio_file.empty() && !options.audio_file.empty()) {
1256 throw std::runtime_error(
1257 "--record-audio records live input and cannot be used with --audio-file");
1258 }
1259 if (!options.record_audio_file.empty()) {
1260 const fs::path recording_path =
1261 fs::absolute(options.record_audio_file).lexically_normal();
1262 const auto conflicts_with = [&](const std::string &filename) {
1263 return !filename.empty() &&
1264 recording_path == fs::absolute(filename).lexically_normal();
1265 };
1266 if (conflicts_with(options.input_file) ||
1267 conflicts_with(options.graphic_file) ||
1268 conflicts_with(options.output_file)) {
1269 throw std::runtime_error(
1270 "--record-audio output must differ from media input and video output");
1271 }
1272 }
1273 if (!options.graphic_file.empty() && !options.output_file.empty() &&
1274 options.duration <= 0.0 &&
1275 !(options.audio_trunc && !options.audio_file.empty())) {
1276 throw std::runtime_error(
1277 "graphic recording requires --duration <seconds> or "
1278 "--audio-file <media> --audio-trunc");
1279 }
1280 if (options.headless && !isUtilityRequest(options)) {
1281 if (options.input_file.empty() && options.graphic_file.empty()) {
1282 throw std::runtime_error(
1283 "--headless/--silent requires --input <video> or "
1284 "--graphic <image>; camera input is not supported");
1285 }
1286 if (options.output_file.empty()) {
1287 throw std::runtime_error(
1288 "--headless/--silent requires --output <file>");
1289 }
1290 if (!options.graphic_file.empty() && options.duration <= 0.0) {
1291 throw std::runtime_error(
1292 "headless graphic processing requires --duration "
1293 "<seconds>");
1294 }
1295 if (options.repeat && options.duration <= 0.0) {
1296 throw std::runtime_error(
1297 "--headless/--silent with --repeat requires --duration "
1298 "<seconds>");
1299 }
1300 if (options.fullscreen || options.enable_vsync ||
1301 options.enable_screenshot) {
1302 throw std::runtime_error(
1303 "--headless/--silent cannot be combined with "
1304 "--fullscreen, --enable-vsync, or --enable-screenshot");
1305 }
1306 }
1307 if (options.audio_buffers > 0 && !options.enable_audio) {
1308 throw std::runtime_error(
1309 "--enable-audio-buffers requires --enable-audio");
1310 }
1311 if (options.audio_warm_rate_specified && !options.enable_audio) {
1312 throw std::runtime_error(
1313 "--audio-warm-rate requires an enabled audio source");
1314 }
1315 if (!options.audio_file.empty() && options.audio_input_specified) {
1316 throw std::runtime_error(
1317 "--audio-file and --audio-input cannot be used together");
1318 }
1319 if (options.audio_repeat && options.audio_file.empty()) {
1320 throw std::runtime_error(
1321 "--audio-repeat requires --audio-file <media>");
1322 }
1323 if (options.audio_trunc && options.audio_file.empty()) {
1324 throw std::runtime_error(
1325 "--audio-trunc requires --audio-file <media>");
1326 }
1327 if (options.audio_output_specified && !options.audio_pass_through) {
1328 throw std::runtime_error(
1329 "--audio-output requires --pass-through");
1330 }
1332 !options.audio_pass_through) {
1333 throw std::runtime_error(
1334 "--pass-through-gain requires --pass-through");
1335 }
1336 if (options.audio_recording_gain_specified &&
1337 (!options.enable_audio || !options.audio_file.empty() ||
1338 (options.record_audio_file.empty() &&
1339 (options.output_file.empty() || options.png_output ||
1340 options.copy_audio || options.mute_output)))) {
1341 throw std::runtime_error(
1342 "--record-gain requires live audio recording or encoded output");
1343 }
1344 if (options.maximize_fps) {
1345 if (!options.input_file.empty() || !options.graphic_file.empty()) {
1346 throw std::runtime_error(
1347 "--maximize-fps is available only for camera input");
1348 }
1349 if (options.requested_fps <= 0.0) {
1350 throw std::runtime_error(
1351 "--maximize-fps requires a target set with --fps");
1352 }
1353 }
1354 if (options.use_source_fps) {
1355 if (options.input_file.empty()) {
1356 throw std::runtime_error(
1357 "--use-source-fps requires --input <video>");
1358 }
1359 if (options.requested_fps > 0.0) {
1360 throw std::runtime_error(
1361 "--use-source-fps cannot be combined with --fps");
1362 }
1363 }
1364 if ((options.human_background || options.human_black_specified ||
1365 options.human_white_specified) &&
1366 options.human_model.empty()) {
1367 throw std::runtime_error(
1368 "--background, --black, and --white require --human <model.onnx>");
1369 }
1370 if (!options.human_model.empty() &&
1371 options.human_black_point >= options.human_white_point) {
1372 throw std::runtime_error(
1373 "--black must be less than --white for human segmentation");
1374 }
1375 if (options.human_background && options.enable_3d) {
1376 throw std::runtime_error(
1377 "--background is currently available only in 2D mode");
1378 }
1379 return options;
1380 }
1381
1382 void printHelp(std::ostream &output) {
1383 output << "ACMXVK v" << ACMXVK_VERSION_INFO
1384 << " - Vulkan video shader engine\n\n"
1385 << "Usage:\n"
1386 << " acmxvk -i video.mp4 -s shader-directory [options]\n"
1387 << " acmxvk -g image.png -f shader.spv [options]\n"
1388 << " acmxvk -d 0 -s shader-directory [options]\n\n"
1389 << "Resources:\n"
1390 << " -p, --path <directory> Assets root containing data/, shaders/,\n"
1391 << " playlists/, and midi-examples/\n"
1392 << " ACMXVK_PATH Default resource root when --path is absent\n"
1393 << " ACMXVK_SHADER_PATH Default SPIR-V library when shader input is absent\n"
1394 << " ACMX2_PATH is accepted as a data fallback\n\n"
1395 << "Input:\n"
1396 << " -i, --input <file> Read a video file\n"
1397 << " -g, --graphic <file> Read a still image\n"
1398 << " -d, --device <index> Camera device (default 0)\n"
1399 << " -c, --camera-res <WxH> Requested camera dimensions\n"
1400 << " --enumerate-device N List native camera modes and exit\n"
1401 << " --probe-camera N Alias for --enumerate-device\n"
1402 << " --list-camera-devices List native camera indices and names\n"
1403 << " --use-yuv Prefer YUYV camera capture over MJPG\n"
1404 << " --maximize-fps Render at --fps using the latest camera frame\n"
1405 << " --use-source-fps Play video on its reported source clock\n"
1406 << " --use-source-audio Use the video's audio for shader reactivity\n"
1407 << " --probe-hdr <video> Print HDR/color metadata and exit\n"
1408 << " -u, --fps <rate> Camera/output FPS\n"
1409 << " Video files prefer FFmpeg/NVDEC capture\n\n"
1410 << "DNN effects (requires WITH_OPENCV_DNN=ON build):\n"
1411 << " --edge <model.onnx> Replace input with a DexiNed edge map\n"
1412 << " --human <model.onnx> Isolate a person with PP-HumanSeg\n"
1413 << " --onnx <config.yaml> Run a YAML-configured image ONNX model\n"
1414 << " --background Apply shaders only behind the person (2D)\n"
1415 << " --black <0.0-1.0> Alpha black point (default 0.35)\n"
1416 << " --white <0.0-1.0> Alpha white point (default 0.75)\n"
1417 << " --check-dnn Report compiled OpenCV DNN support\n"
1418 << " Backend is benchmarked on the first frame\n\n"
1419 << "Deep Dream (requires WITH_DEEP_DREAM=ON build):\n"
1420 << " --check-deep-dream Probe LibTorch CPU/CUDA autograd support\n"
1421 << " --dream-model <file.pt> Apply a TorchScript feature model\n"
1422 << " --dream-layer <name|N> Select a named or numbered feature layer\n"
1423 << " --dream-iterations <N> Number of ascent steps per input frame (1-100; default 1)\n"
1424 << " --dream-strength <N> Gradient step size (0-10; default 0.05)\n"
1425 << " --dream-feedback <N> Previous dreamed-frame blend (0-0.99; default 0.9)\n"
1426 << " --dream-zoom <N> Feedback zoom per source frame (0.9-1.1; default 1.01)\n"
1427 << " --dream-rotation <N> Feedback rotation degrees per frame (-5 to 5; default 0.1)\n"
1428 << " --dream-size <N> Maximum neural working dimension (default 512; 0=native)\n"
1429 << " --dream-fp16 Use FP16 model and tensors on CUDA\n"
1430 << " --dream-channel <N|all> Target one feature channel (default: all)\n"
1431 << " --dream-octaves <N> Progressive dream scales (1-8; default 1)\n"
1432 << " --dream-octave-scale <N> Scale ratio between octaves (1.1-3; default 1.4)\n"
1433 << " --dream-jitter <N> Spatial gradient jitter in pixels (0-64; default 0)\n"
1434 << " --dream-smoothing <N> Gradient smoothing radius (0-16; default 0)\n"
1435 << " --random-dream <seconds>\n"
1436 << " Randomize safe dream controls at a media-time interval\n"
1437 << " --dream-headless Offline per-frame video dreaming without temporal zoom\n"
1438 << " --deep-orig Same independent-frame mode with a preview window\n"
1439 << " --gpu-filter-before-dream\n"
1440 << " Run acidcam-gpu before Deep Dream\n"
1441 << " Deep Dream runs before the Vulkan shader chain\n\n"
1442 << "Shaders:\n"
1443 << " --build <library.json> Compile a source shader library and exit\n"
1444 << " --builddir <directory> Output directory required by --build\n"
1445 << " --fix <directory> Continue and omit/remove failed shaders\n"
1446 << " --prune Delete GLSL sources that fail compilation\n"
1447 << " --force Confirm permanent deletion by --prune\n"
1448 << " --glslc <executable> GLSL compiler for --build (default: glslc)\n"
1449 << " --parallel <jobs> Concurrent shader jobs for --build (default: 1)\n"
1450 << " -s, --shaders <directory> SPIR-V library with library.json or index.txt\n"
1451 << " -f, --fragment <file.spv> Use one SPIR-V fragment shader\n"
1452 << " --compute <file.spv> Use one SPIR-V compute shader\n"
1453 << " -H, --shader-index <index> Initial library shader index\n"
1454 << " --shader-file <name> Initial library shader filename\n"
1455 << " --uniform <name=value> Override a library.json custom float\n\n"
1456 << "3D model:\n"
1457 << " --enable-3d Map input frames onto a 3D model\n"
1458 << " --model <file> OBJ, MXMOD, or compressed MXMOD model\n"
1459 << " Defaults to the bundled cube.obj\n\n"
1460 << " --shader-pass <indices> Mixed fragment/compute pre-pass chain\n"
1461 << " --shader-pass-files <data> ACMX2 length-prefixed shader filenames\n"
1462 << " --playlist <file> Shader or named multipass playlist\n\n"
1463 << " --cross-fade <seconds> Shader transition duration (default 0.5)\n\n"
1464 << " --enable-playlist Enable the playlist immediately\n"
1465 << " --time-speed <mult> Scale shader time (default 1.0)\n"
1466 << " --normalized Use fixed frame time outside video mode\n"
1467 << " --autopilot-frames <N> Playlist switch interval (minimum 4)\n"
1468 << " Uses decoded frames for video input\n"
1469 << " --autopilot-timeout <N> Alias for --autopilot-frames\n"
1470 << " --autopilot-random <N> Random playlist interval from 4..N\n\n"
1471 << "History cache:\n"
1472 << " --texture-cache Enable Vulkan texture history\n"
1473 << " --texture-cache-array Alias using sampler2DArray history\n"
1474 << " --texture-cache-size N History layers, 1-64 (default 8)\n"
1475 << " --cache-delay N Skip N frames between cache writes\n"
1476 << " --history-test Enable history and the built-in echo demo\n\n"
1477 << "Recording:\n"
1478 << " -o, --output <file> Encode processed output with MXWrite\n"
1479 << " --duration <seconds> Stop after this much output video\n"
1480 << " --max-size <MB> Stop when encoded output exceeds this size\n"
1481 << " --png Write video output as a PNG sequence\n"
1482 << " --generate <N> Save a PNG every N processed frames\n"
1483 << " -e, --prefix <directory> Directory for Z snapshots (default .)\n"
1484 << " -b, --encode-crf <0-51> Encoder quality (default 18)\n"
1485 << " --bitrate <rate> Target VBR, e.g. 10M (disables CRF/CQ)\n"
1486 << " --video-bitrate <rate> Alias for --bitrate\n"
1487 << " --encode-preset <name> Encoder speed/quality preset\n"
1488 << " --encode-tune <name> Encoder content/latency tuning\n"
1489 << " --encode-codec <name> auto, software, nvenc, or exact encoder\n"
1490 << " --encode-params <text> Additional FFmpeg encoder options\n"
1491 << " --list-encoders List available video encoders and exit\n"
1492 << " --list-encoder-options <name>\n"
1493 << " List one encoder's options and exit\n"
1494 << " --encode-realtime Enable low-latency encoder settings\n"
1495 << " --no-drop Block when the encoder queue is full\n"
1496 << " --display-filter Show active shader/filter details\n"
1497 << " --disable-counter Hide the shader/timer/FPS HUD at startup\n"
1498 << " --use-watermark <text> Show a text watermark in the upper-left\n"
1499 << " and hide the preview HUD by default\n"
1500 << " --use-watermark-color <r,g,b>\n"
1501 << " Watermark RGB color (default 255,0,150)\n"
1502 << " --copy-audio Copy input audio into encoded output\n"
1503 << " --mute-output Keep recorded video audio-free\n\n"
1504 << "Audio (requires AUDIO=ON build):\n"
1505 << " -w, --enable-audio Enable live audio-reactive metrics\n"
1506 << " -l, --channels <N> Capture channels (default 2)\n"
1507 << " -q, --sense <0.1-5.0> Audio sensitivity (default 1.0)\n"
1508 << " --audio-warm-rate N Shader ramp per second (default 0.5; 0 off)\n"
1509 << " --audio-input <device> Input index or default\n"
1510 << " --audio-file <media> Media file or M3U/M3U8 reactivity source\n"
1511 << " -y, --pass-through Play live/file audio through an output device\n"
1512 << " --audio-output <device> Output index or default\n"
1513 << " --pass-through-gain N Monitor gain, 0.0-4.0 (default 1.0)\n"
1514 << " --record-gain N Saved/muxed mic gain, 0.0-2.0 (default 1.0)\n"
1515 << " --record-audio <wav> Record live microphone input as PCM16 WAV\n"
1516 << " --audio-repeat Restart file audio at end-of-stream\n"
1517 << " --audio-trunc Stop ACMXVK when file audio finishes\n"
1518 << " Live/file audio is muxed unless --mute-output\n"
1519 << " --enable-audio-buffers N\n"
1520 << " FFT history layers at binding 4\n"
1521 << " --list-devices List RtAudio devices and exit\n"
1522 << " --check-audio Report compiled audio support\n"
1523 << " Provides a 256-bin FFT at binding 3\n\n"
1524 << "MIDI (requires MIDI=ON build):\n"
1525 << " --midi-device <index> Open a MIDI input port (default 0)\n"
1526 << " --midi-monitor Print received MIDI messages\n"
1527 << " --midi-map <file> Load an ACMX2 .midi_cfg mapping\n"
1528 << " --midi-cc <map> Map [channel:]CC to a custom uniform\n"
1529 << " --list-midi List MIDI input ports and exit\n"
1530 << " --check-midi Report compiled MIDI support\n"
1531 << " Paired knobs repeat by distance from 64\n\n"
1532 << "CUDA and filters:\n"
1533 << " --gpu-filter <list> Comma-separated acidcam-gpu indices\n"
1534 << " --gpu-buffer <4-32> Temporal frame count (default 10)\n"
1535 << " -m, --cuda-device <index> Select NVDEC/filter device (default 0)\n"
1536 << " --list-filters List acidcam-gpu filters and exit\n"
1537 << " --list-cuda-devices List MXVK CUDA devices and exit\n"
1538 << " --check-cuda Report interop and filter support\n"
1539 << " Left/Right selects the active filter\n"
1540 << " NVDEC interop follows the MXVK build\n"
1541 << " Filters require WITH_CUDA=ON\n"
1542 << " Video/camera RGBA and rotation stay on GPU\n\n"
1543 << "Window:\n"
1544 << " -r, --resolution <WxH> Render/output resolution override\n"
1545 << " Preview fits display without changing output size\n"
1546 << " -n, --fullscreen Start fullscreen\n"
1547 << " -a, --repeat Repeat video input\n"
1548 << " --rotate <mode> clockwise, 180, or counterclockwise\n"
1549 << " --flip Flip final display/encoded output vertically\n"
1550 << " --enable-vsync Use FIFO presentation\n"
1551 << " --enable-screenshot Enable MXVK F10 screenshots\n\n"
1552 << "Headless processing:\n"
1553 << " --headless Surface-free terminal/batch rendering\n"
1554 << " --silent Alias for --headless\n"
1555 << " Requires video/image input and --output\n"
1556 << " Image input and --repeat require --duration\n\n"
1557 << "Output:\n"
1558 << " --unbuffered Flush stdout/stderr after each write for GUI capture\n"
1559 << " --interface-shm Accept live shader selection from the ACMX interface\n\n"
1560 << "Keys: Up/Down shader or playlist node, Shift+Up/Down post-shader,\n"
1561 << " P playlist/pause, L freeze, T time, U/I step time,\n"
1562 << " Page Up/Down time speed, Q audio time, Home audio delta,\n"
1563 << " Insert/Delete audio sensitivity, End FFT sensitivity,\n"
1564 << " 3 toggle 2D/3D, V 3D view rotation, O 3D oscillation,\n"
1565 << " C 3D wave,\n"
1566 << " X reset skybox view,\n"
1567 << " E watermark, F fullscreen, F9 runtime HUD, K shader lock,\n"
1568 << " M multipass,\n"
1569 << " J random autopilot, Y sequential autopilot, N random crossfade,\n"
1570 << " [/] crossfade effect, Space bypass,\n"
1571 << " W/A/S/D 3D look, +/- 3D zoom, Shift+/- 3D scale,\n"
1572 << " 1/2 zoom sensitivity,\n"
1573 << " Z PNG, 4 TIFF (TIFF=ON), 5 WebP (WEBP=ON),\n"
1574 << " 6 raw RGBA snapshot,\n"
1575 << " mouse drag/wheel 3D look/move,\n"
1576 << " Escape quit\n";
1577 }
1578
1579 [[nodiscard]] std::string cleanEncoderField(std::string value) {
1580 std::replace_if(value.begin(), value.end(), [](char character) { return character == '\t' || character == '\r' || character == '\n'; }, ' ');
1581 return value;
1582 }
1583
1584 void printEncoders(std::ostream &output) {
1585 output << "MXWRITE_ENCODERS\t1\n";
1586 for (const EncoderInfo &encoder : available_video_encoders()) {
1587 output << "ENCODER\t" << cleanEncoderField(encoder.name) << '\t'
1588 << cleanEncoderField(encoder.long_name) << '\t'
1589 << cleanEncoderField(encoder.codec_name) << '\t'
1590 << (encoder.hardware ? "hardware" : "software") << '\t'
1591 << (encoder.experimental ? "experimental" : "stable") << '\t'
1592 << cleanEncoderField(encoder.pixel_formats) << '\n';
1593 }
1594 }
1595
1596 [[nodiscard]] bool printEncoderOptions(std::string_view encoder_name,
1597 std::ostream &output,
1598 std::ostream &error_output) {
1599 const std::string name(encoder_name);
1600 const std::vector<EncoderOptionInfo> options = video_encoder_options(name);
1601 if (options.empty() && !avcodec_find_encoder_by_name(name.c_str())) {
1602 error_output << "acmxvk: encoder not found: " << name << '\n';
1603 return false;
1604 }
1605
1606 output << "MXWRITE_ENCODER_OPTIONS\t1\t" << cleanEncoderField(name) << '\n';
1607 for (const EncoderOptionInfo &option : options) {
1608 output << "OPTION\t" << cleanEncoderField(option.name) << '\t'
1609 << cleanEncoderField(option.type) << '\t'
1610 << cleanEncoderField(option.default_value) << '\t'
1611 << cleanEncoderField(option.minimum) << '\t'
1612 << cleanEncoderField(option.maximum) << '\t'
1613 << cleanEncoderField(option.choices) << '\t'
1614 << cleanEncoderField(option.help) << '\n';
1615 }
1616 return true;
1617 }
1618
1619 [[nodiscard]] std::string trim(std::string text) {
1620 const auto first = std::find_if_not(text.begin(), text.end(), [](unsigned char value) {
1621 return std::isspace(value) != 0;
1622 });
1623 const auto last = std::find_if_not(text.rbegin(), text.rend(), [](unsigned char value) {
1624 return std::isspace(value) != 0;
1625 }).base();
1626 if (first >= last) {
1627 return {};
1628 }
1629 return std::string(first, last);
1630 }
1631} // namespace acmxvk
GLsizei GLsizei * length
#define ACMXVK_VERSION_INFO
std::int64_t parse_video_bitrate(std::string value, std::string_view option)
Definition options.cpp:22
constexpr std::int64_t MAX_FRAME_PIXELS
Definition options.cpp:20
constexpr std::size_t MAX_SHADER_ENTRIES
void validate_string(std::string_view value, StringKind kind, std::string_view context, bool allow_empty)
std::string cleanEncoderField(std::string value)
Definition options.cpp:1579
FrameRotation parseFrameRotation(std::string value)
Definition options.cpp:270
void printEncoders(std::ostream &output)
Definition options.cpp:1584
void printHelp(std::ostream &output)
Definition options.cpp:1382
std::string trim(std::string text)
Definition options.cpp:1619
std::vector< fs::path > resource_directories(const Options &options)
void applyResourceDefaults(Options &options)
Definition options.cpp:301
int parseInteger(std::string_view text, std::string_view option)
Definition options.cpp:168
bool dimensions_supported(int width, int height)
Definition options.cpp:63
void validateLocator(std::string_view value, std::string_view context, bool allow_empty=false)
Definition options.cpp:85
bool printEncoderOptions(std::string_view encoder_name, std::ostream &output, std::ostream &error_output)
Definition options.cpp:1596
FrameRotation
Definition options.hpp:35
std::vector< int > parseIntegerList(std::string_view text, std::string_view option)
Definition options.cpp:205
Options parseOptions(int argc, char **argv)
Definition options.cpp:382
bool isUtilityRequest(const Options &options)
Definition options.cpp:289
double parseNumber(std::string_view text, std::string_view option)
Definition options.cpp:186
bool hasShaderManifest(const fs::path &directory)
Definition options.cpp:69
std::string optionValue(int &index, int argc, char **argv, std::string_view option)
Definition options.cpp:74
void validateOptionStrings(const Options &options)
Definition options.cpp:97
void parseDimensions(std::string_view text, int &width, int &height, std::string_view option)
Definition options.cpp:253
std::array< std::uint8_t, 3 > parseColor(std::string_view text, std::string_view option)
Definition options.cpp:235
std::string encode_params
Definition options.hpp:180
bool gpu_buffer_specified
Definition options.hpp:123
int audio_output_device
Definition options.hpp:58
std::string edge_model
Definition options.hpp:186
std::string snapshot_directory
Definition options.hpp:191
std::string shader_file
Definition options.hpp:170
double max_size_mb
Definition options.hpp:73
bool dream_zoom_specified
Definition options.hpp:133
std::int64_t encode_bitrate
Definition options.hpp:49
bool unbuffered_output
Definition options.hpp:156
bool midi_device_specified
Definition options.hpp:119
std::string glslc_executable
Definition options.hpp:173
double audio_recording_gain
Definition options.hpp:77
std::vector< int > shader_pass_indices
Definition options.hpp:160
bool dream_octave_scale_specified
Definition options.hpp:139
double dream_octave_scale
Definition options.hpp:84
std::string encode_codec
Definition options.hpp:179
std::string dream_layer
Definition options.hpp:190
bool cuda_device_specified
Definition options.hpp:124
bool use_source_audio
Definition options.hpp:90
double time_speed
Definition options.hpp:72
double random_dream_interval
Definition options.hpp:85
std::string build_manifest
Definition options.hpp:171
bool gpu_filter_before_dream
Definition options.hpp:145
std::string dream_model
Definition options.hpp:189
bool dream_rotation_specified
Definition options.hpp:134
double audio_pass_through_gain
Definition options.hpp:76
std::string build_directory
Definition options.hpp:172
double dream_rotation
Definition options.hpp:83
std::vector< std::string > custom_uniform_overrides
Definition options.hpp:162
bool dream_feedback_specified
Definition options.hpp:132
std::string fragment_shader
Definition options.hpp:168
std::vector< std::string > shader_pass_files
Definition options.hpp:161
double duration
Definition options.hpp:70
bool random_dream_specified
Definition options.hpp:142
std::string audio_file
Definition options.hpp:183
bool audio_recording_gain_specified
Definition options.hpp:112
bool audio_input_specified
Definition options.hpp:108
std::string resource_directory
Definition options.hpp:192
bool human_background
Definition options.hpp:146
FrameRotation frame_rotation
Definition options.hpp:159
std::string output_file
Definition options.hpp:176
int texture_cache_size
Definition options.hpp:55
bool list_camera_devices
Definition options.hpp:117
std::string midi_map_file
Definition options.hpp:185
std::string model_file
Definition options.hpp:174
bool enable_playlist
Definition options.hpp:96
std::vector< int > gpu_filter_indices
Definition options.hpp:164
std::string watermark_text
Definition options.hpp:193
double dream_feedback
Definition options.hpp:81
std::vector< std::string > midi_cc_mappings
Definition options.hpp:163
std::string graphic_file
Definition options.hpp:166
double dream_strength
Definition options.hpp:80
int audio_input_device
Definition options.hpp:57
bool audio_warm_rate_specified
Definition options.hpp:109
double audio_warm_rate
Definition options.hpp:75
bool list_cuda_devices
Definition options.hpp:126
std::string shader_directory
Definition options.hpp:167
std::string onnx_configuration
Definition options.hpp:188
std::string encode_tune
Definition options.hpp:178
std::string record_audio_file
Definition options.hpp:184
std::string human_model
Definition options.hpp:187
int gpu_frame_buffer_size
Definition options.hpp:61
bool list_audio_devices
Definition options.hpp:116
std::string playlist_file
Definition options.hpp:175
bool dream_channel_specified
Definition options.hpp:137
double dream_zoom
Definition options.hpp:82
bool dream_smoothing_specified
Definition options.hpp:141
double requested_fps
Definition options.hpp:69
bool dream_jitter_specified
Definition options.hpp:140
int enumerate_camera_device
Definition options.hpp:46
std::array< std::uint8_t, 3 > watermark_color
Definition options.hpp:194
bool enable_texture_cache
Definition options.hpp:97
bool check_deep_dream
Definition options.hpp:129
std::string list_encoder_options
Definition options.hpp:181
std::string encode_preset
Definition options.hpp:177
bool audio_output_specified
Definition options.hpp:110
std::string input_file
Definition options.hpp:165
bool dream_size_specified
Definition options.hpp:135
bool list_midi_devices
Definition options.hpp:121
double cross_fade_duration
Definition options.hpp:71
bool dream_octaves_specified
Definition options.hpp:138
bool dream_iterations_specified
Definition options.hpp:130
bool resolution_specified
Definition options.hpp:86
bool audio_pass_through
Definition options.hpp:113
bool enable_screenshot
Definition options.hpp:95
bool build_parallel_specified
Definition options.hpp:155
std::string probe_hdr_file
Definition options.hpp:182
double human_black_point
Definition options.hpp:78
bool audio_pass_through_gain_specified
Definition options.hpp:111
double human_white_point
Definition options.hpp:79
bool use_source_fps
Definition options.hpp:89
int autopilot_random_timeout
Definition options.hpp:51
bool human_white_specified
Definition options.hpp:148
int generate_interval
Definition options.hpp:52
bool list_gpu_filters
Definition options.hpp:125
bool human_black_specified
Definition options.hpp:147
std::string compute_shader
Definition options.hpp:169
double audio_sensitivity
Definition options.hpp:74
bool dream_strength_specified
Definition options.hpp:131