Many scene graphs you use are too large to fit into system memory. Consequently, you need to load data dynamically at run time. Because loading data from a hard drive is relatively slow, to prevent breaking the frame rate, you should:
Fork off a database (DBASE) process to handle database paging asynchronously in the background.
Anticipate which pages of data to load and which to delete.
This chapter describes how to page the database efficiently in the following sections:
Because many scene graphs are too large to hold into system memory, your application must anticipate which pages of memory to load and which to delete. Pages of memory are often associated with nodes in the scene graph: a node encapsulates a part of the scene which occupies a page of memory.
Figure 12-1 shows pages of memory represented as squares; each page corresponds to a node in the scene graph. The triangle represents the position and direction of the motion of the eyepoint.
In Figure 12-1, pages 6, 7, 10, 11, 12, 15, and 16 are currently in memory, pages 1, 2, and 5 are good candidates for loading, and pages 12, 15, and 16 are good candidates for removal.
Because loading data from disk is relatively slow, loading deserves its own process so that it can run continuously and asynchronously in the background.
To use a database paging process, you must:
Fork off a DBASE process.
Call a database function of your creation, which handles memory allocation and deallocation, and the loading of the data.
The following code performs those tasks:
pfInit(); pfMultiprocess( PFMP_FORK_DRAW | PFMP_FORK_DBASE ); pfConfig(); pfDBaseFunc( myDBaseFunc );
myDBaseFunc, of type pfDBaseFuncType, needs to handle data loading and memory allocation and deallocation.
The APP and DBASE processes need to share data. They reside, however, in separate virtual memory spaces. To share data, they must allocate memory in the shared memory arena, as the following code shows:
typedef struct ( pfScene *Scene; ) SharedData; SharedData *shared; void *arena; arena = pfGetsharedArena(); shared = (SharedData *)pfMalloc( sizeof(SharedData), arena ); shared->scene = pfNewScene();
These lines of code, except for the last, must be placed between pfInit and pfConfig. To deallocate the memory malloc'd, use pfFree.
The final line of code makes the scene node, the root node of the scene graph, accessible to the DBASE process.
Because of user interaction, such as moving through a scene, the scene graph in memory often changes: nodes representing pages of memory are deleted or added to the scene graph according to the motion of the eyepoint. The DBASE process should not change the scene directly because it should anticipate where the eyepoint will go. If the process were to change the scene graph immediately, the anticipated page of memory would likely display too soon. Instead, the DBASE process should:
Cache scene graph changes in a pfBuffer.
Add and remove nodes from the scene graph in the buffer.
Delete nodes removed from the scene graph in the buffer.
Merge the changes from the buffer into the scene graph when the APP process calls pfSync.
Carry out the deletion request.
The following sections explain how to perform these tasks.
Instead of changing the scene graph directly, you should:
Create a buffer.
Make it active.
Create the necessary scene graph changes in the buffer.
The following lines of code complete these tasks.
pfBuffer *buf; node *d, *e; buf = pfNewBuffer(); pfSelectBuffer( buf ); d = pfNewGroup(); e = pfNewGeode(); pfAddChild( d, e );
Figure 12-2 shows how a buffer is created and how nodes are created and grouped.
Once the scene graph in the buffer is complete, you must connect the changes to the main scene graph. To remove node C and connect the scene graph in the buffer to node A, use the following lines of code:
pfBufferRemoveChild( a, c ); pfBufferAddChild( a, d );
These lines of code request but do not cause the actions to be performed. The actions are performed with the next call to pfSync.
Once you request a node to be removed, you should request that it be deleted as well. The following line of code removes node C:
pfAsyncDelete( c );
This code requests the deletion, but the deletion is not performed until the next call to pfSync.
Figure 12-3 shows the linking and deletion of nodes.
Figure 12-3 shows that although node C was removed and deleted, its data remains in the cache.
int success; success = pfMergeBuffer();
success is non-zero if the merge is successful.
When you merge the buffers, the following occurs:
Nodes D and E are placed in the scope of the main scene graph buffer.
The buff buffer is cleared.
Figure 12-4 shows these changes.
The merge is not performed until the next call to pfSync.