Redesign how interface instantiation works

Transferring some argument pack is much easier than trying to
deserialize into an existing object when you also have to transfer more
information than just that object.
This commit is contained in:
Robbert van der Helm
2020-12-08 17:33:51 +01:00
parent f4a5aa91fb
commit 5eb1fe2de2
7 changed files with 100 additions and 42 deletions
+2
View File
@@ -77,6 +77,7 @@ vst3_plugin_sources = [
'src/common/communication/common.cpp',
'src/common/logging/common.cpp',
'src/common/logging/vst3.cpp',
'src/common/serialization/vst3/component.cpp',
'src/common/serialization/vst3/plugin-factory.cpp',
'src/common/configuration.cpp',
'src/common/plugins.cpp',
@@ -108,6 +109,7 @@ host_sources = [
if with_vst3
host_sources += [
'src/common/logging/vst3.cpp',
'src/common/serialization/vst3/component.cpp',
'src/common/serialization/vst3/plugin-factory.cpp',
'src/wine-host/bridges/vst3.cpp',
'src/wine-host/bridges/vst3-impls.cpp',
+11 -5
View File
@@ -56,20 +56,26 @@ void Vst3Logger::log_request(bool is_host_vst, const WantsPluginFactory&) {
}
}
void Vst3Logger::log_response(bool is_host_vst, const Configuration&) {
void Vst3Logger::log_response(
bool is_host_vst,
const std::optional<YaComponent::Arguments>& args) {
if (BOOST_UNLIKELY(logger.verbosity >= Logger::Verbosity::most_events)) {
std::ostringstream message;
message << get_log_prefix(is_host_vst) << " <Configuration>";
if (args) {
message << get_log_prefix(is_host_vst) << " <IComponent* #"
<< args->instance_id << ">";
} else {
message << get_log_prefix(is_host_vst) << " <nullptr>";
}
log(message.str());
}
}
void Vst3Logger::log_response(bool is_host_vst, const YaComponent&) {
void Vst3Logger::log_response(bool is_host_vst, const Configuration&) {
if (BOOST_UNLIKELY(logger.verbosity >= Logger::Verbosity::most_events)) {
std::ostringstream message;
// TODO: Add the instance ID after we implement that
message << get_log_prefix(is_host_vst) << " <IComponent*>";
message << get_log_prefix(is_host_vst) << " <Configuration>";
log(message.str());
}
+2 -1
View File
@@ -49,8 +49,9 @@ class Vst3Logger {
void log_request(bool is_host_vst, const WantsConfiguration&);
void log_request(bool is_host_vst, const WantsPluginFactory&);
void log_response(bool is_host_vst,
const std::optional<YaComponent::Arguments>&);
void log_response(bool is_host_vst, const Configuration&);
void log_response(bool is_host_vst, const YaComponent&);
void log_response(bool is_host_vst, const YaPluginFactory&);
Logger& logger;
+6
View File
@@ -3,6 +3,12 @@
TODO: Once this is more fleshed out, move this document to `docs/`, and perhaps
replace this readme with a link to that document.
TODO: There are now two approaches in use: the factory takes an interface
pointer for serialization and deserializes into an object directly, and the
component uses an args struct because the alternative involving pointers is just
too unsafe (as we also have to communicate additional payload data). This should
probably be unified into only using the latter appraoch.
The VST3 SDK uses an architecture where every object inherits from an interface,
and every interface inherits from `FUnknown` which offers a dynamic casting
interface through `queryInterface()`. Every interface gets a unique identifier.
+22 -7
View File
@@ -16,14 +16,19 @@
#include "component.h"
YaComponent::YaComponent(){FUNKNOWN_CTOR}
YaComponent::YaComponent(
Steinberg::IPtr<Steinberg::Vst::IComponent> component) {
FUNKNOWN_CTOR
YaComponent::Arguments::Arguments(
Steinberg::IPtr<Steinberg::Vst::IComponent> component,
size_t instance_id)
: instance_id(instance_id) {
// `IComponent::getControllerClassId`
component->getControllerClassId(edit_controller_cid);
Steinberg::TUID cid;
if (component->getControllerClassId(cid) == Steinberg::kResultOk) {
edit_controller_cid = std::to_array(cid);
}
}
YaComponent::YaComponent(const Arguments&& args) : arguments(std::move(args)) {
FUNKNOWN_CTOR
// Everything else is handled directly through callbacks to minimize the
// potential for errors
@@ -49,3 +54,13 @@ tresult PLUGIN_API YaComponent::queryInterface(Steinberg::FIDString _iid,
*obj = nullptr;
return Steinberg::kNoInterface;
}
tresult PLUGIN_API YaComponent::getControllerClassId(Steinberg::TUID classId) {
if (arguments.edit_controller_cid) {
std::copy(arguments.edit_controller_cid->begin(),
arguments.edit_controller_cid->end(), classId);
return Steinberg::kResultOk;
} else {
return Steinberg::kNotImplemented;
}
}
+51 -19
View File
@@ -16,9 +16,15 @@
#pragma once
#include <optional>
#include "src/common/serialization/common.h"
#include <bitsery/ext/pointer.h>
#include <bitsery/ext/std_optional.h>
#include <bitsery/traits/array.h>
#include <pluginterfaces/vst/ivstcomponent.h>
using Steinberg::TBool, Steinberg::int32, Steinberg::tresult;
using Steinberg::TBool, Steinberg::int8, Steinberg::int32, Steinberg::tresult;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
@@ -36,14 +42,44 @@ using Steinberg::TBool, Steinberg::int32, Steinberg::tresult;
class YaComponent : public Steinberg::Vst::IComponent {
public:
/**
* Request the Wine plugin host to instantiate a new IComponent to pass
* through a call to `IPluginFactory::createInstance(cid, IComponent::iid,
* These are the arguments for creating a `YaComponentPluginImpl`.
*/
struct Arguments {
/**
* Read arguments from an existing implementation.
*/
Arguments(Steinberg::IPtr<Steinberg::Vst::IComponent> component,
size_t isntance_id);
/**
* The unique identifier for this specific instance.
*/
native_size_t instance_id;
/**
* The class ID of this component's corresponding editor controller. You
* can't use C-style array in `std::optional`s.
*/
std::optional<std::array<int8, std::extent_v<Steinberg::TUID>>>
edit_controller_cid;
template <typename S>
void serialize(S& s) {
s.value8b(instance_id);
s.ext(edit_controller_cid, bitsery::ext::StdOptional{},
[](S& s, auto& cid) { s.container1b(cid); });
}
};
/**
* Message to request the Wine plugin host to instantiate a new IComponent
* to pass through a call to `IPluginFactory::createInstance(cid,
* IComponent::iid,
* ...)`.
*/
struct Create {
// TODO: This should be `std::optional<YaComponent>`, and we need a way
// to deserialize that into an existing YaComponent.
using Response = YaComponent&;
// TODO: Create a `native_tvalue` wrapper, and then also add them here
using Response = std::optional<Arguments>;
Steinberg::TUID cid;
@@ -53,12 +89,11 @@ class YaComponent : public Steinberg::Vst::IComponent {
}
};
YaComponent();
/**
* Create a copy of an existing component.
* Instantiate this instance with arguments read from another interface
* implementation.
*/
explicit YaComponent(Steinberg::IPtr<Steinberg::Vst::IComponent> component);
YaComponent(const Arguments&& args);
/**
* @remark The plugin side implementation should send a control message to
@@ -97,19 +132,16 @@ class YaComponent : public Steinberg::Vst::IComponent {
virtual tresult PLUGIN_API
getState(Steinberg::IBStream* state) override = 0;
template <typename S>
void serialize(S& s) {
s.container1b(edit_controller_cid);
}
private:
/**
* The class ID of this component's corresponding editor controller.
*/
Steinberg::TUID edit_controller_cid;
Arguments arguments;
// TODO: As explained in a few other places, `YaComponent` objects should be
// assigned a unique ID for identification
};
template <typename S>
void serialize(S& s, std::optional<YaComponent::Arguments>& args) {
s.ext(args, bitsery::ext::StdOptional{});
}
#pragma GCC diagnostic pop
+6 -10
View File
@@ -66,17 +66,13 @@ void Vst3Bridge::run() {
// ID to that.
// - Add that ID to `YaComponent` and set it in the object
// we create here.
// - In case `factory` is a null pointer, allow returning
// `nullopt`. Not sure how that is going to work with
// the deserialization.
if (!component) {
// TODO: Handle
if (component) {
// TODO: Generate a unique instance ID
return std::make_optional<YaComponent::Arguments>(
component, 420691337);
} else {
return std::nullopt;
}
// TODO: Implement `YaComponentHostImpl` and create an instance
// based on `component`
YaComponent* removeme = nullptr;
return *removeme;
},
[&](const WantsPluginFactory&) -> WantsPluginFactory::Response {
return *plugin_factory;