mirror of
https://github.com/goodtft/LCD-show.git
synced 2025-01-19 18:52:16 +01:00
293 lines
12 KiB
C++
293 lines
12 KiB
C++
#include "config.h"
|
|
|
|
#include "statistics.h"
|
|
|
|
#ifdef STATISTICS
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <memory.h>
|
|
#include <pthread.h>
|
|
#include <syslog.h>
|
|
|
|
#include "tick.h"
|
|
#include "text.h"
|
|
#include "spi.h"
|
|
#include "util.h"
|
|
#include "mailbox.h"
|
|
#include "mem_alloc.h"
|
|
#include "dma.h"
|
|
|
|
volatile uint64_t timeWastedPollingGPU = 0;
|
|
volatile float statsSpiBusSpeed = 0;
|
|
volatile int statsBcmCoreSpeed = 0;
|
|
volatile int statsCpuFrequency = 0;
|
|
volatile double statsCpuTemperature = 0;
|
|
double spiThreadUtilizationRate;
|
|
double spiBusDataRate;
|
|
int statsGpuPollingWasted = 0;
|
|
uint64_t statsBytesTransferred = 0;
|
|
|
|
int frameSkipTimeHistorySize = 0;
|
|
uint64_t frameSkipTimeHistory[FRAME_HISTORY_MAX_SIZE] = {};
|
|
|
|
#ifdef FRAME_COMPLETION_TIME_STATISTICS
|
|
|
|
#define FRAME_COMPLETION_HISTORY_MAX_SIZE 480
|
|
uint64_t frameCompletionTimeHistory[FRAME_COMPLETION_HISTORY_MAX_SIZE] = {};
|
|
int frameCompletionTimeHistorySize = 0;
|
|
|
|
int statsFrameIntervalsY[FRAME_COMPLETION_HISTORY_MAX_SIZE] = {};
|
|
int statsFrameIntervalsSize = 0;
|
|
int statsTargetFrameRateY = 0;
|
|
int statsAvgFrameRateIntervalY = 0;
|
|
|
|
void AddFrameCompletionTimeMarker()
|
|
{
|
|
for(int i = frameCompletionTimeHistorySize; i >= 1; --i)
|
|
frameCompletionTimeHistory[i] = frameCompletionTimeHistory[i-1];
|
|
frameCompletionTimeHistory[0] = tick();
|
|
if (frameCompletionTimeHistorySize+1 < FRAME_COMPLETION_HISTORY_MAX_SIZE)
|
|
++frameCompletionTimeHistorySize;
|
|
}
|
|
#else
|
|
void AddFrameCompletionTimeMarker() {}
|
|
#endif
|
|
|
|
char dmaChannelsText[32] = {};
|
|
char fpsText[32] = {};
|
|
char spiUsagePercentageText[32] = {};
|
|
char spiBusDataRateText[32] = {};
|
|
uint16_t spiUsageColor = 0, fpsColor = 0;
|
|
char statsFrameSkipText[32] = {};
|
|
char spiSpeedText[32] = {};
|
|
char spiSpeedText2[32] = {};
|
|
char cpuTemperatureText[32] = {};
|
|
uint16_t cpuTemperatureColor = 0;
|
|
char gpuPollingWastedText[32] = {};
|
|
uint16_t gpuPollingWastedColor = 0;
|
|
|
|
char cpuMemoryUsedText[32] = {};
|
|
char gpuMemoryUsedText[32] = {};
|
|
|
|
uint64_t statsLastPrint = 0;
|
|
|
|
void UpdateStatisticsNumbers()
|
|
{
|
|
// BCM core and SPI bus speed
|
|
int freq = (int)MailboxRet2(0x00030002/*Get Clock Rate*/, 0x4/*CORE*/);
|
|
statsBcmCoreSpeed = freq/1000000;
|
|
statsSpiBusSpeed = (float)freq/(1000000*spi->clk);
|
|
|
|
// CPU temperature
|
|
statsCpuTemperature = MailboxRet2(0x00030006/*Get Temperature*/, 0)/1000.0;
|
|
|
|
// Raspberry pi main CPU speed
|
|
statsCpuFrequency = (int)MailboxRet2(0x00030002/*Get Clock Rate*/, 0x3/*ARM*/) / 1000000;
|
|
}
|
|
|
|
void DrawStatisticsOverlay(uint16_t *framebuffer)
|
|
{
|
|
DrawText(framebuffer, gpuFrameWidth, gpuFramebufferScanlineStrideBytes, gpuFrameHeight, fpsText, 1, 1, fpsColor, 0);
|
|
DrawText(framebuffer, gpuFrameWidth, gpuFramebufferScanlineStrideBytes, gpuFrameHeight, statsFrameSkipText, strlen(fpsText)*6, 1, RGB565(31,0,0), 0);
|
|
|
|
#if DISPLAY_DRAWABLE_WIDTH > 130
|
|
#ifdef USE_DMA_TRANSFERS
|
|
DrawText(framebuffer, gpuFrameWidth, gpuFramebufferScanlineStrideBytes, gpuFrameHeight, dmaChannelsText, 1, 10, RGB565(31, 44, 8), 0);
|
|
#endif
|
|
#ifdef USE_SPI_THREAD
|
|
DrawText(framebuffer, gpuFrameWidth, gpuFramebufferScanlineStrideBytes, gpuFrameHeight, spiUsagePercentageText, 75, 10, spiUsageColor, 0);
|
|
#endif
|
|
DrawText(framebuffer, gpuFrameWidth, gpuFramebufferScanlineStrideBytes, gpuFrameHeight, spiBusDataRateText, 60, 1, 0xFFFF, 0);
|
|
#endif
|
|
|
|
#if DISPLAY_DRAWABLE_WIDTH > 180
|
|
DrawText(framebuffer, gpuFrameWidth, gpuFramebufferScanlineStrideBytes, gpuFrameHeight, spiSpeedText, 120, 1, RGB565(31,14,20), 0);
|
|
DrawText(framebuffer, gpuFrameWidth, gpuFramebufferScanlineStrideBytes, gpuFrameHeight, spiSpeedText2, 120, 10, RGB565(10,24,31), 0);
|
|
DrawText(framebuffer, gpuFrameWidth, gpuFramebufferScanlineStrideBytes, gpuFrameHeight, cpuTemperatureText, 190, 1, cpuTemperatureColor, 0);
|
|
DrawText(framebuffer, gpuFrameWidth, gpuFramebufferScanlineStrideBytes, gpuFrameHeight, gpuPollingWastedText, 222, 1, gpuPollingWastedColor, 0);
|
|
#endif
|
|
|
|
#if (defined(DISPLAY_FLIP_ORIENTATION_IN_SOFTWARE) && DISPLAY_DRAWABLE_HEIGHT >= 290) || (!defined(DISPLAY_FLIP_ORIENTATION_IN_SOFTWARE) && DISPLAY_DRAWABLE_WIDTH >= 290)
|
|
DrawText(framebuffer, gpuFrameWidth, gpuFramebufferScanlineStrideBytes, gpuFrameHeight, cpuMemoryUsedText, 250, 1, RGB565(31,50,21), 0);
|
|
DrawText(framebuffer, gpuFrameWidth, gpuFramebufferScanlineStrideBytes, gpuFrameHeight, gpuMemoryUsedText, 250, 10, RGB565(31,50,31), 0);
|
|
#endif
|
|
|
|
#ifdef FRAME_COMPLETION_TIME_STATISTICS
|
|
|
|
#ifdef DISPLAY_FLIP_ORIENTATION_IN_SOFTWARE
|
|
#define FRAMERATE_GRAPH_WIDTH gpuFrameHeight
|
|
#define FRAMERATE_GRAPH_MIN_Y 20
|
|
#define FRAMERATE_GRAPH_MAX_Y (gpuFrameWidth - 10)
|
|
#define AT(x,y) ((x)*(gpuFramebufferScanlineStrideBytes>>1)+(y))
|
|
#else
|
|
#define FRAMERATE_GRAPH_WIDTH gpuFrameWidth
|
|
#define FRAMERATE_GRAPH_MIN_Y 20
|
|
#define FRAMERATE_GRAPH_MAX_Y (gpuFrameHeight - 10)
|
|
#define AT(x,y) ((y)*(gpuFramebufferScanlineStrideBytes>>1)+(x))
|
|
#endif
|
|
for(int i = 0; i < MIN(statsFrameIntervalsSize, FRAMERATE_GRAPH_WIDTH); ++i)
|
|
{
|
|
int x = FRAMERATE_GRAPH_WIDTH-1-i;
|
|
int y = statsFrameIntervalsY[i];
|
|
framebuffer[AT(x, FRAMERATE_GRAPH_MIN_Y)] = RGB565(31,0,0);
|
|
framebuffer[AT(x, FRAMERATE_GRAPH_MIN_Y+1)] = RGB565(0,0,0);
|
|
framebuffer[AT(x, statsTargetFrameRateY-1)] = RGB565(0,0,0);
|
|
framebuffer[AT(x, statsTargetFrameRateY)] = RGB565(0,63,0);
|
|
framebuffer[AT(x, statsTargetFrameRateY+1)] = RGB565(0,0,0);
|
|
framebuffer[AT(x, statsAvgFrameRateIntervalY-1)] = RGB565(0,0,0);
|
|
framebuffer[AT(x, statsAvgFrameRateIntervalY)] = RGB565(29,50,7);
|
|
framebuffer[AT(x, statsAvgFrameRateIntervalY+1)] = RGB565(0,0,0);
|
|
framebuffer[AT(x, y-3)] = RGB565(0,0,0);
|
|
framebuffer[AT(x, y-2)] = RGB565(0,0,0);
|
|
framebuffer[AT(x, y-1)] = RGB565(5,11,5);
|
|
framebuffer[AT(x, y)] = RGB565(31,63,31);
|
|
framebuffer[AT(x, y+1)] = RGB565(5,11,5);
|
|
framebuffer[AT(x, y+2)] = RGB565(0,0,0);
|
|
framebuffer[AT(x, y+3)] = RGB565(0,0,0);
|
|
framebuffer[AT(x, FRAMERATE_GRAPH_MAX_Y-1)] = RGB565(0,0,0);
|
|
framebuffer[AT(x, FRAMERATE_GRAPH_MAX_Y)] = RGB565(15,30,15);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void RefreshStatisticsOverlayText()
|
|
{
|
|
uint64_t now = tick();
|
|
uint64_t elapsed = now - statsLastPrint;
|
|
if (elapsed < STATISTICS_REFRESH_INTERVAL) return;
|
|
|
|
#ifdef FRAME_COMPLETION_TIME_STATISTICS
|
|
if (frameCompletionTimeHistorySize > 1)
|
|
{
|
|
uint64_t maxInterval = 4000000 / TARGET_FRAME_RATE;
|
|
uint64_t accumIntervals = 0;
|
|
for(int i = 0; i < frameCompletionTimeHistorySize-1; ++i)
|
|
{
|
|
uint64_t interval = MIN(frameCompletionTimeHistory[i] - frameCompletionTimeHistory[i+1], maxInterval);
|
|
accumIntervals += interval;
|
|
statsFrameIntervalsY[i] = FRAMERATE_GRAPH_MAX_Y - (FRAMERATE_GRAPH_MAX_Y - FRAMERATE_GRAPH_MIN_Y) * interval / maxInterval;
|
|
}
|
|
statsTargetFrameRateY = FRAMERATE_GRAPH_MAX_Y - (FRAMERATE_GRAPH_MAX_Y - FRAMERATE_GRAPH_MIN_Y) * (1000000/TARGET_FRAME_RATE) / maxInterval;
|
|
statsAvgFrameRateIntervalY = FRAMERATE_GRAPH_MAX_Y - (FRAMERATE_GRAPH_MAX_Y - FRAMERATE_GRAPH_MIN_Y) * (accumIntervals / (frameCompletionTimeHistorySize-1)) / maxInterval;
|
|
statsFrameIntervalsSize = frameCompletionTimeHistorySize-1;
|
|
}
|
|
else
|
|
statsFrameIntervalsSize = 0;
|
|
#endif
|
|
|
|
UpdateStatisticsNumbers();
|
|
|
|
#ifdef USE_DMA_TRANSFERS
|
|
sprintf(dmaChannelsText, "DMATx=%d,Rx=%d", dmaTxChannel, dmaRxChannel);
|
|
#endif
|
|
#ifdef KERNEL_MODULE_CLIENT
|
|
spiThreadUtilizationRate = 0; // TODO
|
|
int spiRate = 0;
|
|
strcpy(spiUsagePercentageText, "N/A");
|
|
#else
|
|
uint64_t spiThreadIdleFor = __atomic_load_n(&spiThreadIdleUsecs, __ATOMIC_RELAXED);
|
|
__sync_fetch_and_sub(&spiThreadIdleUsecs, spiThreadIdleFor);
|
|
if (__atomic_load_n(&spiThreadSleeping, __ATOMIC_RELAXED)) spiThreadIdleFor += tick() - spiThreadSleepStartTime;
|
|
spiThreadUtilizationRate = MIN(1.0, MAX(0.0, 1.0 - spiThreadIdleFor / (double)STATISTICS_REFRESH_INTERVAL));
|
|
int spiRate = (int)MIN(100, (spiThreadUtilizationRate*100.0));
|
|
sprintf(spiUsagePercentageText, "%d%%", spiRate);
|
|
#endif
|
|
spiBusDataRate = (double)8.0 * statsBytesTransferred * 1000.0 / (elapsed / 1000.0);
|
|
|
|
if (spiRate < 90) spiUsageColor = RGB565(0,63,0);
|
|
else if (spiRate < 100) spiUsageColor = RGB565(31,63,0);
|
|
else spiUsageColor = RGB565(31,0, 0);
|
|
|
|
if (spiBusDataRate > 1000000) sprintf(spiBusDataRateText, "%.2fmbps", spiBusDataRate/1000000.0);
|
|
else if (spiBusDataRate > 1000) sprintf(spiBusDataRateText, "%.2fkbps", spiBusDataRate/1000.0);
|
|
else sprintf(spiBusDataRateText, "%.2fbps", spiBusDataRate);
|
|
|
|
uint64_t wastedTime = __atomic_load_n(&timeWastedPollingGPU, __ATOMIC_RELAXED);
|
|
__atomic_fetch_sub(&timeWastedPollingGPU, wastedTime, __ATOMIC_RELAXED);
|
|
//const double gpuPollingWastedScalingFactor = 0.369; // A crude heuristic to scale time spent in useless polling to what Linux 'top' tool shows as % usage percentages
|
|
statsGpuPollingWasted = (int)(wastedTime /** gpuPollingWastedScalingFactor*/ * 100 / (now - statsLastPrint));
|
|
|
|
statsBytesTransferred = 0;
|
|
|
|
if (statsBcmCoreSpeed > 0 && statsCpuFrequency > 0) sprintf(spiSpeedText, "%d/%dMHz", statsCpuFrequency, statsBcmCoreSpeed);
|
|
else spiSpeedText[0] = '\0';
|
|
|
|
if (statsSpiBusSpeed > 0) sprintf(spiSpeedText2, "SPI:%.3fMHz (/%d)", statsSpiBusSpeed, spi->clk);
|
|
else spiSpeedText2[0] = '\0';
|
|
|
|
if (statsCpuTemperature > 0)
|
|
{
|
|
sprintf(cpuTemperatureText, "%.1fc", statsCpuTemperature);
|
|
if (statsCpuTemperature >= 80) cpuTemperatureColor = RGB565(31, 0, 0);
|
|
else if (statsCpuTemperature >= 65) cpuTemperatureColor = RGB565(31, 63, 0);
|
|
else cpuTemperatureColor = RGB565(0, 63, 0);
|
|
}
|
|
|
|
if (statsGpuPollingWasted > 0)
|
|
{
|
|
gpuPollingWastedColor = (statsGpuPollingWasted > 5) ? RGB565(31, 0, 0) : RGB565(31, 63, 0);
|
|
sprintf(gpuPollingWastedText, "+%d%%", statsGpuPollingWasted);
|
|
}
|
|
else gpuPollingWastedText[0] = '\0';
|
|
|
|
statsLastPrint = now;
|
|
|
|
if (frameTimeHistorySize >= 3)
|
|
{
|
|
int numInterlacedFramesInHistory = 0;
|
|
int numProgressiveFramesInHistory = 0;
|
|
for(int i = 0; i < frameTimeHistorySize; ++i)
|
|
if (frameTimeHistory[i].interlaced)
|
|
++numInterlacedFramesInHistory;
|
|
else
|
|
++numProgressiveFramesInHistory;
|
|
|
|
int frames = frameTimeHistorySize;
|
|
if (numInterlacedFramesInHistory)
|
|
frames += numProgressiveFramesInHistory; // Progressive frames count twice as interlaced
|
|
int fps = (0.5 + (frames - 1) * 1000000.0 / (frameTimeHistory[frameTimeHistorySize-1].time - frameTimeHistory[0].time));
|
|
#ifdef NO_INTERLACING
|
|
sprintf(fpsText, "%d", fps);
|
|
fpsColor = 0xFFFF;
|
|
#else
|
|
if (numInterlacedFramesInHistory > 0)
|
|
{
|
|
if (numProgressiveFramesInHistory > 0) sprintf(fpsText, "%di/%d", fps, numProgressiveFramesInHistory);
|
|
else sprintf(fpsText, "%di", fps);
|
|
fpsColor = RGB565(31, 30, 11);
|
|
}
|
|
else
|
|
{
|
|
sprintf(fpsText, "%dp", fps);
|
|
fpsColor = 0xFFFF;
|
|
}
|
|
#endif
|
|
if (frameSkipTimeHistorySize > 0) sprintf(statsFrameSkipText, "-%d", frameSkipTimeHistorySize);
|
|
else statsFrameSkipText[0] = '\0';
|
|
}
|
|
else
|
|
{
|
|
strcpy(fpsText, "-");
|
|
statsFrameSkipText[0] = '\0';
|
|
fpsColor = 0xFFFF;
|
|
}
|
|
|
|
#if (defined(DISPLAY_FLIP_ORIENTATION_IN_SOFTWARE) && DISPLAY_DRAWABLE_HEIGHT > 302) || (!defined(DISPLAY_FLIP_ORIENTATION_IN_SOFTWARE) && DISPLAY_DRAWABLE_WIDTH > 302)
|
|
#define HINTSUFFIX "MB"
|
|
#else
|
|
#define HINTSUFFIX ""
|
|
#endif
|
|
|
|
sprintf(cpuMemoryUsedText, "CPU:%.2f" HINTSUFFIX, totalCpuMemoryAllocated/1024.0/1024.0);
|
|
|
|
#ifdef USE_DMA_TRANSFERS
|
|
if (totalGpuMemoryUsed > 0)
|
|
sprintf(gpuMemoryUsedText, "GPU:%.2f" HINTSUFFIX, totalGpuMemoryUsed/1024.0/1024.0);
|
|
#endif
|
|
}
|
|
#else
|
|
void RefreshStatisticsOverlayText() {}
|
|
void DrawStatisticsOverlay(uint16_t *) {}
|
|
#endif // ~STATISTICS
|