pfBuffer(3pf) OpenGL Performer 3.2.2 libpf C Reference Pages pfBuffer(3pf)NAME
pfNewBuffer, pfSelectBuffer, pfMergeBuffer, pfBufferScope,
pfGetBufferScope, pfBufferAdd, pfBufferRemove, pfBufferInsert,
pfBufferReplace, pfBufferSet, pfAsyncDelete, pfGetCurBuffer - Create,
select, and merge a pfBuffer.
FUNCTION SPECIFICATION
#include <Performer/pf.h>
pfBuffer * pfNewBuffer(void);
void pfSelectBuffer(pfBuffer* buf);
int pfMergeBuffer(void);
void pfBufferScope(pfBuffer *buf, pfObject *obj, int scope);
int pfGetBufferScope(pfBuffer *buf, pfObject *obj);
int pfBufferAdd(void *parent, void *child);
int pfBufferRemove(void *parent, void *child);
int pfBufferInsert(void *parent, int index, void *child);
int pfBufferReplace(void *parent, void *oldChild,
void *newChild);
int pfBufferSet(void *parent, int index, void *child);
int pfAsyncDelete(void *mem);
pfBuffer* pfGetCurBuffer(void);
PARAMETERS
buf identifies a pfBuffer
obj identifies a pfObject
DESCRIPTION
A pfBuffer is a data structure that logically encompasses libpf objects
such as pfNodes. Newly created objects are automatically "attached" to
the current pfBuffer specified by pfSelectBuffer. Later, any objects
created in buf may be merged into the main OpenGL Performer processing
stream with pfMergeBuffer. In conjunction with a forked DBASE process
(see pfMultiprocess and pfDBaseFunc), the pfBuffer mechanism supports
asynchronous parallel creation and deletion of database objects. This is
the foundation of a real-time database paging system.
pfNewBuffer creates and returns a handle to a pfBuffer.
pfSelectBuffer makes buf the current pfBuffer. Once buf is current, all
Page 1
pfBuffer(3pf) OpenGL Performer 3.2.2 libpf C Reference Pages pfBuffer(3pf)
subsequently created libpf objects will be automatically associated with
buf and these objects may only be accessed through OpenGL Performer
routines when buf is the current pfBuffer (except for pfBufferAddChild
and pfBufferRemoveChild, see the pfGroup man page). A given pfBuffer
should only be current in a single process at any given time. In this
way, a pfBuffer restricts access to a given object to a single process,
avoiding hard-to-find errors due to multiprocessed data collisions.
pfGetCurBuffer returns the current pfBuffer.
Only libpf objects are subject to pfBuffer access restrictions. libpf
objects include pfNodes such as pfGroup, pfGeode and pfUpdatables such as
pfLODState, pfChannel, pfEarthSky. libpr objects such as pfGeoSets,
pfGeoStates, and pfMaterials have no pfBuffer restrictions so they may be
accessed by any process at any time although care must be taken by the
application to avoid multiprocessed collisions on these data structures.
pfMergeBuffer merges the current pfBuffer with the main OpenGL Performer
pfBuffer. This main pfBuffer is created by pfConfig and will resist
deletion and merging and should only be made current in the APP process
(however, it is legal to select a different buffer in the APP process).
If called in a process other than the APP, pfMergeBuffer will block until
the APP calls pfSync, at which time the APP will merge the current
pfBuffer into the main pfBuffer and then allow the process that requested
the merge to continue. If called in the APP, pfMergeBuffer will
immediately execute the merge. After pfMergeBuffer returns, any objects
that were created in the current pfBuffer may only be accessed in the APP
process when the APP pfBuffer has been selected as the current pfBuffer.
In other words, the merged pfBuffer has been "reset" and its objects now
"exist" only in the APP pfBuffer. The addresses of libpf objects are not
changed by pfMergeBuffer.
Any number of pfBuffers may be used and merged (pfMergeBuffer) by any
number of processes for multithreaded database manipulation, subject to
the following restrictions:
1. A given pfBuffer should be current (via pfSelectBuffer) in only
a single process at any given time.
2. Each process which selects a pfBuffer must be forked, not
sproced.
Specifically, pfBuffer usage is not restricted to the DBASE process (see
pfConfig).
pfBufferAddChild and pfBufferRemoveChild provide access to nodes that do
not exist in the current pfBuffer. Either, none, or both of group and
node may exist outside the current pfBuffer. pfBufferAddChild and
pfBufferRemoveChild act just like their non-buffered counterparts
pfAddChild and pfRemoveChild except that the addition or removal request
is not carried out immediately but is recorded by the current pfBuffer.
The request is delayed until the first pfMergeBuffer when both group and
Page 2
pfBuffer(3pf) OpenGL Performer 3.2.2 libpf C Reference Pages pfBuffer(3pf)
node are found in the main OpenGL Performer pfBuffer. The list of
pfBufferAddChild and pfBufferRemoveChild requests is traversed in
pfMergeBuffer after all nodes have been merged. pfBufferAddChild and
pfBufferRemoveChild return TRUE if the request was recorded and FALSE
otherwise.
pfBufferSet sets the specified child index of a parent node to the given
child. It acts as a replace command but uses an index to identify the
replaced child. The current implementation supports pfBufferSet only for
setting a source array in a pfAlign node.
In addition to the pfGroup-specific pfBufferAddChild and
pfBufferRemoveChild routines, a pfBuffer allows generic list management
for pfGroup, pfGeode, pfText, and pfPipeWindow objects. These functions,
pfBufferAdd, pfBufferRemove, pfBufferInsert, pfBufferReplace can be used
to manage a pfGroup's list of pfNodes, a pfGeode's list of pfGeoSets, a
pfText's list of pfStrings, or a pfPipeWindow's list of pfChannels
respectively. These routines infer the proper action to take from the
argument types. For example, the following code fragment is equivalent
to pfBufferAddChild(group, geode):
pfGroup *group;
pfGeode *geode;
pfBufferAdd(group, geode);
pfBufferAdd, pfBufferRemove, pfBufferInsert, pfBufferReplace, pfBufferSet
all act similarly in that they do not have effect until pfMergeBuffer is
called and all parties have been merged into the main OpenGL Performer
buffer. They return -1 if the argument types are not consistent (e.g.,
pfBufferRemove(group, geoset)), 0 if the request is immediately processed
(this happens when all parties already have scope in the current
pfBuffer), and 1 if the request is buffered until the next pfMergeBuffer.
pfBufferScope sets the scope of obj with respect to pfBuffer buf. If
scope is TRUE, then obj is "added" to buf so that when buf is made
current (pfSelectBuffer) in a process, obj may be accessed through OpenGL
Performer routines in that same process. When scope is FALSE, obj is
"removed" from buf. pfBufferScope's primary purpose is to move objects
between pfBuffers, particularly from the main APP pfBuffer into an
application pfBuffer typically used for asynchronous database
manipulations. In this case the object's scope would be set to FALSE in
the old pfBuffer and TRUE in the new pfBuffer. It is undefined when an
object has scope in multiple pfBuffers since this violates the
multiprocessing data exclusion requirement of OpenGL Performer.
pfGetBufferScope returns TRUE or FALSE indicating the scope of obj in
pfBuffer buf.
When using pfBuffers for database paging, it is sometimes desirable to
Page 3
pfBuffer(3pf) OpenGL Performer 3.2.2 libpf C Reference Pages pfBuffer(3pf)
retain certain, common database models ("library models") in memory.
Examples are trees, houses, and other "culture" which are instanced on
paged terrain patches. One instancing mechanism is to create the library
models in one pfBuffer and later use pfBufferAddChild to attach the
models to scene graphs created in another pfBuffer. This is classic
instancing which uses transformations (pfSCS) to properly position the
models. However, this mechanism suffers from 2 performance problems:
1. pfMergeBuffer will adversely impact the APP process,
proportional to the number of pfBufferAddChild and
pfBufferRemoveChild requests.
2. Transformations in the scene graph reduce OpenGL Performer's
ability to sort the database (see pfChanBinSort) and matrix
operations have some cost in the graphics pipeline.
An alternative to classic instancing is "flattening" which creates a
clone of the instanced subtree and then applies the transformation to all
geometry in the cloned subtree. This method eliminates the performance
problems listed above but does increase memory usage.
pfNode* pfBufferClone(pfNode *node, int mode, pfBuffer *buf)
is a version of pfClone which clones node and its subtree, which resides
in buf, into the current pfBuffer. mode is the same argument as that
passed to pfClone (it is currently ignored). Once cloned, a subtree may
be flattened with pfFlatten:
Example 1: Instancing with pfBufferAddChild
libraryBuffer = pfNewBuffer();
pfSelectBuffer(libraryBuffer);
loadLibraryObjects();
pagingBuffer = pfNewBuffer();
pfSelectBuffer(pagingBuffer);
while (!done)
{
pfNode *newStuff;
pfSCS *treeLocation;
/* Load new terrain tile or whatever */
newStuff = loadStuff();
/* Create pfSCS which is location of tree */
treeLocation = pfNewSCS(treeMatrix);
/* Add library model of a tree to treeLocation */
Page 4
pfBuffer(3pf) OpenGL Performer 3.2.2 libpf C Reference Pages pfBuffer(3pf)
pfBufferAddChild(treeLocation, libraryTree);
/* Add instanced tree to newly loaded stuff */
pfAddChild(newStuff, treeLocation);
}
Example 2: Instancing with pfBufferClone and pfFlatten
libraryBuffer = pfNewBuffer();
pfSelectBuffer(libraryBuffer);
loadLibraryObjects();
pagingBuffer = pfNewBuffer();
pfSelectBuffer(pagingBuffer);
while (!done)
{
pfNode *newStuff;
pfSCS *treeLocation;
/* Load new terrain tile or whatever */
newStuff = loadStuff();
/* Create pfSCS which is location of tree */
treeLocation = pfNewSCS(treeMatrix);
/* Clone tree model from library into current, paging buffer */
newTree = pfBufferClone(libraryTree, 0, libraryBuffer);
/* Transform cloned tree */
pfAddChild(treeLocation, newTree);
pfFlatten(treeLocation);
/* Get rid of unneeded treeLocation */
pfRemoveChild(treeLocation, newTree);
pfDelete(treeLocation);
/* Add cloned, flattened tree to newly loaded stuff */
pfAddChild(newStuff, newTree);
}
pfAsyncDelete is a special version of pfDelete which is useful for
asynchronous database deletion. Instead of having immediate effect,
pfAsyncDelete simply registers a deletion request at the time of
Page 5
pfBuffer(3pf) OpenGL Performer 3.2.2 libpf C Reference Pages pfBuffer(3pf)
invocation. These deletion requests are then processed in the DBASE
trigger routine, pfDBase (pfDBase is automatically called if you have not
registered a DBASE callback with pfDBaseFunc). Thus, if the DBASE
processing stage is configured as its own process via pfMultiprocess,
then the deletion will be carried out asynchronously without affecting
(slowing down) the main processing pipelines.
pfAsyncDelete may be called from any process and returns -1 if mem is
NULL or not derived from pfMemory and returns TRUE otherwise. Note that
unlike pfDelete pfAsyncDelete does not check mem's reference count and
return TRUE or FALSE indicating whether mem was successfully deleted or
not. Instead, the reference count check is delayed until the next call to
pfDBase. At this time there is no way to query the success of an
pfAsyncDelete request.
Note that pfDBase should only be called from within the database callback
function (pfDBaseFunc) in the DBASE process just like pfCull and pfDraw
should only be called in the pfChannel CULL and DRAW callbacks
respectively (pfChanTravFunc).
Example 2: How to use a pfBuffer
/* Must create these in shared memory */
static pfGroup **Tiles;
static int *TileStatus;
/*
* Load new tiles and delete old ones.
*/
void
pageDBase(void *data)
{
static pfBuffer *buf = NULL;
pfGroup *root;
if (buf == NULL)
{
buf = pfNewBuffer();
pfSelectBuffer(buf);
}
/* Asynchronously delete unneeded tiles and update their status */
for (allUnneededTiles)
{
/*
* Scene does not have scope in 'buf' so use pfBufferRemoveChild
* Tiles[i] is not really removed until pfMergeBuffer
*/
pfBufferRemoveChild(Scene, Tiles[i]);
/* Delete Tiles[i] at pfDBase time if Tiles[i] only has Scene as
a parent.
Page 6
pfBuffer(3pf) OpenGL Performer 3.2.2 libpf C Reference Pages pfBuffer(3pf)
*/
pfAsyncDelete(Tiles[i]);
/* Update tile status */
TileStatus[i] = TILE_DELETED;
}
/*
* Synchronously load needed tiles and update their status.
*/
LoadNeededDatabaseTiles(Tiles, TileStatus);
for (allLoadedTiles)
{
/*
* Scene does not have scope in 'buf' so use pfBufferAddChild
* loadedTile[i] is not really added until pfMergeBuffer
*/
pfBufferAddChild(Scene, loadedTile[i]);
}
/*
* Merge newly loaded tiles into main pfBuffer then carry out
* all pfBufferAdd/RemoveChild requests.
*/
pfMergeBuffer();
/*
* Carry out pfAsyncDelete requests. Call *after* pfMergeBuffer()
* so that all pfBufferRemoveChild requests have been processed
* and child reference counts have been properly decremented.
*/
pfDBase();
}
:
pfInit();
Tiles = pfMalloc(sizeof(pfGroup*) * NUM_TILES, pfGetSharedArena());
TileStatus = pfMalloc(sizeof(int) * NUM_TILES, pfGetSharedArena());
pfMultiprocess(PFMP_APP_CULL_DRAW | PFMP_FORK_DBASE);
pfConfig();
:
pfDBaseFunc(pageDBase);
while(!done)
{
pfSync();
/* Remove and request deletion of unneeded tiles */
UpdateTileStatus(Tiles, TileStatus);
Page 7
pfBuffer(3pf) OpenGL Performer 3.2.2 libpf C Reference Pages pfBuffer(3pf)pfFrame();
}
NOTES
pfGetCurBuffer will return the APP pfBuffer immediately after pfConfig
returns.
SEE ALSO
pfBufferAddChild, pfBufferRemoveChild, pfConfig, pfDBaseFunc, pfDelete,
pfFrame, pfMultiprocess, pfGroup
Page 8