2019-01-24 00:50:42 -05:00
|
|
|
/*
|
|
|
|
This is code is heavily based on the Lua scripts written by upsilandre
|
|
|
|
The original scripts can be found on his blog here: http://upsilandre.over-blog.com/2018/07/compte-rendu-framerate-nes.html
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include "PerformanceTracker.h"
|
|
|
|
#include "Console.h"
|
|
|
|
#include "PPU.h"
|
|
|
|
#include "DebugHud.h"
|
2019-01-24 17:31:29 -05:00
|
|
|
#include "IKeyManager.h"
|
|
|
|
#include "KeyManager.h"
|
2019-01-24 00:50:42 -05:00
|
|
|
|
|
|
|
enum Colors
|
|
|
|
{
|
|
|
|
Red = 0x00FF0000,
|
|
|
|
Yellow = 0x00FFD800,
|
|
|
|
OpaqueWhite = 0x00FFFFFF,
|
|
|
|
OpaqueBlack = 0x00000000,
|
|
|
|
Black = 0x40000000,
|
|
|
|
White = 0x40FFFFFF,
|
|
|
|
Blue = 0xA00000FF,
|
|
|
|
Transparent = 0xFF000000,
|
|
|
|
};
|
|
|
|
|
|
|
|
PerformanceTracker::PerformanceTracker(shared_ptr<Console> console)
|
|
|
|
{
|
|
|
|
_console = console;
|
|
|
|
_data = PerfTrackerData();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PerformanceTracker::Initialize(int32_t address, AddressType type, PerfTrackerMode mode)
|
|
|
|
{
|
|
|
|
_address = address;
|
|
|
|
_type = type;
|
|
|
|
_mode = mode;
|
|
|
|
_needReset = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
PerfTrackerMode PerformanceTracker::GetMode()
|
|
|
|
{
|
|
|
|
return _mode;
|
|
|
|
}
|
|
|
|
|
2019-01-24 17:31:29 -05:00
|
|
|
void PerformanceTracker::ProcessMouseInput()
|
|
|
|
{
|
|
|
|
bool leftButtonPressed = KeyManager::IsMouseButtonPressed(MouseButton::LeftButton);
|
|
|
|
bool rightButtonPressed = KeyManager::IsMouseButtonPressed(MouseButton::RightButton);
|
|
|
|
if(_leftButtonPressed && leftButtonPressed != _leftButtonPressed) {
|
|
|
|
//Left button was clicked
|
|
|
|
_mode = (PerfTrackerMode)((_mode + 1) % 4);
|
|
|
|
if(_mode == PerfTrackerMode::Disabled) {
|
|
|
|
_mode = PerfTrackerMode::Fullscreen;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(_rightButtonPressed && rightButtonPressed != _rightButtonPressed) {
|
|
|
|
//Right button was clicked
|
|
|
|
_updateSpeed = _updateSpeed == PerfTrackerSpeed::Fast ? PerfTrackerSpeed::Normal : PerfTrackerSpeed::Fast;
|
|
|
|
}
|
|
|
|
|
|
|
|
_leftButtonPressed = leftButtonPressed;
|
|
|
|
_rightButtonPressed = rightButtonPressed;
|
|
|
|
}
|
|
|
|
|
2019-01-24 00:50:42 -05:00
|
|
|
void PerformanceTracker::ProcessEndOfFrame()
|
|
|
|
{
|
|
|
|
if(_mode == PerfTrackerMode::Disabled) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-01-24 17:31:29 -05:00
|
|
|
ProcessMouseInput();
|
|
|
|
|
2019-01-24 00:50:42 -05:00
|
|
|
_data.frameCount++;
|
|
|
|
_data.frameProcessed = false;
|
|
|
|
|
|
|
|
shared_ptr<DebugHud> hud = _console->GetDebugHud();
|
|
|
|
int frame = _console->GetPpu()->GetFrameCount();
|
|
|
|
|
|
|
|
if(_mode == PerfTrackerMode::TextOnly) {
|
|
|
|
hud->DrawRectangle(2, 2, 33, 22, Colors::Black, true, 1, frame);
|
|
|
|
hud->DrawRectangle(2, 2, 33, 22, Colors::OpaqueWhite, false, 1, frame);
|
|
|
|
hud->DrawString(4, 4, std::to_string(_data.fps) + "FPS", Colors::OpaqueWhite, Colors::Transparent, 1, frame);
|
|
|
|
hud->DrawString(4, 14, std::to_string(_data.updateCpu) + "%", Colors::OpaqueWhite, Colors::Transparent, 1, frame);
|
|
|
|
} else if(_mode == PerfTrackerMode::Fullscreen || _mode == PerfTrackerMode::Compact) {
|
|
|
|
hud->DrawRectangle(0, 223, 256, 10, Colors::Black, true, 1, frame);
|
|
|
|
hud->DrawLine(0, 222, 255, 222, Colors::White, 1, frame);
|
|
|
|
hud->DrawLine(0, 233, 255, 233, Colors::White, 1, frame);
|
|
|
|
hud->DrawLine(40, 223, 40, 232, Colors::White, 1, frame);
|
|
|
|
hud->DrawLine(100, 223, 100, 232, Colors::White, 1, frame);
|
|
|
|
hud->DrawLine(170, 223, 170, 232, Colors::White, 1, frame);
|
|
|
|
hud->DrawString(6, 224, std::to_string(_data.fps) + "FPS", Colors::OpaqueWhite, Colors::Transparent, 1, frame);
|
|
|
|
|
|
|
|
string cpuUsage = std::to_string(_data.updateCpu);
|
|
|
|
cpuUsage = std::string(3 - cpuUsage.size(), ' ') + cpuUsage;
|
|
|
|
hud->DrawString(46, 224, "CPU: " + cpuUsage + "%", Colors::OpaqueWhite, Colors::Transparent, 1, frame);
|
|
|
|
|
|
|
|
string avgCpuUsage = std::to_string(_data.totalCpu / _data.cpuEntry);
|
|
|
|
avgCpuUsage = std::string(3 - avgCpuUsage.size(), ' ') + avgCpuUsage;
|
|
|
|
hud->DrawString(105, 224, "Avg FPS: " + std::to_string((_data.gameFrame * 60) / _data.totalFrame), Colors::OpaqueWhite, Colors::Transparent, 1, frame);
|
|
|
|
hud->DrawString(176, 224, "Avg CPU: " + avgCpuUsage + "%", Colors::OpaqueWhite, Colors::Transparent, 1, frame);
|
|
|
|
|
|
|
|
int scale = _mode == PerfTrackerMode::Fullscreen ? 200 : 60;
|
|
|
|
|
|
|
|
hud->DrawRectangle(0, 2, 256, scale + 1, (uint32_t)Colors::Blue, true, 1, frame);
|
|
|
|
hud->DrawLine(0, 2, 255, 2, Colors::White, 1, frame);
|
|
|
|
hud->DrawLine(0, (scale / 4) + 2, 255, (scale / 4) + 2, Colors::White, 1, frame);
|
|
|
|
hud->DrawLine(0, (scale / 2) + 2, 255, (scale / 2) + 2, Colors::White, 1, frame);
|
|
|
|
hud->DrawLine(0, (scale * 3 / 4) + 2, 255, (scale * 3 / 4) + 2, Colors::White, 1, frame);
|
|
|
|
hud->DrawLine(0, scale + 2, 255, scale + 2, Colors::White, 1, frame);
|
|
|
|
hud->DrawString(1, 4, "200%/60FPS", Colors::White, Colors::Transparent, 1, frame);
|
|
|
|
hud->DrawString(0, scale / 2 + 4, "100%/30FPS", Colors::White, Colors::Transparent, 1, frame);
|
|
|
|
|
|
|
|
DrawChart(_data.cpuChartDataPoints, _data.cpuChartPos, Colors::Red, scale, 200);
|
|
|
|
DrawChart(_data.fpsChartDataPoints, _data.fpsChartPos, Colors::Yellow, scale, 60);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PerformanceTracker::DrawChart(int *dataPoints, int startPos, int color, int scale, int maxValue)
|
|
|
|
{
|
|
|
|
shared_ptr<DebugHud> hud = _console->GetDebugHud();
|
|
|
|
int frame = _console->GetPpu()->GetFrameCount();
|
|
|
|
|
|
|
|
int dotX = 0;
|
|
|
|
int i = startPos;
|
|
|
|
do {
|
|
|
|
int dotY1 = dataPoints[i] * scale / maxValue;
|
|
|
|
int dotY2 = dataPoints[(i + 1) % 256] * scale / maxValue;
|
|
|
|
hud->DrawLine(dotX, scale + 2 - dotY1, dotX + 1, scale + 2 - dotY2, color, 1, frame);
|
|
|
|
dotX++;
|
|
|
|
i = (i + 1) % 256;
|
|
|
|
} while(i != startPos);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PerformanceTracker::ProcessCpuExec(AddressTypeInfo & addressInfo)
|
|
|
|
{
|
|
|
|
if(_mode == PerfTrackerMode::Disabled || addressInfo.Address != _address || addressInfo.Type != _type) {
|
|
|
|
//Performance tracker isn't running, or address doesn't match, ignore it
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(_needReset) {
|
|
|
|
//Options have been changed, reset everything
|
|
|
|
_data = PerfTrackerData();
|
|
|
|
_needReset = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(_data.frameProcessed) {
|
|
|
|
//We already hit this instruction once during this frame, don't count it again
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_data.frameProcessed = true;
|
|
|
|
|
|
|
|
//Count the number of frames since the last time we processed this instruction
|
|
|
|
int lag = _data.frameCount - _data.prevFrameCount - 1;
|
|
|
|
_data.prevFrameCount = _data.frameCount;
|
|
|
|
|
|
|
|
if(lag >= 0 && lag < 3 && _data.frameCount > 3) {
|
|
|
|
//Check what percentage of the current frame has been used (since the last NMI)
|
|
|
|
int scanline = _console->GetPpu()->GetCurrentScanline();
|
|
|
|
PPUDebugState state;
|
|
|
|
_console->GetPpu()->GetState(state);
|
|
|
|
int scanlineCount = state.ScanlineCount;
|
|
|
|
_data.partialCpu = ((scanline + 20) % scanlineCount) * 100 / scanlineCount;
|
|
|
|
if(scanline >= 241) {
|
|
|
|
_data.partialCpu = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Store the current frame's CPU usage as a data point for the chart (each lag frame counts as +100% CPU)
|
|
|
|
int finalCpu = _data.partialCpu + (lag * 100);
|
|
|
|
|
|
|
|
//Update the average CPU statistic
|
|
|
|
_data.totalCpu += finalCpu;
|
|
|
|
_data.cpuEntry++;
|
|
|
|
|
|
|
|
//Process the last X frames since NMI, and check which ones are lag frames
|
|
|
|
for(int i = 0; i <= lag; i++) {
|
|
|
|
_data.totalFrame++;
|
|
|
|
_data.isLagFramePos = (_data.isLagFramePos + 1) % 60;
|
|
|
|
_data.isLagFrame[_data.isLagFramePos] = true;
|
|
|
|
}
|
|
|
|
_data.isLagFrame[_data.isLagFramePos] = false;
|
|
|
|
|
|
|
|
_data.gameFrame++;
|
|
|
|
|
2019-01-24 17:31:29 -05:00
|
|
|
_data.updateTimer++;
|
|
|
|
if(_updateSpeed == PerfTrackerSpeed::Fast || _data.updateTimer == 8) {
|
|
|
|
_data.updateTimer = 0;
|
|
|
|
_data.cpuChartDataPoints[_data.cpuChartPos] = finalCpu;
|
|
|
|
_data.cpuChartPos = (_data.cpuChartPos + 1) % 256;
|
|
|
|
|
|
|
|
//Update the onscreen display for the CPU %
|
|
|
|
_data.updateCpu = finalCpu;
|
|
|
|
|
|
|
|
//Calculate the average FPS for the last 60 frames (and add it as a data point for the chart)
|
|
|
|
_data.fps = 0;
|
|
|
|
int i = _data.isLagFramePos;
|
|
|
|
do {
|
|
|
|
_data.fps += _data.isLagFrame[i] ? 0 : 1;
|
|
|
|
i = (i + 1) % 60;
|
|
|
|
} while(i != _data.isLagFramePos);
|
|
|
|
|
|
|
|
_data.fpsChartDataPoints[_data.fpsChartPos] = _data.fps;
|
|
|
|
_data.fpsChartPos = (_data.fpsChartPos + 1) % 256;
|
|
|
|
}
|
2019-01-24 00:50:42 -05:00
|
|
|
}
|
|
|
|
}
|