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:
parent
f679d7d90f
commit
266b99ad8d
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
||||
|
23
src/dxvk/hud/shaders/hud_chunk_frag_background.frag
Normal file
23
src/dxvk/hud/shaders/hud_chunk_frag_background.frag
Normal 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);
|
||||
}
|
65
src/dxvk/hud/shaders/hud_chunk_frag_visualize.frag
Normal file
65
src/dxvk/hud/shaders/hud_chunk_frag_visualize.frag
Normal 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);
|
||||
}
|
25
src/dxvk/hud/shaders/hud_chunk_vert.vert
Normal file
25
src/dxvk/hud/shaders/hud_chunk_vert.vert
Normal 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);
|
||||
}
|
@ -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',
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user