1
0
Fork 0

Use a stable ordering for the parameters

As the fields are specified on the parameters struct.
This commit is contained in:
Robbert van der Helm 2022-01-31 22:20:09 +01:00
parent 260d38580b
commit fe8f1d27d6
3 changed files with 35 additions and 19 deletions

View file

@ -29,6 +29,7 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
// `persist` function we'll create functions that serialize and deserialize those fields // `persist` function we'll create functions that serialize and deserialize those fields
// individually (so they can be added and removed independently of eachother) using JSON. // individually (so they can be added and removed independently of eachother) using JSON.
let mut param_mapping_insert_tokens = Vec::new(); let mut param_mapping_insert_tokens = Vec::new();
let mut param_id_string_tokens = Vec::new();
let mut field_serialize_tokens = Vec::new(); let mut field_serialize_tokens = Vec::new();
let mut field_deserialize_tokens = Vec::new(); let mut field_deserialize_tokens = Vec::new();
for field in fields.named { for field in fields.named {
@ -96,6 +97,7 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
// variant // variant
param_mapping_insert_tokens param_mapping_insert_tokens
.push(quote! { param_map.insert(#param_id, self.#field_name.as_ptr()); }); .push(quote! { param_map.insert(#param_id, self.#field_name.as_ptr()); });
param_id_string_tokens.push(quote! { #param_id, });
} }
(None, Some(stable_name)) => { (None, Some(stable_name)) => {
// We don't know anything about the field types, but because we can generate this // We don't know anything about the field types, but because we can generate this
@ -158,6 +160,10 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
param_map param_map
} }
fn param_ids(self: std::pin::Pin<&Self>) -> &'static [&'static str] {
&[#(#param_id_string_tokens)*]
}
fn serialize_fields(&self) -> ::std::collections::HashMap<String, String> { fn serialize_fields(&self) -> ::std::collections::HashMap<String, String> {
let mut serialized = ::std::collections::HashMap::new(); let mut serialized = ::std::collections::HashMap::new();

View file

@ -369,6 +369,10 @@ pub trait Params {
/// is only valid as long as this pinned object is valid. /// is only valid as long as this pinned object is valid.
fn param_map(self: Pin<&Self>) -> HashMap<&'static str, ParamPtr>; fn param_map(self: Pin<&Self>) -> HashMap<&'static str, ParamPtr>;
/// All parameter IDs from `param_map`, in a stable order. This order will be used to display
/// the parameters.
fn param_ids(self: Pin<&Self>) -> &'static [&'static str];
/// Serialize all fields marked with `#[persist = "stable_name"]` into a hash map containing /// Serialize all fields marked with `#[persist = "stable_name"]` into a hash map containing
/// JSON-representations of those fields so they can be written to the plugin's state and /// JSON-representations of those fields so they can be written to the plugin's state and
/// recalled later. This uses [serialize_field] under the hood. /// recalled later. This uses [serialize_field] under the hood.

View file

@ -111,12 +111,12 @@ struct WrapperInner<'a, P: Plugin> {
/// between process calls. /// between process calls.
output_slices: RwLock<Vec<&'a mut [f32]>>, output_slices: RwLock<Vec<&'a mut [f32]>>,
/// The keys from `param_map` in a stable order.
param_hashes: Vec<u32>,
/// A mapping from parameter ID hashes (obtained from the string parameter IDs) to pointers to /// A mapping from parameter ID hashes (obtained from the string parameter IDs) to pointers to
/// parameters belonging to the plugin. As long as `plugin` does not get recreated, these /// parameters belonging to the plugin. As long as `plugin` does not get recreated, these
/// addresses will remain stable, as they are obtained from a pinned object. /// addresses will remain stable, as they are obtained from a pinned object.
param_by_hash: HashMap<u32, ParamPtr>, param_by_hash: HashMap<u32, ParamPtr>,
/// The keys from `param_map` in a stable order.
param_hashes: Vec<u32>,
/// The default normalized parameter value for every parameter in `param_ids`. We need to store /// The default normalized parameter value for every parameter in `param_ids`. We need to store
/// this in case the host requeries the parmaeter later. /// this in case the host requeries the parmaeter later.
param_defaults_normalized: Vec<f32>, param_defaults_normalized: Vec<f32>,
@ -168,10 +168,10 @@ impl<P: Plugin> WrapperInner<'_, P> {
is_processing: is_processing:
AtomicBool::new(false), AtomicBool::new(false),
output_slices: RwLock::new(Vec::new()), output_slices: RwLock::new(Vec::new()),
param_by_hash:
HashMap::new(),
param_hashes: param_hashes:
Vec::new(), Vec::new(),
param_by_hash:
HashMap::new(),
param_defaults_normalized: param_defaults_normalized:
Vec::new(), Vec::new(),
param_id_to_hash: param_id_to_hash:
@ -186,29 +186,35 @@ impl<P: Plugin> WrapperInner<'_, P> {
// XXX: This unsafe block is unnecessary. rust-analyzer gets a bit confused and this this // XXX: This unsafe block is unnecessary. rust-analyzer gets a bit confused and this this
// `read()` function is from `IBStream` which it definitely is not. // `read()` function is from `IBStream` which it definitely is not.
let param_map = unsafe { wrapper.plugin.read() }.params().param_map(); let param_map = unsafe { wrapper.plugin.read() }.params().param_map();
let param_ids = unsafe { wrapper.plugin.read() }.params().param_ids();
nih_debug_assert!( nih_debug_assert!(
!param_map.contains_key(BYPASS_PARAM_ID), !param_map.contains_key(BYPASS_PARAM_ID),
"The wrapper alread yadds its own bypass parameter" "The wrapper alread yadds its own bypass parameter"
); );
wrapper.param_by_hash = param_map // Only calculate these hashes once, and in the stable order defined by the plugin
.iter() let param_id_hashes_ptrs = param_ids.iter().filter_map(|id| {
.map(|(id, p)| (hash_param_id(id), *p)) let param_ptr = param_map.get(id)?;
Some((id, hash_param_id(id), param_ptr))
});
wrapper.param_hashes = param_id_hashes_ptrs
.clone()
.map(|(_, hash, _)| hash)
.collect(); .collect();
wrapper.param_hashes = wrapper.param_by_hash.keys().copied().collect(); wrapper.param_by_hash = param_id_hashes_ptrs
wrapper.param_defaults_normalized = wrapper .clone()
.param_hashes .map(|(_, hash, ptr)| (hash, *ptr))
.iter()
.map(|hash| unsafe { wrapper.param_by_hash[hash].normalized_value() })
.collect(); .collect();
wrapper.param_id_to_hash = param_map wrapper.param_defaults_normalized = param_id_hashes_ptrs
.into_keys() .clone()
.map(|id| (hash_param_id(id), id)) .map(|(_, _, ptr)| unsafe { ptr.normalized_value() })
.collect(); .collect();
wrapper.param_hash_to_id = wrapper wrapper.param_id_to_hash = param_id_hashes_ptrs
.param_id_to_hash .clone()
.iter() .map(|(id, hash, _)| (hash, *id))
.map(|(&hash, &id)| (id, hash)) .collect();
wrapper.param_hash_to_id = param_id_hashes_ptrs
.map(|(id, hash, _)| (*id, hash))
.collect(); .collect();
// FIXME: Right now this is safe, but if we are going to have a singleton main thread queue // FIXME: Right now this is safe, but if we are going to have a singleton main thread queue