#include "pch.hpp"
//===========================================================================//
// File:	 DebugGraphs.cpp												 //
// Project:  GameOS					                                         //
// Contents: Debug interface classes / routines								 //
//---------------------------------------------------------------------------//
// Date      Who    Modification                                             //
// 10/29/99  AG     Created													 //
//																			 //
//---------------------------------------------------------------------------//
// Copyright (C) 1999 Microsoft Corp.						                 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//
#include "Profiler.hpp"
#include "Debugger.hpp"
#include "Texture Manager.hpp"
#include "ErrorHandler.hpp"
#include "DXRasterizer.hpp"



gos_VERTEX Graph[512];



//
//
//
Stat* GraphsActive[20];		// Number of frame graphs active
DWORD NumberGraphsActive=0;



//
// Work out list of active 'frame graphs'
//
void UpdateGraphs()
{
	NumberGraphsActive=0;
	memset( GraphsActive, 0, sizeof(GraphsActive) );

	Stat* pThis=pStatistics;

	while( pThis )
	{
		if( pThis->Flags&Stat_Graph )
		{
			GraphsActive[NumberGraphsActive++]=pThis;
		}
		pThis=pThis->pNext;
	}
}




//
//
// Update the backbuffer with any debugging information just before the blit.
//
void ShowFrameGraphs()
{
	#ifdef LAB_ONLY
	if( gFrameGraph )
	{
		switch( GraphMode )
		{
//
// Bar graph
//
			case Graph_30:
			case Graph_60:
			{
//
// Work out how many graphs have been asked for an scale accordingly
//
				if( ShowColorInfo )
				{
					gos_SetRenderState( gos_State_Texture, FontTexture );
					gos_SetRenderState( gos_State_AlphaTest, 1 );
					CurrentX=30;
					CurrentY=10;
					DbChrX=0;
					DbMaxX=40;
					DbChrY=0;
					DrawText( 0xffffffff, ( GraphMode==Graph_30 ? "30Hz Display" : "60Hz Display" ) );
				}

				DWORD Xpos=20;
				DWORD Ypos=20;
				DWORD CyclesPerPixel=(DWORD)((ProcessorSpeed*1000000.0f / ( GraphMode==Graph_30 ? 30.0f : 60.0f ))/(Environment.screenWidth-40));
				DWORD GraphNumber=0;
				gos_SetRenderState( gos_State_Texture, 0 );
				gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );

				Stat* pStat=pStatistics;
				DWORD TotalCycles=0;
				bool DisplayedRemainder=0;

				while( pStat || TotalCycles )
				{
					if( (0==pStat || ((pStat->Flags&Stat_Graph) && pStat->Type==gos_timedata)) && Ypos<=20+5*18 )
					{
						DWORD Cycles;
						DWORD Pixels;
						
						if( 0==pStat )
						{
							Cycles=(DWORD)(PercentHistory[GraphHead]*100.0f-TotalCycles);
						}
						else
						{
							Cycles=(DWORD)(PercentHistory[GraphHead]);
							TotalCycles+=Cycles;
						}
						Pixels=Cycles/CyclesPerPixel;

						if( 0==pStat && Pixels )
							DisplayedRemainder=true;
						
						while( Xpos+Pixels>Environment.screenWidth-20 )				// Need to wrap bar?
						{
							DWORD ToDraw=Environment.screenWidth-20-Xpos;

							DrawSquare( Xpos, Ypos, ToDraw, 16, gosColorTable[GraphNumber] );

							Xpos=20;
							Ypos+=18;
							if( Ypos>20+5*18 )
							{
								Pixels=0;
								break;
							}
							Pixels=Pixels-ToDraw;
						}

						if( Pixels )
						{
							DrawSquare( Xpos, Ypos, Pixels, 16, gosColorTable[GraphNumber] );
							Xpos+=Pixels;
						}
	
						GraphNumber++;
					}
					if( pStat )
						pStat=pStat->pNext;
					else
						TotalCycles=0;
				}
//
// Now display names (average last 16 frames of positions to stop text bouncing around)
//
				static DWORD AveY[16];
				static DWORD TotalAverageY=0;
				static DWORD YAvePos=0;

				if( ShowColorInfo )
				{
					if( Xpos!=20 )
						Ypos+=18;
					TotalAverageY-=AveY[YAvePos&15];
					TotalAverageY+=Ypos;
					AveY[YAvePos&15]=Ypos;
					YAvePos++;

					while( Ypos<TotalAverageY/16 )
						Ypos+=18;

					gos_SetRenderState( gos_State_Texture, FontTexture );
					gos_SetRenderState( gos_State_AlphaTest, 1 );
					gos_SetRenderState( gos_State_Clipping, 2 );

					GraphNumber=0;
					pStat=pStatistics;
					while( pStat )
					{
						if( (pStat->Flags&Stat_Graph) && pStat->Type==gos_timedata )
						{
							CurrentX=20;
							CurrentY=Ypos;
							DbChrX=0;
							DbMaxX=40;
							DbChrY=0;
							DrawText( gosColorTable[GraphNumber], pStat->Name );
							Ypos+=9;
							GraphNumber++;
						}
						pStat=pStat->pNext;
					}
					if( DisplayedRemainder )
					{
						CurrentX=20;
						CurrentY=Ypos;
						DbChrX=0;
						DbMaxX=40;
						DbChrY=0;
						DrawText( gosColorTable[GraphNumber], "Other areas" );
					}
					gos_SetRenderState( gos_State_Clipping, 0 );
				}
				return;
			}
//
// Pie charts
//
			case Graph_Pie30:
			case Graph_Pie60:
			{
//
// Work out how many graphs have been asked for an scale accordingly
//
				if( ShowColorInfo )
				{
					gos_SetRenderState( gos_State_Texture, FontTexture );
					gos_SetRenderState( gos_State_AlphaTest, 1 );
					CurrentX=2;
					CurrentY=2;
					DbChrX=0;
					DbMaxX=40;
					DbChrY=0;
					DrawText( 0xffffffff, ( GraphMode==Graph_Pie30 ? "30Hz" : "60Hz" ) );
				}

				bool DisplayedRemainder=0;
				DWORD PieAngle=0;
				float PieNumber=0;
				DWORD CyclesPerPixel=(DWORD)((ProcessorSpeed*1000000.0f / ( GraphMode==Graph_Pie30 ? 30.0f : 60.0f ))/(100.0f));
				DWORD GraphNumber=0;
				gos_SetRenderState( gos_State_Texture, 0 );
				gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );

				Stat* pStat=pStatistics;
				DWORD TotalCycles=0;

				while( (pStat || TotalCycles) && PieNumber<400.0f )
				{
					if( 0==pStat || ((pStat->Flags&Stat_Graph) && pStat->Type==gos_timedata) )
					{
						DWORD Cycles;
						DWORD Pixels;
						
						if( 0==pStat )
						{
							Cycles=(DWORD)(PercentHistory[GraphHead]*100.0f-TotalCycles);
						}
						else
						{
							Cycles=(DWORD)PercentHistory[GraphHead];
							TotalCycles+=Cycles;
						}
						Pixels=Cycles/CyclesPerPixel;

						if( 0==pStat && Pixels )
							DisplayedRemainder=true;
						
						while( PieAngle+Pixels>100 )						// Go over 1 pie chart?
						{
							DWORD ToDraw=100-PieAngle;

							for( int t2=0; t2<ToDraw; t2++ )
								PieData[t2+PieAngle+1].argb=gosColorTable[GraphNumber];

							gos_DrawFans( PieData, 102 );

							PieAngle=0;
							PieNumber+=100.0f;

							for( int t0=0; t0<103; t0++ )
								PieData[t0].x+=100.0f;
							
							if( PieNumber>=400.0f )
							{
								Pixels=0;
								break;
							}
							Pixels=Pixels-ToDraw;
						}

						if( Pixels )
						{
							for( int t2=0; t2<Pixels; t2++ )
								PieData[t2+PieAngle+1].argb=gosColorTable[GraphNumber];
							PieAngle+=Pixels;
						}
	
						GraphNumber++;
					}
					if( pStat )
						pStat=pStat->pNext;
					else
						TotalCycles=0;
				}

				if( PieAngle )
					gos_DrawFans( PieData, PieAngle+2 );
//
// Reset pie chart data
//
				for( int t0=0; t0<103; t0++ )
					PieData[t0].x-=PieNumber;
//
// Now display names (average last 16 frames of positions to stop text bouncing around)
//
				if( ShowColorInfo )
				{
					gos_SetRenderState( gos_State_Texture, FontTexture );
					gos_SetRenderState( gos_State_AlphaTest, 1 );
					gos_SetRenderState( gos_State_Clipping, 2 );

					static DWORD AveX[16];
					static DWORD TotalAverageX=0;
					static DWORD XAvePos=0;

					DWORD Xpos=(DWORD)(PieNumber+110.0f);
					
					TotalAverageX-=AveX[XAvePos&15];
					TotalAverageX+=Xpos;
					AveX[XAvePos&15]=Xpos;
					XAvePos++;

					while( Xpos<TotalAverageX/16 )
						Xpos+=100;

					DWORD Ypos=20;
					GraphNumber=0;
					pStat=pStatistics;
					while( pStat )
					{
						if( (pStat->Flags&Stat_Graph) && pStat->Type==gos_timedata )
						{
							CurrentX=Xpos;
							CurrentY=Ypos;
							DbChrX=0;
							DbMaxX=40;
							DbChrY=0;
							DrawText( gosColorTable[GraphNumber], pStat->Name );
							Ypos+=9;
							GraphNumber++;
						}
						pStat=pStat->pNext;
					}
					if( DisplayedRemainder )
					{
						CurrentX=Xpos;
						CurrentY=Ypos;
						DbChrX=0;
						DbMaxX=40;
						DbChrY=0;
						DrawText( gosColorTable[GraphNumber], "Other areas" );
					}
				}
				gos_SetRenderState( gos_State_Clipping, 0 );
				PieNumber=0;
				return;
			}
//
// Display profile data as FRAME GRAPHS
//
			default:
			{
//
// Work out how many graphs have been asked for an scale accordingly
//
				DWORD NumGraph=NumberGraphsActive;
				if( NumGraph==0 )
					return;
//
// Center graphs in the middle of the screen
//
				int gOffset;
				int tOffset;
				if( Environment.screenWidth<640 )
				{
					gos_SetRenderState( gos_State_Clipping, 2 );
					gOffset=Environment.screenWidth-640;
					tOffset=-60;
				}
				else
				{
					gos_SetRenderState( gos_State_Clipping, 0 );
					gOffset=(Environment.screenWidth-640)/2;
					tOffset=gOffset;
				}	
//
// Update any graphs selected in the profiller
//
				InitTextDisplay();
				DebuggerAlpha=0xff000000;
//
// Maximum 20 graphs, ranging from 100 pixels tall to 16 pixels
//
				if( NumGraph>12 )
					NumGraph=12;
				if( NumGraph<2 )
					NumGraph=1;

				int GraphSize=(Environment.screenHeight-40)/NumGraph;
				if( GraphSize>(120+18) )
					GraphSize=(120+18);
				if( GraphSize<(16+18) )
					GraphSize=(16+18);

				GraphSize-=18;
				
				int GraphY=20;

				for( DWORD cg=0; ((GraphY+GraphSize+18)<=(Environment.screenHeight-20)) && cg<NumGraph; cg++ )
				{
					Stat* pStat=GraphsActive[cg];

					if( pStat->Flags&Stat_Graph )
					{
//
// Should I show milliseconds or percent of frame time?
//
						bool ShowMilliseconds=gShowGraphsAsTime && pStat->Type==gos_timedata;
//
// Show I draw a solid background behind graphs?
//
						if( gShowGraphBackground )
						{
							gos_SetRenderState( gos_State_Texture, 0 );
							gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
							if( gNoGraph )
								DrawSquare( gOffset+68+510, GraphY+5+GraphSize/2, 9*6, 12, 0x004020 );
							else
								DrawSquare( gOffset+58, GraphY-3, 578, GraphSize+19, 0x004020 );
						}
//
// Setup states for text labels
//
						gos_SetRenderState( gos_State_Texture, FontTexture );
						gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
						gos_SetRenderState( gos_State_AlphaTest, 1 );
//
// Name of graph (top left)
//
						CurrentX=tOffset+68;
						CurrentY=GraphY;
						DbChrX=0;
						DbChrY=0;
						DbMaxX=40;
						if( !gNoGraph )
							DrawText( 0xffffffff, pStat->Name );
//
// Units of graph (top right)
//				
						if( ShowMilliseconds )
							CurrentX=gOffset+68+512-6-6*(strlen("Milliseconds")>40 ? 40 : strlen("Milliseconds"));
						else
							CurrentX=gOffset+68+512-6-6*(strlen(pStat->TypeName)>40 ? 40 : strlen(pStat->TypeName));
						CurrentY=GraphY;
						DbChrX=0;
						DbChrY=0;
						if( !gNoGraph )
						{
							if( ShowMilliseconds )
								DrawText( 0xffffffff, "Milliseconds" );
							else
								DrawText( 0xffffffff, pStat->TypeName );
						}
//
// Maximum
//
						double tMax;
						if( pStat->Type==gos_timedata && ShowMilliseconds )
							tMax=floor(pStat->Maximum*OneOverProcessorSpeed)+1.0f;
						else
							tMax=(pStat->Flags&Stat_GraphMax) ? pStat->MaxValue : pStat->Maximum;
						char Buffer[128];

						sprintf( Buffer,"%-8.f",tMax );

						CurrentX=gOffset+68+512;
						CurrentY=GraphY+7;
						DbChrX=0;
						DbChrY=0;
						DbMaxX=((Environment.screenWidth-4)-CurrentX)/6;
						
						if( !gNoGraph )
							DrawText( 0xffffffff, Buffer );
//
// Minimum
//
						double tMin;
						if( pStat->Type==gos_timedata && ShowMilliseconds )
						{
							tMin=floor(pStat->Minimum*OneOverProcessorSpeed);
							if( tMin<0 )
								tMin=0.0f;
						}
						else
							tMin=(pStat->Flags&Stat_GraphMax) ? 0.0 : pStat->Minimum;

						sprintf( Buffer,"%-8.f",tMin );

						CurrentX=gOffset+68+512;
						CurrentY=GraphY+7+GraphSize;
						DbChrX=0;
						DbChrY=0;
						DbMaxX=((Environment.screenWidth-4)-CurrentX)/6;
						
						if( !gNoGraph )
							DrawText( 0xffffffff, Buffer );
//
// Calculate current Value (or average of last 16)
//
						double tCurrent=GetHistory(pStat,GraphHead);
						if( gShowAverage && pStat->Count>15 )
						{
							if( pStat->Type==gos_timedata && !ShowMilliseconds )
							{
								tCurrent=tCurrent*PercentHistory[GraphHead] +
								GetHistory(pStat,(GraphHead-1)&511) * PercentHistory[(GraphHead-1)&511] +
								GetHistory(pStat,(GraphHead-2)&511) * PercentHistory[(GraphHead-2)&511] +
								GetHistory(pStat,(GraphHead-3)&511) * PercentHistory[(GraphHead-3)&511] +
								GetHistory(pStat,(GraphHead-4)&511) * PercentHistory[(GraphHead-4)&511] +
								GetHistory(pStat,(GraphHead-5)&511) * PercentHistory[(GraphHead-5)&511] +
								GetHistory(pStat,(GraphHead-6)&511) * PercentHistory[(GraphHead-6)&511] +
								GetHistory(pStat,(GraphHead-7)&511) * PercentHistory[(GraphHead-7)&511] +
								GetHistory(pStat,(GraphHead-8)&511) * PercentHistory[(GraphHead-8)&511] +
								GetHistory(pStat,(GraphHead-9)&511) * PercentHistory[(GraphHead-9)&511] +
								GetHistory(pStat,(GraphHead-10)&511) * PercentHistory[(GraphHead-10)&511] +
								GetHistory(pStat,(GraphHead-11)&511) * PercentHistory[(GraphHead-11)&511] +
								GetHistory(pStat,(GraphHead-12)&511) * PercentHistory[(GraphHead-12)&511] +
								GetHistory(pStat,(GraphHead-13)&511) * PercentHistory[(GraphHead-13)&511] +
								GetHistory(pStat,(GraphHead-14)&511) * PercentHistory[(GraphHead-14)&511] +
								GetHistory(pStat,(GraphHead-15)&511) * PercentHistory[(GraphHead-15)&511];
								tCurrent/=16.0f;
							}
							else
							{
								tCurrent+=
								GetHistory(pStat,(GraphHead-1)&511) +
								GetHistory(pStat,(GraphHead-2)&511) +
								GetHistory(pStat,(GraphHead-3)&511) +
								GetHistory(pStat,(GraphHead-4)&511) +
								GetHistory(pStat,(GraphHead-5)&511) +
								GetHistory(pStat,(GraphHead-6)&511) +
								GetHistory(pStat,(GraphHead-7)&511) +
								GetHistory(pStat,(GraphHead-8)&511) +
								GetHistory(pStat,(GraphHead-9)&511) +
								GetHistory(pStat,(GraphHead-10)&511) +
								GetHistory(pStat,(GraphHead-11)&511) +
								GetHistory(pStat,(GraphHead-12)&511) +
								GetHistory(pStat,(GraphHead-13)&511) +
								GetHistory(pStat,(GraphHead-14)&511) +
								GetHistory(pStat,(GraphHead-15)&511);
								tCurrent/=16.0f;
							}
						}
						else
						{
							if( pStat->Type==gos_timedata && !ShowMilliseconds )
							{
								tCurrent*=PercentHistory[GraphHead];
							}
						}
//
// Display current value
//
						if( pStat->Type==gos_timedata && ShowMilliseconds )
						{
							tCurrent=tCurrent*OneOverProcessorSpeed;
							sprintf( Buffer,"%-8.2f",tCurrent );
						}
						else
						{
							if( pStat->Flags&Stat_1DP )
								sprintf( Buffer,"%-8.1f",tCurrent );
							else if( pStat->Flags&Stat_2DP )
								sprintf( Buffer,"%-8.2f",tCurrent );
							else if( pStat->Flags&Stat_3DP )
								sprintf( Buffer,"%-8.3f",tCurrent );
							else
								sprintf( Buffer,"%-8.f",tCurrent );
						}

						CurrentX=gOffset+68+512;
						CurrentY=GraphY+7+GraphSize/2;
						DbChrX=0;
						DbChrY=0;
						DbMaxX=((Environment.screenWidth-4)-CurrentX)/6;
						
						DrawText( 0xffffffff, Buffer );
//
// Lines around graph
//
						gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
						gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );
						gos_SetRenderState( gos_State_Texture, 0 );

						if( !gNoGraph )
						{
							DrawLines( gOffset+64,GraphY+9,gOffset+64+512,GraphY+9,0xffffff00 );
							DrawLines( gOffset+64,GraphY+11+GraphSize,gOffset+64+512,GraphY+11+GraphSize,0xffffff00 );
							DrawLines( gOffset+63,GraphY+10,gOffset+63,GraphY+11+GraphSize,0xffffff00 );
							DrawLines( gOffset+63+513,GraphY+10,gOffset+63+513,GraphY+11+GraphSize,0xffffff00 );
						}
//
// Work out graph scale
//				
						float GraphBase=(float)tMin;
						float GraphScale=float((tMax-tMin) ? GraphSize/(tMax-tMin) : 0.0f);
//
// Actual graph dots
//				
						if( !gNoGraph )
						{
							DWORD GraphPointer=(GraphHead+1)&511;
							for( int i=0; i<512; i++ )
							{
								float Data;
								if( pStat->Type==gos_timedata )
								{
									if( ShowMilliseconds )
										Data=(((GetHistory(pStat,GraphPointer)*OneOverProcessorSpeed)-GraphBase)*GraphScale);
									else
										Data=((GetHistory(pStat,GraphPointer)*PercentHistory[GraphPointer]-GraphBase)*GraphScale);
								}
								else
								{
									Data=((GetHistory(pStat,GraphPointer)-GraphBase)*GraphScale);
								}

								if( Data<0.0f )
									Data=0.0f;
								else if( Data>GraphSize )
									Data=(float)GraphSize;

								Graph[i].x=(float)(gOffset+i+64);
								Graph[i].y=GraphY+10+GraphSize-Data;
								GraphPointer=(GraphPointer+1)&511;
							}
							
							gos_DrawPoints( Graph,512 );
						}
						GraphY+=GraphSize+18;
					}
				}
				gos_SetRenderState( gos_State_Clipping, 0 );
				return;
			}
		}
	}
	#endif
}














