ACMX 2.136.0
Dual-Backend Real-Time GPU Video Synthesis
Loading...
Searching...
No Matches
deep_dream.cpp
Go to the documentation of this file.
1#include "deep_dream.hpp"
3
4#include <torch/cuda.h>
5#include <torch/headeronly/version.h>
6#include <torch/torch.h>
7
8#include <algorithm>
9#include <exception>
10#include <iomanip>
11#include <ostream>
12
13namespace acmxvk::dream {
14 namespace {
15 [[nodiscard]] bool autograd_smoke_test(const torch::Device &device) {
16 torch::Tensor input = torch::ones(
17 {1, 1, 2, 2},
18 torch::TensorOptions()
19 .dtype(torch::kFloat32)
20 .device(device)
21 .requires_grad(true));
22 const torch::Tensor loss = input.square().mean();
23 loss.backward();
24 const torch::Tensor expected = torch::full_like(input, 0.5F);
25 return input.grad().defined() &&
26 torch::allclose(input.grad(), expected);
27 }
28 } // namespace
29
30 [[nodiscard]] bool probe(int cuda_device, std::string_view model_file,
31 std::string_view layer, int iterations,
32 float strength, float feedback, float zoom,
33 float rotation_degrees, int max_dimension,
34 bool use_half, int target_channel,
35 int octaves, float octave_scale,
36 int jitter, int smoothing,
37 std::ostream &output,
38 std::ostream &error) {
39 output << "Deep Dream: enabled\n"
40 << "LibTorch version: " << TORCH_VERSION << '\n';
41#ifdef ACMXVK_WITH_MXVK_CUDA
42 output << "Deep Dream CUDA/Vulkan interop: enabled\n";
43#else
44 output << "Deep Dream CUDA/Vulkan interop: unavailable "
45 "(MXVK has no CUDA interop)\n";
46#endif
47
48 try {
49 if (!autograd_smoke_test(torch::Device(torch::kCPU))) {
50 error << "Deep Dream LibTorch CPU autograd smoke test failed\n";
51 return false;
52 }
53 output << "LibTorch CPU autograd: ready\n";
54
55 const c10::DeviceIndex device_count = torch::cuda::device_count();
56 output << "LibTorch CUDA devices: "
57 << static_cast<int>(device_count) << '\n';
58 if (!torch::cuda::is_available() || device_count == 0) {
59 output << "LibTorch CUDA autograd: unavailable "
60 "(no CUDA device visible)\n";
61 if (!model_file.empty()) {
62 error << "Deep Dream model inspection requires an "
63 "available CUDA device\n";
64 return false;
65 }
66 return true;
67 }
68 if (cuda_device < 0 || cuda_device >= device_count) {
69 error << "Deep Dream CUDA device index " << cuda_device
70 << " is outside the available range 0-"
71 << (device_count - 1) << '\n';
72 return false;
73 }
74
75 const torch::Device device(torch::kCUDA, cuda_device);
76 if (!autograd_smoke_test(device)) {
77 error << "Deep Dream LibTorch CUDA autograd smoke test failed "
78 "on device "
79 << cuda_device << '\n';
80 return false;
81 }
82 torch::cuda::synchronize(cuda_device);
83 output << "LibTorch CUDA autograd: ready on device " << cuda_device
84 << '\n'
85 << "LibTorch cuDNN: "
86 << (torch::cuda::cudnn_is_available() ? "ready" : "unavailable")
87 << '\n';
88
89 if (!model_file.empty()) {
90 Model model =
91 Model::load(model_file, cuda_device, layer, use_half);
92 model.print(output);
93 output << "Deep Dream target channel: ";
94 if (target_channel < 0) {
95 output << "all\n";
96 } else {
97 output << target_channel << '\n';
98 }
99 const int test_size = std::max(
100 64, static_cast<int>(model.metadata().minimum_input_size));
101 cv::Mat test_image(test_size, test_size, CV_8UC4);
102 for (int y = 0; y < test_image.rows; ++y) {
103 for (int x = 0; x < test_image.cols; ++x) {
104 test_image.at<cv::Vec4b>(y, x) = cv::Vec4b{
105 static_cast<std::uint8_t>((x * 255) /
106 (test_image.cols - 1)),
107 static_cast<std::uint8_t>((y * 255) /
108 (test_image.rows - 1)),
109 static_cast<std::uint8_t>(((x + y) * 255) /
110 (test_image.cols +
111 test_image.rows - 2)),
112 255U};
113 }
114 }
116 test_image,
117 GradientAscentOptions{iterations, strength, feedback, zoom,
118 rotation_degrees, max_dimension,
119 target_channel, octaves,
120 octave_scale, jitter, smoothing});
121 output << std::fixed << std::setprecision(6)
122 << "Deep Dream gradient ascent: ready"
123 << " (loss=" << result.activation_loss
124 << ", mean gradient=" << result.mean_gradient
125 << ", mean pixel change="
126 << result.mean_pixel_change << ", working size="
127 << result.processed_width << 'x'
128 << result.processed_height << ", octaves="
129 << result.processed_octaves << ", jitter=" << jitter
130 << ", smoothing=" << smoothing << ")\n";
131 if (feedback > 0.0F) {
132 result = model.apply_gradient_ascent(
133 test_image,
134 GradientAscentOptions{iterations, strength, feedback,
135 zoom, rotation_degrees,
136 max_dimension, target_channel,
137 octaves, octave_scale, jitter,
138 smoothing});
139 output << "Deep Dream temporal feedback: ready"
140 << " (blend=" << feedback << ", zoom=" << zoom
141 << ", rotation=" << rotation_degrees
142 << ", next-frame change="
143 << result.mean_pixel_change << ")\n";
144 }
145 }
146 } catch (const std::exception &exception) {
147 error << "Deep Dream LibTorch probe failed: " << exception.what()
148 << '\n';
149 return false;
150 }
151 return true;
152 }
153
154} // namespace acmxvk::dream
GradientAscentResult apply_gradient_ascent(cv::Mat &rgba, const GradientAscentOptions &options={})
static Model load(std::string_view filename, int cuda_device, std::string_view layer={}, bool use_half=false)
const ModelMetadata & metadata() const
void print(std::ostream &output) const
bool autograd_smoke_test(const torch::Device &device)
bool probe(int cuda_device, std::string_view model_file, std::string_view layer, int iterations, float strength, float feedback, float zoom, float rotation_degrees, int max_dimension, bool use_half, int target_channel, int octaves, float octave_scale, int jitter, int smoothing, std::ostream &output, std::ostream &error)