12 constexpr float M1 = 2610.0F / 16384.0F;
13 constexpr float M2 = 2523.0F / 32.0F;
14 constexpr float C1 = 3424.0F / 4096.0F;
15 constexpr float C2 = 2413.0F / 128.0F;
16 constexpr float C3 = 2392.0F / 128.0F;
17 const float power_value =
18 std::pow(std::clamp(encoded, 0.0F, 1.0F), 1.0F / M2);
19 const float numerator = std::max(power_value - C1, 0.0F);
20 const float denominator =
21 std::max(C2 - C3 * power_value, 1.0e-6F);
22 return std::pow(numerator / denominator, 1.0F / M1);
26 constexpr float A = 0.17883277F;
27 constexpr float B = 0.28466892F;
28 constexpr float C = 0.55991073F;
29 encoded = std::max(encoded, 0.0F);
30 if (encoded <= 0.5F) {
31 return encoded * encoded / 3.0F;
33 return (std::exp((encoded - C) / A) + B) / 12.0F;
39 (rgba.type() != CV_8UC4 && rgba.type() != CV_16UC4)) {
42 cv::Mat linear(rgba.rows, rgba.cols, CV_16UC4);
43 const float scale = rgba.type() == CV_16UC4
46 for (
int row = 0; row < rgba.rows; ++row) {
47 auto *destination = linear.ptr<std::uint16_t>(row);
48 for (
int column = 0; column < rgba.cols; ++column) {
49 for (
int channel = 0; channel < 3; ++channel) {
50 const std::size_t offset =
51 static_cast<std::size_t
>(column) * 4U +
52 static_cast<std::size_t
>(channel);
54 rgba.type() == CV_16UC4
56 rgba.ptr<std::uint16_t>(row)[offset]) *
59 rgba.ptr<std::uint8_t>(row)[offset]) *
63 destination[offset] =
static_cast<std::uint16_t
>(
64 std::lround(std::clamp(decoded, 0.0F, 1.0F) *
67 const std::size_t alpha_offset =
68 static_cast<std::size_t
>(column) * 4U + 3U;
69 destination[alpha_offset] =
70 rgba.type() == CV_16UC4
71 ? rgba.ptr<std::uint16_t>(row)[alpha_offset]
72 :
static_cast<std::uint16_t
>(
73 rgba.ptr<std::uint8_t>(row)[alpha_offset] *
81 if (rgba.empty() || rgba.type() != CV_16UC4) {
85 rgba.convertTo(converted, CV_8UC4, 1.0 / 257.0);
90 value = std::max(value, 0.0F);
92 (value * (2.51F * value + 0.03F)) /
93 (value * (2.43F * value + 0.59F) + 0.14F),
98 value = std::clamp(value, 0.0F, 1.0F);
99 return value <= 0.0031308F
101 : 1.055F * std::pow(value, 1.0F / 2.4F) - 0.055F;
105 const std::vector<std::uint16_t> &rgba,
bool hlg) {
106 std::vector<std::uint8_t> converted(rgba.size());
107 const float reference_scale =
108 hlg ? 1000.0F / 203.0F : 10000.0F / 203.0F;
109 for (std::size_t offset = 0; offset + 3U < rgba.size();
111 const auto decode = [hlg](std::uint16_t sample) {
112 const float encoded =
113 static_cast<float>(sample) / 65535.0F;
116 const float red = decode(rgba[offset]) * reference_scale;
118 decode(rgba[offset + 1U]) * reference_scale;
120 decode(rgba[offset + 2U]) * reference_scale;
121 const float bt709_red =
122 1.660491F * red - 0.587641F * green - 0.072850F * blue;
123 const float bt709_green =
124 -0.124550F * red + 1.132900F * green - 0.008349F * blue;
125 const float bt709_blue =
126 -0.018151F * red - 0.100579F * green + 1.118730F * blue;
127 converted[offset] =
static_cast<std::uint8_t
>(std::lround(
129 converted[offset + 1U] =
static_cast<std::uint8_t
>(
132 converted[offset + 2U] =
static_cast<std::uint8_t
>(
135 converted[offset + 3U] =
static_cast<std::uint8_t
>(
136 (
static_cast<std::uint32_t
>(rgba[offset + 3U]) + 128U) /
144 HEADLESS_SHUTDOWN_REQUESTED = 1;
150 options.fullscreen, MXVK_VALIDATION,
152 ? PresentModePreference::Vsync
153 : PresentModePreference::LowLatency,
154 options.headless ? RuntimeMode::Headless
155 : RuntimeMode::Windowed),
158 std::cout <<
"acmxvk: headless mode enabled: surface-free Vulkan "
159 "rendering without an SDL window\n";
161 setClearColor(0.0F, 0.0F, 0.0F, 1.0F);
188 flushFrameReadbacks();
189 }
catch (
const std::exception &error) {
190 std::cerr <<
"acmxvk: unable to flush pending frame readbacks: "
191 << error.what() <<
'\n';
197 vkDeviceWaitIdle(getDevice());
200 std::cout <<
"acmxvk: released 3D model resources\n";
202 const bool should_copy_audio =
205 const bool should_mux_file_audio =
209 const bool should_mux_live_audio =
215 const bool should_write_live_audio =
237 <<
" periodic PNG frames\n";
242#ifdef MXVK_WITH_FFMPEG_CAPTURE
243 if (ffmpeg_capture.is_open()) {
244 ffmpeg_capture.close();
247 if (should_copy_audio) {
253 if (should_mux_file_audio) {
254 const double video_duration =
writer.get_duration();
257 std::cerr <<
"acmxvk: file-audio mux failed; preserving the "
258 "encoded video without audio\n";
261 if (should_write_live_audio) {
262 if (live_audio_recording.
empty()) {
263 std::cerr <<
"acmxvk: standalone audio recording was empty; "
264 "no WAV file was written\n";
267 std::cerr <<
"acmxvk: could not write WAV recording: "
270 std::cout <<
"acmxvk: wrote "
272 <<
" seconds of microphone audio to "
276 if (should_mux_live_audio) {
277 const double video_duration =
writer.get_duration();
278 if (live_audio_recording.
empty()) {
279 std::cerr <<
"acmxvk: live audio recording was empty; preserving "
280 "the encoded video without audio\n";
282 std::move(live_audio_recording.
samples),
285 std::cerr <<
"acmxvk: live-audio mux failed; preserving the "
286 "encoded video without audio\n";
294 mxvk::VK_Window::event(
event);
295 if (
event.type == SDL_EVENT_KEY_DOWN &&
296 event.key.key == SDLK_PAGEUP) {
298 }
else if (
event.type == SDL_EVENT_KEY_DOWN &&
299 event.key.key == SDLK_PAGEDOWN) {
303 if (
event.type == SDL_EVENT_KEY_DOWN && !
event.key.repeat) {
304 switch (
event.key.key) {
329 std::cout <<
"acmxvk: shader effects "
337 std::cout <<
"acmxvk: playlist "
352 std::cout <<
"acmxvk: shader time "
360 std::cout <<
"acmxvk: audio-reactive shader time "
370 std::cout <<
"acmxvk: audio delta-time scaling "
381 std::cout <<
"acmxvk: spectrum sensitivity scaling "
404 std::cout <<
"acmxvk: runtime HUD "
411 std::cout <<
"acmxvk: watermark "
427 std::cout <<
"acmxvk: multipass "
437 std::cout <<
"acmxvk: random autopilot crossfade "
444 std::cout <<
"acmxvk: shader lock "
453 std::chrono::steady_clock::now();
455 std::cout <<
"acmxvk: "
457 <<
" rendering enabled\n";
463 std::cout <<
"acmxvk: 3D view rotation "
471 std::cout <<
"acmxvk: 3D wave effect "
481 std::cout <<
"acmxvk: 3D scale oscillation "
498 std::cout <<
"acmxvk: model view reset\n";
501 case SDLK_LEFTBRACKET:
504 case SDLK_RIGHTBRACKET:
508 case SDLK_UNDERSCORE:
510 if ((
event.key.mod & SDL_KMOD_SHIFT) != 0) {
517 if ((
event.key.mod & SDL_KMOD_SHIFT) != 0) {
525 std::cout <<
"acmxvk: 3D view rotation speed "
533 std::cout <<
"acmxvk: 3D view rotation speed "
555 }
else if (
event.type == SDL_EVENT_MOUSE_MOTION) {
559 const int x =
static_cast<int>(
event.motion.x);
560 const int y =
static_cast<int>(
event.motion.y);
570 }
else if (
event.type == SDL_EVENT_MOUSE_BUTTON_DOWN &&
571 event.button.button == SDL_BUTTON_LEFT) {
578 }
else if (
event.type == SDL_EVENT_MOUSE_BUTTON_UP &&
579 event.button.button == SDL_BUTTON_LEFT) {
584 }
else if (
event.type == SDL_EVENT_MOUSE_WHEEL &&
587 const float wheel =
event.wheel.y != 0.0F
589 :
static_cast<float>(
590 event.wheel.integer_y);
612 std::uint32_t image_index) {
616 recordModel(command_buffer, image_index, VK_NULL_HANDLE);
620 VkCommandBuffer command_buffer, std::uint32_t image_index,
621 VkImageView texture_view,
622 [[maybe_unused]] VkExtent2D texture_extent) {
626 recordModel(command_buffer, image_index, texture_view);
630 std::uint32_t image_index,
631 VkImageView texture_view) {
636 const auto now = std::chrono::steady_clock::now();
637 float delta = std::chrono::duration<float>(
641 delta = std::clamp(delta, 0.0F, 0.1F);
643 float animation_delta = delta;
644 std::uint64_t animation_steps = 1U;
645 double video_timeline = 0.0;
646 std::uint64_t video_frame_index = 0U;
664 animation_delta = 0.0F;
665 animation_steps = 0U;
670 animation_delta =
static_cast<float>(
671 static_cast<double>(animation_steps) /
685 const float wave_step =
691 wave_step *
static_cast<float>(animation_steps),
694 const auto advance_amplitude = [](
float &litude,
696 std::uint64_t steps) {
697 constexpr float AMPLITUDE_RANGE = 0.5F;
698 constexpr float AMPLITUDE_PERIOD =
699 AMPLITUDE_RANGE * 2.0F;
700 float phase = direction >= 0.0F
702 : AMPLITUDE_PERIOD - amplitude;
704 phase + 0.005F *
static_cast<float>(steps),
706 if (phase < AMPLITUDE_RANGE) {
710 amplitude = AMPLITUDE_PERIOD - phase;
724 0.016F *
static_cast<float>(animation_steps),
725 2.0F * std::numbers::pi_v<float>);
728 const bool *keyboard = SDL_GetKeyboardState(
nullptr);
729 const bool model_scale_modifier =
730 (SDL_GetModState() & SDL_KMOD_SHIFT) != 0;
732 keyboard[SDL_SCANCODE_1]) {
738 keyboard[SDL_SCANCODE_2]) {
744 (keyboard[SDL_SCANCODE_EQUALS] ||
745 keyboard[SDL_SCANCODE_KP_PLUS])) {
752 (keyboard[SDL_SCANCODE_MINUS] ||
753 keyboard[SDL_SCANCODE_KP_MINUS])) {
760 if (keyboard[SDL_SCANCODE_W]) {
764 if (keyboard[SDL_SCANCODE_S]) {
773 if (keyboard[SDL_SCANCODE_A]) {
777 if (keyboard[SDL_SCANCODE_D]) {
787 const VkExtent2D extent = getRenderExtent();
788 const float aspect = extent.height > 0U
789 ?
static_cast<float>(extent.width) /
790 static_cast<float>(extent.height)
793 mxvk::UniformBufferObject uniforms{};
794 uniforms.model = glm::scale(
797 uniforms.model = glm::rotate(
799 glm::vec3(1.0F, 0.0F, 0.0F));
800 uniforms.model = glm::rotate(
802 glm::vec3(0.0F, 1.0F, 0.0F));
803 uniforms.model = glm::rotate(
805 glm::vec3(0.0F, 0.0F, 1.0F));
806 uniforms.model = glm::translate(
809 glm::vec3 look_direction{};
810 glm::vec3 camera_up(0.0F, 1.0F, 0.0F);
812 const float rotation =
814 look_direction = glm::vec3(
815 0.48F * std::sin(rotation),
816 0.48F * std::sin(rotation * 0.7F),
817 0.48F * std::cos(rotation));
821 look_direction = glm::normalize(glm::vec3(
822 std::cos(pitch) * std::cos(yaw),
824 std::cos(pitch) * std::sin(yaw))) *
826 camera_up = glm::vec3(-std::sin(pitch) * std::cos(yaw),
828 -std::sin(pitch) * std::sin(yaw));
830 const float camera_offset =
834 const glm::vec3 camera_position =
835 -glm::normalize(look_direction) * camera_offset;
836 uniforms.view = glm::lookAt(camera_position,
837 camera_position + look_direction,
839 uniforms.proj = glm::perspective(
840 glm::radians(120.0F), aspect, 0.01F, 1000.0F);
841 uniforms.proj[1][1] *= -1.0F;
852 mxvk::ModelFragmentPushConstants fragment_constants{};
853 fragment_constants.screenWidth =
static_cast<float>(extent.width);
854 fragment_constants.screenHeight =
static_cast<float>(extent.height);
855 fragment_constants.spriteSizeW =
static_cast<float>(extent.width);
856 fragment_constants.spriteSizeH =
static_cast<float>(extent.height);
858 fragment_constants.params = glm::vec4(
859 1.0F, 1.0F, 1.0F,
static_cast<float>(
shader_time));
860 input_model.setFragmentPushConstants(fragment_constants);
862 if (texture_view != VK_NULL_HANDLE) {
864 command_buffer, image_index, texture_view, uniforms,
868 command_buffer, image_index, 0U, uniforms,
false);
875 std::cout <<
"acmxvk: Ctrl+C received; draining rendered "
876 "frames and closing output\n";
879 setFrameReadbackEnabled(
false);
895 bool clocked_video_handled =
false;
904 double clock_seconds = 0.0;
908 clocked_video_handled =
true;
924 const bool render_latest_camera_frame =
928 !clocked_video_handled) {
934 double clock_seconds = 0.0;
937 const std::uint64_t target_frame =
938 static_cast<std::uint64_t
>(std::floor(
939 std::max(clock_seconds, 0.0) * rate));
950 <<
"acmxvk: camera recording uses real-time "
951 "PTS; slow frames preserve capture duration\n";
963 const VkExtent2D extent = getRenderExtent();
964 const int target_width = extent.width > 0U ?
static_cast<int>(extent.width) :
options.
width;
965 const int target_height =
966 extent.height > 0U ?
static_cast<int>(extent.height) :
options.
height;
977 setFrameReadbackEnabled(
988 std::cout <<
"acmxvk: audio shader warmup disabled\n";
990 std::cout <<
"acmxvk: audio shader warmup "
993 <<
" seconds to full strength)\n";
998 std::chrono::steady_clock::time_point now) {
1009 const float delta = std::max(
1022#ifdef ACMXVK_WITH_DNN
1026 std::cout <<
"acmxvk: PP-HumanSeg enabled: "
1029 ?
"background-only shader composition"
1030 :
"foreground isolation")
1031 <<
", automatic CPU/CUDA backend selection)\n";
1036 std::cout <<
"acmxvk: DexiNed edge detection enabled: "
1038 <<
" (automatic CPU/CUDA backend selection)\n";
1041 generic_onnx_processor =
1042 std::make_unique<dnn::GenericOnnxProcessor>(
1044 std::cout <<
"acmxvk: generic ONNX processing enabled: "
1046 <<
" (automatic CPU/CUDA backend selection)\n";
1052#ifdef ACMXVK_WITH_DEEP_DREAM
1061 metadata.
layers[deep_dream_model->selected_layer()];
1064 deep_dream_model->selected_channels()) {
1065 throw std::runtime_error(
1066 "--dream-channel is outside the selected layer's channel range");
1068 std::cout <<
"acmxvk: Deep Dream preprocessing enabled: "
1074 <<
", working size "
1076 ? std::string(
"native")
1089 <<
", acidcam-gpu order "
1092 <<
"; output feeds the existing Vulkan shader chain\n";
1094 std::cout <<
"acmxvk: randomized Deep Dream controls every "
1096 <<
" media second(s)\n";
1099 std::cout <<
"acmxvk: traditional "
1101 <<
" Deep Dream video mode: "
1102 "independent source frames, temporal feedback/zoom/"
1103 "rotation disabled, no-drop output\n";
1109#ifdef ACMXVK_WITH_CUDA
1113 gpu_filter_engine = std::make_unique<gpu::FilterEngine>(
1119#ifdef ACMXVK_WITH_CUDA
1120 if (gpu_filter_engine !=
nullptr &&
1121 gpu_filter_engine->select_relative_filter(direction) &&
1130 static_cast<void>(direction);
1140 midi_input = std::make_unique<midi::MidiInput>();
1143 throw std::runtime_error(
"could not open MIDI input port " +
1144 std::to_string(port));
1159 for (
int slider = 0; slider < 4; ++slider) {
1160 const int action = 600 + slider * 2;
1161 const bool mapped = std::any_of(
1165 return mapping.primary_action == action &&
1166 mapping.secondary_action == action + 1;
1171 const std::string name =
1172 "slider" + std::to_string(slider + 1);
1173 const auto uniform = std::find_if(
1176 return candidate.name == name;
1179 std::cerr <<
"acmxvk: MIDI " << name
1180 <<
" mapping has no matching custom uniform in "
1186 std::cout <<
"acmxvk: MIDI Slider " << (slider + 1)
1187 <<
" -> " << name <<
" [" << uniform->minimum
1188 <<
", " << uniform->maximum <<
"]\n";
1191 std::size_t active_mappings = 0;
1198 <<
"acmxvk: MIDI map action unavailable in this build: "
1199 << mapping.primary_action <<
':'
1200 << mapping.secondary_action <<
'\n';
1203 std::cout <<
"acmxvk: MIDI map has " << active_mappings
1204 <<
" active mapping(s)";
1206 std::cout <<
" and "
1208 <<
" mapping(s) reserved for unported ACMX2 controls";
1214 const std::size_t equals = mapping_text.find(
'=');
1215 if (equals == std::string::npos || equals == 0 ||
1216 equals + 1 >= mapping_text.size() ||
1217 mapping_text.find(
'=', equals + 1) != std::string::npos) {
1218 throw std::runtime_error(
1219 "--midi-cc requires [channel:]CC=uniform: " +
1223 const std::string source =
trim(mapping_text.substr(0, equals));
1224 const std::string uniform_name =
1225 trim(mapping_text.substr(equals + 1));
1227 throw std::runtime_error(
1228 "--midi-cc contains an invalid uniform name: " +
1234 const std::size_t colon = source.find(
':');
1235 if (colon == std::string::npos) {
1238 if (colon == 0 || colon + 1 >= source.size() ||
1239 source.find(
':', colon + 1) != std::string::npos) {
1240 throw std::runtime_error(
1241 "--midi-cc requires [channel:]CC=uniform: " +
1245 std::string_view(source).substr(0, colon),
"--midi-cc");
1247 std::string_view(source).substr(colon + 1),
"--midi-cc");
1248 if (channel < 1 || channel > 16) {
1249 throw std::runtime_error(
1250 "--midi-cc channel must be between 1 and 16");
1254 if (controller < 0 || controller > 127) {
1255 throw std::runtime_error(
1256 "--midi-cc controller must be between 0 and 127");
1259 const auto uniform = std::find_if(
1262 return candidate.name == uniform_name;
1265 throw std::runtime_error(
1266 "--midi-cc target is not defined in library.json: " +
1269 const std::size_t uniform_index =
static_cast<std::size_t
>(
1271 const auto duplicate = std::find_if(
1274 return mapping.uniform_index == uniform_index;
1277 throw std::runtime_error(
1278 "custom uniform has more than one --midi-cc mapping: " +
1283 {channel, controller, uniform_index, uniform_name});
1284 std::cout <<
"acmxvk: MIDI "
1286 ? std::string(
"any channel")
1287 :
"channel " + std::to_string(channel + 1))
1288 <<
" CC " << controller <<
" -> " << uniform_name
1289 <<
" [" << uniform->minimum <<
", "
1290 << uniform->maximum <<
"]\n";
1297 if (message.
bytes.size() < 3 ||
1298 (message.
bytes[0] & 0xF0U) != 0xB0U) {
1301 const int channel = message.
bytes[0] & 0x0FU;
1302 const int controller = message.
bytes[1] & 0x7FU;
1303 const int value = message.
bytes[2] & 0x7FU;
1304 bool changed =
false;
1306 if (mapping.controller != controller ||
1307 (mapping.channel >= 0 && mapping.channel != channel)) {
1312 const double normalized =
static_cast<double>(value) / 127.0;
1313 const float mapped =
static_cast<float>(
1314 uniform.
minimum + normalized *
1319 std::cout <<
"acmxvk: MIDI CC " << controller <<
" -> "
1320 << mapping.uniform_name <<
'=' << mapped <<
'\n';
1329#ifdef ACMXVK_WITH_CUDA
1330 return gpu_filter_engine !=
nullptr ? SDLK_RIGHT : SDLK_UNKNOWN;
1332 return SDLK_UNKNOWN;
1335#ifdef ACMXVK_WITH_CUDA
1336 return gpu_filter_engine !=
nullptr ? SDLK_LEFT : SDLK_UNKNOWN;
1338 return SDLK_UNKNOWN;
1349 return SDLK_PAGEDOWN;
1354 return SDLK_UNKNOWN;
1360 return SDLK_UNKNOWN;
1412 return SDLK_UNKNOWN;
1431 return SDLK_UNKNOWN;
1464 const bool primary_supported =
1467 const bool secondary_supported =
1470 return primary_supported && secondary_supported;
1476 return "select next CUDA filter";
1478 return "select previous CUDA filter";
1480 return "next shader or playlist node";
1482 return "previous shader or playlist node";
1485 return "increase shader time speed";
1488 return "decrease shader time speed";
1490 return "toggle audio delta-time scaling";
1492 return "toggle spectrum sensitivity scaling";
1494 return "increase audio sensitivity";
1496 return "decrease audio sensitivity";
1498 return "step shader time forward";
1500 return "step shader time backward";
1502 return "rotate model X forward";
1504 return "rotate model X backward";
1506 return "rotate model Y forward";
1508 return "rotate model Y backward";
1510 return "increase 3D manual rotation speed";
1512 return "decrease 3D manual rotation speed";
1514 return "rotate model Z forward";
1516 return "rotate model Z backward";
1518 return "increase model scale";
1520 return "decrease model scale";
1522 return "toggle runtime HUD";
1524 return "toggle shader bypass";
1526 return "decrease 3D view rotation speed";
1528 return "increase 3D view rotation speed";
1530 return "toggle 2D/3D rendering";
1532 return "take TIFF snapshot";
1534 return "take WebP snapshot";
1536 return "take raw RGBA snapshot";
1538 return "toggle 3D wave effect";
1540 return "toggle 3D scale oscillation";
1542 return "decrease model scale";
1544 return "increase model scale";
1546 return "toggle watermark";
1548 return "toggle random autopilot";
1550 return "toggle random autopilot crossfade";
1552 return "toggle shader lock";
1554 return "toggle rendering freeze";
1556 return "toggle multipass";
1558 return "toggle playlist or input pause";
1560 return "toggle audio-reactive shader time";
1562 return "toggle fullscreen";
1564 return "step shader time backward";
1566 return "toggle shader time";
1568 return "step shader time forward";
1570 return "toggle 3D view rotation";
1572 return "reset model view";
1574 return "toggle sequential autopilot";
1576 return "take screenshot";
1578 return "unsupported action";
1587 const auto rotate = [](
float °rees,
float amount) {
1588 degrees = std::fmod(degrees + amount, 360.0F);
1589 if (degrees < 0.0F) {
1613 std::cout <<
"acmxvk: 3D manual rotation speed "
1619 std::cout <<
"acmxvk: 3D manual rotation speed "
1644 std::cout <<
"acmxvk: MIDI action: "
1651 if (key == SDLK_UNKNOWN) {
1658 SDL_Event midi_event{};
1659 midi_event.type = SDL_EVENT_KEY_DOWN;
1660 midi_event.key.type = SDL_EVENT_KEY_DOWN;
1661 midi_event.key.key = key;
1662 midi_event.key.mod =
1663 action == 91 || action == 93 ? SDL_KMOD_SHIFT
1665 midi_event.key.repeat =
false;
1670 std::string_view label) {
1677 const double normalized =
static_cast<double>(value) / 127.0;
1678 const float mapped =
static_cast<float>(
1683 std::cout <<
"acmxvk: MIDI " << label <<
" -> "
1684 << uniform.
name <<
'=' << mapped <<
'\n';
1690 if (message.
bytes.size() < 3) {
1693 bool changed =
false;
1701 const int value = message.
bytes[2] & 0x7FU;
1711 const int uniform_index =
1713 if (uniform_index >= 0) {
1716 static_cast<std::size_t
>(uniform_index), value,
1717 "Slider " + std::to_string(slider + 1)) ||
1732 state.
value = value;
1733 state.
active = value != 64;
1752 const int distance = std::abs(state.
value - 64);
1753 const int frame_skip =
1754 std::max(1, 17 - (distance * 16 / 63));
1759 int action = state.
value > 64
1787 const std::vector<midi::MidiMessage> messages =
1789 bool custom_uniforms_changed =
false;
1791 custom_uniforms_changed =
1793 custom_uniforms_changed =
1797 if (custom_uniforms_changed) {
1802 std::ostringstream text;
1803 text <<
"acmxvk: MIDI #" << message.sequence <<
" +"
1804 << std::fixed << std::setprecision(6)
1805 << message.delta_seconds <<
"s [";
1806 for (std::size_t index = 0; index < message.bytes.size();
1811 text << std::hex << std::uppercase << std::setfill(
'0')
1813 <<
static_cast<unsigned int>(message.bytes[index]);
1816 std::cout << text.str() <<
'\n';
1819 const std::uint64_t dropped =
midi_input->dropped_message_count();
1821 std::cerr <<
"acmxvk: MIDI queue dropped " << dropped
1822 <<
" message(s) total\n";
1841 <<
"acmxvk: source video has no decodable audio "
1842 "track; continuing with silent audio-reactive "
1845 std::cerr <<
" and pass-through disabled";
1851 throw std::runtime_error(
"could not decode --audio-file: " +
1855 std::cout <<
"acmxvk: source video audio drives shader "
1863 std::cerr <<
"acmxvk: file audio output could not be "
1864 "initialized; continuing with silent analysis\n";
1879 std::cerr <<
"acmxvk: audio input could not be initialized; "
1880 "continuing with zero-valued audio metrics\n";
1895 throw std::runtime_error(
1896 "--record-audio requires an active live audio input");
1903 throw std::runtime_error(
1904 "could not start standalone microphone recording");
1914 std::cout <<
"acmxvk: audio sensitivity "
1919 static_cast<void>(amount);
1921 std::cout <<
"acmxvk: audio input is not active\n";
1944 std::cerr <<
"acmxvk: could not start live audio recording; "
1945 "continuing with video-only output\n";
1966 std::cout <<
"acmxvk: media timeline started on first source frame\n";
1976 const auto now = std::chrono::steady_clock::now();
2009 : std::chrono::steady_clock::now();
2010 const auto active_time =
2014 0.0, std::chrono::duration<double>(active_time).count());
2025 const fs::path shader = fs::absolute(
2027 .lexically_normal();
2028 const std::string label =
2029 compute ?
"compute shader" :
"fragment shader";
2030 if (shader.extension() !=
".spv" ||
2031 !fs::is_regular_file(shader)) {
2032 throw std::runtime_error(
2033 label +
" is not a readable .spv file: " +
2037 const mxvk::ShaderModuleInfo module_info =
2038 mxvk::inspect_spirv(mxvk::load_spv(shader.string()));
2039 const mxvk::ShaderStage expected_stage =
2040 compute ? mxvk::ShaderStage::Compute
2041 : mxvk::ShaderStage::Fragment;
2042 if (module_info.stage != expected_stage) {
2043 throw std::runtime_error(
2044 label +
" SPIR-V entry point has the wrong shader stage: " +
2062 for (
const std::string &entry : manifest.
entries) {
2063 const fs::path shader =
2065 if (!shader.empty()) {
2067 "shader manifest entry");
2068 const mxvk::ShaderModuleInfo module_info =
2069 mxvk::inspect_spirv(mxvk::load_spv(shader.string()));
2074 std::sort(
shaders.begin(),
shaders.end(), [](
const fs::path &left,
const fs::path &right) {
2075 std::string left_text = left.generic_string();
2076 std::string right_text = right.generic_string();
2077 std::transform(left_text.begin(), left_text.end(), left_text.begin(),
2078 [](unsigned char character) {
2079 return static_cast<char>(std::tolower(character));
2081 std::transform(right_text.begin(), right_text.end(), right_text.begin(),
2082 [](
unsigned char character) {
2083 return static_cast<char>(std::tolower(character));
2085 return left_text < right_text;
2087 if (shaders.empty()) {
2088 throw std::runtime_error(
"shader manifest contains no readable SPIR-V files: " +
2089 shader_manifest_path.string());
2091 std::cout <<
"acmxvk: loaded " << shaders.size() <<
" shaders from "
2092 << shader_manifest_path.string() <<
'\n';
2093 printCustomUniforms();
2095 if (!options.shader_file.empty()) {
2096 const auto selected = std::find_if(
2097 shaders.begin(), shaders.end(), [&](
const fs::path &path) {
2098 fs::path requested(options.shader_file);
2099 if (requested.extension() !=
".spv") {
2100 requested.replace_extension(
".spv");
2102 return path.filename() == requested.filename() ||
2103 path.lexically_relative(shader_library_directory) == requested;
2105 if (selected == shaders.end()) {
2106 throw std::runtime_error(
"shader file is not listed in the manifest: " +
2107 options.shader_file);
2109 shader_index =
static_cast<std::size_t
>(std::distance(shaders.begin(), selected));
2111 const int count =
static_cast<int>(shaders.size());
2112 const int wrapped_index = ((options.shader_index % count) + count) % count;
2113 shader_index =
static_cast<std::size_t
>(wrapped_index);
2118 for (
const std::string &override_text :
2119 options.custom_uniform_overrides) {
2120 const std::size_t separator = override_text.find(
'=');
2121 if (separator == std::string::npos || separator == 0 ||
2122 separator + 1 >= override_text.size()) {
2123 throw std::runtime_error(
2124 "--uniform requires name=value: " + override_text);
2126 const std::string name =
trim(override_text.substr(0, separator));
2128 trim(override_text.substr(separator + 1)),
"--uniform");
2129 const auto match = std::find_if(
2132 return uniform.name == name;
2135 throw std::runtime_error(
2136 "custom uniform is not defined in library.json: " + name);
2138 match->value = std::clamp(value, match->minimum, match->maximum);
2152 constexpr std::string_view COMPONENTS =
"xyzw";
2153 std::cout <<
"acmxvk: custom uniforms (binding 1):\n";
2154 for (std::size_t index = 0; index <
custom_uniforms.size(); ++index) {
2156 std::cout <<
" " << uniform.
name <<
'=' << uniform.
value
2157 <<
" -> custom_uniforms[" << (index / 4) <<
"]."
2158 << COMPONENTS[index % 4] <<
'\n';
2166 [[nodiscard]] fs::path
2168 std::error_code error;
2169 const fs::path canonical = fs::weakly_canonical(shader, error);
2170 const std::string key =
2171 (error ? shader.lexically_normal() : canonical).string();
2182 std::string_view source) {
2183 if (module_info.usesHistoryTexture &&
2186 std::cout <<
"acmxvk: enabled shared history for " << source
2189 if (module_info.usesSpectrumTexture &&
2192 std::cout <<
"acmxvk: enabled spectrum descriptor for " << source
2195 if (module_info.usesSpectrumHistoryTexture &&
2198 if (
options.audio_buffers == 0) {
2201 std::cout <<
"acmxvk: enabled " <<
options.audio_buffers
2202 <<
" spectrum-history layers for " << source
2224 return options.audio_buffers > 0;
2232 const auto now = std::chrono::steady_clock::now();
2244 std::cerr <<
"acmxvk: could not read compatible interface "
2245 "control state; retrying\n";
2264 std::cout <<
"acmxvk: interface live shader, multipass, playback, "
2265 "overlay, GPU-filter, Deep Dream, and audio-file control "
2267 << (reconnected ?
" (reconnected)" :
"") <<
'\n';
2282 std::cerr <<
"acmxvk: interface control connection lost; "
2288 std::chrono::steady_clock::now() + std::chrono::seconds(2);
2320 std::cout <<
"acmxvk: interface video repeat "
2321 << (
options.repeat ?
"enabled" :
"disabled")
2328 std::cout <<
"acmxvk: interface normalized time "
2329 << (
options.normalized_time ?
"enabled"
2341 std::cout <<
"acmxvk: interface display-filter overlay "
2342 << (
options.display_filter ?
"enabled"
2351 "interface watermark",
true);
2352 }
catch (
const std::exception &error) {
2353 std::cerr <<
"acmxvk: rejected interface watermark: "
2354 << error.what() <<
'\n';
2358 const bool was_enabled =
2360 const bool requested_enabled =
2363 const bool changed =
2378 std::cout <<
"acmxvk: interface watermark "
2381 std::cout <<
" (color="
2382 <<
static_cast<int>(
options.watermark_color[0])
2384 <<
static_cast<int>(
options.watermark_color[1])
2386 <<
static_cast<int>(
options.watermark_color[2])
2395#ifdef ACMXVK_WITH_CUDA
2396 const bool requested_enabled =
2398 const bool currently_enabled = gpu_filter_engine !=
nullptr;
2399 const std::vector<int> effective_indices =
2401 : std::vector<int>{};
2402 if (requested_enabled == currently_enabled &&
2403 options.gpu_filter_indices == effective_indices &&
2404 (!requested_enabled ||
2405 options.gpu_frame_buffer_size ==
2411 std::cerr <<
"acmxvk: rejected enabled interface GPU-filter "
2412 "state without any filter indices\n";
2416 std::unique_ptr<gpu::FilterEngine> replacement;
2417 if (requested_enabled) {
2419 replacement = std::make_unique<gpu::FilterEngine>(
2422 }
catch (
const std::exception &error) {
2424 <<
"acmxvk: rejected interface GPU-filter state: "
2425 << error.what() <<
'\n';
2430 gpu_filter_engine = std::move(replacement);
2431 options.gpu_filter_indices = effective_indices;
2432 if (requested_enabled) {
2446 std::cout <<
"acmxvk: interface CUDA filter chain "
2447 << (requested_enabled ?
"enabled" :
"disabled");
2448 if (requested_enabled) {
2451 <<
" history frames)";
2456 if (announce && requested.
enabled) {
2457 std::cerr <<
"acmxvk: ignored interface GPU-filter state: this "
2458 "build does not include acidcam-gpu\n";
2465#ifdef ACMXVK_WITH_DEEP_DREAM
2468 if (!currently_enabled) {
2472#ifdef ACMXVK_WITH_MXVK_CUDA
2473 cuda_dream_rgba.release();
2477 options.gpu_filter_before_dream =
false;
2480 std::cout <<
"acmxvk: interface Deep Dream disabled\n";
2488 "interface Deep Dream model path");
2491 "interface Deep Dream layer");
2493 throw std::runtime_error(
2494 "iterations must be between 1 and 100");
2496 if (!std::isfinite(requested.
strength) ||
2498 throw std::runtime_error(
2499 "strength must be greater than 0 and no more than 10");
2501 if (!std::isfinite(requested.
feedback) ||
2503 throw std::runtime_error(
2504 "feedback must be between 0 and 0.99");
2506 if (!std::isfinite(requested.
zoom) || requested.
zoom < 0.9F ||
2507 requested.
zoom > 1.1F) {
2508 throw std::runtime_error(
"zoom must be between 0.9 and 1.1");
2510 if (!std::isfinite(requested.
rotation) ||
2512 throw std::runtime_error(
2513 "rotation must be between -5 and 5 degrees");
2518 throw std::runtime_error(
2519 "maximum dimension must be 0 or between 64 and 4096");
2522 throw std::runtime_error(
2523 "channel must be all channels or between 0 and 65535");
2526 throw std::runtime_error(
"octaves must be between 1 and 8");
2531 throw std::runtime_error(
2532 "octave scale must be between 1.1 and 3.0");
2535 throw std::runtime_error(
"jitter must be between 0 and 64");
2538 throw std::runtime_error(
2539 "smoothing must be between 0 and 16");
2542#ifdef ACMXVK_WITH_CUDA
2543 if (gpu_filter_engine ==
nullptr) {
2544 throw std::runtime_error(
2545 "acidcam-gpu-first requires an enabled GPU filter chain");
2548 throw std::runtime_error(
2549 "acidcam-gpu-first requires acidcam-gpu support");
2552 throw std::runtime_error(
2553 "acidcam-gpu-first cannot be used with maximize FPS");
2556 throw std::runtime_error(
2557 "acidcam-gpu-first supports camera and video input");
2559 if (!
options.edge_model.empty() ||
2560 !
options.human_model.empty() ||
2561 !
options.onnx_configuration.empty()) {
2562 throw std::runtime_error(
2563 "acidcam-gpu-first cannot be combined with DNN input effects");
2566 throw std::runtime_error(
2567 "acidcam-gpu-first cannot be enabled for HDR input");
2571 const bool settings_changed =
2572 !currently_enabled ||
2587 options.gpu_filter_before_dream !=
2589 if (!settings_changed) {
2593 const bool reload_model =
2594 !currently_enabled ||
2598 std::unique_ptr<dream::Model> replacement;
2601 replacement = std::make_unique<dream::Model>(
2605 validated_model = replacement.get();
2608 static_cast<std::size_t
>(requested.
channel) >=
2610 throw std::runtime_error(
2611 "channel is outside the selected layer's channel range");
2614 if (replacement !=
nullptr) {
2616#ifdef ACMXVK_WITH_MXVK_CUDA
2617 cuda_dream_rgba.release();
2638 std::cout <<
"acmxvk: interface Deep Dream settings applied: "
2639 << requested.
layer <<
", "
2640 << requested.
iterations <<
" iteration(s), strength "
2641 << requested.
strength <<
", feedback "
2643 <<
", rotation " << requested.
rotation
2646 ?
"acidcam-gpu first"
2647 :
"Deep Dream first")
2648 << (reload_model ?
" (model reloaded)" :
"")
2651 }
catch (
const std::exception &error) {
2652 std::cerr <<
"acmxvk: rejected interface Deep Dream settings: "
2653 << error.what() <<
'\n';
2656 if (announce && requested.
enabled) {
2657 std::cerr <<
"acmxvk: ignored interface Deep Dream settings: this "
2658 "build does not include Deep Dream support\n";
2668 <<
"acmxvk: ignored live audio-file change because this "
2669 "process was not started in audio-file mode\n";
2672 if (requested.
path.empty()) {
2674 <<
"acmxvk: rejected empty interface audio-file request\n";
2678 auto replacement = std::make_unique<audio::FileAudioSource>();
2680 if (!replacement->open(requested.
path)) {
2681 std::cerr <<
"acmxvk: could not switch file audio to: "
2682 << requested.
path <<
'\n';
2685 }
catch (
const std::exception &error) {
2686 std::cerr <<
"acmxvk: rejected interface audio-file request: "
2687 << error.what() <<
'\n';
2691 replacement->set_repeat(requested.
repeat);
2693 !replacement->enable_output(
2695 static_cast<float>(
options.audio_pass_through_gain))) {
2697 <<
"acmxvk: live audio-file output could not be opened; "
2698 "continuing with visual reactivity only\n";
2710 std::cout <<
"acmxvk: switched file audio to: "
2712 << (
options.audio_repeat ?
"on" :
"off")
2713 <<
", trunc=" << (
options.audio_trunc ?
"on" :
"off")
2714 <<
", pass-through="
2715 << (
options.audio_pass_through ?
"on" :
"off")
2718 static_cast<void>(requested);
2719 std::cerr <<
"acmxvk: ignored interface audio-file request: this "
2720 "build does not include audio support\n";
2726 if (requested.
path.empty()) {
2728 <<
"acmxvk: rejected empty interface shader reload\n";
2735 "interface shader reload path");
2736 }
catch (
const std::exception &error) {
2737 std::cerr <<
"acmxvk: rejected interface shader reload: "
2738 << error.what() <<
'\n';
2742 std::error_code error;
2743 const fs::path requested_path =
2744 fs::weakly_canonical(requested.
path, error);
2745 if (error || requested_path.empty() ||
2746 !fs::is_regular_file(requested_path)) {
2747 std::cerr <<
"acmxvk: interface shader reload file is not "
2749 << requested.
path <<
'\n';
2753 std::size_t logical_index =
shaders.size();
2754 const auto shader_match = std::find_if(
2756 [&](
const fs::path &shader) {
2757 std::error_code shader_error;
2758 const fs::path canonical_shader =
2759 fs::weakly_canonical(shader, shader_error);
2760 return !shader_error && canonical_shader == requested_path;
2762 if (shader_match !=
shaders.end()) {
2763 logical_index =
static_cast<std::size_t
>(
2764 std::distance(
shaders.begin(), shader_match));
2768 logical_index =
static_cast<std::size_t
>(requested.
shader_index);
2770 if (logical_index >=
shaders.size()) {
2771 std::cerr <<
"acmxvk: interface shader reload does not identify "
2772 "a shader in the active runtime library: "
2773 << requested_path.string() <<
'\n';
2777 const fs::path logical_shader =
shaders[logical_index];
2778 std::error_code logical_error;
2779 const fs::path canonical_logical =
2780 fs::weakly_canonical(logical_shader, logical_error);
2781 if (logical_error || canonical_logical.empty()) {
2782 std::cerr <<
"acmxvk: interface shader reload could not resolve "
2783 "its runtime-library shader\n";
2786 const std::string logical_key = canonical_logical.string();
2787 const fs::path previously_resolved =
2790 mxvk::ShaderModuleInfo module_info;
2793 "interface shader reload");
2794 module_info = mxvk::inspect_spirv(
2795 mxvk::load_spv(requested_path.string()));
2796 }
catch (
const std::exception &reload_error) {
2797 std::cerr <<
"acmxvk: rejected compiled shader reload: "
2798 << reload_error.what() <<
'\n';
2804 const bool spectrum_history_before =
2807 const bool resources_grew =
2810 spectrum_history_before !=
2814 std::error_code active_error;
2815 const fs::path canonical_previous =
2816 fs::weakly_canonical(previously_resolved, active_error);
2817 const bool active = !active_error &&
2818 std::any_of(active_pipeline.begin(), active_pipeline.end(),
2819 [&](
const fs::path &shader) {
2820 std::error_code shader_error;
2821 return fs::weakly_canonical(shader, shader_error) ==
2822 canonical_previous &&
2825 if (requested_path == canonical_logical)
2831 if (resources_grew) {
2837 std::cout <<
"acmxvk: live reloaded active "
2838 << (module_info.stage == mxvk::ShaderStage::Compute
2841 <<
" shader: " << requested_path.string() <<
'\n';
2843 std::cout <<
"acmxvk: live compiled shader ready for its next "
2845 << requested_path.string() <<
'\n';
2851 std::vector<fs::path> requested_passes;
2854 std::cerr <<
"acmxvk: rejected enabled interface multipass "
2855 "state without any shader passes\n";
2858 requested_passes.reserve(requested.
shader_names.size());
2859 for (
const std::string &name : requested.
shader_names) {
2860 const fs::path requested_path(name);
2861 const bool has_parent_reference = std::any_of(
2862 requested_path.begin(), requested_path.end(),
2863 [](
const fs::path &part) { return part ==
".."; });
2864 if (requested_path.is_absolute() || has_parent_reference) {
2865 std::cerr <<
"acmxvk: rejected unsafe interface "
2866 "multipass shader name: "
2872 if (shader.empty()) {
2873 std::cerr <<
"acmxvk: interface multipass shader is not "
2874 "in the active library: "
2878 requested_passes.push_back(shader);
2882 const bool requested_enabled =
2883 requested.
enabled && !requested_passes.empty();
2889 std::cerr <<
"acmxvk: interface multipass update ignored while "
2890 "shader switching is locked\n";
2906 std::cout <<
"acmxvk: interface multipass enabled ("
2909 std::cout <<
"\n " << shader.filename().string();
2913 std::cout <<
"acmxvk: interface multipass disabled\n";
2918 const std::string &requested_name) {
2919 if (requested_name.empty()) {
2923 const fs::path requested(requested_name);
2924 const bool has_parent_reference =
2925 std::any_of(requested.begin(), requested.end(),
2926 [](
const fs::path &part) { return part ==
".."; });
2927 if (requested.is_absolute() || has_parent_reference) {
2928 std::cerr <<
"acmxvk: rejected unsafe interface shader name: "
2929 << requested_name <<
'\n';
2935 const auto match = std::find(
shaders.begin(),
shaders.end(), shader);
2936 if (shader.empty() || match ==
shaders.end()) {
2937 std::cerr <<
"acmxvk: interface shader is not in the active "
2939 << requested_name <<
'\n';
2943 const std::size_t next_index =
2944 static_cast<std::size_t
>(std::distance(
shaders.begin(), match));
2949 std::cerr <<
"acmxvk: interface shader selection ignored while "
2950 "shader switching is locked\n";
2965 const std::vector<InterfaceUniformValue> &uniform_values) {
2966 if (uniform_values.empty()) {
2970 std::size_t changed_count = 0;
2971 std::size_t ignored_count = 0;
2974 !std::isfinite(incoming.value)) {
2978 const auto match = std::find_if(
2981 return uniform.name == incoming.name;
2987 const std::size_t index =
static_cast<std::size_t
>(
2993 const float value =
static_cast<float>(std::clamp(
2994 static_cast<double>(incoming.value), match->minimum,
3003 if (changed_count > 0) {
3005 std::cout <<
"acmxvk: interface updated " << changed_count
3006 <<
" custom uniform(s)\n";
3008 if (ignored_count > 0) {
3009 std::cerr <<
"acmxvk: interface ignored " << ignored_count
3010 <<
" unknown or invalid custom uniform(s)\n";
3015 for (
const int index :
options.shader_pass_indices) {
3016 if (index < 0 || index >=
static_cast<int>(
shaders.size())) {
3017 throw std::runtime_error(
"shader pass index is out of range: " +
3018 std::to_string(index));
3022 for (
const std::string &name :
options.shader_pass_files) {
3025 if (shader.empty()) {
3026 throw std::runtime_error(
"shader pass file is not listed in the manifest: " +
3035 if (
options.playlist_file.empty()) {
3041 std::cout <<
"acmxvk: playlist loaded "
3043 <<
playlist.size() <<
" nodes from "
3044 <<
options.playlist_file <<
'\n';
3049 const auto resolve = [&](std::string &path,
3050 const fs::path &resource_subdirectory,
3051 std::string_view label) {
3052 if (path.empty() || fs::is_regular_file(path) ||
3053 fs::path(path).is_absolute()) {
3057 if (resolved.empty()) {
3059 options, resource_subdirectory / fs::path(path));
3061 if (!resolved.empty()) {
3062 path = resolved.string();
3063 std::cout <<
"acmxvk: " << label <<
" (resource path): "
3067 resolve(
options.playlist_file,
"playlists",
"playlist");
3068 resolve(
options.midi_map_file,
"midi-examples",
"MIDI map");
3070 if (
options.model_file.empty()) {
3072 std::cout <<
"acmxvk: 3D model (default): "
3073 <<
options.model_file <<
'\n';
3075 resolve(
options.model_file,
"models",
"3D model");
3078 std::string model_name =
3079 fs::path(
options.model_file).filename().string();
3081 model_name.begin(), model_name.end(), model_name.begin(),
3082 [](
unsigned char character) {
3083 return static_cast<char>(std::tolower(character));
3085 if (!model_name.ends_with(
".obj") &&
3086 !model_name.ends_with(
".mxmod") &&
3087 !model_name.ends_with(
".mxmod.z")) {
3088 throw std::runtime_error(
3089 "--model requires an .obj, .mxmod, or .mxmod.z file");
3091 if (!fs::is_regular_file(
options.model_file)) {
3092 throw std::runtime_error(
3093 "3D model was not found: " +
options.model_file);
3095 constexpr std::uintmax_t MAX_MODEL_BYTES =
3096 1024U * 1024U * 1024U;
3109 if (!fs::is_regular_file(font)) {
3110 throw std::runtime_error(
"overlay font was not found: " +
3113 const VkExtent2D preview_extent = getSwapchainExtent();
3114 const int preview_height = preview_extent.height > 0U
3115 ?
static_cast<int>(preview_extent.height)
3117 constexpr int FONT_HEIGHT_DIVISOR = 60;
3119 std::max(12, preview_height / FONT_HEIGHT_DIVISOR);
3123 std::cout <<
"acmxvk: window-scaled output/HUD font "
3129 constexpr std::size_t MAX_OVERLAY_CHARACTERS = 120;
3145 return passes !=
nullptr && !passes->empty() ?
"Post-shader"
3151 if (passes ==
nullptr || passes->empty()) {
3155 std::string description =
"Multipass: ";
3156 for (std::size_t index = 0; index < passes->size(); ++index) {
3158 description +=
", ";
3160 description += (*passes)[index].filename().string();
3169 std::ostringstream description;
3177 const double finite_seconds =
3178 std::isfinite(seconds_value) ? seconds_value : 0.0;
3179 const auto elapsed =
static_cast<std::uint64_t
>(
3180 std::floor(std::max(0.0, finite_seconds)));
3181 const std::uint64_t hours = elapsed / 3600U;
3182 const std::uint64_t minutes = (elapsed / 60U) % 60U;
3183 const std::uint64_t seconds = elapsed % 60U;
3184 std::ostringstream text;
3185 text << std::setfill(
'0') << std::setw(2) << hours <<
':'
3186 << std::setw(2) << minutes <<
':' << std::setw(2) << seconds;
3191 SDL_Window *window = getSDLWindow();
3192 if (window ==
nullptr) {
3196 const auto now = std::chrono::steady_clock::now();
3197 constexpr auto UPDATE_INTERVAL = std::chrono::milliseconds(500);
3204 const bool recording =
writer.is_open() ||
options.png_output;
3209 elapsed_seconds =
writer.is_open()
3218 std::ostringstream title;
3220 title <<
"ACMXVK - Graphics Mode - "
3222 << displayed_frames <<
" frames]";
3224 const std::uint64_t total_frames =
3226 ?
static_cast<std::uint64_t
>(std::llround(
3230 if (total_frames > 0U) {
3231 title << total_frames;
3238 title <<
"ACMXVK - Capture Mode - "
3240 << displayed_frames <<
" frames]";
3244 title <<
" (Recording)";
3246 constexpr double BYTES_PER_MEGABYTE = 1024.0 * 1024.0;
3247 const double file_size_mb =
3248 static_cast<double>(
writer.get_bytes_written()) /
3250 title <<
" [File: " << std::fixed << std::setprecision(2)
3251 << file_size_mb <<
" MB]";
3254 title <<
" (Preview)";
3257 const std::string text = title.str();
3258 SDL_SetWindowTitle(window, text.c_str());
3267 std::uint64_t expected_frames = 0U;
3269 const auto duration_frames =
static_cast<std::uint64_t
>(
3271 expected_frames = std::max<std::uint64_t>(1U, duration_frames);
3275 const auto source_frames =
static_cast<std::uint64_t
>(
3277 if (expected_frames == 0U) {
3278 expected_frames = source_frames;
3280 expected_frames = std::min(expected_frames, source_frames);
3283 if (complete && expected_frames == 0U) {
3287 const auto now = std::chrono::steady_clock::now();
3289 if (expected_frames > 0U) {
3290 const std::uint64_t processed_frames = complete
3295 percent =
static_cast<int>(
3296 (
static_cast<double>(processed_frames) /
3297 static_cast<double>(expected_frames)) *
3300 percent = std::min(percent, 99);
3304 const bool percent_changed =
3306 const bool time_elapsed =
3309 std::chrono::milliseconds(500);
3310 if (!complete && !percent_changed && !time_elapsed) {
3316 const std::uint64_t processed_frames =
3317 complete && expected_frames > 0U ? expected_frames
3319 const std::uint64_t written_frames =
3321 ?
static_cast<std::uint64_t
>(
3322 std::max<std::int64_t>(0,
writer.get_frame_count()))
3324 const double elapsed_seconds =
3327 std::cout <<
"acmxvk: [";
3329 std::cout << std::setw(3) << percent <<
'%';
3333 std::cout <<
"] Frame " << processed_frames <<
'/';
3334 if (expected_frames > 0U) {
3335 std::cout << expected_frames;
3339 std::cout <<
" | Written: " << written_frames
3342 constexpr double BYTES_PER_MEGABYTE = 1024.0 * 1024.0;
3343 const double file_size_mb =
3344 static_cast<double>(
writer.get_bytes_written()) /
3346 std::ostringstream size_text;
3347 size_text << std::fixed << std::setprecision(2) << file_size_mb;
3348 std::cout <<
" | Size: " << size_text.str() <<
" MB";
3357 std::chrono::duration<double>(std::chrono::steady_clock::now() -
3364 std::uint64_t *frame_index)
const {
3372 if (frame_index !=
nullptr) {
3373 *frame_index = index;
3379 double position = 0.0;
3386 return std::max(0.0, position);
3390 std::string text =
"Video: " +
3405 const auto now = std::chrono::steady_clock::now();
3406 const double elapsed =
3408 if (elapsed < 0.5) {
3421 const auto interval = std::chrono::duration_cast<
3422 std::chrono::steady_clock::duration>(
3423 std::chrono::duration<double>(1.0 /
options.requested_fps));
3424 const auto now = std::chrono::steady_clock::now();
3447 const auto now = std::chrono::steady_clock::now();
3455 const double elapsed = std::chrono::duration<double>(
3458 if (elapsed < 1.0) {
3467 const double log_threshold = std::max(
3472 std::ostringstream status;
3473 status <<
"acmxvk: camera delivery: " << std::fixed
3480 std::cout << status.str() <<
'\n';
3491 const SDL_Color shader_color{0U, 96U, 255U, 255U};
3496 shader +=
" [locked]";
3501 10, y, shader_color);
3504 const SDL_Color crossfade_color{255U, 192U, 0U, 255U};
3505 std::ostringstream crossfade_status;
3513 const std::string playlist_description =
3515 if (!playlist_description.empty()) {
3516 const SDL_Color playlist_color{255U, 0U, 255U, 255U};
3517 printPreviewText(playlist_description, 10, y,
3523 if (passes !=
nullptr && !passes->empty()) {
3524 constexpr std::size_t MAX_HUD_PASS_LINES = 8U;
3525 const std::size_t displayed_passes =
3526 std::min(passes->size(), MAX_HUD_PASS_LINES);
3527 for (std::size_t index = 0; index < displayed_passes;
3529 std::ostringstream pass;
3530 pass <<
"Pass [" << (index + 1) <<
'/' << passes->size()
3531 <<
"]: " << (*passes)[index].filename().string();
3536 if (displayed_passes < passes->size()) {
3537 const std::string remaining =
3539 std::to_string(passes->size() - displayed_passes) +
3541 printPreviewText(remaining, 10, y, shader_color);
3547 const SDL_Color model_color{0U, 220U, 180U, 255U};
3548 std::string model_status =
3551 fs::path(
options.model_file).filename().string();
3553 model_status +=
" [wave]";
3556 model_status +=
" [oscillate]";
3563#ifdef ACMXVK_WITH_DNN
3564 const SDL_Color dnn_color{64U, 220U, 128U, 255U};
3565 if (human_segmenter !=
nullptr) {
3568 ?
"DNN: PP-HumanSeg [background]"
3569 :
"DNN: PP-HumanSeg [foreground]",
3573 if (edge_detector !=
nullptr) {
3574 printPreviewText(
"DNN: DexiNed edge", 10, y, dnn_color);
3577 if (generic_onnx_processor !=
nullptr) {
3581 fs::path(
options.onnx_configuration)
3591 const std::string track = fs::path(
3593 ->current_track_path())
3596 if (!track.empty()) {
3597 const SDL_Color track_color{255U, 0U, 255U, 255U};
3605#ifdef ACMXVK_WITH_CUDA
3606 if (gpu_filter_engine !=
nullptr) {
3607 const SDL_Color gpu_color{255U, 0U, 255U, 255U};
3611 gpu_filter_engine->active_filter_description()),
3618 const int remaining =
3620 std::ostringstream status;
3621 status <<
"Autopilot "
3623 if (
options.autopilot_random_timeout > 0) {
3624 status <<
"[4-" <<
options.autopilot_random_timeout
3629 status <<
" next=" << remaining <<
"f";
3634 const SDL_Color autopilot_color{0U, 255U, 255U, 255U};
3640 const SDL_Color status_color{255U, 255U, 255U, 255U};
3647 std::ostringstream fps;
3648 fps <<
"Render: " << std::fixed << std::setprecision(1)
3650 printPreviewText(fps.str(), 10, y, status_color);
3653 std::ostringstream camera_fps;
3654 camera_fps <<
"Camera: ";
3656 camera_fps << std::fixed << std::setprecision(1)
3659 camera_fps <<
"measuring...";
3661 printPreviewText(camera_fps.str(), 10, y, status_color);
3664 const SDL_Color hint_color{128U, 128U, 128U, 255U};
3665 printPreviewText(
"F9: Toggle overlay", 10, y, hint_color);
3675 constexpr int LEFT_MARGIN = 10;
3676 constexpr int TOP_MARGIN = 10;
3682 !
options.watermark_text.empty()
3683 ? preview_line_height
3688 const SDL_Color filter_color{255U, 0U, 255U, 255U};
3695 LEFT_MARGIN, y, filter_color);
3701 LEFT_MARGIN, y, filter_color);
3705 if (!passes.empty()) {
3706 printText(passes, LEFT_MARGIN, y, filter_color);
3709#ifdef ACMXVK_WITH_CUDA
3710 if (gpu_filter_engine !=
nullptr) {
3712 "GPU: " + gpu_filter_engine
3713 ->active_filter_description()),
3714 LEFT_MARGIN, y, filter_color);
3721 const SDL_Color watermark_color{
3723 options.watermark_color[2], 255U};
3725 y, watermark_color);
3730 if (!std::isfinite(value) || value <= 0.0 ||
3731 value >
static_cast<double>(
3732 std::numeric_limits<std::uint32_t>::max())) {
3735 const auto fourcc =
static_cast<std::uint32_t
>(std::llround(value));
3736 std::string name(4,
' ');
3737 for (std::size_t index = 0; index < name.size(); ++index) {
3738 const auto byte =
static_cast<unsigned char>(
3739 (fourcc >> (index * 8U)) & 0xffU);
3740 if (!std::isprint(
byte)) {
3743 name[index] =
static_cast<char>(byte);
3749#ifdef ACMXVK_WITH_DEEP_DREAM
3754#ifdef ACMXVK_WITH_DNN
3755 return edge_detector !=
nullptr || human_segmenter !=
nullptr ||
3756 generic_onnx_processor !=
nullptr;
3763#ifdef ACMXVK_WITH_DEEP_DREAM
3765 !
options.random_dream_specified) {
3769 double timeline = 0.0;
3773 if (!std::isfinite(timeline) || timeline < 0.0) {
3779 std::numeric_limits<std::uint64_t>::max();
3784 const auto period =
static_cast<std::uint64_t
>(
3785 std::floor(timeline /
options.random_dream_interval));
3791 std::uniform_real_distribution<double> strength(0.01, 0.05);
3792 std::uniform_real_distribution<double> feedback(0.55, 0.90);
3793 std::uniform_real_distribution<double> zoom(0.985, 1.015);
3794 std::uniform_real_distribution<double> rotation_magnitude(0.5, 3.0);
3795 std::uniform_real_distribution<double> octave_scale(1.2, 1.6);
3796 std::uniform_int_distribution<int> rotation_direction(0, 1);
3797 std::uniform_int_distribution<int> octaves(1, 8);
3808 std::cout <<
"acmxvk: random dream: strength "
3809 <<
options.dream_strength <<
", feedback "
3810 <<
options.dream_feedback <<
", zoom "
3811 <<
options.dream_zoom <<
", rotation "
3812 <<
options.dream_rotation <<
", octaves "
3813 <<
options.dream_octaves <<
", octave scale "
3814 <<
options.dream_octave_scale <<
'\n';
3819#ifdef ACMXVK_WITH_DEEP_DREAM
3823 if (rgba.type() == CV_16UC4) {
3824 cv::Mat compatible = rgba16ToRgba8(rgba);
3826 std::cout <<
"acmxvk: Deep Dream uses an RGBA8 compatibility "
3827 "copy for HDR input\n";
3831 compatible.convertTo(rgba, CV_16UC4, 257.0);
3840 static_cast<float>(
options.dream_strength),
3841 static_cast<float>(
options.dream_feedback),
3842 static_cast<float>(
options.dream_zoom),
3843 static_cast<float>(
options.dream_rotation),
3846 static_cast<float>(
options.dream_octave_scale),
3848 }
catch (
const std::exception &error) {
3854 "Deep Dream returned a non-finite processed frame");
3858 std::cout <<
"acmxvk: Deep Dream working frame: "
3861 << rgba.rows <<
" source texture ("
3866 static_cast<void>(rgba);
3871#ifdef ACMXVK_WITH_DEEP_DREAM
3872 std::cerr <<
"acmxvk: Deep Dream frame failed: " << message
3873 <<
"; disabling Deep Dream while keeping ACMXVK running\n";
3875#ifdef ACMXVK_WITH_MXVK_CUDA
3876 cuda_dream_rgba.release();
3880 options.gpu_filter_before_dream =
false;
3883 static_cast<void>(message);
3888#ifdef ACMXVK_WITH_DNN
3889 if (rgba.type() == CV_16UC4 &&
3890 (human_segmenter !=
nullptr || edge_detector !=
nullptr ||
3891 generic_onnx_processor !=
nullptr)) {
3892 cv::Mat compatible = rgba16ToRgba8(rgba);
3895 <<
"acmxvk: HDR increment 2: DNN preprocessing uses an "
3896 "RGBA8 compatibility copy before RGBA16 upload\n";
3900 compatible.convertTo(rgba, CV_16UC4, 257.0);
3903 if (human_segmenter !=
nullptr && !rgba.empty()) {
3905 cv::cvtColor(rgba, bgr, cv::COLOR_RGBA2BGR);
3906 const cv::Mat mask = human_segmenter->infer(bgr);
3908 throw std::runtime_error(
3909 "PP-HumanSeg produced an empty person mask");
3911 const float black_point =
3912 static_cast<float>(
options.human_black_point);
3913 const float white_point =
3914 static_cast<float>(
options.human_white_point);
3915 if (
options.human_background) {
3917 bgr, mask, black_point, white_point);
3919 cv::COLOR_BGR2RGBA);
3920 std::vector<cv::Mat> overlay_channels;
3922 alpha.copyTo(overlay_channels[3]);
3926 bgr, mask, black_point, white_point);
3928 cv::subtract(bgr, foreground, background);
3929 cv::cvtColor(background, rgba, cv::COLOR_BGR2RGBA);
3932 bgr, mask, black_point, white_point);
3933 cv::cvtColor(foreground, rgba, cv::COLOR_BGR2RGBA);
3936 if (edge_detector !=
nullptr && !rgba.empty()) {
3940 cv::cvtColor(rgba, bgr, cv::COLOR_RGBA2BGR);
3941 edge_detector->process(bgr, edge);
3943 throw std::runtime_error(
3944 "DexiNed produced an empty edge frame");
3946 if (edge.channels() == 1) {
3947 cv::cvtColor(edge, rgba, cv::COLOR_GRAY2RGBA);
3949 cv::cvtColor(edge, rgba, cv::COLOR_BGR2RGBA);
3951 }
catch (
const std::exception &error) {
3953 <<
"acmxvk: edge inference failed; disabling DNN "
3955 << error.what() <<
'\n';
3956 edge_detector.reset();
3959 if (generic_onnx_processor !=
nullptr && !rgba.empty()) {
3963 cv::cvtColor(rgba, bgr, cv::COLOR_RGBA2BGR);
3964 generic_onnx_processor->process(bgr, processed);
3965 if (processed.empty()) {
3966 throw std::runtime_error(
3967 "generic ONNX model produced an empty frame");
3969 if (processed.channels() == 1) {
3970 cv::cvtColor(processed, rgba,
3971 cv::COLOR_GRAY2RGBA);
3973 cv::cvtColor(processed, rgba,
3974 cv::COLOR_BGR2RGBA);
3976 }
catch (
const std::exception &error) {
3978 <<
"acmxvk: generic ONNX inference failed; disabling "
3980 << error.what() <<
'\n';
3981 generic_onnx_processor.reset();
3985 static_cast<void>(rgba);
3990#ifdef ACMXVK_WITH_DNN
3992 getDevice() == VK_NULL_HANDLE) {
4008 upload.ptr(), upload.cols, upload.rows,
4009 static_cast<int>(upload.step));
4014 if (!
options.graphic_file.empty()) {
4027 bool opened =
false;
4036 : std::to_string(
options.camera_device);
4037 throw std::runtime_error(
"unable to open capture source: " + source);
4044#ifdef MXVK_WITH_FFMPEG_CAPTURE
4047 using_ffmpeg_capture;
4051 if (
options.gpu_filter_before_dream &&
4053 throw std::runtime_error(
4054 "--gpu-filter-before-dream does not support HDR input");
4059 COLOR_TRANSFER_SMPTE2084 ||
4061 COLOR_TRANSFER_ARIB_STD_B67);
4064 COLOR_TRANSFER_ARIB_STD_B67;
4067 std::ostringstream timeline;
4068 timeline <<
"acmxvk: video timeline: " << std::fixed
4075 timeline <<
", duration unavailable";
4077 std::cout << timeline.str() <<
'\n';
4079 std::cout <<
"acmxvk: HDR input metadata detected\n";
4082 std::cout <<
"acmxvk: HDR processing active: native "
4083 "RGBA16 input, RGBA16F effects/history, and "
4084 "normalized RGBA16 Vulkan readback\n";
4087 <<
"acmxvk: HDR transfer: "
4089 <<
" decoded to linear BT.2020 before effects and "
4090 "encoded after effects\n";
4093 <<
"acmxvk: HDR preview: presentation-only "
4094 "BT.2020-to-BT.709 SDR tone mapping active; "
4095 "Main10 recording remains unchanged\n";
4099 <<
"acmxvk: HDR transfer "
4101 <<
" is not PQ or HLG; preserving transfer-encoded "
4102 "values through the precision path\n";
4106 <<
"acmxvk: HDR precision path unavailable because this "
4107 "build is not using MXVK FFmpeg capture; falling "
4113 <<
"acmxvk: source-FPS playback enabled at "
4115 <<
" FPS; early frames wait and late frames are skipped\n";
4122 capture.set(cv::CAP_PROP_BUFFERSIZE, 1.0);
4125 const int requested_fourcc =
options.use_yuv
4126 ? cv::VideoWriter::fourcc(
4128 : cv::VideoWriter::fourcc(
4129 'M',
'J',
'P',
'G');
4130 capture.set(cv::CAP_PROP_FOURCC,
4131 static_cast<double>(requested_fourcc));
4132 if (
options.requested_fps > 0.0) {
4137 std::lround(
capture.get(cv::CAP_PROP_FRAME_WIDTH)));
4139 std::lround(
capture.get(cv::CAP_PROP_FRAME_HEIGHT)));
4146 capture.get(cv::CAP_PROP_FOURCC));
4148 std::cout <<
"acmxvk: camera opened: "
4155 std::cout <<
" at an unreported frame rate";
4157 std::cout <<
", format=" << reported_fourcc <<
'\n';
4161 std::cerr <<
"acmxvk: camera mode warning: requested "
4162 <<
options.camera_width <<
'x'
4163 <<
options.camera_height <<
" but driver reports "
4168 if (
options.requested_fps > 0.0 &&
4172 std::cerr <<
"acmxvk: camera mode warning: requested "
4174 <<
" FPS but driver reports "
4177 const std::string requested_format =
4178 options.use_yuv ?
"YUYV" :
"MJPG";
4179 if (reported_fourcc !=
"unknown" &&
4180 reported_fourcc != requested_format) {
4181 std::cerr <<
"acmxvk: camera mode warning: requested "
4182 << requested_format <<
" but driver reports "
4183 << reported_fourcc <<
'\n';
4188 <<
"acmxvk: maximize FPS active: asynchronous camera "
4189 "capture, Vulkan render target "
4190 <<
options.requested_fps <<
" FPS\n";
4193 <<
"acmxvk: maximize FPS note: VSync may cap the "
4194 "render rate to the display refresh\n";
4201 int source_width =
options.width;
4202 int source_height =
options.height;
4207#ifdef MXVK_WITH_FFMPEG_CAPTURE
4208 if (using_ffmpeg_capture) {
4209 source_width = ffmpeg_capture.width();
4210 source_height = ffmpeg_capture.height();
4220 source_width =
static_cast<int>(
4221 std::lround(
capture.get(cv::CAP_PROP_FRAME_WIDTH)));
4222 source_height =
static_cast<int>(
4223 std::lround(
capture.get(cv::CAP_PROP_FRAME_HEIGHT)));
4226 if (source_width <= 0 || source_height <= 0) {
4235 std::swap(source_width, source_height);
4238 return {source_width, source_height};
4242 int render_width =
options.width;
4243 int render_height =
options.height;
4244 if (!
options.resolution_specified) {
4247 throw std::runtime_error(
4248 "input source dimensions are outside the supported range");
4251 render_width = source_width;
4252 render_height = source_height;
4254 options.height = render_height;
4260 std::cout <<
"acmxvk: automatic output resolution: "
4261 << render_width <<
'x' << render_height <<
" from "
4264 std::cout <<
" after input rotation";
4268 std::cout <<
"acmxvk: requested output resolution: "
4269 << render_width <<
'x' << render_height <<
'\n';
4271 setRenderExtent(
static_cast<std::uint32_t
>(render_width),
4272 static_cast<std::uint32_t
>(render_height));
4275 std::cout <<
"acmxvk: headless output resolution: "
4276 << render_width <<
'x' << render_height <<
'\n';
4281 std::cout <<
"acmxvk: fullscreen presentation uses the display "
4282 "extent without changing the output resolution\n";
4286 SDL_Window *window = getSDLWindow();
4287 if (window ==
nullptr) {
4288 throw std::runtime_error(
4289 "unable to configure preview without an SDL window");
4292 int preview_width = render_width;
4293 int preview_height = render_height;
4294 SDL_Rect usable_bounds{};
4295 SDL_DisplayID display = SDL_GetDisplayForWindow(window);
4297 display = SDL_GetPrimaryDisplay();
4300 SDL_GetDisplayUsableBounds(display, &usable_bounds) &&
4301 usable_bounds.w > 0 && usable_bounds.h > 0) {
4302 constexpr double PREVIEW_DISPLAY_FRACTION = 0.9;
4303 const double width_scale =
4304 (
static_cast<double>(usable_bounds.w) *
4305 PREVIEW_DISPLAY_FRACTION) /
4307 const double height_scale =
4308 (
static_cast<double>(usable_bounds.h) *
4309 PREVIEW_DISPLAY_FRACTION) /
4311 const double preview_scale =
4312 std::min({1.0, width_scale, height_scale});
4313 preview_width = std::max(
4314 1,
static_cast<int>(std::lround(render_width * preview_scale)));
4315 preview_height = std::max(
4316 1,
static_cast<int>(std::lround(render_height * preview_scale)));
4319 const float render_aspect =
static_cast<float>(render_width) /
4320 static_cast<float>(render_height);
4321 if (!SDL_SetWindowAspectRatio(window, render_aspect,
4323 std::cerr <<
"acmxvk: unable to lock preview aspect ratio: "
4324 << SDL_GetError() <<
'\n';
4326 if (!SDL_SetWindowSize(window, preview_width, preview_height)) {
4327 throw std::runtime_error(
4328 std::string(
"unable to apply preview resolution: ") +
4331 SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED,
4332 SDL_WINDOWPOS_CENTERED);
4333 if (!SDL_SyncWindow(window)) {
4334 std::cerr <<
"acmxvk: window resize sync warning: "
4335 << SDL_GetError() <<
'\n';
4338 int actual_width = 0;
4339 int actual_height = 0;
4340 SDL_GetWindowSizeInPixels(window, &actual_width, &actual_height);
4341 std::cout <<
"acmxvk: preview resolution: " << actual_width <<
'x'
4343 if (preview_width != render_width ||
4344 preview_height != render_height) {
4345 std::cout <<
" (" << render_width <<
'x' << render_height
4346 <<
" output, preview fitted to display)";
4352 if (
options.requested_fps > 0.0) {
4356 double source_fps = 0.0;
4357#ifdef MXVK_WITH_FFMPEG_CAPTURE
4358 if (using_ffmpeg_capture) {
4359 source_fps = ffmpeg_capture.fps();
4363 source_fps =
capture.get(cv::CAP_PROP_FPS);
4365 if (std::isfinite(source_fps) && source_fps > 0.0) {
4373#ifndef ACMXVK_WITH_TIFF
4375 std::cerr <<
"acmxvk: TIFF snapshots require a build configured "
4380#ifndef ACMXVK_WITH_WEBP
4382 std::cerr <<
"acmxvk: WebP snapshots require a build configured "
4391 std::cerr <<
"acmxvk: snapshot queue is full; request ignored\n";
4394 std::error_code error;
4395 const fs::path directory(
options.snapshot_directory);
4396 fs::create_directories(directory, error);
4397 if (error || !fs::is_directory(directory)) {
4398 std::cerr <<
"acmxvk: unable to create snapshot directory: "
4399 << directory.string() <<
'\n';
4407 setFrameReadbackEnabled(
true);
4409 <<
" snapshot requested\n";
4414 options.generate_interval > 0;
4418 if (
options.output_file.empty() &&
options.generate_interval <= 0) {
4422 const VkExtent2D extent = getRenderExtent();
4423 if (
options.resolution_specified) {
4425 ?
static_cast<int>(extent.width)
4428 ?
static_cast<int>(extent.height)
4440 std::cout <<
"acmxvk: writing PNG sequence to "
4444 if (
options.generate_interval > 0) {
4445 if (!
options.output_file.empty()) {
4449 }
else if (!
options.input_file.empty()) {
4457 std::cout <<
"acmxvk: saving every " <<
options.generate_interval
4462 EncodeOptions encode_options;
4463 encode_options.preset =
options.encode_preset;
4464 encode_options.tune =
options.encode_tune;
4465 encode_options.crf =
options.encode_crf;
4466 encode_options.bit_rate =
options.encode_bitrate;
4467 encode_options.codec =
options.encode_codec;
4468 encode_options.ffmpeg_options =
options.encode_params;
4469 encode_options.realtime =
options.encode_realtime;
4470 encode_options.block_when_full =
options.no_drop;
4475 throw std::runtime_error(
4476 "HDR Main10 output requires even width and height");
4478 encode_options.hdr.enabled =
true;
4479 encode_options.hdr.color_primaries =
4481 encode_options.hdr.color_trc =
4483 encode_options.hdr.color_space =
4485 encode_options.hdr.color_range =
4487 encode_options.hdr.mastering_display =
4489 encode_options.hdr.content_light =
4492 <<
"acmxvk: HDR output: HEVC Main10 with captured "
4494 <<
" color metadata (software libx265)\n";
4497 if (
options.encode_bitrate > 0) {
4498 std::cout <<
"acmxvk: encoder rate control: target VBR "
4499 <<
options.encode_bitrate <<
" bits/s\n";
4501 std::cout <<
"acmxvk: encoder rate control: CRF/CQ "
4502 <<
options.encode_crf <<
'\n';
4507 throw std::runtime_error(
"unable to open output video: " +
4514 << (
options.no_drop ?
" (no-drop)\n" :
"\n");
4517 <<
"acmxvk: recorded video audio disabled (--mute-output); "
4518 "reactivity and pass-through remain active\n";
4522 setFrameReadbackEnabled(
true);
4538 setFrameReadbackEnabled(
false);
4549 std::vector<std::uint8_t> &rgba,
4550 const std::vector<std::uint16_t> *rgba16, uint32_t width,
4553 std::cerr <<
"acmxvk: received frame readback without queued metadata\n";
4568 if (rgba16 !=
nullptr &&
4576 job.
rgba = std::move(rgba);
4580 std::cout <<
"acmxvk: queued "
4582 <<
" snapshot: " << path.string() <<
'\n';
4590 std::uint8_t *output_pixels = rgba.data();
4592 const std::uint16_t *hdr_output_pixels =
4593 rgba16 !=
nullptr ? rgba16->data() :
nullptr;
4594 cv::Mat hdr_resized;
4597 const cv::Mat source(
static_cast<int>(height),
static_cast<int>(width),
4598 CV_8UC4, rgba.data());
4600 0.0, 0.0, cv::INTER_LINEAR);
4601 output_pixels = resized.ptr();
4602 if (rgba16 !=
nullptr) {
4603 const cv::Mat hdr_source(
4604 static_cast<int>(height),
static_cast<int>(width),
4606 const_cast<std::uint16_t *
>(rgba16->data()));
4607 cv::resize(hdr_source, hdr_resized,
4609 0.0, cv::INTER_LINEAR);
4610 hdr_output_pixels = hdr_resized.ptr<std::uint16_t>();
4616 if (hdr_output_pixels ==
nullptr) {
4617 throw std::runtime_error(
4618 "HDR Main10 recording did not receive an RGBA16 "
4622 writer.write_hdr_rgba16_at_pts(
4623 const_cast<std::uint16_t *
>(hdr_output_pixels),
4624 static_cast<std::int64_t
>(request.
pts));
4627 const_cast<std::uint16_t *
>(hdr_output_pixels));
4630 writer.write_at_pts(output_pixels,
4631 static_cast<std::int64_t
>(request.
pts));
4633 writer.write(output_pixels);
4642 if (
options.generate_interval > 0 &&
4644 static_cast<std::uint64_t
>(
options.generate_interval) ==
4656 double output_duration = 0.0;
4660 }
else if (
writer.is_open()) {
4661 output_duration =
writer.get_duration();
4666 if (output_duration >=
options.duration) {
4674 const double maximum_bytes =
options.max_size_mb * 1024.0 * 1024.0;
4675 if (
static_cast<double>(
writer.get_bytes_written()) >=
4677 std::cout <<
"acmxvk: maximum output size reached ("
4678 <<
options.max_size_mb <<
" MB)\n";
4686 std::vector<std::uint16_t> &rgba, uint32_t width, uint32_t height) {
4689 <<
"acmxvk: HDR readback: normalized RGBA16 received from "
4690 "the final Vulkan HDR intermediate"
4692 ?
"; feeding MXWrite's HEVC Main10 encoder\n"
4693 :
"; converting to RGBA8 for snapshots/output\n");
4696 std::vector<std::uint8_t> rgba8 =
4717 std::cout <<
"acmxvk: loaded 3D model: "
4722 <<
" indices; skybox camera centered; view rotation "
4726 if (getDevice() != VK_NULL_HANDLE) {
4727 vkDeviceWaitIdle(getDevice());
4735 if (!ensureRenderResources()) {
4736 throw std::runtime_error(
"MXVK failed to initialize render resources");
4742 frame_sprite = createSprite(source_width, source_height);
4752 static_cast<std::uint32_t
>(
options.audio_buffers));
4757 source_width, source_height,
4758 static_cast<uint32_t
>(
options.texture_cache_size));
4761 source_width, source_height,
4762 static_cast<uint32_t
>(
options.texture_cache_size));
4765 const std::string initial_fragment =
4773 source_width, source_height,
4777 source_width, source_height,
4781 if (
options.human_background &&
4785 static_cast<std::uint32_t
>(source_width),
4786 static_cast<std::uint32_t
>(source_height), 1U);
4787 const cv::Mat transparent(source_height, source_width,
4788 CV_8UC4, cv::Scalar::all(0));
4790 transparent.ptr(), transparent.cols, transparent.rows,
4791 static_cast<int>(transparent.step));
4802 std::cerr <<
"acmxvk: capture did not provide an initial frame\n";
4825 getDevice() == VK_NULL_HANDLE) {
4833 std::vector<std::uint8_t> captured;
4834 std::uint32_t captured_width = 0;
4835 std::uint32_t captured_height = 0;
4836 captureSnapshotPixels(captured, captured_width,
4838 const VkExtent2D extent = getRenderExtent();
4839 if (captured.empty() || captured_width == 0U ||
4840 captured_height == 0U || extent.width == 0U ||
4841 extent.height == 0U) {
4842 throw std::runtime_error(
4843 "the previous rendered frame is unavailable");
4846 cv::Mat captured_rgba(
static_cast<int>(captured_height),
4847 static_cast<int>(captured_width),
4848 CV_8UC4, captured.data());
4849 cv::Mat previous_rgba;
4850 if (captured_width == extent.width &&
4851 captured_height == extent.height) {
4852 previous_rgba = captured_rgba;
4854 const double captured_aspect =
4855 static_cast<double>(captured_width) / captured_height;
4856 const double target_aspect =
4857 static_cast<double>(extent.width) / extent.height;
4858 cv::Rect crop(0, 0,
static_cast<int>(captured_width),
4859 static_cast<int>(captured_height));
4860 if (captured_aspect > target_aspect) {
4861 crop.width = std::max(
4862 1,
static_cast<int>(std::lround(
4863 captured_height * target_aspect)));
4865 (
static_cast<int>(captured_width) - crop.width) / 2;
4866 }
else if (captured_aspect < target_aspect) {
4867 crop.height = std::max(
4868 1,
static_cast<int>(std::lround(
4869 captured_width / target_aspect)));
4870 crop.y = (
static_cast<int>(captured_height) -
4874 cv::resize(captured_rgba(crop), previous_rgba,
4875 cv::Size(
static_cast<int>(extent.width),
4876 static_cast<int>(extent.height)),
4877 0.0, 0.0, cv::INTER_LINEAR);
4885 extent.width, extent.height, 1U);
4888 extent.width, extent.height, 1U);
4891 const cv::Mat linear_previous =
4894 linear_previous.ptr<std::uint16_t>(),
4895 static_cast<int>(extent.width),
4896 static_cast<int>(extent.height),
4897 static_cast<int>(linear_previous.step));
4900 previous_rgba.ptr(),
static_cast<int>(extent.width),
4901 static_cast<int>(extent.height),
4902 static_cast<int>(previous_rgba.step));
4909 }
catch (
const std::exception &error) {
4913 std::cerr <<
"acmxvk: crossfade snapshot unavailable: "
4914 << error.what() <<
"; switching immediately\n";
4922 double elapsed = 0.0;
4923 double video_timeline = 0.0;
4931 elapsed = std::chrono::duration<double>(
4936 elapsed /
options.cross_fade_duration, 0.0, 1.0));
4949 index = (index % count + count) % count;
4951 std::cout <<
"acmxvk: crossfade shader: "
4962 std::cout <<
"acmxvk: model scale " <<
model_scale <<
'\n';
4969 std::uniform_int_distribution<std::size_t> distribution(
4981 std::cout <<
"acmxvk: pause is available for video and graphic input\n";
4986 std::cout <<
"acmxvk: input pause "
4992 std::cout <<
"acmxvk: freeze is available for video and graphic input\n";
4998 std::cout <<
"acmxvk: rendering freeze "
5004 std::cout <<
"acmxvk: shader time stepped to " <<
shader_time <<
'\n';
5009 if (std::abs(
options.time_speed) < 0.01) {
5012 std::cout <<
"acmxvk: shader time speed " <<
options.time_speed <<
'\n';
5016 SDL_Window *window = getSDLWindow();
5017 if (window ==
nullptr) {
5020 const bool fullscreen =
5021 (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) != 0;
5022 if (!SDL_SetWindowFullscreen(window, !fullscreen)) {
5023 std::cerr <<
"acmxvk: unable to toggle fullscreen: "
5024 << SDL_GetError() <<
'\n';
5027 std::cout <<
"acmxvk: fullscreen "
5028 << (!fullscreen ?
"enabled" :
"disabled") <<
'\n';
5032 if (
options.autopilot_random_timeout > 0) {
5033 std::uniform_int_distribution<int> distribution(
5034 4, std::max(4,
options.autopilot_random_timeout));
5045 std::cout <<
"acmxvk: " << action <<
" playlist node "
5053 double video_timeline = 0.0;
5054 std::uint64_t video_frame_index = 0U;
5067 const std::uint64_t advance =
5075 std::cout <<
"acmxvk: "
5076 << (sequential ?
"sequential autopilot" :
"autopilot")
5077 <<
" requires playlist mode (press P first)\n";
5081 std::cout <<
"acmxvk: autopilot has no playlist entries\n";
5088 std::cout <<
"acmxvk: autopilot disabled\n";
5096 if (
options.autopilot_random_timeout <= 0 &&
options.autopilot_frames <= 0) {
5097 options.autopilot_frames = 300;
5100 std::cout <<
"acmxvk: " << (sequential ?
"sequential " :
"random ")
5101 <<
"autopilot enabled (";
5102 if (
options.autopilot_random_timeout > 0) {
5103 std::cout <<
"random interval 4-" <<
options.autopilot_random_timeout
5117 const std::uint64_t remaining =
static_cast<std::uint64_t
>(
5119 if (frame_advance < remaining) {
5130 std::uniform_int_distribution<std::size_t> distribution(0,
5134 next = (next + 1) %
playlist.size();
5141 if (
options.autopilot_random_timeout > 0) {
5151 const auto count =
static_cast<std::ptrdiff_t
>(
shaders.size());
5153 auto index =
static_cast<std::ptrdiff_t
>(
shader_index) + direction;
5154 index = (index % count + count) % count;
5169 const auto count =
static_cast<std::ptrdiff_t
>(
playlist.size());
5171 auto index =
static_cast<std::ptrdiff_t
>(
playlist_index) + direction;
5172 index = (index % count + count) % count;
5181 std::vector<fs::path> pipeline;
5196 pipeline.emplace_back(
5199 if (pipeline.empty()) {
5202 if (
options.human_background) {
5205 for (fs::path &shader : pipeline)
5219 const mxvk::ShaderModuleInfo module_info =
5220 mxvk::inspect_spirv(mxvk::load_spv(shader.string()));
5221 if (module_info.stage != mxvk::ShaderStage::Fragment ||
5222 module_info.usesHistoryTexture ||
5223 module_info.usesSpectrumTexture ||
5224 module_info.usesSpectrumHistoryTexture) {
5231 if (getDevice() == VK_NULL_HANDLE) {
5234 vkDeviceWaitIdle(getDevice());
5235 detachPostProcessingShader();
5237 setPostProcessingPresentFragmentShader(
5246 setPostProcessingTextureConsumerEnabled(
5252 ? getSwapchainFormat()
5253 : getSceneColorFormat());
5254 const fs::path desired_model_shader =
5255 direct_model_shader.empty()
5257 : direct_model_shader;
5261 desired_model_shader.string());
5267 if (!direct_model_shader.empty()) {
5268 const auto selected = std::find(
5269 pipeline.begin(), pipeline.end(), direct_model_shader);
5270 if (selected != pipeline.end()) {
5271 pipeline.erase(selected);
5273 if (pipeline.empty()) {
5276 std::cout <<
"acmxvk: 3D texture effect: "
5277 << direct_model_shader.filename().string()
5278 <<
" [fragment, evaluated on model UVs]\n";
5281 std::cout <<
"acmxvk: 3D texture prepass: fragment/compute "
5282 "chain output mapped onto model UVs\n";
5288 pipeline.emplace_back(
5291 if (pipeline.empty()) {
5295 std::vector<PostProcessingEffect> effects;
5296 effects.reserve(pipeline.size());
5298 std::numeric_limits<std::size_t>::max();
5299 for (std::size_t index = 0; index < pipeline.size(); ++index) {
5300 const fs::path &shader = pipeline[index];
5301 PostProcessingEffect effect{
5302 shader.string(), {1.0F, 1.0F, 1.0F, 0.0F},
false};
5309 }
else if (
options.human_background &&
5319 effect.spectrumHistoryLayerCount =
5320 static_cast<std::uint32_t
>(
options.audio_buffers);
5322 effects.push_back(effect);
5326 sprite->enableExtendedUBO();
5332 sprite->enableSpectrumHistoryTexture(
5334 static_cast<std::uint32_t
>(
options.audio_buffers));
5338 std::cout <<
"acmxvk: Vulkan shader pipeline (" << pipeline.size() <<
" passes):\n";
5339 for (std::size_t index = 0; index < pipeline.size(); ++index) {
5340 const bool compute =
5341 index < post_process_effect_stages.size() &&
5342 post_process_effect_stages[index] ==
5343 mxvk::ShaderStage::Compute;
5344 std::cout <<
" " << (index + 1) <<
": "
5345 << pipeline[index].filename().string() <<
" ["
5346 << (compute ?
"compute" :
"fragment") <<
"]\n";
5367 bool skipped =
false;
5368#ifdef MXVK_WITH_FFMPEG_CAPTURE
5369 if (using_ffmpeg_capture) {
5370 skipped = ffmpeg_capture.skip();
5388 setFrameReadbackEnabled(
false);
5394#ifdef MXVK_WITH_FFMPEG_CAPTURE
5395 if (using_ffmpeg_capture && ffmpeg_capture.seek_start()) {
5397 const bool restarted =
5400 if (!ffmpeg_seek_repeat_logged) {
5402 <<
"acmxvk: video repeat: in-place FFmpeg seek; "
5403 << (ffmpeg_capture.using_hardware_decode()
5404 ?
"NVDEC decoder and CUDA device preserved\n"
5405 :
"software decoder preserved\n");
5406 ffmpeg_seek_repeat_logged =
true;
5410 std::cerr <<
"acmxvk: in-place FFmpeg repeat did not produce a "
5411 "frame; reopening the input\n";
5417 throw std::runtime_error(
"unable to restart video input: " +
options.input_file);
5424 if (!std::isfinite(rate) || rate <= 0.0) {
5428 std::uint64_t target_frame =
static_cast<std::uint64_t
>(
5429 std::floor(std::max(clock_seconds, 0.0) * rate));
5431 const double next_frame_time =
5433 const double wait_seconds = next_frame_time - clock_seconds;
5434 if (wait_seconds > 0.0) {
5435 std::this_thread::sleep_for(
5436 std::chrono::duration<double>(wait_seconds));
5438 double updated_clock = 0.0;
5440 target_frame =
static_cast<std::uint64_t
>(
5441 std::floor(std::max(updated_clock, 0.0) * rate));
5448 const std::uint64_t frames_to_advance =
5450 for (std::uint64_t frame = 0; frame < frames_to_advance; ++frame) {
5451 const bool discard = frame + 1 < frames_to_advance;
5467 std::cout <<
"acmxvk: media-clock synchronization active; "
5468 "late video frames will be skipped and encoded "
5469 "with timeline PTS\n";
5476#ifdef MXVK_WITH_FFMPEG_CAPTURE
5477 if (ffmpeg_capture.is_open()) {
5478 ffmpeg_capture.close();
5480 using_ffmpeg_capture =
false;
5490#ifdef MXVK_WITH_FFMPEG_CAPTURE
5491 if (ffmpeg_capture.open(
options.input_file,
options.cuda_device)) {
5492 using_ffmpeg_capture =
true;
5494 std::cout <<
"acmxvk: video capture: FFmpeg ";
5495 if (ffmpeg_capture.using_hardware_decode()) {
5496 std::cout <<
"with CUDA/NVDEC";
5497 if (ffmpeg_capture.hardware_decode_device() >= 0) {
5498 std::cout <<
" on device "
5499 << ffmpeg_capture.hardware_decode_device();
5503 std::cout <<
"software decode\n";
5511 std::cout <<
"acmxvk: video capture: OpenCV fallback\n";
5520#ifdef MXVK_WITH_FFMPEG_CAPTURE
5521 if (using_ffmpeg_capture) {
5526 if (!ffmpeg_capture.readRgba16(ffmpeg_rgba16, width, height,
5528 ffmpeg_rgba16.empty() || width <= 0 || height <= 0 ||
5529 pitch < width * 8) {
5532 rgba = cv::Mat(height, width, CV_16UC4,
5533 ffmpeg_rgba16.data(),
5534 static_cast<std::size_t
>(pitch));
5537 if (!ffmpeg_capture.readRgba(ffmpeg_rgba, width, height, pitch,
5539 ffmpeg_rgba.empty() || width <= 0 || height <= 0 ||
5540 pitch < width * 4) {
5543 rgba = cv::Mat(height, width, CV_8UC4, ffmpeg_rgba.data(),
5544 static_cast<std::size_t
>(pitch));
5548 return capture.readRgba(rgba,
false);
5555 for (uint32_t layer = 0; layer <
frame_sprite->getHistoryLayerCount(); ++layer) {
5561 std::cout <<
"acmxvk: initialized " <<
frame_sprite->getHistoryLayerCount()
5562 <<
" Vulkan history-cache layers (delay " <<
options.cache_delay
5567#ifdef ACMXVK_WITH_CUDA
5568 if (gpu_filter_engine !=
nullptr && rgba.type() == CV_8UC4) {
5569 updateFilteredCudaHistoryFrame();
5573 if (rgba.type() == CV_16UC4) {
5575 const cv::Mat linear_history =
5578 linear_history.ptr<std::uint16_t>(), linear_history.cols,
5579 linear_history.rows,
5580 static_cast<int>(linear_history.step));
5584 rgba.ptr<uint16_t>(), rgba.cols, rgba.rows,
5585 static_cast<int>(rgba.step));
5588 frame_sprite->updateHistoryTexture(rgba.ptr(), rgba.cols, rgba.rows,
5589 static_cast<int>(rgba.step));
5599 if (!std::isfinite(rate) || rate <= 0.0) {
5602 const auto interval = std::chrono::duration_cast<
5603 std::chrono::steady_clock::duration>(std::chrono::duration<double>(
5604 static_cast<double>(
options.cache_delay + 1) / rate));
5605 const auto now = std::chrono::steady_clock::now();
5615 bool history_updated =
false;
5616#if defined(ACMXVK_WITH_CUDA) && defined(ACMXVK_WITH_DEEP_DREAM) && \
5617 defined(ACMXVK_WITH_MXVK_CUDA)
5618 if (
options.gpu_filter_before_dream &&
5620 !cuda_dream_rgba.empty()) {
5621 const cv::cuda::GpuMat &history_input =
5624 : cuda_rotated_rgba;
5625 updateCudaHistoryFrame(history_input,
5626 gpu_filter_engine->stream());
5627 history_updated =
true;
5630#ifdef ACMXVK_WITH_CUDA
5631 if (!history_updated && gpu_filter_engine !=
nullptr) {
5632 updateFilteredCudaHistoryFrame();
5633 history_updated =
true;
5638 history_updated =
true;
5640 if (!history_updated) {
5650#ifdef ACMXVK_WITH_MXVK_CUDA
5651 void MainWindow::updateModelTextureCuda(
const cv::cuda::GpuMat &rgba,
5652 cv::cuda::Stream &source_stream) {
5653 if (!model_initialized) {
5656 if (input_model.updatePrimaryTextureCuda(rgba, source_stream)) {
5660 rgba.download(cuda_model_fallback_rgba, source_stream);
5661 source_stream.waitForCompletion();
5662 if (!cuda_model_fallback_logged) {
5663 std::cerr <<
"acmxvk: direct CUDA model-texture upload "
5664 "unavailable; using host staging\n";
5665 cuda_model_fallback_logged =
true;
5667 if (!input_model.updatePrimaryTexture(
5668 cuda_model_fallback_rgba.ptr(),
5669 cuda_model_fallback_rgba.cols,
5670 cuda_model_fallback_rgba.rows,
5671 static_cast<int>(cuda_model_fallback_rgba.step))) {
5672 throw std::runtime_error(
5673 "MXVK could not update the 3D model texture");
5677 void MainWindow::updateCudaHistoryFrame(
const cv::cuda::GpuMat &rgba,
5678 cv::cuda::Stream &source_stream) {
5679 if (frame_sprite->updateHistoryTextureCuda(rgba, source_stream)) {
5683 rgba.download(cuda_history_fallback_rgba, source_stream);
5684 source_stream.waitForCompletion();
5685 if (!cuda_history_fallback_logged) {
5686 std::cerr <<
"acmxvk: direct CUDA history upload unavailable; "
5687 "using a host-staging fallback\n";
5688 cuda_history_fallback_logged =
true;
5690 frame_sprite->updateHistoryTexture(
5691 cuda_history_fallback_rgba.ptr(),
5692 cuda_history_fallback_rgba.cols,
5693 cuda_history_fallback_rgba.rows,
5694 static_cast<int>(cuda_history_fallback_rgba.step));
5697#ifdef ACMXVK_WITH_CUDA
5698 void MainWindow::updateFilteredCudaHistoryFrame() {
5699 updateCudaHistoryFrame(gpu_filter_engine->output(),
5700 gpu_filter_engine->stream());
5704 void MainWindow::initializeCudaHistory(
const cv::cuda::GpuMat &rgba,
5705 cv::cuda::Stream &source_stream,
5707 if (!historyCacheEnabled() || history_initialized) {
5710 for (uint32_t layer = 0;
5711 layer < frame_sprite->getHistoryLayerCount(); ++layer) {
5712 updateCudaHistoryFrame(rgba, source_stream);
5714 history_initialized =
true;
5715 history_delay_counter = 0;
5716 camera_history_clock_started =
false;
5717 std::cout <<
"acmxvk: initialized "
5718 << frame_sprite->getHistoryLayerCount()
5719 << (filtered ?
" filtered" :
" NVDEC")
5720 <<
" Vulkan history-cache layers (delay "
5721 << options.cache_delay <<
")\n";
5724#ifdef ACMXVK_WITH_CUDA
5725 void MainWindow::uploadInputFrame(
const cv::cuda::GpuMat &rgba,
5726 cv::cuda::Stream &source_stream) {
5727 if (!gpu_filter_engine->process(rgba, source_stream)) {
5728 throw std::runtime_error(
5729 "acidcam-gpu rejected the CUDA RGBA input frame");
5731 if (!frame_sprite->updateTextureCuda(
5732 gpu_filter_engine->output(),
5733 gpu_filter_engine->stream())) {
5734 throw std::runtime_error(
5735 "MXVK could not upload the CUDA-filtered frame");
5737 updateModelTextureCuda(gpu_filter_engine->output(),
5738 gpu_filter_engine->stream());
5742 [[nodiscard]]
const cv::cuda::GpuMat &
5743 MainWindow::rotateCudaFrame(
const cv::cuda::GpuMat &rgba,
5744 cv::cuda::Stream &source_stream) {
5745 switch (options.frame_rotation) {
5749 cv::cuda::transpose(rgba, cuda_rotation_transpose, source_stream);
5750 cv::cuda::flip(cuda_rotation_transpose, cuda_rotated_rgba, 1,
5754 cv::cuda::flip(rgba, cuda_rotated_rgba, -1, source_stream);
5757 cv::cuda::transpose(rgba, cuda_rotation_transpose, source_stream);
5758 cv::cuda::flip(cuda_rotation_transpose, cuda_rotated_rgba, 0,
5762 return cuda_rotated_rgba;
5767#ifdef ACMXVK_WITH_CUDA
5768 if (gpu_filter_engine !=
nullptr && rgba.type() == CV_8UC4) {
5769 if (!gpu_filter_engine->process(rgba)) {
5770 throw std::runtime_error(
5771 "acidcam-gpu rejected the RGBA input frame");
5774 gpu_filter_engine->output(),
5775 gpu_filter_engine->stream())) {
5776 throw std::runtime_error(
5777 "MXVK could not upload the CUDA-filtered frame");
5779 updateModelTextureCuda(gpu_filter_engine->output(),
5780 gpu_filter_engine->stream());
5783 if (gpu_filter_engine !=
nullptr && rgba.type() == CV_16UC4 &&
5786 <<
"acmxvk: HDR increment 2: bypassing RGBA8 CUDA filters to "
5787 "preserve the 16-bit source texture\n";
5791 if (rgba.type() == CV_16UC4) {
5793 rgba.ptr<std::uint16_t>(), rgba.cols, rgba.rows,
5794 static_cast<int>(rgba.step));
5796 std::cout <<
"acmxvk: uploaded first RGBA16 HDR source frame "
5801 frame_sprite->updateTexture(rgba.ptr(), rgba.cols, rgba.rows,
5802 static_cast<int>(rgba.step));
5805 cv::Mat model_compatible;
5806 const cv::Mat *model_input = &rgba;
5808 model_compatible = rgba16ToRgba8(rgba);
5809 model_input = &model_compatible;
5813 model_input->ptr(), model_input->cols, model_input->rows,
5814 static_cast<int>(model_input->step))) {
5815 throw std::runtime_error(
5816 "MXVK could not update the 3D model texture");
5830 cv::cvtColor(bgr, rgba, cv::COLOR_BGR2RGBA);
5842#ifdef ACMXVK_WITH_CUDA
5843 if (gpu_filter_engine !=
nullptr) {
5844 initializeCudaHistory(gpu_filter_engine->output(),
5845 gpu_filter_engine->stream(),
true);
5854#if defined(ACMXVK_WITH_MXVK_CUDA) && defined(ACMXVK_WITH_DEEP_DREAM)
5855 [[nodiscard]]
bool MainWindow::readCudaDeepDreamFrame() {
5856 cv::cuda::Stream *capture_stream =
nullptr;
5857#ifdef MXVK_WITH_FFMPEG_CAPTURE
5858 if (using_ffmpeg_capture) {
5859 if (!ffmpeg_capture.readGpuRgba(cuda_input_rgba,
5860 ffmpeg_cuda_stream,
false)) {
5863 capture_stream = &ffmpeg_cuda_stream;
5867 if (!capture.readGpuRgba(cuda_input_rgba,
false)) {
5870 capture_stream = &capture.cudaStream();
5873 const cv::cuda::GpuMat *dream_input = &cuda_input_rgba;
5874 cv::cuda::Stream *dream_stream = capture_stream;
5875 bool filtered =
false;
5876 const bool filter_before_dream = options.gpu_filter_before_dream;
5877#ifdef ACMXVK_WITH_CUDA
5878 if (filter_before_dream && gpu_filter_engine !=
nullptr) {
5879 if (!gpu_filter_engine->process(cuda_input_rgba,
5881 throw std::runtime_error(
5882 "acidcam-gpu rejected the pre-dream CUDA frame");
5884 dream_input = &gpu_filter_engine->output();
5885 dream_stream = &gpu_filter_engine->stream();
5890 updateRandomDreamSettings();
5891 dream::GradientAscentResult dream_result;
5892 const cv::cuda::GpuMat *processed_input = dream_input;
5893 bool dream_processed =
false;
5896 *dream_input, cuda_dream_rgba, *dream_stream,
5897 dream::GradientAscentOptions{
5898 options.dream_iterations,
5899 static_cast<float>(options.dream_strength),
5900 static_cast<float>(options.dream_feedback),
5901 static_cast<float>(options.dream_zoom),
5902 static_cast<float>(options.dream_rotation),
5903 options.dream_size, options.dream_channel,
5904 options.dream_octaves,
5905 static_cast<float>(options.dream_octave_scale),
5906 options.dream_jitter, options.dream_smoothing});
5908 std::isfinite(dream_result.mean_pixel_change);
5909 if (dream_processed) {
5910 processed_input = &cuda_dream_rgba;
5912 handleDeepDreamRuntimeError(
5913 "Deep Dream returned a non-finite CUDA frame");
5915 }
catch (
const std::exception &error) {
5916 handleDeepDreamRuntimeError(error.what());
5919 const cv::cuda::GpuMat &render_input =
5920 rotateCudaFrame(*processed_input, *dream_stream);
5921 const cv::cuda::GpuMat *final_input = &render_input;
5922 cv::cuda::Stream *final_stream = dream_stream;
5923#ifdef ACMXVK_WITH_CUDA
5924 if (!filtered && gpu_filter_engine !=
nullptr) {
5925 if (!gpu_filter_engine->process(render_input, *dream_stream)) {
5926 throw std::runtime_error(
5927 "acidcam-gpu rejected the post-dream CUDA frame");
5929 final_input = &gpu_filter_engine->output();
5930 final_stream = &gpu_filter_engine->stream();
5934 if (!frame_sprite->updateTextureCuda(*final_input, *final_stream)) {
5935 final_input->download(cuda_input_fallback_rgba, *final_stream);
5936 final_stream->waitForCompletion();
5937 if (!cuda_input_fallback_logged) {
5939 <<
"acmxvk: direct Deep Dream/Vulkan upload "
5940 "unavailable; using host staging\n";
5941 cuda_input_fallback_logged =
true;
5943 frame_sprite->updateTexture(
5944 cuda_input_fallback_rgba.ptr(), cuda_input_fallback_rgba.cols,
5945 cuda_input_fallback_rgba.rows,
5946 static_cast<int>(cuda_input_fallback_rgba.step));
5948 updateModelTextureCuda(*final_input, *final_stream);
5950 if (dream_processed && !dream_processing_logged) {
5951 std::cout <<
"acmxvk: Deep Dream CUDA working frame: "
5952 << dream_result.processed_width <<
'x'
5953 << dream_result.processed_height <<
" -> "
5954 << render_input.cols <<
'x' << render_input.rows
5955 <<
" Vulkan texture ("
5956 << dream_result.processed_octaves <<
" octave(s))\n";
5957 dream_processing_logged =
true;
5959 if (!cuda_input_path_logged) {
5961 <<
"acmxvk: CUDA interop path active: capture/NVDEC -> ";
5962 if (filter_before_dream && filtered) {
5963 std::cout <<
"acidcam-gpu -> ";
5965 if (dream_processed) {
5966 std::cout <<
"LibTorch Deep Dream -> ";
5969 std::cout <<
"CUDA rotation -> ";
5971 if (!filter_before_dream && filtered) {
5972 std::cout <<
"acidcam-gpu -> ";
5974 std::cout <<
"Vulkan texture";
5975 if (cuda_input_fallback_logged) {
5976 std::cout <<
" (host-staging upload fallback)";
5979 cuda_input_path_logged =
true;
5982 const bool history_was_initialized = history_initialized;
5983 initializeCudaHistory(*final_input, *final_stream, filtered);
5984 if (source_kind != SourceKind::Camera && history_was_initialized &&
5985 ++history_delay_counter > options.cache_delay) {
5986 updateCudaHistoryFrame(*final_input, *final_stream);
5987 history_delay_counter = 0;
5989 if (source_kind == SourceKind::Camera) {
5990 camera_history_clock_started =
false;
6000#if defined(ACMXVK_WITH_MXVK_CUDA) && defined(ACMXVK_WITH_DEEP_DREAM)
6002#ifdef ACMXVK_WITH_DNN
6003 && edge_detector ==
nullptr && human_segmenter ==
nullptr &&
6004 generic_onnx_processor ==
nullptr
6007 return readCudaDeepDreamFrame();
6010#ifdef ACMXVK_WITH_CUDA
6013 cv::cuda::Stream *capture_stream =
nullptr;
6014#ifdef MXVK_WITH_FFMPEG_CAPTURE
6015 if (using_ffmpeg_capture) {
6016 if (!ffmpeg_capture.readGpuRgba(cuda_input_rgba,
6017 ffmpeg_cuda_stream,
false)) {
6020 capture_stream = &ffmpeg_cuda_stream;
6024 if (!
capture.readGpuRgba(cuda_input_rgba,
false)) {
6027 capture_stream = &
capture.cudaStream();
6029 const cv::cuda::GpuMat &filter_input =
6030 rotateCudaFrame(cuda_input_rgba, *capture_stream);
6032 if (!cuda_input_path_logged) {
6033#ifdef MXVK_WITH_FFMPEG_CAPTURE
6034 if (using_ffmpeg_capture) {
6035 std::cout <<
"acmxvk: CUDA input path active: FFmpeg "
6036 << (ffmpeg_capture.using_hardware_decode()
6037 ?
"NVDEC -> CUDA RGBA -> "
6038 :
"software decode -> CUDA upload -> ");
6043 <<
"acmxvk: CUDA input path active: MXVK capture -> ";
6046 std::cout <<
"CUDA rotation -> ";
6049 <<
"acidcam-gpu temporal buffer -> Vulkan texture\n";
6050 cuda_input_path_logged =
true;
6053 initializeCudaHistory(gpu_filter_engine->output(),
6054 gpu_filter_engine->stream(),
true);
6056 history_was_initialized &&
6058 updateFilteredCudaHistoryFrame();
6064#ifdef ACMXVK_WITH_MXVK_CUDA
6065#if defined(MXVK_WITH_FFMPEG_CAPTURE)
6066 if (using_ffmpeg_capture &&
6067 ffmpeg_capture.using_hardware_decode() &&
6069 if (!ffmpeg_capture.readGpuRgba(cuda_input_rgba,
6070 ffmpeg_cuda_stream,
false)) {
6073 const cv::cuda::GpuMat &render_input =
6074 rotateCudaFrame(cuda_input_rgba, ffmpeg_cuda_stream);
6076 ffmpeg_cuda_stream)) {
6077 render_input.download(cuda_input_fallback_rgba,
6078 ffmpeg_cuda_stream);
6079 ffmpeg_cuda_stream.waitForCompletion();
6080 if (!cuda_input_fallback_logged) {
6082 <<
"acmxvk: direct NVDEC/Vulkan upload unavailable; "
6083 "using host staging\n";
6084 cuda_input_fallback_logged =
true;
6087 cuda_input_fallback_rgba.ptr(),
6088 cuda_input_fallback_rgba.cols,
6089 cuda_input_fallback_rgba.rows,
6090 static_cast<int>(cuda_input_fallback_rgba.step));
6092 updateModelTextureCuda(render_input, ffmpeg_cuda_stream);
6093 if (!cuda_input_path_logged) {
6094 std::cout <<
"acmxvk: CUDA input path active: FFmpeg "
6095 "NVDEC -> CUDA RGBA -> ";
6097 std::cout <<
"CUDA rotation -> ";
6099 std::cout <<
"Vulkan texture";
6100 if (cuda_input_fallback_logged) {
6101 std::cout <<
" (host-staging fallback)";
6104 cuda_input_path_logged =
true;
6107 initializeCudaHistory(render_input, ffmpeg_cuda_stream,
false);
6108 if (history_was_initialized &&
6110 updateCudaHistoryFrame(render_input, ffmpeg_cuda_stream);
6123 if (!requires_host_frame
6124#ifdef MXVK_WITH_FFMPEG_CAPTURE
6125 && !using_ffmpeg_capture
6157 const auto now = std::chrono::steady_clock::now();
6159 const float wall_delta =
6164 double video_timeline = 0.0;
6165 const bool video_timeline_available =
6167 float delta = wall_delta;
6168 if (video_timeline_available) {
6171 <<
"acmxvk: shader clock: decoded video timeline; "
6172 "effects are independent of processing speed\n";
6185 delta =
static_cast<float>(
6189 }
else if (
options.normalized_time) {
6192 const float frame_rate =
6193 video_timeline_available
6195 : (delta > 0.0F ? 1.0F / delta : 0.0F);
6196 float raw_audio_amplitude = 0.0F;
6197 float audio_sensitivity = 1.0F;
6198 float audio_amplitude = 0.0F;
6199 float audio_frequency = 0.0F;
6200 float audio_peak = 0.0F;
6201 float audio_rms = 0.0F;
6202 float audio_smooth = 0.0F;
6203 float audio_low = 0.0F;
6204 float audio_mid = 0.0F;
6205 float audio_high = 0.0F;
6206 float audio_sample_rate = 44100.0F;
6208 std::vector<float> spectrum_values;
6213 double source_audio_time = 0.0;
6214 if (
options.use_source_audio &&
6224 std::cout <<
"acmxvk: audio source finished, stopping "
6225 "(--audio-trunc)\n";
6232 raw_audio_amplitude = metrics.
amplitude;
6235 const float sense = audio_sensitivity * 4.0F * warmup;
6236 audio_amplitude = raw_audio_amplitude * audio_sensitivity *
6237 static_cast<float>(
options.time_speed) *
6238 delta_scale * warmup;
6240 audio_peak = std::sqrt(std::max(metrics.
peak, 0.0F)) * sense;
6241 audio_rms = std::sqrt(std::max(metrics.
rms, 0.0F)) * sense;
6242 audio_smooth = std::sqrt(std::max(metrics.
smooth, 0.0F)) * sense;
6243 audio_low = std::sqrt(std::max(metrics.
low, 0.0F)) * sense;
6244 audio_mid = std::sqrt(std::max(metrics.
mid, 0.0F)) * sense;
6245 audio_high = std::sqrt(std::max(metrics.
high, 0.0F)) * sense;
6246 audio_sample_rate =
static_cast<float>(
audio_engine->sample_rate());
6248 const float spectrum_scale =
6251 for (
float &value : spectrum_values) {
6252 value *= spectrum_scale;
6258 shader_time +=
static_cast<double>(raw_audio_amplitude) *
6259 static_cast<double>(audio_sensitivity) *
6261 static_cast<double>(delta_scale);
6269 audio_amplitude * raw_audio_amplitude;
6270 if (video_timeline_available) {
6271 const std::uint64_t source_frame =
6273 if (source_frame <= 58U) {
6275 0.2F + 0.1F *
static_cast<float>(source_frame);
6277 const std::uint64_t phase = (source_frame - 59U) % 100U;
6280 ? 5.9F - 0.1F *
static_cast<float>(phase)
6282 0.1F *
static_cast<float>(phase - 50U);
6297 const float elapsed =
static_cast<float>(
shader_time);
6298 const float compatibility_time = video_timeline_available
6299 ?
static_cast<float>(
6301 : std::chrono::duration<float>(
6304 const float shader_frame =
6305 video_timeline_available
6308 frame_sprite->setShaderParams(1.0F, 1.0F, 1.0F, elapsed);
6311 static_cast<float>(width),
6312 static_cast<float>(height));
6313 frame_sprite->setUniform1(delta, audio_amplitude, audio_frequency,
6316 audio_sample_rate, audio_peak);
6318 static_cast<float>(
frame_sprite->getHistoryLayerCount()),
6319 audio_rms, audio_smooth);
6320 frame_sprite->setAudioBands(audio_low, audio_mid, audio_high);
6327 static_cast<float>(width),
6328 static_cast<float>(height));
6330 glm::vec4(delta, audio_amplitude, audio_frequency,
6333 shader_frame, elapsed, audio_sample_rate, audio_peak);
6336 static_cast<float>(
frame_sprite->getHistoryLayerCount()),
6337 audio_rms, audio_smooth);
6338 for (std::size_t index = 0;
6346 glm::vec4(audio_low, audio_mid, audio_high, 0.0F);
6355 setPostProcessingShaderParams(index, 1.0F, 1.0F, 1.0F,
6360 static_cast<float>(width),
6361 static_cast<float>(height));
6362 sprite->setUniform1(delta, audio_amplitude, audio_frequency,
6364 sprite->setUniform2(shader_frame, elapsed,
6365 audio_sample_rate, audio_peak);
6366 sprite->setUniform3(
6368 static_cast<float>(
frame_sprite->getHistoryLayerCount()),
6369 audio_rms, audio_smooth);
6370 sprite->setAudioBands(audio_low, audio_mid, audio_high);
6373 if (!spectrum_values.empty()) {
6375 spectrum_values.data(),
6376 static_cast<std::uint32_t
>(spectrum_values.size()));
6377 if (
options.audio_buffers > 0) {
6379 spectrum_values.data(),
6380 static_cast<std::uint32_t
>(spectrum_values.size()));
6383 sprite->updateSpectrumTexture(
6384 spectrum_values.data(),
6385 static_cast<std::uint32_t
>(spectrum_values.size()));
6386 if (
options.audio_buffers > 0) {
6387 sprite->updateSpectrumHistoryTexture(
6388 spectrum_values.data(),
6389 static_cast<std::uint32_t
>(spectrum_values.size()));
void transfer_audio(std::string_view, std::string_view)
Copy the audio track from one media file to another via FFmpeg.
std::chrono::steady_clock::duration source_playback_paused_duration
bool render_pacing_started
std::unordered_map< std::string, fs::path > shader_reload_overrides
std::unique_ptr< midi::MidiInput > midi_input
std::unique_ptr< audio::FileAudioSource > file_audio_source
float model_rotation_y_degrees
fs::path shader_library_directory
InterfaceClient interface_client
void configureMidiMappings()
void requestSnapshot(SnapshotFormat format)
bool shader_history_required
mxvk::ModelFragmentUniforms model_fragment_uniforms
std::uint64_t previous_autopilot_video_frame
void updateCrossfade(const std::chrono::steady_clock::time_point now)
std::chrono::steady_clock::time_point hud_session_start
bool async_camera_initial_wait_completed
bool model_video_timeline_initialized
fs::path resolvedShaderPath(const fs::path &shader) const
std::uint64_t hud_fps_frame_count
void queueRuntimeHud(int &y, int line_height)
std::mt19937 autopilot_rng
std::vector< mxvk::VK_Sprite * > post_process_sprites
double camera_last_logged_fps
bool random_dream_timeline_initialized
void paceMaximizedRendering()
std::uint64_t output_frame_count
int camera_reported_width
void event(SDL_Event &event) override
bool shader_spectrum_history_required
void setSourcePlaybackClockPaused(bool paused)
std::chrono::steady_clock::time_point source_playback_clock_start
std::chrono::steady_clock::time_point source_playback_pause_start
bool model_mouse_dragging
mxvk::VK_Sprite * crossfade_previous_sprite
bool recording_frame_has_pts
void adjustAudioSensitivity(float amount)
float model_wave_direction_y
std::chrono::steady_clock::time_point camera_fps_last_tick
bool legacy_alpha_increasing
void cycleCrossfade(int direction)
float audio_warmup_envelope
void adjustModelScale(float amount)
double crossfade_start_video_timeline
void dispatchMidiAction(int action)
const std::vector< fs::path > * activePasses() const
void updateHumanOverlayTexture()
float model_rotation_z_degrees
bool mediaClockSeconds(double &seconds) const
double previous_random_dream_timeline
std::vector< float > custom_uniform_values
bool hdr_input_precision_enabled
void onSwapchainRecreated() override
std::chrono::steady_clock::time_point audio_warmup_last_tick
std::string activePassDescription() const
bool media_clock_sync_logged
double hudVideoPositionSeconds() const
fs::path model_effect_shader
std::chrono::steady_clock::time_point window_title_last_update
std::deque< ReadbackRequest > readback_requests
MainWindow(Options options)
std::uint64_t generated_frame_count
std::uint64_t png_frame_count
float model_camera_distance
void applyCustomUniformOverrides()
float model_camera_movement_speed
void onRecordPostProcessingTexture(VkCommandBuffer command_buffer, std::uint32_t image_index, VkImageView texture_view, VkExtent2D texture_extent) override
void apply_interface_gpu_filter_state(const InterfaceGpuFilterState &requested, bool announce)
void apply_interface_audio_file_state(const InterfaceAudioFileState &requested)
int history_delay_counter
void updateCameraHistory()
double camera_reported_fps
fs::path shader_manifest_path
std::vector< MidiKnobState > midi_knob_states
void apply_interface_shader_reload(const InterfaceReloadState &requested)
bool crossfade_uses_video_timeline
void updateWindowTitle(bool force=false)
bool continuousReadbackEnabled() const
void applyDnnEffects(cv::Mat &rgba)
std::array< int, 4 > midi_slider_uniform_indices
std::string_view activeShaderRole() const
float updateAudioWarmup(std::chrono::steady_clock::time_point now)
float model_wave_audio_step
fs::path directModelFragmentShader() const
std::uint64_t recording_frame_pts
int preview_overlay_font_size
mxvk::VK_Sprite * frame_sprite
void handleFrameReadback(std::vector< std::uint8_t > &rgba, const std::vector< std::uint16_t > *rgba16, uint32_t width, uint32_t height)
fs::path png_output_directory
std::chrono::steady_clock::time_point interface_next_connect_attempt
bool audio_warmup_started
std::uint64_t observed_midi_drops
void onRecordCustomRendering(VkCommandBuffer command_buffer, std::uint32_t image_index) override
bool readLatestCameraFrame()
void onFrameReadbackRgba16(std::vector< std::uint16_t > &rgba, uint32_t width, uint32_t height) override
bool readTrackedInputFrame()
bool isMidiModelAction(int action) const
bool isMidiMappingSupported(const midi::MidiMapping &mapping) const
void selectGpuFilter(int direction)
bool model_scale_oscillation_active
void apply_interface_deep_dream_state(const InterfaceDeepDreamState &requested, bool announce)
void resolveConfiguredResourcePaths()
bool spectrumHistoryEnabledForShaders() const
std::uint64_t next_clock_output_frame
std::chrono::steady_clock::time_point crossfade_start_time
bool camera_recording_clock_logged
float model_view_rotation_degrees
static std::string formatHudTime(double seconds_value)
bool applyMidiCc(const midi::MidiMessage &message)
void startMediaTimelineIfReady()
bool video_shader_timeline_initialized
void updateRandomDreamSettings()
void apply_interface_overlay_state(const InterfaceOverlayState &requested, bool announce)
std::chrono::steady_clock::time_point previous_frame
bool initial_frame_pending
double previous_video_shader_timeline
void apply_interface_playback_state(const InterfacePlaybackState &requested, bool announce)
std::chrono::steady_clock::time_point hud_fps_last_tick
int autopilot_interval_frames
static bool usesMidiDeltaDirection(const midi::MidiMapping &mapping)
bool async_camera_frame_uploaded
std::string hudVideoTimeString() const
int headless_progress_last_percent
bool autopilot_random_crossfade
bool readHostRgba(cv::Mat &rgba)
void toggleAutopilot(bool sequential)
bool audioSourceOpen() const
float model_pitch_degrees
std::uint32_t interface_last_reload_sequence
bool hdr_transfer_processing_enabled
void recordModel(VkCommandBuffer command_buffer, std::uint32_t image_index, VkImageView texture_view)
void applyShaderPipeline()
bool hdr_cuda_filter_bypass_logged
void logSelectedPlaylistNode(std::string_view action) const
std::unique_ptr< audio::AudioEngine > audio_engine
double video_duration_seconds
bool hdr_dream_compatibility_logged
std::vector< MidiCcMapping > midi_cc_mappings
bool spectrum_scale_by_sensitivity
mxvk::VK_Sprite * human_overlay_sprite
bool historyCacheEnabled() const
bool headless_shutdown_logged
void apply_interface_uniform_values(const std::vector< InterfaceUniformValue > &uniform_values)
std::vector< fs::path > activeShaderPipeline() const
std::size_t crossfade_shader_index
std::chrono::steady_clock::time_point next_render_tick
void stepShaderTime(double amount)
std::uint64_t previous_model_video_frame
bool source_playback_clock_paused
double camera_delivered_fps
VideoHdrInfo video_hdr_info
std::uint64_t frame_count
void updateCameraFrameRate()
void initializeGpuFilters()
void onFrameReadbackScheduled() override
bool hostPreprocessingEnabled() const
float model_wave_amplitude_z
double hudWallElapsedSeconds() const
std::string activePlaylistDescription() const
bool autopilot_sequential
void handleDeepDreamRuntimeError(std::string_view message)
int camera_reported_height
SnapshotFormat pending_snapshot_format
void startLiveAudioRecordingIfNeeded()
std::string hudElapsedTimeString() const
std::pair< int, int > source_dimensions()
void apply_interface_multipass_state(const InterfaceMultipassState &requested)
std::uint64_t camera_fps_frame_count
void initializeDeepDream()
static std::string captureFourccName(double value)
float model_wave_direction_z
void uploadCustomUniforms()
void initializeHistory(const cv::Mat &rgba)
float model_scale_oscillation_phase
float model_wave_direction_x
std::uint32_t spectrumBinCount() const
std::size_t playlist_index
void applyDeepDreamEffect(cv::Mat &rgba)
std::chrono::steady_clock::time_point camera_history_next_update
bool handleCaptureEnd(bool discard=false)
bool source_frame_received
void configureRenderResolution()
std::uint32_t interface_last_audio_file_sequence
void adjustTimeSpeed(double amount)
void uploadInputFrame(const cv::Mat &rgba)
SDL_Keycode midiActionKey(int action) const
float model_camera_rotation_speed
bool autopilot_video_timeline_initialized
bool readClockedVideoFrame(double clock_seconds)
bool currentVideoTimeline(double &timeline, std::uint64_t *frame_index=nullptr) const
float model_rotation_speed
void updateShaderUniforms(int width, int height)
LatestCameraFrame latest_camera_frame
std::chrono::steady_clock::time_point headless_progress_last_emit
bool headless_progress_complete
std::vector< fs::path > configured_passes
bool applyMidiMap(const midi::MidiMessage &message)
void maybeRandomizeCrossfade()
bool spectrumTextureEnabledForShaders() const
std::vector< midi::MidiMapping > midi_action_mappings
void apply_interface_shader_selection(const std::string &requested_name)
bool isMidiSliderMapping(const midi::MidiMapping &mapping) const
bool hdr_dnn_compatibility_logged
void resetAutopilotInterval()
bool video_shader_clock_logged
cv::Mat human_overlay_rgba
bool interface_connection_warning_reported
void recordShaderResources(const mxvk::ShaderModuleInfo &module_info, std::string_view source)
float model_rotation_x_degrees
bool camera_history_clock_started
bool dream_processing_logged
void sync_interface_control()
fs::path generate_output_directory
void initializeOverlayFont()
std::vector< PlaylistNode > playlist
bool model_texture_prepass_active
static std::string clipOverlayText(std::string text)
static constexpr std::uint32_t COMPATIBILITY_SPECTRUM_BIN_COUNT
bool setMidiUniform(std::size_t uniform_index, int value, std::string_view label)
std::uint64_t snapshot_count
std::uint64_t autopilotFrameAdvance()
mxvk::VKAbstractModel input_model
void emitHeadlessProgress(bool complete)
std::chrono::steady_clock::time_point compatibility_clock_start
std::vector< fs::path > shaders
void onFrameReadback(std::vector< std::uint8_t > &rgba, uint32_t width, uint32_t height) override
bool shader_spectrum_required
std::vector< ShaderManifest::CustomUniform > custom_uniforms
bool hdr_input_upload_logged
void updateHudFrameRate()
std::uint32_t interface_last_sequence
void start_requested_audio_recording()
std::string_view midiActionName(int action) const
void updateHistoryFrame(const cv::Mat &rgba)
cv::Mat latest_camera_history_rgba
bool media_timeline_started
std::size_t crossfade_post_process_index
std::mt19937 random_dream_rng
void initialize_interface_control()
std::uint64_t video_source_frame_count
std::chrono::steady_clock::time_point model_last_render_time
std::string currentShader() const
SnapshotWriter snapshot_writer
void dispatchMidiModelAction(int action)
void selectShader(int direction)
void printCustomUniforms() const
float model_wave_amplitude_y
std::uint64_t decoded_video_frame_count
std::uint64_t random_dream_period
void selectPlaylistNode(int direction)
float model_wave_amplitude_x
static void savePng(const fs::path &path, std::uint8_t *rgba, int width, int height)
static std::string_view formatName(SnapshotFormat format) noexcept
static constexpr std::uint32_t spectrum_bin_count()
static bool mux_recording_into_video(std::vector< float > samples, unsigned int sample_rate, const std::string &video_path, double video_duration)
std::size_t selected_channels() const
static Model load(std::string_view filename, int cuda_device, std::string_view layer={}, bool use_half=false)
cv::Mat decode_hdr_transfer(const cv::Mat &rgba, bool hlg)
float encode_srgb(float value)
constexpr int COLOR_TRANSFER_ARIB_STD_B67
constexpr int COLOR_TRANSFER_SMPTE2084
float tone_map_channel(float value)
cv::Mat rgba16ToRgba8(const cv::Mat &rgba)
volatile std::sig_atomic_t HEADLESS_SHUTDOWN_REQUESTED
float decode_pq(float encoded)
float decode_hlg(float encoded)
std::vector< std::uint8_t > tone_map_hdr_rgba16(const std::vector< std::uint16_t > &rgba, bool hlg)
bool write_wav_file(const AudioRecording &recording, const std::string &filename)
cv::Mat hardenedAlphaMask(const cv::Mat &image, const cv::Mat &mask, float black_point, float white_point)
cv::Mat isolateBody(const cv::Mat &image, const cv::Mat &mask, float black_point, float white_point)
std::vector< MidiMapping > load_mapping_file(const std::string &filename)
std::vector< PlaylistNode > load_playlist(const fs::path &playlist_path, const std::vector< fs::path > &available_shaders, const fs::path &library_directory, std::ostream &warning_output)
fs::path resolveShaderManifestEntry(const fs::path &directory, std::string entry)
cv::Mat loadRgbaImage(const std::string &filename)
bool rotationSwapsDimensions(FrameRotation rotation)
fs::path default_model_path(const Options &options)
fs::path snapshot_path(const fs::path &directory, std::uint32_t width, std::uint32_t height, std::uint64_t &counter, SnapshotFormat format, std::chrono::system_clock::time_point timestamp)
std::size_t playlist_shader_count(const std::vector< PlaylistNode > &playlist) noexcept
fs::path echo_cache_shader_path(const Options &options)
double probeVideoDuration(const std::string &filename)
std::string trim(std::string text)
fs::path human_composite_shader_path(const Options &options)
ShaderManifest loadShaderManifest(const fs::path &directory)
fs::path model_fragment_shader_path(const Options &options)
void printVideoHdrInfo(const VideoHdrInfo &info, std::ostream &output)
fs::path output_frame_directory(const std::string &filename, std::string_view suffix)
fs::path model_vertex_shader_path(const Options &options)
void request_headless_shutdown(int signal_number) noexcept
int parseInteger(std::string_view text, std::string_view option)
bool dimensions_supported(int width, int height)
void rotateFrame(cv::Mat &frame, FrameRotation rotation)
fs::path passthrough_shader_path(const Options &options)
fs::path frame_path(const fs::path &directory, std::uint64_t index)
VideoHdrInfo probeVideoHdrInfo(const std::string &filename)
fs::path hdr_preview_shader_path(const Options &options, bool hlg)
fs::path find_shader_path(const std::vector< fs::path > &available_shaders, const fs::path &library_directory, std::string name)
fs::path crossfade_shader_path(const Options &options, std::size_t shader_index)
fs::path sprite_vertex_shader_path(const Options &options)
fs::path hdr_transfer_shader_path(const Options &options, bool hlg, bool encode)
double parseNumber(std::string_view text, std::string_view option)
void create_output_directory(const fs::path &directory)
bool isValidCustomUniformName(const std::string &name)
constexpr std::array< std::string_view, 35 > CROSSFADE_NAMES
fs::path overlay_font_path(const Options &options)
fs::path flip_shader_path(const Options &options)
fs::path find_resource(const Options &options, const fs::path &relative_path)
std::uint32_t request_sequence
std::vector< int > filter_indices
std::vector< std::string > shader_names
std::array< std::uint8_t, 3 > watermark_color
std::string watermark_text
std::uint32_t request_sequence
std::int32_t shader_index
std::string selected_shader_name
InterfaceOverlayState overlay
std::vector< InterfaceUniformValue > uniform_values
InterfaceAudioFileState audio_file
InterfaceMultipassState multipass
InterfaceGpuFilterState gpu_filters
InterfaceReloadState reload
InterfacePlaybackState playback
InterfaceDeepDreamState deep_dream
SnapshotFormat snapshot_format
bool midi_device_specified
double audio_recording_gain
double dream_octave_scale
double random_dream_interval
bool gpu_filter_before_dream
double audio_pass_through_gain
std::string fragment_shader
bool random_dream_specified
std::string midi_map_file
std::vector< int > gpu_filter_indices
std::string watermark_text
std::vector< std::string > midi_cc_mappings
std::string shader_directory
std::string onnx_configuration
std::string record_audio_file
int gpu_frame_buffer_size
std::string compute_shader
std::vector< std::string > entries
std::vector< CustomUniform > custom_uniforms
std::vector< std::uint16_t > rgba16
std::vector< std::uint8_t > rgba
double duration_seconds() const
std::vector< float > samples
std::vector< unsigned char > bytes