Component system in Version 2 (Part 1)
Posted on Nov 6, 2015Lightmetrica Devepment
Hi,
In this article, I will try to describe the improvement of component system in Lightmetrica Version 2 currently in development.
One of the major objective of this project is to create highly extensive framework for the renderer development. In order to achieve this goal, the user of the framework wants to have flexibility to extend the framework with low cost.
The overall design of the Lightmetrica
follows the design pattern known as dependency injection (DI),
which make is possible to decouple the dependencies between classes.
In particular, the all extensibule classes and its functions are
accessed via interfaces (in C++, the class with all member functions are pure virtual member functions with =0
).
All instantiation of interfaces are controlled via factory functions.
In Lightmetrica v1, ComponentFactory
is responsible for this.
Following this design, the user of the interface don’t have to know
the header of the actual implementation.
For example, let struct A
be a interface defined in a.h
and struct A1
inheris and implements A
defined in a1.cpp
.
The macros like LM_COMPONENT_INTERFACE_DEF
is required to register the class to the component system.
// Header a.h
struct A : public Component
{
LM_COMPONENT_INTERFACE_DEF("A");
virtual void F() = 0;
...
};
// Header a1.cpp
struct A1 final : public A
{
LM_COMPONENT_IMPL_DEF("A1");
void F() { ... }
...
};
LM_COMPONENT_REGISTER_IMPL(A1, A);
And when the user want to create an instance of A1,
the function ComponentFactory::Create
is used.
std::unique_ptr<A> a(ComponentFactory::Create("A1"));
...
Note that when we want to create an instance,
we don’t have to neighter include nor define unnecessary headers (in this case, a1.h
or something), which make it possible to reduce code size as well as the compilation time.
In order to allow the user to extend the feature of the framework as plugins,
the implementation of the interface is separated to the different dynamic libraries.
All the implementer of the plugin need to do is to implement an interface (e.g., implements BSDF
interface in order to extend materials).
This is the core feature to retain extensibility to the framework.
However one problem is found through the development.
With the version 1, the user needs to
the core library (liblightmetrica
) and plugins need to
be compiled compiled with the same compiler, that is,
that is, the implementation of the plugin is ABI dependent.
It’s because the implementation utilizes dynamic polymorphism of C++,
the mechanism implicitly assumes the existence and the structure of the vtable
is same between caller and the creator of the instance.
Unnability to combine different kinds of compilers restricts the usability of the framework. In order to resolve the problem, in the version 2, we redesign the component system from scratch. The new component system make is possible to create portable plugins that the implemener of the plugin can freely selects the compilers, irrespective to what compiler is used for the core library.
In the next article, we will describe the design of the new component system and how we implemented it.