[BACK]Return to stats.C CVS log [TXT][DIR] Up to [Development] / performer / src / pguide / libpf / C++

File: [Development] / performer / src / pguide / libpf / C++ / stats.C (download)

Revision 1.1, Tue Nov 21 21:39:38 2000 UTC (16 years, 11 months ago) by flynnt
Branch: MAIN
CVS Tags: HEAD

Initial check-in based on OpenGL Performer 2.4 tree.
-flynnt

//
// Copyright 1995, Silicon Graphics, Inc.
// ALL RIGHTS RESERVED
//
// This source code ("Source Code") was originally derived from a
// code base owned by Silicon Graphics, Inc. ("SGI")
// 
// LICENSE: SGI grants the user ("Licensee") permission to reproduce,
// distribute, and create derivative works from this Source Code,
// provided that: (1) the user reproduces this entire notice within
// both source and binary format redistributions and any accompanying
// materials such as documentation in printed or electronic format;
// (2) the Source Code is not to be used, or ported or modified for
// use, except in conjunction with OpenGL Performer; and (3) the
// names of Silicon Graphics, Inc.  and SGI may not be used in any
// advertising or publicity relating to the Source Code without the
// prior written permission of SGI.  No further license or permission
// may be inferred or deemed or construed to exist with regard to the
// Source Code or the code base of which it forms a part. All rights
// not expressly granted are reserved.
// 
// This Source Code is provided to Licensee AS IS, without any
// warranty of any kind, either express, implied, or statutory,
// including, but not limited to, any warranty that the Source Code
// will conform to specifications, any implied warranties of
// merchantability, fitness for a particular purpose, and freedom
// from infringement, and any warranty that the documentation will
// conform to the program, or any warranty that the Source Code will
// be error free.
// 
// IN NO EVENT WILL SGI BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT
// LIMITED TO DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES,
// ARISING OUT OF, RESULTING FROM, OR IN ANY WAY CONNECTED WITH THE
// SOURCE CODE, WHETHER OR NOT BASED UPON WARRANTY, CONTRACT, TORT OR
// OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
// PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM,
// OR AROSE OUT OF USE OR RESULTS FROM USE OF, OR LACK OF ABILITY TO
// USE, THE SOURCE CODE.
// 
// Contact information:  Silicon Graphics, Inc., 
// 1600 Amphitheatre Pkwy, Mountain View, CA  94043, 
// or:  http://www.sgi.com
//
//
// file: statsC.C
// -------------
// 
// OpenGL Performer example using cull and draw process callbacks
// and demonstrating Performer Statistics and Debugging utilities.
// 
// $Revision: 1.1 $ 
// $Date: 2000/11/21 21:39:38 $
// 
// Commandline options:
// --------------------
//  'e': use an earth-sky model instead of just clearing screen
//  'D': set on PFMP_CULL_DL_DRAW
//  'o': turn on PFMP_CULLoDRAW - needs forked draw
//  'p': arg to pfMultiprocess
//  'S': do sleeps in APP, CULL, and DRAW so can see mp history timing lines
//  'w': write out scene to scene.out at start of prog
//  '?': get usage
//
// Run-time controls:
// -------------------
//    Left-mouse: advance
//  Middle-mouse: stop
//   Right-mouse: retreat
// PRINTSCREEN-key: snap picture of window to a stats.%d.rgb file
//       ESC-key: exits
//        F1-key: profile
//	  F2-key: toggle use of second channel for stats
//    UPARROWKEY: increase number of frames of stats PFTIMES history 
//  DOWNARROWKEY: decrease number of frames of stats PFTIMES history 
//	      p/P: print currently enabled statistics to stderr
//	      a/A: toggle entire statistics class enable
//	      s/S: toggle system CPU statistics class enable
//	      d/D: toggle database statistics class enable
//	      c/C: toggle cull statistics class enable
//	      f/F: toggle fill stats - depth complexity
//	        g: toggle graphics statistics class enable
//	        G: toggle geometry attribute statistics - counts for 
//				normals, colors, texcoords)
//	      h/F: toggle proc frame time history stats - the timing graph
//	      m/M: toggle mp process frame time stats - all frame time stats
//	      q/Q: do a pfQuery and print results to stderr
//	      r/R: reset view
//	        t: toggle tmesh lengths graph
//		T: toggle transparency fill stats -  is part of fill stats
//	      v/V: toggle Database Node Visibility stats
//	      z/Z: toggle z-compare check in fill stats


#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <math.h>
#include <getopt.h> // for cmdline handler 

#include <X11/X.h>
#include <X11/keysym.h>

#include <Performer/pf/pfPipeWindow.h>
#include <Performer/pf/pfChannel.h>
#include <Performer/pf/pfEarthSky.h>
#include <Performer/pf/pfFrameStats.h>
#include <Performer/pf/pfScene.h>

#include <Performer/pr/pfLight.h>

#include <Performer/pfdu.h>
#include <Performer/pfutil.h>

static void CullChannel(pfChannel *chan, void *data);
static void DrawChannel(pfChannel *chan, void *data);
static void IsectFunc(void *data);
static void DrawStatsChannel (pfChannel *channel, void *data);
static void CullStatsChannel(pfChannel *ch, void *data);
static void OpenPipeWin(pfPipeWindow *pw);
static void UpdateView(void);
static void GetXInput(void);
static void Usage(void);
static void EnableStatsChan(int flag);


#define HISPEED		0.1f
#define LOSPEED		0.001f


//
// structure that resides in shared memory so that the
// application, cull, and draw processes can access it.

typedef struct
{
    pfPipeWindow *pw;
    int        exitFlag;
    int        reset;
    int        inWindow;
    float       mouseX;
    float	mouseY;
    int		mouseButtons;
    int		winSizeX;
    int		winSizeY;
    pfCoord	view;
    float	sceneSize;
    int		drawStats;
    pfFrameStats *frameStats;
    int		statsInterval;
    int		statsEnable, statsClass, statsClassMode;
    int		statsHistFrames;
    int		printStats;
    int		doStatsQuery;
    pfChannel	*chan, *statsChan;
    int	haveStatsChan;
    float	speed;
    int		snapImage;
    int         Sleep;
    float	SleepApp;
    float	SleepDraw;
    float	SleepCull;
    float	SleepIsect;
} SharedData;

static SharedData *Shared;

// light source created and updated in draw-process 

static pfLight *Sun;
static int ProcSplit = PFMP_DEFAULT;
static int CulloDraw = 0;
static int CullDLDraw = 0;
static int WriteScene = 0;
static int varESky = 0;
static int CachedCull = 0;
static int WinOriginX=0, WinOriginY=0, WinSizeX=800, WinSizeY=600;
static int WinType = PFPWIN_TYPE_X;

static void
Nap(double t)
{
    static int	firstTime = 1, nSpins = 1;
    int		i;
    double	start;
    
    if (firstTime)
    {
	char	*spin;
	
	firstTime = 0;
	if (spin = getenv("PFSPINCOUNT"))
	    nSpins = atoi(spin);
    }
    
    if (nSpins < 0)
    {
	sginap(1);
	return;
    }
    
    t *= 0.001f;
    
    start = pfGetTime();
    
    do
    {    
    	for (i=0; i<nSpins; i++)
	    sginap(0);
	
    } while (pfGetTime() - start < t);
}


//
//			    Main and Commandline Processing
//

char ProgName[PF_MAXSTRING];
char OptionStr[] = "CDeop:Swx?";


//
//	Usage() -- print usage advice and exit. This procedure
//	is executed in the application process.


static void
Usage (void)
{
    fprintf(stderr, 
	    "Usage: %s [-ewS] [-p procSplit] [file.ext ...]\n", ProgName);
    exit(1);
}

//
//	docmdline() -- use getopt to get command-line arguments, 
//	executed at the start of the application process.


static int
docmdline(int argc, char *argv[])
{
    int	    opt;
    
    strcpy(ProgName, argv[0]);
    
    // process command-line arguments 
    while ((opt = getopt(argc, argv, OptionStr)) != -1)
    {
	switch (opt)
	{
	case 'e': // use an earth-sky model instead of just clearing screen 
	    varESky = 1;
	    break;
	case 'D':  // set on PFMP_CULL_DL_DRAW 
	    CullDLDraw = PFMP_CULL_DL_DRAW;
	    break;
	case 'C': // Cache cull results 
	    CachedCull = 1;
	    break;
	case 'o':  // turn on PFMP_CULLoDRAW - needs forked draw 
	    CulloDraw = PFMP_CULLoDRAW;
	    break;
	case 'p': // arg to pfMultiprocess 
	    ProcSplit = atoi(optarg);
	    break;
	case 'S': // do sleeps in APP,CULL, and DRAW so can see app timing lines 
	    Shared->Sleep ^= 1;
	    break;
	case 'w': // write out scene to scene.out at start of prog 
	    WriteScene = 1;
	    break;
	case 'W': // Set the window size 
	    if (sscanf(optarg, "%ld,%ld,%ld,%ld", 
		       &WinOriginX, &WinOriginY,
		       &WinSizeX, &WinSizeY) != 4)
	    {
		if (sscanf(optarg, "%ld,%ld", &WinSizeX, &WinSizeY) != 2)
		{
		    if (sscanf(optarg, "%ld", &WinSizeX) == 1)
		    {
			WinSizeY = WinSizeX;
			WinOriginX = -1;
		    }
		    else
			WinSizeX = -1;
		} 
		else
		    WinOriginX = -1;
	    }
	    break;
	case 'x':
	    WinType ^= PFPWIN_TYPE_X;
	    break;
	case '?': // get usage 
	    Usage();
	}
    }
    return optind;
}

//
//	main() -- program entry point. this procedure
//	is executed in the application process.


int
main (int argc, char *argv[])
{
    int		    arg;
    int		    found;
    pfNode *root;
    pfBox           bbox;
    float	    far = 10000.0f;
    FILE	   *fp=0;
    pfCoord	    reset;
    
#if 0
    if (argc < 2)
	Usage();
#endif    
    
    arg = docmdline(argc, argv);
    
    pfInit();
    pfNotifyLevel(PFNFY_DEBUG);
    
    pfMultiprocess(ProcSplit | CulloDraw | CullDLDraw);
    
    // allocate and init shared before fork()'ing parallel processes 
    Shared = (SharedData*)pfCalloc(1, sizeof(SharedData), pfGetSharedArena());
    Shared->haveStatsChan = 1;
    Shared->statsEnable = 0;
    Shared->statsClass = 0;
    Shared->statsHistFrames = 4;
    Shared->statsClassMode = 0;
    Shared->speed = 0.0f;
    
    Shared->winSizeX = WinSizeX;
    Shared->winSizeY = WinSizeY;

    // Load all loader DSO's before pfConfig() forks 
    for (found = arg; found < argc; found++)
	pfdInitConverter(argv[found]);
    
    // initiate multi-processing mode set in pfMultiprocess call 
    pfConfig();
    
    pfScene *scene = new pfScene();
    
    if (arg < argc)
    {
	// specify directories where geometry and textures exist 
	if (!(getenv("PFPATH")))
	    pfFilePath(
		       "."
		       ":./data"
		       ":../data"
		       ":../../../../data"
		       ":/usr/share/Performer/data"
		       );
	
	// load files named by command line arguments 
	for (found = 0; arg < argc; arg++)
	{
	    if ((root = pfdLoadFile(argv[arg])) != NULL)
	    {
		scene->addChild(root);
		found++;
	    }
	}
    }
    
    pfPipe *p = pfGetPipe(0);
    pfPhase(PFPHASE_FLOAT);
    
    Shared->pw = new pfPipeWindow(p);
    Shared->pw->setName("OpenGL Performer");
    Shared->pw->setWinType(WinType);
    Shared->pw->setOriginSize(WinOriginX, WinOriginY, WinSizeX, WinSizeY);
    // Open and configure the GL window. 
    Shared->pw->setConfigFunc(OpenPipeWin);
    Shared->pw->config();
    
    pfFrameRate(15.0f);
    
    pfChannel *chan = new pfChannel(p);
    chan->setTravFunc(PFTRAV_CULL, CullChannel);
    chan->setTravFunc(PFTRAV_DRAW, DrawChannel);
    pfIsectFunc(IsectFunc);
    chan->setScene(scene);
    chan->setNearFar(0.1f, far);
    // vertical FOV is matched to window aspect ratio 
    chan->setFOV(45.0f, -1.0f);
    Shared->chan = chan;
    
    // Create an earth/sky model that draws sky/ground/horizon 
    pfEarthSky *eSky = new pfEarthSky();
    if (varESky)
    {
	eSky->setMode(PFES_BUFFER_CLEAR, PFES_SKY_GRND);
	eSky->setAttr(PFES_GRND_HT, -10.0f);
    }
    else 
    {
	eSky->setMode(PFES_BUFFER_CLEAR, PFES_FAST);
	eSky->setColor(PFES_CLEAR, .3f, .3f, .7f, 1.0f);
    }
    
    chan->setESky(eSky);
    
    // create stats channel 
    pfChannel *statsChan = new pfChannel(p);
    statsChan->setTravFunc(PFTRAV_CULL, CullStatsChannel);
    statsChan->setTravFunc(PFTRAV_DRAW, DrawStatsChannel);
    statsChan->setViewport(0.5f, 1.0f, 0.0f, 1.0f);
    // don't do any statistics collection for the stats-display chan 
    statsChan->getFStats()->setClass(PFSTATS_ALL, PFSTATS_OFF );
    Shared->statsChan = statsChan;
    
    statsChan->setScene(scene);
    statsChan->setTravMode(PFTRAV_DRAW, PFDRAW_OFF);
    statsChan->setStatsMode(PFCSTATS_DRAW, PFSTATS_ALL );
    
    // Set initial view to be "in front" of scene 
    if (found)
    {	
	pfVec3 view;
	float sceneSize;
	// determine extent of scene's geometry 
	pfuTravCalcBBox(scene, &bbox);
	// view point at center of bbox 
	view.add(bbox.min, bbox.max);
	Shared->view.xyz.scale(0.5f, view);
	
	// find max dimension 
	sceneSize = bbox.max[PF_X] - bbox.min[PF_X];
	sceneSize = PF_MAX2(sceneSize, bbox.max[PF_Y] - bbox.min[PF_Y]);
	sceneSize = PF_MAX2(sceneSize, bbox.max[PF_Z] - bbox.min[PF_Z]);
	sceneSize = PF_MIN2(sceneSize, 0.5f*far);
	Shared->sceneSize = sceneSize;
	
	// offset so all is visible 
	Shared->view.xyz[PF_Y] -=      sceneSize;
	Shared->view.xyz[PF_Z] += 0.25f*sceneSize;
	Shared->view.hpr.set(0.0f, 0.0f, 0.0f);
	chan->setView(Shared->view.xyz, Shared->view.hpr);
    }
    else
    {
	Shared->view.xyz.set(0.0f, 0.0f, 100.0f);
	PFSET_VEC3(bbox.min, -5000.0f, -5000.0f, -1000000.0f);
	PFSET_VEC3(bbox.max, 5000.0f, 5000.0f, 10000000.0f);
    }
    reset.xyz.copy(Shared->view.xyz);
    reset.hpr.copy(Shared->view.hpr);
    
    chan->setTravMode(PFTRAV_CULL, PFCULL_ALL);
    
    // initialize channel statistics for mail channel
    Shared->frameStats = chan->getFStats();
    // this would enable only proc frame times stats to be
    // displayed in the main channel
    //
    // pfChanStatsMode(chan, PFCSTATS_DRAW, PFFSTATS_ENPFTIMES);
    
    Shared->frameStats->setAttr(PFFSTATS_UPDATE_FRAMES, 30.0f);
    
    if (found && WriteScene)
    {
	fp = fopen ("scene.out","w");
	pfPrint(scene, PFTRAV_SELF|PFTRAV_DESCEND, PFPRINT_VB_DEBUG, fp);
	fclose(fp);
    }
    {
	pfFrameStats *fstats = new pfFrameStats();
	// pfuTravCountDB enables GFX and DB stats and
	// counts the scene stats into the CUR framestats 
	
	pfuTravCountDB((pfNode*)scene, fstats);
	// print the enabled and counted stats 
	pfPrint(fstats, PFFSTATS_BUF_CUR | fstats->getClass(PFSTATS_ALL),
		PFPRINT_VB_ON, fp);
	fstats->checkDelete();
    }
    
    if (CachedCull)
	pfuTravCachedCull((pfNode *) scene, 1);
    
    // main simulation loop 
    while (!Shared->exitFlag)
    {
	// sleep so can see line 
	if (Shared->Sleep)
	    Nap(Shared->SleepApp);
	
	// wait until next frame boundary 
	pfSync();
	
	// sleep so can see line 
	if (Shared->Sleep)
	    Nap(Shared->SleepApp);
	
	// Set view parameters. 
	if (Shared->reset)
	{
	    Shared->view.xyz.copy(reset.xyz);
	    Shared->view.hpr.copy(reset.hpr);
	    Shared->reset = 0;
	}
	else
	    UpdateView();
	chan->setView(Shared->view.xyz, Shared->view.hpr);
	if (CachedCull)
	{ // Flag all channel viewpoints as dirty 
	    pfuChanCullCache  *ccc = (pfuChanCullCache *)chan->getChanData();
	    ccc->chanId = 0;
	    ccc->updateFrame = pfGetFrameCount();
	    chan->passChanData();
	}
	
	// initiate traversal using current state 
	pfFrame();
	
	{ // set stats channel 
	    static int haveStatsChan = -1;
	    if (haveStatsChan != Shared->haveStatsChan)
		EnableStatsChan(Shared->haveStatsChan);
	    haveStatsChan = Shared->haveStatsChan;
	}
	
	// process stats selections 
	if (Shared->statsEnable)
	{
	    if (Shared->frameStats->getClass(Shared->statsEnable))
		Shared->frameStats->setClass(Shared->statsEnable, PFSTATS_OFF);
	    else
		Shared->frameStats->setClass(Shared->statsEnable, PFSTATS_ON);
	    Shared->statsEnable = 0;
	}
	if (Shared->statsClassMode)
	{
	    Shared->frameStats->setClassMode(Shared->statsClass,
                                             Shared->statsClassMode, 
					     !(Shared->frameStats->getClassMode(Shared->statsClass) &
					       Shared->statsClassMode));
	    Shared->statsClassMode = 0;
	}
	if (Shared->printStats)
	{ // print all enabled statistics 
	    pfPrint(Shared->frameStats, 
		    PFFSTATS_BUF_PREV | PFFSTATS_BUF_AVG |
		    Shared->frameStats->getClass(PFSTATS_ALL), 
		    PFPRINT_VB_INFO, NULL);
	    Shared->printStats = 0;
	}
	if (Shared->doStatsQuery)
	{ // show how to query a few individual statistics 
	    // note: if the corresponding stats class and mode is not enabled
	    // 	then the query will simply return 0 for that value.
	    
	    unsigned int  qtmp[5];
	    float ftmp[5];
	    int ret;
	    
	    qtmp[0] = PFFSTATS_BUF_PREV | PFSTATSVAL_GFX_GEOM_TRIS;
	    qtmp[1] = PFFSTATS_BUF_PREV | PFFSTATSVAL_PFTIMES_PROC_TOTAL;
	    qtmp[2] = PFFSTATS_BUF_PREV | PFSTATSVAL_CPU_SYS_BUSY;
	    qtmp[3] = NULL;
	    ret = Shared->frameStats->mQuery(qtmp, ftmp, 0);
	    fprintf(stderr, "ret = %d bytes\n", ret);
	    fprintf(stderr, "Query num tris: %.0f\n", ftmp[0]);
	    fprintf(stderr, "Query frame time: %.0f msecs\n", ftmp[1]*1000.0f);	
	    fprintf(stderr, "Query  sys busy: %.0f%%\n", ftmp[2]);	
	    Shared->doStatsQuery = 0;
	}
	if (Shared->frameStats->getClass(PFFSTATS_ENPFTIMES) &&
	    (Shared->frameStats->getClassMode(PFFSTATS_PFTIMES) &
	     PFFSTATS_PFTIMES_HIST) &&
	    (((int) Shared->frameStats->getAttr(PFFSTATS_PFTIMES_HIST_FRAMES))
	     != Shared->statsHistFrames))
	{
	    Shared->frameStats->setAttr(PFFSTATS_PFTIMES_HIST_FRAMES, 
					Shared->statsHistFrames);
	    Shared->statsHistFrames =
		Shared->frameStats->getAttr(PFFSTATS_PFTIMES_HIST_FRAMES);
	    // fprintf(stderr, "Set stats history frames to %d\n", 
	    //	    Shared->statsHistFrames); 
	}
    }
    
    // terminate cull and draw processes (if they exist) 
    pfExit();
    
    // exit to operating system 
    return 0;
}

static void 
EnableStatsChan(int flag)
{
    if (!Shared->statsChan)
    {
	Shared->chan->setViewport(0.0f, 1.0f, 0.0f, 1.0f);
	return;
    }
    if (flag)
    {
	// put view on the left,stats on the right 
	Shared->chan->setViewport(0.0f, 0.5f, 0.0f, 1.0f);
	Shared->statsChan->setTravMode(PFTRAV_DRAW, PFDRAW_ON);
    }
    else 
    {
	// put view on the left,stats on the right 
	Shared->chan->setViewport(0.0f, 1.0f, 0.0f, 1.0f);
	Shared->statsChan->setTravMode(PFTRAV_DRAW, PFDRAW_OFF);
    }
}

// 
//	UpdateView() updates the eyepoint based on the information
//	placed in shared memory by GetInput().

static void    
UpdateView(void)
{
    pfCoord *view = &Shared->view;
    float mx, my;
    float cp;
    
    if (!Shared->inWindow)
    {
	Shared->speed = 0.0f;
	return;
    }
    switch (Shared->mouseButtons)
    {
    case Button1Mask: // LEFTMOUSE: faster forward or slower backward
	if (Shared->speed > 0.0f)
	    Shared->speed *= 1.2f;
	else
	    Shared->speed /= 1.2f;
	
	if (PF_ABSLT(Shared->speed, LOSPEED*Shared->sceneSize))
	    Shared->speed = LOSPEED*Shared->sceneSize;
	else if (Shared->speed >=  HISPEED*Shared->sceneSize)
	    Shared->speed = HISPEED*Shared->sceneSize;
	break;
    case Button2Mask:	// MIDDLEMOUSE: stop moving and pick 
	Shared->speed = 0.0f;
	break;
    case Button3Mask: // RIGHTMOUSE: faster backward or slower foreward
	if (Shared->speed < 0.0f)
	    Shared->speed *= 1.2f;
	else
	    Shared->speed /= 1.2f;
	
	if (PF_ABSLT(Shared->speed, LOSPEED*Shared->sceneSize))
	    Shared->speed = -LOSPEED*Shared->sceneSize;
	else if (Shared->speed <=  -HISPEED*Shared->sceneSize)
	    Shared->speed = -HISPEED*Shared->sceneSize;
	break;
    }
    
    if (Shared->mouseButtons & (Button1Mask | Button2Mask | Button3Mask))
    {
	// update view direction 
	mx = 2.0f*(Shared->mouseX / (float)Shared->winSizeX) - 1.0f;
	my = 2.0f*(Shared->mouseY / (float)Shared->winSizeY) - 1.0f;
	view->hpr[PF_H] -= mx*PF_ABS(mx);
	view->hpr[PF_P]  = my*PF_ABS(my)*90.0f;
	view->hpr[PF_R]  = 0.0f;
    }
    
    // update view position 
    cp = cosf(PF_DEG2RAD(view->hpr[PF_P]));
    view->xyz[PF_X] += Shared->speed*sinf(-PF_DEG2RAD(view->hpr[PF_H]))*cp;
    view->xyz[PF_Y] += Shared->speed*cosf(-PF_DEG2RAD(view->hpr[PF_H]))*cp;
    view->xyz[PF_Z] += Shared->speed*sinf( PF_DEG2RAD(view->hpr[PF_P]));
}

//
//	OpenPipeWin() -- create a pipeline: setup the window system,
//	the OpenGL, and OpenGL Performer. this procedure is executed in
//	the draw process (when there is a separate draw process).


Display *Dsp=NULL;

static void
OpenPipeWin(pfPipeWindow *pw)
{
    Window win;
    
    pw->open();
    
    
    {  
	Dsp = pfGetCurWSConnection();
	win = pw->select()->getWSWindow();
	XSelectInput(Dsp, win, StructureNotifyMask | PointerMotionMask |
		     FocusChangeMask | ButtonPressMask | ButtonReleaseMask | 
		     KeyPressMask);
	XMapWindow(Dsp, win);
    }
    // create a light source in the "south-west" (QIII) 
    Sun = new pfLight();
    Sun->setPos(-0.3f, -0.3f, 1.0f, 0.0f);
}

static void
IsectFunc(void *)
{
    // sleep to show time in timing graph 
    Nap(2 * Shared->SleepIsect);
}

//
//	CullChannel() -- traverse the scene graph and generate a
// 	display list for the draw process.  This procedure is 
//	executed in the cull process.


static void
CullChannel(pfChannel *, void *)
{
    // 
    // pfDrawGeoSet or other display listable Performer routines
    // could be invoked before or after pfCull()
    
    
    pfCull();
    
    if (Shared->Sleep)
	Nap(Shared->SleepCull);
}

//
//	DrawChannel() -- draw a channel and read input queue. this
//	procedure is executed in the draw process (when there is a
//	separate draw process).

static void
DrawChannel (pfChannel *channel, void *)
{
    // rebind light so it stays fixed in position 
    Sun->on();
    
    // erase framebuffer and draw Earth-Sky model 
    channel->clear();
    
    // spin loop to show time before pfDraw() in timing graph 
    if (Shared->Sleep)
	Nap(Shared->SleepDraw);
    
    // snap picture of screen 
    
    
    // take snapshot of image 
    if (Shared->snapImage)
    {
	static char str[80];
	static int count = 0;
	pfPipe *cpipe = channel->getPipe();
        int id = pfGetId(cpipe)*10 + count++;
	sprintf(str, "stats.%d.rgb", id);
        fprintf(stderr, "Saving pipe %d image in file %s\n",
		pfGetId(cpipe), str);
	
	// read from FRONT-buffer to get channel stats info 
        glReadBuffer(GL_FRONT);
	pfuSaveImage(str, 0, 0, 
		     Shared->winSizeX, 
		     Shared->winSizeY, 0);
	
	Shared->snapImage = 0;
        fprintf(stderr, "Done\n");
    }
    
    
    // invoke Performer draw-processing for this frame 
    pfDraw();
    
    // draw Performer throughput statistics 
    if (Shared->drawStats)
	channel->drawStats();
    
    Shared->pw->getSize(&Shared->winSizeX, &Shared->winSizeY);
    
	GetXInput();
    
    // spin loop to show time after pfDraw() in timing graph 
    if (Shared->Sleep)
	Nap(Shared->SleepDraw);
    
}


static void
GetXInput(void)
{
    static int x, y;
    int haveMotion = 0;
    pfWSConnection dsp = pfGetCurWSConnection();
    
    if (XEventsQueued(dsp, QueuedAfterFlush))
    while (XEventsQueued(dsp, QueuedAlready))
    {
	XEvent event;
	
	XNextEvent(Dsp, &event);
	switch (event.type) 
	{
	case ConfigureNotify:
	    break;
	case FocusIn:
	    Shared->inWindow = 1;
	    break;
	case FocusOut:
	    Shared->inWindow = 0;
	    break;
	case MotionNotify: 
	    {
		XMotionEvent *motion_event = (XMotionEvent *) &event;
		x =  motion_event->x;
		y =  Shared->winSizeY - motion_event->y;;
		haveMotion = 1;
		Shared->inWindow = 1;
	    }
	    break;
	case ButtonPress: 
	    {
		XButtonEvent *button_event = (XButtonEvent *) &event;
		x = event.xbutton.x;
		y = Shared->winSizeY - event.xbutton.y;
		Shared->inWindow = 1;
		switch (button_event->button) {
		case Button1:
		    Shared->mouseButtons |= Button1Mask;
		    break;
		case Button2:
		    Shared->mouseButtons |= Button2Mask;
		    break;
		case Button3:
		    Shared->mouseButtons |= Button3Mask;
		    break;
		}
	    }
	    break;
	case ButtonRelease:
	    {
		XButtonEvent *button_event = (XButtonEvent *) &event;
		x = event.xbutton.x;
		y = Shared->winSizeY - event.xbutton.y;
		switch (button_event->button) {
		case Button1:
		    Shared->mouseButtons &= ~Button1Mask;
		    break;
		case Button2:
		    Shared->mouseButtons &= ~Button2Mask;
		    break;
		case Button3:
		    Shared->mouseButtons &= ~Button3Mask;
		    break;
		}
	    }
	    break;
	case KeyPress:
	    {
		char buf[100];
		int rv;
		KeySym ks;
		rv = XLookupString(&event.xkey, buf, sizeof(buf), &ks, 0);
		switch(ks) {
		case XK_Escape: 
		    exit(0);
		    break;
		case XK_space:  // stop 
		    Shared->speed = 0.0f;
		    break;
		case XK_r:
		case XK_R:
		    Shared->reset = 1;
		    break;
		case XK_F1:
		    Shared->drawStats = !Shared->drawStats;
		    break;
		    // F2-key toggles separate channel for stats display 
		case XK_F2:
		    Shared->haveStatsChan = !Shared->haveStatsChan;
		    break;
		case XK_Print:
		    Shared->snapImage = 1;
		    break;
		case XK_Up:
		    Shared->statsHistFrames++;
		    break;
		case XK_Down:
		    Shared->statsHistFrames--;
		    break;
		case XK_p: // print currently enabled statistics to stderr 
		    Shared->printStats = 1;
		    break;
		case XK_a: // toggle entire statistics class enable 
		case XK_A:
		    Shared->statsEnable = PFSTATS_ALL;
		    break;
		case XK_s: // toggle system CPU statistics class enable 
		case XK_S:
		    Shared->statsEnable = PFSTATSHW_ENCPU;
		    break;
		case XK_d: // toggle database statistics class enable 
		case XK_D:
		    Shared->statsEnable = PFFSTATS_ENDB;
		    break;
		case XK_c: // toggle cull statistics class enable 
		case XK_C:
		    Shared->statsEnable = PFFSTATS_ENCULL;
		    break;
		case XK_f: // toggle fill stats - depth complexity 
		case XK_F:
		    Shared->statsEnable = PFSTATSHW_ENGFXPIPE_FILL;
		    break;
		case XK_g:
		    Shared->statsEnable =  PFSTATS_ENGFX;
		    break;
		case XK_G: // toggle geometry attribute statistics 
		    // (normals, colors, texcoords)
		    
		    Shared->statsClass = PFSTATS_GFX;
		    Shared->statsClassMode = PFSTATS_GFX_ATTR_COUNTS;
		    break;
		case XK_h: // toggle proc frame time history stats 
		case XK_H:
		    Shared->statsClass = PFFSTATS_PFTIMES;
		    Shared->statsClassMode = PFFSTATS_PFTIMES_HIST;
		    break;
		case XK_n: // raise nap values 
		    Shared->Sleep = 1;
		    Shared->SleepApp += 1.0f;
		    Shared->SleepIsect = Shared->SleepApp;
		    Shared->SleepCull = Shared->SleepApp*3.0f;
		    Shared->SleepDraw = Shared->SleepApp;
		    break;
		case XK_N:
		    Shared->Sleep = 1;
		    Shared->SleepApp -= 1.0f; // lower nap values 
		    Shared->SleepIsect = Shared->SleepApp;
		    Shared->SleepCull = Shared->SleepApp*3.0f;
		    Shared->SleepDraw = Shared->SleepApp;
		    break;
		case XK_Pause: // toggle naps 
		    Shared->Sleep = 0;
		    if (!Shared->Sleep)
			break;
		case XK_m: // toggle mp process frame time stats 
		case XK_M:
		    Shared->statsEnable = PFFSTATS_ENPFTIMES;
		    break;
		case XK_q:
		case XK_Q:
		    Shared->doStatsQuery = 1;
		    break;
		case XK_t: // toggle tmesh lengths graph 
		    Shared->statsClass = PFSTATS_GFX;
		    Shared->statsClassMode = PFSTATS_GFX_TSTRIP_LENGTHS;
		    break;
		case XK_T: // toggle transparency fill stats 
		    Shared->statsClass = PFSTATSHW_GFXPIPE_FILL;
		    Shared->statsClassMode = PFSTATSHW_GFXPIPE_FILL_TRANSPARENT;
		    break;
		case XK_v: // toggle database visiblity stats 
		case XK_V:
		    Shared->statsClass = PFFSTATS_DB;
		    Shared->statsClassMode = PFFSTATS_DB_VIS;
		    break;
		case XK_z: // toggle depth-complexity z-compare stats 
		case XK_Z:
		    Shared->statsClass = PFSTATSHW_GFXPIPE_FILL;
		    Shared->statsClassMode = PFSTATSHW_GFXPIPE_FILL_DEPTHCMP;
		    break;
		}
	    }
	}// switch 
    }
    XFlush(dsp);
    Shared->mouseX = x;
    Shared->mouseY = y;
}


// Stats channel Callbacks 

static void 
CullStatsChannel(pfChannel *, void *) 
{ 
    // empty
}

static pfVec4  statsbclr(0.3f, 0.3f, 0.3f, 1.0f);
static void
DrawStatsChannel (pfChannel *channel, void *)
{
    pfClear(PFCL_COLOR, &statsbclr);
    // draw Performer throughput statistics 
    Shared->frameStats->draw(channel);
}