mirror of
https://github.com/EduApps-CDG/OpenDX
synced 2024-12-30 09:45:37 +01:00
[hud] Added frametime graph
Enable with DXVK_HUD=frametimes.
This commit is contained in:
parent
a4f9e5f0d5
commit
388fe02158
@ -67,6 +67,7 @@ Manipulation of Direct3D libraries in multi-player games may be considered cheat
|
|||||||
The `DXVK_HUD` environment variable controls a HUD which can display the framerate and some stat counters. It accepts a comma-separated list of the following options:
|
The `DXVK_HUD` environment variable controls a HUD which can display the framerate and some stat counters. It accepts a comma-separated list of the following options:
|
||||||
- `devinfo`: Displays the name of the GPU and the driver version.
|
- `devinfo`: Displays the name of the GPU and the driver version.
|
||||||
- `fps`: Shows the current frame rate.
|
- `fps`: Shows the current frame rate.
|
||||||
|
- `frametimes`: Shows a frame time graph.
|
||||||
- `submissions`: Shows the number of command buffers submitted per frame.
|
- `submissions`: Shows the number of command buffers submitted per frame.
|
||||||
- `drawcalls`: Shows the number of draw calls and render passes per frame.
|
- `drawcalls`: Shows the number of draw calls and render passes per frame.
|
||||||
- `pipelines`: Shows the total number of graphics and compute pipelines.
|
- `pipelines`: Shows the total number of graphics and compute pipelines.
|
||||||
|
@ -13,6 +13,7 @@ namespace dxvk::hud {
|
|||||||
m_renderer (m_device, m_context),
|
m_renderer (m_device, m_context),
|
||||||
m_uniformBuffer (createUniformBuffer()),
|
m_uniformBuffer (createUniformBuffer()),
|
||||||
m_hudDeviceInfo (device),
|
m_hudDeviceInfo (device),
|
||||||
|
m_hudFramerate (config.elements),
|
||||||
m_hudStats (config.elements) {
|
m_hudStats (config.elements) {
|
||||||
this->setupConstantState();
|
this->setupConstantState();
|
||||||
}
|
}
|
||||||
@ -31,7 +32,7 @@ namespace dxvk::hud {
|
|||||||
this->setupFramebuffer(size);
|
this->setupFramebuffer(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_hudFps.update();
|
m_hudFramerate.update();
|
||||||
m_hudStats.update(m_device);
|
m_hudStats.update(m_device);
|
||||||
|
|
||||||
this->beginRenderPass(recreateFbo);
|
this->beginRenderPass(recreateFbo);
|
||||||
@ -74,13 +75,8 @@ namespace dxvk::hud {
|
|||||||
m_context, m_renderer, position);
|
m_context, m_renderer, position);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_config.elements.test(HudElement::Framerate)) {
|
position = m_hudFramerate.render(m_context, m_renderer, position);
|
||||||
position = m_hudFps.render(
|
position = m_hudStats .render(m_context, m_renderer, position);
|
||||||
m_context, m_renderer, position);
|
|
||||||
}
|
|
||||||
|
|
||||||
position = m_hudStats.render(
|
|
||||||
m_context, m_renderer, position);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ namespace dxvk::hud {
|
|||||||
Rc<DxvkFramebuffer> m_renderTargetFbo;
|
Rc<DxvkFramebuffer> m_renderTargetFbo;
|
||||||
|
|
||||||
HudDeviceInfo m_hudDeviceInfo;
|
HudDeviceInfo m_hudDeviceInfo;
|
||||||
HudFps m_hudFps;
|
HudFps m_hudFramerate;
|
||||||
HudStats m_hudStats;
|
HudStats m_hudStats;
|
||||||
|
|
||||||
void render();
|
void render();
|
||||||
|
@ -7,6 +7,7 @@ namespace dxvk::hud {
|
|||||||
const std::unordered_map<std::string, HudElement> g_hudElements = {{
|
const std::unordered_map<std::string, HudElement> g_hudElements = {{
|
||||||
{ "devinfo", HudElement::DeviceInfo },
|
{ "devinfo", HudElement::DeviceInfo },
|
||||||
{ "fps", HudElement::Framerate },
|
{ "fps", HudElement::Framerate },
|
||||||
|
{ "frametimes", HudElement::Frametimes },
|
||||||
{ "drawcalls", HudElement::StatDrawCalls },
|
{ "drawcalls", HudElement::StatDrawCalls },
|
||||||
{ "submissions", HudElement::StatSubmissions },
|
{ "submissions", HudElement::StatSubmissions },
|
||||||
{ "pipelines", HudElement::StatPipelines },
|
{ "pipelines", HudElement::StatPipelines },
|
||||||
|
@ -13,10 +13,11 @@ namespace dxvk::hud {
|
|||||||
enum class HudElement {
|
enum class HudElement {
|
||||||
DeviceInfo = 0,
|
DeviceInfo = 0,
|
||||||
Framerate = 1,
|
Framerate = 1,
|
||||||
StatDrawCalls = 2,
|
Frametimes = 2,
|
||||||
StatSubmissions = 3,
|
StatDrawCalls = 3,
|
||||||
StatPipelines = 4,
|
StatSubmissions = 4,
|
||||||
StatMemory = 5,
|
StatPipelines = 5,
|
||||||
|
StatMemory = 6,
|
||||||
};
|
};
|
||||||
|
|
||||||
using HudElements = Flags<HudElement>;
|
using HudElements = Flags<HudElement>;
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
#include "dxvk_hud_fps.h"
|
#include "dxvk_hud_fps.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
|
||||||
namespace dxvk::hud {
|
namespace dxvk::hud {
|
||||||
|
|
||||||
HudFps::HudFps()
|
HudFps::HudFps(HudElements elements)
|
||||||
: m_fpsString("FPS: "),
|
: m_elements (elements),
|
||||||
m_prevUpdate(Clock::now()) {
|
m_fpsString ("FPS: "),
|
||||||
|
m_prevFpsUpdate(Clock::now()),
|
||||||
|
m_prevFtgUpdate(Clock::now()) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,20 +22,45 @@ namespace dxvk::hud {
|
|||||||
void HudFps::update() {
|
void HudFps::update() {
|
||||||
m_frameCount += 1;
|
m_frameCount += 1;
|
||||||
|
|
||||||
const TimePoint now = Clock::now();
|
TimePoint now = Clock::now();
|
||||||
const TimeDiff elapsed = std::chrono::duration_cast<TimeDiff>(now - m_prevUpdate);
|
TimeDiff elapsedFps = std::chrono::duration_cast<TimeDiff>(now - m_prevFpsUpdate);
|
||||||
|
TimeDiff elapsedFtg = std::chrono::duration_cast<TimeDiff>(now - m_prevFtgUpdate);
|
||||||
|
m_prevFtgUpdate = now;
|
||||||
|
|
||||||
if (elapsed.count() >= UpdateInterval) {
|
// Update FPS string
|
||||||
const int64_t fps = (10'000'000ll * m_frameCount) / elapsed.count();
|
if (elapsedFps.count() >= UpdateInterval) {
|
||||||
|
const int64_t fps = (10'000'000ll * m_frameCount) / elapsedFps.count();
|
||||||
m_fpsString = str::format("FPS: ", fps / 10, ".", fps % 10);
|
m_fpsString = str::format("FPS: ", fps / 10, ".", fps % 10);
|
||||||
|
|
||||||
m_prevUpdate = now;
|
m_prevFpsUpdate = now;
|
||||||
m_frameCount = 0;
|
m_frameCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update frametime stuff
|
||||||
|
m_dataPoints[m_dataPointId] = float(elapsedFtg.count());
|
||||||
|
m_dataPointId = (m_dataPointId + 1) % NumDataPoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
HudPos HudFps::render(
|
HudPos HudFps::render(
|
||||||
|
const Rc<DxvkContext>& context,
|
||||||
|
HudRenderer& renderer,
|
||||||
|
HudPos position) {
|
||||||
|
if (m_elements.test(HudElement::Framerate)) {
|
||||||
|
position = this->renderFpsText(
|
||||||
|
context, renderer, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_elements.test(HudElement::Frametimes)) {
|
||||||
|
position = this->renderFrametimeGraph(
|
||||||
|
context, renderer, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HudPos HudFps::renderFpsText(
|
||||||
const Rc<DxvkContext>& context,
|
const Rc<DxvkContext>& context,
|
||||||
HudRenderer& renderer,
|
HudRenderer& renderer,
|
||||||
HudPos position) {
|
HudPos position) {
|
||||||
@ -44,4 +72,62 @@ namespace dxvk::hud {
|
|||||||
return HudPos { position.x, position.y + 24 };
|
return HudPos { position.x, position.y + 24 };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HudPos HudFps::renderFrametimeGraph(
|
||||||
|
const Rc<DxvkContext>& context,
|
||||||
|
HudRenderer& renderer,
|
||||||
|
HudPos position) {
|
||||||
|
std::array<HudVertex, NumDataPoints * 2> vData;
|
||||||
|
|
||||||
|
// 60 FPS = optimal, 10 FPS = worst
|
||||||
|
const float targetUs = 16'666.6f;
|
||||||
|
const float minUs = 5'000.0f;
|
||||||
|
const float maxUs = 100'000.0f;
|
||||||
|
|
||||||
|
// Ten times the maximum/minimum number
|
||||||
|
// of milliseconds for a single frame
|
||||||
|
uint32_t minMs = 0xFFFFFFFFu;
|
||||||
|
uint32_t maxMs = 0x00000000u;
|
||||||
|
|
||||||
|
// Paint the time points
|
||||||
|
for (uint32_t i = 0; i < NumDataPoints; i++) {
|
||||||
|
float us = m_dataPoints[(m_dataPointId + i) % NumDataPoints];
|
||||||
|
|
||||||
|
minMs = std::min(minMs, uint32_t(us / 100.0f));
|
||||||
|
maxMs = std::max(maxMs, uint32_t(us / 100.0f));
|
||||||
|
|
||||||
|
float r = std::clamp(-1.0f + us / targetUs, 0.0f, 1.0f);
|
||||||
|
float g = std::clamp( 3.0f - us / targetUs, 0.0f, 1.0f);
|
||||||
|
float l = std::sqrt(r * r + g * g);
|
||||||
|
|
||||||
|
HudTexCoord tc = { 0u, 0u };
|
||||||
|
HudColor color = { r / l, g / l, 0.0f, 1.0f };
|
||||||
|
|
||||||
|
float x = position.x + float(i);
|
||||||
|
float y = position.y + 24.0f;
|
||||||
|
|
||||||
|
float hVal = std::log2(std::max((us - minUs) / targetUs + 1.0f, 1.0f))
|
||||||
|
/ std::log2((maxUs - minUs) / targetUs);
|
||||||
|
float h = std::clamp(40.0f * hVal, 2.0f, 40.0f);
|
||||||
|
|
||||||
|
vData[2 * i + 0] = HudVertex { { x, y }, tc, color };
|
||||||
|
vData[2 * i + 1] = HudVertex { { x, y - h }, tc, color };
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.drawLines(context, vData.size(), vData.data());
|
||||||
|
|
||||||
|
// Paint min/max frame times in the entire window
|
||||||
|
renderer.drawText(context, 14.0f,
|
||||||
|
{ position.x, position.y + 44.0f },
|
||||||
|
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||||
|
str::format("min: ", minMs / 10, ".", minMs % 10));
|
||||||
|
|
||||||
|
renderer.drawText(context, 14.0f,
|
||||||
|
{ position.x + 150.0f, position.y + 44.0f },
|
||||||
|
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||||
|
str::format("max: ", maxMs / 10, ".", maxMs % 10));
|
||||||
|
|
||||||
|
return HudPos { position.x, position.y + 66.0f };
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
|
#include "dxvk_hud_config.h"
|
||||||
#include "dxvk_hud_renderer.h"
|
#include "dxvk_hud_renderer.h"
|
||||||
|
|
||||||
namespace dxvk::hud {
|
namespace dxvk::hud {
|
||||||
@ -16,10 +17,11 @@ namespace dxvk::hud {
|
|||||||
using TimeDiff = std::chrono::microseconds;
|
using TimeDiff = std::chrono::microseconds;
|
||||||
using TimePoint = typename Clock::time_point;
|
using TimePoint = typename Clock::time_point;
|
||||||
|
|
||||||
constexpr static int64_t UpdateInterval = 500'000;
|
constexpr static uint32_t NumDataPoints = 300;
|
||||||
|
constexpr static int64_t UpdateInterval = 500'000;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
HudFps();
|
HudFps(HudElements elements);
|
||||||
~HudFps();
|
~HudFps();
|
||||||
|
|
||||||
void update();
|
void update();
|
||||||
@ -31,10 +33,26 @@ namespace dxvk::hud {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
const HudElements m_elements;
|
||||||
|
|
||||||
std::string m_fpsString;
|
std::string m_fpsString;
|
||||||
|
|
||||||
TimePoint m_prevUpdate;
|
TimePoint m_prevFpsUpdate;
|
||||||
int64_t m_frameCount = 0;
|
TimePoint m_prevFtgUpdate;
|
||||||
|
int64_t m_frameCount = 0;
|
||||||
|
|
||||||
|
std::array<float, NumDataPoints> m_dataPoints = {};
|
||||||
|
uint32_t m_dataPointId = 0;
|
||||||
|
|
||||||
|
HudPos renderFpsText(
|
||||||
|
const Rc<DxvkContext>& context,
|
||||||
|
HudRenderer& renderer,
|
||||||
|
HudPos position);
|
||||||
|
|
||||||
|
HudPos renderFrametimeGraph(
|
||||||
|
const Rc<DxvkContext>& context,
|
||||||
|
HudRenderer& renderer,
|
||||||
|
HudPos position);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -35,10 +35,10 @@ namespace dxvk::hud {
|
|||||||
* will use this color for the most part.
|
* will use this color for the most part.
|
||||||
*/
|
*/
|
||||||
struct HudColor {
|
struct HudColor {
|
||||||
float x;
|
float r;
|
||||||
float y;
|
float g;
|
||||||
float z;
|
float b;
|
||||||
float w;
|
float a;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user