XGL Architecture Guide
  Rechercher uniquement dans ce livre
Télécharger cet ouvrage au format PDF

Object Interactions

4

This chapter provides information on XGL objects. It includes information on the following topics:
  • API object instantiation
  • Pipeline loading and pipeline object instantiation
  • Communication between objects
  • Object destruction

Opening XGL

The API operator xgl_open()initializes XGL. This operator first checks that the XglGlobalState object does not already exist (to prevent multiple xgl_open() calls). If this is the case, the xgl_open() routine instantiates the Global State object. The Global State object maintains the state of the XGL environment and is responsible for the loading and manipulation of the pipeline shared library objects and the corresponding XglPipeLib objects. There is only one Global State object in the XGL environment.
The XglGlobalState object loads the software pipeline shared library and then calls the function xgli_create_PipeLib() (defined in the software pipeline shared library) to create the XglSwpLib object. The XglSwpLib object defines the member functions necessary to create and destroy the software pipeline subclasses of the DI provided classes. For more information on pipeline loading, see "How the Pipelines Are Created and Managed" on page 64.
The Global State object also creates the System State object. The System State object handles API object creation and destruction, error mode setting, and internal error reporting. In future releases, it may be possible to have more than one System State object. If multiple System States are allowed, the Global State object will manage them.

Note - The software pipeline is loaded when XGL is opened so that the open fails if there is no software pipeline.

How API Calls Are Mapped to XGL Internal Calls

From the API perspective, all API-visible XGL objects are device independent. The application manipulates XGL objects and executes primitives via C object handles and C function calls. The C function calls and object handles are mapped to the C++ internal code by a set of C wrappers that provide the translation between the C API and the C++ device-independent classes and objects. For example, the application might ask to have a 2D Context created:
ctx = xgl_object_create(SysState, XGL_2D_CTX, NULL, NULL);

This call is mapped in XglWrapApi.cc to the C++ internal call:
obj = (XglObject*)((XglSysState*) sys_state)->createContext2d();

Each API object has a wrapper function that maps its object-specific operators and attribute set and get calls to internal calls. For example, an API request to set the line style in a 2D Context object:
xgl_object_set(ctx, XGL_CTX_LINE_STYLE, XGL_LINE_PATTERNED, NULL);

is mapped to the internal call:
ctx2d->setLineStyle(<argument>);

Instantiation of API Objects

As mentioned in Chapter 1, an object consists of member data and the methods (or functions) needed to operate on that data. A class is a container for data and methods common to a set of similar objects. An instance of a class inherits the data and methods of each of its parent classes, starting from the top of the class tree and adding the data and methods from each of the derived classes. Thus, for example, a 3D Context object has the following inheritance:

Imported image(504x75)

The 3D Context object inherits the following data and methods from its parent classes:
XglDbgObjectThis object is intended for debugging purposes only.
XglApiObjectAdds fields for the object type and application data.
XglObjectAdds mechanisms to enable information on state changes to be communicated between objects.
XglCtxObjectAdds data and methods to enable the use of the Pcache object.
XglContextAdds data and methods shared between 2D and 3D Context objects. For example, surface front face attributes are available to both 2D and 3D objects.
XglContext3dAdds 3D Context object-specific data and methods, such as attributes for depth cue color.

System State Object and API Object Lists

All API objects are instantiated through the System State object. The object creation call is routed by the wrapper function to the System State object, which instantiates the object. The System State object keeps track of all API objects in two lists: a list of pointers to Device objects and a list of pointers to all other API objects. When the System State object instantiates an API object, it adds a pointer to the object to the list.
For most API objects, object instantiation is performed in a reasonably straight-forward way, with the System State object invoking object instantiation and the object constructor initializing default values. However, object instantiation for the Device object and the Context object is more complex, since each of these API objects is actually composed of more than one internal object. The sections that follow discuss the components of the Device and Context objects.

What Is a Device Object?

From the perspective of the XGL API, a Device object is an abstract entity that represents a drawing surface. From the internal perspective, however, a Device object is actually composed of two objects that are linked together throughout the lifetime of the objects. The components of the Device object are:
  • A device-independent object subclassed and instantiated from one of the leaves of the Device class, such as XglRasterWin.
  • A device-dependent object subclassed and instantiated from a corresponding leaf of the XglDpDev class, which in the case of a window raster device might be XglDpDevGx. The XglDpDev subclassed object is created as part of the Device object creation process and remains associated with the Device object for the lifetime of the Device instance.
The two ways of looking at a Device object are illustrated in Figure 4-1, using a window raster device as an example.

Graphique

Figure 4-1

The Device object was designed with separate device-independent and device-dependent components to isolate the Device object's device-dependent operations from its device-independent operations. In this way, each pipeline implementation can define the actual device-specific operations for the device.
Instantiation of a Device object is a complex process involving numerous steps. See "How the Pipelines Are Created and Managed" on page 64 for information on this process.

What Is a Context Object?

The Context object is responsible for rendering and storing attribute values. As with the Device object, the conceptual API view of the Context object is different from its actual internal representation. From the perspective of the application, the Context object is a single entity that represents the graphics pipeline. Internally, however, the Context object has several component objects. When the API Context object is initially created, it is composed of the following two objects:
  • A device-independent object subclassed and instantiated from one of the leaves of the Context class, such as XglContext3d.
  • A device-dependent object subclassed and instantiated from one of the leaves of the XglPipeCtx class for the software pipeline, such as XglSwpCtx3d. The software pipeline-context object is linked with the Context object for the lifetime of the Context object.
Additionally, when the Device object is associated with the Context object, the following object becomes a component of the Context object:
  • A device-dependent object for the device pipeline that is subclassed and instantiated from the leaves of the XglPipeCtx class that correspond to the already existing Device and Context, such as XglDpCtx3dGx.
Figure 4-2 shows a 2D Context object that has been associated with a GX frame buffer.

Graphique

Figure 4-2

Device and Context Association

The device pipeline-context object is created during the first association of a Context and a Device and corresponds to a specific Device-Context pair. When a Device and a Context are first associated, the device-dependent part of the Device object creates a device pipeline-context object (XglDpCtx) for the device. If a Device is set on a Context with which it was previously associated, the Context will disassociate from its current device and ask the Device to retrieve the handle to the pre-existing XglDpCtx object. If the Device object finds a pre-existing entry for an XglDpCtx object for this Context, it returns a pointer to the object to the Context.
Figure 4-3 illustrates the relationships between the various DI and Dp objects when the Device is first associated with the Context. It shows a Window Raster Device object, including its device-independent part (XglRasterWin) and its device-dependent part (XglDpDev), the device pipeline-context object, and the Device's list of existing XglDpCtx objects.

Graphique

Figure 4-3

The Device object can be concurrently associated with more than one Context object. In this case, there is a device pipeline-context object created for each Device-Context pair. Figure 4-4 shows a Device object with two associated Context objects.

Graphique

Figure 4-4

How the Pipelines Are Created and Managed

Pipeline creation is a dynamic process that occurs in phases as XGL is opened and initialized. The process can be summarized in the following general steps:
  1. The XGL environment is set up.

  2. A Device object is created.

  3. A Context object is created.

  4. The Device object is associated with the Context object.

The following sections describe this process. See Figure 4-5 on page 67 for an illustration of an instantiated runtime system.

Note - It is assumed that only one software pipeline exists in the XGL system, although the actual software pipeline can vary from one runtime environment to another.

The XGL Environment Is Set Up

Imported image(136x194)

When an application calls xgl_open() to initialize XGL, the following set of events occurs. The events are illustrated in the diagram in the margin. Objects are represented by ovals; functions are in boxes. Black arrows represent control flow; shaded arrows represent pointers.
  1. The xgl_open() call creates the XglGlobalState object, which is unique in the XGL system.

  2. The XglGlobalState object loads the software pipeline (xglSUNWswp.so.4) through dlopen().

  3. The XglGlobalState object creates the unique XglSwpLib object for the software pipeline by calling xgli_create_PipeLib(), which is defined in xglSUNWswp.so.4 and accessed through dlsym().

  4. The xgl_open() call creates the XglSysState object, and a handle to the System State object is returned to the Global State object. The System State object maintains two object lists: one for Device objects and one for non-Device API objects. During the creation of the System State, the predefined objects (line patterns, markers, stipple rasters) are created.

A Device Object Is Created

When the application calls xgl_object_create() to create a Device object (such as a window raster for a GX frame buffer), it passes in the device type and the window descriptor containing the X window identifying information. The illustration in the margin shows the creation of a Device object. The following events occur:

Imported image(136x303)

  1. The XglSysState object initiates the creation of the device-independent part of the Device object. In the case of a window raster, for example, XglRasterWin is created.

  2. When XglRasterWin is created, it calls XglDrawable::grabDrawable() to obtain an XglDrawable object. The XglDrawable grabDrawable() function determines the type of drawable required for the raster, and returns a drawable object of the appropriate type.

  3. During the creation process of XglRasterWin (the device-independent Device object), the Device object asks the XglGlobalState object to create its device-dependent part.

  4. To start the process of creating the device-dependent part of the Device object, the XglGlobalState object first traverses its XglDpLibList object list to determine if an object for the particular pipeline library already exists. If it finds a matching ID entry in the object list, the object exists, and the process proceeds to Step 6.

    If XglGlobalState does not find a match in its XglDpLibList, XglGlobalState loads the shared library using dlopen(). The name of the shared library is obtained from the Drawable object.

  5. The XglGlobalState object creates the subclassed XglDpLib object for the pipeline being loaded, which is XglDpLibGx in this example. To do this, XglGlobalState calls xgli_create_PipeLib(), which is defined in the pipeline shared library and accessed through dlsym(). xgli_create_PipeLib() creates an instance of the pipeline derived XglDpLib class and returns a pointer. This pointer is then appended to the XglGlobalState's XglDpLibList object for future reference. Note that the XglDpLib object represents one pipeline.

  6. The device pipeline derived XglDpLib object creates or retrieves an instance of the XglDpMgr pipeline derived object. There is one XglDpMgr object per device category, such as a frame buffer.

  1. The XglGlobalState object then asks the XglDpMgr object to create the device-dependent subclassed XglDpDev object. An XglDpDev object is created for each new XGL Device. A pointer to this object is returned to the XglRasterWin object. A pointer to the XglRasterWin object is stored in the System State's list of existing Device objects.

A Context Object Is Created

Imported image(136x153)

When the application calls xgl_object_create() to create a Context object, for example, XglContext3d, the following events occur:
  1. The XglSysState object initiates the creation of the Context 3D object.

  2. The Context object, through the Global State object, requests that the XglSwpLib object create a software pipeline-context object for the specific Context (in this example, XglSwpCtx3d). This object remains attached to the Context object for the lifetime of the Context object.

    The pointer to the initialized 3D Context object is stored in the System State object's list of existing non-Device API objects.

The illustration in the margin shows the creation of a Context object.

The Device Is Associated With the Context

Imported image(136x152)

When the application calls xgl_object_set() to associate the Device object and the Context object, the following events occur:
  1. The Context object will disassociate from the current Device object, if any, and associate with the new Device object. (If the two Device objects are the same, no changes will occur, and the process is complete.)

  2. The Context object will ask the Device object to retrieve the handle to the XglDpCtx object that corresponds to the specific Context. This object may have already been created by a previous association of the same Context and Device.

  3. To retrieve the handle to the XglDpCtx object, the Device object will match against its list of XglDpCtx objects to determine whether an XglDpCtx subclassed object (e.g., XglDpCtx3dGx) for the specific Context and Device

pair already exists. If it exists, the handle to it is returned. Otherwise, the Device object will ask the subclassed XglDpDev object to create an XglDpCtx object for the Context and Device pair. The Device object will store a pointer to these objects for the lifetime of the Context object.
Figure 4-5 illustrates an instantiated runtime system. Black lines represent one-way or two-way relationships between objects. Shaded arrows represent pointers from an object list to the object.

Graphique

Figure 4-5

Object Communication

This section describes how the internal relationship between XGL objects is set up and how messages are passed between objects.

API Object Relationships

During an XGL session, certain API objects are associated with other objects that serve as resources containing information relevant to rendering. This association between objects is established by the application's use of attributes whose values are pointers to objects.
Objects are associated with each other in the following manner: each object has a set of attributes, and one of these attributes may be a pointer to another object (or an array of pointers to objects). For example, during an XGL session, the application associates the Device object with the Context object, and it may also associate additional objects with the Context object, such as Memory Raster objects and Stroke Font objects. A relationship in which an object uses another object is referred to as a using relationship. Table 4-1 lists XGL objects that use other objects via attributes whose values are XGL object pointers.
Table 4-1
User ObjectLinking AttributeUsed Object Class
XglContext2dXGL_CTX_DEVICEXglRasterMem
XglRasterWin
XGL_CTX_GLOBAL_MODEL_TRANS
XGL_CTX_LOCAL_MODEL_TRANS
XGL_CTX_MODEL_TRANS (Read-only)
XGL_CTX_VIEW_TRANS
XGL_MC_TO_DC_TRANS (Read-only)
XglTransform
XglTransform
XglTransform
XglTransform
XglTransform
XGL_CTX_LINE_PATTERN
XGL_CTX_EDGE_PATTERN
XglLinePattern
XglLinePattern
XGL_CTX_MARKERXglMarker
XGL_CTX_RASTER_FPAT
XGL_CTX_SURF_FRONT_FPAT
XglRasterMem
XglRasterMem
Table 4-1
User ObjectLinking AttributeUsed Object Class

XGL_CTX_SFONT_0
XGL_CTX_SFONT_1
XGL_CTX_SFONT_2
XGL_CTX_SFONT_3
XglSfont
XglSfont
XglSfont
XglSfont
XglContext3dAll the above and:
XGL_3D_CTX_NORMAL_TRANS (Read-only)XglTransform (3D)
XGL_3D_CTX_SURF_BACK_FPATXglRasterMem
XGL_3D_CTX_LIGHTSXglLight
XGL_3D_CTX_SURF_FRONT_TMAP
XGL_3D_CX_SURF_BACK_TMAP
XglTextureMap
XGL_3D_CTX_SURF_FRONT_DMAP
XGL_3D_CX_SURF_BACK_DMAP
XglDmapTexture
XglDeviceXGL_DEV_COLOR_MAP
XGL_DEV_CONTEXTS
XglCmap
XglContext
XglRasterMemSame attributes as XglDevice
XglRasterWinSame attributes as XglDevice

Architecture of Object Relationships

The relationship between API objects is implemented with an object registration and message passing mechanism. This mechanism is defined in the XglObject class and is inherited by XglObject's subclasses. It is designed to do the following:
  • Inform interested user objects of changes in used objects.

    When a used object is changed, it might need to communicate its changes to some of its users. For example, if a Device is attached to a Context and the Device color map is changed, the Device needs to warn the Context that the color map has changed. In some cases, however, users might not be concerned about changes and, therefore, might not want to be informed.

  • Delay the destruction of a used object until it no longer has users.

    When an object is asked to destroy itself (by xgl_object_destroy()), it must only destroy itself if it does not have any other object referencing it. If it does have another object referencing it, it must postpone its destruction until it no longer has users. For example, if a Transform object is attached to the Context object and the API operator xgl_object_destroy() is invoked to destroy the Transform, the Transform must not destroy itself immediately, since it is still referenced by the Context object. It can only destroy itself when the Context attribute referencing the Transform is reset to another Transform.

The basic data structure of the object registration and message passing mechanism is the user list.

Object Registration: The User List

An API object stores information on associated objects in its user list. The user list is a linked list that stores a pointer to the user object, a Boolean value indicating whether the user object wants to be notified of changes, and a reference count on a per object basis.
The reference count records how many times a user object has registered itself with the used object. For example, an application program can associate the same Line Pattern object with a Context object twice, once to set the pattern of lines and once to set the pattern of surface edges. If the application does this, the Line Pattern object will register the Context object as a user two times, and the reference count of the Line Pattern object's user list will be incremented accordingly. If the pattern for lines is then changed to a different pattern, the Context will remove itself as a user of the original pattern object for line patterns and register itself as a user of the new pattern object. When this occurs, the original pattern object decrements its reference count for the Context object by one. The user list is illustrated in Figure 4-6 on page 71.

Graphique

Figure 4-6

How the User List Works

An object registers itself with another object by invoking the addUser() member function of the XglObject class and specifying whether it is interested in receiving a message from the object if a change occurs in the object's state. A call to addUser() is shown below:

  void addUser(XglObject* obj,Xgl_boolean notify_interest =FALSE);  

addUser() determines whether the registering object is already registered in the used object's user list. If the registering object is not in the user list, addUser() adds a new node to the user list. If the notify_interest variable is set to TRUE, addUser() registers the object as interested in being notified if the used object changes, and, if the registering object has registered
itself before, the used object increments the reference count for that user and OR's the current notify_interest flag with the already existing flag. The addUser() function is defined in Object.cc as:

  void XglObject::addUser(XglObject* obj, Xgl_boolean notify_interest)  
  {  
       XglUser* user = userList;  
  
       while (user) {  
           if (user->object == obj) {  
                user->refCount++;  
                user->interest = user->interest || notify_interest;  
                return;  
           }  
           user = user->next;  
       }  
  
       // Create the user  
       user = new XglUser(obj,notify_interest);  
       user->next = userList;  
       userList = user;  
  }  

When a user object no longer uses another object, it invokes removeUser() to remove it from the used object's user list. removeUser() traverses the used object's user list, locates the node for the user object, and decrements the reference count field for that object node by one. If the reference count is zero, the node is deleted from the user list. removeUser() is a member function of the XglObject class and is declared in XglObject.h.
As a side effect, removeUser() deletes the used object if the API operator xgl_object_destroy() has been invoked to delete the object. However, the used object will not destroy itself if it has any users but will simply note that it was asked to destroy itself. Thus, if a used object has been asked to destroy itself and the used object's only user asks the used object to remove the user from the used object's user list, the used object traverses its list, decrements the reference count, deletes the node, and then destroys itself.
removeUser() is defined in Object.cc as:

  void XglObject::removeUser(XglObject* obj)  
  {  
       XglUser* user, *prev_user;  
  
       if (userList) {  
           for (user=userList, prev_user=user; user;  
                prev_user=user, user=user->next){  
                if (user->object == obj) {  
                    if (--(user->refCount)==0) {  
                         if (user == userList) //object is in first node  
                             userList = user->next;  
                         else  
                             prev_user->next = user->next;  
                    delete user;  
                    if (destroyed && (userList == NULL))  
                          delete this;  
                }  
                return;  
           }  
       }  
    }  
  }  

Message Passing

Whenever an API object is changed, some or all of its users might want to be notified of the changes. For example, if a Window Raster Device object is attached to many Context objects, all of which have expressed an interest in being notified of changes, and the dimensions of the window change, the Device object must notify its interested users that its state has changed.
It is the responsibility of the user object to inform the used object that it is using the used object and that it is interested in being notified of any changes. It is the responsibility of the used object to notify interested users whenever a state change occurs. These operations are handled by the XglObject member functions send() and receive().

How Message Passing Works

A used object sends a message to interested users by invoking the XglObject member function send(). This function traverses an object's user list and sends a message to each of the object's interested users. send() specifies who the sender is and contains information on what aspect of the object's state has changed. The function is defined as follows:

  void XglObject::send(const XglMsg &msg)  
  {  
       XglUser* user = userList;  
  
       while (user) {  
           if (user->interest) {  
                user->object->receive(this,msg);  
           }  
           user = user->next;  
       }  
  }  

A user object processes the local impact of changes of a used object with the receive() function. receive() has two arguments: a pointer to the object that is sending the message and the message itself. When an object, such as the Context, receives a message, it determines which of the objects it is currently using sent the message, and it does processing based on the nature of the message. When the Context has processed the message, it passes the message to its parent class, and this class, in turn, passes the message to its parent class, until the message reaches the top of the DI hierarchy. The message is passed upward because the attributes associated with the message might be defined in any of the object's parent classes.
receive() is a virtual function in XglObject.h and is invoked as follows:

  virtual void receive(XglObject* obj, const XglMsg &msg);  

receive() can be overridden by each API object to handle the specific processing for its own attributes. Only a given class or subclass can do the specific processing triggered by an incoming message from an object currently associated with an object of this class.

Destroying Objects and Closing XGL

All API objects are destroyed through the System State object. When the application program calls xgl_object_destroy() to destroy an object, the System State object searches the appropriate API object list, removes the object from the list, and then calls the destroy function for the API objects. This destroy function is also used to destroy all existing API objects during xgl_close().

Destroying the Device Object

An application call to xgl_object_destroy() with a Device handle as the input parameter destroys the Device object if it is not used by any Context object. When the Device object is asked to destroy itself, the following events occur:
  1. The System State object removes the Device object from its Device object list and destroys the Device object if it is not referenced by any users.

  2. The Device object determines whether if has any users. If it does not have any users, it notifies all the objects that it is currently using that it will no longer use them. Then it destroys itself. In the process, it also destroys its device-dependent part, XglDpDev. If it has users, it notes that it was asked to destroy itself.

Destroying the Context Object

An application call to xgl_object_destroy() with a Context handle as the input parameter destroys the Context object. When a Context is destroyed, the following events occur:
  1. The System State object removes the Context object from the list of objects it has created.

  2. The XglSysState asks the Context object to destroy itself. During this process, the Context object determines whether it has any users.

    a. If the Context has users, it notes that it was asked to destroy itself.

    b. If the Context does not have users, it does the following:

    i. It notifies all the objects that it is currently using that it will no longer use them.

ii. It destroys the software pipeline object. iii. It destroys itself.

Closing XGL

When XGL is closed, the System State object deletes all the objects it created in the same process that is used in an xgl_object_destroy() call. The pipeline XglDpDev object is destroyed as part of the Device object destruction, and the XglDpCtx object is destroyed during the corresponding Device destruction or Context destruction. The System State object then deletes itself.
During xgl_close(), the Global State object is destroyed, and the destructor of the XglDpLib object is called. The XglDpLib destructor is explicitly invoked using the handle to the object. It is the responsibility of the XglDpLib object to destroy the XglDpMgr object(s) for the pipeline, since there may be one or more XglDpMgr objects for a particular pipeline.
The Global State object executes dlclose() to remove the reference to the pipeline shared object from XGL's process space.