ACMX 2.136.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
40
41#if defined(__linux__) || defined(__APPLE__) || defined(_WIN32)
43 close();
44
45#if defined(__linux__) || defined(__APPLE__)
46 impl->lock_handle =
48 if (impl->lock_handle == SEM_FAILED) {
49 if (!impl->open_error_reported) {
50 std::cerr << "acmxvk: interface control unavailable: sem_open("
52 << ") failed: " << std::strerror(errno) << '\n';
53 impl->open_error_reported = true;
54 }
55 return false;
56 }
57
58 impl->shm_fd =
59 ::shm_open(ipc::SHADER_SELECTION_SHM_NAME, O_RDWR, 0666);
60 if (impl->shm_fd < 0) {
61 const int open_error = errno;
62 if (!impl->open_error_reported) {
63 std::cerr << "acmxvk: interface control unavailable: shm_open("
65 << ") failed: " << std::strerror(open_error) << '\n';
66 impl->open_error_reported = true;
67 }
68 close();
69 return false;
70 }
71
72 constexpr std::size_t SHARED_MEMORY_SIZE =
73 sizeof(ipc::ShaderSelectionData);
74 struct stat shm_stat{};
75 if (::fstat(impl->shm_fd, &shm_stat) != 0) {
76 const int stat_error = errno;
77 if (!impl->open_error_reported) {
78 std::cerr << "acmxvk: interface control unavailable: fstat("
80 << ") failed: " << std::strerror(stat_error) << '\n';
81 impl->open_error_reported = true;
82 }
83 close();
84 return false;
85 }
86 if (shm_stat.st_size < static_cast<off_t>(SHARED_MEMORY_SIZE)) {
87 if (!impl->open_error_reported) {
88 std::cerr << "acmxvk: interface control unavailable: "
89 << ipc::SHADER_SELECTION_SHM_NAME << " has size "
90 << shm_stat.st_size << " bytes; expected "
91 << SHARED_MEMORY_SIZE << '\n';
92 impl->open_error_reported = true;
93 }
94 close();
95 return false;
96 }
97
98 void *mapped = ::mmap(nullptr, SHARED_MEMORY_SIZE,
99 PROT_READ | PROT_WRITE, MAP_SHARED,
100 impl->shm_fd, 0);
101 if (mapped == MAP_FAILED) {
102 const int map_error = errno;
103 if (!impl->open_error_reported) {
104 std::cerr << "acmxvk: interface control unavailable: mmap("
106 << SHARED_MEMORY_SIZE
107 << ") failed: " << std::strerror(map_error) << '\n';
108 impl->open_error_reported = true;
109 }
110 close();
111 return false;
112 }
113 impl->selection = static_cast<ipc::ShaderSelectionData *>(mapped);
114#else
115 impl->lock_handle = ::OpenMutexW(
116 SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE,
117 ipc::SHADER_SELECTION_MUTEX_NAME_WINDOWS);
118 if (impl->lock_handle == nullptr) {
119 if (!impl->open_error_reported) {
120 std::cerr << "acmxvk: interface control unavailable: OpenMutexW "
121 "failed with Windows error "
122 << ::GetLastError() << '\n';
123 impl->open_error_reported = true;
124 }
125 return false;
126 }
127
128 impl->mapping_handle = ::OpenFileMappingW(
129 FILE_MAP_READ, FALSE, ipc::SHADER_SELECTION_MAPPING_NAME_WINDOWS);
130 if (impl->mapping_handle == nullptr) {
131 const DWORD open_error = ::GetLastError();
132 if (!impl->open_error_reported) {
133 std::cerr
134 << "acmxvk: interface control unavailable: OpenFileMappingW "
135 "failed with Windows error "
136 << open_error << '\n';
137 impl->open_error_reported = true;
138 }
139 close();
140 return false;
141 }
142
143 constexpr std::size_t SHARED_MEMORY_SIZE =
144 sizeof(ipc::ShaderSelectionData);
145 void *mapped = ::MapViewOfFile(impl->mapping_handle, FILE_MAP_READ, 0,
146 0, SHARED_MEMORY_SIZE);
147 if (mapped == nullptr) {
148 const DWORD map_error = ::GetLastError();
149 if (!impl->open_error_reported) {
150 std::cerr
151 << "acmxvk: interface control unavailable: MapViewOfFile "
152 "failed with Windows error "
153 << map_error << '\n';
154 impl->open_error_reported = true;
155 }
156 close();
157 return false;
158 }
159 impl->selection = static_cast<ipc::ShaderSelectionData *>(mapped);
160#endif
161 impl->open_error_reported = false;
162 return true;
163 }
164
165 void InterfaceClient::close() noexcept {
166 if (impl->selection != nullptr) {
167#if defined(__linux__) || defined(__APPLE__)
168 ::munmap(impl->selection, sizeof(ipc::ShaderSelectionData));
169#else
170 ::UnmapViewOfFile(impl->selection);
171#endif
172 impl->selection = nullptr;
173 }
174#if defined(__linux__) || defined(__APPLE__)
175 if (impl->shm_fd >= 0) {
176 ::close(impl->shm_fd);
177 impl->shm_fd = -1;
178 }
179 if (impl->lock_handle != SEM_FAILED) {
180 ::sem_close(impl->lock_handle);
181 impl->lock_handle = SEM_FAILED;
182 }
183#else
184 if (impl->mapping_handle != nullptr) {
185 ::CloseHandle(impl->mapping_handle);
186 impl->mapping_handle = nullptr;
187 }
188 if (impl->lock_handle != nullptr) {
189 ::CloseHandle(impl->lock_handle);
190 impl->lock_handle = nullptr;
191 }
192#endif
193 }
194
195 bool InterfaceClient::is_open() const noexcept {
196#if defined(__linux__) || defined(__APPLE__)
197 return impl->selection != nullptr && impl->shm_fd >= 0 &&
198 impl->lock_handle != SEM_FAILED;
199#elif defined(_WIN32)
200 return impl->selection != nullptr && impl->mapping_handle != nullptr &&
201 impl->lock_handle != nullptr;
202#else
203 return false;
204#endif
205 }
206
207 bool InterfaceClient::read(InterfaceState &state) const {
208 if (!is_open()) {
209 return false;
210 }
211 ipc::InterfaceLock lock(impl->lock_handle);
212 if (!lock) {
213 return false;
214 }
215 if (impl->selection->magic != ipc::SHADER_SELECTION_MAGIC ||
216 impl->selection->version != ipc::SHADER_SELECTION_VERSION) {
217 return false;
218 }
219
220 InterfaceState next;
221 next.sequence = impl->selection->sequence;
222 const auto name_end = std::find(
223 std::begin(impl->selection->selected_shader_name),
224 std::end(impl->selection->selected_shader_name), '\0');
225 next.selected_shader_name.assign(
226 std::begin(impl->selection->selected_shader_name), name_end);
227
228 next.multipass.enabled = impl->selection->shader_pass_enabled != 0;
229 const std::uint32_t pass_count = std::min(
230 impl->selection->shader_pass_count, ipc::MAX_PASS_COUNT);
231 next.multipass.shader_names.reserve(pass_count);
232 for (std::uint32_t index = 0; index < pass_count; ++index) {
233 const char *name_begin = impl->selection->shader_pass_names[index];
234 const char *name_limit = name_begin + ipc::MAX_SHADER_NAME;
235 const char *shader_name_end =
236 std::find(name_begin, name_limit, '\0');
237 if (shader_name_end != name_begin) {
238 next.multipass.shader_names.emplace_back(name_begin,
239 shader_name_end);
240 }
241 }
242
243 const std::uint32_t uniform_count = std::min(
244 impl->selection->custom_uniform_count, ipc::MAX_CUSTOM_UNIFORMS);
245 next.uniform_values.reserve(uniform_count);
246 for (std::uint32_t index = 0; index < uniform_count; ++index) {
247 const char *name_begin =
248 impl->selection->custom_uniform_names[index];
249 const char *name_limit = name_begin + ipc::MAX_UNIFORM_NAME;
250 const char *uniform_name_end =
251 std::find(name_begin, name_limit, '\0');
252 next.uniform_values.push_back(
253 {std::string(name_begin, uniform_name_end),
254 impl->selection->custom_uniform_values[index]});
255 }
256
257 next.playback.repeat = impl->selection->repeat_enabled != 0;
258 next.playback.normalized_time =
259 impl->selection->normalized_time_enabled != 0;
260 next.overlay.display_filter =
261 impl->selection->display_filter_enabled != 0;
262 next.overlay.watermark_enabled =
263 impl->selection->watermark_enabled != 0;
264 const auto watermark_end = std::find(
265 std::begin(impl->selection->watermark_text),
266 std::end(impl->selection->watermark_text), '\0');
267 next.overlay.watermark_text.assign(
268 std::begin(impl->selection->watermark_text), watermark_end);
269 next.overlay.watermark_color = {impl->selection->watermark_r,
270 impl->selection->watermark_g,
271 impl->selection->watermark_b};
272
273 next.gpu_filters.enabled =
274 impl->selection->gpu_filter_enabled != 0;
275 next.gpu_filters.frame_buffer_size =
276 static_cast<int>(impl->selection->gpu_buffer_size);
277 const std::uint32_t gpu_filter_count = std::min(
278 impl->selection->gpu_filter_count, ipc::MAX_GPU_FILTER_COUNT);
279 next.gpu_filters.filter_indices.reserve(gpu_filter_count);
280 for (std::uint32_t index = 0; index < gpu_filter_count; ++index) {
281 const int filter_index =
282 impl->selection->gpu_filter_indices[index];
283 if (filter_index >= 0) {
284 next.gpu_filters.filter_indices.push_back(filter_index);
285 }
286 }
287
288 next.deep_dream.enabled = impl->selection->dream_enabled != 0;
289 next.deep_dream.fp16 = impl->selection->dream_fp16 != 0;
290 next.deep_dream.gpu_filter_first =
291 impl->selection->dream_gpu_filter_first != 0;
292 next.deep_dream.iterations = impl->selection->dream_iterations;
293 next.deep_dream.maximum_dimension =
294 impl->selection->dream_maximum_dimension;
295 next.deep_dream.channel = impl->selection->dream_channel;
296 next.deep_dream.octaves = impl->selection->dream_octaves;
297 next.deep_dream.jitter = impl->selection->dream_jitter;
298 next.deep_dream.smoothing = impl->selection->dream_smoothing;
299 next.deep_dream.strength = impl->selection->dream_strength;
300 next.deep_dream.feedback = impl->selection->dream_feedback;
301 next.deep_dream.zoom = impl->selection->dream_zoom;
302 next.deep_dream.rotation = impl->selection->dream_rotation;
303 next.deep_dream.octave_scale =
304 impl->selection->dream_octave_scale;
305 const auto dream_model_end = std::find(
306 std::begin(impl->selection->dream_model_path),
307 std::end(impl->selection->dream_model_path), '\0');
308 next.deep_dream.model_path.assign(
309 std::begin(impl->selection->dream_model_path), dream_model_end);
310 const auto dream_layer_end = std::find(
311 std::begin(impl->selection->dream_layer),
312 std::end(impl->selection->dream_layer), '\0');
313 next.deep_dream.layer.assign(
314 std::begin(impl->selection->dream_layer), dream_layer_end);
315
316 next.audio_file.request_sequence =
317 impl->selection->audio_file_sequence;
318 const auto audio_path_end = std::find(
319 std::begin(impl->selection->audio_file_path),
320 std::end(impl->selection->audio_file_path), '\0');
321 next.audio_file.path.assign(
322 std::begin(impl->selection->audio_file_path), audio_path_end);
323 next.audio_file.output_device =
324 impl->selection->audio_output_device;
325 next.audio_file.pass_through =
326 impl->selection->audio_pass_through != 0;
327 next.audio_file.trunc = impl->selection->audio_trunc != 0;
328 next.audio_file.repeat = impl->selection->audio_repeat != 0;
329
330 next.reload.request_sequence = impl->selection->reload_sequence;
331 next.reload.shader_index = impl->selection->reload_shader_index;
332 const auto reload_path_end = std::find(
333 std::begin(impl->selection->reload_shader_path),
334 std::end(impl->selection->reload_shader_path), '\0');
335 next.reload.path.assign(
336 std::begin(impl->selection->reload_shader_path), reload_path_end);
337
338 state = std::move(next);
339 return true;
340 }
341#else
343 std::cerr << "acmxvk: interface control is unavailable on this platform\n";
344 return false;
345 }
346
347 void InterfaceClient::close() noexcept {}
348
349 bool InterfaceClient::is_open() const noexcept { return false; }
350
352 static_cast<void>(state);
353 return false;
354 }
355#endif
356} // 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