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;
39#if defined(__linux__) || defined(__APPLE__) || defined(_WIN32)
43#if defined(__linux__) || defined(__APPLE__)
45 if (
impl->lock_handle == SEM_FAILED) {
46 if (!
impl->open_error_reported) {
48 impl->open_error_reported =
true;
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;
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;
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;
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;
94 impl->selection =
static_cast<ipc::ShaderSelectionData *
>(mapped);
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;
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;
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;
133 impl->selection =
static_cast<ipc::ShaderSelectionData *
>(mapped);
135 impl->open_error_reported =
false;
140 if (
impl->selection !=
nullptr) {
141#if defined(__linux__) || defined(__APPLE__)
142 ::munmap(
impl->selection,
sizeof(ipc::ShaderSelectionData));
144 ::UnmapViewOfFile(
impl->selection);
146 impl->selection =
nullptr;
148#if defined(__linux__) || defined(__APPLE__)
149 if (
impl->shm_fd >= 0) {
153 if (
impl->lock_handle != SEM_FAILED) {
154 ::sem_close(
impl->lock_handle);
155 impl->lock_handle = SEM_FAILED;
158 if (
impl->mapping_handle !=
nullptr) {
159 ::CloseHandle(
impl->mapping_handle);
160 impl->mapping_handle =
nullptr;
162 if (
impl->lock_handle !=
nullptr) {
163 ::CloseHandle(
impl->lock_handle);
164 impl->lock_handle =
nullptr;
170#if defined(__linux__) || defined(__APPLE__)
171 return impl->selection !=
nullptr &&
impl->shm_fd >= 0 &&
impl->lock_handle != SEM_FAILED;
173 return impl->selection !=
nullptr &&
impl->mapping_handle !=
nullptr &&
impl->lock_handle !=
nullptr;
183 ipc::InterfaceLock lock(
impl->lock_handle);
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);
196 next.multipass.enabled =
impl->selection->shader_pass_enabled != 0;
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];
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);
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];
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]});
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};
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);
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);
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);
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;
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);
268 state = std::move(next);
273 std::cerr <<
"acmxvk: interface control is unavailable on this platform\n";
282 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