mirror of
https://github.com/EduApps-CDG/OpenDX
synced 2024-12-30 09:45:37 +01:00
587 lines
15 KiB
C++
587 lines
15 KiB
C++
#include "dxvk_hud_item.h"
|
|
|
|
#include <iomanip>
|
|
#include <version.h>
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
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 / 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, 0.25f, 0.25f, 1.0f },
|
|
"FPS:");
|
|
|
|
renderer.drawText(16.0f,
|
|
{ position.x + 60.0f, position.y },
|
|
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
|
m_frameRate);
|
|
|
|
position.y += 8.0f;
|
|
return position;
|
|
}
|
|
|
|
|
|
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 += 18.0f;
|
|
|
|
renderer.drawText(12.0f,
|
|
{ position.x, position.y },
|
|
{ 1.0f, 0.25f, 0.25f, 1.0f },
|
|
"min:");
|
|
|
|
renderer.drawText(12.0f,
|
|
{ position.x + 45.0f, position.y },
|
|
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
|
str::format(minMs / 10, ".", minMs % 10));
|
|
|
|
renderer.drawText(12.0f,
|
|
{ position.x + 150.0f, position.y },
|
|
{ 1.0f, 0.25f, 0.25f, 1.0f },
|
|
"max:");
|
|
|
|
renderer.drawText(12.0f,
|
|
{ position.x + 195.0f, position.y },
|
|
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
|
str::format(maxMs / 10, ".", maxMs % 10));
|
|
|
|
position.y += 4.0f;
|
|
return position;
|
|
}
|
|
|
|
|
|
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 = std::max(m_diffCounter, currCounter - m_prevCounter);
|
|
m_prevCounter = currCounter;
|
|
|
|
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(time - m_lastUpdate);
|
|
|
|
if (elapsed.count() >= UpdateInterval) {
|
|
m_showCounter = m_diffCounter;
|
|
m_diffCounter = 0;
|
|
|
|
m_lastUpdate = time;
|
|
}
|
|
}
|
|
|
|
|
|
HudPos HudSubmissionStatsItem::render(
|
|
HudRenderer& renderer,
|
|
HudPos position) {
|
|
position.y += 16.0f;
|
|
|
|
renderer.drawText(16.0f,
|
|
{ position.x, position.y },
|
|
{ 1.0f, 0.5f, 0.25f, 1.0f },
|
|
"Queue submissions: ");
|
|
|
|
renderer.drawText(16.0f,
|
|
{ position.x + 228.0f, position.y },
|
|
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
|
str::format(m_showCounter));
|
|
|
|
position.y += 8.0f;
|
|
return position;
|
|
}
|
|
|
|
|
|
HudDrawCallStatsItem::HudDrawCallStatsItem(const Rc<DxvkDevice>& device)
|
|
: m_device(device) {
|
|
|
|
}
|
|
|
|
|
|
HudDrawCallStatsItem::~HudDrawCallStatsItem() {
|
|
|
|
}
|
|
|
|
|
|
void HudDrawCallStatsItem::update(dxvk::high_resolution_clock::time_point time) {
|
|
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(time - m_lastUpdate);
|
|
|
|
m_frameCount += 1;
|
|
|
|
if (elapsed.count() >= UpdateInterval) {
|
|
DxvkStatCounters counters = m_device->getStatCounters();
|
|
auto diffCounters = counters.diff(m_prevCounters);
|
|
|
|
m_gpCount = diffCounters.getCtr(DxvkStatCounter::CmdDrawCalls) / m_frameCount;
|
|
m_cpCount = diffCounters.getCtr(DxvkStatCounter::CmdDispatchCalls) / m_frameCount;
|
|
m_rpCount = diffCounters.getCtr(DxvkStatCounter::CmdRenderPassCount) / m_frameCount;
|
|
|
|
m_prevCounters = counters;
|
|
m_lastUpdate = time;
|
|
m_frameCount = 0;
|
|
}
|
|
}
|
|
|
|
|
|
HudPos HudDrawCallStatsItem::render(
|
|
HudRenderer& renderer,
|
|
HudPos position) {
|
|
position.y += 16.0f;
|
|
renderer.drawText(16.0f,
|
|
{ position.x, position.y },
|
|
{ 0.25f, 0.5f, 1.0f, 1.0f },
|
|
"Draw calls:");
|
|
|
|
renderer.drawText(16.0f,
|
|
{ position.x + 192.0f, position.y },
|
|
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
|
str::format(m_gpCount));
|
|
|
|
position.y += 20.0f;
|
|
renderer.drawText(16.0f,
|
|
{ position.x, position.y },
|
|
{ 0.25f, 0.5f, 1.0f, 1.0f },
|
|
"Dispatch calls:");
|
|
|
|
renderer.drawText(16.0f,
|
|
{ position.x + 192.0f, position.y },
|
|
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
|
str::format(m_cpCount));
|
|
|
|
position.y += 20.0f;
|
|
renderer.drawText(16.0f,
|
|
{ position.x, position.y },
|
|
{ 0.25f, 0.5f, 1.0f, 1.0f },
|
|
"Render passes:");
|
|
|
|
renderer.drawText(16.0f,
|
|
{ position.x + 192.0f, position.y },
|
|
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
|
str::format(m_rpCount));
|
|
|
|
position.y += 8.0f;
|
|
return position;
|
|
}
|
|
|
|
|
|
HudPipelineStatsItem::HudPipelineStatsItem(const Rc<DxvkDevice>& device)
|
|
: m_device(device) {
|
|
|
|
}
|
|
|
|
|
|
HudPipelineStatsItem::~HudPipelineStatsItem() {
|
|
|
|
}
|
|
|
|
|
|
void HudPipelineStatsItem::update(dxvk::high_resolution_clock::time_point time) {
|
|
DxvkStatCounters counters = m_device->getStatCounters();
|
|
|
|
m_graphicsPipelines = counters.getCtr(DxvkStatCounter::PipeCountGraphics);
|
|
m_computePipelines = counters.getCtr(DxvkStatCounter::PipeCountCompute);
|
|
}
|
|
|
|
|
|
HudPos HudPipelineStatsItem::render(
|
|
HudRenderer& renderer,
|
|
HudPos position) {
|
|
position.y += 16.0f;
|
|
renderer.drawText(16.0f,
|
|
{ position.x, position.y },
|
|
{ 1.0f, 0.25f, 1.0f, 1.0f },
|
|
"Graphics pipelines:");
|
|
|
|
renderer.drawText(16.0f,
|
|
{ position.x + 240.0f, position.y },
|
|
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
|
str::format(m_graphicsPipelines));
|
|
|
|
position.y += 20.0f;
|
|
renderer.drawText(16.0f,
|
|
{ position.x, position.y },
|
|
{ 1.0f, 0.25f, 1.0f, 1.0f },
|
|
"Compute pipelines:");
|
|
|
|
renderer.drawText(16.0f,
|
|
{ position.x + 240.0f, position.y },
|
|
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
|
str::format(m_computePipelines));
|
|
|
|
position.y += 8.0f;
|
|
return position;
|
|
}
|
|
|
|
|
|
HudMemoryStatsItem::HudMemoryStatsItem(const Rc<DxvkDevice>& device)
|
|
: m_device(device), m_memory(device->adapter()->memoryProperties()) {
|
|
|
|
}
|
|
|
|
|
|
HudMemoryStatsItem::~HudMemoryStatsItem() {
|
|
|
|
}
|
|
|
|
|
|
void HudMemoryStatsItem::update(dxvk::high_resolution_clock::time_point time) {
|
|
for (uint32_t i = 0; i < m_memory.memoryHeapCount; i++)
|
|
m_heaps[i] = m_device->getMemoryStats(i);
|
|
}
|
|
|
|
|
|
HudPos HudMemoryStatsItem::render(
|
|
HudRenderer& renderer,
|
|
HudPos position) {
|
|
for (uint32_t i = 0; i < m_memory.memoryHeapCount; i++) {
|
|
bool isDeviceLocal = m_memory.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT;
|
|
|
|
uint64_t memUsedMib = m_heaps[i].memoryUsed >> 20;
|
|
uint64_t percentage = (100 * m_heaps[i].memoryUsed) / m_memory.memoryHeaps[i].size;
|
|
|
|
std::string label = str::format(isDeviceLocal ? "Vidmem" : "Sysmem", " heap ", i, ":");
|
|
std::string text = str::format(std::setfill(' '), std::setw(5), memUsedMib, " MB (", percentage, "%)");
|
|
|
|
position.y += 16.0f;
|
|
renderer.drawText(16.0f,
|
|
{ position.x, position.y },
|
|
{ 1.0f, 1.0f, 0.25f, 1.0f },
|
|
label);
|
|
|
|
renderer.drawText(16.0f,
|
|
{ position.x + 168.0f, position.y },
|
|
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
|
text);
|
|
position.y += 4.0f;
|
|
}
|
|
|
|
position.y += 4.0f;
|
|
return position;
|
|
}
|
|
|
|
|
|
HudGpuLoadItem::HudGpuLoadItem(const Rc<DxvkDevice>& device)
|
|
: m_device(device) {
|
|
|
|
}
|
|
|
|
|
|
HudGpuLoadItem::~HudGpuLoadItem() {
|
|
|
|
}
|
|
|
|
|
|
void HudGpuLoadItem::update(dxvk::high_resolution_clock::time_point time) {
|
|
uint64_t ticks = std::chrono::duration_cast<std::chrono::microseconds>(time - m_lastUpdate).count();
|
|
|
|
if (ticks >= UpdateInterval) {
|
|
DxvkStatCounters counters = m_device->getStatCounters();
|
|
uint64_t currGpuIdleTicks = counters.getCtr(DxvkStatCounter::GpuIdleTicks);
|
|
|
|
m_diffGpuIdleTicks = currGpuIdleTicks - m_prevGpuIdleTicks;
|
|
m_prevGpuIdleTicks = currGpuIdleTicks;
|
|
|
|
uint64_t busyTicks = ticks > m_diffGpuIdleTicks
|
|
? uint64_t(ticks - m_diffGpuIdleTicks)
|
|
: uint64_t(0);
|
|
|
|
m_gpuLoadString = str::format((100 * busyTicks) / ticks, "%");
|
|
m_lastUpdate = time;
|
|
}
|
|
}
|
|
|
|
|
|
HudPos HudGpuLoadItem::render(
|
|
HudRenderer& renderer,
|
|
HudPos position) {
|
|
position.y += 16.0f;
|
|
|
|
renderer.drawText(16.0f,
|
|
{ position.x, position.y },
|
|
{ 0.25f, 0.5f, 0.25f, 1.0f },
|
|
"GPU:");
|
|
|
|
renderer.drawText(16.0f,
|
|
{ position.x + 60.0f, position.y },
|
|
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
|
m_gpuLoadString);
|
|
|
|
position.y += 8.0f;
|
|
return position;
|
|
}
|
|
|
|
|
|
HudCompilerActivityItem::HudCompilerActivityItem(const Rc<DxvkDevice>& device)
|
|
: m_device(device) {
|
|
|
|
}
|
|
|
|
|
|
HudCompilerActivityItem::~HudCompilerActivityItem() {
|
|
|
|
}
|
|
|
|
|
|
void HudCompilerActivityItem::update(dxvk::high_resolution_clock::time_point time) {
|
|
DxvkStatCounters counters = m_device->getStatCounters();
|
|
bool doShow = counters.getCtr(DxvkStatCounter::PipeCompilerBusy);
|
|
|
|
if (!doShow) {
|
|
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(time - m_timeShown);
|
|
doShow = elapsed.count() <= MinShowDuration;
|
|
}
|
|
|
|
if (doShow && !m_show)
|
|
m_timeShown = time;
|
|
|
|
m_show = doShow;
|
|
}
|
|
|
|
|
|
HudPos HudCompilerActivityItem::render(
|
|
HudRenderer& renderer,
|
|
HudPos position) {
|
|
if (m_show) {
|
|
renderer.drawText(16.0f,
|
|
{ position.x, renderer.surfaceSize().height - 20.0f },
|
|
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
|
"Compiling shaders...");
|
|
}
|
|
|
|
return position;
|
|
}
|
|
|
|
}
|