//===========================================================================//
// File:	l4vidper.cpp																	 	  //
// Project: MUNGA           Brick: Video Renderer Manager	                 //
// Contents: Interface specification Video performance monitoring tools		  //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 03/5/96  GAC  initial coding													        //
//---------------------------------------------------------------------------//
// Copyright (C) 1995, Virtual World Entertainment, Inc.                     //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#include "l4munga.hpp"
#if !defined(L4VIDPER_HPP)
#	include "l4vidper.hpp"
#endif

extern "C" const int32 __sect_time;
extern "C" const int32 __last_cull_time;
extern "C" const int32 __last_draw_time;
extern "C" const int32 __last_frame_time;
extern "C" const int32 __last_pxpl_time;
extern "C" const int32 __last_frame_prims;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Constructor for the StripChartRenderable
// Used to performance monitor a variable that can change every frame
//
StripChartRenderable::StripChartRenderable(
	Entity					*entity,					// Entity to attach the renderable to
	ExecutionType			execution_type,		// How/when to execute the renderable
	dpl_VIEW					*this_view,				// the view associated with our eye
	float						left,
	float						right,
	float						top,
	float						bottom,
	int						max_samples,			// The number of samples to hold
	int						max_value,				//	The maximum value allowed
	int						min_value,				// the minimum value allowed
	int						*value_to_chart,		// the value we will be graphing
	int						update_interval		// How frequently (in frames) to update the graphics
):
	VideoRenderable(entity,	execution_type)
{
	int
		i;
	//
	// Save the incoming data inside this class
	//
	myView				=	this_view;
	myLeft				=	left;
	myRight				=	right;
	myTop					=	top;
	myBottom				=	bottom;
	myMaxSamples		=	max_samples;
	myMaxValue			=	max_value;
	myMinValue			=	min_value;
	myValue				=	value_to_chart;
	myUpdateInterval	=	update_interval;
	myFrameCount		=	0;
	myCurrentSample	=	0;
	//
	// Allocate the RAM to store the values we are charting and clear them out
	//
	myValueStorage = new int[max_samples];
	if(!myValueStorage)
		Fail("stripchart couldn't allocate value storage\n");

	Check_Pointer(myValueStorage);
	for(i = 0; i<max_samples; i++)
	{
		myValueStorage[i] = 0;
	}
	//
	// Allocate space for the display list
	//
	myDisplayList		=	dpl2d_NewDisplayList();
	if(!myDisplayList)
		Fail("stripchart couldn't allocate a display list\n");
	//
	// Draw the bounding box for the chart
	//
	dpl2d_OpenDisplayList			(myDisplayList, dpl2d_open_mode_clear);
	dpl2d_AddFullScreenClipRegion (myDisplayList);
	dpl2d_AddSetColor					(myDisplayList,	0.0f, 0.75f, 0.0f);
	dpl2d_AddOpenPolyline			(myDisplayList);
	dpl2d_AddPoint         			(myDisplayList,	myLeft,	myBottom);
	dpl2d_AddPoint         			(myDisplayList,	myRight,	myBottom);
	dpl2d_AddPoint         			(myDisplayList,	myRight,	myTop);
	dpl2d_AddPoint         			(myDisplayList,	myLeft,	myTop);
	dpl2d_AddPoint         			(myDisplayList,	myLeft,	myBottom);
	dpl2d_AddClosePolyline			(myDisplayList);
	dpl2d_CloseDisplayList			(myDisplayList);
	dpl2d_FlushDisplayList			(myDisplayList);
	//
	// Bind the chart to our eyepoint
	//
	dpl2d_SetViewDisplayList 		( myView, myDisplayList );
	dpl_FlushView ( myView );
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Destructor for the StripChartRenderable
//
StripChartRenderable::~StripChartRenderable()
{
	//
	// Make sure our structure is still in one piece
	//
	Check(this);
	//
	// Unhook the display list from the view, then delete both lists
	//
	dpl2d_SetViewDisplayList ( myView, NULL );
	dpl_FlushView ( myView );
	dpl2d_DeleteDisplayList(myDisplayList);
	delete	myValueStorage;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// TestInstance for the StripChartRenderable
//
Logical
	StripChartRenderable::TestInstance() const
{
	VideoRenderable::TestInstance();
	return True;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Execute for the StripChartRenderable
//
void
	StripChartRenderable::Execute()
{
	int
		i;
	//
	// Increment frame counter
	//
	myFrameCount++;
	//
	// Grab the statistic we're tracking and stuff it into the buffer
	//
	myValueStorage[myCurrentSample] = *myValue;
	//
	// If it's time to redraw the graph, do it
	//
	if(myFrameCount >= myUpdateInterval)
	{
   	myFrameCount = 0;
		//
		// Figure the scale factor for the graph
		//
		float
			current_x,
			current_y,
			x_increment,
			y_increment;
		x_increment = (myRight - myLeft)/ (float)(myMaxSamples);
		y_increment = (myBottom - myTop)/((float)myMaxValue - (float)myMinValue);
		dpl2d_OpenDisplayList			(myDisplayList, dpl2d_open_mode_clear);
		dpl2d_AddFullScreenClipRegion (myDisplayList);
		dpl2d_AddSetColor					(myDisplayList,	0.0f, 0.75f, 0.0f);
		dpl2d_AddOpenPolyline			(myDisplayList);
		dpl2d_AddPoint         			(myDisplayList,	myLeft,	myBottom);
		dpl2d_AddPoint         			(myDisplayList,	myRight,	myBottom);
		dpl2d_AddPoint         			(myDisplayList,	myRight,	myTop);
		dpl2d_AddPoint         			(myDisplayList,	myLeft,	myTop);
		dpl2d_AddPoint         			(myDisplayList,	myLeft,	myBottom);
		dpl2d_AddClosePolyline			(myDisplayList);
		dpl2d_AddOpenPolyline			(myDisplayList);
		current_x	=	myLeft;
		for(i = 0; i < myMaxSamples; i++)
		{
			current_y =	myBottom-((float)myValueStorage[i] * y_increment);
			dpl2d_AddPoint(myDisplayList,current_x,current_y);
			current_x += x_increment;
		}
		dpl2d_AddClosePolyline			(myDisplayList);
		dpl2d_AddOpenLines				(myDisplayList);
		dpl2d_AddPoint         			(myDisplayList,
												myLeft + (x_increment*myCurrentSample),
												myBottom);
		dpl2d_AddPoint         			(myDisplayList,
												myLeft + (x_increment*myCurrentSample),
												myTop);
		dpl2d_AddCloseLines				(myDisplayList);
		dpl2d_CloseDisplayList			(myDisplayList);
		dpl2d_FlushDisplayList			(myDisplayList);
	}
	//
	// Update the current sample pointer
	//
	myCurrentSample ++;
	if(myCurrentSample >= myMaxSamples)
		myCurrentSample = 0;

	//
	// Call the execute method in our parent
	//
	VideoRenderable::Execute();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Constructor for the RendererChartRenderable
// Used to performance monitor a variable that can change every frame
//
RendererChartRenderable::RendererChartRenderable(
	Entity					*entity,					// Entity to attach the renderable to
	ExecutionType			execution_type,		// How/when to execute the renderable
	dpl_VIEW					*this_view,				// the view associated with our eye
	float						left,
	float						right,
	float						top,
	float						bottom,
	int						max_samples,			// The number of samples to hold
	int						max_value,				//	The maximum value allowed
	int						min_value,				// the minimum value allowed
	int						update_interval		// How frequently (in frames) to update the graphics
):
	VideoRenderable(entity,	execution_type)
{
	int
		i;
	//
	// Save the incoming data inside this class
	//
	myView				=	this_view;
	myLeft				=	left;
	myRight				=	right;
	myTop					=	top;
	myBottom				=	bottom;
	myMaxSamples		=	max_samples;
	myMaxValue			=	max_value;
	myMinValue			=	min_value;
	myUpdateInterval	=	update_interval;
	myFrameCount		=	0;
	myCurrentSample	=	0;
	//
	// Allocate the RAM to store the values we are charting and clear them out
	//
	myCullStorage			=	new int[max_samples];
	myDrawStorage			=	new int[max_samples];
	myFrameStorage			=	new int[max_samples];
	myPixelPlanesStorage = 	new int[max_samples];
	myPrimativeStorage	=	new int[max_samples];
	if(!myCullStorage || !myDrawStorage || !myFrameStorage || !myPixelPlanesStorage || !myPrimativeStorage)
		Fail("RendererChartRenderable couldn't allocate storage\n");

	Check_Pointer(myValueStorage);
	for(i = 0; i<max_samples; i++)
	{
		myCullStorage[i] = 0;
		myDrawStorage[i] = 0;
		myFrameStorage[i] = 0;
		myPixelPlanesStorage[i] = 0;
		myPrimativeStorage[i] = 0;
	}
	//
	// Allocate space for the display list
	//
	myDisplayList		=	dpl2d_NewDisplayList();
	if(!myDisplayList)
		Fail("stripchart couldn't allocate a display list\n");
	//
	// Draw the bounding box for the chart
	//
	dpl2d_OpenDisplayList			(myDisplayList, dpl2d_open_mode_clear);
	dpl2d_AddFullScreenClipRegion (myDisplayList);
	dpl2d_AddSetColor					(myDisplayList,	0.0f, 0.75f, 0.0f);
	dpl2d_AddSetLineWidth 			(myDisplayList,1.0);
	dpl2d_AddOpenPolyline			(myDisplayList);
	dpl2d_AddPoint         			(myDisplayList,	myLeft,	myBottom);
	dpl2d_AddPoint         			(myDisplayList,	myRight,	myBottom);
	dpl2d_AddPoint         			(myDisplayList,	myRight,	myTop);
	dpl2d_AddPoint         			(myDisplayList,	myLeft,	myTop);
	dpl2d_AddPoint         			(myDisplayList,	myLeft,	myBottom);
	dpl2d_AddClosePolyline			(myDisplayList);
	//
	// Bind the chart to our eyepoint
	//
	mySystemDisplayList = dpl2d_GetViewDisplayList	(myView);
	if(mySystemDisplayList)
	{
		// If a display list was already installed, call it at the end of this
		// 2d display list so it will still show up.  (ugley, but as this is
		// test code only it should be sufficient)
		dpl2d_AddCallDisplayList	(myDisplayList, mySystemDisplayList );

	}
	dpl2d_CloseDisplayList			(myDisplayList);
	dpl2d_FlushDisplayList			(myDisplayList);
	//
	// hook our dlist up to the view so it will be drawn
	//
	dpl2d_SetViewDisplayList 		( myView, myDisplayList );
	dpl_FlushView ( myView );
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Destructor for the RendererChartRenderable
//
RendererChartRenderable::~RendererChartRenderable()
{
	//
	// Make sure our structure is still in one piece
	//
	Check(this);
	//
	// Unhook the display list from the view, then delete both lists
	//
	dpl2d_SetViewDisplayList ( myView, NULL );
	dpl_FlushView ( myView );
	dpl2d_DeleteDisplayList(myDisplayList);
	delete myCullStorage;
	delete myDrawStorage;
	delete myFrameStorage;
	delete myPixelPlanesStorage;
	delete myPrimativeStorage;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// TestInstance for the RendererChartRenderable
//
Logical
	RendererChartRenderable::TestInstance() const
{
	VideoRenderable::TestInstance();
	return True;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Execute for the RendererChartRenderable
//
void
	RendererChartRenderable::Execute()
{
	int
		i;
	//
	// Increment frame counter
	//
	myFrameCount++;
	//
	// Grab the statistic we're tracking and stuff it into the buffer
	//
	myCullStorage[myCurrentSample] = __last_cull_time;
	myDrawStorage[myCurrentSample] = __last_draw_time;
	myFrameStorage[myCurrentSample] = __last_frame_time;
	myPixelPlanesStorage[myCurrentSample] = __last_pxpl_time;
	myPrimativeStorage[myCurrentSample] = __last_frame_prims;
	//
	// If it's time to redraw the graph, do it
	//
	if(myFrameCount >= myUpdateInterval)
	{
   	myFrameCount = 0;
		//
		// Figure the scale factor for the graph
		//
		float
			current_x,
			current_y,
			x_increment,
			y_increment;
		x_increment = (myRight - myLeft)/ (float)(myMaxSamples);
		y_increment = (myBottom - myTop)/((float)myMaxValue - (float)myMinValue);
		dpl2d_OpenDisplayList			(myDisplayList, dpl2d_open_mode_clear);
		dpl2d_AddFullScreenClipRegion (myDisplayList);
		dpl2d_AddSetLineWidth 			(myDisplayList,1.0);
		dpl2d_AddSetColor					(myDisplayList,	0.0f, 0.75f, 0.0f);
		dpl2d_AddOpenPolyline			(myDisplayList);
		dpl2d_AddPoint         			(myDisplayList,	myLeft,	myBottom);
		dpl2d_AddPoint         			(myDisplayList,	myRight,	myBottom);
		dpl2d_AddPoint         			(myDisplayList,	myRight,	myTop);
		dpl2d_AddPoint         			(myDisplayList,	myLeft,	myTop);
		dpl2d_AddPoint         			(myDisplayList,	myLeft,	myBottom);
		dpl2d_AddClosePolyline			(myDisplayList);
		//
		// Draw the primative count trace
		//
		dpl2d_AddOpenLines				(myDisplayList);
		dpl2d_AddPoint(myDisplayList,myLeft, myBottom + (500.0f * y_increment * 50.0f));
		dpl2d_AddPoint(myDisplayList,myRight, myBottom + (500.0f * y_increment * 50.0f));
		dpl2d_AddPoint(myDisplayList,myLeft, myBottom + (1000.0f * y_increment * 50.0f));
		dpl2d_AddPoint(myDisplayList,myRight, myBottom + (1000.0f * y_increment * 50.0f));
		dpl2d_AddCloseLines           (myDisplayList);
		dpl2d_AddOpenPolyline			(myDisplayList);
		current_x	=	myLeft;
		for(i = 0; i < myMaxSamples; i++)
		{
			current_y =	myBottom+((float)myPrimativeStorage[i] * y_increment * 50.0f);
			dpl2d_AddPoint(myDisplayList,current_x,current_y);
			current_x += x_increment;
		}
		dpl2d_AddClosePolyline			(myDisplayList);
		//
		// Draw the frame time trace
		//
		dpl2d_AddSetColor					(myDisplayList,	0.75f, 0.75f, 0.75f);
		dpl2d_AddOpenPolyline			(myDisplayList);
		current_x	=	myLeft;
		for(i = 0; i < myMaxSamples; i++)
		{
			current_y =	myBottom-((float)myFrameStorage[i] * y_increment);
			dpl2d_AddPoint(myDisplayList,current_x,current_y);
			current_x += x_increment;
		}
		dpl2d_AddClosePolyline			(myDisplayList);
		//
		// Draw the draw time trace
		//
		dpl2d_AddSetColor					(myDisplayList,	0.75f, 0.0f, 0.0f);
		dpl2d_AddOpenPolyline			(myDisplayList);
		current_x	=	myLeft;
		for(i = 0; i < myMaxSamples; i++)
		{
			current_y =	myBottom-((float)myDrawStorage[i] * y_increment);
			dpl2d_AddPoint(myDisplayList,current_x,current_y);
			current_x += x_increment;
		}
		dpl2d_AddClosePolyline			(myDisplayList);
		//
		// Draw the pixel planes time trace
		//
		dpl2d_AddSetColor					(myDisplayList,	0.0f, 0.0f, 0.75f);
		dpl2d_AddOpenPolyline			(myDisplayList);
		current_x	=	myLeft;
		for(i = 0; i < myMaxSamples; i++)
		{
			current_y =	myBottom-((float)myPixelPlanesStorage[i] * y_increment);
			dpl2d_AddPoint(myDisplayList,current_x,current_y);
			current_x += x_increment;
		}
		dpl2d_AddClosePolyline			(myDisplayList);
		//
		// Stack the cull time on top of the draw
		//
		dpl2d_AddSetColor					(myDisplayList,	0.0f, 0.75f, 0.0f);
		dpl2d_AddOpenPolyline			(myDisplayList);
		current_x	=	myLeft;
		for(i = 0; i < myMaxSamples; i++)
		{
			current_y =	myBottom-(((float)myCullStorage[i] + (float)myDrawStorage[i]) * y_increment);
			dpl2d_AddPoint(myDisplayList,current_x,current_y);
			current_x += x_increment;
		}
		dpl2d_AddClosePolyline			(myDisplayList);
		//
		// Draw a line indicating the current time
		//
		dpl2d_AddOpenLines				(myDisplayList);
		dpl2d_AddPoint         			(myDisplayList,
												myLeft + (x_increment*myCurrentSample),
												myBottom);
		dpl2d_AddPoint         			(myDisplayList,
												myLeft + (x_increment*myCurrentSample),
												myTop);
		dpl2d_AddCloseLines				(myDisplayList);
		//
		// If there is a pre existing dlist, insert a call to it here
		//
		if(mySystemDisplayList)
		{
			dpl2d_AddCallDisplayList	(myDisplayList, mySystemDisplayList );
		}
		//
		// Close up the display list and flush it
		//
		dpl2d_CloseDisplayList			(myDisplayList);
		dpl2d_FlushDisplayList			(myDisplayList);
	}
	//
	// Update the current sample pointer
	//
	myCurrentSample ++;
	if(myCurrentSample >= myMaxSamples)
		myCurrentSample = 0;

	//
	// Call the execute method in our parent
	//
	VideoRenderable::Execute();
}
