#include "pch.hpp"
//===========================================================================//
// File:	 Profile.cpp													 //
// Project:  GameOS					                                         //
// Contents: Profiler														 //
//---------------------------------------------------------------------------//
// Date      Who    Modification                                             //
// 05/27/98  AG		Created													 //
//---------------------------------------------------------------------------//
// Copyright (C) 1999 Microsoft Corp.						                 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//
#include "Profiler.hpp"
#include "3DRasterizer.hpp"
#include "Windows.hpp"
#include "DXRasterizer.hpp"
#include "ErrorHandler.hpp"
#include "Debugger.hpp"
#include "Registry.hpp"
#include "MemoryManager.hpp"
#include "Perf.h"
#include "Texture Manager.hpp"

float PercentHistory[512];		// Used to remember cycles per frame
float FrameHistory[8];
int FrameTime;

float ReturnRDTSCInfo();
BYTE	ProfileFlags[32];		// Remember graphs activated 
DWORD	AddNumber=0;

Stat*	pStatistics=0;			// Pointer to chain of statistics
DWORD	NumberStatistics=0;		// Entries in statistic list
DWORD	GraphHead;				// Pointer to current element in statistics History

extern DWORD NumberMemoryMappedFilesOpen;
extern DWORD NumberFilesOpen;

DWORD TextureShift;

// for memory tracking
extern HGOSHEAP DefaultHeap;
extern double MinimumCycleTime;
extern double MinimumProcessorTime;
extern __int64 QPCfrequency;
extern DWORD NumberFTOL;
extern DWORD gFreezeInPause;

extern DWORD RenderersSkipped;

extern DWORD TTFCached;
extern DWORD TTFDiscarded;
extern DWORD TTFUsed;
extern DWORD TTFMemory;
extern DWORD TTF256;
extern DWORD TTF128;
extern DWORD TTF64;
extern DWORD TTF32;
extern DWORD TTF16;

extern float frameRate;

extern DWORD PrimitivesRendered;
extern DWORD PointsRendered;
extern DWORD LinesRendered;
extern DWORD TrianglesRendered;
extern DWORD QuadsRendered;
extern DWORD FanCalls;
extern DWORD StripCalls;
extern float FanLength;
extern float StripLength;
extern DWORD IndexedTriangleCalls;
extern float IndexedTriangleLength;
extern DWORD IndexedVBTriangleCalls;
extern float IndexedVBTriangleLength;

extern DWORD LastSpecularPrim;
extern DWORD LastPerspectivePrim;
extern DWORD LastAlphaPrim;
extern DWORD LastTexturePrim;
extern DWORD LastFilterPrim;
extern DWORD NumPointSampled;
extern DWORD NumBilinear;
extern DWORD NumTrilinear;

extern DWORD TotalRenderStateChanges;
extern DWORD RenderStateCalls;
extern DWORD UniqueRenderStateCalls;

extern DWORD SetupViewportCalls;
extern DWORD SetupViewportClearCalls;
extern DWORD SetupViewportClearCallsZ;

extern DWORD NumSpecular;
extern DWORD NumPerspective;
extern DWORD NumAlpha;
extern DWORD NumTextured;

extern DWORD TexturesUsed;
extern DWORD TextureKUsed,AlphaTextureKUsed,TextureVKUsed;
extern DWORD TextureNSVKUsed;
extern float TextureAGPMegs,TextureLocalMegs;
extern DWORD VerticesUploaded;
extern DWORD TexturesChanged;
extern DWORD TextureUnlocked;
extern DWORD TexturesLoaded;
extern DWORD TexturesCreated;
extern DWORD TexturesBumped;
extern DWORD TexturesSwapped;
extern DWORD TextureBytesPaged;
extern DWORD TextureBytesPaged1;
extern DWORD TexturesDestroyed;
extern DWORD TotalTexMemoryAllocated;
extern DWORD gNumberOfJoystickCounts;

extern volatile __int64 TimeMM;
extern volatile __int64 TimeControls;
extern volatile __int64 TimeScripts;
extern volatile __int64 TimeScriptsUpdate;
extern volatile __int64 TimeD3D;
extern volatile __int64 TimeDXShow;
extern volatile __int64 TimeGameLogic;
extern volatile __int64 TimeNetwork;
extern volatile __int64 TimeRenderers;
extern volatile __int64 TimeOutSideLoop;
extern volatile __int64 TimeDisplay;
extern volatile __int64 TimeEndScene;
extern volatile __int64 TimeFont3D;
extern volatile __int64 TimeWindows;
extern volatile __int64 TimeDebugger;
extern volatile __int64 TimeGameOS;
extern volatile __int64 TimeConvertTextures;
extern volatile __int64 TimeMipmapTextures;
extern volatile __int64 TimeUploadTextures;
extern volatile __int64 TimeRenderTextures;

float CyclesPerSecond=400.0;
__int64 StartCPS=0,StartCycles=0;

__int64 TimeMLR=0;
__int64 TimeInMLR=0;
__int64 TimeToRenderNow=0;
__int64 StartTimeRenderers=0;
__int64 TimeClearViewPort=0;
__int64 TimeInClearViewPort=0;
__int64 tFrameRateSmooth=0;

extern DWORD BytesLoaded;
DWORD TotalBytesLoaded=0;
extern DWORD MemoryAllocated;
extern DWORD MemoryFreed;
DWORD TotalMem=0;
float TotalMemoryAllocated=0;
int MemoryDelta=0;
DWORD MemoryAllocs=0,MemoryFrees=0;
DWORD DebuggerTextures=0;

__int64 TimeScriptsCallbacks=0;


float PercentMM;
float PercentLog;
float TimeAudio;
float TimeVideo;
__int64 TimeWholeLoop;
float BackBufferSize=0.0;
float QPCAccuracy=0.0;
float TimeRunning=0.0;
float PercentDebugger=100.0f;

float* pStatHistory;
DWORD StatsOffset=0;

//
// Init all statistics
//
void InitStatistics()
{
	SPEW(( GROUP_GAMEOS, "InitStatistics()" ));
//
//
//
	pStatistics=0;
	AddNumber=0;
	GraphHead=511;
	memset( FrameHistory, 0, sizeof(FrameHistory) );
	memset( PercentHistory, 0, sizeof(PercentHistory) );
	NewPerformanceRegister=1;
	FrameTime=0;
//
// Maximum values for some of the graphs
//
	frameRate=60;
	PercentMM=100;
	TimeAudio=20;
	TimeVideo=20;
	PercentLog=100;
//
// Setup Internal GOS statistics
//
	#ifdef LAB_ONLY
	AddStatistic( "Framerate", "f/s", gos_float, &frameRate, Stat_Graph+Stat_GraphMax );

	StatisticFormat( "" );
	StatisticFormat( "Time spent in last frame" );
	StatisticFormat( "========================" );
	StatisticFormat( "" );

	AddStatistic( "Game logic",						"%",			gos_timedata, (void*)&TimeGameLogic,		0 );
	AddStatistic( "Update renderers",				"%",			gos_timedata, (void*)&TimeRenderers,		0 );
	AddStatistic( "End 3D scene",					"%",			gos_timedata, (void*)&TimeEndScene,			0 );
	AddStatistic( "Blt or flip backbuffer",			"%",			gos_timedata, (void*)&TimeDisplay,			0 );
	AddStatistic( "Scripts execute",				"%",			gos_timedata, (void*)&TimeScripts,			0 );
	AddStatistic( "Scripts renderers",				"%",			gos_timedata, (void*)&TimeScriptsUpdate,	0 );
	AddStatistic( "Scripts callbacks",				"%",			gos_timedata, (void*)&TimeScriptsCallbacks, 0 );
	AddStatistic( "Networking",						"%",			gos_timedata, (void*)&TimeNetwork,			0 );
	AddStatistic( "Windows messages",				"%",			gos_timedata, (void*)&TimeWindows,			0 );
	AddStatistic( "GameOS functions",				"%",			gos_timedata, (void*)&TimeGameOS,			0 );

	StatisticFormat( "" );
	StatisticFormat( "Individual Routines" );
	StatisticFormat( "===================" );
	StatisticFormat( "" );

	AddStatistic( "Renderers: ClearViewPort",		"%",			gos_timedata, (void*)&TimeClearViewPort,	0 );
	AddStatistic( "Renderers: Generating mipmaps",	"%",			gos_timedata, (void*)&TimeMipmapTextures,	0 );
	AddStatistic( "Renderers: Converting textures",	"%",			gos_timedata, (void*)&TimeConvertTextures,	0 );
	AddStatistic( "Renderers: Uploading textures",	"%",			gos_timedata, (void*)&TimeUploadTextures,	0 );
	AddStatistic( "Renderers: Render to a texture",	"%",			gos_timedata, (void*)&TimeRenderTextures,	0 );
	AddStatistic( "Renderers: Direct3D",			"%",			gos_timedata, (void*)&TimeD3D,				0 );
	AddStatistic( "Renderers: DirectShow",			"%",			gos_timedata, (void*)&TimeDXShow,			0 );
	AddStatistic( "GameOS   : Memory manager",		"%",			gos_timedata, (void*)&TimeMM,				0 );
	AddStatistic( "GameOS   : Read controls",		"%",			gos_timedata, (void*)&TimeControls,			0 );
	AddStatistic( "GameOS   : Outside gameloop",	"%",			gos_timedata, (void*)&TimeOutSideLoop,		0 );
	AddStatistic( "Area     : Font3D",				"%",			gos_timedata, (void*)&TimeFont3D,			0 );
	AddStatistic( "Area     : ^Break debugger",		"%",			gos_float, &PercentDebugger, Stat_2DP+Stat_GraphMax+Stat_AutoReset );

	StatisticFormat( "" );
	StatisticFormat( "GameOS Threads" );
	StatisticFormat( "==============" );
	StatisticFormat( "" );

	AddStatistic( "Streaming audio",		 		"Milliseconds",	gos_float, &TimeAudio, Stat_2DP+Stat_GraphMax+Stat_AutoReset );
	AddStatistic( "Streaming video",		 		"Milliseconds",	gos_float, &TimeVideo, Stat_2DP+Stat_GraphMax+Stat_AutoReset );

	StatisticFormat( "" );
	StatisticFormat( "GameOS Events" );
	StatisticFormat( "=============" );
	StatisticFormat( "" );

	AddStatistic( "Processor speed",				"MHz",			gos_float, &CyclesPerSecond, Stat_1DP );
	AddStatistic( "Cycle drift from QPC",			"seconds",		gos_float, &QPCAccuracy, Stat_2DP );
	AddStatistic( "Time running",					"seconds",		gos_float, &TimeRunning, Stat_2DP );
	#ifdef _DEBUG
	AddStatistic( "_ftol calls",					"calls",		gos_DWORD, &NumberFTOL, Stat_AutoReset );
	#endif
	AddStatistic( "Logging memory used",			"%",			gos_float, &PercentLog, Stat_2DP+Stat_GraphMax );
	AddStatistic( "Bytes loaded from disk",			"bytes",		gos_DWORD, &BytesLoaded, Stat_AutoReset );
	AddStatistic( "Total bytes loaded from disk",	"bytes",		gos_DWORD, &TotalBytesLoaded, Stat_Total );
	AddStatistic( "Memory mapped files open",		"files",		gos_DWORD, &NumberMemoryMappedFilesOpen, 0 );
	AddStatistic( "Regular files open",				"files",		gos_DWORD, &NumberFilesOpen, 0 );
	AddStatistic( "Number of memory allocations",	"allocs",		gos_DWORD, &MemoryAllocs, Stat_AutoReset );
	AddStatistic( "Number of memory frees",			"frees",		gos_DWORD, &MemoryFrees, Stat_AutoReset );
	AddStatistic( "Memory allocated",				"bytes",		gos_DWORD, &MemoryAllocated, Stat_AutoReset );
	AddStatistic( "Memory freed",					"bytes",		gos_DWORD, &MemoryFreed, Stat_AutoReset );
	AddStatistic( "Memory change",					"bytes",		gos_int, &MemoryDelta, Stat_AutoReset );
	AddStatistic( "Total memory allocated",			"Meg",			gos_float, &TotalMemoryAllocated, Stat_Total+Stat_3DP );
	AddStatistic( "Calls to reinit Joysticks",		"calls",		gos_DWORD, &gNumberOfJoystickCounts, Stat_AutoReset );

	StatisticFormat( "" );
	StatisticFormat( "Renderer" );
	StatisticFormat( "========" );
	StatisticFormat( "" );

	AddStatistic( "Number of renderers skipped",	"renderers",	gos_DWORD, &RenderersSkipped, 0 );
	AddStatistic( "BackBuffer(s) + Z Buffer size",	"k",			gos_float, &BackBufferSize, Stat_AutoReset+Stat_Total );
	AddStatistic( "Points rendered",				"points",		gos_DWORD, &PointsRendered, Stat_AutoReset );
	AddStatistic( "Lines rendered",					"lines",		gos_DWORD, &LinesRendered, Stat_AutoReset );
	AddStatistic( "Triangles rendered",				"tris",			gos_DWORD, &TrianglesRendered, Stat_AutoReset );
	AddStatistic( "Quads rendered",					"quads",		gos_DWORD, &QuadsRendered, Stat_AutoReset );
	AddStatistic( "Strips",							"strips",		gos_DWORD, &StripCalls, Stat_AutoReset );
	AddStatistic( "Ave strip length",				"tris",			gos_float, &StripLength, Stat_AutoReset );
	AddStatistic( "Fans",							"fans",			gos_DWORD, &FanCalls, Stat_AutoReset );
	AddStatistic( "Ave fan length",					"tris",			gos_float, &FanLength, Stat_AutoReset );
	AddStatistic( "Indexed triangles",				"tris",			gos_DWORD, &IndexedTriangleCalls, Stat_AutoReset );
	AddStatistic( "Ave indexed length",				"tris",			gos_float, &IndexedTriangleLength, Stat_AutoReset );
	AddStatistic( "Vertexbuffers rendered",			"tris",			gos_DWORD, &IndexedVBTriangleCalls, Stat_AutoReset );
	AddStatistic( "Ave Vertex buffer length",		"tris",			gos_float, &IndexedVBTriangleLength, Stat_AutoReset );
	AddStatistic( "Total primitives rendered",		"prims",		gos_DWORD, &PrimitivesRendered, Stat_AutoReset );

	StatisticFormat( "" );

	AddStatistic( "Textured",						"prims",		gos_DWORD, &NumTextured, Stat_AutoReset );

	AddStatistic( "Clipped",						"prims",		gos_DWORD, &NumClipped, Stat_AutoReset );
	AddStatistic( "GuardBandClipped",				"prims",		gos_DWORD, &NumGuardBandClipped, Stat_AutoReset );

	AddStatistic( "Point sampled",					"prims",		gos_DWORD, &NumPointSampled, Stat_AutoReset );
	AddStatistic( "Bilinear filtered",				"prims",		gos_DWORD, &NumBilinear, Stat_AutoReset );
	AddStatistic( "Trilinear filtered",				"prims",		gos_DWORD, &NumTrilinear, Stat_AutoReset );
	AddStatistic( "Perspective"		,				"prims",		gos_DWORD, &NumPerspective, Stat_AutoReset );
	AddStatistic( "Specular enabled",				"prims",		gos_DWORD, &NumSpecular, Stat_AutoReset );
	AddStatistic( "Alpha blended",					"prims",		gos_DWORD, &NumAlpha, Stat_AutoReset );
	AddStatistic( "Z Compare enabled",				"prims",		gos_DWORD, &NumZCompare, Stat_AutoReset );
	AddStatistic( "Z Write enabled",				"prims",		gos_DWORD, &NumZWrite, Stat_AutoReset );
	AddStatistic( "Alpha test enabled",				"prims",		gos_DWORD, &NumAlphaTest, Stat_AutoReset );
	AddStatistic( "Dithered",						"prims",		gos_DWORD, &NumDithered, Stat_AutoReset );
	AddStatistic( "Texture wrap",					"prims",		gos_DWORD, &NumWrapped, Stat_AutoReset );
	AddStatistic( "Texture clamp",					"prims",		gos_DWORD, &NumClamped, Stat_AutoReset );
	AddStatistic( "Flat shaded",					"prims",		gos_DWORD, &NumFlat, Stat_AutoReset );
	AddStatistic( "Gouraud shaded",					"prims",		gos_DWORD, &NumGouraud, Stat_AutoReset );
	AddStatistic( "Blend decal",					"prims",		gos_DWORD, &NumDecal, Stat_AutoReset );
	AddStatistic( "Blend modulate",					"prims",		gos_DWORD, &NumModulate, Stat_AutoReset );
	AddStatistic( "Blend modulate alpha",			"prims",		gos_DWORD, &NumModulateAlpha, Stat_AutoReset );
	AddStatistic( "Mono enabled",					"prims",		gos_DWORD, &NumMono, Stat_AutoReset );
	AddStatistic( "Fog enabled",					"prims",		gos_DWORD, &NumFog, Stat_AutoReset );
	AddStatistic( "Culled",							"prims",		gos_DWORD, &NumCulled, Stat_AutoReset );

	StatisticFormat( "" );

	AddStatistic( "Total renderstate changes",		"states",		gos_DWORD, &TotalRenderStateChanges, Stat_AutoReset );
	AddStatistic( "Calls to SetRenderState",		"calls",		gos_DWORD, &RenderStateCalls, Stat_AutoReset );
	AddStatistic( "Unique SetRenderState calls",	"calls",		gos_DWORD, &UniqueRenderStateCalls, Stat_AutoReset );

	StatisticFormat( "" );

	AddStatistic( "Calls to SetupViewport",			"calls",		gos_DWORD, &SetupViewportCalls, Stat_AutoReset );
	AddStatistic( "SetupViewports that clear",		"calls",		gos_DWORD, &SetupViewportClearCalls, Stat_AutoReset );
	AddStatistic( "SetupViewports that clear Z",	"calls",		gos_DWORD, &SetupViewportClearCallsZ, Stat_AutoReset );

	StatisticFormat( "" );
	StatisticFormat( "True Type Fonts" );
	StatisticFormat( "===============" );
	StatisticFormat( "" );

	AddStatistic( "Strings cached",					"strings",		gos_DWORD, &TTFCached, 0 );
	AddStatistic( "Strings discarded",				"strings",		gos_DWORD, &TTFDiscarded, 0 );
	AddStatistic( "Textures used",					"textures",		gos_DWORD, &TTFUsed, 0 );
	AddStatistic( "Texture memory (k)",				"k",			gos_DWORD, &TTFMemory, 0 );
	AddStatistic( "256*256",						"textures",		gos_DWORD, &TTF256, 0 );
	AddStatistic( "128*128",						"textures",		gos_DWORD, &TTF128, 0 );
	AddStatistic( " 64*64 ",						"textures",		gos_DWORD, &TTF64, 0 );
	AddStatistic( " 32*32 ",						"textures",		gos_DWORD, &TTF32, 0 );
	AddStatistic( " 16*16 ",						"textures",		gos_DWORD, &TTF16, 0 );

	StatisticFormat( "" );
	StatisticFormat( "Textures" );
	StatisticFormat( "========" );
	StatisticFormat( "" );

	AddStatistic( "Textures used",					"textures",		gos_DWORD, &TexturesUsed, 0 );
	AddStatistic( "Handle changes",					"changes",		gos_DWORD, &TexturesChanged, Stat_AutoReset );
	AddStatistic( "SysMem for all textures (k)",	"k",			gos_DWORD, &TotalTexMemoryAllocated, Stat_AutoReset );
	AddStatistic( "SysMem for used textures (k)",	"k",			gos_DWORD, &TextureKUsed, Stat_AutoReset );
	AddStatistic( "SysMem for used alpha textures (k)",	"k",		gos_DWORD, &AlphaTextureKUsed, Stat_AutoReset );
	AddStatistic( "Current texture shrink factor",	"x",			gos_DWORD, &TextureShift, 0 );
	AddStatistic( "Vidmem for no-shrink textures (k)",	"k",		gos_DWORD, &TextureNSVKUsed, Stat_AutoReset );
	AddStatistic( "Vidmem for used textures (k)",	"k",			gos_DWORD, &TextureVKUsed, Stat_AutoReset );
	AddStatistic( "AGP bandwidth (MB/s)",			"Meg/s",		gos_float, &TextureAGPMegs, Stat_AutoReset+Stat_2DP );
	AddStatistic( "Video memory bandwidth (MB/s)",	"Meg/s",		gos_float, &TextureLocalMegs, Stat_AutoReset+Stat_2DP );
	AddStatistic( "Loaded from disk",				"loaded",		gos_DWORD, &TexturesLoaded, Stat_AutoReset );
	AddStatistic( "Created",						"created",		gos_DWORD, &TexturesCreated, Stat_AutoReset );
	AddStatistic( "Destroyed",						"destroyed",	gos_DWORD, &TexturesDestroyed, Stat_AutoReset );
	AddStatistic( "Bumped from VRAM",				"bumped",		gos_DWORD, &TexturesBumped, Stat_AutoReset );
	AddStatistic( "Textures uploaded into VRAM",	"textures",		gos_DWORD, &TexturesSwapped, Stat_AutoReset );
	AddStatistic( "Texture memory loaded (bytes)",	"bytes",		gos_DWORD, &TextureBytesPaged, Stat_AutoReset );
	AddStatistic( "Mipmap memory loaded (bytes)",	"bytes",		gos_DWORD, &TextureBytesPaged1, Stat_AutoReset );
	AddStatistic( "Textures unlocked",				"bytes",		gos_DWORD, &TextureUnlocked, Stat_AutoReset );
	#ifdef _ARMOR
	AddStatistic( "Vertex memory uploaded (bytes)",	"bytes",		gos_DWORD, &VerticesUploaded, Stat_AutoReset );
	#endif

	#if 0
	StatisticFormat( "" );
	StatisticFormat( "Texture Heaps" );
	StatisticFormat( "=============" );
	StatisticFormat( "" );

	AddStatistic( "Solid 256*256",					"textures",		gos_DWORD, &TexS_256, 0 );
	AddStatistic( "Solid 128*128",					"textures",		gos_DWORD, &TexS_128, 0 );
	AddStatistic( "Solid  64*64",					"textures",		gos_DWORD, &TexS_64, 0 );
	AddStatistic( "Solid  32*32",					"textures",		gos_DWORD, &TexS_32, 0 );
	AddStatistic( "Solid  16*16",					"textures",		gos_DWORD, &TexS_16, 0 );

	StatisticFormat( "" );
	
	AddStatistic( "Keyed 256*256",					"textures",		gos_DWORD, &TexK_256, 0 );
	AddStatistic( "Keyed 128*128",					"textures",		gos_DWORD, &TexK_128, 0 );
	AddStatistic( "Keyed  64*64",					"textures",		gos_DWORD, &TexK_64, 0 );
	AddStatistic( "Keyed  32*32",					"textures",		gos_DWORD, &TexK_32, 0 );
	AddStatistic( "Keyed  16*16",					"textures",		gos_DWORD, &TexK_16, 0 );

	StatisticFormat( "" );
	
	AddStatistic( "Alpha 256*256",					"textures",		gos_DWORD, &TexA_256, 0 );
	AddStatistic( "Alpha 128*128",					"textures",		gos_DWORD, &TexA_128, 0 );
	AddStatistic( "Alpha  64*64",					"textures",		gos_DWORD, &TexA_64, 0 );
	AddStatistic( "Alpha  32*32",					"textures",		gos_DWORD, &TexA_32, 0 );
	AddStatistic( "Alpha  16*16",					"textures",		gos_DWORD, &TexA_16, 0 );

	StatisticFormat( "" );
	#endif

	#endif // LAB_ONLY
//
// Init some global statistic counters
//
	GetTimeStampStart(TimeWholeLoop);	
	TimeDebugger=0;
	UpdateGraphs();
	TotalBytesLoaded=0;
	DebuggerTextures=0;
	SPEW(( GROUP_GAMEOS, "InitStatistics() - Finished" ));
}

//
// Clean up all statistics
//
void DestroyStatistics()
{
	#ifdef LAB_ONLY
	SPEW(( GROUP_GAMEOS, "DestroyStatistics()" ));
	Stat* pStat=pStatistics;

	while( pStat )
	{
		if( pStat->BlockOwner )
		{
			gos_Free( pStat->History );
		}
		Stat* pTemp=pStat;
		pStat=pStat->pNext;
		gos_Free( pTemp );
	}

	pStatHistory=0;
	StatsOffset=0;

	pStatistics=0;
	NumberStatistics=0;
	SPEW(( GROUP_GAMEOS, "DestroyStatistics() - Finished" ));
	#endif
}








//
// Called at the end of a loop to save the previous frames statistics
//
void UpdateStatistics()
{
	#ifdef LAB_ONLY
//
// Check RDTSC is accurate with clock
//
	__int64 timenow;
	QueryPerformanceCounter( (LARGE_INTEGER*)&timenow );
	__int64 cyclesnow=GetCycles();
	if( !StartCPS && !StartCycles )
		CyclesPerSecond=ProcessorSpeed;
	else
		CyclesPerSecond=(float)((cyclesnow-StartCycles)/((timenow-StartCPS)/(double)QPCfrequency)/1000000.0);
	StartCPS=timenow;
	StartCycles=cyclesnow;
	QPCAccuracy=ReturnRDTSCInfo();
	TimeRunning=(float)gos_GetElapsedTime();
//
// End timing of whole game loop
//
	GetTimeStampEnd(TimeWholeLoop);	
	double totaltime=(double)TimeWholeLoop;
	double FrameTotalTime=totaltime;
	if( NoDebuggerStats )
	{
		totaltime-=(double)TimeDebugger;
	}
	if( TimeWholeLoop==0 )
		TimeWholeLoop=1;
	if( totaltime==0 )
		totaltime=1;
	if( FrameTotalTime==0 )
		FrameTotalTime=1;
	totaltime=100.0/totaltime;
	FrameTotalTime=100.0/FrameTotalTime;
	GetTimeStampStart(TimeWholeLoop);	
//
// Work out percentages of total time for all areas
//
	PercentDebugger=(float)(TimeDebugger*FrameTotalTime);
	if( PercentDebugger > 100.0f )
		PercentDebugger=100.0;
	float LogSize=(float)((BYTE*)LogInfo.Current-(BYTE*)LogInfo.First);
	PercentLog=(LogSize/LOGBLOCKSIZE)*100.f;
	if( PercentLog > 100.0f )
		PercentLog=100.0;
//
// Any processing of the statistics must be carried out here
//
	if( FanCalls )
		FanLength=FanLength/FanCalls;

	if( StripCalls )
		StripLength=StripLength/StripCalls;

	if( IndexedTriangleCalls )
		IndexedTriangleLength=IndexedTriangleLength/IndexedTriangleCalls;
	if( IndexedVBTriangleCalls )
		IndexedVBTriangleLength=IndexedVBTriangleLength/IndexedVBTriangleCalls;

	LastFilterPrim=PrimitivesRendered-LastFilterPrim;
	switch( RenderStates[gos_State_Filter] )
	{
		case gos_FilterNone:
			NumPointSampled+=LastFilterPrim;
			break;
		case gos_FilterBiLinear:
			NumBilinear+=LastFilterPrim;
			break;
		case gos_FilterTriLinear:
			NumTrilinear+=LastFilterPrim;
			break;
	}

	LastMonoPrim=PrimitivesRendered-LastMonoPrim;
	if( RenderStates[gos_State_MonoEnable] )
		NumMono+=LastMonoPrim;

	LastFogPrim=PrimitivesRendered-LastFogPrim;
	if( RenderStates[gos_State_Fog] )
		NumFog+=LastFogPrim;

	LastShadePrim=PrimitivesRendered-LastShadePrim;
	if( RenderStates[gos_State_ShadeMode]==gos_ShadeFlat )
		NumFlat+=LastShadePrim;
	else
		NumGouraud+=LastShadePrim;

	LastBlendPrim=PrimitivesRendered-LastBlendPrim;
	if( RenderStates[gos_State_TextureMapBlend]==gos_BlendDecal )
		NumDecal+=LastBlendPrim;
	else if( RenderStates[gos_State_TextureMapBlend]==gos_BlendModulate )
		NumModulate+=LastBlendPrim;
	else
		NumModulateAlpha+=LastBlendPrim;

	LastClipped=PrimitivesRendered-LastClipped;
	if( RenderStates[gos_State_Clipping] )
	{
		if( RenderStates[gos_State_Clipping]==1 || !HasGuardBandClipping )
			NumClipped+=LastClipped;
		else
			NumGuardBandClipped+=LastClipped;
	}

	LastDitheredPrim=PrimitivesRendered-LastDitheredPrim;
	if( RenderStates[gos_State_Dither] )
		NumDithered+=LastDitheredPrim;

	LastCulledPrim=PrimitivesRendered-LastCulledPrim;
	if( RenderStates[gos_State_Culling]!=gos_Cull_None )
		NumCulled+=LastCulledPrim;

	LastTextureAddressPrim=PrimitivesRendered-LastTextureAddressPrim;
	if( RenderStates[gos_State_TextureAddress]==gos_TextureWrap )
		NumWrapped+=LastTextureAddressPrim;
	else
		NumClamped+=LastTextureAddressPrim;

	LastZWritePrim=PrimitivesRendered-LastZWritePrim;
	if( RenderStates[gos_State_ZWrite] )
		NumZWrite+=LastZWritePrim;

	LastZComparePrim=PrimitivesRendered-LastZComparePrim;
	if( RenderStates[gos_State_ZCompare] )
		NumZCompare+=LastZComparePrim;

	LastSpecularPrim=PrimitivesRendered-LastSpecularPrim;
	if( RenderStates[gos_State_Specular] )
		NumSpecular+=LastSpecularPrim;

	LastPerspectivePrim=PrimitivesRendered-LastPerspectivePrim;
	if( RenderStates[gos_State_Perspective] )
		NumPerspective+=LastPerspectivePrim;

	LastAlphaPrim=PrimitivesRendered-LastAlphaPrim;
	if( RenderStates[gos_State_AlphaMode]!=gos_Alpha_OneZero)
		NumAlpha+=LastAlphaPrim;

	LastAlphaTestPrim=PrimitivesRendered-LastAlphaTestPrim;
	if( RenderStates[gos_State_AlphaTest] )
		NumAlphaTest+=LastAlphaTestPrim;

	LastTexturePrim=PrimitivesRendered-LastTexturePrim;
	if( RenderStates[gos_State_Texture]!=0 )
		NumTextured+=LastTexturePrim;
//
//
//
	MemoryDelta=MemoryAllocated-MemoryFreed;
	TotalMem+=MemoryDelta;
	TotalMemoryAllocated=(float)TotalMem/(1024.0f*1024.0f);
	TotalBytesLoaded+=BytesLoaded;
//
// Get and reset the number of textures used in frame
//
	TexturesUsed=GetTexturesUsed();
	TextureAGPMegs=(TextureAGPMegs*frameRate)/(1024.0f*1024.0f);
	TextureLocalMegs=(TextureLocalMegs*frameRate)/(1024.0f*1024.0f);
//
// Get primary / backbuffer (maybe triple buffer) memory usage
//
	BackBufferSize=(float)((Environment.screenWidth*Environment.screenHeight*(Environment.bitDepth>>3))*
						  ((Environment.fullScreen && HardwareRenderer) ? (TripleBuffer ? 3 : 2) : 1 ));
//
// If there is a Z buffer, add that size
//
	BackBufferSize+=(Environment.disableZBuffer ? 0 : (Environment.screenWidth*Environment.screenHeight*(ZBddsd.ddpfPixelFormat.dwZBufferBitDepth>>3)) );
//
// Add size of dekstop to window display mode size
//
	BackBufferSize+=Environment.fullScreen ? 0 : (GetDeviceCaps(DesktopDC,HORZRES) * GetDeviceCaps(DesktopDC,VERTRES) * (GetDeviceCaps(DesktopDC,BITSPIXEL)>>3));
//
// Return result in K
//
	BackBufferSize/=1024.0f;
//
// When gStopSystem is active, do not record anymore
//
	if( !(DebuggerActive && gStopSystem || (gFreezeInPause && gFreezeLogic && 1==LogMode)) )
	{
		GraphHead=(GraphHead+1)&511;
		PercentHistory[GraphHead]=(float)totaltime;
		FrameTime=(FrameTime+1)&7;
		FrameHistory[FrameTime]=(float)(100.0/FrameTotalTime*OneOverProcessorSpeed);
	}
//
// Now grab current values and check for min/max etc...
//
	Stat* pStat=pStatistics;
	while( pStat )
	{
		float temp=0;
		bool NoMinMax=0;

		if( 0==(pStat->Flags&Stat_Format) )
		{
			switch( pStat->Type )
			{
				case gos_DWORD:
				{
					temp=(float)(*(DWORD*)pStat->pVariable);
					break;			
				}
				case gos_WORD:
				{
					temp=*(WORD*)pStat->pVariable;
					break;			
				}
				case gos_BYTE:
				{
					temp=*(BYTE*)pStat->pVariable;
					break;			
				}
				case gos_int:
				{
					temp=(float)(*(int*)pStat->pVariable);
					break;			
				}
				case gos_short:
				{
					temp=*(short*)pStat->pVariable;
					break;			
				}
				case gos_char:
				{
					temp=*(char*)pStat->pVariable;
					break;			
				}
				case gos_float:
				{
					temp=*(float*)pStat->pVariable;
					break;			
				}
				case gos_double:
				{
					temp=(float)(*(double*)pStat->pVariable);
					break;			
				}
				case gos_int64:
				{
					temp=(float)(*(__int64*)pStat->pVariable);
					break;			
				}
				case gos_timedata:
				{
					temp=(float)(*(__int64*)pStat->pVariable);
					break;
				}
				case gos_cycledata:
				{
					DWORD tCount=((gos_CycleData*)(pStat->pVariable))->Count;
					if( 0==tCount )
					{
						temp=0.0f;
						NoMinMax=1;
					}
					else
					{
						if( pStat->Flags&0x80000000 )
						{
							if( PerfCounters && PerfCounterSelected!=-1 )
							{						
								temp=(float)DecodePerformanceMonitors( &((gos_CycleData*)(pStat->pVariable))->TotalCounter[0] );
								temp=(float)(temp/tCount-MinimumProcessorTime);
							}
							else
								temp=0;
						}
						else
							temp=(float)((((gos_CycleData*)(pStat->pVariable))->Cycles)/tCount-MinimumCycleTime);
						if( temp<0.0f )
							temp=0.0f;
					}
					break;			
				}
				default:
					STOP(( "Bad statistic type '%s'", pStat->Name ));
			}
//
// Update the value, unless frozen in the debugger
//
			if( !(DebuggerActive && gStopSystem || (gFreezeInPause && gFreezeLogic && 1==LogMode)) )
			{
				if( pStat->Count==512 )
				{
					pStat->Total-=GetHistory(pStat, GraphHead);	// Average is average of last 512 values
					if( pStat->Type==gos_timedata )
					{
						pStat->TotalPercentage-=GetHistory(pStat,GraphHead)*PercentHistory[GraphHead];
					}
				}

				SetHistory(pStat,GraphHead,temp);

				pStat->Total+=temp;

				if( pStat->Type==gos_timedata )
				{
					pStat->TotalPercentage+=temp*PercentHistory[GraphHead];
				}

				if( pStat->Count==0 )
				{
					pStat->Minimum=temp;
					pStat->Maximum=temp;
					pStat->MaxPercentage=(float)(totaltime*temp);
					pStat->MinPercentage=(float)(totaltime*temp);
				}
				else
				{
					if( !NoMinMax )
					{
						if( pStat->Type==gos_timedata )
						{
							if( temp*totaltime > pStat->MaxPercentage )
								pStat->MaxPercentage=(float)(temp*totaltime);
							if( temp*totaltime < pStat->MinPercentage )
								pStat->MinPercentage=(float)(temp*totaltime);
						}
						if( temp < pStat->Minimum )
							pStat->Minimum=temp;
						if( temp > pStat->Maximum )
							pStat->Maximum=temp;
					}
				}

				if( pStat->Count<512 )
					pStat->Count++;								// How many values are stored in buffer
			}
		}
//
// Reset any statistics marked for automatically being reset
//
		if( gos_cycledata==pStat->Type )
		{
			((gos_CycleData*)(pStat->pVariable))->Count=0;
			((gos_CycleData*)(pStat->pVariable))->Cycles=0;
			((gos_CycleData*)(pStat->pVariable))->TotalCounter[0]=0;
			((gos_CycleData*)(pStat->pVariable))->TotalCounter[1]=0;
		}
		else
		{		
			if( pStat->Flags&Stat_AutoReset )
			{
				switch( pStat->Type )
				{
					case gos_DWORD:
					{
						if( pStat->Flags )
							*(DWORD*)pStat->pVariable=0;
						break;			
					}
					case gos_WORD:
					{
						if( pStat->Flags )
							*(WORD*)pStat->pVariable=0;
						break;			
					}
					case gos_BYTE:
					{
						if( pStat->Flags )
							*(BYTE*)pStat->pVariable=0;
						break;			
					}
					case gos_int:
					{
						if( pStat->Flags )
							*(int*)pStat->pVariable=0;
						break;			
					}
					case gos_short:
					{
						if( pStat->Flags )
							*(short*)pStat->pVariable=0;
						break;			
					}
					case gos_char:
					{
						if( pStat->Flags )
							*(char*)pStat->pVariable=0;
						break;			
					}
					case gos_float:
					{
						if( pStat->Flags )
							*(float*)pStat->pVariable=0;
						break;			
					}
					case gos_double:
					{
						if( pStat->Flags )
							*(double*)pStat->pVariable=0;
						break;			
					}
					case gos_int64:
					{
						if( pStat->Flags )
							*(__int64*)pStat->pVariable=0;
						break;			
					}
					case gos_timedata:
					{
						if( pStat->Flags )
							*(__int64*)pStat->pVariable=0;
						break;			
					}
					default:
						STOP(( "Bad statistic type" ));
				}
			}
		}
		pStat=pStat->pNext;
	}
	LastFilterPrim=0;
	LastSpecularPrim=0;
	LastPerspectivePrim=0;
	LastAlphaPrim=0;
	LastAlphaTestPrim=0;
	LastTexturePrim=0;
	LastZComparePrim=0;
	LastClipped=0;
	LastZWritePrim=0;
	LastDitheredPrim=0;
	LastTextureAddressPrim=0;
	LastShadePrim=0;
	LastCulledPrim=0;
	LastBlendPrim=0;
	LastMonoPrim=0;
	LastFogPrim=0;
	TimeRenderers=0;
	TimeOutSideLoop=0;
	TimeMM=0;
	TimeControls=0;
	TimeDebugger=0;
	TimeScripts=0;
	TimeScriptsUpdate=0;
	TimeFont3D=0;
	TimeDisplay=0;
	TimeGameLogic=0;
	TimeNetwork=0;
	TextureUnlocked=0;
	TimeConvertTextures=0;
	TimeMipmapTextures=0;
	TimeRenderTextures=0;
	TimeUploadTextures=0;
	TimeD3D=0;
	TimeDXShow=0;
	TimeInClearViewPort=0;
	TimeToRenderNow=0;
	TimeGameOS=0;
//
// Update Memory manager statistics
//
	MM_UpdateStatistics( AllHeaps );

	#endif
//
// Bump texture manager onto next frame
//
	CTexInfo::NextFrame();	// increment frame number for texture usage monitoring
}






//
// Reset statistics at the start of a frame
//
void ResetStatistics()
{
	LOG_BLOCK("Gos::ResetStatistics");
	UpdateStatistics();

}








//
// Application visible
//
// This command adds text to the statistic display and can be used for blank lines or titles
//
void __stdcall StatisticFormat( char* String )
{
	#ifdef LAB_ONLY
	if( status!=Uninitialized && status!=GameInit )
		STOP(( "StatisticFormat only valid during init code" ));

	Stat* pLast=0;
	Stat* pStat=pStatistics;
	while( pStat )
	{
		pLast=pStat;
		pStat=pStat->pNext;
	}
//
// Check for returns
//
	for( int i=0; i<strlen(String); i++ )
		if( String[i]=='\n' )
			STOP(( "Formatting must be a single line" ));
//
// Create a new statistic block and link it into the list
//
	DWORD NameLen=strlen(String);
	
	Stat* pNew=(Stat*)gos_Malloc( sizeof(Stat)+NameLen, DefaultHeap );
	pNew->Flags=Stat_Format;
	pNew->pNext=0;
	pNew->BlockOwner=0;
	strcpy( pNew->Name, String );
//
// Either start list or make last point to this one
//
	if( pLast )
	{
		pLast->pNext=pNew;
	}
	else
	{
		pStatistics=pNew;
	}
	#endif
//
//
//
	NumberStatistics++;
}




//
// Application visible
//
// Adds a value to the list of variables being watched each frame
//
// Name      is the name of the variable ("Frame Rate")
// TypeName  is the name of the type of variable ("Hz")
// Type      is how the varibles is stored
// Value     is a pointer to the value
// Flags if true, the variable will be cleared to zero after each frame
//
// Each statistic added requires about 4K of space allocated (for graphing values)
//
void __stdcall AddStatistic( char* Name, char* TypeName, gosType Type, void* Value, DWORD Flags )
{
	#ifdef LAB_ONLY
	if( status!=Uninitialized && status!=GameInit )
		STOP(( "AddStatistic only valid during init code" ));
//
// Deal with performance monitor statistics
//
	if( Type==gos_cycledata )
	{
		if( (Flags&0x80000000)==0 )
		{
			TypeName="Cycles";
		}
		else
		{
			TypeName="Perf Counter";
		}
	}
//
// Setup default flags for gos_timedata types
//
	if( Type==gos_timedata )
	{
		*(__int64*)Value=100;
		Flags|=Stat_AutoReset+Stat_GraphMax+Stat_2DP;
		TypeName="%";
	}
//
// Check this variable is already not being spied upon (also find last in list)
//
	Stat* pLast=0;
	Stat* pStat=pStatistics;
	while( pStat )
	{
		if( Type==gos_cycledata && ((Flags&0x80000000)==0) )
		{
			if( 0==stricmp(Name, pStat->Name) )
				STOP(( "AddStatistic called more than once for same variable" ));
		}
		pLast=pStat;
		pStat=pStat->pNext;
	}
//
// Remember graphs enabled
//
	if( !UpdatedExe )
	{
		if( 0!=(ProfileFlags[AddNumber>>3]&(1<<(AddNumber&7))) )
			Flags|=Stat_Graph;
		else
			Flags&=~Stat_Graph;
		AddNumber++;
		if( AddNumber==256 )
			AddNumber=255;
	}
//
// Create a new statistic block and link it into the list
//
	DWORD NameLen=strlen(Name);
	
	Stat* pNew=(Stat*)gos_Malloc( sizeof(Stat)+NameLen+strlen(TypeName)+1, DefaultHeap );
	memset( pNew, 0, sizeof(Stat) );
	pNew->Type=Type;
	pNew->pVariable=Value;
	pNew->Flags=Flags;
	strcpy( pNew->Name, Name );
	strcpy( &pNew->Name[NameLen+1], TypeName );
	pNew->TypeName=&pNew->Name[NameLen+1];
//
// Make room for history
//
	pNew->BlockOwner=0;
	if( StatsOffset==0 )
	{
		pNew->BlockOwner=1;
		pStatHistory=(float*)gos_Malloc(512*4*StatsInBlock, DefaultHeap);
		memset( pStatHistory, 0, 512*4*StatsInBlock );
	}
	pNew->History=&pStatHistory[StatsOffset];	
	StatsOffset=(StatsOffset+1)&(StatsInBlock-1);
//
// Check for a default graph maximum being given
//
	if( Flags&Stat_GraphMax )
	{
		float Max=0;

		switch( Type )
		{
			case gos_DWORD:
			{
				Max=(float)(*(DWORD*)Value);
				*(DWORD*)Value=0;
				break;			
			}
			case gos_WORD:
			{
				Max=*(WORD*)Value;
				*(WORD*)Value=0;
				break;			
			}
			case gos_BYTE:
			{
				Max=*(BYTE*)Value;
				*(BYTE*)Value=0;
				break;			
			}
			case gos_int:
			{
				Max=(float)(*(int*)Value);
				*(int*)Value=0;
				break;			
			}
			case gos_short:
			{
				Max=*(short*)Value;
				*(short*)Value=0;
				break;			
			}
			case gos_char:
			{
				Max=*(char*)Value;
				*(char*)Value=0;
				break;			
			}
			case gos_float:
			{
				Max=*(float*)Value;
				*(float*)Value=0;
				break;			
			}
			case gos_double:
			{
				Max=(float)(*(double*)Value);
				*(double*)Value=0;
				break;			
			}
			case gos_int64:
			{
				Max=(float)(*(__int64*)Value);
				*(__int64*)Value=0;
				break;			
			}
			case gos_timedata:
			{
				Max=(float)(*(__int64*)Value);
				*(__int64*)Value=0;
				break;			
			}
			case gos_cycledata:
			{
				if( (Flags&0x80000000)==0 )
				{
					Max=(float)(((gos_CycleData*)Value)->Cycles);
					((gos_CycleData*)Value)->Cycles=0;
					((gos_CycleData*)Value)->Count=0;
				}
				else
				{
					Max=(float)(((gos_CycleData*)Value)->TotalCounter[0]);
					((gos_CycleData*)Value)->TotalCounter[0]=0;
					((gos_CycleData*)Value)->TotalCounter[1]=0;
				}
				break;			
			}
			default:
				STOP(( "Bad statistic type '%s'", pNew->Name ));
		}
		pNew->MaxValue=Max;
	}
//
// Either start list or make last point to this one
//
	if( pLast )
	{
		pLast->pNext=pNew;
	}
	else
	{
		pStatistics=pNew;
	}
	#endif
//
//
//
	NumberStatistics++;
//
// When using cycledata, they take two lines
//
	if( Type==gos_cycledata && (Flags&0x80000000)==0 )
	{
		((gos_CycleData*)Value)->InsideStartEnd=0;
		AddStatistic( Name, TypeName, Type, Value, (Flags|0x80000000)&~Stat_Graph );
	}
}







//
//
//
float __stdcall gos_ReturnStatistic( const char* Name, int Frame )
{
	#ifdef LAB_ONLY
	Stat* pStat=pStatistics;
	gosASSERT( Name && *Name && Frame>-512 && Frame<=0 );
	while( pStat )
	{
		if( 0==stricmp(Name, pStat->Name) )
		{
			float value=GetHistory(pStat,(Frame+GraphHead-1)&511);

			if( pStat->Type==gos_timedata )
			{
				value*=OneOverProcessorSpeed;
			}
			return value;
		}
		pStat=pStat->pNext;
	}
	STOP(( "Cannot find statistic \"%s\"",Name ));
	#endif
	return 0;
}













//
// Storage for save/restore statistics
//
#ifdef LAB_ONLY
	BYTE StatSaveBuffer[4096];
	BYTE* SavedData;


//
// Store all statistics during debugger
//
void SaveStatistics()
{
	BYTE* pSave=StatSaveBuffer;

	Stat* pStat=pStatistics;
	while( pStat )
	{
		if( pStat->Flags&Stat_AutoReset )
		{
			switch( pStat->Type )
			{
				case gos_DWORD:
				{
					if( pStat->Flags )
					{
						*(DWORD*)pSave=*(DWORD*)pStat->pVariable;
						pSave+=4;
					}
					break;			
				}
				case gos_WORD:
				{
					if( pStat->Flags )
					{
						*(WORD*)pSave=*(WORD*)pStat->pVariable;
						pSave+=2;
					}
					break;			
				}
				case gos_BYTE:
				{
					if( pStat->Flags )
					{
						*(BYTE*)pSave=*(BYTE*)pStat->pVariable;
						pSave+=1;
					}
					break;			
				}
				case gos_int:
				{
					if( pStat->Flags )
					{
						*(int*)pSave=*(int*)pStat->pVariable;
						pSave+=4;
					}
					break;			
				}
				case gos_short:
				{
					if( pStat->Flags )
					{
						*(short*)pSave=*(short*)pStat->pVariable;
						pSave+=2;
					}
					break;			
				}
				case gos_char:
				{
					if( pStat->Flags )
					{
						*(char*)pSave=*(char*)pStat->pVariable;
						pSave+=1;
					}
					break;			
				}
				case gos_float:
				{
					if( pStat->Flags )
					{
						*(float*)pSave=*(float*)pStat->pVariable;
						pSave+=4;
					}
					break;			
				}
				case gos_double:
				{
					if( pStat->Flags )
					{
						*(double*)pSave=*(double*)pStat->pVariable;
						pSave+=8;
					}
					break;			
				}
				case gos_int64:
				{
					if( pStat->Flags )
					{
						*(__int64*)pSave=*(__int64*)pStat->pVariable;
						pSave+=8;
					}
					break;			
				}
				case gos_timedata:
				{
					if( pStat->Flags )
					{
						*(__int64*)pSave=*(__int64*)pStat->pVariable;
						pSave+=8;
					}
					break;			
				}
				case gos_cycledata:
				{
					if( pStat->Flags )
					{
						memcpy( pSave, pStat->pVariable, sizeof(gos_CycleData) );
						pSave+=sizeof(gos_CycleData);
					}
					break;			
				}
				default:
					STOP(( "Bad statistic type '%s'", pStat->Name ));
			}
		}
		pStat=pStat->pNext;
	}

	#ifdef _ARMOR
	*(DWORD*)pSave=NumberFTOL;
	pSave+=4;
	#endif
	*(DWORD*)pSave=LastFilterPrim;
	pSave+=4;
	*(DWORD*)pSave=LastSpecularPrim;
	pSave+=4;
	*(DWORD*)pSave=LastPerspectivePrim;
	pSave+=4;
	*(DWORD*)pSave=LastAlphaPrim;
	pSave+=4;
	*(DWORD*)pSave=LastTexturePrim;
	pSave+=4;
	*(DWORD*)pSave=LastZComparePrim;
	pSave+=4;
	*(DWORD*)pSave=LastClipped;
	pSave+=4;
	*(DWORD*)pSave=LastZWritePrim;
	pSave+=4;
	*(DWORD*)pSave=LastAlphaTestPrim;
	pSave+=4;
	*(DWORD*)pSave=LastDitheredPrim;
	pSave+=4;
	*(DWORD*)pSave=LastTextureAddressPrim;
	pSave+=4;
	*(DWORD*)pSave=LastCulledPrim;
	pSave+=4;
	*(DWORD*)pSave=LastShadePrim;
	pSave+=4;
	*(DWORD*)pSave=LastBlendPrim;
	pSave+=4;
	*(DWORD*)pSave=LastMonoPrim;
	pSave+=4;
	*(DWORD*)pSave=LastFogPrim;
	pSave+=4;
	*(__int64*)pSave=TimeRenderers;
	pSave+=8;
	*(__int64*)pSave=TimeOutSideLoop;
	pSave+=8;
	*(__int64*)pSave=TimeMM;
	pSave+=8;
	*(__int64*)pSave=TimeControls;
	pSave+=8;
	*(__int64*)pSave=TimeDebugger;
	pSave+=8;
	*(__int64*)pSave=TimeScripts;
	pSave+=8;
	*(__int64*)pSave=TimeScriptsUpdate;
	pSave+=8;
	*(__int64*)pSave=TimeDisplay;
	pSave+=8;
	*(__int64*)pSave=TimeGameLogic;
	pSave+=8;
	*(__int64*)pSave=TimeNetwork;
	pSave+=8;
	*(__int64*)pSave=TimeD3D;
	pSave+=8;
	*(__int64*)pSave=TimeFont3D;
	pSave+=8;
	*(__int64*)pSave=TimeDXShow;
	pSave+=8;
	*(__int64*)pSave=TimeInMLR;
	pSave+=8;
	*(__int64*)pSave=TimeInClearViewPort;
	pSave+=8;
	*(__int64*)pSave=TimeToRenderNow;
	pSave+=8;
	*(__int64*)pSave=TimeGameOS;
	pSave+=8;
	*(__int64*)pSave=TimeUploadTextures;
	pSave+=8;
	*(__int64*)pSave=TimeRenderTextures;
	pSave+=8;
	*(__int64*)pSave=TimeConvertTextures;
	pSave+=8;
	*(__int64*)pSave=TimeMipmapTextures;
	pSave+=8;
	*(DWORD*)pSave=VerticesUploaded;
	pSave+=4;
	SavedData=pSave;
	if( pSave-StatSaveBuffer > 4096 )
		STOP(( "Increase StatSaveBuffer size" ));
}



//
// Restore statistics after debugger
//
void RestoreStatistics()
{
	BYTE* pSave=StatSaveBuffer;

	Stat* pStat=pStatistics;
	while( pStat )
	{
		if( pStat->Flags&Stat_AutoReset )
		{
			switch( pStat->Type )
			{
				case gos_DWORD:
				{
					if( pStat->Flags )
					{
						*(DWORD*)pStat->pVariable=*(DWORD*)pSave;
						pSave+=4;
					}
					break;			
				}
				case gos_WORD:
				{
					if( pStat->Flags )
					{
						*(WORD*)pStat->pVariable=*(WORD*)pSave;
						pSave+=2;
					}
					break;			
				}
				case gos_BYTE:
				{
					if( pStat->Flags )
					{
						*(BYTE*)pStat->pVariable=*(BYTE*)pSave;
						pSave+=1;
					}
					break;			
				}
				case gos_int:
				{
					if( pStat->Flags )
					{
						*(int*)pStat->pVariable=*(int*)pSave;
						pSave+=4;
					}
					break;			
				}
				case gos_short:
				{
					if( pStat->Flags )
					{
						*(short*)pStat->pVariable=*(short*)pSave;
						pSave+=2;
					}
					break;			
				}
				case gos_char:
				{
					if( pStat->Flags )
					{
						*(char*)pStat->pVariable=*(char*)pSave;
						pSave+=1;
					}
					break;			
				}
				case gos_float:
				{
					if( pStat->Flags )
					{
						*(float*)pStat->pVariable=*(float*)pSave;
						pSave+=4;
					}
					break;			
				}
				case gos_double:
				{
					if( pStat->Flags )
					{
						*(double*)pStat->pVariable=*(double*)pSave;
						pSave+=8;
					}
					break;			
				}
				case gos_int64:
				{
					if( pStat->Flags )
					{
						*(__int64*)pStat->pVariable=*(__int64*)pSave;
						pSave+=8;
					}
					break;			
				}
				case gos_timedata:
				{
					if( pStat->Flags )
					{
						*(__int64*)pStat->pVariable=*(__int64*)pSave;
						pSave+=8;
					}
					break;			
				}
				case gos_cycledata:
				{
					if( pStat->Flags )
					{
						memcpy( pStat->pVariable, pSave, sizeof(gos_CycleData) );
						pSave+=sizeof(gos_CycleData);
					}
					break;			
				}
				default:
					STOP(( "Bad statistic type '%s'", pStat->Name ));
			}
		}
		pStat=pStat->pNext;
	}

	#ifdef _ARMOR
	NumberFTOL=*(DWORD*)pSave;
	pSave+=4;
	#endif
	LastFilterPrim=*(DWORD*)pSave;
	pSave+=4;
	LastSpecularPrim=*(DWORD*)pSave;
	pSave+=4;
	LastPerspectivePrim=*(DWORD*)pSave;
	pSave+=4;
	LastAlphaPrim=*(DWORD*)pSave;
	pSave+=4;
	LastTexturePrim=*(DWORD*)pSave;
	pSave+=4;
	LastZComparePrim=*(DWORD*)pSave;
	pSave+=4;
	LastClipped=*(DWORD*)pSave;
	pSave+=4;
	LastZWritePrim=*(DWORD*)pSave;
	pSave+=4;
	LastAlphaTestPrim=*(DWORD*)pSave;
	pSave+=4;
	LastDitheredPrim=*(DWORD*)pSave;
	pSave+=4;
	LastTextureAddressPrim=*(DWORD*)pSave;
	pSave+=4;
	LastCulledPrim=*(DWORD*)pSave;
	pSave+=4;
	LastShadePrim=*(DWORD*)pSave;
	pSave+=4;
	LastBlendPrim=*(DWORD*)pSave;
	pSave+=4;
	LastMonoPrim=*(DWORD*)pSave;
	pSave+=4;
	LastFogPrim=*(DWORD*)pSave;
	pSave+=4;
	TimeRenderers=*(__int64*)pSave;
	pSave+=8;
	TimeOutSideLoop=*(__int64*)pSave;
	pSave+=8;
	TimeMM=*(__int64*)pSave;
	pSave+=8;
	TimeControls=*(__int64*)pSave;
	pSave+=8;
	TimeDebugger=*(__int64*)pSave;
	pSave+=8;
	TimeScripts=*(__int64*)pSave;
	pSave+=8;
	TimeScriptsUpdate=*(__int64*)pSave;
	pSave+=8;
	TimeDisplay=*(__int64*)pSave;
	pSave+=8;
	TimeGameLogic=*(__int64*)pSave;
	pSave+=8;
	TimeNetwork=*(__int64*)pSave;
	pSave+=8;
	TimeD3D=*(__int64*)pSave;
	pSave+=8;
	TimeFont3D=*(__int64*)pSave;
	pSave+=8;
	TimeDXShow=*(__int64*)pSave;
	pSave+=8;
	TimeInMLR=*(__int64*)pSave;
	pSave+=8;
	TimeInClearViewPort=*(__int64*)pSave;
	pSave+=8;
	TimeToRenderNow=*(__int64*)pSave;
	pSave+=8;
	TimeGameOS=*(__int64*)pSave;
	pSave+=8;
	TimeUploadTextures=*(__int64*)pSave;
	pSave+=8;
	TimeRenderTextures=*(__int64*)pSave;
	pSave+=8;
	TimeConvertTextures=*(__int64*)pSave;
	pSave+=8;
	TimeMipmapTextures=*(__int64*)pSave;
	pSave+=8;
	VerticesUploaded=*(DWORD*)pSave;
	pSave+=4;
	if( pSave!=SavedData )
		STOP(( "Save/Restore stats did not work" ));
}
#endif




