diff --git a/README.md b/README.md index 6182f5073..d8d22f4d5 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/src/dxvk/hud/dxvk_hud.cpp b/src/dxvk/hud/dxvk_hud.cpp index 88efa405b..9b1d17688 100644 --- a/src/dxvk/hud/dxvk_hud.cpp +++ b/src/dxvk/hud/dxvk_hud.cpp @@ -51,6 +51,7 @@ namespace dxvk::hud { addItem("pipelines", -1, device); addItem("descriptors", -1, device); addItem("memory", -1, device); + addItem("allocations", -1, device); addItem("cs", -1, device); addItem("gpuload", -1, device); addItem("compiler", -1, device); diff --git a/src/dxvk/hud/dxvk_hud_item.cpp b/src/dxvk/hud/dxvk_hud_item.cpp index 699262ea7..0a8873552 100644 --- a/src/dxvk/hud/dxvk_hud_item.cpp +++ b/src/dxvk/hud/dxvk_hud_item.cpp @@ -1,5 +1,9 @@ #include "dxvk_hud_item.h" +#include +#include +#include + #include #include @@ -611,6 +615,235 @@ namespace dxvk::hud { } + + + HudMemoryDetailsItem::HudMemoryDetailsItem(const Rc& 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 chunkMemoryAllocated = { }; + std::array 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(m_pageMaskView)); + + context->bindShader(Rc(m_vs)); + context->bindShader(Rc(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(Rc(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& 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); } diff --git a/src/dxvk/hud/dxvk_hud_item.h b/src/dxvk/hud/dxvk_hud_item.h index 6b0e93fd7..b5fef160b 100644 --- a/src/dxvk/hud/dxvk_hud_item.h +++ b/src/dxvk/hud/dxvk_hud_item.h @@ -390,6 +390,58 @@ namespace dxvk::hud { }; + /** + * \brief HUD item to display detailed memory allocation info + */ + class HudMemoryDetailsItem : public HudItem { + + public: + + HudMemoryDetailsItem(const Rc& 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 m_device; + DxvkMemoryAllocationStats m_stats; + + Rc m_vs; + Rc m_fsBackground; + Rc m_fsVisualize; + + Rc m_pageMaskBuffer; + Rc 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 */ diff --git a/src/dxvk/hud/dxvk_hud_renderer.cpp b/src/dxvk/hud/dxvk_hud_renderer.cpp index dc96c74bd..3b74a0ee3 100644 --- a/src/dxvk/hud/dxvk_hud_renderer.cpp +++ b/src/dxvk/hud/dxvk_hud_renderer.cpp @@ -33,10 +33,10 @@ namespace dxvk::hud { void HudRenderer::beginFrame( - const Rc& context, - VkExtent2D surfaceSize, - float scale, - float opacity) { + const Rc& 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); } } diff --git a/src/dxvk/hud/dxvk_hud_renderer.h b/src/dxvk/hud/dxvk_hud_renderer.h index bcfb67543..45184be7a 100644 --- a/src/dxvk/hud/dxvk_hud_renderer.h +++ b/src/dxvk/hud/dxvk_hud_renderer.h @@ -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: diff --git a/src/dxvk/hud/shaders/hud_chunk_frag_background.frag b/src/dxvk/hud/shaders/hud_chunk_frag_background.frag new file mode 100644 index 000000000..ef836baef --- /dev/null +++ b/src/dxvk/hud/shaders/hud_chunk_frag_background.frag @@ -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); +} diff --git a/src/dxvk/hud/shaders/hud_chunk_frag_visualize.frag b/src/dxvk/hud/shaders/hud_chunk_frag_visualize.frag new file mode 100644 index 000000000..95d50f69e --- /dev/null +++ b/src/dxvk/hud/shaders/hud_chunk_frag_visualize.frag @@ -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); +} diff --git a/src/dxvk/hud/shaders/hud_chunk_vert.vert b/src/dxvk/hud/shaders/hud_chunk_vert.vert new file mode 100644 index 000000000..cab2fc6ba --- /dev/null +++ b/src/dxvk/hud/shaders/hud_chunk_vert.vert @@ -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); +} diff --git a/src/dxvk/meson.build b/src/dxvk/meson.build index b78ffd72d..16e9c98ee 100644 --- a/src/dxvk/meson.build +++ b/src/dxvk/meson.build @@ -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',