From 6ee905c79f6bdc89037868bedbbd4396b1f6e7f5 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sat, 22 May 2021 17:27:54 +0200 Subject: [PATCH] Add a realtime-safe bitsery extension for variants I blindly assumed the original implementation also did this, but this version `std::variant` objects from being reinitialized if we're deserializing a variant that's also currently active in the object we're deserializing into. For simple structs this won't make any difference, but in yabridge we often use variants to differentiate between things like single precision and double precision audio buffers. Those buffers are allocated on the heap, so recreating the objects every time we deserialize them adds a lot of unnecessary overhead. --- src/common/bitsery/ext/in-place-variant.h | 83 +++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 src/common/bitsery/ext/in-place-variant.h diff --git a/src/common/bitsery/ext/in-place-variant.h b/src/common/bitsery/ext/in-place-variant.h new file mode 100644 index 00000000..5157aa26 --- /dev/null +++ b/src/common/bitsery/ext/in-place-variant.h @@ -0,0 +1,83 @@ +// yabridge: a Wine VST bridge +// Copyright (C) 2020-2021 Robbert van der Helm +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include + +namespace bitsery { +namespace ext { + +/** + * A temporary replacement for `bitsery::ext::StdVariant` to avoid + * reinitializing the object we're deserializing into if the requested variant + * is currently active. For storing audio buffers we use variants containing + * float and double vectors to have a type safe way to disambiguate between + * single and double precision audio, but as it turns out bitsery's + * `std::variant` extension would always reinitialize those objects, undoing our + * efforts to prevent allocations. + */ +template +class InPlaceVariant : public StdVariant { + public: + template + void deserialize(Des& des, std::variant& obj, Fnc&&) const { + size_t index{}; + details::readSize( + des.adapter(), index, sizeof...(Ts), + std::integral_constant{}); + + this->execIndex(index, obj, [this, &des](auto& data, auto index) { + constexpr size_t Index = decltype(index)::value; + using TElem = + typename std::variant_alternative>::type; + + // Most of this is copied directly from the original implementation. + // We just added the check here to reuse the existing object if + // possible. + if (std::holds_alternative(data)) { + TElem& item = std::get(data); + this->serializeType(des, item); + } else { + TElem item = ::bitsery::Access::create(); + this->serializeType(des, item); + data = std::variant(std::in_place_index_t{}, + std::move(item)); + } + }); + } +}; + +template +InPlaceVariant(Overloads...) -> InPlaceVariant; +} // namespace ext + +namespace traits { + +template +struct ExtensionTraits, Variant> { + static_assert( + bitsery::details::IsSpecializationOf::value, + "InPlaceVariant only works with std::variant"); + using TValue = void; + static constexpr bool SupportValueOverload = false; + static constexpr bool SupportObjectOverload = true; + static constexpr bool SupportLambdaOverload = false; +}; + +} // namespace traits +} // namespace bitsery