This commit is contained in:
romkazvo
2023-08-07 19:29:24 +08:00
commit 34d6c5d489
4832 changed files with 1389451 additions and 0 deletions

View File

@@ -0,0 +1,648 @@
////////////////////////////////////////////////////////////////////////////
//
// Crytek Engine Source File.
// Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
// File name: timedemorecorder.cpp
// Version: v1.00
// Created: 2/8/2003 by Timur.
// Compilers: Visual Studio.NET
// Description:
// -------------------------------------------------------------------------
// History:
//
////////////////////////////////////////////////////////////////////////////
#include "StdAfx.h"
#include "TimeDemoRecorder.h"
#include <CryFile.h>
#include "Game.h"
#if defined(WIN32) && !defined(WIN64)
//#include "Psapi.h" // PSAPI is not supported on windows9x
//#pragma comment(lib,"Psapi.lib") // PSAPI is not supported on windows9x
#endif
//////////////////////////////////////////////////////////////////////////
// Brush Export structures.
//////////////////////////////////////////////////////////////////////////
#define TIMEDEMO_FILE_SIGNATURE "CRY"
#define TIMEDEMO_FILE_TYPE 150
#define TIMEDEMO_FILE_VERSION 1
#define FIXED_TIME_STEP 0.02f // Assume runing at 50fps.
#pragma pack(push,1)
struct STimeDemoHeader
{
char signature[3]; // File signature.
int filetype; // File type.
int version; // File version.
//////////////////////////////////////////////////////////////////////////
int numFrames;
float totalTime;
char reserved[128];
};
//////////////////////////////////////////////////////////////////////////
struct STimeDemoFrame
{
Vec3 pos;
Vec3 angles;
float frametime;
unsigned int nActionFlags[2];
float fLeaning;
int nPolygonsPerFrame;
char reserved[28];
};
#pragma pack(pop)
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
CTimeDemoRecorder::CTimeDemoRecorder( ISystem *pSystem )
{
m_pSystem = pSystem;
assert(m_pSystem);
m_pGame = pSystem->GetIGame();
assert(m_pGame);
m_bRecording = false;
m_bPlaying = false;
m_currFrameIter = m_records.end();
m_recordStartTime = 0;
m_recordEndTime = 0;
m_lastFrameTime = 0;
m_totalDemoTime = 0;
m_recordedDemoTime = 0;
m_lastAveFrameRate = 0;
m_lastPlayedTotalTime = 0;
m_fixedTimeStep = 0;
m_maxLoops = 1000;
m_demo_scroll_pause = 1;
m_demo_noinfo = 1;
m_bPaused = false;
// Register demo variables.
pSystem->GetIConsole()->Register( "demo_num_runs",&m_maxLoops,1000,0,"Number of times to loop timedemo" );
pSystem->GetIConsole()->Register( "demo_scroll_pause",&m_demo_scroll_pause,1,0,"ScrollLock pauses demo play/record" );
pSystem->GetIConsole()->Register( "demo_noinfo",&m_demo_noinfo,0,0,"Disable info display during demo playback" );
pSystem->GetIConsole()->Register( "demo_quit",&m_demo_quit,0,0,"Quit game after demo runs finished" );
pSystem->GetIConsole()->Register( "demo_screenshot_frame",&m_demo_screenshot_frame,0,0,"Make screenshot on specified frame during demo playback" );
}
//////////////////////////////////////////////////////////////////////////
CTimeDemoRecorder::~CTimeDemoRecorder()
{
}
//////////////////////////////////////////////////////////////////////////
void CTimeDemoRecorder::Record( bool bEnable )
{
if (bEnable == m_bRecording)
return;
m_bRecording = bEnable;
m_bPlaying = false;
if (m_bRecording)
{
// Start recording.
m_records.clear();
m_recordStartTime = GetTime();
m_lastFrameTime = m_recordStartTime;
StartSession();
}
else
{
// Stop recording.
m_recordedDemoTime = m_totalDemoTime;
m_lastFrameTime = GetTime();
StopSession();
}
m_currFrameIter = m_records.end();
m_currentFrame = 0;
m_totalDemoTime = 0;
}
//////////////////////////////////////////////////////////////////////////
void CTimeDemoRecorder::Play( bool bEnable )
{
if (bEnable == m_bPlaying)
return;
if (m_records.empty())
return;
m_bRecording = false;
m_bPlaying = bEnable;
if (m_bPlaying)
{
LogInfo( "==============================================================" );
LogInfo( "TimeDemo Play Started , (Total Frames: %d, Recorded Time: %.2fs)",(int)m_records.size(),m_recordedDemoTime );
// Start demo playback.
m_currFrameIter = m_records.begin();
m_lastPlayedTotalTime = 0;
StartSession();
}
else
{
LogInfo( "TimeDemo Play Ended, (%d Runs Performed)",m_numLoops );
LogInfo( "==============================================================" );
// End demo playback.
m_currFrameIter = m_records.end();
m_lastPlayedTotalTime = m_totalDemoTime;
StopSession();
}
m_currentFrame = 0;
m_totalDemoTime = 0;
m_numLoops = 0;
m_fpsCounter = 0;
m_lastFpsTimeRecorded = GetTime();
m_currFPS = 0;
m_minFPS = 10000;
m_maxFPS = -10000;
m_nMaxPolys = INT_MIN;
m_nMinPolys = INT_MAX;
}
//////////////////////////////////////////////////////////////////////////
void CTimeDemoRecorder::Save( const char *filename )
{
CCryFile file;
if (!file.Open( filename,"wb" ))
{
m_pSystem->Warning( VALIDATOR_MODULE_GAME,VALIDATOR_WARNING,0,filename,"Cannot open time demo file %s",filename );
return;
}
m_file = filename;
// Save Time demo file.
STimeDemoHeader hdr;
memset( &hdr,0,sizeof(hdr) );
memcpy( hdr.signature,TIMEDEMO_FILE_SIGNATURE,3 );
hdr.filetype = TIMEDEMO_FILE_TYPE;
hdr.version = TIMEDEMO_FILE_VERSION;
hdr.numFrames = m_records.size();
hdr.totalTime = m_recordedDemoTime;
file.Write( &hdr,sizeof(hdr) );
for (FrameRecords::iterator it = m_records.begin(); it != m_records.end(); ++it)
{
FrameRecord &rec = *it;
STimeDemoFrame frame;
frame.angles = rec.playerAngles;
frame.pos = rec.playerPos;
frame.frametime = rec.frameTime;
*frame.nActionFlags = *rec.nActionFlags;
frame.fLeaning = rec.fLeaning;
frame.nPolygonsPerFrame = rec.nPolygons;
file.Write( &frame,sizeof(frame) );
}
/*
XmlNodeRef root = m_pSystem->CreateXmlNode( "TimeDemo" );
root->setAttr( "TotalTime",m_recordedDemoTime );
for (FrameRecords::iterator it = m_records.begin(); it != m_records.end(); ++it)
{
FrameRecord &rec = *it;
XmlNodeRef xmlRecord = root->newChild( "Frame" );
xmlRecord->setAttr( "Pos",rec.playerPos );
xmlRecord->setAttr( "Ang",rec.playerAngles );
xmlRecord->setAttr( "Time",rec.frameTime );
}
root->saveToFile( filename );
*/
}
//////////////////////////////////////////////////////////////////////////
void CTimeDemoRecorder::Load( const char *filename )
{
m_records.clear();
m_recordedDemoTime = 0;
m_totalDemoTime = 0;
CCryFile file;
if (!file.Open( filename,"rb" ))
{
m_pSystem->Warning( VALIDATOR_MODULE_GAME,VALIDATOR_WARNING,0,filename,"Cannot open time demo file %s",filename );
return;
}
m_file = filename;
// Load Time demo file.
STimeDemoHeader hdr;
file.Read( &hdr,sizeof(hdr) );
m_recordedDemoTime = hdr.totalTime;
m_totalDemoTime = m_recordedDemoTime;
for (int i = 0; i < hdr.numFrames && !file.IsEof(); i++)
{
STimeDemoFrame frame;
FrameRecord rec;
file.Read( &frame,sizeof(frame) );
rec.playerAngles = frame.angles;
rec.playerPos = frame.pos;
rec.frameTime = frame.frametime;
*rec.nActionFlags = *frame.nActionFlags;
rec.fLeaning = frame.fLeaning;
rec.nPolygons = frame.nPolygonsPerFrame;
m_records.push_back( rec );
}
/*
XmlNodeRef root = m_pSystem->LoadXmlFile( filename );
if (!root)
{
// No such demo file.
m_pSystem->Warning( VALIDATOR_MODULE_GAME,VALIDATOR_WARNING,0,filename,"Cannot open time demo file %s",filename );
return;
}
root->getAttr( "TotalTime",m_recordedDemoTime );
m_totalDemoTime = m_recordedDemoTime;
for (int i = 0; i < root->getChildCount(); i++)
{
FrameRecord rec;
XmlNodeRef xmlRecord = root->getChild(i);
xmlRecord->getAttr( "Pos",rec.playerPos );
xmlRecord->getAttr( "Ang",rec.playerAngles );
xmlRecord->getAttr( "Time",rec.frameTime );
m_records.push_back( rec );
}
*/
}
//////////////////////////////////////////////////////////////////////////
void CTimeDemoRecorder::Update()
{
if (m_bRecording)
{
RecordFrame();
}
else if (m_bPlaying)
{
PlayFrame();
}
}
//////////////////////////////////////////////////////////////////////////
void CTimeDemoRecorder::RecordFrame()
{
float time = GetTime();
float frameTime = time - m_lastFrameTime;
if (m_bPaused)
{
m_lastFrameTime = time;
return;
}
FrameRecord rec;
rec.frameTime = frameTime;
IEntity *pPlayer = NULL;
pPlayer = GetIXGame( m_pGame )->GetMyPlayer();
if (pPlayer)
{
rec.playerPos = pPlayer->GetPos();
rec.playerAngles = pPlayer->GetAngles();
}
// Record current processing command.
CXEntityProcessingCmd &cmd = ((CXGame*)m_pGame)->m_pClient->m_PlayerProcessingCmd;
*rec.nActionFlags = *cmd.m_nActionFlags;
rec.fLeaning = cmd.GetLeaning();
m_totalDemoTime += rec.frameTime;
int nPolygons,nShadowVolPolys;
m_pSystem->GetIRenderer()->GetPolyCount(nPolygons,nShadowVolPolys);
rec.nPolygons = nPolygons;
m_records.push_back( rec );
m_currentFrame++;
m_lastFrameTime = GetTime();
}
//////////////////////////////////////////////////////////////////////////
void CTimeDemoRecorder::PlayFrame()
{
if (m_currFrameIter == m_records.end()) // can't playback empty records.
return;
float time = GetTime();
float frameTime = time - m_lastFrameTime;
if (m_bPaused)
{
m_lastFrameTime = time;
return;
}
FrameRecord &rec = *m_currFrameIter;
IEntity *pPlayer = NULL;
pPlayer = GetIXGame( m_pGame )->GetMyPlayer();
if (pPlayer)
{
pPlayer->SetPos( rec.playerPos );
pPlayer->SetAngles( rec.playerAngles );
GetIXGame( m_pGame )->SetViewAngles( rec.playerAngles );
}
// Overwrite current processing command.
CXEntityProcessingCmd &cmd = ((CXGame*)m_pGame)->m_pClient->m_PlayerProcessingCmd;
*cmd.m_nActionFlags = *rec.nActionFlags;
cmd.SetLeaning( rec.fLeaning );
m_totalDemoTime += frameTime;
int nPolygons,nShadowVolPolys;
m_pSystem->GetIRenderer()->GetPolyCount(nPolygons,nShadowVolPolys);
m_nPolysCounter += nPolygons;
m_nCurrPolys = nPolygons;
if (nPolygons > m_nMaxPolys)
m_nMaxPolys = nPolygons;
if (nPolygons < m_nMinPolys)
m_nMinPolys = nPolygons;
m_nTotalPolysRecorded += rec.nPolygons;
m_nTotalPolysPlayed += nPolygons;
//////////////////////////////////////////////////////////////////////////
// Calculate Frame Rates.
//////////////////////////////////////////////////////////////////////////
// Skip some frames before calculating frame rates.
if (time - m_lastFpsTimeRecorded > 1)
{
// Skip some frames before recording frame rates.
if (m_currentFrame > 60)
{
m_currFPS = (float)m_fpsCounter / (time - m_lastFpsTimeRecorded);
if (m_currFPS > m_maxFPS)
{
m_maxFPS_Frame = m_currentFrame;
m_maxFPS = m_currFPS;
}
if (m_currFPS < m_minFPS)
{
m_minFPS_Frame = m_currentFrame;
m_minFPS = m_currFPS;
}
}
m_nPolysPerSec = (int)(m_nPolysCounter / (time - m_lastFpsTimeRecorded));
m_nPolysCounter = 0;
m_fpsCounter = 0;
m_lastFpsTimeRecorded = time;
}
else
{
m_fpsCounter++;
}
//////////////////////////////////////////////////////////////////////////
m_currFrameIter++;
m_currentFrame++;
if(m_demo_screenshot_frame && m_currentFrame == m_demo_screenshot_frame)
{
/* ICVar * p_demo_file = pSystem->GetIConsole()->GetCVar("demo_file");
char szFileName[256]="";
sprinyf("demo_%s_%d.jpg",p_demo_file->GetString(),m_currentFrame);*/
m_pSystem->GetIRenderer()->ScreenShot();
}
// Play looped.
if (m_currFrameIter == m_records.end())
{
m_lastPlayedTotalTime = m_totalDemoTime;
m_lastAveFrameRate = GetAverageFrameRate();
// Log info to file.
LogRun();
m_totalDemoTime = 0;
m_currFrameIter = m_records.begin();
m_currentFrame = 0;
m_numLoops++;
m_nTotalPolysPlayed = 0;
m_nTotalPolysRecorded = 0;
// Stop playing if max runs reached.
if (m_numLoops > m_maxLoops)
{
Play(false);
if (m_demo_quit)
{
// Immidiate game abort after num loops done.
exit(0);
}
}
}
m_lastFrameTime = GetTime();
}
//////////////////////////////////////////////////////////////////////////
float CTimeDemoRecorder::GetTime()
{
// Must be asynchronius time, used for profiling.
return m_pSystem->GetITimer()->GetAsyncCurTime();
}
//////////////////////////////////////////////////////////////////////////
int CTimeDemoRecorder::GetNumFrames() const
{
return m_records.size();
}
//////////////////////////////////////////////////////////////////////////
float CTimeDemoRecorder::GetAverageFrameRate() const
{
float aveFrameTime = m_totalDemoTime / m_currentFrame;
float aveFrameRate = 1.0f / aveFrameTime;
return aveFrameRate;
}
//////////////////////////////////////////////////////////////////////////
void CTimeDemoRecorder::RenderInfo()
{
if (m_demo_noinfo != 0)
return;
const char *sInfo = "";
m_bPaused = false;
if (m_bRecording || m_bPlaying)
{
if (m_demo_scroll_pause != 0 && GetISystem()->GetIInput())
{
bool bPaused = (GetISystem()->GetIInput()->GetKeyState(XKEY_SCROLLLOCK) & 1);
if (bPaused)
{
m_bPaused = true;
sInfo = " (Paused)";
}
}
}
IRenderer *pRenderer = m_pSystem->GetIRenderer();
if (m_bRecording)
{
float fColor[4] = {1,0,0,1};
pRenderer->Draw2dLabel( 1,1, 1.3f, fColor,false,"Recording TimeDemo%s, Frames: %d",sInfo,m_currentFrame );
}
else if (m_bPlaying)
{
float aveFrameRate = GetAverageFrameRate();
int numFrames = GetNumFrames();
float fColor[4] = {0,1,0,1};
pRenderer->Draw2dLabel( 1,1, 1.3f, fColor,false,"Playing TimeDemo%s, Frame %d of %d (Looped: %d)",sInfo,m_currentFrame,numFrames,m_numLoops );
pRenderer->Draw2dLabel( 1,1+15, 1.3f, fColor,false,"Last Played Length: %.2fs, FPS: %.2f",m_lastPlayedTotalTime,m_lastAveFrameRate );
pRenderer->Draw2dLabel( 1,1+30, 1.3f, fColor,false,"Average FPS: %.2f, FPS: %.2f, Polys/Frame: %d",aveFrameRate,m_currFPS,m_nCurrPolys );
pRenderer->Draw2dLabel( 1,1+45, 1.3f, fColor,false,"Polys Rec/Play Ratio: %.2f",(float)m_nTotalPolysRecorded/m_nTotalPolysPlayed );
}
}
//////////////////////////////////////////////////////////////////////////
void CTimeDemoRecorder::SetConsoleVar( const char *sVarName,float value )
{
ICVar *pVar = m_pSystem->GetIConsole()->GetCVar( sVarName );
if (pVar)
pVar->Set( value );
}
//////////////////////////////////////////////////////////////////////////
float CTimeDemoRecorder::GetConsoleVar( const char *sVarName )
{
ICVar *pVar = m_pSystem->GetIConsole()->GetCVar( sVarName );
if (pVar)
return pVar->GetFVal();
return 0;
}
//////////////////////////////////////////////////////////////////////////
void CTimeDemoRecorder::StartSession()
{
m_nTotalPolysRecorded = 0;
m_nTotalPolysPlayed = 0;
m_lastAveFrameRate = 0;
m_lastPlayedTotalTime = 0;
// Register to frame profiler.
m_pSystem->GetIProfileSystem()->AddPeaksListener( this );
// Remember old time step, and set constant one.
m_fixedTimeStep = GetConsoleVar( "fixed_time_step" );
SetConsoleVar( "fixed_time_step",FIXED_TIME_STEP );
SetConsoleVar( "ai_ignoreplayer",1 );
SetConsoleVar( "ai_soundperception",0 );
SetConsoleVar( "mov_NoCutscenes",1 );
// Profile
m_oldPeakTolerance = GetConsoleVar( "profile_peak" );
SetConsoleVar( "profile_peak",50 );
m_lastFrameTime = GetTime();
}
//////////////////////////////////////////////////////////////////////////
void CTimeDemoRecorder::StopSession()
{
// Set old time step.
SetConsoleVar( "fixed_time_step",m_fixedTimeStep );
SetConsoleVar( "ai_ignoreplayer",0 );
SetConsoleVar( "ai_soundperception",1 );
SetConsoleVar( "mov_NoCutscenes",0 );
// Profile.
SetConsoleVar( "profile_peak",m_oldPeakTolerance );
m_pSystem->GetIProfileSystem()->RemovePeaksListener( this );
}
//////////////////////////////////////////////////////////////////////////
void CTimeDemoRecorder::LogInfo( const char *format,... )
{
if (m_demo_noinfo != 0)
return;
va_list ArgList;
char szBuffer[1024];
va_start(ArgList, format);
vsprintf(szBuffer, format, ArgList);
va_end(ArgList);
char path_buffer[_MAX_PATH];
char drive[_MAX_DRIVE];
char dir[_MAX_DIR];
char fname[_MAX_FNAME];
char ext[_MAX_EXT];
m_pSystem->GetILog()->Log( szBuffer );
_splitpath( m_file.c_str(), drive, dir, fname, ext );
_makepath( path_buffer, drive, dir,fname,"log" );
FILE *hFile = fopen( path_buffer,"at" );
if (hFile)
{
// Write the string to the file and close it
fprintf(hFile, "%s\n",szBuffer );
fclose(hFile);
}
}
//////////////////////////////////////////////////////////////////////////
void CTimeDemoRecorder::OnFrameProfilerPeak( CFrameProfiler *pProfiler,float fPeakTime )
{
if (m_bPlaying && !m_bPaused)
{
LogInfo( " -Peak at Frame %d, %.2fms : %s (count: %d)",m_currentFrame,fPeakTime,pProfiler->m_name,pProfiler->m_count );
}
}
void CTimeDemoRecorder::LogRun()
{
int numFrames = m_records.size();
LogInfo( "!TimeDemo Run %d Finished.",m_numLoops );
LogInfo( " Play Time: %.2fs, Average FPS: %.2f",m_lastPlayedTotalTime,m_lastAveFrameRate );
LogInfo( " Min FPS: %.2f at frame %d, Max FPS: %.2f at frame %d",m_minFPS,m_minFPS_Frame,m_maxFPS,m_maxFPS_Frame );
LogInfo( " Average Tri/Sec: %d, Tri/Frame: %d",(int)(m_nTotalPolysPlayed/m_lastPlayedTotalTime),m_nTotalPolysPlayed/numFrames );
LogInfo( " Recorded/Played Tris ratio: %.2f",(float)m_nTotalPolysRecorded/m_nTotalPolysPlayed );
#if defined(WIN32) && !defined(WIN64)
// PSAPI is not supported on window9x
// so, don't use it
//PROCESS_MEMORY_COUNTERS pc;
//HANDLE hProcess = GetCurrentProcess();
//pc.cb = sizeof(pc);
//GetProcessMemoryInfo( hProcess,&pc,sizeof(pc) );
//int MB = 1024*1024;
//LogInfo( " Memory Usage: WorkingSet=%dMb, PageFile=%dMb, PageFaults=%d",pc.WorkingSetSize/MB,pc.PagefileUsage/MB,pc.PageFaultCount );
#endif
}