From 5d3119180699ab459395865f0cc39cb073235d44 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sun, 11 Sep 2022 18:40:46 +0200 Subject: [PATCH] Implement CLAP callback requests --- .../bridges/clap-impls/host-proxy.cpp | 25 ++++++++++++++++++- src/wine-host/bridges/clap-impls/host-proxy.h | 10 ++++++++ src/wine-host/bridges/clap.cpp | 8 +++--- src/wine-host/bridges/clap.h | 18 ++++++------- src/wine-host/bridges/common.h | 14 +++++------ 5 files changed, 54 insertions(+), 21 deletions(-) diff --git a/src/wine-host/bridges/clap-impls/host-proxy.cpp b/src/wine-host/bridges/clap-impls/host-proxy.cpp index 51f5a661..979bfc06 100644 --- a/src/wine-host/bridges/clap-impls/host-proxy.cpp +++ b/src/wine-host/bridges/clap-impls/host-proxy.cpp @@ -65,5 +65,28 @@ clap_host_proxy::host_request_process(const struct clap_host* host) { void CLAP_ABI clap_host_proxy::host_request_callback(const struct clap_host* host) { - // TODO: Implement + assert(host && host->host_data); + auto self = static_cast(host->host_data); + + // TODO: Log + + // Only schedule a `clap_plugin::on_main_thread()` call if we don't already + // have a pending one. This limits the number of unnecessarily stacked + // calls. + bool expected = false; + if (self->has_pending_host_callbacks_.compare_exchange_strong(expected, + true)) { + // We're acquiring a lock on the instance and then move it into the task + // to prevent this instance from being removed before this callback has + // been run + auto instance_lock = + self->bridge_.get_instance(self->owner_instance_id()); + self->bridge_.main_context_.schedule_task( + [self, instance_lock = std::move(instance_lock)]() { + const auto& [instance, _] = instance_lock; + self->has_pending_host_callbacks_.store(false); + + instance.plugin->on_main_thread(instance.plugin.get()); + }); + } } diff --git a/src/wine-host/bridges/clap-impls/host-proxy.h b/src/wine-host/bridges/clap-impls/host-proxy.h index 7832eea9..97efe188 100644 --- a/src/wine-host/bridges/clap-impls/host-proxy.h +++ b/src/wine-host/bridges/clap-impls/host-proxy.h @@ -16,6 +16,8 @@ #pragma once +#include + #include #include "../../common/serialization/clap/plugin-factory.h" @@ -78,4 +80,12 @@ class clap_host_proxy { * the start of the struct and directly casting the `clap_host_t*`. */ const clap_host_t host_vtable_; + + /** + * Keeps track of whether there are pending host callbacks. Used to prevent + * calling `clap_plugin::on_main_thread()` multiple times in a row when the + * plugin calls `clap_host::request_callback()` multiple times before + * `clap_plugin::on_main_thread()` is called. + */ + std::atomic_bool has_pending_host_callbacks_ = false; }; diff --git a/src/wine-host/bridges/clap.cpp b/src/wine-host/bridges/clap.cpp index 15ee1c21..307362c8 100644 --- a/src/wine-host/bridges/clap.cpp +++ b/src/wine-host/bridges/clap.cpp @@ -306,10 +306,6 @@ void ClapBridge::close_sockets() { sockets_.close(); } -size_t ClapBridge::generate_instance_id() noexcept { - return current_instance_id_.fetch_add(1); -} - std::pair> ClapBridge::get_instance(size_t instance_id) noexcept { std::shared_lock lock(object_instances_mutex_); @@ -318,6 +314,10 @@ ClapBridge::get_instance(size_t instance_id) noexcept { object_instances_.at(instance_id), std::move(lock)); } +size_t ClapBridge::generate_instance_id() noexcept { + return current_instance_id_.fetch_add(1); +} + // TODO: Implement audio processing // std::optional ClapBridge::setup_shared_audio_buffers( // size_t instance_id) { diff --git a/src/wine-host/bridges/clap.h b/src/wine-host/bridges/clap.h index 69db7b13..6c56bbd5 100644 --- a/src/wine-host/bridges/clap.h +++ b/src/wine-host/bridges/clap.h @@ -305,6 +305,15 @@ class ClapBridge : public HostBridge { // } // } + /** + * Fetch the plugin instance along with a lock valid for the instance's + * lifetime. This is mostly just to save some boilerplate everywhere. Use + * C++17's structured binding as syntactic sugar to not have to deal with + * the lock handle. + */ + std::pair> + get_instance(size_t instance_id) noexcept; + /** * A logger instance we'll use to log about failed * `clap_host::get_extension()` calls, so they can be hidden on verbosity @@ -324,15 +333,6 @@ class ClapBridge : public HostBridge { */ size_t generate_instance_id() noexcept; - /** - * Fetch the plugin instance along with a lock valid for the instance's - * lifetime. This is mostly just to save some boilerplate everywhere. Use - * C++17's structured binding as syntactic sugar to not have to deal with - * the lock handle. - */ - std::pair> - get_instance(size_t instance_id) noexcept; - /** * Sets up the shared memory audio buffers for a plugin instance plugin * instance and return the configuration so the native plugin can connect to diff --git a/src/wine-host/bridges/common.h b/src/wine-host/bridges/common.h index 69d0e235..17cc3a6a 100644 --- a/src/wine-host/bridges/common.h +++ b/src/wine-host/bridges/common.h @@ -100,6 +100,13 @@ class HostBridge { */ const ghc::filesystem::path plugin_path_; + /** + * The IO context used for event handling so that all events and window + * message handling can be performed from a single thread, even when hosting + * multiple plugins. + */ + MainContext& main_context_; + protected: /** * Used as part of the watchdog that shuts down a plugin when the remote @@ -109,13 +116,6 @@ class HostBridge { */ virtual void close_sockets() = 0; - /** - * The IO context used for event handling so that all events and window - * message handling can be performed from a single thread, even when hosting - * multiple plugins. - */ - MainContext& main_context_; - /** * A logger, just like we have on the plugin side. This is normally not * needed because we can just print to STDERR, but this way we can