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

[dxvk] Reimplement HUD rendering to use Vulkan directly

And change how rendering works in general so that we emit fewer
draw calls.
This commit is contained in:
Philip Rebohle 2024-09-30 21:18:06 +02:00 committed by Philip Rebohle
parent b35c0bce4f
commit 63b200f08d
22 changed files with 2580 additions and 1397 deletions

View File

@ -402,8 +402,10 @@ namespace dxvk {
m_imageViews.at(imageIndex), m_colorspace, VkRect2D(),
m_swapImageView, m_colorspace, VkRect2D());
// if (m_hud != nullptr)
// m_hud->render(m_context, info.format, info.imageExtent);
if (m_hud) {
m_hud->render(m_context->beginExternalRendering(),
m_imageViews.at(imageIndex), m_colorspace);
}
m_blitter->endPresent(m_context->beginExternalRendering(),
m_imageViews.at(imageIndex));

View File

@ -16,28 +16,23 @@ namespace dxvk::hud {
HudPos HudSamplerCount::render(
HudRenderer& renderer,
HudPos position) {
position.y += 16.0f;
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position) {
position.y += 16;
renderer.drawText(16, position, 0xffc0ff00u, "Samplers:");
renderer.drawText(16, { position.x + 120, position.y }, 0xffffffffu, m_samplerCount);
renderer.drawText(16.0f,
{ position.x, position.y },
{ 0.0f, 1.0f, 0.75f, 1.0f },
"Samplers:");
renderer.drawText(16.0f,
{ position.x + 120.0f, position.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
m_samplerCount);
position.y += 8.0f;
position.y += 8;
return position;
}
HudTextureMemory::HudTextureMemory(D3D9DeviceEx* device)
: m_device (device)
, m_allocatedString ("")
, m_mappedString ("") {}
: m_device (device)
, m_allocatedString ("")
, m_mappedString ("") { }
void HudTextureMemory::update(dxvk::high_resolution_clock::time_point time) {
@ -62,40 +57,26 @@ namespace dxvk::hud {
HudPos HudTextureMemory::render(
HudRenderer& renderer,
HudPos position) {
position.y += 16.0f;
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position) {
position.y += 16;
renderer.drawText(16, position, 0xffc0ff00u, "Mappable:");
renderer.drawText(16, { position.x + 120, position.y }, 0xffffffffu, m_allocatedString);
renderer.drawText(16.0f,
{ position.x, position.y },
{ 0.0f, 1.0f, 0.75f, 1.0f },
"Mappable:");
renderer.drawText(16.0f,
{ position.x + 120.0f, position.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
m_allocatedString);
position.y += 24.0f;
renderer.drawText(16.0f,
{ position.x, position.y },
{ 0.0f, 1.0f, 0.75f, 1.0f },
"Mapped:");
renderer.drawText(16.0f,
{ position.x + 120.0f, position.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
m_mappedString);
position.y += 8.0f;
position.y += 20;
renderer.drawText(16, position, 0xffc0ff00u, "Mapped:");
renderer.drawText(16, { position.x + 120, position.y }, 0xffffffffu, m_mappedString);
position.y += 8;
return position;
}
HudFixedFunctionShaders::HudFixedFunctionShaders(D3D9DeviceEx* device)
: m_device (device)
, m_ffShaderCount ("") {}
: m_device (device)
, m_ffShaderCount ("") {}
void HudFixedFunctionShaders::update(dxvk::high_resolution_clock::time_point time) {
@ -108,29 +89,26 @@ namespace dxvk::hud {
HudPos HudFixedFunctionShaders::render(
HudRenderer& renderer,
HudPos position) {
position.y += 16.0f;
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position) {
position.y += 16;
renderer.drawText(16, position, 0xffc0ff00u, "FF Shaders:");
renderer.drawText(16, { position.x + 155, position.y }, 0xffffffffu, m_ffShaderCount);
renderer.drawText(16.0f,
{ position.x, position.y },
{ 0.0f, 1.0f, 0.75f, 1.0f },
"FF Shaders:");
renderer.drawText(16.0f,
{ position.x + 155.0f, position.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
m_ffShaderCount);
position.y += 8.0f;
position.y += 8;
return position;
}
HudSWVPState::HudSWVPState(D3D9DeviceEx* device)
: m_device (device)
, m_isSWVPText ("") {}
void HudSWVPState::update(dxvk::high_resolution_clock::time_point time) {
if (m_device->IsSWVP()) {
if (m_device->CanOnlySWVP()) {
@ -149,21 +127,16 @@ namespace dxvk::hud {
HudPos HudSWVPState::render(
HudRenderer& renderer,
HudPos position) {
position.y += 16.0f;
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position) {
position.y += 16;
renderer.drawText(16, position, 0xffc0ff00u, "Vertex Processing:");
renderer.drawText(16, { position.x + 240, position.y }, 0xffffffffu, m_isSWVPText);
renderer.drawText(16.0f,
{ position.x, position.y },
{ 0.0f, 1.0f, 0.75f, 1.0f },
"Vertex Processing:");
renderer.drawText(16.0f,
{ position.x + 240.0f, position.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
m_isSWVPText);
position.y += 8.0f;
position.y += 8;
return position;
}

View File

@ -17,8 +17,11 @@ namespace dxvk::hud {
void update(dxvk::high_resolution_clock::time_point time);
HudPos render(
HudRenderer& renderer,
HudPos position);
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
private:
@ -28,80 +31,92 @@ namespace dxvk::hud {
};
/**
* \brief HUD item to display unmappable memory
*/
class HudTextureMemory : public HudItem {
constexpr static int64_t UpdateInterval = 500'000;
/**
* \brief HUD item to display unmappable memory
*/
class HudTextureMemory : public HudItem {
constexpr static int64_t UpdateInterval = 500'000;
public:
public:
HudTextureMemory(D3D9DeviceEx* device);
HudTextureMemory(D3D9DeviceEx* device);
void update(dxvk::high_resolution_clock::time_point time);
void update(dxvk::high_resolution_clock::time_point time);
HudPos render(
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
HudPos render(
HudRenderer& renderer,
HudPos position);
private:
private:
D3D9DeviceEx* m_device;
D3D9DeviceEx* m_device;
uint32_t m_maxAllocated = 0;
uint32_t m_maxUsed = 0;
uint32_t m_maxMapped = 0;
uint32_t m_maxAllocated = 0;
uint32_t m_maxUsed = 0;
uint32_t m_maxMapped = 0;
dxvk::high_resolution_clock::time_point m_lastUpdate
= dxvk::high_resolution_clock::now();
dxvk::high_resolution_clock::time_point m_lastUpdate
= dxvk::high_resolution_clock::now();
std::string m_allocatedString;
std::string m_mappedString;
std::string m_allocatedString;
std::string m_mappedString;
};
};
/**
* \brief HUD item to display amount of generated fixed function shaders
*/
class HudFixedFunctionShaders : public HudItem {
public:
/**
* \brief HUD item to display amount of generated fixed function shaders
*/
class HudFixedFunctionShaders : public HudItem {
HudFixedFunctionShaders(D3D9DeviceEx* device);
public:
void update(dxvk::high_resolution_clock::time_point time);
HudFixedFunctionShaders(D3D9DeviceEx* device);
HudPos render(
HudRenderer& renderer,
HudPos position);
void update(dxvk::high_resolution_clock::time_point time);
private:
HudPos render(
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
D3D9DeviceEx* m_device;
private:
std::string m_ffShaderCount;
D3D9DeviceEx* m_device;
};
std::string m_ffShaderCount;
/**
* \brief HUD item to whether or not we're in SWVP mode
*/
class HudSWVPState : public HudItem {
public:
};
HudSWVPState(D3D9DeviceEx* device);
void update(dxvk::high_resolution_clock::time_point time);
/**
* \brief HUD item to whether or not we're in SWVP mode
*/
class HudSWVPState : public HudItem {
HudPos render(
HudRenderer& renderer,
HudPos position);
public:
private:
HudSWVPState(D3D9DeviceEx* device);
D3D9DeviceEx* m_device;
void update(dxvk::high_resolution_clock::time_point time);
std::string m_isSWVPText;
HudPos render(
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
};
private:
D3D9DeviceEx* m_device;
std::string m_isSWVPText;
};
}

View File

@ -851,8 +851,10 @@ namespace dxvk {
m_wctx->imageViews.at(imageIndex), m_colorspace, dstRect,
swapImageView, m_colorspace, srcRect);
// if (m_hud != nullptr)
// m_hud->render(m_context, info.format, info.imageExtent);
if (m_hud) {
m_hud->render(m_context->beginExternalRendering(),
m_wctx->imageViews.at(imageIndex), m_colorspace);
}
m_blitter->endPresent(m_context->beginExternalRendering(),
m_wctx->imageViews.at(imageIndex));

View File

@ -8,112 +8,51 @@ namespace dxvk::hud {
const Rc<DxvkDevice>& device)
: m_device (device),
m_renderer (device),
m_hudItems (device),
m_scale (m_hudItems.getOption<float>("scale", 1.0f)),
m_opacity (m_hudItems.getOption<float>("opacity", 1.0f)) {
// Sanitize scaling factor
if (m_scale < 0.01f)
m_scale = 1.0f;
// Sanitize HUD opacity factor
if (m_opacity != 1.0f)
m_opacity = std::max(std::min(m_opacity, 1.0f), 0.1f);
// Set up constant state
m_rsState.polygonMode = VK_POLYGON_MODE_FILL;
m_rsState.cullMode = VK_CULL_MODE_BACK_BIT;
m_rsState.frontFace = VK_FRONT_FACE_CLOCKWISE;
m_rsState.depthClipEnable = VK_FALSE;
m_rsState.depthBiasEnable = VK_FALSE;
m_rsState.conservativeMode = VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT;
m_rsState.sampleCount = VK_SAMPLE_COUNT_1_BIT;
m_rsState.flatShading = VK_FALSE;
m_rsState.lineMode = VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT;
m_blendMode.enableBlending = VK_TRUE;
m_blendMode.colorSrcFactor = VK_BLEND_FACTOR_ONE;
m_blendMode.colorDstFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
m_blendMode.colorBlendOp = VK_BLEND_OP_ADD;
m_blendMode.alphaSrcFactor = VK_BLEND_FACTOR_ONE;
m_blendMode.alphaDstFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
m_blendMode.alphaBlendOp = VK_BLEND_OP_ADD;
m_blendMode.writeMask = VK_COLOR_COMPONENT_R_BIT
| VK_COLOR_COMPONENT_G_BIT
| VK_COLOR_COMPONENT_B_BIT
| VK_COLOR_COMPONENT_A_BIT;
m_hudItems (device) {
// Retrieve and sanitize options
m_options.scale = std::clamp(m_hudItems.getOption<float>("scale", 1.0f), 0.25f, 4.0f);
m_options.opacity = std::clamp(m_hudItems.getOption<float>("opacity", 1.0f), 0.1f, 1.0f);
addItem<HudVersionItem>("version", -1);
addItem<HudDeviceInfoItem>("devinfo", -1, m_device);
addItem<HudFpsItem>("fps", -1);
addItem<HudFrameTimeItem>("frametimes", -1);
addItem<HudFrameTimeItem>("frametimes", -1, device, &m_renderer);
addItem<HudSubmissionStatsItem>("submissions", -1, device);
addItem<HudDrawCallStatsItem>("drawcalls", -1, device);
addItem<HudPipelineStatsItem>("pipelines", -1, device);
addItem<HudDescriptorStatsItem>("descriptors", -1, device);
addItem<HudMemoryStatsItem>("memory", -1, device);
addItem<HudMemoryDetailsItem>("allocations", -1, device);
addItem<HudMemoryDetailsItem>("allocations", -1, device, &m_renderer);
addItem<HudCsThreadItem>("cs", -1, device);
addItem<HudGpuLoadItem>("gpuload", -1, device);
addItem<HudCompilerActivityItem>("compiler", -1, device);
}
Hud::~Hud() {
}
void Hud::update() {
m_hudItems.update();
}
void Hud::render(
const Rc<DxvkContext>& ctx,
VkSurfaceFormatKHR surfaceFormat,
VkExtent2D surfaceSize) {
this->setupRendererState(ctx, surfaceFormat, surfaceSize);
this->renderHudElements(ctx);
const DxvkContextObjects& ctx,
const Rc<DxvkImageView>& dstView,
VkColorSpaceKHR dstColorSpace) {
auto key = m_renderer.getPipelineKey(dstView, dstColorSpace);
m_renderer.beginFrame(ctx, dstView, dstColorSpace, m_options);
m_hudItems.render(ctx, key, m_options, m_renderer);
m_renderer.flushDraws(ctx, dstView, dstColorSpace, m_options);
}
Rc<Hud> Hud::createHud(const Rc<DxvkDevice>& device) {
return new Hud(device);
}
void Hud::setupRendererState(
const Rc<DxvkContext>& ctx,
VkSurfaceFormatKHR surfaceFormat,
VkExtent2D surfaceSize) {
VkColorSpaceKHR colorSpace = surfaceFormat.colorSpace;
if (lookupFormatInfo(surfaceFormat.format)->flags.test(DxvkFormatFlag::ColorSpaceSrgb))
colorSpace = VK_COLOR_SPACE_PASS_THROUGH_EXT;
VkViewport viewport;
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = float(surfaceSize.width);
viewport.height = float(surfaceSize.height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
VkRect2D scissor;
scissor.offset = { 0, 0 };
scissor.extent = surfaceSize;
ctx->setViewports(1, &viewport, &scissor);
ctx->setRasterizerState(m_rsState);
ctx->setBlendMode(0, m_blendMode);
ctx->setSpecConstant(VK_PIPELINE_BIND_POINT_GRAPHICS, 0, colorSpace);
m_renderer.beginFrame(ctx, surfaceSize, m_scale, m_opacity);
}
void Hud::renderHudElements(const Rc<DxvkContext>& ctx) {
m_hudItems.render(m_renderer);
}
}

View File

@ -6,15 +6,7 @@
#include "dxvk_hud_renderer.h"
namespace dxvk::hud {
/**
* \brief HUD uniform data
* Shader data for the HUD.
*/
struct HudUniformData {
VkExtent2D surfaceSize;
};
/**
* \brief DXVK HUD
*
@ -41,13 +33,14 @@ namespace dxvk::hud {
* \brief Render HUD
*
* Renders the HUD to the given context.
* \param [in] ctx Device context
* \param [in] surfaceSize Image size, in pixels
* \param [in] ctx Context objects for rendering
* \param [in] dstView Swap chain image view
* \param [in] dstColorSpace Color space
*/
void render(
const Rc<DxvkContext>& ctx,
VkSurfaceFormatKHR surfaceFormat,
VkExtent2D surfaceSize);
const DxvkContextObjects& ctx,
const Rc<DxvkImageView>& dstView,
VkColorSpaceKHR dstColorSpace);
/**
* \brief Adds a HUD item if enabled
@ -74,25 +67,12 @@ namespace dxvk::hud {
private:
const Rc<DxvkDevice> m_device;
Rc<DxvkDevice> m_device;
DxvkRasterizerState m_rsState;
DxvkBlendMode m_blendMode;
HudUniformData m_uniformData;
HudRenderer m_renderer;
HudItemSet m_hudItems;
float m_scale;
float m_opacity;
void setupRendererState(
const Rc<DxvkContext>& ctx,
VkSurfaceFormatKHR surfaceFormat,
VkExtent2D surfaceSize);
void renderHudElements(
const Rc<DxvkContext>& ctx);
HudOptions m_options;
};

View File

@ -2,7 +2,13 @@
#include <hud_chunk_frag_background.h>
#include <hud_chunk_frag_visualize.h>
#include <hud_chunk_vert.h>
#include <hud_chunk_vert_background.h>
#include <hud_chunk_vert_visualize.h>
#include <hud_frame_time_eval.h>
#include <hud_graph_frag.h>
#include <hud_graph_vert.h>
#include <iomanip>
#include <version.h>
@ -70,11 +76,15 @@ namespace dxvk::hud {
}
void HudItemSet::render(HudRenderer& renderer) {
HudPos position = { 8.0f, 8.0f };
void HudItemSet::render(
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer) {
HudPos position = { 8, 8 };
for (const auto& item : m_items)
position = item->render(renderer, position);
position = item->render(ctx, key, options, renderer, position);
}
@ -88,16 +98,15 @@ namespace dxvk::hud {
HudPos HudVersionItem::render(
HudRenderer& renderer,
HudPos position) {
position.y += 16.0f;
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position) {
position.y += 16;
renderer.drawText(16, position, 0xffffffffu, "DXVK " DXVK_VERSION);
renderer.drawText(16.0f,
{ position.x, position.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
"DXVK " DXVK_VERSION);
position.y += 8.0f;
position.y += 8;
return position;
}
@ -114,16 +123,15 @@ namespace dxvk::hud {
HudPos HudClientApiItem::render(
HudRenderer& renderer,
HudPos position) {
position.y += 16.0f;
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position) {
position.y += 16;
renderer.drawText(16, position, 0xffffffffu, m_api);
renderer.drawText(16.0f,
{ position.x, position.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
m_api);
position.y += 8.0f;
position.y += 8;
return position;
}
@ -148,27 +156,21 @@ namespace dxvk::hud {
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);
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position) {
position.y += 16;
renderer.drawText(16, position, 0xffffffffu, m_deviceName);
position.y += 24.0f;
renderer.drawText(16.0f,
{ position.x, position.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
m_driverName);
position.y += 24;
renderer.drawText(16, position, 0xffffffffu, m_driverName);
position.y += 20.0f;
renderer.drawText(16.0f,
{ position.x, position.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
m_driverVer);
position.y += 20;
renderer.drawText(16, position, 0xffffffffu, m_driverVer);
position.y += 8.0f;
position.y += 8;
return position;
}
@ -193,108 +195,536 @@ namespace dxvk::hud {
HudPos HudFpsItem::render(
HudRenderer& renderer,
HudPos position) {
position.y += 16.0f;
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position) {
position.y += 16;
renderer.drawText(16.0f,
{ position.x, position.y },
{ 1.0f, 0.25f, 0.25f, 1.0f },
"FPS:");
renderer.drawText(16, position, 0xff4040ffu, "FPS:");
renderer.drawText(16, { position.x + 60, position.y },
0xffffffffu, m_frameRate);
renderer.drawText(16.0f,
{ position.x + 60.0f, position.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
m_frameRate);
position.y += 8.0f;
position.y += 8;
return position;
}
HudFrameTimeItem::HudFrameTimeItem() { }
HudFrameTimeItem::~HudFrameTimeItem() { }
HudFrameTimeQueryPool::HudFrameTimeQueryPool(
const Rc<DxvkDevice>& device)
: m_vkd(device->vkd()) {
VkQueryPoolCreateInfo info = { VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO };
info.queryType = VK_QUERY_TYPE_TIMESTAMP;
info.queryCount = 1;
VkResult vr = m_vkd->vkCreateQueryPool(m_vkd->device(), &info, nullptr, &m_pool);
if (vr != VK_SUCCESS)
throw DxvkError(str::format("Failed to create frame time query pool: ", vr));
}
void HudFrameTimeItem::update(dxvk::high_resolution_clock::time_point time) {
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(time - m_lastUpdate);
HudFrameTimeQueryPool::~HudFrameTimeQueryPool() {
m_vkd->vkDestroyQueryPool(m_vkd->device(), m_pool, nullptr);
}
m_dataPoints[m_dataPointId] = float(elapsed.count());
m_dataPointId = (m_dataPointId + 1) % NumDataPoints;
m_lastUpdate = time;
HudFrameTimeItem::HudFrameTimeItem(const Rc<DxvkDevice>& device, HudRenderer* renderer)
: m_device (device),
m_gfxSetLayout (createDescriptorSetLayout()),
m_gfxPipelineLayout (createPipelineLayout()) {
createComputePipeline(*renderer);
renderer->createShaderModule(m_vs, VK_SHADER_STAGE_VERTEX_BIT, sizeof(hud_graph_vert), hud_graph_vert);
renderer->createShaderModule(m_fs, VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(hud_graph_frag), hud_graph_frag);
}
HudFrameTimeItem::~HudFrameTimeItem() {
auto vk = m_device->vkd();
for (const auto& p : m_gfxPipelines)
vk->vkDestroyPipeline(vk->device(), p.second, nullptr);
vk->vkDestroyShaderModule(vk->device(), m_vs.stageInfo.module, nullptr);
vk->vkDestroyShaderModule(vk->device(), m_fs.stageInfo.module, nullptr);
vk->vkDestroyPipeline(vk->device(), m_computePipeline, nullptr);
vk->vkDestroyPipelineLayout(vk->device(), m_computePipelineLayout, nullptr);
vk->vkDestroyDescriptorSetLayout(vk->device(), m_computeSetLayout, nullptr);
vk->vkDestroyPipelineLayout(vk->device(), m_gfxPipelineLayout, nullptr);
vk->vkDestroyDescriptorSetLayout(vk->device(), m_gfxSetLayout, nullptr);
}
HudPos HudFrameTimeItem::render(
HudRenderer& renderer,
HudPos position) {
std::array<HudGraphPoint, NumDataPoints> points;
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position) {
if (!m_gpuBuffer)
createResources(ctx);
// 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 hVal = std::log2(std::max((us - minUs) / targetUs + 1.0f, 1.0f))
/ std::log2((maxUs - minUs) / targetUs);
points[i].value = std::max(hVal, 1.0f / 40.0f);
points[i].color = color;
}
renderer.drawGraph(position,
HudPos { float(NumDataPoints), 40.0f },
points.size(), points.data());
position.y += 58.0f;
HudPos minPos = { 12, -128 };
HudPos maxPos = { 162, -128 };
renderer.drawText(12.0f,
{ position.x, position.y },
{ 1.0f, 0.25f, 0.25f, 1.0f },
"min:");
HudPos graphPos = { 8, -120 };
HudPos graphSize = { NumDataPoints, 80 };
uint32_t dataPoint = m_nextDataPoint++;
processFrameTimes(ctx, key, renderer,
dataPoint, minPos, maxPos);
drawFrameTimeGraph(ctx, key, renderer,
dataPoint, graphPos, graphSize);
if (m_nextDataPoint >= NumDataPoints)
m_nextDataPoint = 0u;
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;
}
void HudFrameTimeItem::processFrameTimes(
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
HudRenderer& renderer,
uint32_t dataPoint,
HudPos minPos,
HudPos maxPos) {
// Write current time stamp to the buffer
DxvkBufferSliceHandle sliceHandle = m_gpuBuffer->getSliceHandle();
ctx.cmd->cmdResetQueryPool(DxvkCmdBuffer::InitBuffer,
m_queryPool->handle(), 0, 1);
ctx.cmd->cmdWriteTimestamp(DxvkCmdBuffer::InitBuffer,
VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
m_queryPool->handle(), 0);
ctx.cmd->cmdCopyQueryPoolResults(DxvkCmdBuffer::InitBuffer,
m_queryPool->handle(), 0, 1, sliceHandle.handle,
sliceHandle.offset + (dataPoint & 1u) * sizeof(uint64_t), sizeof(uint64_t),
VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
VkMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 };
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
barrier.dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
VkDependencyInfo depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };
depInfo.memoryBarrierCount = 1u;
depInfo.pMemoryBarriers = &barrier;
ctx.cmd->cmdPipelineBarrier(DxvkCmdBuffer::InitBuffer, &depInfo);
// Process contents of the buffer and write out text draws
VkDescriptorSet set = ctx.descriptorPool->alloc(m_computeSetLayout);
auto bufferLayout = computeBufferLayout();
VkDescriptorBufferInfo frameTimeBuffer = m_gpuBuffer->getDescriptor(
0, bufferLayout.timestampSize).buffer;
VkDescriptorBufferInfo drawInfoBuffer = m_gpuBuffer->getDescriptor(
bufferLayout.drawInfoOffset, bufferLayout.drawInfoSize).buffer;
VkDescriptorBufferInfo drawParamBuffer = m_gpuBuffer->getDescriptor(
bufferLayout.drawParamOffset, bufferLayout.drawParamSize).buffer;
VkBufferView textBufferView = m_textView->handle();
std::array<VkWriteDescriptorSet, 4> descriptorWrites = {{
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
set, 0, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &frameTimeBuffer },
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
set, 1, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &drawParamBuffer },
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
set, 2, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &drawInfoBuffer },
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
set, 3, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, nullptr, nullptr, &textBufferView },
}};
ctx.cmd->updateDescriptorSets(
descriptorWrites.size(),
descriptorWrites.data());
ctx.cmd->cmdBindPipeline(DxvkCmdBuffer::InitBuffer,
VK_PIPELINE_BIND_POINT_COMPUTE, m_computePipeline);
ctx.cmd->cmdBindDescriptorSet(DxvkCmdBuffer::InitBuffer,
VK_PIPELINE_BIND_POINT_COMPUTE, m_computePipelineLayout,
set, 0, nullptr);
ComputePushConstants pushConstants = { };
pushConstants.msPerTick = m_device->properties().core.properties.limits.timestampPeriod / 1000000.0f;
pushConstants.dataPoint = dataPoint;
pushConstants.textPosMinX = minPos.x + 48;
pushConstants.textPosMinY = minPos.y;
pushConstants.textPosMaxX = maxPos.x + 48;
pushConstants.textPosMaxY = maxPos.y;
ctx.cmd->cmdPushConstants(DxvkCmdBuffer::InitBuffer,
m_computePipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT,
0, sizeof(pushConstants), &pushConstants);
ctx.cmd->cmdDispatch(DxvkCmdBuffer::InitBuffer, 1, 1, 1);
barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 };
barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
barrier.srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
barrier.dstAccessMask = m_gpuBuffer->info().access;
barrier.dstStageMask = m_gpuBuffer->info().stages;
ctx.cmd->cmdPipelineBarrier(DxvkCmdBuffer::InitBuffer, &depInfo);
// Display the min/max numbers
renderer.drawText(12, minPos, 0xff4040ff, "min:");
renderer.drawText(12, maxPos, 0xff4040ff, "max:");
renderer.drawTextIndirect(ctx, key, drawParamBuffer,
drawInfoBuffer, textBufferView, 2u);
// Make sure GPU resources are being kept alive as necessary
ctx.cmd->trackResource<DxvkAccess::Write>(m_gpuBuffer->getAllocation());
ctx.cmd->trackResource<DxvkAccess::Write>(m_queryPool);
}
void HudFrameTimeItem::drawFrameTimeGraph(
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
HudRenderer& renderer,
uint32_t dataPoint,
HudPos graphPos,
HudPos graphSize) {
ctx.cmd->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,
VK_PIPELINE_BIND_POINT_GRAPHICS, getPipeline(renderer, key));
auto set = ctx.descriptorPool->alloc(m_gfxSetLayout);
VkDescriptorBufferInfo bufferDescriptor = m_gpuBuffer->getDescriptor(0,
computeBufferLayout().timestampSize).buffer;
VkWriteDescriptorSet descriptorWrite = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
descriptorWrite.dstSet = set;
descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
descriptorWrite.descriptorCount = 1;
descriptorWrite.pBufferInfo = &bufferDescriptor;
ctx.cmd->updateDescriptorSets(1, &descriptorWrite);
ctx.cmd->cmdBindDescriptorSet(DxvkCmdBuffer::ExecBuffer,
VK_PIPELINE_BIND_POINT_GRAPHICS, m_gfxPipelineLayout,
set, 0, nullptr);
RenderPushConstants pushConstants = { };
pushConstants.hud = renderer.getPushConstants();
pushConstants.x = graphPos.x;
pushConstants.y = graphPos.y;
pushConstants.w = graphSize.x;
pushConstants.h = graphSize.y;
pushConstants.frameIndex = dataPoint;
ctx.cmd->cmdPushConstants(DxvkCmdBuffer::ExecBuffer, m_gfxPipelineLayout,
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
0, sizeof(pushConstants), &pushConstants);
ctx.cmd->cmdDraw(4, 1, 0, 0);
ctx.cmd->trackResource<DxvkAccess::Read>(m_gpuBuffer->getAllocation());
}
void HudFrameTimeItem::createResources(
const DxvkContextObjects& ctx) {
auto bufferLayout = computeBufferLayout();
DxvkBufferCreateInfo bufferInfo = { };
bufferInfo.size = bufferLayout.totalSize;
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT
| VK_BUFFER_USAGE_TRANSFER_SRC_BIT
| VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT
| VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
| VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT
| VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
bufferInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT
| VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT
| VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT
| VK_PIPELINE_STAGE_VERTEX_SHADER_BIT
| VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
bufferInfo.access = VK_ACCESS_TRANSFER_READ_BIT
| VK_ACCESS_TRANSFER_WRITE_BIT
| VK_ACCESS_INDIRECT_COMMAND_READ_BIT
| VK_ACCESS_SHADER_READ_BIT
| VK_ACCESS_SHADER_WRITE_BIT;
m_gpuBuffer = m_device->createBuffer(bufferInfo, VK_MEMORY_HEAP_DEVICE_LOCAL_BIT);
DxvkBufferViewKey textViewInfo = { };
textViewInfo.format = VK_FORMAT_R8_UINT;
textViewInfo.usage = VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
textViewInfo.offset = bufferLayout.textOffset;
textViewInfo.size = bufferLayout.textSize;
m_textView = m_gpuBuffer->createView(textViewInfo);
// Zero-init buffer so we don't display random garbage at the start
DxvkBufferSliceHandle bufferSlice = m_gpuBuffer->getSliceHandle();
ctx.cmd->cmdFillBuffer(DxvkCmdBuffer::InitBuffer,
bufferSlice.handle, bufferSlice.offset, bufferSlice.length, 0u);
VkMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 };
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
barrier.dstAccessMask = m_gpuBuffer->info().access;
barrier.dstStageMask = m_gpuBuffer->info().stages;
VkDependencyInfo depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };
depInfo.memoryBarrierCount = 1u;
depInfo.pMemoryBarriers = &barrier;
ctx.cmd->cmdPipelineBarrier(DxvkCmdBuffer::InitBuffer, &depInfo);
ctx.cmd->trackResource<DxvkAccess::Write>(m_gpuBuffer->getAllocation());
// We'll use and initialize this later as necessary
m_queryPool = new HudFrameTimeQueryPool(m_device);
}
void HudFrameTimeItem::createComputePipeline(
HudRenderer& renderer) {
auto vk = m_device->vkd();
std::array<VkDescriptorSetLayoutBinding, 4> bindings = {{
{ 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT },
{ 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT },
{ 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT },
{ 3, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT },
}};
VkDescriptorSetLayoutCreateInfo setLayoutInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
setLayoutInfo.bindingCount = bindings.size();
setLayoutInfo.pBindings = bindings.data();
VkResult vr = vk->vkCreateDescriptorSetLayout(vk->device(),
&setLayoutInfo, nullptr, &m_computeSetLayout);
if (vr != VK_SUCCESS)
throw DxvkError(str::format("Failed to create frame time compute set layout: ", vr));
VkPushConstantRange pushConstantRange = { };
pushConstantRange.offset = 0u;
pushConstantRange.size = sizeof(ComputePushConstants);
pushConstantRange.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
VkPipelineLayoutCreateInfo pipelineLayoutInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
pipelineLayoutInfo.setLayoutCount = 1u;
pipelineLayoutInfo.pSetLayouts = &m_computeSetLayout;
pipelineLayoutInfo.pushConstantRangeCount = 1;
pipelineLayoutInfo.pPushConstantRanges = &pushConstantRange;
vr = vk->vkCreatePipelineLayout(vk->device(),
&pipelineLayoutInfo, nullptr, &m_computePipelineLayout);
if (vr != VK_SUCCESS)
throw DxvkError(str::format("Failed to create frame time compute pipeline layout: ", vr));
HudShaderModule shader = { };
renderer.createShaderModule(shader, VK_SHADER_STAGE_COMPUTE_BIT,
sizeof(hud_frame_time_eval), hud_frame_time_eval);
VkComputePipelineCreateInfo info = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO };
info.stage = shader.stageInfo;
info.layout = m_computePipelineLayout;
info.basePipelineIndex = -1;
vr = vk->vkCreateComputePipelines(vk->device(),
VK_NULL_HANDLE, 1, &info, nullptr, &m_computePipeline);
if (vr != VK_SUCCESS)
throw DxvkError(str::format("Failed to create frame time compute pipeline: ", vr));
vk->vkDestroyShaderModule(vk->device(), shader.stageInfo.module, nullptr);
}
VkDescriptorSetLayout HudFrameTimeItem::createDescriptorSetLayout() {
auto vk = m_device->vkd();
std::array<VkDescriptorSetLayoutBinding, 1> bindings = {{
{ 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT },
}};
VkDescriptorSetLayoutCreateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
info.bindingCount = bindings.size();
info.pBindings = bindings.data();
VkDescriptorSetLayout layout = VK_NULL_HANDLE;
VkResult vr = vk->vkCreateDescriptorSetLayout(
vk->device(), &info, nullptr, &layout);
if (vr != VK_SUCCESS)
throw DxvkError(str::format("Failed to create frame time graphics pipeline descriptor set layout: ", vr));
return layout;
}
VkPipelineLayout HudFrameTimeItem::createPipelineLayout() {
auto vk = m_device->vkd();
VkPushConstantRange pushConstantRange = { };
pushConstantRange.offset = 0u;
pushConstantRange.size = sizeof(RenderPushConstants);
pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
VkPipelineLayoutCreateInfo info = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
info.setLayoutCount = 1u;
info.pSetLayouts = &m_gfxSetLayout;
info.pushConstantRangeCount = 1;
info.pPushConstantRanges = &pushConstantRange;
VkPipelineLayout layout = VK_NULL_HANDLE;
VkResult vr = vk->vkCreatePipelineLayout(
vk->device(), &info, nullptr, &layout);
if (vr != VK_SUCCESS)
throw DxvkError(str::format("Failed to create frame time graphics pipeline layout: ", vr));
return layout;
}
VkPipeline HudFrameTimeItem::getPipeline(
HudRenderer& renderer,
const HudPipelineKey& key) {
auto entry = m_gfxPipelines.find(key);
if (entry != m_gfxPipelines.end())
return entry->second;
VkPipeline pipeline = createPipeline(renderer, key);
m_gfxPipelines.insert({ key, pipeline });
return pipeline;
}
VkPipeline HudFrameTimeItem::createPipeline(
HudRenderer& renderer,
const HudPipelineKey& key) {
auto vk = m_device->vkd();
HudSpecConstants specConstants = renderer.getSpecConstants(key);
VkSpecializationInfo specInfo = renderer.getSpecInfo(&specConstants);
std::array<VkPipelineShaderStageCreateInfo, 2> stages = { };
stages[0] = m_vs.stageInfo;
stages[1] = m_fs.stageInfo;
stages[1].pSpecializationInfo = &specInfo;
VkPipelineRenderingCreateInfo rtInfo = { VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO };
rtInfo.colorAttachmentCount = 1;
rtInfo.pColorAttachmentFormats = &key.format;
VkPipelineVertexInputStateCreateInfo viState = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
VkPipelineInputAssemblyStateCreateInfo iaState = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
iaState.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
VkPipelineViewportStateCreateInfo vpState = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };
VkPipelineRasterizationStateCreateInfo rsState = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
rsState.cullMode = VK_CULL_MODE_NONE;
rsState.polygonMode = VK_POLYGON_MODE_FILL;
rsState.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rsState.lineWidth = 1.0f;
constexpr uint32_t sampleMask = 0x1;
VkPipelineMultisampleStateCreateInfo msState = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
msState.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
msState.pSampleMask = &sampleMask;
VkPipelineColorBlendAttachmentState cbAttachment = { };
cbAttachment.blendEnable = VK_TRUE;
cbAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
cbAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
cbAttachment.colorBlendOp = VK_BLEND_OP_ADD;
cbAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
cbAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
cbAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
cbAttachment.colorWriteMask =
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
VkPipelineColorBlendStateCreateInfo cbOpaqueState = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO };
cbOpaqueState.attachmentCount = 1;
cbOpaqueState.pAttachments = &cbAttachment;
static const std::array<VkDynamicState, 2> dynStates = {
VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT,
VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT,
};
VkPipelineDynamicStateCreateInfo dynState = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
dynState.dynamicStateCount = dynStates.size();
dynState.pDynamicStates = dynStates.data();
VkGraphicsPipelineCreateInfo info = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, &rtInfo };
info.stageCount = stages.size();
info.pStages = stages.data();
info.pVertexInputState = &viState;
info.pInputAssemblyState = &iaState;
info.pViewportState = &vpState;
info.pRasterizationState = &rsState;
info.pMultisampleState = &msState;
info.pColorBlendState = &cbOpaqueState;
info.pDynamicState = &dynState;
info.layout = m_gfxPipelineLayout;
info.basePipelineIndex = -1;
VkPipeline pipeline = { };
VkResult vr = vk->vkCreateGraphicsPipelines(vk->device(),
VK_NULL_HANDLE, 1, &info, nullptr, &pipeline);
if (vr != VK_SUCCESS)
throw DxvkError(str::format("Failed to create HUD memory detail pipeline 1: ", vr));
return pipeline;
}
HudFrameTimeItem::BufferLayout HudFrameTimeItem::computeBufferLayout() {
struct ComputeTimestampBuffer {
std::array<uint64_t, 2u> timestamps;
std::array<float, NumDataPoints> intervals;
float avgMs;
float minMs;
float maxMs;
};
BufferLayout result = { };
result.timestampSize = align(sizeof(ComputeTimestampBuffer), 256u);
result.drawInfoOffset = result.timestampSize;
result.drawInfoSize = align(sizeof(HudTextDrawInfo) * NumTextDraws, 256u);
result.drawParamOffset = result.drawInfoOffset + result.drawInfoSize;
result.drawParamSize = align(sizeof(VkDrawIndirectCommand) * NumTextDraws, 256u);
result.textOffset = result.drawParamOffset + result.drawParamSize;
result.textSize = 256u;
result.totalSize = result.textOffset + result.textSize;
return result;
}
HudSubmissionStatsItem::HudSubmissionStatsItem(const Rc<DxvkDevice>& device)
: m_device(device) {
@ -342,32 +772,20 @@ namespace dxvk::hud {
HudPos HudSubmissionStatsItem::render(
HudRenderer& renderer,
HudPos position) {
position.y += 16.0f;
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position) {
position.y += 16;
renderer.drawText(16, position, 0xff4080ff, "Queue submissions:");
renderer.drawText(16, { position.x + 228, position.y }, 0xffffffffu, m_submitString);
renderer.drawText(16.0f,
{ position.x, position.y },
{ 1.0f, 0.5f, 0.25f, 1.0f },
"Queue submissions:");
position.y += 20;
renderer.drawText(16, position, 0xff4080ff, "Queue syncs:");
renderer.drawText(16, { position.x + 228, position.y }, 0xffffffffu, m_syncString);
renderer.drawText(16.0f,
{ position.x + 228.0f, position.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
m_submitString);
position.y += 20.0f;
renderer.drawText(16.0f,
{ position.x, position.y },
{ 1.0f, 0.5f, 0.25f, 1.0f },
"Queue syncs:");
renderer.drawText(16.0f,
{ position.x + 228.0f, position.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
m_syncString);
position.y += 8.0f;
position.y += 8;
return position;
}
@ -403,53 +821,28 @@ namespace dxvk::hud {
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:");
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position) {
position.y += 16;
renderer.drawText(16, position, 0xffff8040, "Draw calls:");
renderer.drawText(16, { position.x + 192, position.y }, 0xffffffffu, str::format(m_gpCount));
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;
renderer.drawText(16, position, 0xffff8040, "Dispatch calls:");
renderer.drawText(16, { position.x + 192, position.y }, 0xffffffffu, str::format(m_cpCount));
position.y += 20.0f;
renderer.drawText(16.0f,
{ position.x, position.y },
{ 0.25f, 0.5f, 1.0f, 1.0f },
"Dispatch calls:");
position.y += 20;
renderer.drawText(16, position, 0xffff8040, "Render passes:");
renderer.drawText(16, { position.x + 192, position.y }, 0xffffffffu, str::format(m_rpCount));
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;
renderer.drawText(16, position, 0xffff8040, "Barriers:");
renderer.drawText(16, { position.x + 192, position.y }, 0xffffffffu, str::format(m_pbCount));
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 += 20.0f;
renderer.drawText(16.0f,
{ position.x, position.y },
{ 0.25f, 0.5f, 1.0f, 1.0f },
"Barriers:");
renderer.drawText(16.0f,
{ position.x + 192.0f, position.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
str::format(m_pbCount));
position.y += 8.0f;
position.y += 8;
return position;
}
@ -475,44 +868,26 @@ namespace dxvk::hud {
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));
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position) {
position.y += 16;
renderer.drawText(16, position, 0xffff40ff, "Graphics pipelines:");
renderer.drawText(16, { position.x + 240, position.y }, 0xffffffffu, str::format(m_graphicsPipelines));
if (m_graphicsLibraries) {
position.y += 20.0f;
renderer.drawText(16.0f,
{ position.x, position.y },
{ 1.0f, 0.25f, 1.0f, 1.0f },
"Graphics shaders:");
renderer.drawText(16.0f,
{ position.x + 240.0f, position.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
str::format(m_graphicsLibraries));
position.y += 20;
renderer.drawText(16, position, 0xffff40ff, "Graphics shaders:");
renderer.drawText(16, { position.x + 240, position.y }, 0xffffffffu, str::format(m_graphicsLibraries));
}
position.y += 20.0f;
renderer.drawText(16.0f,
{ position.x, position.y },
{ 1.0f, 0.25f, 1.0f, 1.0f },
"Compute pipelines:");
position.y += 20;
renderer.drawText(16, position, 0xffff40ff, "Compute shaders:");
renderer.drawText(16, { position.x + 240, position.y }, 0xffffffffu, str::format(m_computePipelines));
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;
position.y += 8;
return position;
}
@ -537,31 +912,20 @@ namespace dxvk::hud {
HudPos HudDescriptorStatsItem::render(
HudRenderer& renderer,
HudPos position) {
position.y += 16.0f;
renderer.drawText(16.0f,
{ position.x, position.y },
{ 1.0f, 0.25f, 0.5f, 1.0f },
"Descriptor pools:");
renderer.drawText(16.0f,
{ position.x + 216.0f, position.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
str::format(m_descriptorPoolCount));
position.y += 20.0f;
renderer.drawText(16.0f,
{ position.x, position.y },
{ 1.0f, 0.25f, 0.5f, 1.0f },
"Descriptor sets:");
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position) {
position.y += 16;
renderer.drawText(16, position, 0xff8040ff, "Descriptor pools:");
renderer.drawText(16, { position.x + 216, position.y }, 0xffffffffu, str::format(m_descriptorPoolCount));
renderer.drawText(16.0f,
{ position.x + 216.0f, position.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
str::format(m_descriptorSetCount));
position.y += 20;
renderer.drawText(16, position, 0xff8040ff, "Descriptor sets:");
renderer.drawText(16, { position.x + 216, position.y }, 0xffffffffu, str::format(m_descriptorSetCount));
position.y += 8.0f;
position.y += 8;
return position;
}
@ -584,8 +948,11 @@ namespace dxvk::hud {
HudPos HudMemoryStatsItem::render(
HudRenderer& renderer,
HudPos position) {
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
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;
@ -599,57 +966,49 @@ namespace dxvk::hud {
std::string text = str::format(std::setfill(' '), std::setw(5), memAllocatedMib, " MB (", percentage, "%) ",
std::setw(5 + (percentage < 10 ? 1 : 0) + (percentage < 100 ? 1 : 0)), memUsedMib, " MB used");
position.y += 16.0f;
renderer.drawText(16.0f,
{ position.x, position.y },
{ 1.0f, 1.0f, 0.25f, 1.0f },
label);
position.y += 16;
renderer.drawText(16, position, 0xff40ffffu, label);
renderer.drawText(16, { position.x + 168, position.y }, 0xffffffffu, text);
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;
}
position.y += 4.0f;
position.y += 4;
return position;
}
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(
const Rc<DxvkDevice>& device,
HudRenderer* renderer)
: m_device (device),
m_setLayout (createSetLayout()),
m_pipelineLayout (createPipelineLayout()) {
renderer->createShaderModule(m_fsBackground, VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(hud_chunk_frag_background), hud_chunk_frag_background);
renderer->createShaderModule(m_vsBackground, VK_SHADER_STAGE_VERTEX_BIT, sizeof(hud_chunk_vert_background), hud_chunk_vert_background);
renderer->createShaderModule(m_fsVisualize, VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(hud_chunk_frag_visualize), hud_chunk_frag_visualize);
renderer->createShaderModule(m_vsVisualize, VK_SHADER_STAGE_VERTEX_BIT, sizeof(hud_chunk_vert_visualize), hud_chunk_vert_visualize);
}
HudMemoryDetailsItem::~HudMemoryDetailsItem() {
auto vk = m_device->vkd();
for (const auto& p : m_pipelines) {
vk->vkDestroyPipeline(vk->device(), p.second.background, nullptr);
vk->vkDestroyPipeline(vk->device(), p.second.visualize, nullptr);
}
vk->vkDestroyShaderModule(vk->device(), m_vsBackground.stageInfo.module, nullptr);
vk->vkDestroyShaderModule(vk->device(), m_fsBackground.stageInfo.module, nullptr);
vk->vkDestroyShaderModule(vk->device(), m_vsVisualize.stageInfo.module, nullptr);
vk->vkDestroyShaderModule(vk->device(), m_fsVisualize.stageInfo.module, nullptr);
vk->vkDestroyPipelineLayout(vk->device(), m_pipelineLayout, nullptr);
vk->vkDestroyDescriptorSetLayout(vk->device(), m_setLayout, nullptr);
}
@ -666,203 +1025,379 @@ namespace dxvk::hud {
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;
}
if (m_displayCacheStats)
pos.y -= 20.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;
}
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position) {
// Layout, align the entire element to the bottom right.
int32_t x = -564;
int32_t y = -20;
if (m_displayCacheStats) {
uint32_t hitCount = m_cacheStats.requestCount - m_cacheStats.missCount;
uint32_t hitRate = (100 * hitCount) / std::max(m_cacheStats.requestCount, 1u);
std::string cacheStr = str::format("Cache: ", m_cacheStats.size >> 10, " kB (", hitRate, "% hit)");
renderer.drawText(14, { x, y }, 0xffffffffu, cacheStr);
renderer.drawText(14.0f,
{ pos.x, pos.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
cacheStr);
y -= 24;
}
for (uint32_t i = m_stats.memoryTypes.size(); i; i--) {
const auto& type = m_stats.memoryTypes.at(i - 1);
if (!type.allocated)
continue;
// Compute layout and gather memory stats
DxvkMemoryStats stats = { };
int32_t w = 0;
int32_t h = 0;
for (uint32_t j = 0; j < type.chunkCount; j++) {
const auto& chunk = m_stats.chunks.at(type.chunkIndex + j);
stats.memoryAllocated += chunk.capacity;
stats.memoryUsed += chunk.used;
int32_t chunkWidth = (chunk.pageCount + 15u) / 16u + 2;
if (x + w + chunkWidth > -8) {
w = 0;
h += 34;
}
w += chunkWidth + 6;
}
if (w)
h += 34;
y -= h;
w = 0;
h = 8;
// Draw individual chunks
for (uint32_t j = 0; j < type.chunkCount; j++) {
const auto& chunk = m_stats.chunks.at(type.chunkIndex + j);
// Default VRAM, blue
uint32_t color = 0xff804020u;
if (type.properties.propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) {
// Cached memory, green
color = 0xff208020u;
} else if (!(type.properties.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) {
if (!chunk.mapped) {
// Fallback allocation, grey
color = 0xff202020u;
} else {
// Uncached memory, red
color = 0xff202080u;
}
} else if (chunk.mapped) {
// Host-visible VRAM, yellow
color = 0xff208080u;
}
int32_t chunkWidth = (chunk.pageCount + 15u) / 16u + 2;
if (x + w + chunkWidth > -8) {
w = 0;
h += 34;
}
drawChunk({ x + w, y + h }, { chunkWidth, 24 }, color, chunk);
w += chunkWidth + 6;
}
// Render descriptive text
std::string headline = str::format("Mem type ", i, " [", type.properties.heapIndex, "]: ",
type.chunkCount, " chunk", type.chunkCount != 1u ? "s" : "", " (", (stats.memoryAllocated >> 20u), " MB, ",
((stats.memoryUsed >= (1u << 20u)) ? stats.memoryUsed >> 20 : stats.memoryUsed >> 10),
(stats.memoryUsed >= (1u << 20u) ? " MB" : " kB"), " used)");
renderer.drawText(14, { x, y }, 0xffffffffu, headline);
y -= 24;
}
flushDraws(ctx, key, options, renderer);
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);
DxvkBufferViewKey viewInfo = { };
viewInfo.format = VK_FORMAT_UNDEFINED;
viewInfo.offset = 0;
viewInfo.size = info.size;
viewInfo.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
m_pageMaskView = m_pageMaskBuffer->createView(viewInfo);
}
if (!m_stats.pageMasks.empty()) {
context->invalidateBuffer(m_pageMaskBuffer, m_pageMaskBuffer->allocateSlice());
std::memcpy(m_pageMaskBuffer->mapPtr(0), &m_stats.pageMasks.at(0), size);
}
void HudMemoryDetailsItem::drawChunk(
HudPos pos,
HudPos size,
uint32_t color,
const DxvkMemoryChunkStats& chunk) {
auto& draw = m_drawInfos.emplace_back();
draw.x = pos.x;
draw.y = pos.y;
draw.w = size.x;
draw.h = size.y;
draw.pageMask = chunk.pageMaskOffset;
draw.pageCount = chunk.pageCount;
draw.color = color;
}
void HudMemoryDetailsItem::drawChunk(
HudRenderer& renderer,
HudPos pos,
HudPos size,
const VkMemoryType& memoryType,
const DxvkMemoryChunkStats& stats) const {
DxvkContext* context = renderer.getContext();
VkExtent2D surfaceSize = renderer.surfaceSize();
void HudMemoryDetailsItem::flushDraws(
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer) {
if (m_drawInfos.empty())
return;
static const DxvkInputAssemblyState iaState = {
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
VK_FALSE, 0 };
PipelinePair pipelines = getPipeline(renderer, key);
context->setInputAssemblyState(iaState);
context->bindResourceBufferView(VK_SHADER_STAGE_FRAGMENT_BIT, 0, Rc<DxvkBufferView>(m_pageMaskView));
ctx.cmd->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,
VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.background);
context->bindShader<VK_SHADER_STAGE_VERTEX_BIT>(Rc<DxvkShader>(m_vs));
context->bindShader<VK_SHADER_STAGE_FRAGMENT_BIT>(Rc<DxvkShader>(m_fsBackground));
// Bind resources
VkDescriptorSet set = ctx.descriptorPool->alloc(m_setLayout);
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;
VkDescriptorBufferInfo drawDescriptor = { };
VkDescriptorBufferInfo dataDescriptor = { };
context->pushConstants(0, sizeof(args), &args);
context->draw(4, 1, 0, 0);
updateDataBuffer(drawDescriptor, dataDescriptor);
context->bindShader<VK_SHADER_STAGE_FRAGMENT_BIT>(Rc<DxvkShader>(m_fsVisualize));
std::array<VkWriteDescriptorSet, 2> descriptorWrites = {{
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
set, 0, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &drawDescriptor },
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
set, 1, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &dataDescriptor },
}};
args.pos = pos;
args.size = size;
ctx.cmd->updateDescriptorSets(
descriptorWrites.size(),
descriptorWrites.data());
if (memoryType.propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) {
args.color = 0xff208020u;
} else if (!(memoryType.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) {
if (!stats.mapped)
args.color = 0xff202020u;
else
args.color = 0xff202080u;
} else if (stats.mapped) {
args.color = 0xff208080u;
ctx.cmd->cmdBindDescriptorSet(DxvkCmdBuffer::ExecBuffer,
VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout,
set, 0, nullptr);
HudPushConstants pushConstants = renderer.getPushConstants();
ctx.cmd->cmdPushConstants(DxvkCmdBuffer::ExecBuffer, m_pipelineLayout,
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
0, sizeof(pushConstants), &pushConstants);
ctx.cmd->cmdDraw(4, m_drawInfos.size(), 0, 0);
ctx.cmd->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,
VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.visualize);
ctx.cmd->cmdDraw(4, m_drawInfos.size(), 0, 0);
// Track data buffer lifetime
ctx.cmd->trackResource<DxvkAccess::Read>(m_dataBuffer->getAllocation());
m_drawInfos.clear();
}
void HudMemoryDetailsItem::updateDataBuffer(
VkDescriptorBufferInfo& drawDescriptor,
VkDescriptorBufferInfo& dataDescriptor) {
size_t drawInfoSize = m_drawInfos.size() * sizeof(DrawInfo);
size_t drawInfoSizeAligned = align(drawInfoSize, 256u);
size_t chunkDataSize = m_stats.pageMasks.size() * sizeof(uint32_t);
size_t chunkDataSizeAligned = align(chunkDataSize, 256u);
size_t bufferSize = align(drawInfoSizeAligned + chunkDataSizeAligned, 2048u);
if (!m_dataBuffer || m_dataBuffer->info().size < bufferSize) {
DxvkBufferCreateInfo bufferInfo;
bufferInfo.size = bufferSize;
bufferInfo.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
bufferInfo.access = VK_ACCESS_SHADER_READ_BIT;
bufferInfo.stages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
m_dataBuffer = m_device->createBuffer(bufferInfo,
VK_MEMORY_HEAP_DEVICE_LOCAL_BIT |
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
} else {
args.color = 0xff804020u;
// Ensure we can update the buffer without overriding live data
m_dataBuffer->assignSlice(m_dataBuffer->allocateSlice());
}
context->pushConstants(0, sizeof(args), &args);
context->draw(4, 1, 0, 0);
// Update draw infos and pad unused area with zeroes
std::memcpy(m_dataBuffer->mapPtr(0), m_drawInfos.data(), drawInfoSize);
std::memset(m_dataBuffer->mapPtr(drawInfoSize), 0, drawInfoSizeAligned - drawInfoSize);
// Update chunk data and pad with zeroes
std::memcpy(m_dataBuffer->mapPtr(drawInfoSizeAligned), m_stats.pageMasks.data(), chunkDataSize);
std::memset(m_dataBuffer->mapPtr(drawInfoSizeAligned + chunkDataSize), 0, chunkDataSizeAligned - chunkDataSize);
// Write back descriptors
drawDescriptor = m_dataBuffer->getDescriptor(0, drawInfoSizeAligned).buffer;
dataDescriptor = m_dataBuffer->getDescriptor(drawInfoSizeAligned, chunkDataSizeAligned).buffer;
}
VkDescriptorSetLayout HudMemoryDetailsItem::createSetLayout() {
auto vk = m_device->vkd();
static const std::array<VkDescriptorSetLayoutBinding, 2> bindings = {{
{ 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT },
{ 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT },
}};
VkDescriptorSetLayoutCreateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
info.bindingCount = bindings.size();
info.pBindings = bindings.data();
VkDescriptorSetLayout layout = VK_NULL_HANDLE;
VkResult vr = vk->vkCreateDescriptorSetLayout(vk->device(), &info, nullptr, &layout);
if (vr != VK_SUCCESS)
throw DxvkError(str::format("Failed to create HUD descriptor set layout: ", vr));
return layout;
}
VkPipelineLayout HudMemoryDetailsItem::createPipelineLayout() {
auto vk = m_device->vkd();
VkPushConstantRange pushConstantRange = { };
pushConstantRange.offset = 0u;
pushConstantRange.size = sizeof(HudPushConstants);
pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
VkPipelineLayoutCreateInfo info = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
info.setLayoutCount = 1;
info.pSetLayouts = &m_setLayout;
info.pushConstantRangeCount = 1;
info.pPushConstantRanges = &pushConstantRange;
VkPipelineLayout layout = VK_NULL_HANDLE;
VkResult vr = vk->vkCreatePipelineLayout(vk->device(), &info, nullptr, &layout);
if (vr != VK_SUCCESS)
throw DxvkError(str::format("Failed to create HUD descriptor set layout: ", vr));
return layout;
}
HudMemoryDetailsItem::PipelinePair HudMemoryDetailsItem::createPipeline(
HudRenderer& renderer,
const HudPipelineKey& key) {
auto vk = m_device->vkd();
HudSpecConstants specConstants = renderer.getSpecConstants(key);
VkSpecializationInfo specInfo = renderer.getSpecInfo(&specConstants);
std::array<VkPipelineShaderStageCreateInfo, 2> backgroundStages = { };
backgroundStages[0] = m_vsBackground.stageInfo;
backgroundStages[1] = m_fsBackground.stageInfo;
backgroundStages[1].pSpecializationInfo = &specInfo;
std::array<VkPipelineShaderStageCreateInfo, 2> visualizeStages = { };
visualizeStages[0] = m_vsVisualize.stageInfo;
visualizeStages[1] = m_fsVisualize.stageInfo;
visualizeStages[1].pSpecializationInfo = &specInfo;
VkPipelineRenderingCreateInfo rtInfo = { VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO };
rtInfo.colorAttachmentCount = 1;
rtInfo.pColorAttachmentFormats = &key.format;
VkPipelineVertexInputStateCreateInfo viState = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
VkPipelineInputAssemblyStateCreateInfo iaState = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
iaState.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
VkPipelineViewportStateCreateInfo vpState = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };
VkPipelineRasterizationStateCreateInfo rsState = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
rsState.cullMode = VK_CULL_MODE_NONE;
rsState.polygonMode = VK_POLYGON_MODE_FILL;
rsState.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rsState.lineWidth = 1.0f;
constexpr uint32_t sampleMask = 0x1;
VkPipelineMultisampleStateCreateInfo msState = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
msState.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
msState.pSampleMask = &sampleMask;
VkPipelineColorBlendAttachmentState cbAttachment = { };
cbAttachment.blendEnable = VK_TRUE;
cbAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
cbAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
cbAttachment.colorBlendOp = VK_BLEND_OP_ADD;
cbAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
cbAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
cbAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
cbAttachment.colorWriteMask =
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
VkPipelineColorBlendStateCreateInfo cbOpaqueState = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO };
cbOpaqueState.attachmentCount = 1;
cbOpaqueState.pAttachments = &cbAttachment;
static const std::array<VkDynamicState, 2> dynStates = {
VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT,
VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT,
};
VkPipelineDynamicStateCreateInfo dynState = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
dynState.dynamicStateCount = dynStates.size();
dynState.pDynamicStates = dynStates.data();
VkGraphicsPipelineCreateInfo info = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, &rtInfo };
info.stageCount = backgroundStages.size();
info.pStages = backgroundStages.data();
info.pVertexInputState = &viState;
info.pInputAssemblyState = &iaState;
info.pViewportState = &vpState;
info.pRasterizationState = &rsState;
info.pMultisampleState = &msState;
info.pColorBlendState = &cbOpaqueState;
info.pDynamicState = &dynState;
info.layout = m_pipelineLayout;
info.basePipelineIndex = -1;
PipelinePair pipelines = { };
VkResult vr = vk->vkCreateGraphicsPipelines(vk->device(), VK_NULL_HANDLE,
1, &info, nullptr, &pipelines.background);
if (vr != VK_SUCCESS)
throw DxvkError(str::format("Failed to create HUD memory detail pipeline 1: ", vr));
info.stageCount = visualizeStages.size();
info.pStages = visualizeStages.data();
vr = vk->vkCreateGraphicsPipelines(vk->device(), VK_NULL_HANDLE,
1, &info, nullptr, &pipelines.visualize);
if (vr != VK_SUCCESS)
throw DxvkError(str::format("Failed to create HUD memory detail pipeline 2: ", vr));
return pipelines;
}
HudMemoryDetailsItem::PipelinePair HudMemoryDetailsItem::getPipeline(
HudRenderer& renderer,
const HudPipelineKey& key) {
auto entry = m_pipelines.find(key);
if (entry != m_pipelines.end())
return entry->second;
PipelinePair pipeline = createPipeline(renderer, key);
m_pipelines.insert({ key, pipeline });
return pipeline;
}
@ -918,31 +1453,20 @@ namespace dxvk::hud {
HudPos HudCsThreadItem::render(
HudRenderer& renderer,
HudPos position) {
position.y += 16.0f;
renderer.drawText(16.0f,
{ position.x, position.y },
{ 0.25f, 1.0f, 0.25f, 1.0f },
"CS chunks:");
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position) {
position.y += 16;
renderer.drawText(16, position, 0xff40ff40, "CS chunks:");
renderer.drawText(16, { position.x + 132, position.y }, 0xffffffffu, m_csChunkString);
renderer.drawText(16.0f,
{ position.x + 132.0f, position.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
m_csChunkString);
position.y += 20;
renderer.drawText(16, position, 0xff40ff40, "CS syncs:");
renderer.drawText(16, { position.x + 132, position.y }, 0xffffffffu, m_csSyncString);
position.y += 20.0f;
renderer.drawText(16.0f,
{ position.x, position.y },
{ 0.25f, 1.0f, 0.25f, 1.0f },
"CS syncs:");
renderer.drawText(16.0f,
{ position.x + 132.0f, position.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
m_csSyncString);
position.y += 8.0f;
position.y += 8;
return position;
}
@ -979,21 +1503,16 @@ namespace dxvk::hud {
HudPos HudGpuLoadItem::render(
HudRenderer& renderer,
HudPos position) {
position.y += 16.0f;
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position) {
position.y += 16;
renderer.drawText(16, position, 0xff408040u, "GPU:");
renderer.drawText(16, { position.x + 60, position.y }, 0xffffffffu, m_gpuLoadString);
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;
position.y += 8;
return position;
}
@ -1047,18 +1566,18 @@ namespace dxvk::hud {
HudPos HudCompilerActivityItem::render(
HudRenderer& renderer,
HudPos position) {
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position) {
if (m_show) {
std::string string = "Compiling shaders...";
if (m_showPercentage)
string = str::format(string, " (", computePercentage(), "%)");
renderer.drawText(16.0f,
{ position.x, float(renderer.surfaceSize().height) / renderer.scale() - 20.0f },
{ 1.0f, 1.0f, 1.0f, 1.0f },
string);
renderer.drawText(16, { position.x, -20 }, 0xffffffffu, string);
}
return position;

View File

@ -33,13 +33,18 @@ namespace dxvk::hud {
/**
* \brief Renders the HUD
*
* \param [in] renderer HUD renderer
* \param [in] ctx Raw context objects
* \param [in] options HUD options
* \param [in] renderer HUD renderer for text rendering
* \param [in] position Base offset
* \returns Base offset for next item
*/
virtual HudPos render(
HudRenderer& renderer,
HudPos position) = 0;
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position) = 0;
};
@ -70,7 +75,10 @@ namespace dxvk::hud {
* \returns Base offset for next item
*/
void render(
HudRenderer& renderer);
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer);
/**
* \brief Creates a HUD item if enabled
@ -129,8 +137,11 @@ namespace dxvk::hud {
public:
HudPos render(
HudRenderer& renderer,
HudPos position);
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
};
@ -147,8 +158,11 @@ namespace dxvk::hud {
~HudClientApiItem();
HudPos render(
HudRenderer& renderer,
HudPos position);
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
private:
@ -169,8 +183,11 @@ namespace dxvk::hud {
~HudDeviceInfoItem();
HudPos render(
HudRenderer& renderer,
HudPos position);
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
private:
@ -195,8 +212,11 @@ namespace dxvk::hud {
void update(dxvk::high_resolution_clock::time_point time);
HudPos render(
HudRenderer& renderer,
HudPos position);
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
private:
@ -210,29 +230,136 @@ namespace dxvk::hud {
/**
* \brief HUD item to display the frame rate
* \brief Trackable GPU query pool
*/
class HudFrameTimeItem : public HudItem {
constexpr static size_t NumDataPoints = 304;
class HudFrameTimeQueryPool : public DxvkResource {
public:
HudFrameTimeItem();
HudFrameTimeQueryPool(
const Rc<DxvkDevice>& device);
~HudFrameTimeItem();
~HudFrameTimeQueryPool();
void update(dxvk::high_resolution_clock::time_point time);
HudPos render(
HudRenderer& renderer,
HudPos position);
VkQueryPool handle() const {
return m_pool;
}
private:
dxvk::high_resolution_clock::time_point m_lastUpdate
= dxvk::high_resolution_clock::now();
Rc<vk::DeviceFn> m_vkd;
VkQueryPool m_pool = VK_NULL_HANDLE;
std::array<float, NumDataPoints> m_dataPoints = {};
uint32_t m_dataPointId = 0;
};
/**
* \brief HUD item to display the frame rate
*/
class HudFrameTimeItem : public HudItem {
constexpr static size_t NumDataPoints = 420u;
constexpr static size_t NumTextDraws = 2u;
public:
HudFrameTimeItem(
const Rc<DxvkDevice>& device,
HudRenderer* renderer);
~HudFrameTimeItem();
HudPos render(
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
private:
struct ComputePushConstants {
float msPerTick;
uint32_t dataPoint;
int16_t textPosMinX;
int16_t textPosMinY;
int16_t textPosMaxX;
int16_t textPosMaxY;
};
struct RenderPushConstants {
HudPushConstants hud;
int16_t x;
int16_t y;
int16_t w;
int16_t h;
uint32_t frameIndex;
};
struct BufferLayout {
size_t timestampSize;
size_t drawInfoOffset;
size_t drawInfoSize;
size_t drawParamOffset;
size_t drawParamSize;
size_t textOffset;
size_t textSize;
size_t totalSize;
};
Rc<DxvkDevice> m_device;
Rc<DxvkBuffer> m_gpuBuffer;
Rc<DxvkBufferView> m_textView;
Rc<HudFrameTimeQueryPool> m_queryPool;
VkDescriptorSetLayout m_computeSetLayout = VK_NULL_HANDLE;
VkPipelineLayout m_computePipelineLayout = VK_NULL_HANDLE;
VkPipeline m_computePipeline = VK_NULL_HANDLE;
HudShaderModule m_vs;
HudShaderModule m_fs;
VkDescriptorSetLayout m_gfxSetLayout = VK_NULL_HANDLE;
VkPipelineLayout m_gfxPipelineLayout = VK_NULL_HANDLE;
std::unordered_map<HudPipelineKey,
VkPipeline, DxvkHash, DxvkEq> m_gfxPipelines;
uint32_t m_nextDataPoint = 0u;
void processFrameTimes(
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
HudRenderer& renderer,
uint32_t dataPoint,
HudPos minPos,
HudPos maxPos);
void drawFrameTimeGraph(
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
HudRenderer& renderer,
uint32_t dataPoint,
HudPos graphPos,
HudPos graphSize);
void createResources(
const DxvkContextObjects& ctx);
void createComputePipeline(
HudRenderer& renderer);
VkDescriptorSetLayout createDescriptorSetLayout();
VkPipelineLayout createPipelineLayout();
VkPipeline getPipeline(
HudRenderer& renderer,
const HudPipelineKey& key);
VkPipeline createPipeline(
HudRenderer& renderer,
const HudPipelineKey& key);
static BufferLayout computeBufferLayout();
};
@ -251,8 +378,11 @@ namespace dxvk::hud {
void update(dxvk::high_resolution_clock::time_point time);
HudPos render(
HudRenderer& renderer,
HudPos position);
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
private:
@ -289,8 +419,11 @@ namespace dxvk::hud {
void update(dxvk::high_resolution_clock::time_point time);
HudPos render(
HudRenderer& renderer,
HudPos position);
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
private:
@ -323,8 +456,11 @@ namespace dxvk::hud {
void update(dxvk::high_resolution_clock::time_point time);
HudPos render(
HudRenderer& renderer,
HudPos position);
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
private:
@ -351,8 +487,11 @@ namespace dxvk::hud {
void update(dxvk::high_resolution_clock::time_point time);
HudPos render(
HudRenderer& renderer,
HudPos position);
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
private:
@ -378,8 +517,11 @@ namespace dxvk::hud {
void update(dxvk::high_resolution_clock::time_point time);
HudPos render(
HudRenderer& renderer,
HudPos position);
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
private:
@ -397,26 +539,36 @@ namespace dxvk::hud {
constexpr static int64_t UpdateInterval = 500'000;
public:
HudMemoryDetailsItem(const Rc<DxvkDevice>& device);
HudMemoryDetailsItem(
const Rc<DxvkDevice>& device,
HudRenderer* renderer);
~HudMemoryDetailsItem();
void update(dxvk::high_resolution_clock::time_point time);
HudPos render(
HudRenderer& renderer,
HudPos position);
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
private:
struct ShaderArgs {
HudPos pos;
HudPos size;
HudPos scale;
float opacity;
struct DrawInfo {
int16_t x;
int16_t y;
int16_t w;
int16_t h;
uint16_t pageMask;
uint16_t pageCount;
uint32_t color;
uint32_t maskIndex;
uint32_t pageCount;
};
struct PipelinePair {
VkPipeline background = VK_NULL_HANDLE;
VkPipeline visualize = VK_NULL_HANDLE;
};
Rc<DxvkDevice> m_device;
@ -425,24 +577,50 @@ namespace dxvk::hud {
high_resolution_clock::time_point m_lastUpdate = { };
bool m_displayCacheStats = false;
bool m_displayCacheStats = false;
Rc<DxvkShader> m_vs;
Rc<DxvkShader> m_fsBackground;
Rc<DxvkShader> m_fsVisualize;
Rc<DxvkBuffer> m_dataBuffer;
std::vector<DrawInfo> m_drawInfos;
Rc<DxvkBuffer> m_pageMaskBuffer;
Rc<DxvkBufferView> m_pageMaskView;
HudShaderModule m_vsBackground;
HudShaderModule m_fsBackground;
void uploadChunkData(
HudRenderer& renderer);
HudShaderModule m_vsVisualize;
HudShaderModule m_fsVisualize;
VkDescriptorSetLayout m_setLayout = VK_NULL_HANDLE;
VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE;
std::unordered_map<HudPipelineKey,
PipelinePair, DxvkHash, DxvkEq> m_pipelines;
void drawChunk(
HudRenderer& renderer,
HudPos pos,
HudPos size,
const VkMemoryType& memoryType,
const DxvkMemoryChunkStats& stats) const;
HudPos pos,
HudPos size,
uint32_t color,
const DxvkMemoryChunkStats& chunk);
void flushDraws(
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer);
void updateDataBuffer(
VkDescriptorBufferInfo& drawDescriptor,
VkDescriptorBufferInfo& dataDescriptor);
VkDescriptorSetLayout createSetLayout();
VkPipelineLayout createPipelineLayout();
PipelinePair createPipeline(
HudRenderer& renderer,
const HudPipelineKey& key);
PipelinePair getPipeline(
HudRenderer& renderer,
const HudPipelineKey& key);
};
@ -461,8 +639,11 @@ namespace dxvk::hud {
void update(dxvk::high_resolution_clock::time_point time);
HudPos render(
HudRenderer& renderer,
HudPos position);
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
private:
@ -500,8 +681,11 @@ namespace dxvk::hud {
void update(dxvk::high_resolution_clock::time_point time);
HudPos render(
HudRenderer& renderer,
HudPos position);
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
private:
@ -532,8 +716,11 @@ namespace dxvk::hud {
void update(dxvk::high_resolution_clock::time_point time);
HudPos render(
HudRenderer& renderer,
HudPos position);
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
private:

View File

@ -1,364 +1,634 @@
#include "dxvk_hud_renderer.h"
#include <hud_graph_frag.h>
#include <hud_graph_vert.h>
#include <hud_text_frag.h>
#include <hud_text_vert.h>
namespace dxvk::hud {
HudRenderer::HudRenderer(const Rc<DxvkDevice>& device)
: m_mode (Mode::RenderNone),
m_scale (1.0f),
m_surfaceSize { 0, 0 },
m_device (device),
m_textShaders (createTextShaders()),
m_graphShaders (createGraphShaders()),
m_dataBuffer (createDataBuffer()),
m_dataView (createDataView()),
m_dataOffset (0ull),
m_fontBuffer (createFontBuffer()),
m_fontBufferView(createFontBufferView()),
m_fontImage (createFontImage()),
m_fontView (createFontView()),
m_fontSampler (createFontSampler()) {
struct HudGlyphGpuData {
int16_t x;
int16_t y;
int16_t w;
int16_t h;
int16_t originX;
int16_t originY;
};
struct HudFontGpuData {
float size;
float advance;
uint32_t padding[2];
HudGlyphGpuData glyphs[256];
};
static const std::array<VkSpecializationMapEntry, 2> HudSpecConstantMap = {{
{ 0, offsetof(HudSpecConstants, dstSpace), sizeof(VkColorSpaceKHR) },
{ 1, offsetof(HudSpecConstants, dstIsSrgb), sizeof(VkBool32) },
}};
HudRenderer::HudRenderer(const Rc<DxvkDevice>& device)
: m_device (device),
m_textSetLayout (createSetLayout()),
m_textPipelineLayout (createPipelineLayout()) {
createShaderModule(m_textVs, VK_SHADER_STAGE_VERTEX_BIT, sizeof(hud_text_vert), hud_text_vert);
createShaderModule(m_textFs, VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(hud_text_frag), hud_text_frag);
}
HudRenderer::~HudRenderer() {
auto vk = m_device->vkd();
for (const auto& p : m_textPipelines)
vk->vkDestroyPipeline(vk->device(), p.second, nullptr);
vk->vkDestroyShaderModule(vk->device(), m_textVs.stageInfo.module, nullptr);
vk->vkDestroyShaderModule(vk->device(), m_textFs.stageInfo.module, nullptr);
vk->vkDestroyPipelineLayout(vk->device(), m_textPipelineLayout, nullptr);
vk->vkDestroyDescriptorSetLayout(vk->device(), m_textSetLayout, nullptr);
}
void HudRenderer::beginFrame(
const Rc<DxvkContext>& context,
VkExtent2D surfaceSize,
float scale,
float opacity) {
if (!m_initialized)
this->initFontTexture(context);
const DxvkContextObjects& ctx,
const Rc<DxvkImageView>& dstView,
VkColorSpaceKHR dstColorSpace,
const HudOptions& options) {
if (!m_fontTextureView) {
createFontResources();
uploadFontResources(ctx);
}
m_mode = Mode::RenderNone;
m_scale = scale;
m_opacity = opacity;
m_surfaceSize = surfaceSize;
m_context = context;
VkExtent3D extent = dstView->mipLevelExtent(0u);
m_pushConstants.surfaceSize = { extent.width, extent.height };
m_pushConstants.opacity = options.opacity;
m_pushConstants.scale = options.scale;
VkViewport viewport = { };
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = float(extent.width);
viewport.height = float(extent.height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
VkRect2D scissor = { };
scissor.offset = { 0u, 0u };
scissor.extent = { extent.width, extent.height };
ctx.cmd->cmdSetViewport(1, &viewport);
ctx.cmd->cmdSetScissor(1, &scissor);
}
void HudRenderer::drawText(
float size,
HudPos pos,
HudColor color,
const std::string& text) {
uint32_t size,
HudPos pos,
uint32_t color,
const std::string& text) {
if (text.empty())
return;
beginTextRendering();
auto& draw = m_textDraws.emplace_back();
draw.textOffset = m_textData.size();
draw.textLength = text.size();
draw.fontSize = size;
draw.posX = pos.x;
draw.posY = pos.y;
draw.color = color;
// Copy string into string buffer, but extend it to cover a full cache
// line to avoid potential CPU performance issues with the upload.
std::string textCopy = text;
textCopy.resize(align(text.size(), CACHE_LINE_SIZE), ' ');
VkDeviceSize offset = allocDataBuffer(textCopy.size());
std::memcpy(m_dataBuffer->mapPtr(offset), textCopy.data(), textCopy.size());
// Enforce HUD opacity factor on alpha
if (m_opacity != 1.0f)
color.a *= m_opacity;
// Fill in push constants for the next draw
HudTextPushConstants pushData;
pushData.color = color;
pushData.pos = pos;
pushData.offset = offset;
pushData.size = size;
pushData.scale.x = m_scale / std::max(float(m_surfaceSize.width), 1.0f);
pushData.scale.y = m_scale / std::max(float(m_surfaceSize.height), 1.0f);
m_context->pushConstants(0, sizeof(pushData), &pushData);
// Draw with orignal vertex count
m_context->draw(6 * text.size(), 1, 0, 0);
m_textData.resize(draw.textOffset + draw.textLength);
std::memcpy(&m_textData[draw.textOffset], text.data(), draw.textLength);
}
void HudRenderer::drawGraph(
HudPos pos,
HudPos size,
size_t pointCount,
const HudGraphPoint* pointData) {
beginGraphRendering();
VkDeviceSize dataSize = pointCount * sizeof(*pointData);
VkDeviceSize offset = allocDataBuffer(dataSize);
std::memcpy(m_dataBuffer->mapPtr(offset), pointData, dataSize);
HudGraphPushConstants pushData;
pushData.offset = offset / sizeof(*pointData);
pushData.count = pointCount;
pushData.pos = pos;
pushData.size = size;
pushData.scale.x = m_scale / std::max(float(m_surfaceSize.width), 1.0f);
pushData.scale.y = m_scale / std::max(float(m_surfaceSize.height), 1.0f);
pushData.opacity = m_opacity;
void HudRenderer::flushDraws(
const DxvkContextObjects& ctx,
const Rc<DxvkImageView>& dstView,
VkColorSpaceKHR dstColorSpace,
const HudOptions& options) {
if (m_textDraws.empty())
return;
m_context->pushConstants(0, sizeof(pushData), &pushData);
m_context->draw(4, 1, 0, 0);
}
void HudRenderer::beginTextRendering() {
if (m_mode != Mode::RenderText) {
m_mode = Mode::RenderText;
// Align text size so that we're guaranteed to be able to put draw
// parameters where we want, and can also upload data without
// running into perf issues due to incomplete cache lines.
size_t textSizeAligned = align(m_textData.size(), 256u);
m_textData.resize(textSizeAligned);
m_context->bindShader<VK_SHADER_STAGE_VERTEX_BIT>(Rc<DxvkShader>(m_textShaders.vert));
m_context->bindShader<VK_SHADER_STAGE_FRAGMENT_BIT>(Rc<DxvkShader>(m_textShaders.frag));
m_context->bindResourceBufferView(VK_SHADER_STAGE_VERTEX_BIT, 0, Rc<DxvkBufferView>(m_fontBufferView));
m_context->bindResourceBufferView(VK_SHADER_STAGE_VERTEX_BIT, 1, Rc<DxvkBufferView>(m_dataView));
m_context->bindResourceSampler(VK_SHADER_STAGE_FRAGMENT_BIT, 2, Rc<DxvkSampler>(m_fontSampler));
m_context->bindResourceImageView(VK_SHADER_STAGE_FRAGMENT_BIT, 2, Rc<DxvkImageView>(m_fontView));
static const DxvkInputAssemblyState iaState = {
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
VK_FALSE, 0 };
m_context->setInputAssemblyState(iaState);
// We'll use indirect draws and then just use aligned subsections
// of the data buffer to write our draw parameters
size_t drawInfoSize = align(m_textDraws.size() * sizeof(HudTextDrawInfo), 256u);
size_t drawArgsSize = align(m_textDraws.size() * sizeof(VkDrawIndirectCommand), 256u);
// Align buffer size to something large so we don't recreate it all the time
size_t bufferSize = align(textSizeAligned + drawInfoSize + drawArgsSize, 2048u);
if (!m_textBuffer || m_textBuffer->info().size < bufferSize) {
DxvkBufferCreateInfo textBufferInfo = { };
textBufferInfo.size = bufferSize;
textBufferInfo.usage = VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT
| VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
| VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
textBufferInfo.stages = VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT
| VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
textBufferInfo.access = VK_ACCESS_SHADER_READ_BIT
| VK_ACCESS_INDIRECT_COMMAND_READ_BIT;
m_textBuffer = m_device->createBuffer(textBufferInfo,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
DxvkBufferViewKey textViewInfo = { };
textViewInfo.format = VK_FORMAT_R8_UINT;
textViewInfo.offset = 0u;
textViewInfo.size = bufferSize;
textViewInfo.usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
m_textBufferView = m_textBuffer->createView(textViewInfo);
} else {
// Discard and invalidate buffer so we can safely update it
m_textBuffer->assignSlice(Rc<DxvkResourceAllocation>(m_textBuffer->allocateSlice()));
}
}
void HudRenderer::beginGraphRendering() {
if (m_mode != Mode::RenderGraph) {
m_mode = Mode::RenderGraph;
// Upload aligned text data in such a way that we write full cache lines
std::memcpy(m_textBuffer->mapPtr(0), m_textData.data(), textSizeAligned);
m_context->bindShader<VK_SHADER_STAGE_VERTEX_BIT>(Rc<DxvkShader>(m_graphShaders.vert));
m_context->bindShader<VK_SHADER_STAGE_FRAGMENT_BIT>(Rc<DxvkShader>(m_graphShaders.frag));
m_context->bindResourceBufferView(VK_SHADER_STAGE_FRAGMENT_BIT, 0, Rc<DxvkBufferView>(m_dataView));
// Upload draw parameters and pad aligned region with zeroes
size_t drawInfoCopySize = m_textDraws.size() * sizeof(HudTextDrawInfo);
std::memcpy(m_textBuffer->mapPtr(textSizeAligned), m_textDraws.data(), drawInfoCopySize);
std::memset(m_textBuffer->mapPtr(textSizeAligned + drawInfoCopySize), 0, drawInfoSize - drawInfoCopySize);
static const DxvkInputAssemblyState iaState = {
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
VK_FALSE, 0 };
// Emit indirect draw parameters
size_t drawArgWriteSize = m_textDraws.size() * sizeof(VkDrawIndirectCommand);
size_t drawArgOffset = textSizeAligned + drawInfoSize;
m_context->setInputAssemblyState(iaState);
auto drawArgs = reinterpret_cast<VkDrawIndirectCommand*>(m_textBuffer->mapPtr(drawArgOffset));
for (size_t i = 0; i < m_textDraws.size(); i++) {
drawArgs[i].vertexCount = 6u * m_textDraws[i].textLength;
drawArgs[i].instanceCount = 1u;
drawArgs[i].firstVertex = 0u;
drawArgs[i].firstInstance = 0u;
}
std::memset(m_textBuffer->mapPtr(drawArgOffset + drawArgWriteSize), 0, drawArgsSize - drawArgWriteSize);
// Draw the actual text
VkDescriptorBufferInfo textBufferDescriptor = m_textBuffer->getDescriptor(textSizeAligned, drawInfoSize).buffer;
VkDescriptorBufferInfo drawBufferDescriptor = m_textBuffer->getDescriptor(drawArgOffset, drawArgWriteSize).buffer;
drawTextIndirect(ctx, getPipelineKey(dstView, dstColorSpace),
drawBufferDescriptor, textBufferDescriptor,
m_textBufferView->handle(), m_textDraws.size());
// Ensure all used resources are kept alive
ctx.cmd->trackResource<DxvkAccess::None>(m_textBuffer->getAllocation());
ctx.cmd->trackResource<DxvkAccess::Read>(m_fontBuffer->getAllocation());
ctx.cmd->trackResource<DxvkAccess::Read>(m_fontTexture->getAllocation());
ctx.cmd->trackSampler(m_fontSampler);
// Reset internal text buffers
m_textDraws.clear();
m_textData.clear();
}
VkDeviceSize HudRenderer::allocDataBuffer(VkDeviceSize size) {
if (m_dataOffset + size > m_dataBuffer->info().size) {
m_context->invalidateBuffer(m_dataBuffer, m_dataBuffer->allocateSlice());
m_dataOffset = 0;
}
VkDeviceSize offset = m_dataOffset;
m_dataOffset = align(offset + size, 64);
return offset;
}
void HudRenderer::drawTextIndirect(
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const VkDescriptorBufferInfo& drawArgs,
const VkDescriptorBufferInfo& drawInfos,
VkBufferView text,
uint32_t drawCount) {
// Bind the correct pipeline for the swap chain
VkPipeline pipeline = getPipeline(key);
HudRenderer::ShaderPair HudRenderer::createTextShaders() {
ShaderPair result;
ctx.cmd->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,
VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
SpirvCodeBuffer vsCode(hud_text_vert);
SpirvCodeBuffer fsCode(hud_text_frag);
const std::array<DxvkBindingInfo, 2> vsBindings = {{
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 0, VK_IMAGE_VIEW_TYPE_MAX_ENUM, VK_SHADER_STAGE_VERTEX_BIT, VK_ACCESS_SHADER_READ_BIT },
{ VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_IMAGE_VIEW_TYPE_MAX_ENUM, VK_SHADER_STAGE_VERTEX_BIT, VK_ACCESS_SHADER_READ_BIT },
// Bind resources
VkDescriptorSet set = ctx.descriptorPool->alloc(m_textSetLayout);
VkDescriptorBufferInfo fontBufferDescriptor = m_fontBuffer->getDescriptor(0, m_fontBuffer->info().size).buffer;
VkDescriptorImageInfo fontTextureDescriptor = { };
fontTextureDescriptor.sampler = m_fontSampler->handle();
fontTextureDescriptor.imageView = m_fontTextureView->handle();
fontTextureDescriptor.imageLayout = m_fontTexture->info().layout;
std::array<VkWriteDescriptorSet, 4> descriptorWrites = {{
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
set, 0, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &fontBufferDescriptor },
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
set, 1, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &drawInfos },
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
set, 2, 0, 1, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, nullptr, nullptr, &text },
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
set, 3, 0, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &fontTextureDescriptor },
}};
const std::array<DxvkBindingInfo, 1> fsBindings = {{
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2, VK_IMAGE_VIEW_TYPE_MAX_ENUM, VK_SHADER_STAGE_FRAGMENT_BIT, VK_ACCESS_SHADER_READ_BIT },
}};
ctx.cmd->updateDescriptorSets(
descriptorWrites.size(),
descriptorWrites.data());
DxvkShaderCreateInfo vsInfo;
vsInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
vsInfo.bindingCount = vsBindings.size();
vsInfo.bindings = vsBindings.data();
vsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT;
vsInfo.pushConstSize = sizeof(HudTextPushConstants);
vsInfo.outputMask = 0x3;
result.vert = new DxvkShader(vsInfo, std::move(vsCode));
ctx.cmd->cmdBindDescriptorSet(DxvkCmdBuffer::ExecBuffer,
VK_PIPELINE_BIND_POINT_GRAPHICS, m_textPipelineLayout,
set, 0, nullptr);
DxvkShaderCreateInfo fsInfo;
fsInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fsInfo.bindingCount = fsBindings.size();
fsInfo.bindings = fsBindings.data();
fsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT;
fsInfo.pushConstSize = sizeof(HudTextPushConstants);
fsInfo.inputMask = 0x3;
fsInfo.outputMask = 0x1;
result.frag = new DxvkShader(fsInfo, std::move(fsCode));
ctx.cmd->cmdPushConstants(DxvkCmdBuffer::ExecBuffer, m_textPipelineLayout,
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
0, sizeof(m_pushConstants), &m_pushConstants);
// Emit the actual draw call
ctx.cmd->cmdDrawIndirect(drawArgs.buffer, drawArgs.offset,
drawCount, sizeof(VkDrawIndirectCommand));
}
HudPipelineKey HudRenderer::getPipelineKey(
const Rc<DxvkImageView>& dstView,
VkColorSpaceKHR dstColorSpace) const {
HudPipelineKey key;
key.format = dstView->info().format;
key.colorSpace = dstColorSpace;
return key;
}
HudSpecConstants HudRenderer::getSpecConstants(
const HudPipelineKey& key) const {
HudSpecConstants result = { };
result.dstSpace = key.colorSpace;
result.dstIsSrgb = lookupFormatInfo(key.format)->flags.test(DxvkFormatFlag::ColorSpaceSrgb);
return result;
}
HudRenderer::ShaderPair HudRenderer::createGraphShaders() {
ShaderPair result;
SpirvCodeBuffer vsCode(hud_graph_vert);
SpirvCodeBuffer fsCode(hud_graph_frag);
const std::array<DxvkBindingInfo, 1> fsBindings = {{
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 0, VK_IMAGE_VIEW_TYPE_MAX_ENUM, VK_SHADER_STAGE_FRAGMENT_BIT, VK_ACCESS_SHADER_READ_BIT },
}};
DxvkShaderCreateInfo vsInfo;
vsInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
vsInfo.outputMask = 0x1;
vsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
vsInfo.pushConstSize = sizeof(HudGraphPushConstants);
result.vert = new DxvkShader(vsInfo, std::move(vsCode));
DxvkShaderCreateInfo fsInfo;
fsInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fsInfo.bindingCount = fsBindings.size();
fsInfo.bindings = fsBindings.data();
fsInfo.inputMask = 0x1;
fsInfo.outputMask = 0x1;
fsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
fsInfo.pushConstSize = sizeof(HudGraphPushConstants);
result.frag = new DxvkShader(fsInfo, std::move(fsCode));
return result;
}
Rc<DxvkBuffer> HudRenderer::createDataBuffer() {
DxvkBufferCreateInfo info;
info.size = DataBufferSize;
info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
| VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
info.stages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT
| VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
info.access = VK_ACCESS_SHADER_READ_BIT;
return m_device->createBuffer(info,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
HudPushConstants HudRenderer::getPushConstants() const {
return m_pushConstants;
}
Rc<DxvkBufferView> HudRenderer::createDataView() {
DxvkBufferViewKey info;
info.format = VK_FORMAT_R8_UINT;
info.offset = 0;
info.size = m_dataBuffer->info().size;
info.usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
return m_dataBuffer->createView(info);
VkSpecializationInfo HudRenderer::getSpecInfo(
const HudSpecConstants* constants) const {
VkSpecializationInfo specInfo = { };
specInfo.mapEntryCount = HudSpecConstantMap.size();
specInfo.pMapEntries = HudSpecConstantMap.data();
specInfo.dataSize = sizeof(*constants);
specInfo.pData = constants;
return specInfo;
}
Rc<DxvkBuffer> HudRenderer::createFontBuffer() {
DxvkBufferCreateInfo info;
info.size = sizeof(HudFontGpuData);
info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
| VK_BUFFER_USAGE_TRANSFER_DST_BIT;
info.stages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT
| VK_PIPELINE_STAGE_TRANSFER_BIT;
info.access = VK_ACCESS_SHADER_READ_BIT
| VK_ACCESS_TRANSFER_WRITE_BIT;
return m_device->createBuffer(info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
void HudRenderer::createShaderModule(
HudShaderModule& shader,
VkShaderStageFlagBits stage,
size_t size,
const uint32_t* code) const {
shader.moduleInfo.codeSize = size;
shader.moduleInfo.pCode = code;
shader.stageInfo.stage = stage;
shader.stageInfo.pName = "main";
if (m_device->features().khrMaintenance5.maintenance5
|| m_device->features().extGraphicsPipelineLibrary.graphicsPipelineLibrary) {
shader.stageInfo.pNext = &shader.moduleInfo;
return;
}
auto vk = m_device->vkd();
VkResult vr = vk->vkCreateShaderModule(vk->device(),
&shader.moduleInfo, nullptr, &shader.stageInfo.module);
if (vr != VK_SUCCESS)
throw DxvkError(str::format("Failed to create swap chain blit shader module: ", vr));
}
Rc<DxvkBufferView> HudRenderer::createFontBufferView() {
DxvkBufferViewKey info;
info.format = VK_FORMAT_UNDEFINED;
info.offset = 0;
info.size = m_fontBuffer->info().size;
info.usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
void HudRenderer::createFontResources() {
DxvkBufferCreateInfo fontBufferInfo;
fontBufferInfo.size = sizeof(HudFontGpuData);
fontBufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT
| VK_BUFFER_USAGE_TRANSFER_SRC_BIT
| VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
fontBufferInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT
| VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
fontBufferInfo.access = VK_ACCESS_TRANSFER_WRITE_BIT
| VK_ACCESS_TRANSFER_READ_BIT
| VK_ACCESS_SHADER_READ_BIT;
return m_fontBuffer->createView(info);
}
m_fontBuffer = m_device->createBuffer(fontBufferInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
DxvkImageCreateInfo fontTextureInfo;
fontTextureInfo.type = VK_IMAGE_TYPE_2D;
fontTextureInfo.format = VK_FORMAT_R8_UNORM;
fontTextureInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT;
fontTextureInfo.extent = { g_hudFont.width, g_hudFont.height, 1u };
fontTextureInfo.numLayers = 1u;
fontTextureInfo.mipLevels = 1u;
fontTextureInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT
| VK_IMAGE_USAGE_TRANSFER_SRC_BIT
| VK_IMAGE_USAGE_SAMPLED_BIT;
fontTextureInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT
| VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
fontTextureInfo.access = VK_ACCESS_TRANSFER_WRITE_BIT
| VK_ACCESS_TRANSFER_READ_BIT
| VK_ACCESS_SHADER_READ_BIT;
fontTextureInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
fontTextureInfo.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
fontTextureInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
Rc<DxvkImage> HudRenderer::createFontImage() {
DxvkImageCreateInfo info;
info.type = VK_IMAGE_TYPE_2D;
info.format = VK_FORMAT_R8_UNORM;
info.flags = 0;
info.sampleCount = VK_SAMPLE_COUNT_1_BIT;
info.extent = { g_hudFont.width, g_hudFont.height, 1 };
info.numLayers = 1;
info.mipLevels = 1;
info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT
| VK_IMAGE_USAGE_SAMPLED_BIT;
info.stages = VK_PIPELINE_STAGE_TRANSFER_BIT
| VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
info.access = VK_ACCESS_TRANSFER_WRITE_BIT
| VK_ACCESS_SHADER_READ_BIT;
info.tiling = VK_IMAGE_TILING_OPTIMAL;
info.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
return m_device->createImage(info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
}
Rc<DxvkImageView> HudRenderer::createFontView() {
DxvkImageViewKey info;
info.viewType = VK_IMAGE_VIEW_TYPE_2D;
info.format = m_fontImage->info().format;
info.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
info.aspects = VK_IMAGE_ASPECT_COLOR_BIT;
info.mipIndex = 0;
info.mipCount = 1;
info.layerIndex = 0;
info.layerCount = 1;
return m_fontImage->createView(info);
}
Rc<DxvkSampler> HudRenderer::createFontSampler() {
DxvkSamplerKey info = { };
info.setFilter(VK_FILTER_LINEAR,
m_fontTexture = m_device->createImage(fontTextureInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
DxvkImageViewKey fontTextureViewInfo;
fontTextureViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
fontTextureViewInfo.format = VK_FORMAT_R8_UNORM;
fontTextureViewInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
fontTextureViewInfo.aspects = VK_IMAGE_ASPECT_COLOR_BIT;
fontTextureViewInfo.mipIndex = 0u;
fontTextureViewInfo.mipCount = 1u;
fontTextureViewInfo.layerIndex = 0u;
fontTextureViewInfo.layerCount = 1u;
m_fontTextureView = m_fontTexture->createView(fontTextureViewInfo);
DxvkSamplerKey samplerInfo;
samplerInfo.setFilter(VK_FILTER_LINEAR,
VK_FILTER_LINEAR, VK_SAMPLER_MIPMAP_MODE_NEAREST);
info.setAddressModes(
samplerInfo.setAddressModes(
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE);
info.setUsePixelCoordinates(true);
return m_device->createSampler(info);
samplerInfo.setUsePixelCoordinates(true);
m_fontSampler = m_device->createSampler(samplerInfo);
}
void HudRenderer::initFontTexture(
const Rc<DxvkContext>& context) {
HudFontGpuData gpuData = { };
gpuData.size = float(g_hudFont.size);
gpuData.advance = float(g_hudFont.advance);
for (uint32_t i = 0; i < g_hudFont.charCount; i++) {
auto src = &g_hudFont.glyphs[i];
auto dst = &gpuData.glyphs[src->codePoint];
dst->x = src->x;
dst->y = src->y;
dst->w = src->w;
dst->h = src->h;
dst->originX = src->originX;
dst->originY = src->originY;
void HudRenderer::uploadFontResources(
const DxvkContextObjects& ctx) {
size_t bufferDataSize = sizeof(HudFontGpuData);
size_t textureDataSize = g_hudFont.width * g_hudFont.height;
DxvkBufferCreateInfo bufferInfo;
bufferInfo.size = bufferDataSize + textureDataSize;
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
bufferInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT;
bufferInfo.access = VK_ACCESS_TRANSFER_READ_BIT;
auto uploadBuffer = m_device->createBuffer(bufferInfo,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
HudFontGpuData glyphData = { };
glyphData.size = float(g_hudFont.size);
glyphData.advance = float(g_hudFont.advance);
for (size_t i = 0; i < g_hudFont.charCount; i++) {
auto& src = g_hudFont.glyphs[i];
auto& dst = glyphData.glyphs[src.codePoint];
dst.x = src.x;
dst.y = src.y;
dst.w = src.w;
dst.h = src.h;
dst.originX = src.originX;
dst.originY = src.originY;
}
context->uploadBuffer(m_fontBuffer, &gpuData);
std::memcpy(uploadBuffer->mapPtr(0), &glyphData, bufferDataSize);
std::memcpy(uploadBuffer->mapPtr(bufferDataSize), g_hudFont.texture, textureDataSize);
context->uploadImage(m_fontImage,
VkImageSubresourceLayers { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 },
g_hudFont.texture, g_hudFont.width, g_hudFont.width * g_hudFont.height);
m_initialized = true;
auto uploadSlice = uploadBuffer->getSliceHandle();
auto fontSlice = m_fontBuffer->getSliceHandle();
VkImageMemoryBarrier2 imageBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 };
imageBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
imageBarrier.dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
imageBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageBarrier.newLayout = m_fontTexture->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
imageBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
imageBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
imageBarrier.image = m_fontTexture->handle();
imageBarrier.subresourceRange = m_fontTexture->getAvailableSubresources();
VkDependencyInfo depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };
depInfo.imageMemoryBarrierCount = 1u;
depInfo.pImageMemoryBarriers = &imageBarrier;
ctx.cmd->cmdPipelineBarrier(DxvkCmdBuffer::InitBuffer, &depInfo);
VkBufferCopy2 bufferRegion = { VK_STRUCTURE_TYPE_BUFFER_COPY_2 };
bufferRegion.srcOffset = uploadSlice.offset;
bufferRegion.dstOffset = fontSlice.offset;
bufferRegion.size = bufferDataSize;
VkCopyBufferInfo2 bufferCopy = { VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2 };
bufferCopy.srcBuffer = uploadSlice.handle;
bufferCopy.dstBuffer = fontSlice.handle;
bufferCopy.regionCount = 1;
bufferCopy.pRegions = &bufferRegion;
ctx.cmd->cmdCopyBuffer(DxvkCmdBuffer::InitBuffer, &bufferCopy);
VkBufferImageCopy2 imageRegion = { VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2 };
imageRegion.bufferOffset = uploadSlice.offset + bufferDataSize;
imageRegion.imageExtent = m_fontTexture->info().extent;
imageRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
imageRegion.imageSubresource.layerCount = 1u;
VkCopyBufferToImageInfo2 imageCopy = { VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2 };
imageCopy.srcBuffer = uploadSlice.handle;
imageCopy.dstImage = m_fontTexture->handle();
imageCopy.dstImageLayout = m_fontTexture->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
imageCopy.regionCount = 1;
imageCopy.pRegions = &imageRegion;
ctx.cmd->cmdCopyBufferToImage(DxvkCmdBuffer::InitBuffer, &imageCopy);
VkMemoryBarrier2 memoryBarrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 };
memoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
memoryBarrier.srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
memoryBarrier.dstAccessMask = m_fontBuffer->info().access;
memoryBarrier.dstStageMask = m_fontBuffer->info().stages;
imageBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 };
imageBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
imageBarrier.srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
imageBarrier.dstAccessMask = m_fontTexture->info().access;
imageBarrier.dstStageMask = m_fontTexture->info().stages;
imageBarrier.oldLayout = m_fontTexture->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
imageBarrier.newLayout = m_fontTexture->info().layout;
imageBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
imageBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
imageBarrier.image = m_fontTexture->handle();
imageBarrier.subresourceRange = m_fontTexture->getAvailableSubresources();
depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };
depInfo.memoryBarrierCount = 1u;
depInfo.pMemoryBarriers = &memoryBarrier;
depInfo.imageMemoryBarrierCount = 1u;
depInfo.pImageMemoryBarriers = &imageBarrier;
ctx.cmd->cmdPipelineBarrier(DxvkCmdBuffer::InitBuffer, &depInfo);
ctx.cmd->trackResource<DxvkAccess::Read>(uploadBuffer->getAllocation());
ctx.cmd->trackResource<DxvkAccess::Write>(m_fontBuffer->getAllocation());
ctx.cmd->trackResource<DxvkAccess::Write>(m_fontTexture->getAllocation());
}
VkDescriptorSetLayout HudRenderer::createSetLayout() {
auto vk = m_device->vkd();
static const std::array<VkDescriptorSetLayoutBinding, 4> bindings = {{
{ 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT },
{ 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT },
{ 2, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT },
{ 3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT },
}};
VkDescriptorSetLayoutCreateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
info.bindingCount = bindings.size();
info.pBindings = bindings.data();
VkDescriptorSetLayout layout = VK_NULL_HANDLE;
VkResult vr = vk->vkCreateDescriptorSetLayout(vk->device(), &info, nullptr, &layout);
if (vr != VK_SUCCESS)
throw DxvkError(str::format("Failed to create HUD descriptor set layout: ", vr));
return layout;
}
VkPipelineLayout HudRenderer::createPipelineLayout() {
auto vk = m_device->vkd();
VkPushConstantRange pushConstantRange = { };
pushConstantRange.offset = 0u;
pushConstantRange.size = sizeof(HudPushConstants);
pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
VkPipelineLayoutCreateInfo info = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
info.setLayoutCount = 1;
info.pSetLayouts = &m_textSetLayout;
info.pushConstantRangeCount = 1;
info.pPushConstantRanges = &pushConstantRange;
VkPipelineLayout layout = VK_NULL_HANDLE;
VkResult vr = vk->vkCreatePipelineLayout(vk->device(), &info, nullptr, &layout);
if (vr != VK_SUCCESS)
throw DxvkError(str::format("Failed to create HUD descriptor set layout: ", vr));
return layout;
}
VkPipeline HudRenderer::createPipeline(
const HudPipelineKey& key) {
auto vk = m_device->vkd();
HudSpecConstants specConstants = getSpecConstants(key);
VkSpecializationInfo specInfo = getSpecInfo(&specConstants);
std::array<VkPipelineShaderStageCreateInfo, 2> stages = { };
stages[0] = m_textVs.stageInfo;
stages[1] = m_textFs.stageInfo;
stages[1].pSpecializationInfo = &specInfo;
VkPipelineRenderingCreateInfo rtInfo = { VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO };
rtInfo.colorAttachmentCount = 1;
rtInfo.pColorAttachmentFormats = &key.format;
VkPipelineVertexInputStateCreateInfo viState = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
VkPipelineInputAssemblyStateCreateInfo iaState = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
iaState.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
VkPipelineViewportStateCreateInfo vpState = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };
VkPipelineRasterizationStateCreateInfo rsState = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
rsState.cullMode = VK_CULL_MODE_NONE;
rsState.polygonMode = VK_POLYGON_MODE_FILL;
rsState.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rsState.lineWidth = 1.0f;
constexpr uint32_t sampleMask = 0x1;
VkPipelineMultisampleStateCreateInfo msState = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
msState.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
msState.pSampleMask = &sampleMask;
VkPipelineColorBlendAttachmentState cbAttachment = { };
cbAttachment.blendEnable = VK_TRUE;
cbAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
cbAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
cbAttachment.colorBlendOp = VK_BLEND_OP_ADD;
cbAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
cbAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
cbAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
cbAttachment.colorWriteMask =
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
VkPipelineColorBlendStateCreateInfo cbOpaqueState = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO };
cbOpaqueState.attachmentCount = 1;
cbOpaqueState.pAttachments = &cbAttachment;
static const std::array<VkDynamicState, 2> dynStates = {
VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT,
VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT,
};
VkPipelineDynamicStateCreateInfo dynState = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
dynState.dynamicStateCount = dynStates.size();
dynState.pDynamicStates = dynStates.data();
VkGraphicsPipelineCreateInfo blitInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, &rtInfo };
blitInfo.stageCount = stages.size();
blitInfo.pStages = stages.data();
blitInfo.pVertexInputState = &viState;
blitInfo.pInputAssemblyState = &iaState;
blitInfo.pViewportState = &vpState;
blitInfo.pRasterizationState = &rsState;
blitInfo.pMultisampleState = &msState;
blitInfo.pColorBlendState = &cbOpaqueState;
blitInfo.pDynamicState = &dynState;
blitInfo.layout = m_textPipelineLayout;
blitInfo.basePipelineIndex = -1;
VkPipeline pipeline = VK_NULL_HANDLE;
VkResult vr = vk->vkCreateGraphicsPipelines(vk->device(), VK_NULL_HANDLE,
1, &blitInfo, nullptr, &pipeline);
if (vr != VK_SUCCESS)
throw DxvkError(str::format("Failed to create swap chain blit pipeline: ", vr));
return pipeline;
}
VkPipeline HudRenderer::getPipeline(
const HudPipelineKey& key) {
auto entry = m_textPipelines.find(key);
if (entry != m_textPipelines.end())
return entry->second;
VkPipeline pipeline = createPipeline(key);
m_textPipelines.insert({ key, pipeline });
return pipeline;
}
}

View File

@ -5,7 +5,16 @@
#include "dxvk_hud_font.h"
namespace dxvk::hud {
/**
* \brief HUD options
*/
struct HudOptions {
float scale = 1.0f;
float opacity = 1.0f;
};
/**
* \brief HUD coordinates
*
@ -13,82 +22,69 @@ namespace dxvk::hud {
* corner of the swap image, in pixels.
*/
struct HudPos {
float x;
float y;
};
/**
* \brief Color
*
* SRGB color with alpha channel. The text
* will use this color for the most part.
*/
struct HudColor {
float r;
float g;
float b;
float a;
int32_t x = 0;
int32_t y = 0;
};
/**
* \brief Normalized color
*
* SRGB color with alpha channel.
*/
struct HudNormColor {
uint8_t a;
uint8_t b;
uint8_t g;
uint8_t r;
};
/**
* \brief Graph point with color
*/
struct HudGraphPoint {
float value;
HudNormColor color;
};
/**
* \brief HUD push constant data
* \brief Draw parameters for text
*/
struct HudTextPushConstants {
HudColor color;
HudPos pos;
uint32_t offset;
float size;
HudPos scale;
struct HudTextDrawInfo {
uint32_t textOffset = 0u;
uint16_t textLength = 0u;
uint16_t fontSize = 0u;
int16_t posX = 0;
int16_t posY = 0;
uint32_t color = 0u;
};
struct HudGraphPushConstants {
uint32_t offset;
uint32_t count;
HudPos pos;
HudPos size;
HudPos scale;
float opacity;
struct HudPushConstants {
VkExtent2D surfaceSize;
float opacity;
float scale;
};
/**
* \brief Glyph data
* \brief Pipeline key
*/
struct HudGlyphGpuData {
int16_t x;
int16_t y;
int16_t w;
int16_t h;
int16_t originX;
int16_t originY;
struct HudPipelineKey {
VkFormat format = VK_FORMAT_UNDEFINED;
VkColorSpaceKHR colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
size_t hash() const {
DxvkHashState hash;
hash.add(uint32_t(format));
hash.add(uint32_t(colorSpace));
return hash;
}
bool eq(const HudPipelineKey& other) const {
return format == other.format && colorSpace == other.colorSpace;
}
};
struct HudFontGpuData {
float size;
float advance;
uint32_t padding[2];
HudGlyphGpuData glyphs[256];
/**
* \brief Specialization constants
*/
struct HudSpecConstants {
VkColorSpaceKHR dstSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
VkBool32 dstIsSrgb = VK_FALSE;
};
/**
* \brief Shader module info
*/
struct HudShaderModule {
VkShaderModuleCreateInfo moduleInfo = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
VkPipelineShaderStageCreateInfo stageInfo = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO };
};
/**
* \brief Text renderer for the HUD
*
@ -103,99 +99,92 @@ namespace dxvk::hud {
const Rc<DxvkDevice>& device);
~HudRenderer();
void beginFrame(
const Rc<DxvkContext>& context,
VkExtent2D surfaceSize,
float scale,
float opacity);
const DxvkContextObjects& ctx,
const Rc<DxvkImageView>& dstView,
VkColorSpaceKHR dstColorSpace,
const HudOptions& options);
void drawText(
float size,
HudPos pos,
HudColor color,
const std::string& text);
void drawGraph(
HudPos pos,
HudPos size,
size_t pointCount,
const HudGraphPoint* pointData);
uint32_t size,
HudPos pos,
uint32_t color,
const std::string& text);
DxvkContext* getContext() {
m_mode = Mode::RenderNone;
return m_context.ptr();
}
void drawTextIndirect(
const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const VkDescriptorBufferInfo& drawArgs,
const VkDescriptorBufferInfo& drawInfos,
VkBufferView text,
uint32_t drawCount);
VkExtent2D surfaceSize() const {
return m_surfaceSize;
}
void flushDraws(
const DxvkContextObjects& ctx,
const Rc<DxvkImageView>& dstView,
VkColorSpaceKHR dstColorSpace,
const HudOptions& options);
float scale() const {
return m_scale;
}
HudPipelineKey getPipelineKey(
const Rc<DxvkImageView>& dstView,
VkColorSpaceKHR dstColorSpace) const;
HudSpecConstants getSpecConstants(
const HudPipelineKey& key) const;
HudPushConstants getPushConstants() const;
VkSpecializationInfo getSpecInfo(
const HudSpecConstants* constants) const;
void createShaderModule(
HudShaderModule& shader,
VkShaderStageFlagBits stage,
size_t size,
const uint32_t* code) const;
float opacity() const {
return m_opacity;
}
private:
enum class Mode {
RenderNone,
RenderText,
RenderGraph,
};
struct ShaderPair {
Rc<DxvkShader> vert;
Rc<DxvkShader> frag;
};
Mode m_mode;
float m_scale;
float m_opacity;
VkExtent2D m_surfaceSize;
Rc<DxvkDevice> m_device;
Rc<DxvkDevice> m_device;
Rc<DxvkContext> m_context;
ShaderPair m_textShaders;
ShaderPair m_graphShaders;
Rc<DxvkBuffer> m_dataBuffer;
Rc<DxvkBufferView> m_dataView;
VkDeviceSize m_dataOffset;
Rc<DxvkBuffer> m_fontBuffer;
Rc<DxvkImage> m_fontTexture;
Rc<DxvkImageView> m_fontTextureView;
Rc<DxvkSampler> m_fontSampler;
Rc<DxvkBuffer> m_fontBuffer;
Rc<DxvkBufferView> m_fontBufferView;
Rc<DxvkImage> m_fontImage;
Rc<DxvkImageView> m_fontView;
Rc<DxvkSampler> m_fontSampler;
Rc<DxvkBuffer> m_textBuffer;
Rc<DxvkBufferView> m_textBufferView;
bool m_initialized = false;
std::vector<HudTextDrawInfo> m_textDraws;
std::vector<char> m_textData;
void beginTextRendering();
void beginGraphRendering();
HudShaderModule m_textVs;
HudShaderModule m_textFs;
VkDeviceSize allocDataBuffer(VkDeviceSize size);
VkDescriptorSetLayout m_textSetLayout = VK_NULL_HANDLE;
VkPipelineLayout m_textPipelineLayout = VK_NULL_HANDLE;
ShaderPair createTextShaders();
ShaderPair createGraphShaders();
HudPushConstants m_pushConstants = { };
Rc<DxvkBuffer> createDataBuffer();
Rc<DxvkBufferView> createDataView();
std::unordered_map<HudPipelineKey,
VkPipeline, DxvkHash, DxvkEq> m_textPipelines;
void createFontResources();
void uploadFontResources(
const DxvkContextObjects& ctx);
VkDescriptorSetLayout createSetLayout();
VkPipelineLayout createPipelineLayout();
VkPipeline createPipeline(
const HudPipelineKey& key);
VkPipeline getPipeline(
const HudPipelineKey& key);
Rc<DxvkBuffer> createFontBuffer();
Rc<DxvkBufferView> createFontBufferView();
Rc<DxvkImage> createFontImage();
Rc<DxvkImageView> createFontView();
Rc<DxvkSampler> createFontSampler();
void initFontTexture(
const Rc<DxvkContext>& context);
};
}

View File

@ -4,20 +4,17 @@
#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;
uvec2 surface_size;
float opacity;
uint color;
uint maskIndex;
uint pageCount;
float scale;
};
layout(location = 0) out vec4 o_color;
void main() {
vec4 rgba = unpackUnorm4x8(color);
o_color = vec4(encodeOutput(rgba.rgb), rgba.a * opacity);
o_color = vec4(0.0f, 0.0f, 0.0f, 0.75f);
o_color.a *= opacity;
o_color = linear_to_output(o_color);
}

View File

@ -5,32 +5,31 @@
#include "hud_frag_common.glsl"
layout(location = 0) in vec2 v_coord;
layout(location = 1, component = 0) flat in uint v_color;
layout(location = 1, component = 1) flat in uint v_mask_index;
layout(location = 1, component = 2) flat in uint v_page_count;
layout(location = 0) out vec4 o_color;
layout(binding = 0, std430)
layout(binding = 1, std430)
readonly buffer mask_data_t {
uint masks[];
};
layout(push_constant)
uniform push_data_t {
vec2 pos;
vec2 size;
vec2 scale;
uvec2 surface_size;
float opacity;
uint color;
uint maskIndex;
uint pageCount;
float scale;
};
void main() {
vec4 rgba = unpackUnorm4x8(color);
float dx = dFdx(v_coord.x * float(pageCount)) / 2.0f - 0.5f;
float dx = dFdx(v_coord.x * float(v_page_count)) / 2.0f - 0.5f;
uvec2 pageRange = uvec2(clamp(
(v_coord.xx * float(pageCount)) + vec2(-dx, dx),
vec2(0.0), vec2(float(pageCount - 1u))));
(v_coord.xx * float(v_page_count)) + vec2(-dx, dx),
vec2(0.0), vec2(float(v_page_count - 1u))));
uint bitsTotal = max(pageRange.y - pageRange.x, 1u);
uint bitsSet = 0u;
@ -40,26 +39,28 @@ void main() {
if (shift + bitsTotal <= 32u) {
bitsSet = bitCount(bitfieldExtract(
masks[maskIndex + index], int(shift), int(bitsTotal)));
masks[v_mask_index + index], int(shift), int(bitsTotal)));
} else {
bitsSet = bitCount(masks[maskIndex + (index++)] >> shift);
bitsSet = bitCount(masks[v_mask_index + (index++)] >> shift);
uint bitsCounted = 32u - shift;
while (bitsCounted + 32u <= bitsTotal) {
bitsSet += bitCount(masks[maskIndex + (index++)]);
bitsSet += bitCount(masks[v_mask_index + (index++)]);
bitsCounted += 32u;
}
if (bitsCounted < bitsTotal) {
bitsSet += bitCount(bitfieldExtract(
masks[maskIndex + (index++)], 0, int(bitsTotal - bitsCounted)));
masks[v_mask_index + (index++)], 0, int(bitsTotal - bitsCounted)));
}
}
if (bitsSet == 0u)
discard;
vec4 color = unpackUnorm4x8(v_color);
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);
o_color = vec4(mix(color.rgb, vec3(1.0f), blendFactor), color.a * opacity);
o_color = linear_to_output(o_color);
}

View File

@ -1,25 +0,0 @@
#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

@ -0,0 +1,47 @@
#version 450
struct draw_info_t {
uint packed_xy;
uint packed_wh;
uint packed_range;
uint color;
};
layout(binding = 0, std430)
readonly buffer draw_data_t {
draw_info_t draw_infos[];
};
layout(push_constant)
uniform push_data_t {
uvec2 surface_size;
float opacity;
float scale;
};
vec2 unpack_u16(uint v) {
// Inputs may be signed
int hi = int(v);
int lo = int(v << 16);
return vec2(float(lo >> 16), float(hi >> 16));
}
void main() {
draw_info_t draw = draw_infos[gl_InstanceIndex];
vec2 coord = vec2(
float(gl_VertexIndex & 1),
float(gl_VertexIndex >> 1));
vec2 surface_size_f = vec2(surface_size) / scale;
vec2 pos = unpack_u16(draw.packed_xy);
vec2 size = unpack_u16(draw.packed_wh);
pos = mix(pos, surface_size_f + pos, lessThan(pos, vec2(0.0f)));
vec2 pixel_pos = pos + size * coord + (2.0f * coord - 1.0f);
vec2 scaled_pos = 2.0f * (pixel_pos / surface_size_f) - 1.0f;
gl_Position = vec4(scaled_pos, 0.0f, 1.0f);
}

View File

@ -0,0 +1,58 @@
#version 450
struct draw_info_t {
uint packed_xy;
uint packed_wh;
uint packed_range;
uint color;
};
layout(binding = 0, std430)
readonly buffer draw_data_t {
draw_info_t draw_infos[];
};
layout(push_constant)
uniform push_data_t {
uvec2 surface_size;
float opacity;
float scale;
};
layout(location = 0) out vec2 o_coord;
layout(location = 1, component = 0) out uint o_color;
layout(location = 1, component = 1) out uint o_mask_index;
layout(location = 1, component = 2) out uint o_page_count;
vec2 unpack_u16(uint v) {
// Inputs may be signed
int hi = int(v);
int lo = int(v << 16);
return vec2(float(lo >> 16), float(hi >> 16));
}
void main() {
draw_info_t draw = draw_infos[gl_InstanceIndex];
vec2 coord = vec2(
float(gl_VertexIndex & 1),
float(gl_VertexIndex >> 1));
o_coord = coord;
o_color = draw.color;
o_mask_index = bitfieldExtract(draw.packed_range, 0, 16);
o_page_count = bitfieldExtract(draw.packed_range, 16, 16);
vec2 surface_size_f = vec2(surface_size) / scale;
vec2 pos = unpack_u16(draw.packed_xy);
vec2 size = unpack_u16(draw.packed_wh);
pos = mix(pos, surface_size_f + pos, lessThan(pos, vec2(0.0f)));
vec2 pixel_pos = pos + size * coord;
vec2 scaled_pos = 2.0f * (pixel_pos / surface_size_f) - 1.0f;
gl_Position = vec4(scaled_pos, 0.0f, 1.0f);
}

View File

@ -1,68 +1,27 @@
#define VK_COLOR_SPACE_SRGB_NONLINEAR_KHR (0)
#define VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT (1000104002)
#define VK_COLOR_SPACE_HDR10_ST2084_EXT (1000104008)
#define VK_COLOR_SPACE_PASS_THROUGH_EXT (1000104013)
#include "../../shaders/dxvk_color_space.glsl"
#define HUD_NITS (203.0f)
layout(constant_id = 0) const uint s_color_space = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
layout(constant_id = 1) const bool s_srgb = false;
const mat3 rec709_to_xyz = mat3(
0.4123908, 0.2126390, 0.0193308,
0.3575843, 0.7151687, 0.1191948,
0.1804808, 0.0721923, 0.9505322);
vec4 linear_to_output(vec4 color) {
switch (s_color_space) {
case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: {
if (!s_srgb)
color.rgb = linear_to_srgb(color.rgb);
const mat3 xyz_to_rec2020 = mat3(
1.7166512, -0.6666844, 0.0176399,
-0.3556708, 1.6164812, -0.0427706,
-0.2533663, 0.0157685, 0.9421031);
const mat3 rec709_to_rec2020 = xyz_to_rec2020 * rec709_to_xyz;
// Spec constants must always default to
// zero for DXVK to handle them properly
layout(constant_id = 0) const uint hud_color_space = 0;
vec3 encodeSrgb(vec3 linear) {
bvec3 isLo = lessThanEqual(linear, vec3(0.0031308f));
vec3 loPart = linear * 12.92f;
vec3 hiPart = pow(linear, vec3(5.0f / 12.0f)) * 1.055f - 0.055f;
return mix(hiPart, loPart, isLo);
}
vec3 encodePq(vec3 nits) {
const float c1 = 0.8359375f;
const float c2 = 18.8515625f;
const float c3 = 18.6875f;
const float m1 = 0.1593017578125f;
const float m2 = 78.84375f;
vec3 y = clamp(nits / 10000.0f, vec3(0.0f), vec3(1.0f));
vec3 y_m1 = pow(y, vec3(m1));
vec3 num = c1 + c2 * y_m1;
vec3 den = 1.0f + c3 * y_m1;
return pow(num / den, vec3(m2));
}
vec3 encodeScRgb(vec3 nits) {
return nits / 80.0f;
}
vec3 encodeOutput(vec3 linear) {
switch (hud_color_space) {
default:
return linear;
case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR:
return encodeSrgb(linear);
case VK_COLOR_SPACE_HDR10_ST2084_EXT: {
vec3 rec2020 = rec709_to_rec2020 * linear;
return encodePq(rec2020 * HUD_NITS);
return color;
}
case VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT:
return encodeScRgb(linear * HUD_NITS);
color.rgb = nits_to_sc_rgb(color.rgb * SDR_NITS);
return color;
case VK_COLOR_SPACE_HDR10_ST2084_EXT:
color.rgb = rec709_to_rec2020 * color.rgb;
color.rgb = nits_to_pq(color.rgb * SDR_NITS);
return color;
default: /* pass through */
return color;
}
}

View File

@ -0,0 +1,197 @@
#version 460
#extension GL_KHR_memory_scope_semantics : enable
#define NUM_FRAME_TIME_STAMPS (420u)
#define MAX_TEXT_LENGTH (16u)
#define MAX_TEXT_DRAWS (2u)
struct draw_param_t {
uint vertex_count;
uint instance_count;
uint first_vertex;
uint first_instance;
};
struct draw_info_t {
uint text_offset;
uint text_length_and_size;
uint packed_xy;
uint color;
};
layout(binding = 0, std430)
workgroupcoherent buffer timestamp_buffer_t {
uvec2 frame_timestamps_raw[2u];
float frame_interval_ms[NUM_FRAME_TIME_STAMPS];
float frame_time_avg_ms;
float frame_time_min_ms;
float frame_time_max_ms;
};
layout(binding = 1, std430)
writeonly buffer draw_param_buffer_t {
draw_param_t draw_params[];
};
layout(binding = 2, std430)
writeonly buffer draw_info_buffer_t {
draw_info_t draw_infos[];
};
layout(binding = 3)
uniform writeonly uimageBuffer text_buffer;
layout(push_constant)
uniform push_data_t {
float ms_per_tick;
uint curr_data_point;
uint packed_xy_for_min;
uint packed_xy_for_max;
};
layout(local_size_x = 256) in;
uint extract_digit(inout float number) {
float high_part = floor(number / 10.0f + 0.03125f);
float digit = number - 10.0f * high_part;
number = high_part;
return uint(digit);
}
// Three-way reduction: Sum, Min, Max
shared vec3 ms_shared[NUM_FRAME_TIME_STAMPS / 2u];
shared uint text_chars[MAX_TEXT_DRAWS][MAX_TEXT_LENGTH];
shared uint text_length[MAX_TEXT_DRAWS];
void main() {
uint tid = gl_LocalInvocationIndex;
if (tid == 0u) {
uint curr_index = curr_data_point & 1u;
uint prev_index = curr_index ^ 1u;
uvec2 curr_time = frame_timestamps_raw[curr_index];
uvec2 prev_time = frame_timestamps_raw[prev_index];
// We can't require 64-bit integer support, just do this manually
// and account for the possibility of timestamps wrapping around.
uvec2 borrow;
uvec2 time_diff;
time_diff.x = usubBorrow(curr_time.x, prev_time.x, borrow.x);
time_diff.y = usubBorrow(curr_time.y, prev_time.y + borrow.x, borrow.y);
if (borrow.y != 0u)
time_diff = uvec2(0u);
// Ignore first frame that has no timestamp at all
if ((prev_time.x | prev_time.y) == 0u)
time_diff = uvec2(0u);
// We will most likely lose a few bits here, but that's fine
float ticks = time_diff.x + (time_diff.y * pow(2.0f, 32.0f));
frame_interval_ms[curr_data_point] = ticks * ms_per_tick;
}
controlBarrier(gl_ScopeWorkgroup, gl_ScopeWorkgroup,
gl_StorageSemanticsBuffer, gl_SemanticsAcquireRelease);
// Perform initial reduction on frame interval data and write
// everything to shared memory. There are more efficient ways
// to do reductions, but we cannot require a lot of shader
// features just for the frame time HUD.
if (2u * tid < NUM_FRAME_TIME_STAMPS) {
float a = frame_interval_ms[2u * tid + 0u];
float b = frame_interval_ms[2u * tid + 1u];
ms_shared[tid] = vec3(a + b, min(a, b), max(a, b));
}
barrier();
uint input_count = NUM_FRAME_TIME_STAMPS / 2u;
while (input_count > 1u) {
uint output_count = (input_count + 1u) / 2u;
if (tid + output_count < input_count) {
vec3 a = ms_shared[tid];
vec3 b = ms_shared[tid + output_count];
ms_shared[tid] = vec3(a.x + b.x,
min(a.y, b.y), max(a.z, b.z));
}
barrier();
input_count = output_count;
}
// Write reduced stats to buffer
vec3 stats = ms_shared[0u];
if (tid == 0u) {
frame_time_avg_ms = stats.x / float(NUM_FRAME_TIME_STAMPS);
frame_time_min_ms = stats.y;
frame_time_max_ms = stats.z;
}
if (tid < MAX_TEXT_DRAWS) {
// Convert number to string with a single decimal point. This
// is fairly naive code, but this is not performance-critical.
text_chars[tid][0] = 0x73; // 's'
text_chars[tid][1] = 0x6d; // 'm'
text_chars[tid][2] = 0x20; // ' '
uint zero = 0x30; // '0'
float number = round(10.0f * (tid == 0u ? stats.y : stats.z));
text_chars[tid][3] = zero + extract_digit(number);
text_chars[tid][4] = 0x2e; // '.'
text_chars[tid][5] = zero + extract_digit(number);
uint len = 6u;
while (number > 0.0f && len < MAX_TEXT_LENGTH)
text_chars[tid][len++] = zero + extract_digit(number);
text_length[tid] = len;
// Emit draw infos for the text
draw_infos[tid].text_offset = MAX_TEXT_LENGTH * tid;
draw_infos[tid].text_length_and_size = len | (12u << 16u);
draw_infos[tid].packed_xy = tid == 0u
? packed_xy_for_min : packed_xy_for_max;
draw_infos[tid].color = 0xffffffffu;
// Emit indirect draw parameters
draw_params[tid].vertex_count = 6u * len;
draw_params[tid].instance_count = 1u;
draw_params[tid].first_vertex = 0u;
draw_params[tid].first_instance = 0u;
}
barrier();
// Use remaining threads to write text into the acual string
// buffer. Characters are stored in reverse order in LDS, so
// fix that up.
uint text_id = tid / MAX_TEXT_LENGTH;
uint text_ch = tid % MAX_TEXT_LENGTH;
if (text_id < MAX_TEXT_DRAWS) {
uint len = text_length[text_id];
uint ch = 0x20; // ' '
if (text_ch < len)
ch = text_chars[text_id][len - text_ch - 1u];
imageStore(text_buffer, int(tid), uvec4(ch));
}
}

View File

@ -7,48 +7,80 @@
layout(location = 0) in vec2 v_coord;
layout(location = 0) out vec4 o_color;
struct line_point_t {
float value;
uint color;
};
#define NUM_FRAME_TIME_STAMPS (420)
layout(binding = 0, std430)
readonly buffer line_data_t {
line_point_t points[];
readonly buffer timestamp_buffer_t {
uvec2 frame_timestamps_raw[2u];
float frame_interval_ms[NUM_FRAME_TIME_STAMPS];
float frame_time_avg_ms;
float frame_time_min_ms;
float frame_time_max_ms;
};
layout(push_constant)
uniform push_data_t {
uint offset;
uint count;
vec2 pos;
vec2 size;
vec2 scale;
uvec2 surface_size;
float opacity;
float scale;
uint packed_xy;
uint packed_wh;
uint frame_index;
};
void main() {
float cx = v_coord.x * float(count);
float fx = fract(cx);
int max_index = NUM_FRAME_TIME_STAMPS - 1;
// We need to roll our own linear interpolation here
uint p0_idx = min(uint(floor(cx)), count - 1) + offset;
uint p1_idx = min(uint(ceil(cx)), count - 1) + offset;
int compute_real_index(int local_index) {
int real_index = int(frame_index) - local_index;
line_point_t p0 = points[p0_idx];
line_point_t p1 = points[p1_idx];
if (real_index < 0)
real_index += NUM_FRAME_TIME_STAMPS;
float value = mix(p0.value, p1.value, fx);
float alpha = value + v_coord.y - 1.0f;
alpha = min(alpha / dFdy(v_coord.y), 1.0f);
if (alpha <= 0.0f)
discard;
o_color = mix(
unpackUnorm4x8(p0.color),
unpackUnorm4x8(p1.color), fx);
o_color *= alpha * opacity;
o_color.rgb = encodeOutput(o_color.rgb);
return real_index;
}
float sample_at(float position) {
int local_index = int(position);
int lo_index = compute_real_index(clamp(local_index, 0, max_index));
int hi_index = compute_real_index(clamp(local_index + 1, 0, max_index));
float lo_value = frame_interval_ms[lo_index];
float hi_value = frame_interval_ms[hi_index];
return mix(lo_value, hi_value, fract(position));
}
void main() {
float x_pos = (1.0f - v_coord.x) * float(NUM_FRAME_TIME_STAMPS);
float x_delta = abs(dFdx(x_pos)) / 2.0f;
float y_delta = abs(dFdy(v_coord.y));
float ms_l = sample_at(x_pos - x_delta);
float ms_r = sample_at(x_pos + x_delta);
float ms_lo = min(ms_l, ms_r);
float ms_hi = max(ms_l, ms_r);
float ms_max = clamp(max(frame_time_max_ms, frame_time_avg_ms * 2.0f), 20.0f, 200.0f);
float val_lo = min(ms_lo / ms_max, 1.0f) - y_delta;
float val_hi = min(ms_hi / ms_max, 1.0f) - y_delta;
float diff_lo = min(v_coord.y - val_lo, 0.0f);
float diff_hi = min(val_hi - v_coord.y, 0.0f);
float ms_y = ms_max * v_coord.y;
vec4 line_color = vec4(0.776f, 0.812f, 0.882f, 1.0f);
vec4 bg_color = vec4(0.0f, 0.0f, 0.0f, 0.75f);
// Try to draw a somewhat defined line
float diff = (diff_lo + diff_hi) + y_delta;
o_color = mix(bg_color, line_color, clamp(diff / y_delta, 0.0f, 1.0f));
o_color = linear_to_output(o_color);
o_color.a *= opacity;
}

View File

@ -4,21 +4,35 @@ layout(location = 0) out vec2 o_coord;
layout(push_constant)
uniform push_data_t {
uint offset;
uint count;
vec2 pos;
vec2 size;
vec2 scale;
uvec2 surface_size;
float opacity;
float scale;
uint packed_xy;
uint packed_wh;
uint frame_index;
};
vec2 unpack_u16(uint v) {
// Inputs may be signed
int hi = int(v);
int lo = int(v << 16);
return vec2(float(lo >> 16), float(hi >> 16));
}
void main() {
vec2 coord = vec2(
float(gl_VertexIndex & 1),
float(gl_VertexIndex >> 1));
o_coord = coord;
o_coord = vec2(coord.x, 1.0f - coord.y);
vec2 surface_size_f = vec2(surface_size) / scale;
vec2 pos = unpack_u16(packed_xy);
pos = mix(pos, surface_size_f + pos, lessThan(pos, vec2(0.0f)));
vec2 size = unpack_u16(packed_wh);
vec2 pixel_pos = pos + size * coord;
vec2 scaled_pos = 2.0f * scale * pixel_pos - 1.0f;
vec2 scaled_pos = 2.0f * (pixel_pos / surface_size_f) - 1.0f;
gl_Position = vec4(scaled_pos, 0.0f, 1.0f);
}

View File

@ -4,7 +4,14 @@
#include "hud_frag_common.glsl"
layout(binding = 2) uniform sampler2D s_font;
layout(binding = 3) uniform sampler2D s_font;
layout(push_constant)
uniform push_data_t {
uvec2 surface_size;
float opacity;
float scale;
};
layout(location = 0) in vec2 v_texcoord;
layout(location = 1) in vec4 v_color;
@ -25,8 +32,8 @@ void main() {
vec3 r_shadow = vec3(0.0f, 0.0f, 0.0f);
o_color.rgb = mix(r_shadow, r_center, r_alpha_center);
o_color.a = r_alpha_shadow * v_color.a;
o_color.a = r_alpha_shadow * v_color.a * opacity;
o_color.rgb *= o_color.a;
o_color.rgb = encodeOutput(o_color.rgb);
o_color = linear_to_output(o_color);
}

View File

@ -1,4 +1,4 @@
#version 450
#version 460
struct font_info_t {
float size;
@ -18,15 +18,25 @@ readonly buffer font_buffer_t {
glyph_info_t glyph_data[];
};
layout(binding = 1) uniform usamplerBuffer text_buffer;
struct draw_info_t {
uint text_offset;
uint text_length_and_size;
uint packed_xy;
uint color;
};
layout(binding = 1, std430)
readonly buffer draw_buffer_t {
draw_info_t draw_infos[];
};
layout(binding = 2) uniform usamplerBuffer text_buffer;
layout(push_constant)
uniform push_data_t {
vec4 text_color;
vec2 text_pos;
uint text_offset;
float text_size;
vec2 hud_scale;
uvec2 surface_size;
float opacity;
float scale;
};
layout(location = 0) out vec2 o_texcoord;
@ -42,7 +52,8 @@ vec2 unpack_u16(uint v) {
}
void main() {
o_color = text_color;
draw_info_t draw_info = draw_infos[gl_DrawID];
o_color = unpackUnorm4x8(draw_info.color);
// Compute character index and vertex index for the current
// character. We'll render two triangles per character.
@ -50,7 +61,7 @@ void main() {
uint vtx_idx = gl_VertexIndex - 6 * chr_idx;
// Load glyph info based on vertex index
uint glyph_idx = texelFetch(text_buffer, int(text_offset + chr_idx)).x;
uint glyph_idx = texelFetch(text_buffer, int(draw_info.text_offset + chr_idx)).x;
glyph_info_t glyph_info = glyph_data[glyph_idx];
// Compute texture coordinate from glyph data
@ -64,12 +75,18 @@ void main() {
// Compute vertex position. We can easily do this here since our
// font is a monospace font, otherwise we'd need to preprocess
// the strings to render in a compute shader.
float size_factor = text_size / font_data.size;
uint text_size = bitfieldExtract(draw_info.text_length_and_size, 16, 16);
float size_factor = float(text_size) / font_data.size;
vec2 surface_size_f = vec2(surface_size) / scale;
vec2 text_pos = unpack_u16(draw_info.packed_xy);
text_pos = mix(text_pos, surface_size_f + text_pos, lessThan(text_pos, vec2(0.0f)));
vec2 local_pos = tex_wh * coord - unpack_u16(glyph_info.packed_origin)
+ vec2(font_data.advance * float(chr_idx), 0.0f);
vec2 pixel_pos = text_pos + size_factor * local_pos;
vec2 scaled_pos = 2.0f * hud_scale * pixel_pos - 1.0f;
vec2 scaled_pos = 2.0f * (pixel_pos / surface_size_f) - 1.0f;
gl_Position = vec4(scaled_pos, 0.0f, 1.0f);
}

View File

@ -53,7 +53,10 @@ dxvk_shaders = files([
'hud/shaders/hud_chunk_frag_background.frag',
'hud/shaders/hud_chunk_frag_visualize.frag',
'hud/shaders/hud_chunk_vert.vert',
'hud/shaders/hud_chunk_vert_background.vert',
'hud/shaders/hud_chunk_vert_visualize.vert',
'hud/shaders/hud_frame_time_eval.comp',
'hud/shaders/hud_graph_frag.frag',
'hud/shaders/hud_graph_vert.vert',