2019-12-12 20:46:17 +01:00
|
|
|
#include "dxvk_hud_item.h"
|
|
|
|
|
2019-12-12 21:01:30 +01:00
|
|
|
#include <version.h>
|
|
|
|
|
2019-12-12 20:46:17 +01:00
|
|
|
namespace dxvk::hud {
|
|
|
|
|
|
|
|
HudItem::~HudItem() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void HudItem::update(dxvk::high_resolution_clock::time_point time) {
|
|
|
|
// Do nothing by default. Some items won't need this.
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HudItemSet::HudItemSet() {
|
|
|
|
std::string configStr = env::getEnvVar("DXVK_HUD");
|
|
|
|
|
|
|
|
if (configStr == "full") {
|
|
|
|
// Just enable everything
|
|
|
|
m_enableFull = true;
|
|
|
|
} else if (configStr == "1") {
|
|
|
|
m_enabled.insert("devinfo");
|
|
|
|
m_enabled.insert("fps");
|
|
|
|
} else {
|
|
|
|
std::string::size_type pos = 0;
|
|
|
|
std::string::size_type end = 0;
|
|
|
|
|
|
|
|
while (pos < configStr.size()) {
|
|
|
|
end = configStr.find(',', pos);
|
|
|
|
|
|
|
|
if (end == std::string::npos)
|
|
|
|
end = configStr.size();
|
|
|
|
|
|
|
|
m_enabled.insert(configStr.substr(pos, end - pos));
|
|
|
|
pos = end + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HudItemSet::~HudItemSet() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void HudItemSet::update() {
|
|
|
|
auto time = dxvk::high_resolution_clock::now();
|
|
|
|
|
|
|
|
for (const auto& item : m_items)
|
|
|
|
item->update(time);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void HudItemSet::render(HudRenderer& renderer) {
|
|
|
|
HudPos position = { 8.0f, 8.0f };
|
|
|
|
|
|
|
|
for (const auto& item : m_items)
|
|
|
|
position = item->render(renderer, position);
|
|
|
|
}
|
|
|
|
|
2019-12-12 21:01:30 +01:00
|
|
|
|
|
|
|
HudPos HudVersionItem::render(
|
|
|
|
HudRenderer& renderer,
|
|
|
|
HudPos position) {
|
|
|
|
position.y += 16.0f;
|
|
|
|
|
|
|
|
renderer.drawText(16.0f,
|
|
|
|
{ position.x, position.y },
|
|
|
|
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
|
|
|
"DXVK " DXVK_VERSION);
|
|
|
|
|
|
|
|
position.y += 8.0f;
|
|
|
|
return position;
|
|
|
|
}
|
|
|
|
|
2019-12-12 21:06:48 +01:00
|
|
|
|
|
|
|
HudClientApiItem::HudClientApiItem(const Rc<DxvkDevice>& device)
|
|
|
|
: m_device(device) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HudClientApiItem::~HudClientApiItem() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HudPos HudClientApiItem::render(
|
|
|
|
HudRenderer& renderer,
|
|
|
|
HudPos position) {
|
|
|
|
position.y += 16.0f;
|
|
|
|
|
|
|
|
renderer.drawText(16.0f,
|
|
|
|
{ position.x, position.y },
|
|
|
|
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
|
|
|
m_device->clientApi());
|
|
|
|
|
|
|
|
position.y += 8.0f;
|
|
|
|
return position;
|
|
|
|
}
|
|
|
|
|
2019-12-12 21:15:05 +01:00
|
|
|
|
|
|
|
HudDeviceInfoItem::HudDeviceInfoItem(const Rc<DxvkDevice>& device) {
|
|
|
|
VkPhysicalDeviceProperties props = device->adapter()->deviceProperties();
|
|
|
|
|
|
|
|
m_deviceName = props.deviceName;
|
|
|
|
m_driverVer = str::format("Driver: ",
|
|
|
|
VK_VERSION_MAJOR(props.driverVersion), ".",
|
|
|
|
VK_VERSION_MINOR(props.driverVersion), ".",
|
|
|
|
VK_VERSION_PATCH(props.driverVersion));
|
|
|
|
m_vulkanVer = str::format("Vulkan: ",
|
|
|
|
VK_VERSION_MAJOR(props.apiVersion), ".",
|
|
|
|
VK_VERSION_MINOR(props.apiVersion), ".",
|
|
|
|
VK_VERSION_PATCH(props.apiVersion));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HudDeviceInfoItem::~HudDeviceInfoItem() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HudPos HudDeviceInfoItem::render(
|
|
|
|
HudRenderer& renderer,
|
|
|
|
HudPos position) {
|
|
|
|
position.y += 16.0f;
|
|
|
|
renderer.drawText(16.0f,
|
|
|
|
{ position.x, position.y },
|
|
|
|
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
|
|
|
m_deviceName);
|
|
|
|
|
|
|
|
position.y += 24.0f;
|
|
|
|
renderer.drawText(16.0f,
|
|
|
|
{ position.x, position.y },
|
|
|
|
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
|
|
|
m_driverVer);
|
|
|
|
|
|
|
|
position.y += 20.0f;
|
|
|
|
renderer.drawText(16.0f,
|
|
|
|
{ position.x, position.y },
|
|
|
|
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
|
|
|
m_vulkanVer);
|
|
|
|
|
|
|
|
position.y += 8.0f;
|
|
|
|
return position;
|
|
|
|
}
|
|
|
|
|
2019-12-12 21:24:39 +01:00
|
|
|
|
|
|
|
HudFpsItem::HudFpsItem() { }
|
|
|
|
HudFpsItem::~HudFpsItem() { }
|
|
|
|
|
|
|
|
|
|
|
|
void HudFpsItem::update(dxvk::high_resolution_clock::time_point time) {
|
|
|
|
m_frameCount += 1;
|
|
|
|
|
|
|
|
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(time - m_lastUpdate);
|
|
|
|
|
|
|
|
if (elapsed.count() >= UpdateInterval) {
|
|
|
|
int64_t fps = (10'000'000ll * m_frameCount) / elapsed.count();
|
|
|
|
|
|
|
|
m_frameRate = str::format("FPS: ", fps / 10, ".", fps % 10);
|
|
|
|
m_frameCount = 0;
|
|
|
|
m_lastUpdate = time;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HudPos HudFpsItem::render(
|
|
|
|
HudRenderer& renderer,
|
|
|
|
HudPos position) {
|
|
|
|
position.y += 16.0f;
|
|
|
|
|
|
|
|
renderer.drawText(16.0f,
|
|
|
|
{ position.x, position.y },
|
|
|
|
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
|
|
|
m_frameRate);
|
|
|
|
|
|
|
|
position.y += 8.0f;
|
|
|
|
return position;
|
|
|
|
}
|
|
|
|
|
2019-12-12 21:34:37 +01:00
|
|
|
|
|
|
|
HudFrameTimeItem::HudFrameTimeItem() { }
|
|
|
|
HudFrameTimeItem::~HudFrameTimeItem() { }
|
|
|
|
|
|
|
|
|
|
|
|
void HudFrameTimeItem::update(dxvk::high_resolution_clock::time_point time) {
|
|
|
|
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(time - m_lastUpdate);
|
|
|
|
|
|
|
|
m_dataPoints[m_dataPointId] = float(elapsed.count());
|
|
|
|
m_dataPointId = (m_dataPointId + 1) % NumDataPoints;
|
|
|
|
|
|
|
|
m_lastUpdate = time;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HudPos HudFrameTimeItem::render(
|
|
|
|
HudRenderer& renderer,
|
|
|
|
HudPos position) {
|
|
|
|
std::array<HudLineVertex, NumDataPoints * 2> vData;
|
|
|
|
position.y += 40.0f;
|
|
|
|
|
|
|
|
// 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::min(std::max(-1.0f + us / targetUs, 0.0f), 1.0f);
|
|
|
|
float g = std::min(std::max( 3.0f - us / targetUs, 0.0f), 1.0f);
|
|
|
|
float l = std::sqrt(r * r + g * g);
|
|
|
|
|
|
|
|
HudNormColor color = {
|
|
|
|
uint8_t(255.0f * (r / l)),
|
|
|
|
uint8_t(255.0f * (g / l)),
|
|
|
|
uint8_t(0), uint8_t(255) };
|
|
|
|
|
|
|
|
float x = position.x + float(i);
|
|
|
|
float y = position.y;
|
|
|
|
|
|
|
|
float hVal = std::log2(std::max((us - minUs) / targetUs + 1.0f, 1.0f))
|
|
|
|
/ std::log2((maxUs - minUs) / targetUs);
|
|
|
|
float h = std::min(std::max(40.0f * hVal, 2.0f), 40.0f);
|
|
|
|
|
|
|
|
vData[2 * i + 0] = HudLineVertex { { x, y }, color };
|
|
|
|
vData[2 * i + 1] = HudLineVertex { { x, y - h }, color };
|
|
|
|
}
|
|
|
|
|
|
|
|
renderer.drawLines(vData.size(), vData.data());
|
|
|
|
|
|
|
|
// Paint min/max frame times in the entire window
|
|
|
|
position.y += 20.0f;
|
|
|
|
|
|
|
|
renderer.drawText(14.0f,
|
|
|
|
{ position.x, position.y },
|
|
|
|
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
|
|
|
str::format("min: ", minMs / 10, ".", minMs % 10));
|
|
|
|
|
|
|
|
renderer.drawText(14.0f,
|
|
|
|
{ position.x + 150.0f, position.y },
|
|
|
|
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
|
|
|
str::format("max: ", maxMs / 10, ".", maxMs % 10));
|
|
|
|
|
|
|
|
position.y += 2.0f;
|
|
|
|
return position;
|
|
|
|
}
|
|
|
|
|
2019-12-13 10:47:40 +01:00
|
|
|
|
|
|
|
HudSubmissionStatsItem::HudSubmissionStatsItem(const Rc<DxvkDevice>& device)
|
|
|
|
: m_device(device) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HudSubmissionStatsItem::~HudSubmissionStatsItem() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void HudSubmissionStatsItem::update(dxvk::high_resolution_clock::time_point time) {
|
|
|
|
DxvkStatCounters counters = m_device->getStatCounters();
|
|
|
|
|
|
|
|
uint32_t currCounter = counters.getCtr(DxvkStatCounter::QueueSubmitCount);
|
|
|
|
m_diffCounter = currCounter - m_prevCounter;
|
|
|
|
m_prevCounter = currCounter;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HudPos HudSubmissionStatsItem::render(
|
|
|
|
HudRenderer& renderer,
|
|
|
|
HudPos position) {
|
|
|
|
position.y += 16.0f;
|
|
|
|
|
|
|
|
renderer.drawText(16.0f,
|
|
|
|
{ position.x, position.y },
|
|
|
|
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
|
|
|
str::format("Queue submissions: ", m_diffCounter));
|
|
|
|
|
|
|
|
position.y += 8.0f;
|
|
|
|
return position;
|
|
|
|
}
|
|
|
|
|
2019-12-13 10:56:46 +01:00
|
|
|
|
|
|
|
HudDrawCallStatsItem::HudDrawCallStatsItem(const Rc<DxvkDevice>& device)
|
|
|
|
: m_device(device) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HudDrawCallStatsItem::~HudDrawCallStatsItem() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void HudDrawCallStatsItem::update(dxvk::high_resolution_clock::time_point time) {
|
|
|
|
DxvkStatCounters counters = m_device->getStatCounters();
|
|
|
|
|
|
|
|
m_diffCounters = counters.diff(m_prevCounters);
|
|
|
|
m_prevCounters = counters;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HudPos HudDrawCallStatsItem::render(
|
|
|
|
HudRenderer& renderer,
|
|
|
|
HudPos position) {
|
|
|
|
uint64_t gpCalls = m_diffCounters.getCtr(DxvkStatCounter::CmdDrawCalls);
|
|
|
|
uint64_t cpCalls = m_diffCounters.getCtr(DxvkStatCounter::CmdDispatchCalls);
|
|
|
|
uint64_t rpCalls = m_diffCounters.getCtr(DxvkStatCounter::CmdRenderPassCount);
|
|
|
|
|
|
|
|
std::string strDrawCalls = str::format("Draw calls: ", gpCalls);
|
|
|
|
std::string strDispatchCalls = str::format("Dispatch calls: ", cpCalls);
|
|
|
|
std::string strRenderPasses = str::format("Render passes: ", rpCalls);
|
|
|
|
|
|
|
|
position.y += 16.0f;
|
|
|
|
renderer.drawText(16.0f,
|
|
|
|
{ position.x, position.y },
|
|
|
|
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
|
|
|
strDrawCalls);
|
|
|
|
|
|
|
|
position.y += 20.0f;
|
|
|
|
renderer.drawText(16.0f,
|
|
|
|
{ position.x, position.y },
|
|
|
|
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
|
|
|
strDispatchCalls);
|
|
|
|
|
|
|
|
position.y += 20.0f;
|
|
|
|
renderer.drawText(16.0f,
|
|
|
|
{ position.x, position.y },
|
|
|
|
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
|
|
|
strRenderPasses);
|
|
|
|
|
|
|
|
position.y += 8.0f;
|
|
|
|
return position;
|
|
|
|
}
|
|
|
|
|
2019-12-12 20:46:17 +01:00
|
|
|
}
|