Component system in Version 2 (Part 1)

Posted on Nov 6, 2015
Lightmetrica 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.