pfBuffer(3pf) OpenGL Performer 3.2.2 libpf C++ Reference Pages pfBuffer(3pf)NAME
pfBuffer, pfAsyncDelete, pfGetCurBuffer - Create, select, and merge a
pfBuffer.
FUNCTION SPECIFICATION
#include <Performer/pf/pfBuffer.h>
pfBuffer::pfBuffer();
void pfBuffer::select(void);
static int pfBuffer::merge(void);
void pfBuffer::setScope(pfObject *obj, int scope);
int pfBuffer::getScope(pfObject *obj);
static int pfBuffer::add(void *parent, void *child);
static int pfBuffer::remove(void *parent, void *child);
static int pfBuffer::insert(void *parent, int index, void *child);
static int pfBuffer::replace(void *parent, void *oldChild,
void *newChild);
static int pfBuffer::set(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 pfBuffer::select. Later, any objects
created in the pfBuffer may be merged into the main OpenGL Performer
processing stream with pfBuffer::merge. 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.
new pfBuffer creates and returns a handle to a pfBuffer. pfBuffers
cannot be created statically, on the stack, from the heap or in arrays.
pfBuffer::select makes the pfBuffer the current pfBuffer. Once the
Page 1
pfBuffer(3pf) OpenGL Performer 3.2.2 libpf C++ Reference Pages pfBuffer(3pf)
pfBuffer is current, all subsequently created libpf objects will be
automatically associated with the pfBuffer and these objects may only be
accessed through OpenGL Performer routines when the pfBuffer is the
current pfBuffer (except for pfGroup::bufferAddChild and
pfGroup::bufferRemoveChild, 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.
pfBuffer::merge 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, pfBuffer::merge 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, pfBuffer::merge
will immediately execute the merge. After pfBuffer::merge 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 pfBuffer::merge.
Any number of pfBuffers may be used and merged (pfBuffer::merge) by any
number of processes for multithreaded database manipulation, subject to
the following restrictions:
1. A given pfBuffer should be current (via pfBuffer::select) 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).
pfGroup::bufferAddChild and pfGroup::bufferRemoveChild provide access to
nodes that do not exist in the current pfBuffer. Either, none, or both
of the pfBuffer and node may exist outside the current pfBuffer.
pfGroup::bufferAddChild and pfGroup::bufferRemoveChild act just like
their non-buffered counterparts pfGroup::addChild and
pfGroup::removeChild except that the addition or removal request is not
Page 2
pfBuffer(3pf) OpenGL Performer 3.2.2 libpf C++ Reference Pages pfBuffer(3pf)
carried out immediately but is recorded by the current pfBuffer. The
request is delayed until the first pfBuffer::merge when both the parent
pfGroup and node are found in the main OpenGL Performer pfBuffer. The
list of pfGroup::bufferAddChild and pfGroup::bufferRemoveChild requests
is traversed in pfBuffer::merge after all nodes have been merged.
pfGroup::bufferAddChild and pfGroup::bufferRemoveChild return TRUE if the
request was recorded and FALSE otherwise.
pfBuffer::set 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 pfBuffer::set
only for setting a source array in a pfAlign node.
In addition to the pfGroup-specific pfGroup::bufferAddChild and
pfGroup::bufferRemoveChild routines, a pfBuffer allows generic list
management for pfGroup, pfGeode, pfText, and pfPipeWindow objects. These
functions, pfGroup::bufferAdd, pfGroup::bufferRemove,
pfGroup::bufferInsert, pfGroup::bufferReplace 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
group->bufferAddChild(geode):
pfGroup *group;
pfGeode *geode;
pfBuffer::add(group, geode);
pfGroup::bufferAdd, pfGroup::bufferRemove, pfGroup::bufferInsert,
pfGroup::bufferReplace, pfBuffer::set all act similarly in that they do
not have effect until pfBuffer::merge 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., pfBuffer::remove(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 pfBuffer::merge.
pfBuffer::setScope sets the scope of obj with respect to the pfBuffer. If
scope is TRUE, then obj is "added" to the pfBuffer so that when the
pfBuffer is made current (pfBuffer::select) in a process, obj may be
accessed through OpenGL Performer routines in that same process. When
scope is FALSE, obj is "removed" from the pfBuffer. pfBuffer::setScope'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
Page 3
pfBuffer(3pf) OpenGL Performer 3.2.2 libpf C++ Reference Pages pfBuffer(3pf)
Performer. pfBuffer::getScope returns TRUE or FALSE indicating the scope
of obj in pfBuffer the pfBuffer.
When using pfBuffers for database paging, it is sometimes desirable to
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 pfGroup::bufferAddChild 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. pfBuffer::merge will adversely impact the APP process,
proportional to the number of pfBuffer::addChild and
pfBuffer::removeChild requests.
2. Transformations in the scene graph reduce OpenGL Performer's
ability to sort the database (see pfChannel::setBinSort) 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* pfNode::bufferClone(int mode, pfBuffer *buf)
is a version of pfNode::clone which clones the pfBuffer and its subtree,
which resides in buf, into the current pfBuffer. mode is the same
argument as that passed to pfNode::clone (it is currently ignored). Once
cloned, a subtree may be flattened with pfNode::flatten.:
Example 1: Instancing with pfGroup::bufferAddChild
libraryBuffer = new pfBuffer;
libraryBuffer->select();
loadLibraryObjects();
pagingBuffer = new pfBuffer;
pagingBuffer->select();
while (!done)
{
pfNode *newStuff;
pfSCS *treeLocation;
/* Load new terrain tile or whatever */
newStuff = loadStuff();
Page 4
pfBuffer(3pf) OpenGL Performer 3.2.2 libpf C++ Reference Pages pfBuffer(3pf)
/* Create pfSCS which is location of tree */
treeLocation = new pfSCS(treeMatrix);
/* Add library model of a tree to treeLocation */
treeLocation->bufferAddChild(libraryTree);
/* Add instanced tree to newly loaded stuff */
newStuff->addChild(treeLocation);
}
Example 2: Instancing with pfBufferClone and pfFlatten
libraryBuffer = new pfBuffer;
libraryBuffer->select();
loadLibraryObjects();
pagingBuffer = new pfBuffer;
pagingBuffer->select();
while (!done)
{
pfNode *newStuff;
pfSCS *treeLocation;
/* Load new terrain tile or whatever */
newStuff = loadStuff();
/* Create pfSCS which is location of tree */
treeLocation = new pfSCS(treeMatrix);
/* Clone tree model from library into current, paging buffer */
newTree = libraryTree->bufferClone(0, libraryBuffer);
/* Transform cloned tree */
treeLocation->addChild(newTree);
treeLocation->flatten();
/* Get rid of unneeded treeLocation */
treeLocation->removeChild(newTree);
pfDelete(treeLocation);
/* Add cloned, flattened tree to newly loaded stuff */
newStuff->addChild(newTree);
}
Page 5
pfBuffer(3pf) OpenGL Performer 3.2.2 libpf C++ Reference Pages pfBuffer(3pf)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
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 = new pfBuffer;
buf->select();
}
/* 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
*/
Scene->bufferRemoveChild(Tiles[i]);
Page 6
pfBuffer(3pf) OpenGL Performer 3.2.2 libpf C++ Reference Pages pfBuffer(3pf)
/* Delete Tiles[i] at pfDBase time if Tiles[i] only has Scene as
a parent.
*/
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
*/
Scene->bufferAddChild(loadedTile[i]);
}
/*
* Merge newly loaded tiles into main pfBuffer then carry out
* all pfBufferAdd/RemoveChild requests.
*/
pfBuffer::merge();
/*
* Carry out pfAsyncDelete requests. Call *after* pfBuffer::merge()
* 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 */
Page 7
pfBuffer(3pf) OpenGL Performer 3.2.2 libpf C++ Reference Pages pfBuffer(3pf)
UpdateTileStatus(Tiles, TileStatus);
pfFrame();
}
NOTES
pfGetCurBuffer will return the APP pfBuffer immediately after pfConfig
returns.
SEE ALSO
pfBuffer, pfConfig, pfDBaseFunc, pfFrame, pfMultiprocess, pfGroup
Page 8