1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2025-02-20 19:54:19 +01:00

[hud] Add HUD item to visualize memory chunk allocation

This commit is contained in:
Philip Rebohle 2024-09-19 04:36:24 +02:00 committed by Philip Rebohle
parent f679d7d90f
commit 266b99ad8d
10 changed files with 419 additions and 8 deletions

View File

@ -56,6 +56,7 @@ The `DXVK_HUD` environment variable controls a HUD which can display the framera
- `pipelines`: Shows the total number of graphics and compute pipelines.
- `descriptors`: Shows the number of descriptor pools and descriptor sets.
- `memory`: Shows the amount of device memory allocated and used.
- `allocations`: Shows detailed memory chunk suballocation info.
- `gpuload`: Shows estimated GPU load. May be inaccurate.
- `version`: Shows DXVK version.
- `api`: Shows the D3D feature level used by the application.

View File

@ -51,6 +51,7 @@ namespace dxvk::hud {
addItem<HudPipelineStatsItem>("pipelines", -1, device);
addItem<HudDescriptorStatsItem>("descriptors", -1, device);
addItem<HudMemoryStatsItem>("memory", -1, device);
addItem<HudMemoryDetailsItem>("allocations", -1, device);
addItem<HudCsThreadItem>("cs", -1, device);
addItem<HudGpuLoadItem>("gpuload", -1, device);
addItem<HudCompilerActivityItem>("compiler", -1, device);

View File

@ -1,5 +1,9 @@
#include "dxvk_hud_item.h"
#include <hud_chunk_frag_background.h>
#include <hud_chunk_frag_visualize.h>
#include <hud_chunk_vert.h>
#include <iomanip>
#include <version.h>
@ -611,6 +615,235 @@ namespace dxvk::hud {
}
HudMemoryDetailsItem::HudMemoryDetailsItem(const Rc<DxvkDevice>& device)
: m_device(device) {
DxvkShaderCreateInfo shaderInfo;
shaderInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
shaderInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
shaderInfo.pushConstSize = sizeof(ShaderArgs);
shaderInfo.outputMask = 0x1;
m_vs = new DxvkShader(shaderInfo, SpirvCodeBuffer(hud_chunk_vert));
shaderInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
shaderInfo.outputMask = 0x1;
m_fsBackground = new DxvkShader(shaderInfo, SpirvCodeBuffer(hud_chunk_frag_background));
DxvkBindingInfo pageMaskBinding = {
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 0, VK_IMAGE_VIEW_TYPE_MAX_ENUM,
VK_SHADER_STAGE_FRAGMENT_BIT, VK_ACCESS_SHADER_READ_BIT };
shaderInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
shaderInfo.bindingCount = 1;
shaderInfo.bindings = &pageMaskBinding;
shaderInfo.inputMask = 0x1;
shaderInfo.outputMask = 0x1;
m_fsVisualize = new DxvkShader(shaderInfo, SpirvCodeBuffer(hud_chunk_frag_visualize));
}
HudMemoryDetailsItem::~HudMemoryDetailsItem() {
}
void HudMemoryDetailsItem::update(dxvk::high_resolution_clock::time_point time) {
m_device->getMemoryAllocationStats(m_stats);
}
HudPos HudMemoryDetailsItem::render(
HudRenderer& renderer,
HudPos position) {
uploadChunkData(renderer);
// Chunk memory per type, not including dedicated allocations
std::array<VkDeviceSize, VK_MAX_MEMORY_TYPES> chunkMemoryAllocated = { };
std::array<VkDeviceSize, VK_MAX_MEMORY_TYPES> chunkMemoryUsed = { };
// Compute layout, align the entire element to the bottom right.
float maxWidth = 556.0f;
HudPos pos = {
float(renderer.surfaceSize().width) / renderer.scale() - 8.0f - maxWidth,
float(renderer.surfaceSize().height) / renderer.scale() - 8.0f,
};
for (uint32_t i = 0; i < m_stats.memoryTypes.size(); i++) {
const auto& type = m_stats.memoryTypes.at(i);
if (!type.allocated)
continue;
// Reserve space for one line of text
pos.y -= 20.0f;
float width = 0.0f;
for (uint32_t j = 0; j < type.chunkCount; j++) {
const auto& chunk = m_stats.chunks.at(type.chunkIndex + j);
chunkMemoryAllocated.at(i) += chunk.capacity;
chunkMemoryUsed.at(i) += chunk.used;
float pixels = float((chunk.pageCount + 15u) / 16u);
if (width + pixels > maxWidth) {
pos.y -= 30.0f;
width = 0.0f;
}
width += pixels + 6.0f;
}
pos.y -= 30.0f + 4.0f;
}
// Actually render the thing
for (uint32_t i = 0; i < m_stats.memoryTypes.size(); i++) {
const auto& type = m_stats.memoryTypes.at(i);
if (!type.allocated)
continue;
VkDeviceSize dedicated = type.allocated - chunkMemoryAllocated.at(i);
VkDeviceSize allocated = chunkMemoryAllocated.at(i) + dedicated;
VkDeviceSize used = chunkMemoryUsed.at(i) + dedicated;
std::string headline = str::format("Mem type ", i, " [", type.properties.heapIndex, "]: ",
type.chunkCount, " chunk", type.chunkCount != 1u ? "s" : "", " (", (allocated >> 20u), " MB, ",
((used >= (1u << 20u)) ? used >> 20 : used >> 10),
(used >= (1u << 20u) ? " MB" : " kB"), " used)");
renderer.drawText(14.0f,
{ pos.x, pos.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
headline);
pos.y += 8.0f;
float width = 0.0f;
for (uint32_t j = 0; j < type.chunkCount; j++) {
const auto& chunk = m_stats.chunks.at(type.chunkIndex + j);
float pixels = float((chunk.pageCount + 15u) / 16u);
if (width + pixels > maxWidth) {
pos.y += 30.0f;
width = 0.0f;
}
drawChunk(renderer,
{ pos.x + width, pos.y },
{ pixels, 24.0f },
type.properties, chunk);
width += pixels + 6.0f;
}
pos.y += 46.0f;
}
return position;
}
void HudMemoryDetailsItem::uploadChunkData(HudRenderer& renderer) {
DxvkContext* context = renderer.getContext();
VkDeviceSize size = sizeof(uint32_t) * m_stats.pageMasks.size();
if (m_pageMaskBuffer == nullptr || m_pageMaskBuffer->info().size < size) {
DxvkBufferCreateInfo info = { };
info.size = std::max(VkDeviceSize(1u << 14),
(VkDeviceSize(-1) >> bit::lzcnt(size - 1u)) + 1u);
info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
info.access = VK_ACCESS_SHADER_READ_BIT;
info.stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
m_pageMaskBuffer = m_device->createBuffer(info,
VK_MEMORY_HEAP_DEVICE_LOCAL_BIT |
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
DxvkBufferViewCreateInfo viewInfo = { };
viewInfo.format = VK_FORMAT_UNDEFINED;
viewInfo.rangeOffset = 0;
viewInfo.rangeLength = info.size;
viewInfo.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
m_pageMaskView = m_device->createBufferView(m_pageMaskBuffer, viewInfo);
}
if (!m_stats.pageMasks.empty()) {
context->invalidateBuffer(m_pageMaskBuffer, m_pageMaskBuffer->allocSlice());
std::memcpy(m_pageMaskBuffer->mapPtr(0), &m_stats.pageMasks.at(0), size);
}
}
void HudMemoryDetailsItem::drawChunk(
HudRenderer& renderer,
HudPos pos,
HudPos size,
const VkMemoryType& memoryType,
const DxvkMemoryChunkStats& stats) const {
DxvkContext* context = renderer.getContext();
VkExtent2D surfaceSize = renderer.surfaceSize();
static const DxvkInputAssemblyState iaState = {
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
VK_FALSE, 0 };
context->setInputAssemblyState(iaState);
context->bindResourceBufferView(VK_SHADER_STAGE_FRAGMENT_BIT, 0, Rc<DxvkBufferView>(m_pageMaskView));
context->bindShader<VK_SHADER_STAGE_VERTEX_BIT>(Rc<DxvkShader>(m_vs));
context->bindShader<VK_SHADER_STAGE_FRAGMENT_BIT>(Rc<DxvkShader>(m_fsBackground));
ShaderArgs args = { };
args.pos.x = pos.x - 1.0f;
args.pos.y = pos.y - 1.0f;
args.size.x = size.x + 2.0f;
args.size.y = size.y + 2.0f;
args.scale.x = renderer.scale() / std::max(float(surfaceSize.width), 1.0f);
args.scale.y = renderer.scale() / std::max(float(surfaceSize.height), 1.0f);
args.opacity = renderer.opacity();
args.color = 0xc0000000u;
args.maskIndex = stats.pageMaskOffset;
args.pageCount = stats.pageCount;
context->pushConstants(0, sizeof(args), &args);
context->draw(4, 1, 0, 0);
context->bindShader<VK_SHADER_STAGE_FRAGMENT_BIT>(Rc<DxvkShader>(m_fsVisualize));
args.pos = pos;
args.size = size;
if (!(memoryType.propertyFlags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT)) {
if (!(memoryType.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT))
args.color = 0xff202020u;
else if (memoryType.propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT)
args.color = 0xff208020u;
else
args.color = 0xff202080u;
} else if (memoryType.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
args.color = 0xff208080u;
} else {
args.color = 0xff804020u;
}
context->pushConstants(0, sizeof(args), &args);
context->draw(4, 1, 0, 0);
}
HudCsThreadItem::HudCsThreadItem(const Rc<DxvkDevice>& device)
: m_device(device) {
@ -799,7 +1032,7 @@ namespace dxvk::hud {
string = str::format(string, " (", computePercentage(), "%)");
renderer.drawText(16.0f,
{ position.x, renderer.surfaceSize().height / renderer.scale() - 20.0f },
{ position.x, float(renderer.surfaceSize().height) / renderer.scale() - 20.0f },
{ 1.0f, 1.0f, 1.0f, 1.0f },
string);
}

View File

@ -390,6 +390,58 @@ namespace dxvk::hud {
};
/**
* \brief HUD item to display detailed memory allocation info
*/
class HudMemoryDetailsItem : public HudItem {
public:
HudMemoryDetailsItem(const Rc<DxvkDevice>& device);
~HudMemoryDetailsItem();
void update(dxvk::high_resolution_clock::time_point time);
HudPos render(
HudRenderer& renderer,
HudPos position);
private:
struct ShaderArgs {
HudPos pos;
HudPos size;
HudPos scale;
float opacity;
uint32_t color;
uint32_t maskIndex;
uint32_t pageCount;
};
Rc<DxvkDevice> m_device;
DxvkMemoryAllocationStats m_stats;
Rc<DxvkShader> m_vs;
Rc<DxvkShader> m_fsBackground;
Rc<DxvkShader> m_fsVisualize;
Rc<DxvkBuffer> m_pageMaskBuffer;
Rc<DxvkBufferView> m_pageMaskView;
void uploadChunkData(
HudRenderer& renderer);
void drawChunk(
HudRenderer& renderer,
HudPos pos,
HudPos size,
const VkMemoryType& memoryType,
const DxvkMemoryChunkStats& stats) const;
};
/**
* \brief HUD item to display CS thread statistics
*/

View File

@ -33,10 +33,10 @@ namespace dxvk::hud {
void HudRenderer::beginFrame(
const Rc<DxvkContext>& context,
VkExtent2D surfaceSize,
float scale,
float opacity) {
const Rc<DxvkContext>& context,
VkExtent2D surfaceSize,
float scale,
float opacity) {
if (!m_initialized)
this->initFontTexture(context);
@ -128,7 +128,6 @@ namespace dxvk::hud {
VK_FALSE, 0 };
m_context->setInputAssemblyState(iaState);
m_context->setInputLayout(0, nullptr, 0, nullptr);
}
}
@ -147,7 +146,6 @@ namespace dxvk::hud {
VK_FALSE, 0 };
m_context->setInputAssemblyState(iaState);
m_context->setInputLayout(0, nullptr, 0, nullptr);
}
}

View File

@ -121,7 +121,12 @@ namespace dxvk::hud {
HudPos size,
size_t pointCount,
const HudGraphPoint* pointData);
DxvkContext* getContext() {
m_mode = Mode::RenderNone;
return m_context.ptr();
}
VkExtent2D surfaceSize() const {
return m_surfaceSize;
}
@ -129,6 +134,10 @@ namespace dxvk::hud {
float scale() const {
return m_scale;
}
float opacity() const {
return m_opacity;
}
private:

View File

@ -0,0 +1,23 @@
#version 450
#extension GL_GOOGLE_include_directive : require
#include "hud_frag_common.glsl"
layout(location = 0) out vec4 o_color;
layout(push_constant)
uniform push_data_t {
vec2 pos;
vec2 size;
vec2 scale;
float opacity;
uint color;
uint maskIndex;
uint pageCount;
};
void main() {
vec4 rgba = unpackUnorm4x8(color);
o_color = vec4(encodeOutput(rgba.rgb), rgba.a * opacity);
}

View File

@ -0,0 +1,65 @@
#version 450
#extension GL_GOOGLE_include_directive : require
#include "hud_frag_common.glsl"
layout(location = 0) in vec2 v_coord;
layout(location = 0) out vec4 o_color;
layout(binding = 0, std430)
readonly buffer mask_data_t {
uint masks[];
};
layout(push_constant)
uniform push_data_t {
vec2 pos;
vec2 size;
vec2 scale;
float opacity;
uint color;
uint maskIndex;
uint pageCount;
};
void main() {
vec4 rgba = unpackUnorm4x8(color);
float dx = dFdx(v_coord.x * float(pageCount)) / 2.0f - 0.5f;
uvec2 pageRange = uvec2(clamp(
(v_coord.xx * float(pageCount)) + vec2(-dx, dx),
vec2(0.0), vec2(float(pageCount - 1u))));
uint bitsTotal = max(pageRange.y - pageRange.x, 1u);
uint bitsSet = 0u;
uint index = pageRange.x / 32u;
uint shift = pageRange.x % 32u;
if (shift + bitsTotal <= 32u) {
bitsSet = bitCount(bitfieldExtract(
masks[maskIndex + index], int(shift), int(bitsTotal)));
} else {
bitsSet = bitCount(masks[maskIndex + (index++)] >> shift);
uint bitsCounted = 32u - shift;
while (bitsCounted + 32u <= bitsTotal) {
bitsSet += bitCount(masks[maskIndex + (index++)]);
bitsCounted += 32u;
}
if (bitsCounted < bitsTotal) {
bitsSet += bitCount(bitfieldExtract(
masks[maskIndex + (index++)], 0, int(bitsTotal - bitsCounted)));
}
}
if (bitsSet == 0u)
discard;
float blendFactor = 0.5f * float(bitsSet) / max(float(bitsTotal), 1.0f);
o_color = vec4(mix(rgba.rgb, vec3(1.0f), blendFactor), rgba.a * opacity);
o_color.rgb = encodeOutput(o_color.rgb);
}

View File

@ -0,0 +1,25 @@
#version 450
layout(location = 0) out vec2 o_coord;
layout(push_constant)
uniform push_data_t {
vec2 pos;
vec2 size;
vec2 scale;
float opacity;
uint color;
uint maskIndex;
uint pageCount;
};
void main() {
vec2 coord = vec2(
float(gl_VertexIndex & 1),
float(gl_VertexIndex >> 1));
o_coord = coord;
vec2 pixel_pos = pos + size * coord;
vec2 scaled_pos = 2.0f * scale * pixel_pos - 1.0f;
gl_Position = vec4(scaled_pos, 0.0f, 1.0f);
}

View File

@ -50,6 +50,10 @@ dxvk_shaders = files([
'shaders/dxvk_unpack_d24s8.comp',
'shaders/dxvk_unpack_d32s8.comp',
'hud/shaders/hud_chunk_frag_background.frag',
'hud/shaders/hud_chunk_frag_visualize.frag',
'hud/shaders/hud_chunk_vert.vert',
'hud/shaders/hud_graph_frag.frag',
'hud/shaders/hud_graph_vert.vert',