12#if defined(__linux__) || defined(__APPLE__)
23#if defined(__linux__) || defined(__APPLE__) || defined(_WIN32)
25#if defined(__linux__) || defined(__APPLE__)
27 sem_t *lock_handle = SEM_FAILED;
29 HANDLE mapping_handle =
nullptr;
30 HANDLE lock_handle =
nullptr;
41#if defined(__linux__) || defined(__APPLE__) || defined(_WIN32)
45#if defined(__linux__) || defined(__APPLE__)
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;
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;
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;
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: "
90 << shm_stat.st_size <<
" bytes; expected "
91 << SHARED_MEMORY_SIZE <<
'\n';
92 impl->open_error_reported =
true;
98 void *mapped = ::mmap(
nullptr, SHARED_MEMORY_SIZE,
99 PROT_READ | PROT_WRITE, MAP_SHARED,
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;
113 impl->selection =
static_cast<ipc::ShaderSelectionData *
>(mapped);
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;
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) {
134 <<
"acmxvk: interface control unavailable: OpenFileMappingW "
135 "failed with Windows error "
136 << open_error <<
'\n';
137 impl->open_error_reported =
true;
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) {
151 <<
"acmxvk: interface control unavailable: MapViewOfFile "
152 "failed with Windows error "
153 << map_error <<
'\n';
154 impl->open_error_reported =
true;
159 impl->selection =
static_cast<ipc::ShaderSelectionData *
>(mapped);
161 impl->open_error_reported =
false;
166 if (
impl->selection !=
nullptr) {
167#if defined(__linux__) || defined(__APPLE__)
168 ::munmap(
impl->selection,
sizeof(ipc::ShaderSelectionData));
170 ::UnmapViewOfFile(
impl->selection);
172 impl->selection =
nullptr;
174#if defined(__linux__) || defined(__APPLE__)
175 if (
impl->shm_fd >= 0) {
179 if (
impl->lock_handle != SEM_FAILED) {
180 ::sem_close(
impl->lock_handle);
181 impl->lock_handle = SEM_FAILED;
184 if (
impl->mapping_handle !=
nullptr) {
185 ::CloseHandle(
impl->mapping_handle);
186 impl->mapping_handle =
nullptr;
188 if (
impl->lock_handle !=
nullptr) {
189 ::CloseHandle(
impl->lock_handle);
190 impl->lock_handle =
nullptr;
196#if defined(__linux__) || defined(__APPLE__)
197 return impl->selection !=
nullptr &&
impl->shm_fd >= 0 &&
198 impl->lock_handle != SEM_FAILED;
200 return impl->selection !=
nullptr &&
impl->mapping_handle !=
nullptr &&
201 impl->lock_handle !=
nullptr;
211 ipc::InterfaceLock lock(
impl->lock_handle);
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);
228 next.multipass.enabled =
impl->selection->shader_pass_enabled != 0;
229 const std::uint32_t pass_count = std::min(
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];
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,
243 const std::uint32_t uniform_count = std::min(
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];
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]});
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};
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(
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);
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);
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;
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);
338 state = std::move(next);
343 std::cerr <<
"acmxvk: interface control is unavailable on this platform\n";
352 static_cast<void>(state);
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