Performer Perfly Basics

Before we could start programming Performer Perfly to run in stereo, we had to understand the original program. Perfly is provided with Performer 2.0 in these directories: usr/share/Performer/src/sample/C++/common and perfly. Support for these files can be found in the man pages for pfChannel, pfPipeWindow, pfWindow, and pfPipe, as well as the online help manual IRIS Performer Programmer's Guide.


Viewing Frustum

Imagine a square or rectangular pyramid with the tip cut off of it. Then tip the pyramid over and lay it so the face that was just cut off is facing you . This is the shape of the viewing frustum. It has 6 sides, called clipping planes, labeled in this figure. Clipping planes are used to determine which objects should be culled from the scene. This is explained in more detail in the ISECT section below. The viewing frustum defines the portion of the scene visible from the user's eyepoint. It is also used to help determine the level of detail (LOD) necessary for viewing a scene. That is, to decrease stress, the objects can be displayed with less detail if they are far away from the viewer. As the objects get closer to the near clipping plane, their LOD goes up, so the user may see a building suddenly get windows and doors. This LOD method is not quite perfected, but it does help ease processor stress. The angle of the side clipping planes is determined by the field of view (FOV) angle in degrees set by the user. The usual setting is 45 degrees, but by manipulating this number, more of the scene can be displayed.

Return to the top

Performer's Processes

Each scene in Performer is represented as a tree of nodes. Each node is either an object, or a set of objects. For example, the node of a Mobil gas station may have a gas pump node,a building node, and a node representing the canopy over the pumps. Any objects under a given node are said to be within the bounding volume of that node. "Bounding volume is a convex region that encompasses a geometric object or a collection of such objects". It includes some space around the objects as well, so the bounding volume may be larger than the node it surrounds. Having objects arranged in this way greatly assists the speed at which the scene can be rendered, specifically with the intersection (isec), cull, application (app), and draw processes. These processes can be sequenced with multiprocessing, when available. That is, if more than one processor is available, different processes can be assigned to different processors so that the program executes as efficiently as possible.
The APP process updates and queries the scene. It does simulation processing, including reading input from the keyboard or mouse, updating the visual database, and interacting between other networked simulation stations.
The DRAW process takes the display list generated by the cull process and renders the geometry to a geometry pipeline to create an image for display. It issues graphics library commands to draw the scene.
The CULL process discards database objects that are completely outside of the viewing frustum. It then adds all potentially visible objects to a display list, which is given to the draw stage to render. If the programmer doesn't use the isec process and only uses the cull process, then images that intersect the viewing frustum are discarded as well. The cull process passes through the scene geometry like the isec process and compares it to the frustum. Another type of culling involves computing potential visibility of objects in a scene that overlap. This is called occlusion testing. By using occlusion testing, it is possible to reduce the number of polygons necessary to render a scene. The cull process also selects a level of detail for each object to be rendered, sorts objects, and optimizes state management, including stress management.
The ISECT process is an optional process within Performer. It interacts with the cull process and the viewing frustum to determine which nodes should be displayed. When the user is facing a certain direction, there is a limited field of view that is bounded by the viewing frustum, as described above. Because of this limited view, all of the objects outside of it do not need to be drawn. The isec process determines what objects are outside the viewing frustum and passes this information on to the cull process. There are three different cases that the isec process encounters. The first is when the bounding volume of a given node is entirely outside the frustum. When this occurs, not only can that node be pruned, but also every node within its bounding volume. For instance, if the gas station is not in view, then the pumps, canopy, and building must not be either. The second case is when the bounding volume of a node is entirely inside the frustum. Then, none of the lower nodes should be pruned. That is, if the entire gas station is in view, then the user sees the whole station. The third case is when the bounding volume is partially within the frustum. In this case, the isec process must move down the tree and test the child nodes to see if they are within the frustum. Because the bounding volume may be larger than the object it surrounds, it is possible for it to be partially in the viewing frustum and yet have none of its geometry displayed to the user. Once each node has been tested to see if it is within the viewing frustum, the information is passed on to the cull process.

Return to the top

main.C

When we were first trying to understand how perfly worked, we went through the main function and traced every function call and found what every line did. The notes are here so you don't have to do that.
    1.  pfInit()
	This must be the first call in a libpf application.  It 
        initialized IRIS Performer, creates shared memory, and 
        prepares everything to be used.

    2.  InitSharedMem(argc, argv)
	This function is in generic.C.  It 
	   a.  allocates memory for the arena and screen. This must 
               be done before pfConfig(), so the forked processes 
               have the same pointers.  It also finds the size of 
               the screen, orders channels, etc.
	   b.  initViewState()
		 i.  in perfly.c
		ii.  sets up defaults for the struct SharedViewState  
	   c.  tells where to look for data files (.flt, etc)
	   d.  converts all necessary files with 
               pfdInitConvertor(char[])
	   e.  sets the ViewState->guiFormat
	   f.  finds number of channels and pipes

     3.  InitConfig()
	This function is in generic.C and
	   a.  pfMultipipe(int)
		  i.  sets multiprocessing mode based on number of 
                      processors  
		 ii.  must be done between pfInit and pfConfig
		iii.  different parameters allow the cull, draw, 
                      and app processes to happen simultaneously, 
                      or at specified times.
	   b.  pfMultipipe(int) sets number of pipes
	   c.  pfIsectFunc(func) sets intersection callback
	   d.  checks for restricted CPUs

     4.  pfConfig() forks extra processes if configured for 
         multiprocessing

     5.  pfuInitUtil() and pfiInit() initialize the utility 
         libraries

     6.  InitScene() is in generic.C and
	   a.  gets the scene from the command line
	   b.  keeps track of elapsed time
	   c.  initializes the scene (collision, textures, 
               environment)
	   d.  converts the scene of loaded
	   e.  initView(pfScene *) is in perfly.C
		  i.  gets the boundaries of the scene
	   	 ii.  sets the initial view
		iii.  sets the near and far viewing planes

     7.  InitPipe() is in generic.C and initializes the pipeline(s).
	   a.  initializes each pipe with a pfPipeWindow, screen, 
               name, and configuration
	   b.  pfStageConfigFunc(pfPipe, stageMask, configFunc) 
               configures processes not associated with pfPipes 
               (e.g. database processes, intersection, cull)

     8.  pfFrame() starts off first frame of cull and draw processes 
         in parallel with app

     9.  IniGUI() 
	This is in generic.C and initializes the GUI first so that 
        it is the first channel
	   a.  initializes GUI to master channel and pipe
	   b.  enables GUI and the ViewState member gui

    10.  InitChannel() 
	This is in generic.C
	   a.  distributes channels across pipes if multipipe, else 
               creates channels on one pipe
	   b.  disables sorting for OPAQUE bin in CULLoDRAW mode
	   c.  orders channels
	   d.  makes master channel the source for keyboard and 
               mouse input
	   e.  sets the viewports, if multipipe
	   f.  attaches channels to master channel to for a group
	   g.  sets field of view
	   h.  sets callback routines
	   i.  attaches the ViewState members to the master channel
	   j.  matches the vertical field of view to the window 
               aspect ratio
	   k.  calls the makeSimple(fov) function creates a frustum 
               on an axis and sets the angle
	   l.  sets up the stats
	   m.  initializes the GUI to the master channel

    11.  pfuInitInput(pfPipeWindow, input) initializes the input
         handling(X or GL) for mouse and event inputs

    12.  set up processor locking for app process

    13.  pfFrameRate(rate) sets desired frame rate

    14.  pfPhase(phase) sets the multiprocessing sync phase

    15.  Application main loop
           a.  PreFrame() is in generic.C and updates mouse buttons 
               and view
	   b.  pfFrame() triggers cull and draw processes for this 
               frame
	   c.  PostFrame() in generic.C 
		 i.  gets events
		ii.  updateSim(pfChan)
			- processes keyboard input
			- adjusts load management filter (checks for 
                          stress)
			- checks if needs to update channels and GUI
			- resets cullMode and level of detail
			- updates ViewState members like timeOfDay, 
                          stats, env, etc)

    16.  cleans up memory and exits


Return to the top.


Return to the main page.