1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2025-02-21 22:54:16 +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_imageViews.at(imageIndex), m_colorspace, VkRect2D(),
m_swapImageView, m_colorspace, VkRect2D()); m_swapImageView, m_colorspace, VkRect2D());
// if (m_hud != nullptr) if (m_hud) {
// m_hud->render(m_context, info.format, info.imageExtent); m_hud->render(m_context->beginExternalRendering(),
m_imageViews.at(imageIndex), m_colorspace);
}
m_blitter->endPresent(m_context->beginExternalRendering(), m_blitter->endPresent(m_context->beginExternalRendering(),
m_imageViews.at(imageIndex)); m_imageViews.at(imageIndex));

View File

@ -16,28 +16,23 @@ namespace dxvk::hud {
HudPos HudSamplerCount::render( HudPos HudSamplerCount::render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position) { const HudPipelineKey& key,
position.y += 16.0f; 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.y += 8;
{ 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;
return position; return position;
} }
HudTextureMemory::HudTextureMemory(D3D9DeviceEx* device) HudTextureMemory::HudTextureMemory(D3D9DeviceEx* device)
: m_device (device) : m_device (device)
, m_allocatedString ("") , m_allocatedString ("")
, m_mappedString ("") {} , m_mappedString ("") { }
void HudTextureMemory::update(dxvk::high_resolution_clock::time_point time) { void HudTextureMemory::update(dxvk::high_resolution_clock::time_point time) {
@ -62,40 +57,26 @@ namespace dxvk::hud {
HudPos HudTextureMemory::render( HudPos HudTextureMemory::render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position) { const HudPipelineKey& key,
position.y += 16.0f; 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.y += 20;
{ position.x, position.y }, renderer.drawText(16, position, 0xffc0ff00u, "Mapped:");
{ 0.0f, 1.0f, 0.75f, 1.0f }, renderer.drawText(16, { position.x + 120, position.y }, 0xffffffffu, m_mappedString);
"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 += 8;
return position; return position;
} }
HudFixedFunctionShaders::HudFixedFunctionShaders(D3D9DeviceEx* device) HudFixedFunctionShaders::HudFixedFunctionShaders(D3D9DeviceEx* device)
: m_device (device) : m_device (device)
, m_ffShaderCount ("") {} , m_ffShaderCount ("") {}
void HudFixedFunctionShaders::update(dxvk::high_resolution_clock::time_point time) { void HudFixedFunctionShaders::update(dxvk::high_resolution_clock::time_point time) {
@ -108,29 +89,26 @@ namespace dxvk::hud {
HudPos HudFixedFunctionShaders::render( HudPos HudFixedFunctionShaders::render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position) { const HudPipelineKey& key,
position.y += 16.0f; 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.y += 8;
{ 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;
return position; return position;
} }
HudSWVPState::HudSWVPState(D3D9DeviceEx* device) HudSWVPState::HudSWVPState(D3D9DeviceEx* device)
: m_device (device) : m_device (device)
, m_isSWVPText ("") {} , m_isSWVPText ("") {}
void HudSWVPState::update(dxvk::high_resolution_clock::time_point time) { void HudSWVPState::update(dxvk::high_resolution_clock::time_point time) {
if (m_device->IsSWVP()) { if (m_device->IsSWVP()) {
if (m_device->CanOnlySWVP()) { if (m_device->CanOnlySWVP()) {
@ -149,21 +127,16 @@ namespace dxvk::hud {
HudPos HudSWVPState::render( HudPos HudSWVPState::render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position) { const HudPipelineKey& key,
position.y += 16.0f; 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.y += 8;
{ 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;
return position; return position;
} }

View File

@ -17,8 +17,11 @@ namespace dxvk::hud {
void update(dxvk::high_resolution_clock::time_point time); void update(dxvk::high_resolution_clock::time_point time);
HudPos render( HudPos render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position); const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
private: private:
@ -28,80 +31,92 @@ namespace dxvk::hud {
}; };
/** /**
* \brief HUD item to display unmappable memory * \brief HUD item to display unmappable memory
*/ */
class HudTextureMemory : public HudItem { class HudTextureMemory : public HudItem {
constexpr static int64_t UpdateInterval = 500'000; 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( private:
HudRenderer& renderer,
HudPos position);
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; dxvk::high_resolution_clock::time_point m_lastUpdate
uint32_t m_maxUsed = 0; = dxvk::high_resolution_clock::now();
uint32_t m_maxMapped = 0;
dxvk::high_resolution_clock::time_point m_lastUpdate std::string m_allocatedString;
= dxvk::high_resolution_clock::now(); std::string m_mappedString;
std::string m_allocatedString; };
std::string m_mappedString;
};
/** /**
* \brief HUD item to display amount of generated fixed function shaders * \brief HUD item to display amount of generated fixed function shaders
*/ */
class HudFixedFunctionShaders : public HudItem { class HudFixedFunctionShaders : public HudItem {
public:
HudFixedFunctionShaders(D3D9DeviceEx* device); public:
void update(dxvk::high_resolution_clock::time_point time); HudFixedFunctionShaders(D3D9DeviceEx* device);
HudPos render( void update(dxvk::high_resolution_clock::time_point time);
HudRenderer& renderer,
HudPos position);
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( public:
HudRenderer& renderer,
HudPos position);
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, m_wctx->imageViews.at(imageIndex), m_colorspace, dstRect,
swapImageView, m_colorspace, srcRect); swapImageView, m_colorspace, srcRect);
// if (m_hud != nullptr) if (m_hud) {
// m_hud->render(m_context, info.format, info.imageExtent); m_hud->render(m_context->beginExternalRendering(),
m_wctx->imageViews.at(imageIndex), m_colorspace);
}
m_blitter->endPresent(m_context->beginExternalRendering(), m_blitter->endPresent(m_context->beginExternalRendering(),
m_wctx->imageViews.at(imageIndex)); m_wctx->imageViews.at(imageIndex));

View File

@ -8,112 +8,51 @@ namespace dxvk::hud {
const Rc<DxvkDevice>& device) const Rc<DxvkDevice>& device)
: m_device (device), : m_device (device),
m_renderer (device), m_renderer (device),
m_hudItems (device), m_hudItems (device) {
m_scale (m_hudItems.getOption<float>("scale", 1.0f)), // Retrieve and sanitize options
m_opacity (m_hudItems.getOption<float>("opacity", 1.0f)) { m_options.scale = std::clamp(m_hudItems.getOption<float>("scale", 1.0f), 0.25f, 4.0f);
// Sanitize scaling factor m_options.opacity = std::clamp(m_hudItems.getOption<float>("opacity", 1.0f), 0.1f, 1.0f);
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;
addItem<HudVersionItem>("version", -1); addItem<HudVersionItem>("version", -1);
addItem<HudDeviceInfoItem>("devinfo", -1, m_device); addItem<HudDeviceInfoItem>("devinfo", -1, m_device);
addItem<HudFpsItem>("fps", -1); addItem<HudFpsItem>("fps", -1);
addItem<HudFrameTimeItem>("frametimes", -1); addItem<HudFrameTimeItem>("frametimes", -1, device, &m_renderer);
addItem<HudSubmissionStatsItem>("submissions", -1, device); addItem<HudSubmissionStatsItem>("submissions", -1, device);
addItem<HudDrawCallStatsItem>("drawcalls", -1, device); addItem<HudDrawCallStatsItem>("drawcalls", -1, device);
addItem<HudPipelineStatsItem>("pipelines", -1, device); addItem<HudPipelineStatsItem>("pipelines", -1, device);
addItem<HudDescriptorStatsItem>("descriptors", -1, device); addItem<HudDescriptorStatsItem>("descriptors", -1, device);
addItem<HudMemoryStatsItem>("memory", -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<HudCsThreadItem>("cs", -1, device);
addItem<HudGpuLoadItem>("gpuload", -1, device); addItem<HudGpuLoadItem>("gpuload", -1, device);
addItem<HudCompilerActivityItem>("compiler", -1, device); addItem<HudCompilerActivityItem>("compiler", -1, device);
} }
Hud::~Hud() { Hud::~Hud() {
} }
void Hud::update() { void Hud::update() {
m_hudItems.update(); m_hudItems.update();
} }
void Hud::render( void Hud::render(
const Rc<DxvkContext>& ctx, const DxvkContextObjects& ctx,
VkSurfaceFormatKHR surfaceFormat, const Rc<DxvkImageView>& dstView,
VkExtent2D surfaceSize) { VkColorSpaceKHR dstColorSpace) {
this->setupRendererState(ctx, surfaceFormat, surfaceSize); auto key = m_renderer.getPipelineKey(dstView, dstColorSpace);
this->renderHudElements(ctx);
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) { Rc<Hud> Hud::createHud(const Rc<DxvkDevice>& device) {
return new Hud(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" #include "dxvk_hud_renderer.h"
namespace dxvk::hud { namespace dxvk::hud {
/**
* \brief HUD uniform data
* Shader data for the HUD.
*/
struct HudUniformData {
VkExtent2D surfaceSize;
};
/** /**
* \brief DXVK HUD * \brief DXVK HUD
* *
@ -41,13 +33,14 @@ namespace dxvk::hud {
* \brief Render HUD * \brief Render HUD
* *
* Renders the HUD to the given context. * Renders the HUD to the given context.
* \param [in] ctx Device context * \param [in] ctx Context objects for rendering
* \param [in] surfaceSize Image size, in pixels * \param [in] dstView Swap chain image view
* \param [in] dstColorSpace Color space
*/ */
void render( void render(
const Rc<DxvkContext>& ctx, const DxvkContextObjects& ctx,
VkSurfaceFormatKHR surfaceFormat, const Rc<DxvkImageView>& dstView,
VkExtent2D surfaceSize); VkColorSpaceKHR dstColorSpace);
/** /**
* \brief Adds a HUD item if enabled * \brief Adds a HUD item if enabled
@ -74,25 +67,12 @@ namespace dxvk::hud {
private: private:
const Rc<DxvkDevice> m_device; Rc<DxvkDevice> m_device;
DxvkRasterizerState m_rsState;
DxvkBlendMode m_blendMode;
HudUniformData m_uniformData;
HudRenderer m_renderer; HudRenderer m_renderer;
HudItemSet m_hudItems; HudItemSet m_hudItems;
float m_scale; HudOptions m_options;
float m_opacity;
void setupRendererState(
const Rc<DxvkContext>& ctx,
VkSurfaceFormatKHR surfaceFormat,
VkExtent2D surfaceSize);
void renderHudElements(
const Rc<DxvkContext>& ctx);
}; };

View File

@ -2,7 +2,13 @@
#include <hud_chunk_frag_background.h> #include <hud_chunk_frag_background.h>
#include <hud_chunk_frag_visualize.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 <iomanip>
#include <version.h> #include <version.h>
@ -70,11 +76,15 @@ namespace dxvk::hud {
} }
void HudItemSet::render(HudRenderer& renderer) { void HudItemSet::render(
HudPos position = { 8.0f, 8.0f }; const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer) {
HudPos position = { 8, 8 };
for (const auto& item : m_items) 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( HudPos HudVersionItem::render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position) { const HudPipelineKey& key,
position.y += 16.0f; const HudOptions& options,
HudRenderer& renderer,
HudPos position) {
position.y += 16;
renderer.drawText(16, position, 0xffffffffu, "DXVK " DXVK_VERSION);
renderer.drawText(16.0f, position.y += 8;
{ position.x, position.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
"DXVK " DXVK_VERSION);
position.y += 8.0f;
return position; return position;
} }
@ -114,16 +123,15 @@ namespace dxvk::hud {
HudPos HudClientApiItem::render( HudPos HudClientApiItem::render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position) { const HudPipelineKey& key,
position.y += 16.0f; const HudOptions& options,
HudRenderer& renderer,
HudPos position) {
position.y += 16;
renderer.drawText(16, position, 0xffffffffu, m_api);
renderer.drawText(16.0f, position.y += 8;
{ position.x, position.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
m_api);
position.y += 8.0f;
return position; return position;
} }
@ -148,27 +156,21 @@ namespace dxvk::hud {
HudPos HudDeviceInfoItem::render( HudPos HudDeviceInfoItem::render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position) { const HudPipelineKey& key,
position.y += 16.0f; const HudOptions& options,
renderer.drawText(16.0f, HudRenderer& renderer,
{ position.x, position.y }, HudPos position) {
{ 1.0f, 1.0f, 1.0f, 1.0f }, position.y += 16;
m_deviceName); renderer.drawText(16, position, 0xffffffffu, m_deviceName);
position.y += 24.0f; position.y += 24;
renderer.drawText(16.0f, renderer.drawText(16, position, 0xffffffffu, m_driverName);
{ position.x, position.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
m_driverName);
position.y += 20.0f; position.y += 20;
renderer.drawText(16.0f, renderer.drawText(16, position, 0xffffffffu, m_driverVer);
{ position.x, position.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
m_driverVer);
position.y += 8.0f; position.y += 8;
return position; return position;
} }
@ -193,108 +195,536 @@ namespace dxvk::hud {
HudPos HudFpsItem::render( HudPos HudFpsItem::render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position) { const HudPipelineKey& key,
position.y += 16.0f; const HudOptions& options,
HudRenderer& renderer,
HudPos position) {
position.y += 16;
renderer.drawText(16.0f, renderer.drawText(16, position, 0xff4040ffu, "FPS:");
{ position.x, position.y }, renderer.drawText(16, { position.x + 60, position.y },
{ 1.0f, 0.25f, 0.25f, 1.0f }, 0xffffffffu, m_frameRate);
"FPS:");
renderer.drawText(16.0f, position.y += 8;
{ position.x + 60.0f, position.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
m_frameRate);
position.y += 8.0f;
return position; return position;
} }
HudFrameTimeItem::HudFrameTimeItem() { } HudFrameTimeQueryPool::HudFrameTimeQueryPool(
HudFrameTimeItem::~HudFrameTimeItem() { } 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) { HudFrameTimeQueryPool::~HudFrameTimeQueryPool() {
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(time - m_lastUpdate); 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( HudPos HudFrameTimeItem::render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position) { const HudPipelineKey& key,
std::array<HudGraphPoint, NumDataPoints> points; const HudOptions& options,
HudRenderer& renderer,
HudPos position) {
if (!m_gpuBuffer)
createResources(ctx);
// 60 FPS = optimal, 10 FPS = worst HudPos minPos = { 12, -128 };
const float targetUs = 16'666.6f; HudPos maxPos = { 162, -128 };
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;
renderer.drawText(12.0f, HudPos graphPos = { 8, -120 };
{ position.x, position.y }, HudPos graphSize = { NumDataPoints, 80 };
{ 1.0f, 0.25f, 0.25f, 1.0f },
"min:"); 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; 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) HudSubmissionStatsItem::HudSubmissionStatsItem(const Rc<DxvkDevice>& device)
: m_device(device) { : m_device(device) {
@ -342,32 +772,20 @@ namespace dxvk::hud {
HudPos HudSubmissionStatsItem::render( HudPos HudSubmissionStatsItem::render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position) { const HudPipelineKey& key,
position.y += 16.0f; 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.y += 20;
{ position.x, position.y }, renderer.drawText(16, position, 0xff4080ff, "Queue syncs:");
{ 1.0f, 0.5f, 0.25f, 1.0f }, renderer.drawText(16, { position.x + 228, position.y }, 0xffffffffu, m_syncString);
"Queue submissions:");
renderer.drawText(16.0f, position.y += 8;
{ 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;
return position; return position;
} }
@ -403,53 +821,28 @@ namespace dxvk::hud {
HudPos HudDrawCallStatsItem::render( HudPos HudDrawCallStatsItem::render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position) { const HudPipelineKey& key,
position.y += 16.0f; const HudOptions& options,
renderer.drawText(16.0f, HudRenderer& renderer,
{ position.x, position.y }, HudPos position) {
{ 0.25f, 0.5f, 1.0f, 1.0f }, position.y += 16;
"Draw calls:"); 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.y += 20;
{ position.x + 192.0f, position.y }, renderer.drawText(16, position, 0xffff8040, "Dispatch calls:");
{ 1.0f, 1.0f, 1.0f, 1.0f }, renderer.drawText(16, { position.x + 192, position.y }, 0xffffffffu, str::format(m_cpCount));
str::format(m_gpCount));
position.y += 20.0f; position.y += 20;
renderer.drawText(16.0f, renderer.drawText(16, position, 0xffff8040, "Render passes:");
{ position.x, position.y }, renderer.drawText(16, { position.x + 192, position.y }, 0xffffffffu, str::format(m_rpCount));
{ 0.25f, 0.5f, 1.0f, 1.0f },
"Dispatch calls:");
renderer.drawText(16.0f, position.y += 20;
{ position.x + 192.0f, position.y }, renderer.drawText(16, position, 0xffff8040, "Barriers:");
{ 1.0f, 1.0f, 1.0f, 1.0f }, renderer.drawText(16, { position.x + 192, position.y }, 0xffffffffu, str::format(m_pbCount));
str::format(m_cpCount));
position.y += 20.0f; position.y += 8;
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;
return position; return position;
} }
@ -475,44 +868,26 @@ namespace dxvk::hud {
HudPos HudPipelineStatsItem::render( HudPos HudPipelineStatsItem::render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position) { const HudPipelineKey& key,
position.y += 16.0f; const HudOptions& options,
renderer.drawText(16.0f, HudRenderer& renderer,
{ position.x, position.y }, HudPos position) {
{ 1.0f, 0.25f, 1.0f, 1.0f }, position.y += 16;
"Graphics pipelines:"); renderer.drawText(16, position, 0xffff40ff, "Graphics pipelines:");
renderer.drawText(16, { position.x + 240, position.y }, 0xffffffffu, str::format(m_graphicsPipelines));
renderer.drawText(16.0f,
{ position.x + 240.0f, position.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
str::format(m_graphicsPipelines));
if (m_graphicsLibraries) { if (m_graphicsLibraries) {
position.y += 20.0f; position.y += 20;
renderer.drawText(16.0f, renderer.drawText(16, position, 0xffff40ff, "Graphics shaders:");
{ position.x, position.y }, renderer.drawText(16, { position.x + 240, position.y }, 0xffffffffu, str::format(m_graphicsLibraries));
{ 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.0f; position.y += 20;
renderer.drawText(16.0f, renderer.drawText(16, position, 0xffff40ff, "Compute shaders:");
{ position.x, position.y }, renderer.drawText(16, { position.x + 240, position.y }, 0xffffffffu, str::format(m_computePipelines));
{ 1.0f, 0.25f, 1.0f, 1.0f },
"Compute pipelines:");
renderer.drawText(16.0f, position.y += 8;
{ position.x + 240.0f, position.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
str::format(m_computePipelines));
position.y += 8.0f;
return position; return position;
} }
@ -537,31 +912,20 @@ namespace dxvk::hud {
HudPos HudDescriptorStatsItem::render( HudPos HudDescriptorStatsItem::render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position) { const HudPipelineKey& key,
position.y += 16.0f; const HudOptions& options,
renderer.drawText(16.0f, HudRenderer& renderer,
{ position.x, position.y }, HudPos position) {
{ 1.0f, 0.25f, 0.5f, 1.0f }, position.y += 16;
"Descriptor pools:"); 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_descriptorPoolCount));
position.y += 20.0f;
renderer.drawText(16.0f,
{ position.x, position.y },
{ 1.0f, 0.25f, 0.5f, 1.0f },
"Descriptor sets:");
renderer.drawText(16.0f, position.y += 20;
{ position.x + 216.0f, position.y }, renderer.drawText(16, position, 0xff8040ff, "Descriptor sets:");
{ 1.0f, 1.0f, 1.0f, 1.0f }, renderer.drawText(16, { position.x + 216, position.y }, 0xffffffffu, str::format(m_descriptorSetCount));
str::format(m_descriptorSetCount));
position.y += 8.0f; position.y += 8;
return position; return position;
} }
@ -584,8 +948,11 @@ namespace dxvk::hud {
HudPos HudMemoryStatsItem::render( HudPos HudMemoryStatsItem::render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position) { const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position) {
for (uint32_t i = 0; i < m_memory.memoryHeapCount; i++) { for (uint32_t i = 0; i < m_memory.memoryHeapCount; i++) {
bool isDeviceLocal = m_memory.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT; 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::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"); std::setw(5 + (percentage < 10 ? 1 : 0) + (percentage < 100 ? 1 : 0)), memUsedMib, " MB used");
position.y += 16.0f; position.y += 16;
renderer.drawText(16.0f, renderer.drawText(16, position, 0xff40ffffu, label);
{ position.x, position.y }, renderer.drawText(16, { position.x + 168, position.y }, 0xffffffffu, text);
{ 1.0f, 1.0f, 0.25f, 1.0f },
label);
renderer.drawText(16.0f, position.y += 4;
{ position.x + 168.0f, position.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
text);
position.y += 4.0f;
} }
position.y += 4.0f; position.y += 4;
return position; return position;
} }
HudMemoryDetailsItem::HudMemoryDetailsItem(const Rc<DxvkDevice>& device) HudMemoryDetailsItem::HudMemoryDetailsItem(
: m_device(device) { const Rc<DxvkDevice>& device,
DxvkShaderCreateInfo shaderInfo; HudRenderer* renderer)
shaderInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; : m_device (device),
shaderInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; m_setLayout (createSetLayout()),
shaderInfo.pushConstSize = sizeof(ShaderArgs); m_pipelineLayout (createPipelineLayout()) {
shaderInfo.outputMask = 0x1; 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);
m_vs = new DxvkShader(shaderInfo, SpirvCodeBuffer(hud_chunk_vert)); 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);
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() { 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( HudPos HudMemoryDetailsItem::render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position) { const HudPipelineKey& key,
uploadChunkData(renderer); const HudOptions& options,
HudRenderer& renderer,
// Chunk memory per type, not including dedicated allocations HudPos position) {
std::array<VkDeviceSize, VK_MAX_MEMORY_TYPES> chunkMemoryAllocated = { }; // Layout, align the entire element to the bottom right.
std::array<VkDeviceSize, VK_MAX_MEMORY_TYPES> chunkMemoryUsed = { }; int32_t x = -564;
int32_t y = -20;
// 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;
}
if (m_displayCacheStats) { if (m_displayCacheStats) {
uint32_t hitCount = m_cacheStats.requestCount - m_cacheStats.missCount; uint32_t hitCount = m_cacheStats.requestCount - m_cacheStats.missCount;
uint32_t hitRate = (100 * hitCount) / std::max(m_cacheStats.requestCount, 1u); uint32_t hitRate = (100 * hitCount) / std::max(m_cacheStats.requestCount, 1u);
std::string cacheStr = str::format("Cache: ", m_cacheStats.size >> 10, " kB (", hitRate, "% hit)"); std::string cacheStr = str::format("Cache: ", m_cacheStats.size >> 10, " kB (", hitRate, "% hit)");
renderer.drawText(14, { x, y }, 0xffffffffu, cacheStr);
renderer.drawText(14.0f, y -= 24;
{ pos.x, pos.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
cacheStr);
} }
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; return position;
} }
void HudMemoryDetailsItem::uploadChunkData(HudRenderer& renderer) { void HudMemoryDetailsItem::drawChunk(
DxvkContext* context = renderer.getContext(); HudPos pos,
HudPos size,
VkDeviceSize size = sizeof(uint32_t) * m_stats.pageMasks.size(); uint32_t color,
const DxvkMemoryChunkStats& chunk) {
if (m_pageMaskBuffer == nullptr || m_pageMaskBuffer->info().size < size) { auto& draw = m_drawInfos.emplace_back();
DxvkBufferCreateInfo info = { }; draw.x = pos.x;
info.size = std::max(VkDeviceSize(1u << 14), draw.y = pos.y;
(VkDeviceSize(-1) >> bit::lzcnt(size - 1u)) + 1u); draw.w = size.x;
info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; draw.h = size.y;
info.access = VK_ACCESS_SHADER_READ_BIT; draw.pageMask = chunk.pageMaskOffset;
info.stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; draw.pageCount = chunk.pageCount;
draw.color = color;
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( void HudMemoryDetailsItem::flushDraws(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos pos, const HudPipelineKey& key,
HudPos size, const HudOptions& options,
const VkMemoryType& memoryType, HudRenderer& renderer) {
const DxvkMemoryChunkStats& stats) const { if (m_drawInfos.empty())
DxvkContext* context = renderer.getContext(); return;
VkExtent2D surfaceSize = renderer.surfaceSize();
static const DxvkInputAssemblyState iaState = { PipelinePair pipelines = getPipeline(renderer, key);
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
VK_FALSE, 0 };
context->setInputAssemblyState(iaState); ctx.cmd->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,
context->bindResourceBufferView(VK_SHADER_STAGE_FRAGMENT_BIT, 0, Rc<DxvkBufferView>(m_pageMaskView)); VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.background);
context->bindShader<VK_SHADER_STAGE_VERTEX_BIT>(Rc<DxvkShader>(m_vs)); // Bind resources
context->bindShader<VK_SHADER_STAGE_FRAGMENT_BIT>(Rc<DxvkShader>(m_fsBackground)); VkDescriptorSet set = ctx.descriptorPool->alloc(m_setLayout);
ShaderArgs args = { }; VkDescriptorBufferInfo drawDescriptor = { };
args.pos.x = pos.x - 1.0f; VkDescriptorBufferInfo dataDescriptor = { };
args.pos.y = pos.y - 1.0f;
args.size.x = size.x + 2.0f;
args.size.y = size.y + 2.0f;
args.scale.x = renderer.scale() / std::max(float(surfaceSize.width), 1.0f);
args.scale.y = renderer.scale() / std::max(float(surfaceSize.height), 1.0f);
args.opacity = renderer.opacity();
args.color = 0xc0000000u;
args.maskIndex = stats.pageMaskOffset;
args.pageCount = stats.pageCount;
context->pushConstants(0, sizeof(args), &args); updateDataBuffer(drawDescriptor, dataDescriptor);
context->draw(4, 1, 0, 0);
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; ctx.cmd->updateDescriptorSets(
args.size = size; descriptorWrites.size(),
descriptorWrites.data());
if (memoryType.propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) { ctx.cmd->cmdBindDescriptorSet(DxvkCmdBuffer::ExecBuffer,
args.color = 0xff208020u; VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout,
} else if (!(memoryType.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) { set, 0, nullptr);
if (!stats.mapped)
args.color = 0xff202020u; HudPushConstants pushConstants = renderer.getPushConstants();
else
args.color = 0xff202080u; ctx.cmd->cmdPushConstants(DxvkCmdBuffer::ExecBuffer, m_pipelineLayout,
} else if (stats.mapped) { VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
args.color = 0xff208080u; 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 { } 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); // Update draw infos and pad unused area with zeroes
context->draw(4, 1, 0, 0); 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( HudPos HudCsThreadItem::render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position) { const HudPipelineKey& key,
position.y += 16.0f; const HudOptions& options,
renderer.drawText(16.0f, HudRenderer& renderer,
{ position.x, position.y }, HudPos position) {
{ 0.25f, 1.0f, 0.25f, 1.0f }, position.y += 16;
"CS chunks:"); renderer.drawText(16, position, 0xff40ff40, "CS chunks:");
renderer.drawText(16, { position.x + 132, position.y }, 0xffffffffu, m_csChunkString);
renderer.drawText(16.0f, position.y += 20;
{ position.x + 132.0f, position.y }, renderer.drawText(16, position, 0xff40ff40, "CS syncs:");
{ 1.0f, 1.0f, 1.0f, 1.0f }, renderer.drawText(16, { position.x + 132, position.y }, 0xffffffffu, m_csSyncString);
m_csChunkString);
position.y += 20.0f; position.y += 8;
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;
return position; return position;
} }
@ -979,21 +1503,16 @@ namespace dxvk::hud {
HudPos HudGpuLoadItem::render( HudPos HudGpuLoadItem::render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position) { const HudPipelineKey& key,
position.y += 16.0f; 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.y += 8;
{ position.x, position.y },
{ 0.25f, 0.5f, 0.25f, 1.0f },
"GPU:");
renderer.drawText(16.0f,
{ position.x + 60.0f, position.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
m_gpuLoadString);
position.y += 8.0f;
return position; return position;
} }
@ -1047,18 +1566,18 @@ namespace dxvk::hud {
HudPos HudCompilerActivityItem::render( HudPos HudCompilerActivityItem::render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position) { const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position) {
if (m_show) { if (m_show) {
std::string string = "Compiling shaders..."; std::string string = "Compiling shaders...";
if (m_showPercentage) if (m_showPercentage)
string = str::format(string, " (", computePercentage(), "%)"); string = str::format(string, " (", computePercentage(), "%)");
renderer.drawText(16.0f, renderer.drawText(16, { position.x, -20 }, 0xffffffffu, string);
{ position.x, float(renderer.surfaceSize().height) / renderer.scale() - 20.0f },
{ 1.0f, 1.0f, 1.0f, 1.0f },
string);
} }
return position; return position;

View File

@ -33,13 +33,18 @@ namespace dxvk::hud {
/** /**
* \brief Renders the 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 * \param [in] position Base offset
* \returns Base offset for next item * \returns Base offset for next item
*/ */
virtual HudPos render( virtual HudPos render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position) = 0; const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position) = 0;
}; };
@ -70,7 +75,10 @@ namespace dxvk::hud {
* \returns Base offset for next item * \returns Base offset for next item
*/ */
void render( void render(
HudRenderer& renderer); const DxvkContextObjects& ctx,
const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer);
/** /**
* \brief Creates a HUD item if enabled * \brief Creates a HUD item if enabled
@ -129,8 +137,11 @@ namespace dxvk::hud {
public: public:
HudPos render( HudPos render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position); const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
}; };
@ -147,8 +158,11 @@ namespace dxvk::hud {
~HudClientApiItem(); ~HudClientApiItem();
HudPos render( HudPos render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position); const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
private: private:
@ -169,8 +183,11 @@ namespace dxvk::hud {
~HudDeviceInfoItem(); ~HudDeviceInfoItem();
HudPos render( HudPos render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position); const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
private: private:
@ -195,8 +212,11 @@ namespace dxvk::hud {
void update(dxvk::high_resolution_clock::time_point time); void update(dxvk::high_resolution_clock::time_point time);
HudPos render( HudPos render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position); const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
private: 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 { class HudFrameTimeQueryPool : public DxvkResource {
constexpr static size_t NumDataPoints = 304;
public: public:
HudFrameTimeItem(); HudFrameTimeQueryPool(
const Rc<DxvkDevice>& device);
~HudFrameTimeItem(); ~HudFrameTimeQueryPool();
void update(dxvk::high_resolution_clock::time_point time); VkQueryPool handle() const {
return m_pool;
HudPos render( }
HudRenderer& renderer,
HudPos position);
private: private:
dxvk::high_resolution_clock::time_point m_lastUpdate Rc<vk::DeviceFn> m_vkd;
= dxvk::high_resolution_clock::now(); 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); void update(dxvk::high_resolution_clock::time_point time);
HudPos render( HudPos render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position); const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
private: private:
@ -289,8 +419,11 @@ namespace dxvk::hud {
void update(dxvk::high_resolution_clock::time_point time); void update(dxvk::high_resolution_clock::time_point time);
HudPos render( HudPos render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position); const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
private: private:
@ -323,8 +456,11 @@ namespace dxvk::hud {
void update(dxvk::high_resolution_clock::time_point time); void update(dxvk::high_resolution_clock::time_point time);
HudPos render( HudPos render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position); const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
private: private:
@ -351,8 +487,11 @@ namespace dxvk::hud {
void update(dxvk::high_resolution_clock::time_point time); void update(dxvk::high_resolution_clock::time_point time);
HudPos render( HudPos render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position); const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
private: private:
@ -378,8 +517,11 @@ namespace dxvk::hud {
void update(dxvk::high_resolution_clock::time_point time); void update(dxvk::high_resolution_clock::time_point time);
HudPos render( HudPos render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position); const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
private: private:
@ -397,26 +539,36 @@ namespace dxvk::hud {
constexpr static int64_t UpdateInterval = 500'000; constexpr static int64_t UpdateInterval = 500'000;
public: public:
HudMemoryDetailsItem(const Rc<DxvkDevice>& device); HudMemoryDetailsItem(
const Rc<DxvkDevice>& device,
HudRenderer* renderer);
~HudMemoryDetailsItem(); ~HudMemoryDetailsItem();
void update(dxvk::high_resolution_clock::time_point time); void update(dxvk::high_resolution_clock::time_point time);
HudPos render( HudPos render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position); const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
private: private:
struct ShaderArgs { struct DrawInfo {
HudPos pos; int16_t x;
HudPos size; int16_t y;
HudPos scale; int16_t w;
float opacity; int16_t h;
uint16_t pageMask;
uint16_t pageCount;
uint32_t color; 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; Rc<DxvkDevice> m_device;
@ -425,24 +577,50 @@ namespace dxvk::hud {
high_resolution_clock::time_point m_lastUpdate = { }; high_resolution_clock::time_point m_lastUpdate = { };
bool m_displayCacheStats = false; bool m_displayCacheStats = false;
Rc<DxvkShader> m_vs; Rc<DxvkBuffer> m_dataBuffer;
Rc<DxvkShader> m_fsBackground; std::vector<DrawInfo> m_drawInfos;
Rc<DxvkShader> m_fsVisualize;
Rc<DxvkBuffer> m_pageMaskBuffer; HudShaderModule m_vsBackground;
Rc<DxvkBufferView> m_pageMaskView; HudShaderModule m_fsBackground;
void uploadChunkData( HudShaderModule m_vsVisualize;
HudRenderer& renderer); 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( void drawChunk(
HudRenderer& renderer, HudPos pos,
HudPos pos, HudPos size,
HudPos size, uint32_t color,
const VkMemoryType& memoryType, const DxvkMemoryChunkStats& chunk);
const DxvkMemoryChunkStats& stats) const;
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); void update(dxvk::high_resolution_clock::time_point time);
HudPos render( HudPos render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position); const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
private: private:
@ -500,8 +681,11 @@ namespace dxvk::hud {
void update(dxvk::high_resolution_clock::time_point time); void update(dxvk::high_resolution_clock::time_point time);
HudPos render( HudPos render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position); const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
private: private:
@ -532,8 +716,11 @@ namespace dxvk::hud {
void update(dxvk::high_resolution_clock::time_point time); void update(dxvk::high_resolution_clock::time_point time);
HudPos render( HudPos render(
HudRenderer& renderer, const DxvkContextObjects& ctx,
HudPos position); const HudPipelineKey& key,
const HudOptions& options,
HudRenderer& renderer,
HudPos position);
private: private:

View File

@ -1,364 +1,634 @@
#include "dxvk_hud_renderer.h" #include "dxvk_hud_renderer.h"
#include <hud_graph_frag.h>
#include <hud_graph_vert.h>
#include <hud_text_frag.h> #include <hud_text_frag.h>
#include <hud_text_vert.h> #include <hud_text_vert.h>
namespace dxvk::hud { namespace dxvk::hud {
HudRenderer::HudRenderer(const Rc<DxvkDevice>& device) struct HudGlyphGpuData {
: m_mode (Mode::RenderNone), int16_t x;
m_scale (1.0f), int16_t y;
m_surfaceSize { 0, 0 }, int16_t w;
m_device (device), int16_t h;
m_textShaders (createTextShaders()), int16_t originX;
m_graphShaders (createGraphShaders()), int16_t originY;
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 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() { 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( void HudRenderer::beginFrame(
const Rc<DxvkContext>& context, const DxvkContextObjects& ctx,
VkExtent2D surfaceSize, const Rc<DxvkImageView>& dstView,
float scale, VkColorSpaceKHR dstColorSpace,
float opacity) { const HudOptions& options) {
if (!m_initialized) if (!m_fontTextureView) {
this->initFontTexture(context); createFontResources();
uploadFontResources(ctx);
}
m_mode = Mode::RenderNone; VkExtent3D extent = dstView->mipLevelExtent(0u);
m_scale = scale;
m_opacity = opacity; m_pushConstants.surfaceSize = { extent.width, extent.height };
m_surfaceSize = surfaceSize; m_pushConstants.opacity = options.opacity;
m_context = context; 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( void HudRenderer::drawText(
float size, uint32_t size,
HudPos pos, HudPos pos,
HudColor color, uint32_t color,
const std::string& text) { const std::string& text) {
if (text.empty()) if (text.empty())
return; 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 m_textData.resize(draw.textOffset + draw.textLength);
// line to avoid potential CPU performance issues with the upload. std::memcpy(&m_textData[draw.textOffset], text.data(), draw.textLength);
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);
} }
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; void HudRenderer::flushDraws(
pushData.offset = offset / sizeof(*pointData); const DxvkContextObjects& ctx,
pushData.count = pointCount; const Rc<DxvkImageView>& dstView,
pushData.pos = pos; VkColorSpaceKHR dstColorSpace,
pushData.size = size; const HudOptions& options) {
pushData.scale.x = m_scale / std::max(float(m_surfaceSize.width), 1.0f); if (m_textDraws.empty())
pushData.scale.y = m_scale / std::max(float(m_surfaceSize.height), 1.0f); return;
pushData.opacity = m_opacity;
m_context->pushConstants(0, sizeof(pushData), &pushData); // Align text size so that we're guaranteed to be able to put draw
m_context->draw(4, 1, 0, 0); // 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);
void HudRenderer::beginTextRendering() {
if (m_mode != Mode::RenderText) {
m_mode = Mode::RenderText;
m_context->bindShader<VK_SHADER_STAGE_VERTEX_BIT>(Rc<DxvkShader>(m_textShaders.vert)); // We'll use indirect draws and then just use aligned subsections
m_context->bindShader<VK_SHADER_STAGE_FRAGMENT_BIT>(Rc<DxvkShader>(m_textShaders.frag)); // of the data buffer to write our draw parameters
size_t drawInfoSize = align(m_textDraws.size() * sizeof(HudTextDrawInfo), 256u);
m_context->bindResourceBufferView(VK_SHADER_STAGE_VERTEX_BIT, 0, Rc<DxvkBufferView>(m_fontBufferView)); size_t drawArgsSize = align(m_textDraws.size() * sizeof(VkDrawIndirectCommand), 256u);
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)); // Align buffer size to something large so we don't recreate it all the time
m_context->bindResourceImageView(VK_SHADER_STAGE_FRAGMENT_BIT, 2, Rc<DxvkImageView>(m_fontView)); size_t bufferSize = align(textSizeAligned + drawInfoSize + drawArgsSize, 2048u);
static const DxvkInputAssemblyState iaState = { if (!m_textBuffer || m_textBuffer->info().size < bufferSize) {
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, DxvkBufferCreateInfo textBufferInfo = { };
VK_FALSE, 0 }; textBufferInfo.size = bufferSize;
textBufferInfo.usage = VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT
m_context->setInputAssemblyState(iaState); | 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()));
} }
}
// Upload aligned text data in such a way that we write full cache lines
void HudRenderer::beginGraphRendering() { std::memcpy(m_textBuffer->mapPtr(0), m_textData.data(), textSizeAligned);
if (m_mode != Mode::RenderGraph) {
m_mode = Mode::RenderGraph;
m_context->bindShader<VK_SHADER_STAGE_VERTEX_BIT>(Rc<DxvkShader>(m_graphShaders.vert)); // Upload draw parameters and pad aligned region with zeroes
m_context->bindShader<VK_SHADER_STAGE_FRAGMENT_BIT>(Rc<DxvkShader>(m_graphShaders.frag)); size_t drawInfoCopySize = m_textDraws.size() * sizeof(HudTextDrawInfo);
std::memcpy(m_textBuffer->mapPtr(textSizeAligned), m_textDraws.data(), drawInfoCopySize);
m_context->bindResourceBufferView(VK_SHADER_STAGE_FRAGMENT_BIT, 0, Rc<DxvkBufferView>(m_dataView)); std::memset(m_textBuffer->mapPtr(textSizeAligned + drawInfoCopySize), 0, drawInfoSize - drawInfoCopySize);
static const DxvkInputAssemblyState iaState = { // Emit indirect draw parameters
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, size_t drawArgWriteSize = m_textDraws.size() * sizeof(VkDrawIndirectCommand);
VK_FALSE, 0 }; 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) { void HudRenderer::drawTextIndirect(
if (m_dataOffset + size > m_dataBuffer->info().size) { const DxvkContextObjects& ctx,
m_context->invalidateBuffer(m_dataBuffer, m_dataBuffer->allocateSlice()); const HudPipelineKey& key,
m_dataOffset = 0; const VkDescriptorBufferInfo& drawArgs,
} const VkDescriptorBufferInfo& drawInfos,
VkBufferView text,
VkDeviceSize offset = m_dataOffset; uint32_t drawCount) {
m_dataOffset = align(offset + size, 64); // Bind the correct pipeline for the swap chain
return offset; VkPipeline pipeline = getPipeline(key);
}
HudRenderer::ShaderPair HudRenderer::createTextShaders() { ctx.cmd->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,
ShaderPair result; VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
SpirvCodeBuffer vsCode(hud_text_vert); // Bind resources
SpirvCodeBuffer fsCode(hud_text_frag); VkDescriptorSet set = ctx.descriptorPool->alloc(m_textSetLayout);
const std::array<DxvkBindingInfo, 2> vsBindings = {{ VkDescriptorBufferInfo fontBufferDescriptor = m_fontBuffer->getDescriptor(0, m_fontBuffer->info().size).buffer;
{ 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 }, 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 = {{ ctx.cmd->updateDescriptorSets(
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2, VK_IMAGE_VIEW_TYPE_MAX_ENUM, VK_SHADER_STAGE_FRAGMENT_BIT, VK_ACCESS_SHADER_READ_BIT }, descriptorWrites.size(),
}}; descriptorWrites.data());
DxvkShaderCreateInfo vsInfo; ctx.cmd->cmdBindDescriptorSet(DxvkCmdBuffer::ExecBuffer,
vsInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; VK_PIPELINE_BIND_POINT_GRAPHICS, m_textPipelineLayout,
vsInfo.bindingCount = vsBindings.size(); set, 0, nullptr);
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));
DxvkShaderCreateInfo fsInfo; ctx.cmd->cmdPushConstants(DxvkCmdBuffer::ExecBuffer, m_textPipelineLayout,
fsInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
fsInfo.bindingCount = fsBindings.size(); 0, sizeof(m_pushConstants), &m_pushConstants);
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));
// 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; 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; HudPushConstants HudRenderer::getPushConstants() const {
vsInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; return m_pushConstants;
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);
} }
Rc<DxvkBufferView> HudRenderer::createDataView() { VkSpecializationInfo HudRenderer::getSpecInfo(
DxvkBufferViewKey info; const HudSpecConstants* constants) const {
info.format = VK_FORMAT_R8_UINT; VkSpecializationInfo specInfo = { };
info.offset = 0; specInfo.mapEntryCount = HudSpecConstantMap.size();
info.size = m_dataBuffer->info().size; specInfo.pMapEntries = HudSpecConstantMap.data();
info.usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT; specInfo.dataSize = sizeof(*constants);
specInfo.pData = constants;
return m_dataBuffer->createView(info); return specInfo;
} }
Rc<DxvkBuffer> HudRenderer::createFontBuffer() { void HudRenderer::createShaderModule(
DxvkBufferCreateInfo info; HudShaderModule& shader,
info.size = sizeof(HudFontGpuData); VkShaderStageFlagBits stage,
info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT size_t size,
| VK_BUFFER_USAGE_TRANSFER_DST_BIT; const uint32_t* code) const {
info.stages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT shader.moduleInfo.codeSize = size;
| VK_PIPELINE_STAGE_TRANSFER_BIT; shader.moduleInfo.pCode = code;
info.access = VK_ACCESS_SHADER_READ_BIT
| VK_ACCESS_TRANSFER_WRITE_BIT; shader.stageInfo.stage = stage;
shader.stageInfo.pName = "main";
return m_device->createBuffer(info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
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() { void HudRenderer::createFontResources() {
DxvkBufferViewKey info; DxvkBufferCreateInfo fontBufferInfo;
info.format = VK_FORMAT_UNDEFINED; fontBufferInfo.size = sizeof(HudFontGpuData);
info.offset = 0; fontBufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT
info.size = m_fontBuffer->info().size; | VK_BUFFER_USAGE_TRANSFER_SRC_BIT
info.usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_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() { m_fontTexture = m_device->createImage(fontTextureInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
DxvkImageCreateInfo info;
info.type = VK_IMAGE_TYPE_2D; DxvkImageViewKey fontTextureViewInfo;
info.format = VK_FORMAT_R8_UNORM; fontTextureViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
info.flags = 0; fontTextureViewInfo.format = VK_FORMAT_R8_UNORM;
info.sampleCount = VK_SAMPLE_COUNT_1_BIT; fontTextureViewInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
info.extent = { g_hudFont.width, g_hudFont.height, 1 }; fontTextureViewInfo.aspects = VK_IMAGE_ASPECT_COLOR_BIT;
info.numLayers = 1; fontTextureViewInfo.mipIndex = 0u;
info.mipLevels = 1; fontTextureViewInfo.mipCount = 1u;
info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT fontTextureViewInfo.layerIndex = 0u;
| VK_IMAGE_USAGE_SAMPLED_BIT; fontTextureViewInfo.layerCount = 1u;
info.stages = VK_PIPELINE_STAGE_TRANSFER_BIT
| VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; m_fontTextureView = m_fontTexture->createView(fontTextureViewInfo);
info.access = VK_ACCESS_TRANSFER_WRITE_BIT
| VK_ACCESS_SHADER_READ_BIT; DxvkSamplerKey samplerInfo;
info.tiling = VK_IMAGE_TILING_OPTIMAL; samplerInfo.setFilter(VK_FILTER_LINEAR,
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,
VK_FILTER_LINEAR, VK_SAMPLER_MIPMAP_MODE_NEAREST); 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, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE); VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE);
info.setUsePixelCoordinates(true); samplerInfo.setUsePixelCoordinates(true);
return m_device->createSampler(info); 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; void HudRenderer::uploadFontResources(
dst->y = src->y; const DxvkContextObjects& ctx) {
dst->w = src->w; size_t bufferDataSize = sizeof(HudFontGpuData);
dst->h = src->h; size_t textureDataSize = g_hudFont.width * g_hudFont.height;
dst->originX = src->originX;
dst->originY = src->originY; 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, auto uploadSlice = uploadBuffer->getSliceHandle();
VkImageSubresourceLayers { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 }, auto fontSlice = m_fontBuffer->getSliceHandle();
g_hudFont.texture, g_hudFont.width, g_hudFont.width * g_hudFont.height);
VkImageMemoryBarrier2 imageBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 };
m_initialized = true; 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" #include "dxvk_hud_font.h"
namespace dxvk::hud { namespace dxvk::hud {
/**
* \brief HUD options
*/
struct HudOptions {
float scale = 1.0f;
float opacity = 1.0f;
};
/** /**
* \brief HUD coordinates * \brief HUD coordinates
* *
@ -13,82 +22,69 @@ namespace dxvk::hud {
* corner of the swap image, in pixels. * corner of the swap image, in pixels.
*/ */
struct HudPos { struct HudPos {
float x; int32_t x = 0;
float y; int32_t y = 0;
};
/**
* \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;
}; };
/**
* \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 { struct HudTextDrawInfo {
HudColor color; uint32_t textOffset = 0u;
HudPos pos; uint16_t textLength = 0u;
uint32_t offset; uint16_t fontSize = 0u;
float size; int16_t posX = 0;
HudPos scale; int16_t posY = 0;
uint32_t color = 0u;
}; };
struct HudGraphPushConstants {
uint32_t offset; struct HudPushConstants {
uint32_t count; VkExtent2D surfaceSize;
HudPos pos; float opacity;
HudPos size; float scale;
HudPos scale;
float opacity;
}; };
/** /**
* \brief Glyph data * \brief Pipeline key
*/ */
struct HudGlyphGpuData { struct HudPipelineKey {
int16_t x; VkFormat format = VK_FORMAT_UNDEFINED;
int16_t y; VkColorSpaceKHR colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
int16_t w;
int16_t h; size_t hash() const {
int16_t originX; DxvkHashState hash;
int16_t originY; 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; * \brief Specialization constants
uint32_t padding[2]; */
HudGlyphGpuData glyphs[256]; 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 * \brief Text renderer for the HUD
* *
@ -103,99 +99,92 @@ namespace dxvk::hud {
const Rc<DxvkDevice>& device); const Rc<DxvkDevice>& device);
~HudRenderer(); ~HudRenderer();
void beginFrame( void beginFrame(
const Rc<DxvkContext>& context, const DxvkContextObjects& ctx,
VkExtent2D surfaceSize, const Rc<DxvkImageView>& dstView,
float scale, VkColorSpaceKHR dstColorSpace,
float opacity); const HudOptions& options);
void drawText( void drawText(
float size, uint32_t size,
HudPos pos, HudPos pos,
HudColor color, uint32_t color,
const std::string& text); const std::string& text);
void drawGraph(
HudPos pos,
HudPos size,
size_t pointCount,
const HudGraphPoint* pointData);
DxvkContext* getContext() { void drawTextIndirect(
m_mode = Mode::RenderNone; const DxvkContextObjects& ctx,
return m_context.ptr(); const HudPipelineKey& key,
} const VkDescriptorBufferInfo& drawArgs,
const VkDescriptorBufferInfo& drawInfos,
VkBufferView text,
uint32_t drawCount);
VkExtent2D surfaceSize() const { void flushDraws(
return m_surfaceSize; const DxvkContextObjects& ctx,
} const Rc<DxvkImageView>& dstView,
VkColorSpaceKHR dstColorSpace,
const HudOptions& options);
float scale() const { HudPipelineKey getPipelineKey(
return m_scale; 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: private:
enum class Mode {
RenderNone,
RenderText,
RenderGraph,
};
struct ShaderPair { Rc<DxvkDevice> m_device;
Rc<DxvkShader> vert;
Rc<DxvkShader> frag;
};
Mode m_mode;
float m_scale;
float m_opacity;
VkExtent2D m_surfaceSize;
Rc<DxvkDevice> m_device; Rc<DxvkBuffer> m_fontBuffer;
Rc<DxvkContext> m_context; Rc<DxvkImage> m_fontTexture;
Rc<DxvkImageView> m_fontTextureView;
ShaderPair m_textShaders; Rc<DxvkSampler> m_fontSampler;
ShaderPair m_graphShaders;
Rc<DxvkBuffer> m_dataBuffer;
Rc<DxvkBufferView> m_dataView;
VkDeviceSize m_dataOffset;
Rc<DxvkBuffer> m_fontBuffer; Rc<DxvkBuffer> m_textBuffer;
Rc<DxvkBufferView> m_fontBufferView; Rc<DxvkBufferView> m_textBufferView;
Rc<DxvkImage> m_fontImage;
Rc<DxvkImageView> m_fontView;
Rc<DxvkSampler> m_fontSampler;
bool m_initialized = false; std::vector<HudTextDrawInfo> m_textDraws;
std::vector<char> m_textData;
void beginTextRendering(); HudShaderModule m_textVs;
HudShaderModule m_textFs;
void beginGraphRendering();
VkDeviceSize allocDataBuffer(VkDeviceSize size); VkDescriptorSetLayout m_textSetLayout = VK_NULL_HANDLE;
VkPipelineLayout m_textPipelineLayout = VK_NULL_HANDLE;
ShaderPair createTextShaders(); HudPushConstants m_pushConstants = { };
ShaderPair createGraphShaders();
Rc<DxvkBuffer> createDataBuffer(); std::unordered_map<HudPipelineKey,
Rc<DxvkBufferView> createDataView(); 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" #include "hud_frag_common.glsl"
layout(location = 0) out vec4 o_color;
layout(push_constant) layout(push_constant)
uniform push_data_t { uniform push_data_t {
vec2 pos; uvec2 surface_size;
vec2 size;
vec2 scale;
float opacity; float opacity;
uint color; float scale;
uint maskIndex;
uint pageCount;
}; };
layout(location = 0) out vec4 o_color;
void main() { void main() {
vec4 rgba = unpackUnorm4x8(color); o_color = vec4(0.0f, 0.0f, 0.0f, 0.75f);
o_color = vec4(encodeOutput(rgba.rgb), rgba.a * opacity); o_color.a *= opacity;
o_color = linear_to_output(o_color);
} }

View File

@ -5,32 +5,31 @@
#include "hud_frag_common.glsl" #include "hud_frag_common.glsl"
layout(location = 0) in vec2 v_coord; 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(location = 0) out vec4 o_color;
layout(binding = 0, std430) layout(binding = 1, std430)
readonly buffer mask_data_t { readonly buffer mask_data_t {
uint masks[]; uint masks[];
}; };
layout(push_constant) layout(push_constant)
uniform push_data_t { uniform push_data_t {
vec2 pos; uvec2 surface_size;
vec2 size;
vec2 scale;
float opacity; float opacity;
uint color; float scale;
uint maskIndex;
uint pageCount;
}; };
void main() { void main() {
vec4 rgba = unpackUnorm4x8(color); float dx = dFdx(v_coord.x * float(v_page_count)) / 2.0f - 0.5f;
float dx = dFdx(v_coord.x * float(pageCount)) / 2.0f - 0.5f;
uvec2 pageRange = uvec2(clamp( uvec2 pageRange = uvec2(clamp(
(v_coord.xx * float(pageCount)) + vec2(-dx, dx), (v_coord.xx * float(v_page_count)) + vec2(-dx, dx),
vec2(0.0), vec2(float(pageCount - 1u)))); vec2(0.0), vec2(float(v_page_count - 1u))));
uint bitsTotal = max(pageRange.y - pageRange.x, 1u); uint bitsTotal = max(pageRange.y - pageRange.x, 1u);
uint bitsSet = 0u; uint bitsSet = 0u;
@ -40,26 +39,28 @@ void main() {
if (shift + bitsTotal <= 32u) { if (shift + bitsTotal <= 32u) {
bitsSet = bitCount(bitfieldExtract( bitsSet = bitCount(bitfieldExtract(
masks[maskIndex + index], int(shift), int(bitsTotal))); masks[v_mask_index + index], int(shift), int(bitsTotal)));
} else { } else {
bitsSet = bitCount(masks[maskIndex + (index++)] >> shift); bitsSet = bitCount(masks[v_mask_index + (index++)] >> shift);
uint bitsCounted = 32u - shift; uint bitsCounted = 32u - shift;
while (bitsCounted + 32u <= bitsTotal) { while (bitsCounted + 32u <= bitsTotal) {
bitsSet += bitCount(masks[maskIndex + (index++)]); bitsSet += bitCount(masks[v_mask_index + (index++)]);
bitsCounted += 32u; bitsCounted += 32u;
} }
if (bitsCounted < bitsTotal) { if (bitsCounted < bitsTotal) {
bitsSet += bitCount(bitfieldExtract( bitsSet += bitCount(bitfieldExtract(
masks[maskIndex + (index++)], 0, int(bitsTotal - bitsCounted))); masks[v_mask_index + (index++)], 0, int(bitsTotal - bitsCounted)));
} }
} }
if (bitsSet == 0u) if (bitsSet == 0u)
discard; discard;
vec4 color = unpackUnorm4x8(v_color);
float blendFactor = 0.5f * float(bitsSet) / max(float(bitsTotal), 1.0f); 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 = vec4(mix(color.rgb, vec3(1.0f), blendFactor), color.a * opacity);
o_color.rgb = encodeOutput(o_color.rgb); 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) #include "../../shaders/dxvk_color_space.glsl"
#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)
#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( vec4 linear_to_output(vec4 color) {
0.4123908, 0.2126390, 0.0193308, switch (s_color_space) {
0.3575843, 0.7151687, 0.1191948, case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: {
0.1804808, 0.0721923, 0.9505322); if (!s_srgb)
color.rgb = linear_to_srgb(color.rgb);
const mat3 xyz_to_rec2020 = mat3( return color;
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);
} }
case VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT: 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) in vec2 v_coord;
layout(location = 0) out vec4 o_color; layout(location = 0) out vec4 o_color;
struct line_point_t { #define NUM_FRAME_TIME_STAMPS (420)
float value;
uint color;
};
layout(binding = 0, std430) layout(binding = 0, std430)
readonly buffer line_data_t { readonly buffer timestamp_buffer_t {
line_point_t points[]; 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) layout(push_constant)
uniform push_data_t { uniform push_data_t {
uint offset; uvec2 surface_size;
uint count;
vec2 pos;
vec2 size;
vec2 scale;
float opacity; float opacity;
float scale;
uint packed_xy;
uint packed_wh;
uint frame_index;
}; };
void main() { int max_index = NUM_FRAME_TIME_STAMPS - 1;
float cx = v_coord.x * float(count);
float fx = fract(cx);
// We need to roll our own linear interpolation here int compute_real_index(int local_index) {
uint p0_idx = min(uint(floor(cx)), count - 1) + offset; int real_index = int(frame_index) - local_index;
uint p1_idx = min(uint(ceil(cx)), count - 1) + offset;
line_point_t p0 = points[p0_idx]; if (real_index < 0)
line_point_t p1 = points[p1_idx]; real_index += NUM_FRAME_TIME_STAMPS;
float value = mix(p0.value, p1.value, fx); return real_index;
float alpha = value + v_coord.y - 1.0f; }
alpha = min(alpha / dFdy(v_coord.y), 1.0f);
float sample_at(float position) {
if (alpha <= 0.0f) int local_index = int(position);
discard;
int lo_index = compute_real_index(clamp(local_index, 0, max_index));
o_color = mix( int hi_index = compute_real_index(clamp(local_index + 1, 0, max_index));
unpackUnorm4x8(p0.color),
unpackUnorm4x8(p1.color), fx); float lo_value = frame_interval_ms[lo_index];
o_color *= alpha * opacity; float hi_value = frame_interval_ms[hi_index];
o_color.rgb = encodeOutput(o_color.rgb); 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) layout(push_constant)
uniform push_data_t { uniform push_data_t {
uint offset; uvec2 surface_size;
uint count;
vec2 pos;
vec2 size;
vec2 scale;
float opacity; 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() { void main() {
vec2 coord = vec2( vec2 coord = vec2(
float(gl_VertexIndex & 1), float(gl_VertexIndex & 1),
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 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); gl_Position = vec4(scaled_pos, 0.0f, 1.0f);
} }

View File

@ -4,7 +4,14 @@
#include "hud_frag_common.glsl" #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 = 0) in vec2 v_texcoord;
layout(location = 1) in vec4 v_color; layout(location = 1) in vec4 v_color;
@ -25,8 +32,8 @@ void main() {
vec3 r_shadow = vec3(0.0f, 0.0f, 0.0f); vec3 r_shadow = vec3(0.0f, 0.0f, 0.0f);
o_color.rgb = mix(r_shadow, r_center, r_alpha_center); 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 *= 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 { struct font_info_t {
float size; float size;
@ -18,15 +18,25 @@ readonly buffer font_buffer_t {
glyph_info_t glyph_data[]; 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) layout(push_constant)
uniform push_data_t { uniform push_data_t {
vec4 text_color; uvec2 surface_size;
vec2 text_pos; float opacity;
uint text_offset; float scale;
float text_size;
vec2 hud_scale;
}; };
layout(location = 0) out vec2 o_texcoord; layout(location = 0) out vec2 o_texcoord;
@ -42,7 +52,8 @@ vec2 unpack_u16(uint v) {
} }
void main() { 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 // Compute character index and vertex index for the current
// character. We'll render two triangles per character. // character. We'll render two triangles per character.
@ -50,7 +61,7 @@ void main() {
uint vtx_idx = gl_VertexIndex - 6 * chr_idx; uint vtx_idx = gl_VertexIndex - 6 * chr_idx;
// Load glyph info based on vertex index // 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]; glyph_info_t glyph_info = glyph_data[glyph_idx];
// Compute texture coordinate from glyph data // Compute texture coordinate from glyph data
@ -64,12 +75,18 @@ void main() {
// Compute vertex position. We can easily do this here since our // Compute vertex position. We can easily do this here since our
// font is a monospace font, otherwise we'd need to preprocess // font is a monospace font, otherwise we'd need to preprocess
// the strings to render in a compute shader. // 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 local_pos = tex_wh * coord - unpack_u16(glyph_info.packed_origin)
+ vec2(font_data.advance * float(chr_idx), 0.0f); + vec2(font_data.advance * float(chr_idx), 0.0f);
vec2 pixel_pos = text_pos + size_factor * local_pos; 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); 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_background.frag',
'hud/shaders/hud_chunk_frag_visualize.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_frag.frag',
'hud/shaders/hud_graph_vert.vert', 'hud/shaders/hud_graph_vert.vert',