Fields & Field Containers

One central goal in OpenSG's design is easy to use thread-safe data. To do that right, you need to replicate the data so that every thread can have its private copy (called aspect) to work on. At some point these different copies will have to be synchronized, and then the parts that actually changed need to be copied from one aspect to another. To do that, the system needs to know what actually changed. As C++ is not reflective, i.e. the classes cannot tell the system which members they have, OpenSG needs to keep track of the changes. That's what Fields and FieldContainers are for.

Creating a FieldContainer instance

FieldContainer can be created in two ways: By using the FieldContainerFactory or from the class's prototype. You cannot create instances of FieldContainers neither by creating automatic or static variables nor by calling new. You have to use the mentioned two ways.

For generic loaders it is useful to create an object by name, and this is what the factory is for. The factory is a singleton, the single instance can be accessed via FieldContainerFactory::the(), which has functions to create arbitrary field containers, with some special versions to directly create different subsets of field containers (Nodes, NodeCores, Attachments).

For reasons connected to multi-threading (s. [threadsafety]) specific kinds of pointers have to be used. For every FieldContainer type fc there is a specific pointer type fcPtr. It has all the features of a standard pointer, i.e. it can be dereferenced via -> and it can be downcasted to a derived type by DerivedPtr.dcast( ParentPtr );.

Creating a new instance of a specific class is done by calling fcPtr var=fcPtrcreate().

Reference counting

FieldContainers are reference-counted. They are created with a reference count of 0, and the reference count can be manipulated through addRefCP() and subRefCP().

The system increases the reference count only when it stores a reference to an object in the system, e.g. when a node is attached to another node. It does not increase the reference counter for every parameter that is passed around, the pointers mentioned in [fcinstance] are not smart pointers.

The reference count is decreased when an object is removed from the system, e.g. when a node is detached from another node, or explicitly using subRefCP(). If the reference count goes to or below 0, the object is removed. Note that objects are created with a reference count of zero, so if a new object (refCnt: 0) is attached to a node (increasing the refCnt to 1) and removed later on (decreasing it to 0), it will be destroyed. Increasing the reference count before removing it is needed to prevent the destruction.

Manipulation

The FieldContainer is the basic unit for multi-thread safety. To synchronize changes between different copies of the data the system needs to know when and what changed.

This has to be done explicitly by the program. Thus, before changing a FieldContainer beginEditCP(fcPtr, fieldMask); has to be called. After the changes to the FieldContainer are done this also has to be communicated by calling endEditCP(fcPtr, fieldMask);. Here, fcPtr is the pointer to the FieldContainer being changed, fieldMask is a bit mask describing the fields that are changed.

Every FieldContainer defines constants for all its fields that can be used to set up this mask. The naming convention is [FieldContainer]::[FieldName]FieldMask, e.g. Geometry::PositionsFieldMask. These masks can be or-ed together to create the full mask of fields that are changed.

To simplify the begin/endEdit sequences and make it easier to not forget closing the edit (which can result in pretty surprising error) there is a helper class osg::CPEditor.

The CPEditor is an equivalent to the std::auto_ptr in the sense that as it calls the beginEdit as soon as it is created and calls the endEdit as soon as it goes out of scope.

Example: Use CPEditor for begin/endEdit:

    GeoPTypesPtr type = GeoPTypesUI8::create();        
    {
        CPEditor te(type, GeoPTypesUI8::GeoPropDataFieldMask);
        
        type->addValue(GL_POLYGON  );
        type->addValue(GL_TRIANGLES);
        type->addValue(GL_QUADS    );
    }

As a further (small) simplification there is a CPEdit macro that creates the CPEditor instance automatically.

Example: Use CPEdit for begin/endEdit:

    GeoPTypesPtr type = GeoPTypesUI8::create();        
    {
        CPEdit(type, GeoPTypesUI8::GeoPropDataFieldMask);
        
        type->addValue(GL_POLYGON  );
        type->addValue(GL_TRIANGLES);
        type->addValue(GL_QUADS    );
    }

FieldContainer attachments

OpenSG field containers and nodes do not feature an unused pointer to attach data, usually called user data in other systems. Instead, many field containers feature a map to attach specific kinds of field containers called attachments. The most important ones are Nodes and NodeCores, but many other like Window, Viewport, Camera, etc. are derived from AttachmentContainer and, therefore, can carry attachments.

Attachments have to be derived from Attachment (see Creating New FieldContainer Classes for details on how to do that). There are also predefined attachments, right now the only one is NameAttachment, which allows assigning a name to the field containers.

Every AttachmentContainer can hold an arbitrary number of attachments. Attachments are divided into separate groups, and there can be only one attachment of every group attached to an AC. Most attachments are a group, but if needed new ones can be used as replacements for their parents.

Data separation & Thread safety

One of the primary design goals of OpenSG is supporting multi-threaded applications. For asynchronous threads that means that every thread might need its private copy of the data. To combine that with easy usability and efficient access we decided to replicate at the field container level.

When a field container is created not only one instance is created but multiple, per default 2. These are called aspects, and every running thread is associated with one of them. Whenever data is changed in a thread, only the aspect that's associated with it is changed, the rest is left as is.


Generated on Mon Mar 17 11:10:26 2008 for OpenSG by  doxygen 1.5.5