One of the heaviest source of memory allocations was for std::string objects. It’s not really a surprising considering one of Minko’s biggest feature is dynamic data binding between the engine data – on the CPU – and the shader data – on the GPU. And “dynamic” means we have to create those bindings according to some naming conventions and explicit declarations made in “effect” files using JSON. Read: we use lots of strings.
We use strings for material property names, but also for the camera properties or the geometry vertex attribute names. It makes the engine very flexible and extensible while keeping some important aspects of strict typing using templates. But it uses a lot of memory because each property names is stored multiple times.
Materials for example are highly likely to share most (if not all) of their property names. If you have 10 of those materials, the property name strings will use 10 times too much memory.
To solve this, I’ve implemented the flyweight design pattern.
It was very easy to do thanks to the unordered_set STL container and especially the behavior of the “insert” method which gives us a direct access to the corresponding inserted or pre-existing element pointer:
template <typename T>
const T* _value;
static std::unordered_set<T> values;
Flyweight(const T& v) :
template <typename... U>
Flyweight(U... args) :
_value(_value = &(*(getValues().emplace(args...).first)))
Flyweight(const Flyweight& f) :
Flyweight(Flyweight&& f) :
f._value = nullptr;
operator==(const Flyweight<T>& rhs) const
return _value == rhs._value;
operator=(const Flyweight<T>& rhs)
_value = rhs._value;
_value = other._value;
There are a few insteresting things:
- the move constructor is here to make sure that creating Flyweight<T> from a T rvalue should not make any copies;
- thanks to variadic templates, the forward constructor should allow the implicit conversion from any T constructor, for example you a Flyweight<std::string> will be implicitely created when using a string literal;
- the move assignment operator should make manipulations of Flyweight-based containers more efficient;
- the templated cast T operator override should allow Flyweight<T> to T implicit conversions.
Thanks to the forward constructor and the templated cast operator, you can easily change the type of any T by Flyweight<T> without modifying much of the actual implementation – if at all!
The Flyweight class will soon be in Minko on github.