mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 10:08:18 +00:00
Merge pull request #7824 from Robyt3/Client-FPS-Graph-Timebase
Add time scale to debug graphs for constant scrolling speed
This commit is contained in:
commit
0bce368c77
|
@ -75,7 +75,10 @@ static const ColorRGBA gs_ClientNetworkPrintColor{0.7f, 1, 0.7f, 1.0f};
|
|||
static const ColorRGBA gs_ClientNetworkErrPrintColor{1.0f, 0.25f, 0.25f, 1.0f};
|
||||
|
||||
CClient::CClient() :
|
||||
m_DemoPlayer(&m_SnapshotDelta, true, [&]() { UpdateDemoIntraTimers(); })
|
||||
m_DemoPlayer(&m_SnapshotDelta, true, [&]() { UpdateDemoIntraTimers(); }),
|
||||
m_InputtimeMarginGraph(128),
|
||||
m_GametimeMarginGraph(128),
|
||||
m_FpsGraph(4096)
|
||||
{
|
||||
m_StateStartTime = time_get();
|
||||
for(auto &DemoRecorder : m_aDemoRecorder)
|
||||
|
@ -821,11 +824,11 @@ void CClient::DebugRender()
|
|||
float sp = Graphics()->ScreenWidth() / 100.0f;
|
||||
float x = Graphics()->ScreenWidth() - w - sp;
|
||||
|
||||
m_FpsGraph.Scale();
|
||||
m_FpsGraph.Scale(time_freq());
|
||||
m_FpsGraph.Render(Graphics(), TextRender(), x, sp * 5, w, h, "FPS");
|
||||
m_InputtimeMarginGraph.Scale();
|
||||
m_InputtimeMarginGraph.Scale(5 * time_freq());
|
||||
m_InputtimeMarginGraph.Render(Graphics(), TextRender(), x, sp * 6 + h, w, h, "Prediction Margin");
|
||||
m_GametimeMarginGraph.Scale();
|
||||
m_GametimeMarginGraph.Scale(5 * time_freq());
|
||||
m_GametimeMarginGraph.Render(Graphics(), TextRender(), x, sp * 7 + h * 2, w, h, "Gametime Margin");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,13 +6,15 @@
|
|||
|
||||
#include "graph.h"
|
||||
|
||||
CGraph::CGraph(int MaxEntries) :
|
||||
m_Entries(MaxEntries * (sizeof(SEntry) + 2 * CRingBufferBase::ITEM_SIZE), CRingBufferBase::FLAG_RECYCLE)
|
||||
{
|
||||
}
|
||||
|
||||
void CGraph::Init(float Min, float Max)
|
||||
{
|
||||
SetMin(Min);
|
||||
SetMax(Max);
|
||||
m_Index = 0;
|
||||
for(auto &Entry : m_aEntries)
|
||||
Entry.m_Initialized = false;
|
||||
}
|
||||
|
||||
void CGraph::SetMin(float Min)
|
||||
|
@ -25,34 +27,103 @@ void CGraph::SetMax(float Max)
|
|||
m_MaxRange = m_Max = Max;
|
||||
}
|
||||
|
||||
void CGraph::Scale()
|
||||
void CGraph::Scale(int64_t WantedTotalTime)
|
||||
{
|
||||
// Scale X axis for wanted total time
|
||||
if(m_Entries.First() != nullptr)
|
||||
{
|
||||
const int64_t EndTime = m_Entries.Last()->m_Time;
|
||||
bool ScaleTotalTime = false;
|
||||
m_pFirstScaled = nullptr;
|
||||
|
||||
if(m_Entries.First()->m_Time >= EndTime - WantedTotalTime)
|
||||
{
|
||||
m_pFirstScaled = m_Entries.First();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pFirstScaled = m_Entries.Last();
|
||||
while(m_pFirstScaled)
|
||||
{
|
||||
SEntry *pPrev = m_Entries.Prev(m_pFirstScaled);
|
||||
if(pPrev == nullptr)
|
||||
break;
|
||||
if(pPrev->m_Time < EndTime - WantedTotalTime)
|
||||
{
|
||||
// Scale based on actual total time instead of based on wanted total time,
|
||||
// to avoid flickering last segment due to rounding errors.
|
||||
ScaleTotalTime = true;
|
||||
break;
|
||||
}
|
||||
m_pFirstScaled = pPrev;
|
||||
}
|
||||
}
|
||||
|
||||
m_RenderedTotalTime = ScaleTotalTime ? (EndTime - m_pFirstScaled->m_Time) : WantedTotalTime;
|
||||
|
||||
// Ensure that color is applied to first line segment
|
||||
if(m_pFirstScaled)
|
||||
{
|
||||
m_pFirstScaled->m_ApplyColor = true;
|
||||
SEntry *pNext = m_Entries.Next(m_pFirstScaled);
|
||||
if(pNext != nullptr)
|
||||
{
|
||||
pNext->m_ApplyColor = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pFirstScaled = nullptr;
|
||||
m_RenderedTotalTime = 0;
|
||||
}
|
||||
|
||||
// Scale Y axis
|
||||
m_Min = m_MinRange;
|
||||
m_Max = m_MaxRange;
|
||||
for(auto &Entry : m_aEntries)
|
||||
for(SEntry *pEntry = m_pFirstScaled; pEntry != nullptr; pEntry = m_Entries.Next(pEntry))
|
||||
{
|
||||
if(Entry.m_Value > m_Max)
|
||||
m_Max = Entry.m_Value;
|
||||
else if(Entry.m_Value < m_Min)
|
||||
m_Min = Entry.m_Value;
|
||||
if(pEntry->m_Value > m_Max)
|
||||
m_Max = pEntry->m_Value;
|
||||
else if(pEntry->m_Value < m_Min)
|
||||
m_Min = pEntry->m_Value;
|
||||
}
|
||||
}
|
||||
|
||||
void CGraph::Add(float Value, ColorRGBA Color)
|
||||
{
|
||||
InsertAt(m_Index, Value, Color);
|
||||
m_Index = (m_Index + 1) % MAX_VALUES;
|
||||
InsertAt(time_get(), Value, Color);
|
||||
}
|
||||
|
||||
void CGraph::InsertAt(size_t Index, float Value, ColorRGBA Color)
|
||||
void CGraph::InsertAt(int64_t Time, float Value, ColorRGBA Color)
|
||||
{
|
||||
dbg_assert(Index < MAX_VALUES, "Index out of bounds");
|
||||
m_aEntries[Index].m_Initialized = true;
|
||||
m_aEntries[Index].m_Value = Value;
|
||||
m_aEntries[Index].m_Color = Color;
|
||||
SEntry *pEntry = m_Entries.Allocate(sizeof(SEntry));
|
||||
pEntry->m_Time = Time;
|
||||
pEntry->m_Value = Value;
|
||||
pEntry->m_Color = Color;
|
||||
|
||||
// Determine whether the line (pPrev, pEntry) has different
|
||||
// vertex colors than the line (pPrevPrev, pPrev).
|
||||
SEntry *pPrev = m_Entries.Prev(pEntry);
|
||||
if(pPrev == nullptr)
|
||||
{
|
||||
pEntry->m_ApplyColor = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
SEntry *pPrevPrev = m_Entries.Prev(pPrev);
|
||||
if(pPrevPrev == nullptr)
|
||||
{
|
||||
pEntry->m_ApplyColor = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
pEntry->m_ApplyColor = Color != pPrev->m_Color || pPrev->m_Color != pPrevPrev->m_Color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGraph::Render(IGraphics *pGraphics, ITextRender *pTextRender, float x, float y, float w, float h, const char *pDescription) const
|
||||
void CGraph::Render(IGraphics *pGraphics, ITextRender *pTextRender, float x, float y, float w, float h, const char *pDescription)
|
||||
{
|
||||
pGraphics->TextureClear();
|
||||
|
||||
|
@ -66,28 +137,61 @@ void CGraph::Render(IGraphics *pGraphics, ITextRender *pTextRender, float x, flo
|
|||
pGraphics->SetColor(0.95f, 0.95f, 0.95f, 1.0f);
|
||||
IGraphics::CLineItem LineItem(x, y + h / 2, x + w, y + h / 2);
|
||||
pGraphics->LinesDraw(&LineItem, 1);
|
||||
|
||||
pGraphics->SetColor(0.5f, 0.5f, 0.5f, 0.75f);
|
||||
IGraphics::CLineItem aLineItems[2] = {
|
||||
IGraphics::CLineItem(x, y + (h * 3) / 4, x + w, y + (h * 3) / 4),
|
||||
IGraphics::CLineItem(x, y + h / 4, x + w, y + h / 4)};
|
||||
pGraphics->LinesDraw(aLineItems, std::size(aLineItems));
|
||||
for(int i = 1; i < MAX_VALUES; i++)
|
||||
{
|
||||
const auto &Entry0 = m_aEntries[(m_Index + i - 1) % MAX_VALUES];
|
||||
const auto &Entry1 = m_aEntries[(m_Index + i) % MAX_VALUES];
|
||||
if(!Entry0.m_Initialized || !Entry1.m_Initialized)
|
||||
continue;
|
||||
float a0 = (i - 1) / (float)(MAX_VALUES - 1);
|
||||
float a1 = i / (float)(MAX_VALUES - 1);
|
||||
float v0 = (Entry0.m_Value - m_Min) / (m_Max - m_Min);
|
||||
float v1 = (Entry1.m_Value - m_Min) / (m_Max - m_Min);
|
||||
|
||||
IGraphics::CColorVertex aColorVertices[2] = {
|
||||
IGraphics::CColorVertex(0, Entry0.m_Color.r, Entry0.m_Color.g, Entry0.m_Color.b, Entry0.m_Color.a),
|
||||
IGraphics::CColorVertex(1, Entry1.m_Color.r, Entry1.m_Color.g, Entry1.m_Color.b, Entry1.m_Color.a)};
|
||||
pGraphics->SetColorVertex(aColorVertices, std::size(aColorVertices));
|
||||
IGraphics::CLineItem LineItem2(x + a0 * w, y + h - v0 * h, x + a1 * w, y + h - v1 * h);
|
||||
pGraphics->LinesDraw(&LineItem2, 1);
|
||||
if(m_pFirstScaled != nullptr)
|
||||
{
|
||||
IGraphics::CLineItem aValueLineItems[128];
|
||||
size_t NumValueLineItems = 0;
|
||||
|
||||
const int64_t StartTime = m_pFirstScaled->m_Time;
|
||||
|
||||
SEntry *pEntry0 = m_pFirstScaled;
|
||||
int a0 = round_to_int((pEntry0->m_Time - StartTime) * w / m_RenderedTotalTime);
|
||||
int v0 = round_to_int((pEntry0->m_Value - m_Min) * h / (m_Max - m_Min));
|
||||
while(pEntry0 != nullptr)
|
||||
{
|
||||
SEntry *pEntry1 = m_Entries.Next(pEntry0);
|
||||
if(pEntry1 == nullptr)
|
||||
break;
|
||||
|
||||
const int a1 = round_to_int((pEntry1->m_Time - StartTime) * w / m_RenderedTotalTime);
|
||||
const int v1 = round_to_int((pEntry1->m_Value - m_Min) * h / (m_Max - m_Min));
|
||||
|
||||
if(pEntry1->m_ApplyColor)
|
||||
{
|
||||
if(NumValueLineItems)
|
||||
{
|
||||
pGraphics->LinesDraw(aValueLineItems, NumValueLineItems);
|
||||
NumValueLineItems = 0;
|
||||
}
|
||||
|
||||
IGraphics::CColorVertex aColorVertices[2] = {
|
||||
IGraphics::CColorVertex(0, pEntry0->m_Color.r, pEntry0->m_Color.g, pEntry0->m_Color.b, pEntry0->m_Color.a),
|
||||
IGraphics::CColorVertex(1, pEntry1->m_Color.r, pEntry1->m_Color.g, pEntry1->m_Color.b, pEntry1->m_Color.a)};
|
||||
pGraphics->SetColorVertex(aColorVertices, std::size(aColorVertices));
|
||||
}
|
||||
if(NumValueLineItems == std::size(aValueLineItems))
|
||||
{
|
||||
pGraphics->LinesDraw(aValueLineItems, NumValueLineItems);
|
||||
NumValueLineItems = 0;
|
||||
}
|
||||
aValueLineItems[NumValueLineItems] = IGraphics::CLineItem(x + a0, y + h - v0, x + a1, y + h - v1);
|
||||
++NumValueLineItems;
|
||||
|
||||
pEntry0 = pEntry1;
|
||||
a0 = a1;
|
||||
v0 = v1;
|
||||
}
|
||||
if(NumValueLineItems)
|
||||
{
|
||||
pGraphics->LinesDraw(aValueLineItems, NumValueLineItems);
|
||||
}
|
||||
}
|
||||
pGraphics->LinesEnd();
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#include <base/color.h>
|
||||
|
||||
#include <engine/shared/ringbuffer.h>
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
class IGraphics;
|
||||
|
@ -13,33 +15,31 @@ class ITextRender;
|
|||
|
||||
class CGraph
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
MAX_VALUES = 128,
|
||||
};
|
||||
|
||||
private:
|
||||
struct SEntry
|
||||
{
|
||||
bool m_Initialized;
|
||||
int64_t m_Time;
|
||||
float m_Value;
|
||||
ColorRGBA m_Color;
|
||||
bool m_ApplyColor;
|
||||
};
|
||||
SEntry *m_pFirstScaled = nullptr;
|
||||
int64_t m_RenderedTotalTime = 0;
|
||||
float m_Min, m_Max;
|
||||
float m_MinRange, m_MaxRange;
|
||||
SEntry m_aEntries[MAX_VALUES];
|
||||
size_t m_Index;
|
||||
CDynamicRingBuffer<SEntry> m_Entries;
|
||||
|
||||
public:
|
||||
CGraph(int MaxEntries);
|
||||
|
||||
void Init(float Min, float Max);
|
||||
void SetMin(float Min);
|
||||
void SetMax(float Max);
|
||||
|
||||
void Scale();
|
||||
void Scale(int64_t WantedTotalTime);
|
||||
void Add(float Value, ColorRGBA Color = ColorRGBA(1.0f, 1.0f, 1.0f, 0.75f));
|
||||
void InsertAt(size_t Index, float Value, ColorRGBA Color = ColorRGBA(1.0f, 1.0f, 1.0f, 0.75f));
|
||||
void Render(IGraphics *pGraphics, ITextRender *pTextRender, float x, float y, float w, float h, const char *pDescription) const;
|
||||
void InsertAt(int64_t Time, float Value, ColorRGBA Color = ColorRGBA(1.0f, 1.0f, 1.0f, 0.75f));
|
||||
void Render(IGraphics *pGraphics, ITextRender *pTextRender, float x, float y, float w, float h, const char *pDescription);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
|
||||
/* If you are missing that file, acquire a complete release at teeworlds.com. */
|
||||
#include <base/system.h>
|
||||
|
||||
#include "ringbuffer.h"
|
||||
|
||||
CRingBufferBase::CItem *CRingBufferBase::NextBlock(CItem *pItem)
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#ifndef ENGINE_SHARED_RINGBUFFER_H
|
||||
#define ENGINE_SHARED_RINGBUFFER_H
|
||||
|
||||
#include <base/system.h>
|
||||
|
||||
class CRingBufferBase
|
||||
{
|
||||
class CItem
|
||||
|
@ -44,18 +46,13 @@ public:
|
|||
// Will start to destroy items to try to fit the next one
|
||||
FLAG_RECYCLE = 1
|
||||
};
|
||||
static constexpr int ITEM_SIZE = sizeof(CItem);
|
||||
};
|
||||
|
||||
template<typename T, int TSIZE, int TFLAGS = 0>
|
||||
class CStaticRingBuffer : public CRingBufferBase
|
||||
template<typename T>
|
||||
class CTypedRingBuffer : public CRingBufferBase
|
||||
{
|
||||
unsigned char m_aBuffer[TSIZE];
|
||||
|
||||
public:
|
||||
CStaticRingBuffer() { Init(); }
|
||||
|
||||
void Init() { CRingBufferBase::Init(m_aBuffer, TSIZE, TFLAGS); }
|
||||
|
||||
T *Allocate(int Size) { return (T *)CRingBufferBase::Allocate(Size); }
|
||||
int PopFirst() { return CRingBufferBase::PopFirst(); }
|
||||
|
||||
|
@ -65,4 +62,36 @@ public:
|
|||
T *Last() { return (T *)CRingBufferBase::Last(); }
|
||||
};
|
||||
|
||||
template<typename T, int TSIZE, int TFLAGS = 0>
|
||||
class CStaticRingBuffer : public CTypedRingBuffer<T>
|
||||
{
|
||||
unsigned char m_aBuffer[TSIZE];
|
||||
|
||||
public:
|
||||
CStaticRingBuffer() { Init(); }
|
||||
|
||||
void Init() { CRingBufferBase::Init(m_aBuffer, TSIZE, TFLAGS); }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class CDynamicRingBuffer : public CTypedRingBuffer<T>
|
||||
{
|
||||
unsigned char *m_pBuffer = nullptr;
|
||||
|
||||
public:
|
||||
CDynamicRingBuffer(int Size, int Flags = 0) { Init(Size, Flags); }
|
||||
|
||||
virtual ~CDynamicRingBuffer()
|
||||
{
|
||||
free(m_pBuffer);
|
||||
}
|
||||
|
||||
void Init(int Size, int Flags)
|
||||
{
|
||||
free(m_pBuffer);
|
||||
m_pBuffer = static_cast<unsigned char *>(malloc(Size));
|
||||
CRingBufferBase::Init(m_pBuffer, Size, Flags);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -12,6 +12,14 @@
|
|||
|
||||
#include "debughud.h"
|
||||
|
||||
static constexpr int64_t GRAPH_MAX_VALUES = 128;
|
||||
|
||||
CDebugHud::CDebugHud() :
|
||||
m_RampGraph(GRAPH_MAX_VALUES),
|
||||
m_ZoomedInGraph(GRAPH_MAX_VALUES)
|
||||
{
|
||||
}
|
||||
|
||||
void CDebugHud::RenderNetCorrections()
|
||||
{
|
||||
if(!g_Config.m_Debug || g_Config.m_DbgGraphs || !m_pClient->m_Snap.m_pLocalCharacter || !m_pClient->m_Snap.m_pLocalPrevCharacter)
|
||||
|
@ -179,7 +187,7 @@ void CDebugHud::RenderTuning()
|
|||
m_RampGraph.Init(0.0f, 0.0f);
|
||||
m_SpeedTurningPoint = 0;
|
||||
float PreviousRampedSpeed = 1.0f;
|
||||
for(size_t i = 0; i < CGraph::MAX_VALUES; i++)
|
||||
for(int64_t i = 0; i < GRAPH_MAX_VALUES; i++)
|
||||
{
|
||||
// This is a calculation of the speed values per second on the X axis, from 270 to 34560 in steps of 270
|
||||
const float Speed = (i + 1) * StepSizeRampGraph;
|
||||
|
@ -196,12 +204,12 @@ void CDebugHud::RenderTuning()
|
|||
}
|
||||
PreviousRampedSpeed = RampedSpeed;
|
||||
}
|
||||
m_RampGraph.Scale();
|
||||
m_RampGraph.Scale(GRAPH_MAX_VALUES - 1);
|
||||
|
||||
m_ZoomedInGraph.Init(0.0f, 0.0f);
|
||||
PreviousRampedSpeed = 1.0f;
|
||||
MiddleOfZoomedInGraph = m_SpeedTurningPoint;
|
||||
for(size_t i = 0; i < CGraph::MAX_VALUES; i++)
|
||||
for(int64_t i = 0; i < GRAPH_MAX_VALUES; i++)
|
||||
{
|
||||
// This is a calculation of the speed values per second on the X axis, from (MiddleOfZoomedInGraph - 64 * StepSize) to (MiddleOfZoomedInGraph + 64 * StepSize)
|
||||
const float Speed = MiddleOfZoomedInGraph - 64 * StepSizeZoomedInGraph + i * StepSizeZoomedInGraph;
|
||||
|
@ -222,7 +230,7 @@ void CDebugHud::RenderTuning()
|
|||
}
|
||||
PreviousRampedSpeed = RampedSpeed;
|
||||
}
|
||||
m_ZoomedInGraph.Scale();
|
||||
m_ZoomedInGraph.Scale(GRAPH_MAX_VALUES - 1);
|
||||
}
|
||||
|
||||
const float GraphFontSize = 12.0f;
|
||||
|
|
|
@ -21,6 +21,7 @@ class CDebugHud : public CComponent
|
|||
float m_OldVelrampCurvature;
|
||||
|
||||
public:
|
||||
CDebugHud();
|
||||
virtual int Sizeof() const override { return sizeof(*this); }
|
||||
virtual void OnRender() override;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue