ACMX 2.139.0
Dual-Backend Real-Time GPU Video Synthesis
Loading...
Searching...
No Matches
interface_client.cpp
Go to the documentation of this file.
2
4
5#include <algorithm>
6#include <cerrno>
7#include <cstring>
8#include <iostream>
9#include <iterator>
10#include <utility>
11
12#if defined(__linux__) || defined(__APPLE__)
13#include <fcntl.h>
14#include <semaphore.h>
15#include <sys/mman.h>
16#include <sys/stat.h>
17#include <unistd.h>
18#endif
19
20namespace acmxvk {
22 bool open_error_reported = false;
23#if defined(__linux__) || defined(__APPLE__) || defined(_WIN32)
24 ipc::ShaderSelectionData *selection = nullptr;
25#if defined(__linux__) || defined(__APPLE__)
26 int shm_fd = -1;
27 sem_t *lock_handle = SEM_FAILED;
28#else
29 HANDLE mapping_handle = nullptr;
30 HANDLE lock_handle = nullptr;
31#endif
32#endif
33 };
34
35 InterfaceClient::InterfaceClient() : impl(std::make_unique<Impl>()) {}
36
38
39#if defined(__linux__) || defined(__APPLE__) || defined(_WIN32)
41 close();
42
43#if defined(__linux__) || defined(__APPLE__)
44 impl->lock_handle = ::sem_open(ipc::SHADER_SELECTION_SEMAPHORE_NAME, 0);
45 if (impl->lock_handle == SEM_FAILED) {
46 if (!impl->open_error_reported) {
47 std::cerr << "acmxvk: interface control unavailable: sem_open(" << ipc::SHADER_SELECTION_SEMAPHORE_NAME << ") failed: " << std::strerror(errno) << '\n';
48 impl->open_error_reported = true;
49 }
50 return false;
51 }
52
53 impl->shm_fd = ::shm_open(ipc::SHADER_SELECTION_SHM_NAME, O_RDWR, 0666);
54 if (impl->shm_fd < 0) {
55 const int open_error = errno;
56 if (!impl->open_error_reported) {
57 std::cerr << "acmxvk: interface control unavailable: shm_open(" << ipc::SHADER_SELECTION_SHM_NAME << ") failed: " << std::strerror(open_error) << '\n';
58 impl->open_error_reported = true;
59 }
60 close();
61 return false;
62 }
63
64 constexpr std::size_t SHARED_MEMORY_SIZE = sizeof(ipc::ShaderSelectionData);
65 struct stat shm_stat{};
66 if (::fstat(impl->shm_fd, &shm_stat) != 0) {
67 const int stat_error = errno;
68 if (!impl->open_error_reported) {
69 std::cerr << "acmxvk: interface control unavailable: fstat(" << ipc::SHADER_SELECTION_SHM_NAME << ") failed: " << std::strerror(stat_error) << '\n';
70 impl->open_error_reported = true;
71 }
72 close();
73 return false;
74 }
75 if (shm_stat.st_size < static_cast<off_t>(SHARED_MEMORY_SIZE)) {
76 if (!impl->open_error_reported) {
77 std::cerr << "acmxvk: interface control unavailable: " << ipc::SHADER_SELECTION_SHM_NAME << " has size " << shm_stat.st_size << " bytes; expected " << SHARED_MEMORY_SIZE << '\n';
78 impl->open_error_reported = true;
79 }
80 close();
81 return false;
82 }
83
84 void *mapped = ::mmap(nullptr, SHARED_MEMORY_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, impl->shm_fd, 0);
85 if (mapped == MAP_FAILED) {
86 const int map_error = errno;
87 if (!impl->open_error_reported) {
88 std::cerr << "acmxvk: interface control unavailable: mmap(" << ipc::SHADER_SELECTION_SHM_NAME << ", " << SHARED_MEMORY_SIZE << ") failed: " << std::strerror(map_error) << '\n';
89 impl->open_error_reported = true;
90 }
91 close();
92 return false;
93 }
94 impl->selection = static_cast<ipc::ShaderSelectionData *>(mapped);
95#else
96 impl->lock_handle = ::OpenMutexW(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, ipc::SHADER_SELECTION_MUTEX_NAME_WINDOWS);
97 if (impl->lock_handle == nullptr) {
98 if (!impl->open_error_reported) {
99 std::cerr << "acmxvk: interface control unavailable: OpenMutexW "
100 "failed with Windows error "
101 << ::GetLastError() << '\n';
102 impl->open_error_reported = true;
103 }
104 return false;
105 }
106
107 impl->mapping_handle = ::OpenFileMappingW(FILE_MAP_READ, FALSE, ipc::SHADER_SELECTION_MAPPING_NAME_WINDOWS);
108 if (impl->mapping_handle == nullptr) {
109 const DWORD open_error = ::GetLastError();
110 if (!impl->open_error_reported) {
111 std::cerr << "acmxvk: interface control unavailable: OpenFileMappingW "
112 "failed with Windows error "
113 << open_error << '\n';
114 impl->open_error_reported = true;
115 }
116 close();
117 return false;
118 }
119
120 constexpr std::size_t SHARED_MEMORY_SIZE = sizeof(ipc::ShaderSelectionData);
121 void *mapped = ::MapViewOfFile(impl->mapping_handle, FILE_MAP_READ, 0, 0, SHARED_MEMORY_SIZE);
122 if (mapped == nullptr) {
123 const DWORD map_error = ::GetLastError();
124 if (!impl->open_error_reported) {
125 std::cerr << "acmxvk: interface control unavailable: MapViewOfFile "
126 "failed with Windows error "
127 << map_error << '\n';
128 impl->open_error_reported = true;
129 }
130 close();
131 return false;
132 }
133 impl->selection = static_cast<ipc::ShaderSelectionData *>(mapped);
134#endif
135 impl->open_error_reported = false;
136 return true;
137 }
138
139 void InterfaceClient::close() noexcept {
140 if (impl->selection != nullptr) {
141#if defined(__linux__) || defined(__APPLE__)
142 ::munmap(impl->selection, sizeof(ipc::ShaderSelectionData));
143#else
144 ::UnmapViewOfFile(impl->selection);
145#endif
146 impl->selection = nullptr;
147 }
148#if defined(__linux__) || defined(__APPLE__)
149 if (impl->shm_fd >= 0) {
150 ::close(impl->shm_fd);
151 impl->shm_fd = -1;
152 }
153 if (impl->lock_handle != SEM_FAILED) {
154 ::sem_close(impl->lock_handle);
155 impl->lock_handle = SEM_FAILED;
156 }
157#else
158 if (impl->mapping_handle != nullptr) {
159 ::CloseHandle(impl->mapping_handle);
160 impl->mapping_handle = nullptr;
161 }
162 if (impl->lock_handle != nullptr) {
163 ::CloseHandle(impl->lock_handle);
164 impl->lock_handle = nullptr;
165 }
166#endif
167 }
168
169 bool InterfaceClient::is_open() const noexcept {
170#if defined(__linux__) || defined(__APPLE__)
171 return impl->selection != nullptr && impl->shm_fd >= 0 && impl->lock_handle != SEM_FAILED;
172#elif defined(_WIN32)
173 return impl->selection != nullptr && impl->mapping_handle != nullptr && impl->lock_handle != nullptr;
174#else
175 return false;
176#endif
177 }
178
179 bool InterfaceClient::read(InterfaceState &state) const {
180 if (!is_open()) {
181 return false;
182 }
183 ipc::InterfaceLock lock(impl->lock_handle);
184 if (!lock) {
185 return false;
186 }
187 if (impl->selection->magic != ipc::SHADER_SELECTION_MAGIC || impl->selection->version != ipc::SHADER_SELECTION_VERSION) {
188 return false;
189 }
190
191 InterfaceState next;
192 next.sequence = impl->selection->sequence;
193 const auto name_end = std::find(std::begin(impl->selection->selected_shader_name), std::end(impl->selection->selected_shader_name), '\0');
194 next.selected_shader_name.assign(std::begin(impl->selection->selected_shader_name), name_end);
195
196 next.multipass.enabled = impl->selection->shader_pass_enabled != 0;
197 const std::uint32_t pass_count = std::min(impl->selection->shader_pass_count, ipc::MAX_PASS_COUNT);
198 next.multipass.shader_names.reserve(pass_count);
199 for (std::uint32_t index = 0; index < pass_count; ++index) {
200 const char *name_begin = impl->selection->shader_pass_names[index];
201 const char *name_limit = name_begin + ipc::MAX_SHADER_NAME;
202 const char *shader_name_end = std::find(name_begin, name_limit, '\0');
203 if (shader_name_end != name_begin) {
204 next.multipass.shader_names.emplace_back(name_begin, shader_name_end);
205 }
206 }
207
208 const std::uint32_t uniform_count = std::min(impl->selection->custom_uniform_count, ipc::MAX_CUSTOM_UNIFORMS);
209 next.uniform_values.reserve(uniform_count);
210 for (std::uint32_t index = 0; index < uniform_count; ++index) {
211 const char *name_begin = impl->selection->custom_uniform_names[index];
212 const char *name_limit = name_begin + ipc::MAX_UNIFORM_NAME;
213 const char *uniform_name_end = std::find(name_begin, name_limit, '\0');
214 next.uniform_values.push_back({std::string(name_begin, uniform_name_end), impl->selection->custom_uniform_values[index]});
215 }
216
217 next.playback.repeat = impl->selection->repeat_enabled != 0;
218 next.playback.normalized_time = impl->selection->normalized_time_enabled != 0;
219 next.overlay.display_filter = impl->selection->display_filter_enabled != 0;
220 next.overlay.watermark_enabled = impl->selection->watermark_enabled != 0;
221 const auto watermark_end = std::find(std::begin(impl->selection->watermark_text), std::end(impl->selection->watermark_text), '\0');
222 next.overlay.watermark_text.assign(std::begin(impl->selection->watermark_text), watermark_end);
223 next.overlay.watermark_color = {impl->selection->watermark_r, impl->selection->watermark_g, impl->selection->watermark_b};
224
225 next.gpu_filters.enabled = impl->selection->gpu_filter_enabled != 0;
226 next.gpu_filters.frame_buffer_size = static_cast<int>(impl->selection->gpu_buffer_size);
227 const std::uint32_t gpu_filter_count = std::min(impl->selection->gpu_filter_count, ipc::MAX_GPU_FILTER_COUNT);
228 next.gpu_filters.filter_indices.reserve(gpu_filter_count);
229 for (std::uint32_t index = 0; index < gpu_filter_count; ++index) {
230 const int filter_index = impl->selection->gpu_filter_indices[index];
231 if (filter_index >= 0) {
232 next.gpu_filters.filter_indices.push_back(filter_index);
233 }
234 }
235
236 next.deep_dream.enabled = impl->selection->dream_enabled != 0;
237 next.deep_dream.fp16 = impl->selection->dream_fp16 != 0;
238 next.deep_dream.gpu_filter_first = impl->selection->dream_gpu_filter_first != 0;
239 next.deep_dream.iterations = impl->selection->dream_iterations;
240 next.deep_dream.maximum_dimension = impl->selection->dream_maximum_dimension;
241 next.deep_dream.channel = impl->selection->dream_channel;
242 next.deep_dream.octaves = impl->selection->dream_octaves;
243 next.deep_dream.jitter = impl->selection->dream_jitter;
244 next.deep_dream.smoothing = impl->selection->dream_smoothing;
245 next.deep_dream.strength = impl->selection->dream_strength;
246 next.deep_dream.feedback = impl->selection->dream_feedback;
247 next.deep_dream.zoom = impl->selection->dream_zoom;
248 next.deep_dream.rotation = impl->selection->dream_rotation;
249 next.deep_dream.octave_scale = impl->selection->dream_octave_scale;
250 const auto dream_model_end = std::find(std::begin(impl->selection->dream_model_path), std::end(impl->selection->dream_model_path), '\0');
251 next.deep_dream.model_path.assign(std::begin(impl->selection->dream_model_path), dream_model_end);
252 const auto dream_layer_end = std::find(std::begin(impl->selection->dream_layer), std::end(impl->selection->dream_layer), '\0');
253 next.deep_dream.layer.assign(std::begin(impl->selection->dream_layer), dream_layer_end);
254
255 next.audio_file.request_sequence = impl->selection->audio_file_sequence;
256 const auto audio_path_end = std::find(std::begin(impl->selection->audio_file_path), std::end(impl->selection->audio_file_path), '\0');
257 next.audio_file.path.assign(std::begin(impl->selection->audio_file_path), audio_path_end);
258 next.audio_file.output_device = impl->selection->audio_output_device;
259 next.audio_file.pass_through = impl->selection->audio_pass_through != 0;
260 next.audio_file.trunc = impl->selection->audio_trunc != 0;
261 next.audio_file.repeat = impl->selection->audio_repeat != 0;
262
263 next.reload.request_sequence = impl->selection->reload_sequence;
264 next.reload.shader_index = impl->selection->reload_shader_index;
265 const auto reload_path_end = std::find(std::begin(impl->selection->reload_shader_path), std::end(impl->selection->reload_shader_path), '\0');
266 next.reload.path.assign(std::begin(impl->selection->reload_shader_path), reload_path_end);
267
268 state = std::move(next);
269 return true;
270 }
271#else
273 std::cerr << "acmxvk: interface control is unavailable on this platform\n";
274 return false;
275 }
276
277 void InterfaceClient::close() noexcept {}
278
279 bool InterfaceClient::is_open() const noexcept { return false; }
280
282 static_cast<void>(state);
283 return false;
284 }
285#endif
286} // namespace acmxvk
bool is_open() const noexcept
bool read(InterfaceState &state) const
std::unique_ptr< Impl > impl
constexpr std::uint32_t MAX_SHADER_NAME
constexpr std::uint32_t MAX_GPU_FILTER_COUNT
constexpr std::uint32_t SHADER_SELECTION_VERSION
constexpr std::uint32_t MAX_UNIFORM_NAME
constexpr const char * SHADER_SELECTION_SEMAPHORE_NAME
constexpr std::uint32_t SHADER_SELECTION_MAGIC
constexpr std::uint32_t MAX_CUSTOM_UNIFORMS
constexpr const char * SHADER_SELECTION_SHM_NAME
constexpr std::uint32_t MAX_PASS_COUNT