mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-02-20 19:54:19 +01:00
[dxvk] Reimplement HUD rendering to use Vulkan directly
And change how rendering works in general so that we emit fewer draw calls.
This commit is contained in:
parent
b35c0bce4f
commit
63b200f08d
@ -402,8 +402,10 @@ namespace dxvk {
|
||||
m_imageViews.at(imageIndex), m_colorspace, VkRect2D(),
|
||||
m_swapImageView, m_colorspace, VkRect2D());
|
||||
|
||||
// if (m_hud != nullptr)
|
||||
// m_hud->render(m_context, info.format, info.imageExtent);
|
||||
if (m_hud) {
|
||||
m_hud->render(m_context->beginExternalRendering(),
|
||||
m_imageViews.at(imageIndex), m_colorspace);
|
||||
}
|
||||
|
||||
m_blitter->endPresent(m_context->beginExternalRendering(),
|
||||
m_imageViews.at(imageIndex));
|
||||
|
@ -16,28 +16,23 @@ namespace dxvk::hud {
|
||||
|
||||
|
||||
HudPos HudSamplerCount::render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
position.y += 16.0f;
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
position.y += 16;
|
||||
renderer.drawText(16, position, 0xffc0ff00u, "Samplers:");
|
||||
renderer.drawText(16, { position.x + 120, position.y }, 0xffffffffu, m_samplerCount);
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x, position.y },
|
||||
{ 0.0f, 1.0f, 0.75f, 1.0f },
|
||||
"Samplers:");
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x + 120.0f, position.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
m_samplerCount);
|
||||
|
||||
position.y += 8.0f;
|
||||
position.y += 8;
|
||||
return position;
|
||||
}
|
||||
|
||||
HudTextureMemory::HudTextureMemory(D3D9DeviceEx* device)
|
||||
: m_device (device)
|
||||
, m_allocatedString ("")
|
||||
, m_mappedString ("") {}
|
||||
: m_device (device)
|
||||
, m_allocatedString ("")
|
||||
, m_mappedString ("") { }
|
||||
|
||||
|
||||
void HudTextureMemory::update(dxvk::high_resolution_clock::time_point time) {
|
||||
@ -62,40 +57,26 @@ namespace dxvk::hud {
|
||||
|
||||
|
||||
HudPos HudTextureMemory::render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
position.y += 16.0f;
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
position.y += 16;
|
||||
renderer.drawText(16, position, 0xffc0ff00u, "Mappable:");
|
||||
renderer.drawText(16, { position.x + 120, position.y }, 0xffffffffu, m_allocatedString);
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x, position.y },
|
||||
{ 0.0f, 1.0f, 0.75f, 1.0f },
|
||||
"Mappable:");
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x + 120.0f, position.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
m_allocatedString);
|
||||
|
||||
position.y += 24.0f;
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x, position.y },
|
||||
{ 0.0f, 1.0f, 0.75f, 1.0f },
|
||||
"Mapped:");
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x + 120.0f, position.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
m_mappedString);
|
||||
|
||||
position.y += 8.0f;
|
||||
position.y += 20;
|
||||
renderer.drawText(16, position, 0xffc0ff00u, "Mapped:");
|
||||
renderer.drawText(16, { position.x + 120, position.y }, 0xffffffffu, m_mappedString);
|
||||
|
||||
position.y += 8;
|
||||
return position;
|
||||
}
|
||||
|
||||
HudFixedFunctionShaders::HudFixedFunctionShaders(D3D9DeviceEx* device)
|
||||
: m_device (device)
|
||||
, m_ffShaderCount ("") {}
|
||||
: m_device (device)
|
||||
, m_ffShaderCount ("") {}
|
||||
|
||||
|
||||
void HudFixedFunctionShaders::update(dxvk::high_resolution_clock::time_point time) {
|
||||
@ -108,29 +89,26 @@ namespace dxvk::hud {
|
||||
|
||||
|
||||
HudPos HudFixedFunctionShaders::render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
position.y += 16.0f;
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
position.y += 16;
|
||||
renderer.drawText(16, position, 0xffc0ff00u, "FF Shaders:");
|
||||
renderer.drawText(16, { position.x + 155, position.y }, 0xffffffffu, m_ffShaderCount);
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x, position.y },
|
||||
{ 0.0f, 1.0f, 0.75f, 1.0f },
|
||||
"FF Shaders:");
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x + 155.0f, position.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
m_ffShaderCount);
|
||||
|
||||
position.y += 8.0f;
|
||||
position.y += 8;
|
||||
return position;
|
||||
}
|
||||
|
||||
|
||||
HudSWVPState::HudSWVPState(D3D9DeviceEx* device)
|
||||
: m_device (device)
|
||||
, m_isSWVPText ("") {}
|
||||
|
||||
|
||||
|
||||
void HudSWVPState::update(dxvk::high_resolution_clock::time_point time) {
|
||||
if (m_device->IsSWVP()) {
|
||||
if (m_device->CanOnlySWVP()) {
|
||||
@ -149,21 +127,16 @@ namespace dxvk::hud {
|
||||
|
||||
|
||||
HudPos HudSWVPState::render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
position.y += 16.0f;
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
position.y += 16;
|
||||
renderer.drawText(16, position, 0xffc0ff00u, "Vertex Processing:");
|
||||
renderer.drawText(16, { position.x + 240, position.y }, 0xffffffffu, m_isSWVPText);
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x, position.y },
|
||||
{ 0.0f, 1.0f, 0.75f, 1.0f },
|
||||
"Vertex Processing:");
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x + 240.0f, position.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
m_isSWVPText);
|
||||
|
||||
position.y += 8.0f;
|
||||
position.y += 8;
|
||||
return position;
|
||||
}
|
||||
|
||||
|
@ -17,8 +17,11 @@ namespace dxvk::hud {
|
||||
void update(dxvk::high_resolution_clock::time_point time);
|
||||
|
||||
HudPos render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
|
||||
private:
|
||||
|
||||
@ -28,80 +31,92 @@ namespace dxvk::hud {
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief HUD item to display unmappable memory
|
||||
*/
|
||||
class HudTextureMemory : public HudItem {
|
||||
constexpr static int64_t UpdateInterval = 500'000;
|
||||
/**
|
||||
* \brief HUD item to display unmappable memory
|
||||
*/
|
||||
class HudTextureMemory : public HudItem {
|
||||
constexpr static int64_t UpdateInterval = 500'000;
|
||||
public:
|
||||
|
||||
public:
|
||||
HudTextureMemory(D3D9DeviceEx* device);
|
||||
|
||||
HudTextureMemory(D3D9DeviceEx* device);
|
||||
void update(dxvk::high_resolution_clock::time_point time);
|
||||
|
||||
void update(dxvk::high_resolution_clock::time_point time);
|
||||
HudPos render(
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
|
||||
HudPos render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
private:
|
||||
|
||||
private:
|
||||
D3D9DeviceEx* m_device;
|
||||
|
||||
D3D9DeviceEx* m_device;
|
||||
uint32_t m_maxAllocated = 0;
|
||||
uint32_t m_maxUsed = 0;
|
||||
uint32_t m_maxMapped = 0;
|
||||
|
||||
uint32_t m_maxAllocated = 0;
|
||||
uint32_t m_maxUsed = 0;
|
||||
uint32_t m_maxMapped = 0;
|
||||
dxvk::high_resolution_clock::time_point m_lastUpdate
|
||||
= dxvk::high_resolution_clock::now();
|
||||
|
||||
dxvk::high_resolution_clock::time_point m_lastUpdate
|
||||
= dxvk::high_resolution_clock::now();
|
||||
std::string m_allocatedString;
|
||||
std::string m_mappedString;
|
||||
|
||||
std::string m_allocatedString;
|
||||
std::string m_mappedString;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief HUD item to display amount of generated fixed function shaders
|
||||
*/
|
||||
class HudFixedFunctionShaders : public HudItem {
|
||||
public:
|
||||
/**
|
||||
* \brief HUD item to display amount of generated fixed function shaders
|
||||
*/
|
||||
class HudFixedFunctionShaders : public HudItem {
|
||||
|
||||
HudFixedFunctionShaders(D3D9DeviceEx* device);
|
||||
public:
|
||||
|
||||
void update(dxvk::high_resolution_clock::time_point time);
|
||||
HudFixedFunctionShaders(D3D9DeviceEx* device);
|
||||
|
||||
HudPos render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
void update(dxvk::high_resolution_clock::time_point time);
|
||||
|
||||
private:
|
||||
HudPos render(
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
|
||||
D3D9DeviceEx* m_device;
|
||||
private:
|
||||
|
||||
std::string m_ffShaderCount;
|
||||
D3D9DeviceEx* m_device;
|
||||
|
||||
};
|
||||
std::string m_ffShaderCount;
|
||||
|
||||
/**
|
||||
* \brief HUD item to whether or not we're in SWVP mode
|
||||
*/
|
||||
class HudSWVPState : public HudItem {
|
||||
public:
|
||||
};
|
||||
|
||||
HudSWVPState(D3D9DeviceEx* device);
|
||||
|
||||
void update(dxvk::high_resolution_clock::time_point time);
|
||||
/**
|
||||
* \brief HUD item to whether or not we're in SWVP mode
|
||||
*/
|
||||
class HudSWVPState : public HudItem {
|
||||
|
||||
HudPos render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
public:
|
||||
|
||||
private:
|
||||
HudSWVPState(D3D9DeviceEx* device);
|
||||
|
||||
D3D9DeviceEx* m_device;
|
||||
void update(dxvk::high_resolution_clock::time_point time);
|
||||
|
||||
std::string m_isSWVPText;
|
||||
HudPos render(
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
|
||||
};
|
||||
private:
|
||||
|
||||
D3D9DeviceEx* m_device;
|
||||
|
||||
std::string m_isSWVPText;
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -851,8 +851,10 @@ namespace dxvk {
|
||||
m_wctx->imageViews.at(imageIndex), m_colorspace, dstRect,
|
||||
swapImageView, m_colorspace, srcRect);
|
||||
|
||||
// if (m_hud != nullptr)
|
||||
// m_hud->render(m_context, info.format, info.imageExtent);
|
||||
if (m_hud) {
|
||||
m_hud->render(m_context->beginExternalRendering(),
|
||||
m_wctx->imageViews.at(imageIndex), m_colorspace);
|
||||
}
|
||||
|
||||
m_blitter->endPresent(m_context->beginExternalRendering(),
|
||||
m_wctx->imageViews.at(imageIndex));
|
||||
|
@ -8,112 +8,51 @@ namespace dxvk::hud {
|
||||
const Rc<DxvkDevice>& device)
|
||||
: m_device (device),
|
||||
m_renderer (device),
|
||||
m_hudItems (device),
|
||||
m_scale (m_hudItems.getOption<float>("scale", 1.0f)),
|
||||
m_opacity (m_hudItems.getOption<float>("opacity", 1.0f)) {
|
||||
// Sanitize scaling factor
|
||||
if (m_scale < 0.01f)
|
||||
m_scale = 1.0f;
|
||||
|
||||
// Sanitize HUD opacity factor
|
||||
if (m_opacity != 1.0f)
|
||||
m_opacity = std::max(std::min(m_opacity, 1.0f), 0.1f);
|
||||
|
||||
// Set up constant state
|
||||
m_rsState.polygonMode = VK_POLYGON_MODE_FILL;
|
||||
m_rsState.cullMode = VK_CULL_MODE_BACK_BIT;
|
||||
m_rsState.frontFace = VK_FRONT_FACE_CLOCKWISE;
|
||||
m_rsState.depthClipEnable = VK_FALSE;
|
||||
m_rsState.depthBiasEnable = VK_FALSE;
|
||||
m_rsState.conservativeMode = VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT;
|
||||
m_rsState.sampleCount = VK_SAMPLE_COUNT_1_BIT;
|
||||
m_rsState.flatShading = VK_FALSE;
|
||||
m_rsState.lineMode = VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT;
|
||||
|
||||
m_blendMode.enableBlending = VK_TRUE;
|
||||
m_blendMode.colorSrcFactor = VK_BLEND_FACTOR_ONE;
|
||||
m_blendMode.colorDstFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
m_blendMode.colorBlendOp = VK_BLEND_OP_ADD;
|
||||
m_blendMode.alphaSrcFactor = VK_BLEND_FACTOR_ONE;
|
||||
m_blendMode.alphaDstFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
m_blendMode.alphaBlendOp = VK_BLEND_OP_ADD;
|
||||
m_blendMode.writeMask = VK_COLOR_COMPONENT_R_BIT
|
||||
| VK_COLOR_COMPONENT_G_BIT
|
||||
| VK_COLOR_COMPONENT_B_BIT
|
||||
| VK_COLOR_COMPONENT_A_BIT;
|
||||
m_hudItems (device) {
|
||||
// Retrieve and sanitize options
|
||||
m_options.scale = std::clamp(m_hudItems.getOption<float>("scale", 1.0f), 0.25f, 4.0f);
|
||||
m_options.opacity = std::clamp(m_hudItems.getOption<float>("opacity", 1.0f), 0.1f, 1.0f);
|
||||
|
||||
addItem<HudVersionItem>("version", -1);
|
||||
addItem<HudDeviceInfoItem>("devinfo", -1, m_device);
|
||||
addItem<HudFpsItem>("fps", -1);
|
||||
addItem<HudFrameTimeItem>("frametimes", -1);
|
||||
addItem<HudFrameTimeItem>("frametimes", -1, device, &m_renderer);
|
||||
addItem<HudSubmissionStatsItem>("submissions", -1, device);
|
||||
addItem<HudDrawCallStatsItem>("drawcalls", -1, device);
|
||||
addItem<HudPipelineStatsItem>("pipelines", -1, device);
|
||||
addItem<HudDescriptorStatsItem>("descriptors", -1, device);
|
||||
addItem<HudMemoryStatsItem>("memory", -1, device);
|
||||
addItem<HudMemoryDetailsItem>("allocations", -1, device);
|
||||
addItem<HudMemoryDetailsItem>("allocations", -1, device, &m_renderer);
|
||||
addItem<HudCsThreadItem>("cs", -1, device);
|
||||
addItem<HudGpuLoadItem>("gpuload", -1, device);
|
||||
addItem<HudCompilerActivityItem>("compiler", -1, device);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Hud::~Hud() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Hud::update() {
|
||||
m_hudItems.update();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Hud::render(
|
||||
const Rc<DxvkContext>& ctx,
|
||||
VkSurfaceFormatKHR surfaceFormat,
|
||||
VkExtent2D surfaceSize) {
|
||||
this->setupRendererState(ctx, surfaceFormat, surfaceSize);
|
||||
this->renderHudElements(ctx);
|
||||
const DxvkContextObjects& ctx,
|
||||
const Rc<DxvkImageView>& dstView,
|
||||
VkColorSpaceKHR dstColorSpace) {
|
||||
auto key = m_renderer.getPipelineKey(dstView, dstColorSpace);
|
||||
|
||||
m_renderer.beginFrame(ctx, dstView, dstColorSpace, m_options);
|
||||
m_hudItems.render(ctx, key, m_options, m_renderer);
|
||||
m_renderer.flushDraws(ctx, dstView, dstColorSpace, m_options);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Rc<Hud> Hud::createHud(const Rc<DxvkDevice>& device) {
|
||||
return new Hud(device);
|
||||
}
|
||||
|
||||
|
||||
void Hud::setupRendererState(
|
||||
const Rc<DxvkContext>& ctx,
|
||||
VkSurfaceFormatKHR surfaceFormat,
|
||||
VkExtent2D surfaceSize) {
|
||||
VkColorSpaceKHR colorSpace = surfaceFormat.colorSpace;
|
||||
|
||||
if (lookupFormatInfo(surfaceFormat.format)->flags.test(DxvkFormatFlag::ColorSpaceSrgb))
|
||||
colorSpace = VK_COLOR_SPACE_PASS_THROUGH_EXT;
|
||||
|
||||
VkViewport viewport;
|
||||
viewport.x = 0.0f;
|
||||
viewport.y = 0.0f;
|
||||
viewport.width = float(surfaceSize.width);
|
||||
viewport.height = float(surfaceSize.height);
|
||||
viewport.minDepth = 0.0f;
|
||||
viewport.maxDepth = 1.0f;
|
||||
|
||||
VkRect2D scissor;
|
||||
scissor.offset = { 0, 0 };
|
||||
scissor.extent = surfaceSize;
|
||||
|
||||
ctx->setViewports(1, &viewport, &scissor);
|
||||
ctx->setRasterizerState(m_rsState);
|
||||
ctx->setBlendMode(0, m_blendMode);
|
||||
|
||||
ctx->setSpecConstant(VK_PIPELINE_BIND_POINT_GRAPHICS, 0, colorSpace);
|
||||
m_renderer.beginFrame(ctx, surfaceSize, m_scale, m_opacity);
|
||||
}
|
||||
|
||||
|
||||
void Hud::renderHudElements(const Rc<DxvkContext>& ctx) {
|
||||
m_hudItems.render(m_renderer);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,15 +6,7 @@
|
||||
#include "dxvk_hud_renderer.h"
|
||||
|
||||
namespace dxvk::hud {
|
||||
|
||||
/**
|
||||
* \brief HUD uniform data
|
||||
* Shader data for the HUD.
|
||||
*/
|
||||
struct HudUniformData {
|
||||
VkExtent2D surfaceSize;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief DXVK HUD
|
||||
*
|
||||
@ -41,13 +33,14 @@ namespace dxvk::hud {
|
||||
* \brief Render HUD
|
||||
*
|
||||
* Renders the HUD to the given context.
|
||||
* \param [in] ctx Device context
|
||||
* \param [in] surfaceSize Image size, in pixels
|
||||
* \param [in] ctx Context objects for rendering
|
||||
* \param [in] dstView Swap chain image view
|
||||
* \param [in] dstColorSpace Color space
|
||||
*/
|
||||
void render(
|
||||
const Rc<DxvkContext>& ctx,
|
||||
VkSurfaceFormatKHR surfaceFormat,
|
||||
VkExtent2D surfaceSize);
|
||||
const DxvkContextObjects& ctx,
|
||||
const Rc<DxvkImageView>& dstView,
|
||||
VkColorSpaceKHR dstColorSpace);
|
||||
|
||||
/**
|
||||
* \brief Adds a HUD item if enabled
|
||||
@ -74,25 +67,12 @@ namespace dxvk::hud {
|
||||
|
||||
private:
|
||||
|
||||
const Rc<DxvkDevice> m_device;
|
||||
Rc<DxvkDevice> m_device;
|
||||
|
||||
DxvkRasterizerState m_rsState;
|
||||
DxvkBlendMode m_blendMode;
|
||||
|
||||
HudUniformData m_uniformData;
|
||||
HudRenderer m_renderer;
|
||||
HudItemSet m_hudItems;
|
||||
|
||||
float m_scale;
|
||||
float m_opacity;
|
||||
|
||||
void setupRendererState(
|
||||
const Rc<DxvkContext>& ctx,
|
||||
VkSurfaceFormatKHR surfaceFormat,
|
||||
VkExtent2D surfaceSize);
|
||||
|
||||
void renderHudElements(
|
||||
const Rc<DxvkContext>& ctx);
|
||||
HudOptions m_options;
|
||||
|
||||
};
|
||||
|
||||
|
@ -2,7 +2,13 @@
|
||||
|
||||
#include <hud_chunk_frag_background.h>
|
||||
#include <hud_chunk_frag_visualize.h>
|
||||
#include <hud_chunk_vert.h>
|
||||
#include <hud_chunk_vert_background.h>
|
||||
#include <hud_chunk_vert_visualize.h>
|
||||
|
||||
#include <hud_frame_time_eval.h>
|
||||
|
||||
#include <hud_graph_frag.h>
|
||||
#include <hud_graph_vert.h>
|
||||
|
||||
#include <iomanip>
|
||||
#include <version.h>
|
||||
@ -70,11 +76,15 @@ namespace dxvk::hud {
|
||||
}
|
||||
|
||||
|
||||
void HudItemSet::render(HudRenderer& renderer) {
|
||||
HudPos position = { 8.0f, 8.0f };
|
||||
void HudItemSet::render(
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer) {
|
||||
HudPos position = { 8, 8 };
|
||||
|
||||
for (const auto& item : m_items)
|
||||
position = item->render(renderer, position);
|
||||
position = item->render(ctx, key, options, renderer, position);
|
||||
}
|
||||
|
||||
|
||||
@ -88,16 +98,15 @@ namespace dxvk::hud {
|
||||
|
||||
|
||||
HudPos HudVersionItem::render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
position.y += 16.0f;
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
position.y += 16;
|
||||
renderer.drawText(16, position, 0xffffffffu, "DXVK " DXVK_VERSION);
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x, position.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
"DXVK " DXVK_VERSION);
|
||||
|
||||
position.y += 8.0f;
|
||||
position.y += 8;
|
||||
return position;
|
||||
}
|
||||
|
||||
@ -114,16 +123,15 @@ namespace dxvk::hud {
|
||||
|
||||
|
||||
HudPos HudClientApiItem::render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
position.y += 16.0f;
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
position.y += 16;
|
||||
renderer.drawText(16, position, 0xffffffffu, m_api);
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x, position.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
m_api);
|
||||
|
||||
position.y += 8.0f;
|
||||
position.y += 8;
|
||||
return position;
|
||||
}
|
||||
|
||||
@ -148,27 +156,21 @@ namespace dxvk::hud {
|
||||
|
||||
|
||||
HudPos HudDeviceInfoItem::render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
position.y += 16.0f;
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x, position.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
m_deviceName);
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
position.y += 16;
|
||||
renderer.drawText(16, position, 0xffffffffu, m_deviceName);
|
||||
|
||||
position.y += 24.0f;
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x, position.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
m_driverName);
|
||||
position.y += 24;
|
||||
renderer.drawText(16, position, 0xffffffffu, m_driverName);
|
||||
|
||||
position.y += 20.0f;
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x, position.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
m_driverVer);
|
||||
position.y += 20;
|
||||
renderer.drawText(16, position, 0xffffffffu, m_driverVer);
|
||||
|
||||
position.y += 8.0f;
|
||||
position.y += 8;
|
||||
return position;
|
||||
}
|
||||
|
||||
@ -193,108 +195,536 @@ namespace dxvk::hud {
|
||||
|
||||
|
||||
HudPos HudFpsItem::render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
position.y += 16.0f;
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
position.y += 16;
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x, position.y },
|
||||
{ 1.0f, 0.25f, 0.25f, 1.0f },
|
||||
"FPS:");
|
||||
renderer.drawText(16, position, 0xff4040ffu, "FPS:");
|
||||
renderer.drawText(16, { position.x + 60, position.y },
|
||||
0xffffffffu, m_frameRate);
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x + 60.0f, position.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
m_frameRate);
|
||||
|
||||
position.y += 8.0f;
|
||||
position.y += 8;
|
||||
return position;
|
||||
}
|
||||
|
||||
|
||||
HudFrameTimeItem::HudFrameTimeItem() { }
|
||||
HudFrameTimeItem::~HudFrameTimeItem() { }
|
||||
HudFrameTimeQueryPool::HudFrameTimeQueryPool(
|
||||
const Rc<DxvkDevice>& device)
|
||||
: m_vkd(device->vkd()) {
|
||||
VkQueryPoolCreateInfo info = { VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO };
|
||||
info.queryType = VK_QUERY_TYPE_TIMESTAMP;
|
||||
info.queryCount = 1;
|
||||
|
||||
VkResult vr = m_vkd->vkCreateQueryPool(m_vkd->device(), &info, nullptr, &m_pool);
|
||||
|
||||
if (vr != VK_SUCCESS)
|
||||
throw DxvkError(str::format("Failed to create frame time query pool: ", vr));
|
||||
}
|
||||
|
||||
|
||||
void HudFrameTimeItem::update(dxvk::high_resolution_clock::time_point time) {
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(time - m_lastUpdate);
|
||||
HudFrameTimeQueryPool::~HudFrameTimeQueryPool() {
|
||||
m_vkd->vkDestroyQueryPool(m_vkd->device(), m_pool, nullptr);
|
||||
}
|
||||
|
||||
m_dataPoints[m_dataPointId] = float(elapsed.count());
|
||||
m_dataPointId = (m_dataPointId + 1) % NumDataPoints;
|
||||
|
||||
m_lastUpdate = time;
|
||||
HudFrameTimeItem::HudFrameTimeItem(const Rc<DxvkDevice>& device, HudRenderer* renderer)
|
||||
: m_device (device),
|
||||
m_gfxSetLayout (createDescriptorSetLayout()),
|
||||
m_gfxPipelineLayout (createPipelineLayout()) {
|
||||
createComputePipeline(*renderer);
|
||||
|
||||
renderer->createShaderModule(m_vs, VK_SHADER_STAGE_VERTEX_BIT, sizeof(hud_graph_vert), hud_graph_vert);
|
||||
renderer->createShaderModule(m_fs, VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(hud_graph_frag), hud_graph_frag);
|
||||
}
|
||||
|
||||
|
||||
HudFrameTimeItem::~HudFrameTimeItem() {
|
||||
auto vk = m_device->vkd();
|
||||
|
||||
for (const auto& p : m_gfxPipelines)
|
||||
vk->vkDestroyPipeline(vk->device(), p.second, nullptr);
|
||||
|
||||
vk->vkDestroyShaderModule(vk->device(), m_vs.stageInfo.module, nullptr);
|
||||
vk->vkDestroyShaderModule(vk->device(), m_fs.stageInfo.module, nullptr);
|
||||
|
||||
vk->vkDestroyPipeline(vk->device(), m_computePipeline, nullptr);
|
||||
vk->vkDestroyPipelineLayout(vk->device(), m_computePipelineLayout, nullptr);
|
||||
vk->vkDestroyDescriptorSetLayout(vk->device(), m_computeSetLayout, nullptr);
|
||||
|
||||
vk->vkDestroyPipelineLayout(vk->device(), m_gfxPipelineLayout, nullptr);
|
||||
vk->vkDestroyDescriptorSetLayout(vk->device(), m_gfxSetLayout, nullptr);
|
||||
}
|
||||
|
||||
|
||||
HudPos HudFrameTimeItem::render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
std::array<HudGraphPoint, NumDataPoints> points;
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
if (!m_gpuBuffer)
|
||||
createResources(ctx);
|
||||
|
||||
// 60 FPS = optimal, 10 FPS = worst
|
||||
const float targetUs = 16'666.6f;
|
||||
const float minUs = 5'000.0f;
|
||||
const float maxUs = 100'000.0f;
|
||||
|
||||
// Ten times the maximum/minimum number
|
||||
// of milliseconds for a single frame
|
||||
uint32_t minMs = 0xFFFFFFFFu;
|
||||
uint32_t maxMs = 0x00000000u;
|
||||
|
||||
// Paint the time points
|
||||
for (uint32_t i = 0; i < NumDataPoints; i++) {
|
||||
float us = m_dataPoints[(m_dataPointId + i) % NumDataPoints];
|
||||
|
||||
minMs = std::min(minMs, uint32_t(us / 100.0f));
|
||||
maxMs = std::max(maxMs, uint32_t(us / 100.0f));
|
||||
|
||||
float r = std::min(std::max(-1.0f + us / targetUs, 0.0f), 1.0f);
|
||||
float g = std::min(std::max( 3.0f - us / targetUs, 0.0f), 1.0f);
|
||||
float l = std::sqrt(r * r + g * g);
|
||||
|
||||
HudNormColor color = {
|
||||
uint8_t(255.0f * (r / l)),
|
||||
uint8_t(255.0f * (g / l)),
|
||||
uint8_t(0), uint8_t(255) };
|
||||
|
||||
float hVal = std::log2(std::max((us - minUs) / targetUs + 1.0f, 1.0f))
|
||||
/ std::log2((maxUs - minUs) / targetUs);
|
||||
|
||||
points[i].value = std::max(hVal, 1.0f / 40.0f);
|
||||
points[i].color = color;
|
||||
}
|
||||
|
||||
renderer.drawGraph(position,
|
||||
HudPos { float(NumDataPoints), 40.0f },
|
||||
points.size(), points.data());
|
||||
|
||||
position.y += 58.0f;
|
||||
HudPos minPos = { 12, -128 };
|
||||
HudPos maxPos = { 162, -128 };
|
||||
|
||||
renderer.drawText(12.0f,
|
||||
{ position.x, position.y },
|
||||
{ 1.0f, 0.25f, 0.25f, 1.0f },
|
||||
"min:");
|
||||
HudPos graphPos = { 8, -120 };
|
||||
HudPos graphSize = { NumDataPoints, 80 };
|
||||
|
||||
uint32_t dataPoint = m_nextDataPoint++;
|
||||
|
||||
processFrameTimes(ctx, key, renderer,
|
||||
dataPoint, minPos, maxPos);
|
||||
|
||||
drawFrameTimeGraph(ctx, key, renderer,
|
||||
dataPoint, graphPos, graphSize);
|
||||
|
||||
if (m_nextDataPoint >= NumDataPoints)
|
||||
m_nextDataPoint = 0u;
|
||||
|
||||
renderer.drawText(12.0f,
|
||||
{ position.x + 45.0f, position.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
str::format(minMs / 10, ".", minMs % 10));
|
||||
|
||||
renderer.drawText(12.0f,
|
||||
{ position.x + 150.0f, position.y },
|
||||
{ 1.0f, 0.25f, 0.25f, 1.0f },
|
||||
"max:");
|
||||
|
||||
renderer.drawText(12.0f,
|
||||
{ position.x + 195.0f, position.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
str::format(maxMs / 10, ".", maxMs % 10));
|
||||
|
||||
position.y += 4.0f;
|
||||
return position;
|
||||
}
|
||||
|
||||
|
||||
void HudFrameTimeItem::processFrameTimes(
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
HudRenderer& renderer,
|
||||
uint32_t dataPoint,
|
||||
HudPos minPos,
|
||||
HudPos maxPos) {
|
||||
// Write current time stamp to the buffer
|
||||
DxvkBufferSliceHandle sliceHandle = m_gpuBuffer->getSliceHandle();
|
||||
|
||||
ctx.cmd->cmdResetQueryPool(DxvkCmdBuffer::InitBuffer,
|
||||
m_queryPool->handle(), 0, 1);
|
||||
|
||||
ctx.cmd->cmdWriteTimestamp(DxvkCmdBuffer::InitBuffer,
|
||||
VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
|
||||
m_queryPool->handle(), 0);
|
||||
|
||||
ctx.cmd->cmdCopyQueryPoolResults(DxvkCmdBuffer::InitBuffer,
|
||||
m_queryPool->handle(), 0, 1, sliceHandle.handle,
|
||||
sliceHandle.offset + (dataPoint & 1u) * sizeof(uint64_t), sizeof(uint64_t),
|
||||
VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
|
||||
|
||||
VkMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 };
|
||||
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
barrier.srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
|
||||
barrier.dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
|
||||
|
||||
VkDependencyInfo depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };
|
||||
depInfo.memoryBarrierCount = 1u;
|
||||
depInfo.pMemoryBarriers = &barrier;
|
||||
|
||||
ctx.cmd->cmdPipelineBarrier(DxvkCmdBuffer::InitBuffer, &depInfo);
|
||||
|
||||
// Process contents of the buffer and write out text draws
|
||||
VkDescriptorSet set = ctx.descriptorPool->alloc(m_computeSetLayout);
|
||||
|
||||
auto bufferLayout = computeBufferLayout();
|
||||
|
||||
VkDescriptorBufferInfo frameTimeBuffer = m_gpuBuffer->getDescriptor(
|
||||
0, bufferLayout.timestampSize).buffer;
|
||||
|
||||
VkDescriptorBufferInfo drawInfoBuffer = m_gpuBuffer->getDescriptor(
|
||||
bufferLayout.drawInfoOffset, bufferLayout.drawInfoSize).buffer;
|
||||
|
||||
VkDescriptorBufferInfo drawParamBuffer = m_gpuBuffer->getDescriptor(
|
||||
bufferLayout.drawParamOffset, bufferLayout.drawParamSize).buffer;
|
||||
|
||||
VkBufferView textBufferView = m_textView->handle();
|
||||
|
||||
std::array<VkWriteDescriptorSet, 4> descriptorWrites = {{
|
||||
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
|
||||
set, 0, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &frameTimeBuffer },
|
||||
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
|
||||
set, 1, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &drawParamBuffer },
|
||||
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
|
||||
set, 2, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &drawInfoBuffer },
|
||||
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
|
||||
set, 3, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, nullptr, nullptr, &textBufferView },
|
||||
}};
|
||||
|
||||
ctx.cmd->updateDescriptorSets(
|
||||
descriptorWrites.size(),
|
||||
descriptorWrites.data());
|
||||
|
||||
ctx.cmd->cmdBindPipeline(DxvkCmdBuffer::InitBuffer,
|
||||
VK_PIPELINE_BIND_POINT_COMPUTE, m_computePipeline);
|
||||
|
||||
ctx.cmd->cmdBindDescriptorSet(DxvkCmdBuffer::InitBuffer,
|
||||
VK_PIPELINE_BIND_POINT_COMPUTE, m_computePipelineLayout,
|
||||
set, 0, nullptr);
|
||||
|
||||
ComputePushConstants pushConstants = { };
|
||||
pushConstants.msPerTick = m_device->properties().core.properties.limits.timestampPeriod / 1000000.0f;
|
||||
pushConstants.dataPoint = dataPoint;
|
||||
pushConstants.textPosMinX = minPos.x + 48;
|
||||
pushConstants.textPosMinY = minPos.y;
|
||||
pushConstants.textPosMaxX = maxPos.x + 48;
|
||||
pushConstants.textPosMaxY = maxPos.y;
|
||||
|
||||
ctx.cmd->cmdPushConstants(DxvkCmdBuffer::InitBuffer,
|
||||
m_computePipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
0, sizeof(pushConstants), &pushConstants);
|
||||
|
||||
ctx.cmd->cmdDispatch(DxvkCmdBuffer::InitBuffer, 1, 1, 1);
|
||||
|
||||
barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 };
|
||||
barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
|
||||
barrier.srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
|
||||
barrier.dstAccessMask = m_gpuBuffer->info().access;
|
||||
barrier.dstStageMask = m_gpuBuffer->info().stages;
|
||||
|
||||
ctx.cmd->cmdPipelineBarrier(DxvkCmdBuffer::InitBuffer, &depInfo);
|
||||
|
||||
// Display the min/max numbers
|
||||
renderer.drawText(12, minPos, 0xff4040ff, "min:");
|
||||
renderer.drawText(12, maxPos, 0xff4040ff, "max:");
|
||||
|
||||
renderer.drawTextIndirect(ctx, key, drawParamBuffer,
|
||||
drawInfoBuffer, textBufferView, 2u);
|
||||
|
||||
// Make sure GPU resources are being kept alive as necessary
|
||||
ctx.cmd->trackResource<DxvkAccess::Write>(m_gpuBuffer->getAllocation());
|
||||
ctx.cmd->trackResource<DxvkAccess::Write>(m_queryPool);
|
||||
}
|
||||
|
||||
|
||||
void HudFrameTimeItem::drawFrameTimeGraph(
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
HudRenderer& renderer,
|
||||
uint32_t dataPoint,
|
||||
HudPos graphPos,
|
||||
HudPos graphSize) {
|
||||
ctx.cmd->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,
|
||||
VK_PIPELINE_BIND_POINT_GRAPHICS, getPipeline(renderer, key));
|
||||
|
||||
auto set = ctx.descriptorPool->alloc(m_gfxSetLayout);
|
||||
|
||||
VkDescriptorBufferInfo bufferDescriptor = m_gpuBuffer->getDescriptor(0,
|
||||
computeBufferLayout().timestampSize).buffer;
|
||||
|
||||
VkWriteDescriptorSet descriptorWrite = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
|
||||
descriptorWrite.dstSet = set;
|
||||
descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
descriptorWrite.descriptorCount = 1;
|
||||
descriptorWrite.pBufferInfo = &bufferDescriptor;
|
||||
|
||||
ctx.cmd->updateDescriptorSets(1, &descriptorWrite);
|
||||
|
||||
ctx.cmd->cmdBindDescriptorSet(DxvkCmdBuffer::ExecBuffer,
|
||||
VK_PIPELINE_BIND_POINT_GRAPHICS, m_gfxPipelineLayout,
|
||||
set, 0, nullptr);
|
||||
|
||||
RenderPushConstants pushConstants = { };
|
||||
pushConstants.hud = renderer.getPushConstants();
|
||||
pushConstants.x = graphPos.x;
|
||||
pushConstants.y = graphPos.y;
|
||||
pushConstants.w = graphSize.x;
|
||||
pushConstants.h = graphSize.y;
|
||||
pushConstants.frameIndex = dataPoint;
|
||||
|
||||
ctx.cmd->cmdPushConstants(DxvkCmdBuffer::ExecBuffer, m_gfxPipelineLayout,
|
||||
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
0, sizeof(pushConstants), &pushConstants);
|
||||
|
||||
ctx.cmd->cmdDraw(4, 1, 0, 0);
|
||||
|
||||
ctx.cmd->trackResource<DxvkAccess::Read>(m_gpuBuffer->getAllocation());
|
||||
}
|
||||
|
||||
|
||||
void HudFrameTimeItem::createResources(
|
||||
const DxvkContextObjects& ctx) {
|
||||
auto bufferLayout = computeBufferLayout();
|
||||
|
||||
DxvkBufferCreateInfo bufferInfo = { };
|
||||
bufferInfo.size = bufferLayout.totalSize;
|
||||
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT
|
||||
| VK_BUFFER_USAGE_TRANSFER_SRC_BIT
|
||||
| VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT
|
||||
| VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
|
||||
| VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT
|
||||
| VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
|
||||
bufferInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT
|
||||
| VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT
|
||||
| VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT
|
||||
| VK_PIPELINE_STAGE_VERTEX_SHADER_BIT
|
||||
| VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
bufferInfo.access = VK_ACCESS_TRANSFER_READ_BIT
|
||||
| VK_ACCESS_TRANSFER_WRITE_BIT
|
||||
| VK_ACCESS_INDIRECT_COMMAND_READ_BIT
|
||||
| VK_ACCESS_SHADER_READ_BIT
|
||||
| VK_ACCESS_SHADER_WRITE_BIT;
|
||||
|
||||
m_gpuBuffer = m_device->createBuffer(bufferInfo, VK_MEMORY_HEAP_DEVICE_LOCAL_BIT);
|
||||
|
||||
DxvkBufferViewKey textViewInfo = { };
|
||||
textViewInfo.format = VK_FORMAT_R8_UINT;
|
||||
textViewInfo.usage = VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
|
||||
textViewInfo.offset = bufferLayout.textOffset;
|
||||
textViewInfo.size = bufferLayout.textSize;
|
||||
|
||||
m_textView = m_gpuBuffer->createView(textViewInfo);
|
||||
|
||||
// Zero-init buffer so we don't display random garbage at the start
|
||||
DxvkBufferSliceHandle bufferSlice = m_gpuBuffer->getSliceHandle();
|
||||
|
||||
ctx.cmd->cmdFillBuffer(DxvkCmdBuffer::InitBuffer,
|
||||
bufferSlice.handle, bufferSlice.offset, bufferSlice.length, 0u);
|
||||
|
||||
VkMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 };
|
||||
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
barrier.srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
barrier.dstAccessMask = m_gpuBuffer->info().access;
|
||||
barrier.dstStageMask = m_gpuBuffer->info().stages;
|
||||
|
||||
VkDependencyInfo depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };
|
||||
depInfo.memoryBarrierCount = 1u;
|
||||
depInfo.pMemoryBarriers = &barrier;
|
||||
|
||||
ctx.cmd->cmdPipelineBarrier(DxvkCmdBuffer::InitBuffer, &depInfo);
|
||||
ctx.cmd->trackResource<DxvkAccess::Write>(m_gpuBuffer->getAllocation());
|
||||
|
||||
// We'll use and initialize this later as necessary
|
||||
m_queryPool = new HudFrameTimeQueryPool(m_device);
|
||||
}
|
||||
|
||||
|
||||
void HudFrameTimeItem::createComputePipeline(
|
||||
HudRenderer& renderer) {
|
||||
auto vk = m_device->vkd();
|
||||
|
||||
std::array<VkDescriptorSetLayoutBinding, 4> bindings = {{
|
||||
{ 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT },
|
||||
{ 3, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT },
|
||||
}};
|
||||
|
||||
VkDescriptorSetLayoutCreateInfo setLayoutInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
|
||||
setLayoutInfo.bindingCount = bindings.size();
|
||||
setLayoutInfo.pBindings = bindings.data();
|
||||
|
||||
VkResult vr = vk->vkCreateDescriptorSetLayout(vk->device(),
|
||||
&setLayoutInfo, nullptr, &m_computeSetLayout);
|
||||
|
||||
if (vr != VK_SUCCESS)
|
||||
throw DxvkError(str::format("Failed to create frame time compute set layout: ", vr));
|
||||
|
||||
VkPushConstantRange pushConstantRange = { };
|
||||
pushConstantRange.offset = 0u;
|
||||
pushConstantRange.size = sizeof(ComputePushConstants);
|
||||
pushConstantRange.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
|
||||
|
||||
VkPipelineLayoutCreateInfo pipelineLayoutInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
|
||||
pipelineLayoutInfo.setLayoutCount = 1u;
|
||||
pipelineLayoutInfo.pSetLayouts = &m_computeSetLayout;
|
||||
pipelineLayoutInfo.pushConstantRangeCount = 1;
|
||||
pipelineLayoutInfo.pPushConstantRanges = &pushConstantRange;
|
||||
|
||||
vr = vk->vkCreatePipelineLayout(vk->device(),
|
||||
&pipelineLayoutInfo, nullptr, &m_computePipelineLayout);
|
||||
|
||||
if (vr != VK_SUCCESS)
|
||||
throw DxvkError(str::format("Failed to create frame time compute pipeline layout: ", vr));
|
||||
|
||||
HudShaderModule shader = { };
|
||||
renderer.createShaderModule(shader, VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
sizeof(hud_frame_time_eval), hud_frame_time_eval);
|
||||
|
||||
VkComputePipelineCreateInfo info = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO };
|
||||
info.stage = shader.stageInfo;
|
||||
info.layout = m_computePipelineLayout;
|
||||
info.basePipelineIndex = -1;
|
||||
|
||||
vr = vk->vkCreateComputePipelines(vk->device(),
|
||||
VK_NULL_HANDLE, 1, &info, nullptr, &m_computePipeline);
|
||||
|
||||
if (vr != VK_SUCCESS)
|
||||
throw DxvkError(str::format("Failed to create frame time compute pipeline: ", vr));
|
||||
|
||||
vk->vkDestroyShaderModule(vk->device(), shader.stageInfo.module, nullptr);
|
||||
}
|
||||
|
||||
|
||||
VkDescriptorSetLayout HudFrameTimeItem::createDescriptorSetLayout() {
|
||||
auto vk = m_device->vkd();
|
||||
|
||||
std::array<VkDescriptorSetLayoutBinding, 1> bindings = {{
|
||||
{ 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT },
|
||||
}};
|
||||
|
||||
VkDescriptorSetLayoutCreateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
|
||||
info.bindingCount = bindings.size();
|
||||
info.pBindings = bindings.data();
|
||||
|
||||
VkDescriptorSetLayout layout = VK_NULL_HANDLE;
|
||||
VkResult vr = vk->vkCreateDescriptorSetLayout(
|
||||
vk->device(), &info, nullptr, &layout);
|
||||
|
||||
if (vr != VK_SUCCESS)
|
||||
throw DxvkError(str::format("Failed to create frame time graphics pipeline descriptor set layout: ", vr));
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
|
||||
VkPipelineLayout HudFrameTimeItem::createPipelineLayout() {
|
||||
auto vk = m_device->vkd();
|
||||
|
||||
VkPushConstantRange pushConstantRange = { };
|
||||
pushConstantRange.offset = 0u;
|
||||
pushConstantRange.size = sizeof(RenderPushConstants);
|
||||
pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
|
||||
VkPipelineLayoutCreateInfo info = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
|
||||
info.setLayoutCount = 1u;
|
||||
info.pSetLayouts = &m_gfxSetLayout;
|
||||
info.pushConstantRangeCount = 1;
|
||||
info.pPushConstantRanges = &pushConstantRange;
|
||||
|
||||
VkPipelineLayout layout = VK_NULL_HANDLE;
|
||||
VkResult vr = vk->vkCreatePipelineLayout(
|
||||
vk->device(), &info, nullptr, &layout);
|
||||
|
||||
if (vr != VK_SUCCESS)
|
||||
throw DxvkError(str::format("Failed to create frame time graphics pipeline layout: ", vr));
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
|
||||
VkPipeline HudFrameTimeItem::getPipeline(
|
||||
HudRenderer& renderer,
|
||||
const HudPipelineKey& key) {
|
||||
auto entry = m_gfxPipelines.find(key);
|
||||
|
||||
if (entry != m_gfxPipelines.end())
|
||||
return entry->second;
|
||||
|
||||
VkPipeline pipeline = createPipeline(renderer, key);
|
||||
m_gfxPipelines.insert({ key, pipeline });
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
|
||||
VkPipeline HudFrameTimeItem::createPipeline(
|
||||
HudRenderer& renderer,
|
||||
const HudPipelineKey& key) {
|
||||
auto vk = m_device->vkd();
|
||||
|
||||
HudSpecConstants specConstants = renderer.getSpecConstants(key);
|
||||
VkSpecializationInfo specInfo = renderer.getSpecInfo(&specConstants);
|
||||
|
||||
std::array<VkPipelineShaderStageCreateInfo, 2> stages = { };
|
||||
stages[0] = m_vs.stageInfo;
|
||||
stages[1] = m_fs.stageInfo;
|
||||
stages[1].pSpecializationInfo = &specInfo;
|
||||
|
||||
VkPipelineRenderingCreateInfo rtInfo = { VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO };
|
||||
rtInfo.colorAttachmentCount = 1;
|
||||
rtInfo.pColorAttachmentFormats = &key.format;
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo viState = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo iaState = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
|
||||
iaState.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
|
||||
|
||||
VkPipelineViewportStateCreateInfo vpState = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };
|
||||
|
||||
VkPipelineRasterizationStateCreateInfo rsState = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
|
||||
rsState.cullMode = VK_CULL_MODE_NONE;
|
||||
rsState.polygonMode = VK_POLYGON_MODE_FILL;
|
||||
rsState.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
|
||||
rsState.lineWidth = 1.0f;
|
||||
|
||||
constexpr uint32_t sampleMask = 0x1;
|
||||
|
||||
VkPipelineMultisampleStateCreateInfo msState = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
|
||||
msState.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
msState.pSampleMask = &sampleMask;
|
||||
|
||||
VkPipelineColorBlendAttachmentState cbAttachment = { };
|
||||
cbAttachment.blendEnable = VK_TRUE;
|
||||
cbAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
cbAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
cbAttachment.colorBlendOp = VK_BLEND_OP_ADD;
|
||||
cbAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
cbAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
cbAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
|
||||
cbAttachment.colorWriteMask =
|
||||
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
|
||||
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
|
||||
|
||||
VkPipelineColorBlendStateCreateInfo cbOpaqueState = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO };
|
||||
cbOpaqueState.attachmentCount = 1;
|
||||
cbOpaqueState.pAttachments = &cbAttachment;
|
||||
|
||||
static const std::array<VkDynamicState, 2> dynStates = {
|
||||
VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT,
|
||||
VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT,
|
||||
};
|
||||
|
||||
VkPipelineDynamicStateCreateInfo dynState = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
|
||||
dynState.dynamicStateCount = dynStates.size();
|
||||
dynState.pDynamicStates = dynStates.data();
|
||||
|
||||
VkGraphicsPipelineCreateInfo info = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, &rtInfo };
|
||||
info.stageCount = stages.size();
|
||||
info.pStages = stages.data();
|
||||
info.pVertexInputState = &viState;
|
||||
info.pInputAssemblyState = &iaState;
|
||||
info.pViewportState = &vpState;
|
||||
info.pRasterizationState = &rsState;
|
||||
info.pMultisampleState = &msState;
|
||||
info.pColorBlendState = &cbOpaqueState;
|
||||
info.pDynamicState = &dynState;
|
||||
info.layout = m_gfxPipelineLayout;
|
||||
info.basePipelineIndex = -1;
|
||||
|
||||
VkPipeline pipeline = { };
|
||||
VkResult vr = vk->vkCreateGraphicsPipelines(vk->device(),
|
||||
VK_NULL_HANDLE, 1, &info, nullptr, &pipeline);
|
||||
|
||||
if (vr != VK_SUCCESS)
|
||||
throw DxvkError(str::format("Failed to create HUD memory detail pipeline 1: ", vr));
|
||||
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
|
||||
HudFrameTimeItem::BufferLayout HudFrameTimeItem::computeBufferLayout() {
|
||||
struct ComputeTimestampBuffer {
|
||||
std::array<uint64_t, 2u> timestamps;
|
||||
std::array<float, NumDataPoints> intervals;
|
||||
float avgMs;
|
||||
float minMs;
|
||||
float maxMs;
|
||||
};
|
||||
|
||||
BufferLayout result = { };
|
||||
result.timestampSize = align(sizeof(ComputeTimestampBuffer), 256u);
|
||||
result.drawInfoOffset = result.timestampSize;
|
||||
result.drawInfoSize = align(sizeof(HudTextDrawInfo) * NumTextDraws, 256u);
|
||||
result.drawParamOffset = result.drawInfoOffset + result.drawInfoSize;
|
||||
result.drawParamSize = align(sizeof(VkDrawIndirectCommand) * NumTextDraws, 256u);
|
||||
result.textOffset = result.drawParamOffset + result.drawParamSize;
|
||||
result.textSize = 256u;
|
||||
result.totalSize = result.textOffset + result.textSize;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
HudSubmissionStatsItem::HudSubmissionStatsItem(const Rc<DxvkDevice>& device)
|
||||
: m_device(device) {
|
||||
|
||||
@ -342,32 +772,20 @@ namespace dxvk::hud {
|
||||
|
||||
|
||||
HudPos HudSubmissionStatsItem::render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
position.y += 16.0f;
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
position.y += 16;
|
||||
renderer.drawText(16, position, 0xff4080ff, "Queue submissions:");
|
||||
renderer.drawText(16, { position.x + 228, position.y }, 0xffffffffu, m_submitString);
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x, position.y },
|
||||
{ 1.0f, 0.5f, 0.25f, 1.0f },
|
||||
"Queue submissions:");
|
||||
position.y += 20;
|
||||
renderer.drawText(16, position, 0xff4080ff, "Queue syncs:");
|
||||
renderer.drawText(16, { position.x + 228, position.y }, 0xffffffffu, m_syncString);
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x + 228.0f, position.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
m_submitString);
|
||||
|
||||
position.y += 20.0f;
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x, position.y },
|
||||
{ 1.0f, 0.5f, 0.25f, 1.0f },
|
||||
"Queue syncs:");
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x + 228.0f, position.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
m_syncString);
|
||||
|
||||
position.y += 8.0f;
|
||||
position.y += 8;
|
||||
return position;
|
||||
}
|
||||
|
||||
@ -403,53 +821,28 @@ namespace dxvk::hud {
|
||||
|
||||
|
||||
HudPos HudDrawCallStatsItem::render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
position.y += 16.0f;
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x, position.y },
|
||||
{ 0.25f, 0.5f, 1.0f, 1.0f },
|
||||
"Draw calls:");
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
position.y += 16;
|
||||
renderer.drawText(16, position, 0xffff8040, "Draw calls:");
|
||||
renderer.drawText(16, { position.x + 192, position.y }, 0xffffffffu, str::format(m_gpCount));
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x + 192.0f, position.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
str::format(m_gpCount));
|
||||
position.y += 20;
|
||||
renderer.drawText(16, position, 0xffff8040, "Dispatch calls:");
|
||||
renderer.drawText(16, { position.x + 192, position.y }, 0xffffffffu, str::format(m_cpCount));
|
||||
|
||||
position.y += 20.0f;
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x, position.y },
|
||||
{ 0.25f, 0.5f, 1.0f, 1.0f },
|
||||
"Dispatch calls:");
|
||||
position.y += 20;
|
||||
renderer.drawText(16, position, 0xffff8040, "Render passes:");
|
||||
renderer.drawText(16, { position.x + 192, position.y }, 0xffffffffu, str::format(m_rpCount));
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x + 192.0f, position.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
str::format(m_cpCount));
|
||||
position.y += 20;
|
||||
renderer.drawText(16, position, 0xffff8040, "Barriers:");
|
||||
renderer.drawText(16, { position.x + 192, position.y }, 0xffffffffu, str::format(m_pbCount));
|
||||
|
||||
position.y += 20.0f;
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x, position.y },
|
||||
{ 0.25f, 0.5f, 1.0f, 1.0f },
|
||||
"Render passes:");
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x + 192.0f, position.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
str::format(m_rpCount));
|
||||
|
||||
position.y += 20.0f;
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x, position.y },
|
||||
{ 0.25f, 0.5f, 1.0f, 1.0f },
|
||||
"Barriers:");
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x + 192.0f, position.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
str::format(m_pbCount));
|
||||
|
||||
position.y += 8.0f;
|
||||
position.y += 8;
|
||||
return position;
|
||||
}
|
||||
|
||||
@ -475,44 +868,26 @@ namespace dxvk::hud {
|
||||
|
||||
|
||||
HudPos HudPipelineStatsItem::render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
position.y += 16.0f;
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x, position.y },
|
||||
{ 1.0f, 0.25f, 1.0f, 1.0f },
|
||||
"Graphics pipelines:");
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x + 240.0f, position.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
str::format(m_graphicsPipelines));
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
position.y += 16;
|
||||
renderer.drawText(16, position, 0xffff40ff, "Graphics pipelines:");
|
||||
renderer.drawText(16, { position.x + 240, position.y }, 0xffffffffu, str::format(m_graphicsPipelines));
|
||||
|
||||
if (m_graphicsLibraries) {
|
||||
position.y += 20.0f;
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x, position.y },
|
||||
{ 1.0f, 0.25f, 1.0f, 1.0f },
|
||||
"Graphics shaders:");
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x + 240.0f, position.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
str::format(m_graphicsLibraries));
|
||||
position.y += 20;
|
||||
renderer.drawText(16, position, 0xffff40ff, "Graphics shaders:");
|
||||
renderer.drawText(16, { position.x + 240, position.y }, 0xffffffffu, str::format(m_graphicsLibraries));
|
||||
}
|
||||
|
||||
position.y += 20.0f;
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x, position.y },
|
||||
{ 1.0f, 0.25f, 1.0f, 1.0f },
|
||||
"Compute pipelines:");
|
||||
position.y += 20;
|
||||
renderer.drawText(16, position, 0xffff40ff, "Compute shaders:");
|
||||
renderer.drawText(16, { position.x + 240, position.y }, 0xffffffffu, str::format(m_computePipelines));
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x + 240.0f, position.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
str::format(m_computePipelines));
|
||||
|
||||
position.y += 8.0f;
|
||||
position.y += 8;
|
||||
return position;
|
||||
}
|
||||
|
||||
@ -537,31 +912,20 @@ namespace dxvk::hud {
|
||||
|
||||
|
||||
HudPos HudDescriptorStatsItem::render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
position.y += 16.0f;
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x, position.y },
|
||||
{ 1.0f, 0.25f, 0.5f, 1.0f },
|
||||
"Descriptor pools:");
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x + 216.0f, position.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
str::format(m_descriptorPoolCount));
|
||||
|
||||
position.y += 20.0f;
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x, position.y },
|
||||
{ 1.0f, 0.25f, 0.5f, 1.0f },
|
||||
"Descriptor sets:");
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
position.y += 16;
|
||||
renderer.drawText(16, position, 0xff8040ff, "Descriptor pools:");
|
||||
renderer.drawText(16, { position.x + 216, position.y }, 0xffffffffu, str::format(m_descriptorPoolCount));
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x + 216.0f, position.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
str::format(m_descriptorSetCount));
|
||||
position.y += 20;
|
||||
renderer.drawText(16, position, 0xff8040ff, "Descriptor sets:");
|
||||
renderer.drawText(16, { position.x + 216, position.y }, 0xffffffffu, str::format(m_descriptorSetCount));
|
||||
|
||||
position.y += 8.0f;
|
||||
position.y += 8;
|
||||
return position;
|
||||
}
|
||||
|
||||
@ -584,8 +948,11 @@ namespace dxvk::hud {
|
||||
|
||||
|
||||
HudPos HudMemoryStatsItem::render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
for (uint32_t i = 0; i < m_memory.memoryHeapCount; i++) {
|
||||
bool isDeviceLocal = m_memory.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT;
|
||||
|
||||
@ -599,57 +966,49 @@ namespace dxvk::hud {
|
||||
std::string text = str::format(std::setfill(' '), std::setw(5), memAllocatedMib, " MB (", percentage, "%) ",
|
||||
std::setw(5 + (percentage < 10 ? 1 : 0) + (percentage < 100 ? 1 : 0)), memUsedMib, " MB used");
|
||||
|
||||
position.y += 16.0f;
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x, position.y },
|
||||
{ 1.0f, 1.0f, 0.25f, 1.0f },
|
||||
label);
|
||||
position.y += 16;
|
||||
renderer.drawText(16, position, 0xff40ffffu, label);
|
||||
renderer.drawText(16, { position.x + 168, position.y }, 0xffffffffu, text);
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x + 168.0f, position.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
text);
|
||||
position.y += 4.0f;
|
||||
position.y += 4;
|
||||
}
|
||||
|
||||
position.y += 4.0f;
|
||||
position.y += 4;
|
||||
return position;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
HudMemoryDetailsItem::HudMemoryDetailsItem(const Rc<DxvkDevice>& device)
|
||||
: m_device(device) {
|
||||
DxvkShaderCreateInfo shaderInfo;
|
||||
shaderInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
shaderInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
shaderInfo.pushConstSize = sizeof(ShaderArgs);
|
||||
shaderInfo.outputMask = 0x1;
|
||||
|
||||
m_vs = new DxvkShader(shaderInfo, SpirvCodeBuffer(hud_chunk_vert));
|
||||
|
||||
shaderInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
shaderInfo.outputMask = 0x1;
|
||||
|
||||
m_fsBackground = new DxvkShader(shaderInfo, SpirvCodeBuffer(hud_chunk_frag_background));
|
||||
|
||||
DxvkBindingInfo pageMaskBinding = {
|
||||
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 0, VK_IMAGE_VIEW_TYPE_MAX_ENUM,
|
||||
VK_SHADER_STAGE_FRAGMENT_BIT, VK_ACCESS_SHADER_READ_BIT };
|
||||
|
||||
shaderInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
shaderInfo.bindingCount = 1;
|
||||
shaderInfo.bindings = &pageMaskBinding;
|
||||
shaderInfo.inputMask = 0x1;
|
||||
shaderInfo.outputMask = 0x1;
|
||||
|
||||
m_fsVisualize = new DxvkShader(shaderInfo, SpirvCodeBuffer(hud_chunk_frag_visualize));
|
||||
HudMemoryDetailsItem::HudMemoryDetailsItem(
|
||||
const Rc<DxvkDevice>& device,
|
||||
HudRenderer* renderer)
|
||||
: m_device (device),
|
||||
m_setLayout (createSetLayout()),
|
||||
m_pipelineLayout (createPipelineLayout()) {
|
||||
renderer->createShaderModule(m_fsBackground, VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(hud_chunk_frag_background), hud_chunk_frag_background);
|
||||
renderer->createShaderModule(m_vsBackground, VK_SHADER_STAGE_VERTEX_BIT, sizeof(hud_chunk_vert_background), hud_chunk_vert_background);
|
||||
renderer->createShaderModule(m_fsVisualize, VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(hud_chunk_frag_visualize), hud_chunk_frag_visualize);
|
||||
renderer->createShaderModule(m_vsVisualize, VK_SHADER_STAGE_VERTEX_BIT, sizeof(hud_chunk_vert_visualize), hud_chunk_vert_visualize);
|
||||
}
|
||||
|
||||
|
||||
HudMemoryDetailsItem::~HudMemoryDetailsItem() {
|
||||
auto vk = m_device->vkd();
|
||||
|
||||
for (const auto& p : m_pipelines) {
|
||||
vk->vkDestroyPipeline(vk->device(), p.second.background, nullptr);
|
||||
vk->vkDestroyPipeline(vk->device(), p.second.visualize, nullptr);
|
||||
}
|
||||
|
||||
vk->vkDestroyShaderModule(vk->device(), m_vsBackground.stageInfo.module, nullptr);
|
||||
vk->vkDestroyShaderModule(vk->device(), m_fsBackground.stageInfo.module, nullptr);
|
||||
|
||||
vk->vkDestroyShaderModule(vk->device(), m_vsVisualize.stageInfo.module, nullptr);
|
||||
vk->vkDestroyShaderModule(vk->device(), m_fsVisualize.stageInfo.module, nullptr);
|
||||
|
||||
vk->vkDestroyPipelineLayout(vk->device(), m_pipelineLayout, nullptr);
|
||||
vk->vkDestroyDescriptorSetLayout(vk->device(), m_setLayout, nullptr);
|
||||
}
|
||||
|
||||
|
||||
@ -666,203 +1025,379 @@ namespace dxvk::hud {
|
||||
|
||||
|
||||
HudPos HudMemoryDetailsItem::render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
uploadChunkData(renderer);
|
||||
|
||||
// Chunk memory per type, not including dedicated allocations
|
||||
std::array<VkDeviceSize, VK_MAX_MEMORY_TYPES> chunkMemoryAllocated = { };
|
||||
std::array<VkDeviceSize, VK_MAX_MEMORY_TYPES> chunkMemoryUsed = { };
|
||||
|
||||
// Compute layout, align the entire element to the bottom right.
|
||||
float maxWidth = 556.0f;
|
||||
|
||||
HudPos pos = {
|
||||
float(renderer.surfaceSize().width) / renderer.scale() - 8.0f - maxWidth,
|
||||
float(renderer.surfaceSize().height) / renderer.scale() - 8.0f,
|
||||
};
|
||||
|
||||
for (uint32_t i = 0; i < m_stats.memoryTypes.size(); i++) {
|
||||
const auto& type = m_stats.memoryTypes.at(i);
|
||||
|
||||
if (!type.allocated)
|
||||
continue;
|
||||
|
||||
// Reserve space for one line of text
|
||||
pos.y -= 20.0f;
|
||||
|
||||
float width = 0.0f;
|
||||
|
||||
for (uint32_t j = 0; j < type.chunkCount; j++) {
|
||||
const auto& chunk = m_stats.chunks.at(type.chunkIndex + j);
|
||||
chunkMemoryAllocated.at(i) += chunk.capacity;
|
||||
chunkMemoryUsed.at(i) += chunk.used;
|
||||
|
||||
float pixels = float((chunk.pageCount + 15u) / 16u);
|
||||
|
||||
if (width + pixels > maxWidth) {
|
||||
pos.y -= 30.0f;
|
||||
width = 0.0f;
|
||||
}
|
||||
|
||||
width += pixels + 6.0f;
|
||||
}
|
||||
|
||||
pos.y -= 30.0f + 4.0f;
|
||||
}
|
||||
|
||||
if (m_displayCacheStats)
|
||||
pos.y -= 20.0f;
|
||||
|
||||
// Actually render the thing
|
||||
for (uint32_t i = 0; i < m_stats.memoryTypes.size(); i++) {
|
||||
const auto& type = m_stats.memoryTypes.at(i);
|
||||
|
||||
if (!type.allocated)
|
||||
continue;
|
||||
|
||||
VkDeviceSize dedicated = type.allocated - chunkMemoryAllocated.at(i);
|
||||
VkDeviceSize allocated = chunkMemoryAllocated.at(i) + dedicated;
|
||||
VkDeviceSize used = chunkMemoryUsed.at(i) + dedicated;
|
||||
|
||||
std::string headline = str::format("Mem type ", i, " [", type.properties.heapIndex, "]: ",
|
||||
type.chunkCount, " chunk", type.chunkCount != 1u ? "s" : "", " (", (allocated >> 20u), " MB, ",
|
||||
((used >= (1u << 20u)) ? used >> 20 : used >> 10),
|
||||
(used >= (1u << 20u) ? " MB" : " kB"), " used)");
|
||||
|
||||
renderer.drawText(14.0f,
|
||||
{ pos.x, pos.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
headline);
|
||||
|
||||
pos.y += 8.0f;
|
||||
|
||||
float width = 0.0f;
|
||||
|
||||
for (uint32_t j = 0; j < type.chunkCount; j++) {
|
||||
const auto& chunk = m_stats.chunks.at(type.chunkIndex + j);
|
||||
float pixels = float((chunk.pageCount + 15u) / 16u);
|
||||
|
||||
if (width + pixels > maxWidth) {
|
||||
pos.y += 30.0f;
|
||||
width = 0.0f;
|
||||
}
|
||||
|
||||
drawChunk(renderer,
|
||||
{ pos.x + width, pos.y },
|
||||
{ pixels, 24.0f },
|
||||
type.properties, chunk);
|
||||
|
||||
width += pixels + 6.0f;
|
||||
}
|
||||
|
||||
pos.y += 46.0f;
|
||||
}
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
// Layout, align the entire element to the bottom right.
|
||||
int32_t x = -564;
|
||||
int32_t y = -20;
|
||||
|
||||
if (m_displayCacheStats) {
|
||||
uint32_t hitCount = m_cacheStats.requestCount - m_cacheStats.missCount;
|
||||
uint32_t hitRate = (100 * hitCount) / std::max(m_cacheStats.requestCount, 1u);
|
||||
|
||||
std::string cacheStr = str::format("Cache: ", m_cacheStats.size >> 10, " kB (", hitRate, "% hit)");
|
||||
renderer.drawText(14, { x, y }, 0xffffffffu, cacheStr);
|
||||
|
||||
renderer.drawText(14.0f,
|
||||
{ pos.x, pos.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
cacheStr);
|
||||
y -= 24;
|
||||
}
|
||||
|
||||
for (uint32_t i = m_stats.memoryTypes.size(); i; i--) {
|
||||
const auto& type = m_stats.memoryTypes.at(i - 1);
|
||||
|
||||
if (!type.allocated)
|
||||
continue;
|
||||
|
||||
// Compute layout and gather memory stats
|
||||
DxvkMemoryStats stats = { };
|
||||
|
||||
int32_t w = 0;
|
||||
int32_t h = 0;
|
||||
|
||||
for (uint32_t j = 0; j < type.chunkCount; j++) {
|
||||
const auto& chunk = m_stats.chunks.at(type.chunkIndex + j);
|
||||
stats.memoryAllocated += chunk.capacity;
|
||||
stats.memoryUsed += chunk.used;
|
||||
|
||||
int32_t chunkWidth = (chunk.pageCount + 15u) / 16u + 2;
|
||||
|
||||
if (x + w + chunkWidth > -8) {
|
||||
w = 0;
|
||||
h += 34;
|
||||
}
|
||||
|
||||
w += chunkWidth + 6;
|
||||
}
|
||||
|
||||
if (w)
|
||||
h += 34;
|
||||
|
||||
y -= h;
|
||||
|
||||
w = 0;
|
||||
h = 8;
|
||||
|
||||
// Draw individual chunks
|
||||
for (uint32_t j = 0; j < type.chunkCount; j++) {
|
||||
const auto& chunk = m_stats.chunks.at(type.chunkIndex + j);
|
||||
|
||||
// Default VRAM, blue
|
||||
uint32_t color = 0xff804020u;
|
||||
|
||||
if (type.properties.propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) {
|
||||
// Cached memory, green
|
||||
color = 0xff208020u;
|
||||
} else if (!(type.properties.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) {
|
||||
if (!chunk.mapped) {
|
||||
// Fallback allocation, grey
|
||||
color = 0xff202020u;
|
||||
} else {
|
||||
// Uncached memory, red
|
||||
color = 0xff202080u;
|
||||
}
|
||||
} else if (chunk.mapped) {
|
||||
// Host-visible VRAM, yellow
|
||||
color = 0xff208080u;
|
||||
}
|
||||
|
||||
int32_t chunkWidth = (chunk.pageCount + 15u) / 16u + 2;
|
||||
|
||||
if (x + w + chunkWidth > -8) {
|
||||
w = 0;
|
||||
h += 34;
|
||||
}
|
||||
|
||||
drawChunk({ x + w, y + h }, { chunkWidth, 24 }, color, chunk);
|
||||
|
||||
w += chunkWidth + 6;
|
||||
}
|
||||
|
||||
// Render descriptive text
|
||||
std::string headline = str::format("Mem type ", i, " [", type.properties.heapIndex, "]: ",
|
||||
type.chunkCount, " chunk", type.chunkCount != 1u ? "s" : "", " (", (stats.memoryAllocated >> 20u), " MB, ",
|
||||
((stats.memoryUsed >= (1u << 20u)) ? stats.memoryUsed >> 20 : stats.memoryUsed >> 10),
|
||||
(stats.memoryUsed >= (1u << 20u) ? " MB" : " kB"), " used)");
|
||||
|
||||
renderer.drawText(14, { x, y }, 0xffffffffu, headline);
|
||||
|
||||
y -= 24;
|
||||
}
|
||||
|
||||
flushDraws(ctx, key, options, renderer);
|
||||
return position;
|
||||
}
|
||||
|
||||
|
||||
void HudMemoryDetailsItem::uploadChunkData(HudRenderer& renderer) {
|
||||
DxvkContext* context = renderer.getContext();
|
||||
|
||||
VkDeviceSize size = sizeof(uint32_t) * m_stats.pageMasks.size();
|
||||
|
||||
if (m_pageMaskBuffer == nullptr || m_pageMaskBuffer->info().size < size) {
|
||||
DxvkBufferCreateInfo info = { };
|
||||
info.size = std::max(VkDeviceSize(1u << 14),
|
||||
(VkDeviceSize(-1) >> bit::lzcnt(size - 1u)) + 1u);
|
||||
info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
||||
info.access = VK_ACCESS_SHADER_READ_BIT;
|
||||
info.stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
|
||||
m_pageMaskBuffer = m_device->createBuffer(info,
|
||||
VK_MEMORY_HEAP_DEVICE_LOCAL_BIT |
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
|
||||
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
||||
|
||||
DxvkBufferViewKey viewInfo = { };
|
||||
viewInfo.format = VK_FORMAT_UNDEFINED;
|
||||
viewInfo.offset = 0;
|
||||
viewInfo.size = info.size;
|
||||
viewInfo.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
||||
|
||||
m_pageMaskView = m_pageMaskBuffer->createView(viewInfo);
|
||||
}
|
||||
|
||||
if (!m_stats.pageMasks.empty()) {
|
||||
context->invalidateBuffer(m_pageMaskBuffer, m_pageMaskBuffer->allocateSlice());
|
||||
std::memcpy(m_pageMaskBuffer->mapPtr(0), &m_stats.pageMasks.at(0), size);
|
||||
}
|
||||
void HudMemoryDetailsItem::drawChunk(
|
||||
HudPos pos,
|
||||
HudPos size,
|
||||
uint32_t color,
|
||||
const DxvkMemoryChunkStats& chunk) {
|
||||
auto& draw = m_drawInfos.emplace_back();
|
||||
draw.x = pos.x;
|
||||
draw.y = pos.y;
|
||||
draw.w = size.x;
|
||||
draw.h = size.y;
|
||||
draw.pageMask = chunk.pageMaskOffset;
|
||||
draw.pageCount = chunk.pageCount;
|
||||
draw.color = color;
|
||||
}
|
||||
|
||||
|
||||
void HudMemoryDetailsItem::drawChunk(
|
||||
HudRenderer& renderer,
|
||||
HudPos pos,
|
||||
HudPos size,
|
||||
const VkMemoryType& memoryType,
|
||||
const DxvkMemoryChunkStats& stats) const {
|
||||
DxvkContext* context = renderer.getContext();
|
||||
VkExtent2D surfaceSize = renderer.surfaceSize();
|
||||
void HudMemoryDetailsItem::flushDraws(
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer) {
|
||||
if (m_drawInfos.empty())
|
||||
return;
|
||||
|
||||
static const DxvkInputAssemblyState iaState = {
|
||||
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
|
||||
VK_FALSE, 0 };
|
||||
PipelinePair pipelines = getPipeline(renderer, key);
|
||||
|
||||
context->setInputAssemblyState(iaState);
|
||||
context->bindResourceBufferView(VK_SHADER_STAGE_FRAGMENT_BIT, 0, Rc<DxvkBufferView>(m_pageMaskView));
|
||||
ctx.cmd->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,
|
||||
VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.background);
|
||||
|
||||
context->bindShader<VK_SHADER_STAGE_VERTEX_BIT>(Rc<DxvkShader>(m_vs));
|
||||
context->bindShader<VK_SHADER_STAGE_FRAGMENT_BIT>(Rc<DxvkShader>(m_fsBackground));
|
||||
// Bind resources
|
||||
VkDescriptorSet set = ctx.descriptorPool->alloc(m_setLayout);
|
||||
|
||||
ShaderArgs args = { };
|
||||
args.pos.x = pos.x - 1.0f;
|
||||
args.pos.y = pos.y - 1.0f;
|
||||
args.size.x = size.x + 2.0f;
|
||||
args.size.y = size.y + 2.0f;
|
||||
args.scale.x = renderer.scale() / std::max(float(surfaceSize.width), 1.0f);
|
||||
args.scale.y = renderer.scale() / std::max(float(surfaceSize.height), 1.0f);
|
||||
args.opacity = renderer.opacity();
|
||||
args.color = 0xc0000000u;
|
||||
args.maskIndex = stats.pageMaskOffset;
|
||||
args.pageCount = stats.pageCount;
|
||||
VkDescriptorBufferInfo drawDescriptor = { };
|
||||
VkDescriptorBufferInfo dataDescriptor = { };
|
||||
|
||||
context->pushConstants(0, sizeof(args), &args);
|
||||
context->draw(4, 1, 0, 0);
|
||||
updateDataBuffer(drawDescriptor, dataDescriptor);
|
||||
|
||||
context->bindShader<VK_SHADER_STAGE_FRAGMENT_BIT>(Rc<DxvkShader>(m_fsVisualize));
|
||||
std::array<VkWriteDescriptorSet, 2> descriptorWrites = {{
|
||||
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
|
||||
set, 0, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &drawDescriptor },
|
||||
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
|
||||
set, 1, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &dataDescriptor },
|
||||
}};
|
||||
|
||||
args.pos = pos;
|
||||
args.size = size;
|
||||
ctx.cmd->updateDescriptorSets(
|
||||
descriptorWrites.size(),
|
||||
descriptorWrites.data());
|
||||
|
||||
if (memoryType.propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) {
|
||||
args.color = 0xff208020u;
|
||||
} else if (!(memoryType.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) {
|
||||
if (!stats.mapped)
|
||||
args.color = 0xff202020u;
|
||||
else
|
||||
args.color = 0xff202080u;
|
||||
} else if (stats.mapped) {
|
||||
args.color = 0xff208080u;
|
||||
ctx.cmd->cmdBindDescriptorSet(DxvkCmdBuffer::ExecBuffer,
|
||||
VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout,
|
||||
set, 0, nullptr);
|
||||
|
||||
HudPushConstants pushConstants = renderer.getPushConstants();
|
||||
|
||||
ctx.cmd->cmdPushConstants(DxvkCmdBuffer::ExecBuffer, m_pipelineLayout,
|
||||
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
0, sizeof(pushConstants), &pushConstants);
|
||||
|
||||
ctx.cmd->cmdDraw(4, m_drawInfos.size(), 0, 0);
|
||||
|
||||
ctx.cmd->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,
|
||||
VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.visualize);
|
||||
ctx.cmd->cmdDraw(4, m_drawInfos.size(), 0, 0);
|
||||
|
||||
// Track data buffer lifetime
|
||||
ctx.cmd->trackResource<DxvkAccess::Read>(m_dataBuffer->getAllocation());
|
||||
|
||||
m_drawInfos.clear();
|
||||
}
|
||||
|
||||
|
||||
void HudMemoryDetailsItem::updateDataBuffer(
|
||||
VkDescriptorBufferInfo& drawDescriptor,
|
||||
VkDescriptorBufferInfo& dataDescriptor) {
|
||||
size_t drawInfoSize = m_drawInfos.size() * sizeof(DrawInfo);
|
||||
size_t drawInfoSizeAligned = align(drawInfoSize, 256u);
|
||||
|
||||
size_t chunkDataSize = m_stats.pageMasks.size() * sizeof(uint32_t);
|
||||
size_t chunkDataSizeAligned = align(chunkDataSize, 256u);
|
||||
|
||||
size_t bufferSize = align(drawInfoSizeAligned + chunkDataSizeAligned, 2048u);
|
||||
|
||||
if (!m_dataBuffer || m_dataBuffer->info().size < bufferSize) {
|
||||
DxvkBufferCreateInfo bufferInfo;
|
||||
bufferInfo.size = bufferSize;
|
||||
bufferInfo.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
||||
bufferInfo.access = VK_ACCESS_SHADER_READ_BIT;
|
||||
bufferInfo.stages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
|
||||
m_dataBuffer = m_device->createBuffer(bufferInfo,
|
||||
VK_MEMORY_HEAP_DEVICE_LOCAL_BIT |
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
|
||||
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
||||
} else {
|
||||
args.color = 0xff804020u;
|
||||
// Ensure we can update the buffer without overriding live data
|
||||
m_dataBuffer->assignSlice(m_dataBuffer->allocateSlice());
|
||||
}
|
||||
|
||||
context->pushConstants(0, sizeof(args), &args);
|
||||
context->draw(4, 1, 0, 0);
|
||||
// Update draw infos and pad unused area with zeroes
|
||||
std::memcpy(m_dataBuffer->mapPtr(0), m_drawInfos.data(), drawInfoSize);
|
||||
std::memset(m_dataBuffer->mapPtr(drawInfoSize), 0, drawInfoSizeAligned - drawInfoSize);
|
||||
|
||||
// Update chunk data and pad with zeroes
|
||||
std::memcpy(m_dataBuffer->mapPtr(drawInfoSizeAligned), m_stats.pageMasks.data(), chunkDataSize);
|
||||
std::memset(m_dataBuffer->mapPtr(drawInfoSizeAligned + chunkDataSize), 0, chunkDataSizeAligned - chunkDataSize);
|
||||
|
||||
// Write back descriptors
|
||||
drawDescriptor = m_dataBuffer->getDescriptor(0, drawInfoSizeAligned).buffer;
|
||||
dataDescriptor = m_dataBuffer->getDescriptor(drawInfoSizeAligned, chunkDataSizeAligned).buffer;
|
||||
}
|
||||
|
||||
|
||||
VkDescriptorSetLayout HudMemoryDetailsItem::createSetLayout() {
|
||||
auto vk = m_device->vkd();
|
||||
|
||||
static const std::array<VkDescriptorSetLayoutBinding, 2> bindings = {{
|
||||
{ 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT },
|
||||
}};
|
||||
|
||||
VkDescriptorSetLayoutCreateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
|
||||
info.bindingCount = bindings.size();
|
||||
info.pBindings = bindings.data();
|
||||
|
||||
VkDescriptorSetLayout layout = VK_NULL_HANDLE;
|
||||
VkResult vr = vk->vkCreateDescriptorSetLayout(vk->device(), &info, nullptr, &layout);
|
||||
|
||||
if (vr != VK_SUCCESS)
|
||||
throw DxvkError(str::format("Failed to create HUD descriptor set layout: ", vr));
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
|
||||
VkPipelineLayout HudMemoryDetailsItem::createPipelineLayout() {
|
||||
auto vk = m_device->vkd();
|
||||
|
||||
VkPushConstantRange pushConstantRange = { };
|
||||
pushConstantRange.offset = 0u;
|
||||
pushConstantRange.size = sizeof(HudPushConstants);
|
||||
pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
|
||||
VkPipelineLayoutCreateInfo info = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
|
||||
info.setLayoutCount = 1;
|
||||
info.pSetLayouts = &m_setLayout;
|
||||
info.pushConstantRangeCount = 1;
|
||||
info.pPushConstantRanges = &pushConstantRange;
|
||||
|
||||
VkPipelineLayout layout = VK_NULL_HANDLE;
|
||||
VkResult vr = vk->vkCreatePipelineLayout(vk->device(), &info, nullptr, &layout);
|
||||
|
||||
if (vr != VK_SUCCESS)
|
||||
throw DxvkError(str::format("Failed to create HUD descriptor set layout: ", vr));
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
|
||||
HudMemoryDetailsItem::PipelinePair HudMemoryDetailsItem::createPipeline(
|
||||
HudRenderer& renderer,
|
||||
const HudPipelineKey& key) {
|
||||
auto vk = m_device->vkd();
|
||||
|
||||
HudSpecConstants specConstants = renderer.getSpecConstants(key);
|
||||
VkSpecializationInfo specInfo = renderer.getSpecInfo(&specConstants);
|
||||
|
||||
std::array<VkPipelineShaderStageCreateInfo, 2> backgroundStages = { };
|
||||
backgroundStages[0] = m_vsBackground.stageInfo;
|
||||
backgroundStages[1] = m_fsBackground.stageInfo;
|
||||
backgroundStages[1].pSpecializationInfo = &specInfo;
|
||||
|
||||
std::array<VkPipelineShaderStageCreateInfo, 2> visualizeStages = { };
|
||||
visualizeStages[0] = m_vsVisualize.stageInfo;
|
||||
visualizeStages[1] = m_fsVisualize.stageInfo;
|
||||
visualizeStages[1].pSpecializationInfo = &specInfo;
|
||||
|
||||
VkPipelineRenderingCreateInfo rtInfo = { VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO };
|
||||
rtInfo.colorAttachmentCount = 1;
|
||||
rtInfo.pColorAttachmentFormats = &key.format;
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo viState = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo iaState = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
|
||||
iaState.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
|
||||
|
||||
VkPipelineViewportStateCreateInfo vpState = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };
|
||||
|
||||
VkPipelineRasterizationStateCreateInfo rsState = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
|
||||
rsState.cullMode = VK_CULL_MODE_NONE;
|
||||
rsState.polygonMode = VK_POLYGON_MODE_FILL;
|
||||
rsState.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
|
||||
rsState.lineWidth = 1.0f;
|
||||
|
||||
constexpr uint32_t sampleMask = 0x1;
|
||||
|
||||
VkPipelineMultisampleStateCreateInfo msState = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
|
||||
msState.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
msState.pSampleMask = &sampleMask;
|
||||
|
||||
VkPipelineColorBlendAttachmentState cbAttachment = { };
|
||||
cbAttachment.blendEnable = VK_TRUE;
|
||||
cbAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
cbAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
cbAttachment.colorBlendOp = VK_BLEND_OP_ADD;
|
||||
cbAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
cbAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
cbAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
|
||||
cbAttachment.colorWriteMask =
|
||||
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
|
||||
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
|
||||
|
||||
VkPipelineColorBlendStateCreateInfo cbOpaqueState = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO };
|
||||
cbOpaqueState.attachmentCount = 1;
|
||||
cbOpaqueState.pAttachments = &cbAttachment;
|
||||
|
||||
static const std::array<VkDynamicState, 2> dynStates = {
|
||||
VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT,
|
||||
VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT,
|
||||
};
|
||||
|
||||
VkPipelineDynamicStateCreateInfo dynState = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
|
||||
dynState.dynamicStateCount = dynStates.size();
|
||||
dynState.pDynamicStates = dynStates.data();
|
||||
|
||||
VkGraphicsPipelineCreateInfo info = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, &rtInfo };
|
||||
info.stageCount = backgroundStages.size();
|
||||
info.pStages = backgroundStages.data();
|
||||
info.pVertexInputState = &viState;
|
||||
info.pInputAssemblyState = &iaState;
|
||||
info.pViewportState = &vpState;
|
||||
info.pRasterizationState = &rsState;
|
||||
info.pMultisampleState = &msState;
|
||||
info.pColorBlendState = &cbOpaqueState;
|
||||
info.pDynamicState = &dynState;
|
||||
info.layout = m_pipelineLayout;
|
||||
info.basePipelineIndex = -1;
|
||||
|
||||
PipelinePair pipelines = { };
|
||||
VkResult vr = vk->vkCreateGraphicsPipelines(vk->device(), VK_NULL_HANDLE,
|
||||
1, &info, nullptr, &pipelines.background);
|
||||
|
||||
if (vr != VK_SUCCESS)
|
||||
throw DxvkError(str::format("Failed to create HUD memory detail pipeline 1: ", vr));
|
||||
|
||||
info.stageCount = visualizeStages.size();
|
||||
info.pStages = visualizeStages.data();
|
||||
|
||||
vr = vk->vkCreateGraphicsPipelines(vk->device(), VK_NULL_HANDLE,
|
||||
1, &info, nullptr, &pipelines.visualize);
|
||||
|
||||
if (vr != VK_SUCCESS)
|
||||
throw DxvkError(str::format("Failed to create HUD memory detail pipeline 2: ", vr));
|
||||
|
||||
return pipelines;
|
||||
}
|
||||
|
||||
|
||||
HudMemoryDetailsItem::PipelinePair HudMemoryDetailsItem::getPipeline(
|
||||
HudRenderer& renderer,
|
||||
const HudPipelineKey& key) {
|
||||
auto entry = m_pipelines.find(key);
|
||||
|
||||
if (entry != m_pipelines.end())
|
||||
return entry->second;
|
||||
|
||||
PipelinePair pipeline = createPipeline(renderer, key);
|
||||
m_pipelines.insert({ key, pipeline });
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
|
||||
@ -918,31 +1453,20 @@ namespace dxvk::hud {
|
||||
|
||||
|
||||
HudPos HudCsThreadItem::render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
position.y += 16.0f;
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x, position.y },
|
||||
{ 0.25f, 1.0f, 0.25f, 1.0f },
|
||||
"CS chunks:");
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
position.y += 16;
|
||||
renderer.drawText(16, position, 0xff40ff40, "CS chunks:");
|
||||
renderer.drawText(16, { position.x + 132, position.y }, 0xffffffffu, m_csChunkString);
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x + 132.0f, position.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
m_csChunkString);
|
||||
position.y += 20;
|
||||
renderer.drawText(16, position, 0xff40ff40, "CS syncs:");
|
||||
renderer.drawText(16, { position.x + 132, position.y }, 0xffffffffu, m_csSyncString);
|
||||
|
||||
position.y += 20.0f;
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x, position.y },
|
||||
{ 0.25f, 1.0f, 0.25f, 1.0f },
|
||||
"CS syncs:");
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x + 132.0f, position.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
m_csSyncString);
|
||||
|
||||
position.y += 8.0f;
|
||||
position.y += 8;
|
||||
return position;
|
||||
}
|
||||
|
||||
@ -979,21 +1503,16 @@ namespace dxvk::hud {
|
||||
|
||||
|
||||
HudPos HudGpuLoadItem::render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
position.y += 16.0f;
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
position.y += 16;
|
||||
renderer.drawText(16, position, 0xff408040u, "GPU:");
|
||||
renderer.drawText(16, { position.x + 60, position.y }, 0xffffffffu, m_gpuLoadString);
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x, position.y },
|
||||
{ 0.25f, 0.5f, 0.25f, 1.0f },
|
||||
"GPU:");
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x + 60.0f, position.y },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
m_gpuLoadString);
|
||||
|
||||
position.y += 8.0f;
|
||||
position.y += 8;
|
||||
return position;
|
||||
}
|
||||
|
||||
@ -1047,18 +1566,18 @@ namespace dxvk::hud {
|
||||
|
||||
|
||||
HudPos HudCompilerActivityItem::render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position) {
|
||||
if (m_show) {
|
||||
std::string string = "Compiling shaders...";
|
||||
|
||||
if (m_showPercentage)
|
||||
string = str::format(string, " (", computePercentage(), "%)");
|
||||
|
||||
renderer.drawText(16.0f,
|
||||
{ position.x, float(renderer.surfaceSize().height) / renderer.scale() - 20.0f },
|
||||
{ 1.0f, 1.0f, 1.0f, 1.0f },
|
||||
string);
|
||||
renderer.drawText(16, { position.x, -20 }, 0xffffffffu, string);
|
||||
}
|
||||
|
||||
return position;
|
||||
|
@ -33,13 +33,18 @@ namespace dxvk::hud {
|
||||
/**
|
||||
* \brief Renders the HUD
|
||||
*
|
||||
* \param [in] renderer HUD renderer
|
||||
* \param [in] ctx Raw context objects
|
||||
* \param [in] options HUD options
|
||||
* \param [in] renderer HUD renderer for text rendering
|
||||
* \param [in] position Base offset
|
||||
* \returns Base offset for next item
|
||||
*/
|
||||
virtual HudPos render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position) = 0;
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position) = 0;
|
||||
|
||||
};
|
||||
|
||||
@ -70,7 +75,10 @@ namespace dxvk::hud {
|
||||
* \returns Base offset for next item
|
||||
*/
|
||||
void render(
|
||||
HudRenderer& renderer);
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer);
|
||||
|
||||
/**
|
||||
* \brief Creates a HUD item if enabled
|
||||
@ -129,8 +137,11 @@ namespace dxvk::hud {
|
||||
public:
|
||||
|
||||
HudPos render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
|
||||
};
|
||||
|
||||
@ -147,8 +158,11 @@ namespace dxvk::hud {
|
||||
~HudClientApiItem();
|
||||
|
||||
HudPos render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
|
||||
private:
|
||||
|
||||
@ -169,8 +183,11 @@ namespace dxvk::hud {
|
||||
~HudDeviceInfoItem();
|
||||
|
||||
HudPos render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
|
||||
private:
|
||||
|
||||
@ -195,8 +212,11 @@ namespace dxvk::hud {
|
||||
void update(dxvk::high_resolution_clock::time_point time);
|
||||
|
||||
HudPos render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
|
||||
private:
|
||||
|
||||
@ -210,29 +230,136 @@ namespace dxvk::hud {
|
||||
|
||||
|
||||
/**
|
||||
* \brief HUD item to display the frame rate
|
||||
* \brief Trackable GPU query pool
|
||||
*/
|
||||
class HudFrameTimeItem : public HudItem {
|
||||
constexpr static size_t NumDataPoints = 304;
|
||||
class HudFrameTimeQueryPool : public DxvkResource {
|
||||
|
||||
public:
|
||||
|
||||
HudFrameTimeItem();
|
||||
HudFrameTimeQueryPool(
|
||||
const Rc<DxvkDevice>& device);
|
||||
|
||||
~HudFrameTimeItem();
|
||||
~HudFrameTimeQueryPool();
|
||||
|
||||
void update(dxvk::high_resolution_clock::time_point time);
|
||||
|
||||
HudPos render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
VkQueryPool handle() const {
|
||||
return m_pool;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
dxvk::high_resolution_clock::time_point m_lastUpdate
|
||||
= dxvk::high_resolution_clock::now();
|
||||
Rc<vk::DeviceFn> m_vkd;
|
||||
VkQueryPool m_pool = VK_NULL_HANDLE;
|
||||
|
||||
std::array<float, NumDataPoints> m_dataPoints = {};
|
||||
uint32_t m_dataPointId = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief HUD item to display the frame rate
|
||||
*/
|
||||
class HudFrameTimeItem : public HudItem {
|
||||
constexpr static size_t NumDataPoints = 420u;
|
||||
constexpr static size_t NumTextDraws = 2u;
|
||||
public:
|
||||
|
||||
HudFrameTimeItem(
|
||||
const Rc<DxvkDevice>& device,
|
||||
HudRenderer* renderer);
|
||||
|
||||
~HudFrameTimeItem();
|
||||
|
||||
HudPos render(
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
|
||||
private:
|
||||
|
||||
struct ComputePushConstants {
|
||||
float msPerTick;
|
||||
uint32_t dataPoint;
|
||||
int16_t textPosMinX;
|
||||
int16_t textPosMinY;
|
||||
int16_t textPosMaxX;
|
||||
int16_t textPosMaxY;
|
||||
};
|
||||
|
||||
struct RenderPushConstants {
|
||||
HudPushConstants hud;
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
int16_t w;
|
||||
int16_t h;
|
||||
uint32_t frameIndex;
|
||||
};
|
||||
|
||||
struct BufferLayout {
|
||||
size_t timestampSize;
|
||||
size_t drawInfoOffset;
|
||||
size_t drawInfoSize;
|
||||
size_t drawParamOffset;
|
||||
size_t drawParamSize;
|
||||
size_t textOffset;
|
||||
size_t textSize;
|
||||
size_t totalSize;
|
||||
};
|
||||
|
||||
Rc<DxvkDevice> m_device;
|
||||
Rc<DxvkBuffer> m_gpuBuffer;
|
||||
Rc<DxvkBufferView> m_textView;
|
||||
Rc<HudFrameTimeQueryPool> m_queryPool;
|
||||
|
||||
VkDescriptorSetLayout m_computeSetLayout = VK_NULL_HANDLE;
|
||||
VkPipelineLayout m_computePipelineLayout = VK_NULL_HANDLE;
|
||||
VkPipeline m_computePipeline = VK_NULL_HANDLE;
|
||||
|
||||
HudShaderModule m_vs;
|
||||
HudShaderModule m_fs;
|
||||
|
||||
VkDescriptorSetLayout m_gfxSetLayout = VK_NULL_HANDLE;
|
||||
VkPipelineLayout m_gfxPipelineLayout = VK_NULL_HANDLE;
|
||||
|
||||
std::unordered_map<HudPipelineKey,
|
||||
VkPipeline, DxvkHash, DxvkEq> m_gfxPipelines;
|
||||
|
||||
uint32_t m_nextDataPoint = 0u;
|
||||
|
||||
void processFrameTimes(
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
HudRenderer& renderer,
|
||||
uint32_t dataPoint,
|
||||
HudPos minPos,
|
||||
HudPos maxPos);
|
||||
|
||||
void drawFrameTimeGraph(
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
HudRenderer& renderer,
|
||||
uint32_t dataPoint,
|
||||
HudPos graphPos,
|
||||
HudPos graphSize);
|
||||
|
||||
void createResources(
|
||||
const DxvkContextObjects& ctx);
|
||||
|
||||
void createComputePipeline(
|
||||
HudRenderer& renderer);
|
||||
|
||||
VkDescriptorSetLayout createDescriptorSetLayout();
|
||||
|
||||
VkPipelineLayout createPipelineLayout();
|
||||
|
||||
VkPipeline getPipeline(
|
||||
HudRenderer& renderer,
|
||||
const HudPipelineKey& key);
|
||||
|
||||
VkPipeline createPipeline(
|
||||
HudRenderer& renderer,
|
||||
const HudPipelineKey& key);
|
||||
|
||||
static BufferLayout computeBufferLayout();
|
||||
|
||||
};
|
||||
|
||||
@ -251,8 +378,11 @@ namespace dxvk::hud {
|
||||
void update(dxvk::high_resolution_clock::time_point time);
|
||||
|
||||
HudPos render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
|
||||
private:
|
||||
|
||||
@ -289,8 +419,11 @@ namespace dxvk::hud {
|
||||
void update(dxvk::high_resolution_clock::time_point time);
|
||||
|
||||
HudPos render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
|
||||
private:
|
||||
|
||||
@ -323,8 +456,11 @@ namespace dxvk::hud {
|
||||
void update(dxvk::high_resolution_clock::time_point time);
|
||||
|
||||
HudPos render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
|
||||
private:
|
||||
|
||||
@ -351,8 +487,11 @@ namespace dxvk::hud {
|
||||
void update(dxvk::high_resolution_clock::time_point time);
|
||||
|
||||
HudPos render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
|
||||
private:
|
||||
|
||||
@ -378,8 +517,11 @@ namespace dxvk::hud {
|
||||
void update(dxvk::high_resolution_clock::time_point time);
|
||||
|
||||
HudPos render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
|
||||
private:
|
||||
|
||||
@ -397,26 +539,36 @@ namespace dxvk::hud {
|
||||
constexpr static int64_t UpdateInterval = 500'000;
|
||||
public:
|
||||
|
||||
HudMemoryDetailsItem(const Rc<DxvkDevice>& device);
|
||||
HudMemoryDetailsItem(
|
||||
const Rc<DxvkDevice>& device,
|
||||
HudRenderer* renderer);
|
||||
|
||||
~HudMemoryDetailsItem();
|
||||
|
||||
void update(dxvk::high_resolution_clock::time_point time);
|
||||
|
||||
HudPos render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
|
||||
private:
|
||||
|
||||
struct ShaderArgs {
|
||||
HudPos pos;
|
||||
HudPos size;
|
||||
HudPos scale;
|
||||
float opacity;
|
||||
struct DrawInfo {
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
int16_t w;
|
||||
int16_t h;
|
||||
uint16_t pageMask;
|
||||
uint16_t pageCount;
|
||||
uint32_t color;
|
||||
uint32_t maskIndex;
|
||||
uint32_t pageCount;
|
||||
};
|
||||
|
||||
struct PipelinePair {
|
||||
VkPipeline background = VK_NULL_HANDLE;
|
||||
VkPipeline visualize = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
Rc<DxvkDevice> m_device;
|
||||
@ -425,24 +577,50 @@ namespace dxvk::hud {
|
||||
|
||||
high_resolution_clock::time_point m_lastUpdate = { };
|
||||
|
||||
bool m_displayCacheStats = false;
|
||||
bool m_displayCacheStats = false;
|
||||
|
||||
Rc<DxvkShader> m_vs;
|
||||
Rc<DxvkShader> m_fsBackground;
|
||||
Rc<DxvkShader> m_fsVisualize;
|
||||
Rc<DxvkBuffer> m_dataBuffer;
|
||||
std::vector<DrawInfo> m_drawInfos;
|
||||
|
||||
Rc<DxvkBuffer> m_pageMaskBuffer;
|
||||
Rc<DxvkBufferView> m_pageMaskView;
|
||||
HudShaderModule m_vsBackground;
|
||||
HudShaderModule m_fsBackground;
|
||||
|
||||
void uploadChunkData(
|
||||
HudRenderer& renderer);
|
||||
HudShaderModule m_vsVisualize;
|
||||
HudShaderModule m_fsVisualize;
|
||||
|
||||
VkDescriptorSetLayout m_setLayout = VK_NULL_HANDLE;
|
||||
VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE;
|
||||
|
||||
std::unordered_map<HudPipelineKey,
|
||||
PipelinePair, DxvkHash, DxvkEq> m_pipelines;
|
||||
|
||||
void drawChunk(
|
||||
HudRenderer& renderer,
|
||||
HudPos pos,
|
||||
HudPos size,
|
||||
const VkMemoryType& memoryType,
|
||||
const DxvkMemoryChunkStats& stats) const;
|
||||
HudPos pos,
|
||||
HudPos size,
|
||||
uint32_t color,
|
||||
const DxvkMemoryChunkStats& chunk);
|
||||
|
||||
void flushDraws(
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer);
|
||||
|
||||
void updateDataBuffer(
|
||||
VkDescriptorBufferInfo& drawDescriptor,
|
||||
VkDescriptorBufferInfo& dataDescriptor);
|
||||
|
||||
VkDescriptorSetLayout createSetLayout();
|
||||
|
||||
VkPipelineLayout createPipelineLayout();
|
||||
|
||||
PipelinePair createPipeline(
|
||||
HudRenderer& renderer,
|
||||
const HudPipelineKey& key);
|
||||
|
||||
PipelinePair getPipeline(
|
||||
HudRenderer& renderer,
|
||||
const HudPipelineKey& key);
|
||||
|
||||
};
|
||||
|
||||
@ -461,8 +639,11 @@ namespace dxvk::hud {
|
||||
void update(dxvk::high_resolution_clock::time_point time);
|
||||
|
||||
HudPos render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
|
||||
private:
|
||||
|
||||
@ -500,8 +681,11 @@ namespace dxvk::hud {
|
||||
void update(dxvk::high_resolution_clock::time_point time);
|
||||
|
||||
HudPos render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
|
||||
private:
|
||||
|
||||
@ -532,8 +716,11 @@ namespace dxvk::hud {
|
||||
void update(dxvk::high_resolution_clock::time_point time);
|
||||
|
||||
HudPos render(
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const HudOptions& options,
|
||||
HudRenderer& renderer,
|
||||
HudPos position);
|
||||
|
||||
private:
|
||||
|
||||
|
@ -1,364 +1,634 @@
|
||||
#include "dxvk_hud_renderer.h"
|
||||
|
||||
#include <hud_graph_frag.h>
|
||||
#include <hud_graph_vert.h>
|
||||
|
||||
#include <hud_text_frag.h>
|
||||
#include <hud_text_vert.h>
|
||||
|
||||
namespace dxvk::hud {
|
||||
|
||||
HudRenderer::HudRenderer(const Rc<DxvkDevice>& device)
|
||||
: m_mode (Mode::RenderNone),
|
||||
m_scale (1.0f),
|
||||
m_surfaceSize { 0, 0 },
|
||||
m_device (device),
|
||||
m_textShaders (createTextShaders()),
|
||||
m_graphShaders (createGraphShaders()),
|
||||
m_dataBuffer (createDataBuffer()),
|
||||
m_dataView (createDataView()),
|
||||
m_dataOffset (0ull),
|
||||
m_fontBuffer (createFontBuffer()),
|
||||
m_fontBufferView(createFontBufferView()),
|
||||
m_fontImage (createFontImage()),
|
||||
m_fontView (createFontView()),
|
||||
m_fontSampler (createFontSampler()) {
|
||||
struct HudGlyphGpuData {
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
int16_t w;
|
||||
int16_t h;
|
||||
int16_t originX;
|
||||
int16_t originY;
|
||||
};
|
||||
|
||||
|
||||
struct HudFontGpuData {
|
||||
float size;
|
||||
float advance;
|
||||
uint32_t padding[2];
|
||||
HudGlyphGpuData glyphs[256];
|
||||
};
|
||||
|
||||
|
||||
|
||||
static const std::array<VkSpecializationMapEntry, 2> HudSpecConstantMap = {{
|
||||
{ 0, offsetof(HudSpecConstants, dstSpace), sizeof(VkColorSpaceKHR) },
|
||||
{ 1, offsetof(HudSpecConstants, dstIsSrgb), sizeof(VkBool32) },
|
||||
}};
|
||||
|
||||
|
||||
|
||||
HudRenderer::HudRenderer(const Rc<DxvkDevice>& device)
|
||||
: m_device (device),
|
||||
m_textSetLayout (createSetLayout()),
|
||||
m_textPipelineLayout (createPipelineLayout()) {
|
||||
createShaderModule(m_textVs, VK_SHADER_STAGE_VERTEX_BIT, sizeof(hud_text_vert), hud_text_vert);
|
||||
createShaderModule(m_textFs, VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(hud_text_frag), hud_text_frag);
|
||||
}
|
||||
|
||||
|
||||
HudRenderer::~HudRenderer() {
|
||||
|
||||
auto vk = m_device->vkd();
|
||||
|
||||
for (const auto& p : m_textPipelines)
|
||||
vk->vkDestroyPipeline(vk->device(), p.second, nullptr);
|
||||
|
||||
vk->vkDestroyShaderModule(vk->device(), m_textVs.stageInfo.module, nullptr);
|
||||
vk->vkDestroyShaderModule(vk->device(), m_textFs.stageInfo.module, nullptr);
|
||||
|
||||
vk->vkDestroyPipelineLayout(vk->device(), m_textPipelineLayout, nullptr);
|
||||
vk->vkDestroyDescriptorSetLayout(vk->device(), m_textSetLayout, nullptr);
|
||||
}
|
||||
|
||||
|
||||
void HudRenderer::beginFrame(
|
||||
const Rc<DxvkContext>& context,
|
||||
VkExtent2D surfaceSize,
|
||||
float scale,
|
||||
float opacity) {
|
||||
if (!m_initialized)
|
||||
this->initFontTexture(context);
|
||||
const DxvkContextObjects& ctx,
|
||||
const Rc<DxvkImageView>& dstView,
|
||||
VkColorSpaceKHR dstColorSpace,
|
||||
const HudOptions& options) {
|
||||
if (!m_fontTextureView) {
|
||||
createFontResources();
|
||||
uploadFontResources(ctx);
|
||||
}
|
||||
|
||||
m_mode = Mode::RenderNone;
|
||||
m_scale = scale;
|
||||
m_opacity = opacity;
|
||||
m_surfaceSize = surfaceSize;
|
||||
m_context = context;
|
||||
VkExtent3D extent = dstView->mipLevelExtent(0u);
|
||||
|
||||
m_pushConstants.surfaceSize = { extent.width, extent.height };
|
||||
m_pushConstants.opacity = options.opacity;
|
||||
m_pushConstants.scale = options.scale;
|
||||
|
||||
VkViewport viewport = { };
|
||||
viewport.x = 0.0f;
|
||||
viewport.y = 0.0f;
|
||||
viewport.width = float(extent.width);
|
||||
viewport.height = float(extent.height);
|
||||
viewport.minDepth = 0.0f;
|
||||
viewport.maxDepth = 1.0f;
|
||||
|
||||
VkRect2D scissor = { };
|
||||
scissor.offset = { 0u, 0u };
|
||||
scissor.extent = { extent.width, extent.height };
|
||||
|
||||
ctx.cmd->cmdSetViewport(1, &viewport);
|
||||
ctx.cmd->cmdSetScissor(1, &scissor);
|
||||
}
|
||||
|
||||
|
||||
void HudRenderer::drawText(
|
||||
float size,
|
||||
HudPos pos,
|
||||
HudColor color,
|
||||
const std::string& text) {
|
||||
uint32_t size,
|
||||
HudPos pos,
|
||||
uint32_t color,
|
||||
const std::string& text) {
|
||||
if (text.empty())
|
||||
return;
|
||||
|
||||
beginTextRendering();
|
||||
auto& draw = m_textDraws.emplace_back();
|
||||
draw.textOffset = m_textData.size();
|
||||
draw.textLength = text.size();
|
||||
draw.fontSize = size;
|
||||
draw.posX = pos.x;
|
||||
draw.posY = pos.y;
|
||||
draw.color = color;
|
||||
|
||||
// Copy string into string buffer, but extend it to cover a full cache
|
||||
// line to avoid potential CPU performance issues with the upload.
|
||||
std::string textCopy = text;
|
||||
textCopy.resize(align(text.size(), CACHE_LINE_SIZE), ' ');
|
||||
|
||||
VkDeviceSize offset = allocDataBuffer(textCopy.size());
|
||||
std::memcpy(m_dataBuffer->mapPtr(offset), textCopy.data(), textCopy.size());
|
||||
|
||||
// Enforce HUD opacity factor on alpha
|
||||
if (m_opacity != 1.0f)
|
||||
color.a *= m_opacity;
|
||||
|
||||
// Fill in push constants for the next draw
|
||||
HudTextPushConstants pushData;
|
||||
pushData.color = color;
|
||||
pushData.pos = pos;
|
||||
pushData.offset = offset;
|
||||
pushData.size = size;
|
||||
pushData.scale.x = m_scale / std::max(float(m_surfaceSize.width), 1.0f);
|
||||
pushData.scale.y = m_scale / std::max(float(m_surfaceSize.height), 1.0f);
|
||||
|
||||
m_context->pushConstants(0, sizeof(pushData), &pushData);
|
||||
|
||||
// Draw with orignal vertex count
|
||||
m_context->draw(6 * text.size(), 1, 0, 0);
|
||||
m_textData.resize(draw.textOffset + draw.textLength);
|
||||
std::memcpy(&m_textData[draw.textOffset], text.data(), draw.textLength);
|
||||
}
|
||||
|
||||
|
||||
void HudRenderer::drawGraph(
|
||||
HudPos pos,
|
||||
HudPos size,
|
||||
size_t pointCount,
|
||||
const HudGraphPoint* pointData) {
|
||||
beginGraphRendering();
|
||||
|
||||
VkDeviceSize dataSize = pointCount * sizeof(*pointData);
|
||||
VkDeviceSize offset = allocDataBuffer(dataSize);
|
||||
std::memcpy(m_dataBuffer->mapPtr(offset), pointData, dataSize);
|
||||
|
||||
HudGraphPushConstants pushData;
|
||||
pushData.offset = offset / sizeof(*pointData);
|
||||
pushData.count = pointCount;
|
||||
pushData.pos = pos;
|
||||
pushData.size = size;
|
||||
pushData.scale.x = m_scale / std::max(float(m_surfaceSize.width), 1.0f);
|
||||
pushData.scale.y = m_scale / std::max(float(m_surfaceSize.height), 1.0f);
|
||||
pushData.opacity = m_opacity;
|
||||
void HudRenderer::flushDraws(
|
||||
const DxvkContextObjects& ctx,
|
||||
const Rc<DxvkImageView>& dstView,
|
||||
VkColorSpaceKHR dstColorSpace,
|
||||
const HudOptions& options) {
|
||||
if (m_textDraws.empty())
|
||||
return;
|
||||
|
||||
m_context->pushConstants(0, sizeof(pushData), &pushData);
|
||||
m_context->draw(4, 1, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
void HudRenderer::beginTextRendering() {
|
||||
if (m_mode != Mode::RenderText) {
|
||||
m_mode = Mode::RenderText;
|
||||
// Align text size so that we're guaranteed to be able to put draw
|
||||
// parameters where we want, and can also upload data without
|
||||
// running into perf issues due to incomplete cache lines.
|
||||
size_t textSizeAligned = align(m_textData.size(), 256u);
|
||||
m_textData.resize(textSizeAligned);
|
||||
|
||||
m_context->bindShader<VK_SHADER_STAGE_VERTEX_BIT>(Rc<DxvkShader>(m_textShaders.vert));
|
||||
m_context->bindShader<VK_SHADER_STAGE_FRAGMENT_BIT>(Rc<DxvkShader>(m_textShaders.frag));
|
||||
|
||||
m_context->bindResourceBufferView(VK_SHADER_STAGE_VERTEX_BIT, 0, Rc<DxvkBufferView>(m_fontBufferView));
|
||||
m_context->bindResourceBufferView(VK_SHADER_STAGE_VERTEX_BIT, 1, Rc<DxvkBufferView>(m_dataView));
|
||||
m_context->bindResourceSampler(VK_SHADER_STAGE_FRAGMENT_BIT, 2, Rc<DxvkSampler>(m_fontSampler));
|
||||
m_context->bindResourceImageView(VK_SHADER_STAGE_FRAGMENT_BIT, 2, Rc<DxvkImageView>(m_fontView));
|
||||
|
||||
static const DxvkInputAssemblyState iaState = {
|
||||
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
|
||||
VK_FALSE, 0 };
|
||||
|
||||
m_context->setInputAssemblyState(iaState);
|
||||
// We'll use indirect draws and then just use aligned subsections
|
||||
// of the data buffer to write our draw parameters
|
||||
size_t drawInfoSize = align(m_textDraws.size() * sizeof(HudTextDrawInfo), 256u);
|
||||
size_t drawArgsSize = align(m_textDraws.size() * sizeof(VkDrawIndirectCommand), 256u);
|
||||
|
||||
// Align buffer size to something large so we don't recreate it all the time
|
||||
size_t bufferSize = align(textSizeAligned + drawInfoSize + drawArgsSize, 2048u);
|
||||
|
||||
if (!m_textBuffer || m_textBuffer->info().size < bufferSize) {
|
||||
DxvkBufferCreateInfo textBufferInfo = { };
|
||||
textBufferInfo.size = bufferSize;
|
||||
textBufferInfo.usage = VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT
|
||||
| VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
|
||||
| VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
|
||||
textBufferInfo.stages = VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT
|
||||
| VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
|
||||
textBufferInfo.access = VK_ACCESS_SHADER_READ_BIT
|
||||
| VK_ACCESS_INDIRECT_COMMAND_READ_BIT;
|
||||
|
||||
m_textBuffer = m_device->createBuffer(textBufferInfo,
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
|
||||
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
||||
|
||||
DxvkBufferViewKey textViewInfo = { };
|
||||
textViewInfo.format = VK_FORMAT_R8_UINT;
|
||||
textViewInfo.offset = 0u;
|
||||
textViewInfo.size = bufferSize;
|
||||
textViewInfo.usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
|
||||
|
||||
m_textBufferView = m_textBuffer->createView(textViewInfo);
|
||||
} else {
|
||||
// Discard and invalidate buffer so we can safely update it
|
||||
m_textBuffer->assignSlice(Rc<DxvkResourceAllocation>(m_textBuffer->allocateSlice()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void HudRenderer::beginGraphRendering() {
|
||||
if (m_mode != Mode::RenderGraph) {
|
||||
m_mode = Mode::RenderGraph;
|
||||
// Upload aligned text data in such a way that we write full cache lines
|
||||
std::memcpy(m_textBuffer->mapPtr(0), m_textData.data(), textSizeAligned);
|
||||
|
||||
m_context->bindShader<VK_SHADER_STAGE_VERTEX_BIT>(Rc<DxvkShader>(m_graphShaders.vert));
|
||||
m_context->bindShader<VK_SHADER_STAGE_FRAGMENT_BIT>(Rc<DxvkShader>(m_graphShaders.frag));
|
||||
|
||||
m_context->bindResourceBufferView(VK_SHADER_STAGE_FRAGMENT_BIT, 0, Rc<DxvkBufferView>(m_dataView));
|
||||
// Upload draw parameters and pad aligned region with zeroes
|
||||
size_t drawInfoCopySize = m_textDraws.size() * sizeof(HudTextDrawInfo);
|
||||
std::memcpy(m_textBuffer->mapPtr(textSizeAligned), m_textDraws.data(), drawInfoCopySize);
|
||||
std::memset(m_textBuffer->mapPtr(textSizeAligned + drawInfoCopySize), 0, drawInfoSize - drawInfoCopySize);
|
||||
|
||||
static const DxvkInputAssemblyState iaState = {
|
||||
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
|
||||
VK_FALSE, 0 };
|
||||
// Emit indirect draw parameters
|
||||
size_t drawArgWriteSize = m_textDraws.size() * sizeof(VkDrawIndirectCommand);
|
||||
size_t drawArgOffset = textSizeAligned + drawInfoSize;
|
||||
|
||||
m_context->setInputAssemblyState(iaState);
|
||||
auto drawArgs = reinterpret_cast<VkDrawIndirectCommand*>(m_textBuffer->mapPtr(drawArgOffset));
|
||||
|
||||
for (size_t i = 0; i < m_textDraws.size(); i++) {
|
||||
drawArgs[i].vertexCount = 6u * m_textDraws[i].textLength;
|
||||
drawArgs[i].instanceCount = 1u;
|
||||
drawArgs[i].firstVertex = 0u;
|
||||
drawArgs[i].firstInstance = 0u;
|
||||
}
|
||||
|
||||
std::memset(m_textBuffer->mapPtr(drawArgOffset + drawArgWriteSize), 0, drawArgsSize - drawArgWriteSize);
|
||||
|
||||
// Draw the actual text
|
||||
VkDescriptorBufferInfo textBufferDescriptor = m_textBuffer->getDescriptor(textSizeAligned, drawInfoSize).buffer;
|
||||
VkDescriptorBufferInfo drawBufferDescriptor = m_textBuffer->getDescriptor(drawArgOffset, drawArgWriteSize).buffer;
|
||||
|
||||
drawTextIndirect(ctx, getPipelineKey(dstView, dstColorSpace),
|
||||
drawBufferDescriptor, textBufferDescriptor,
|
||||
m_textBufferView->handle(), m_textDraws.size());
|
||||
|
||||
// Ensure all used resources are kept alive
|
||||
ctx.cmd->trackResource<DxvkAccess::None>(m_textBuffer->getAllocation());
|
||||
ctx.cmd->trackResource<DxvkAccess::Read>(m_fontBuffer->getAllocation());
|
||||
ctx.cmd->trackResource<DxvkAccess::Read>(m_fontTexture->getAllocation());
|
||||
ctx.cmd->trackSampler(m_fontSampler);
|
||||
|
||||
// Reset internal text buffers
|
||||
m_textDraws.clear();
|
||||
m_textData.clear();
|
||||
}
|
||||
|
||||
|
||||
VkDeviceSize HudRenderer::allocDataBuffer(VkDeviceSize size) {
|
||||
if (m_dataOffset + size > m_dataBuffer->info().size) {
|
||||
m_context->invalidateBuffer(m_dataBuffer, m_dataBuffer->allocateSlice());
|
||||
m_dataOffset = 0;
|
||||
}
|
||||
|
||||
VkDeviceSize offset = m_dataOffset;
|
||||
m_dataOffset = align(offset + size, 64);
|
||||
return offset;
|
||||
}
|
||||
|
||||
void HudRenderer::drawTextIndirect(
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const VkDescriptorBufferInfo& drawArgs,
|
||||
const VkDescriptorBufferInfo& drawInfos,
|
||||
VkBufferView text,
|
||||
uint32_t drawCount) {
|
||||
// Bind the correct pipeline for the swap chain
|
||||
VkPipeline pipeline = getPipeline(key);
|
||||
|
||||
HudRenderer::ShaderPair HudRenderer::createTextShaders() {
|
||||
ShaderPair result;
|
||||
ctx.cmd->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,
|
||||
VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
|
||||
SpirvCodeBuffer vsCode(hud_text_vert);
|
||||
SpirvCodeBuffer fsCode(hud_text_frag);
|
||||
|
||||
const std::array<DxvkBindingInfo, 2> vsBindings = {{
|
||||
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 0, VK_IMAGE_VIEW_TYPE_MAX_ENUM, VK_SHADER_STAGE_VERTEX_BIT, VK_ACCESS_SHADER_READ_BIT },
|
||||
{ VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_IMAGE_VIEW_TYPE_MAX_ENUM, VK_SHADER_STAGE_VERTEX_BIT, VK_ACCESS_SHADER_READ_BIT },
|
||||
// Bind resources
|
||||
VkDescriptorSet set = ctx.descriptorPool->alloc(m_textSetLayout);
|
||||
|
||||
VkDescriptorBufferInfo fontBufferDescriptor = m_fontBuffer->getDescriptor(0, m_fontBuffer->info().size).buffer;
|
||||
|
||||
VkDescriptorImageInfo fontTextureDescriptor = { };
|
||||
fontTextureDescriptor.sampler = m_fontSampler->handle();
|
||||
fontTextureDescriptor.imageView = m_fontTextureView->handle();
|
||||
fontTextureDescriptor.imageLayout = m_fontTexture->info().layout;
|
||||
|
||||
std::array<VkWriteDescriptorSet, 4> descriptorWrites = {{
|
||||
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
|
||||
set, 0, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &fontBufferDescriptor },
|
||||
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
|
||||
set, 1, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &drawInfos },
|
||||
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
|
||||
set, 2, 0, 1, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, nullptr, nullptr, &text },
|
||||
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
|
||||
set, 3, 0, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &fontTextureDescriptor },
|
||||
}};
|
||||
|
||||
const std::array<DxvkBindingInfo, 1> fsBindings = {{
|
||||
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2, VK_IMAGE_VIEW_TYPE_MAX_ENUM, VK_SHADER_STAGE_FRAGMENT_BIT, VK_ACCESS_SHADER_READ_BIT },
|
||||
}};
|
||||
ctx.cmd->updateDescriptorSets(
|
||||
descriptorWrites.size(),
|
||||
descriptorWrites.data());
|
||||
|
||||
DxvkShaderCreateInfo vsInfo;
|
||||
vsInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
vsInfo.bindingCount = vsBindings.size();
|
||||
vsInfo.bindings = vsBindings.data();
|
||||
vsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
vsInfo.pushConstSize = sizeof(HudTextPushConstants);
|
||||
vsInfo.outputMask = 0x3;
|
||||
result.vert = new DxvkShader(vsInfo, std::move(vsCode));
|
||||
ctx.cmd->cmdBindDescriptorSet(DxvkCmdBuffer::ExecBuffer,
|
||||
VK_PIPELINE_BIND_POINT_GRAPHICS, m_textPipelineLayout,
|
||||
set, 0, nullptr);
|
||||
|
||||
DxvkShaderCreateInfo fsInfo;
|
||||
fsInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
fsInfo.bindingCount = fsBindings.size();
|
||||
fsInfo.bindings = fsBindings.data();
|
||||
fsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
fsInfo.pushConstSize = sizeof(HudTextPushConstants);
|
||||
fsInfo.inputMask = 0x3;
|
||||
fsInfo.outputMask = 0x1;
|
||||
result.frag = new DxvkShader(fsInfo, std::move(fsCode));
|
||||
ctx.cmd->cmdPushConstants(DxvkCmdBuffer::ExecBuffer, m_textPipelineLayout,
|
||||
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
0, sizeof(m_pushConstants), &m_pushConstants);
|
||||
|
||||
// Emit the actual draw call
|
||||
ctx.cmd->cmdDrawIndirect(drawArgs.buffer, drawArgs.offset,
|
||||
drawCount, sizeof(VkDrawIndirectCommand));
|
||||
}
|
||||
|
||||
|
||||
HudPipelineKey HudRenderer::getPipelineKey(
|
||||
const Rc<DxvkImageView>& dstView,
|
||||
VkColorSpaceKHR dstColorSpace) const {
|
||||
HudPipelineKey key;
|
||||
key.format = dstView->info().format;
|
||||
key.colorSpace = dstColorSpace;
|
||||
return key;
|
||||
}
|
||||
|
||||
|
||||
HudSpecConstants HudRenderer::getSpecConstants(
|
||||
const HudPipelineKey& key) const {
|
||||
HudSpecConstants result = { };
|
||||
result.dstSpace = key.colorSpace;
|
||||
result.dstIsSrgb = lookupFormatInfo(key.format)->flags.test(DxvkFormatFlag::ColorSpaceSrgb);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
HudRenderer::ShaderPair HudRenderer::createGraphShaders() {
|
||||
ShaderPair result;
|
||||
|
||||
SpirvCodeBuffer vsCode(hud_graph_vert);
|
||||
SpirvCodeBuffer fsCode(hud_graph_frag);
|
||||
|
||||
const std::array<DxvkBindingInfo, 1> fsBindings = {{
|
||||
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 0, VK_IMAGE_VIEW_TYPE_MAX_ENUM, VK_SHADER_STAGE_FRAGMENT_BIT, VK_ACCESS_SHADER_READ_BIT },
|
||||
}};
|
||||
|
||||
DxvkShaderCreateInfo vsInfo;
|
||||
vsInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
vsInfo.outputMask = 0x1;
|
||||
vsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
vsInfo.pushConstSize = sizeof(HudGraphPushConstants);
|
||||
result.vert = new DxvkShader(vsInfo, std::move(vsCode));
|
||||
|
||||
DxvkShaderCreateInfo fsInfo;
|
||||
fsInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
fsInfo.bindingCount = fsBindings.size();
|
||||
fsInfo.bindings = fsBindings.data();
|
||||
fsInfo.inputMask = 0x1;
|
||||
fsInfo.outputMask = 0x1;
|
||||
fsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
fsInfo.pushConstSize = sizeof(HudGraphPushConstants);
|
||||
result.frag = new DxvkShader(fsInfo, std::move(fsCode));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Rc<DxvkBuffer> HudRenderer::createDataBuffer() {
|
||||
DxvkBufferCreateInfo info;
|
||||
info.size = DataBufferSize;
|
||||
info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
|
||||
| VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
|
||||
info.stages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT
|
||||
| VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
info.access = VK_ACCESS_SHADER_READ_BIT;
|
||||
|
||||
return m_device->createBuffer(info,
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
|
||||
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
||||
HudPushConstants HudRenderer::getPushConstants() const {
|
||||
return m_pushConstants;
|
||||
}
|
||||
|
||||
|
||||
Rc<DxvkBufferView> HudRenderer::createDataView() {
|
||||
DxvkBufferViewKey info;
|
||||
info.format = VK_FORMAT_R8_UINT;
|
||||
info.offset = 0;
|
||||
info.size = m_dataBuffer->info().size;
|
||||
info.usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
|
||||
|
||||
return m_dataBuffer->createView(info);
|
||||
VkSpecializationInfo HudRenderer::getSpecInfo(
|
||||
const HudSpecConstants* constants) const {
|
||||
VkSpecializationInfo specInfo = { };
|
||||
specInfo.mapEntryCount = HudSpecConstantMap.size();
|
||||
specInfo.pMapEntries = HudSpecConstantMap.data();
|
||||
specInfo.dataSize = sizeof(*constants);
|
||||
specInfo.pData = constants;
|
||||
return specInfo;
|
||||
}
|
||||
|
||||
|
||||
Rc<DxvkBuffer> HudRenderer::createFontBuffer() {
|
||||
DxvkBufferCreateInfo info;
|
||||
info.size = sizeof(HudFontGpuData);
|
||||
info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
|
||||
| VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
||||
info.stages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT
|
||||
| VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
info.access = VK_ACCESS_SHADER_READ_BIT
|
||||
| VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
|
||||
return m_device->createBuffer(info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
void HudRenderer::createShaderModule(
|
||||
HudShaderModule& shader,
|
||||
VkShaderStageFlagBits stage,
|
||||
size_t size,
|
||||
const uint32_t* code) const {
|
||||
shader.moduleInfo.codeSize = size;
|
||||
shader.moduleInfo.pCode = code;
|
||||
|
||||
shader.stageInfo.stage = stage;
|
||||
shader.stageInfo.pName = "main";
|
||||
|
||||
if (m_device->features().khrMaintenance5.maintenance5
|
||||
|| m_device->features().extGraphicsPipelineLibrary.graphicsPipelineLibrary) {
|
||||
shader.stageInfo.pNext = &shader.moduleInfo;
|
||||
return;
|
||||
}
|
||||
|
||||
auto vk = m_device->vkd();
|
||||
|
||||
VkResult vr = vk->vkCreateShaderModule(vk->device(),
|
||||
&shader.moduleInfo, nullptr, &shader.stageInfo.module);
|
||||
|
||||
if (vr != VK_SUCCESS)
|
||||
throw DxvkError(str::format("Failed to create swap chain blit shader module: ", vr));
|
||||
}
|
||||
|
||||
|
||||
Rc<DxvkBufferView> HudRenderer::createFontBufferView() {
|
||||
DxvkBufferViewKey info;
|
||||
info.format = VK_FORMAT_UNDEFINED;
|
||||
info.offset = 0;
|
||||
info.size = m_fontBuffer->info().size;
|
||||
info.usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
|
||||
void HudRenderer::createFontResources() {
|
||||
DxvkBufferCreateInfo fontBufferInfo;
|
||||
fontBufferInfo.size = sizeof(HudFontGpuData);
|
||||
fontBufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT
|
||||
| VK_BUFFER_USAGE_TRANSFER_SRC_BIT
|
||||
| VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
||||
fontBufferInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT
|
||||
| VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
|
||||
fontBufferInfo.access = VK_ACCESS_TRANSFER_WRITE_BIT
|
||||
| VK_ACCESS_TRANSFER_READ_BIT
|
||||
| VK_ACCESS_SHADER_READ_BIT;
|
||||
|
||||
return m_fontBuffer->createView(info);
|
||||
}
|
||||
m_fontBuffer = m_device->createBuffer(fontBufferInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
|
||||
DxvkImageCreateInfo fontTextureInfo;
|
||||
fontTextureInfo.type = VK_IMAGE_TYPE_2D;
|
||||
fontTextureInfo.format = VK_FORMAT_R8_UNORM;
|
||||
fontTextureInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT;
|
||||
fontTextureInfo.extent = { g_hudFont.width, g_hudFont.height, 1u };
|
||||
fontTextureInfo.numLayers = 1u;
|
||||
fontTextureInfo.mipLevels = 1u;
|
||||
fontTextureInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT
|
||||
| VK_IMAGE_USAGE_TRANSFER_SRC_BIT
|
||||
| VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
fontTextureInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT
|
||||
| VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
fontTextureInfo.access = VK_ACCESS_TRANSFER_WRITE_BIT
|
||||
| VK_ACCESS_TRANSFER_READ_BIT
|
||||
| VK_ACCESS_SHADER_READ_BIT;
|
||||
fontTextureInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
fontTextureInfo.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
fontTextureInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
||||
Rc<DxvkImage> HudRenderer::createFontImage() {
|
||||
DxvkImageCreateInfo info;
|
||||
info.type = VK_IMAGE_TYPE_2D;
|
||||
info.format = VK_FORMAT_R8_UNORM;
|
||||
info.flags = 0;
|
||||
info.sampleCount = VK_SAMPLE_COUNT_1_BIT;
|
||||
info.extent = { g_hudFont.width, g_hudFont.height, 1 };
|
||||
info.numLayers = 1;
|
||||
info.mipLevels = 1;
|
||||
info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT
|
||||
| VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
info.stages = VK_PIPELINE_STAGE_TRANSFER_BIT
|
||||
| VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
info.access = VK_ACCESS_TRANSFER_WRITE_BIT
|
||||
| VK_ACCESS_SHADER_READ_BIT;
|
||||
info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
info.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
|
||||
return m_device->createImage(info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
}
|
||||
|
||||
|
||||
Rc<DxvkImageView> HudRenderer::createFontView() {
|
||||
DxvkImageViewKey info;
|
||||
info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
info.format = m_fontImage->info().format;
|
||||
info.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
info.aspects = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
info.mipIndex = 0;
|
||||
info.mipCount = 1;
|
||||
info.layerIndex = 0;
|
||||
info.layerCount = 1;
|
||||
|
||||
return m_fontImage->createView(info);
|
||||
}
|
||||
|
||||
|
||||
Rc<DxvkSampler> HudRenderer::createFontSampler() {
|
||||
DxvkSamplerKey info = { };
|
||||
info.setFilter(VK_FILTER_LINEAR,
|
||||
m_fontTexture = m_device->createImage(fontTextureInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
|
||||
DxvkImageViewKey fontTextureViewInfo;
|
||||
fontTextureViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
fontTextureViewInfo.format = VK_FORMAT_R8_UNORM;
|
||||
fontTextureViewInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
fontTextureViewInfo.aspects = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
fontTextureViewInfo.mipIndex = 0u;
|
||||
fontTextureViewInfo.mipCount = 1u;
|
||||
fontTextureViewInfo.layerIndex = 0u;
|
||||
fontTextureViewInfo.layerCount = 1u;
|
||||
|
||||
m_fontTextureView = m_fontTexture->createView(fontTextureViewInfo);
|
||||
|
||||
DxvkSamplerKey samplerInfo;
|
||||
samplerInfo.setFilter(VK_FILTER_LINEAR,
|
||||
VK_FILTER_LINEAR, VK_SAMPLER_MIPMAP_MODE_NEAREST);
|
||||
info.setAddressModes(
|
||||
samplerInfo.setAddressModes(
|
||||
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
|
||||
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
|
||||
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE);
|
||||
info.setUsePixelCoordinates(true);
|
||||
|
||||
return m_device->createSampler(info);
|
||||
samplerInfo.setUsePixelCoordinates(true);
|
||||
|
||||
m_fontSampler = m_device->createSampler(samplerInfo);
|
||||
}
|
||||
|
||||
|
||||
void HudRenderer::initFontTexture(
|
||||
const Rc<DxvkContext>& context) {
|
||||
HudFontGpuData gpuData = { };
|
||||
gpuData.size = float(g_hudFont.size);
|
||||
gpuData.advance = float(g_hudFont.advance);
|
||||
|
||||
for (uint32_t i = 0; i < g_hudFont.charCount; i++) {
|
||||
auto src = &g_hudFont.glyphs[i];
|
||||
auto dst = &gpuData.glyphs[src->codePoint];
|
||||
|
||||
dst->x = src->x;
|
||||
dst->y = src->y;
|
||||
dst->w = src->w;
|
||||
dst->h = src->h;
|
||||
dst->originX = src->originX;
|
||||
dst->originY = src->originY;
|
||||
void HudRenderer::uploadFontResources(
|
||||
const DxvkContextObjects& ctx) {
|
||||
size_t bufferDataSize = sizeof(HudFontGpuData);
|
||||
size_t textureDataSize = g_hudFont.width * g_hudFont.height;
|
||||
|
||||
DxvkBufferCreateInfo bufferInfo;
|
||||
bufferInfo.size = bufferDataSize + textureDataSize;
|
||||
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
||||
bufferInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
bufferInfo.access = VK_ACCESS_TRANSFER_READ_BIT;
|
||||
|
||||
auto uploadBuffer = m_device->createBuffer(bufferInfo,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
||||
|
||||
HudFontGpuData glyphData = { };
|
||||
glyphData.size = float(g_hudFont.size);
|
||||
glyphData.advance = float(g_hudFont.advance);
|
||||
|
||||
for (size_t i = 0; i < g_hudFont.charCount; i++) {
|
||||
auto& src = g_hudFont.glyphs[i];
|
||||
auto& dst = glyphData.glyphs[src.codePoint];
|
||||
|
||||
dst.x = src.x;
|
||||
dst.y = src.y;
|
||||
dst.w = src.w;
|
||||
dst.h = src.h;
|
||||
dst.originX = src.originX;
|
||||
dst.originY = src.originY;
|
||||
}
|
||||
|
||||
context->uploadBuffer(m_fontBuffer, &gpuData);
|
||||
std::memcpy(uploadBuffer->mapPtr(0), &glyphData, bufferDataSize);
|
||||
std::memcpy(uploadBuffer->mapPtr(bufferDataSize), g_hudFont.texture, textureDataSize);
|
||||
|
||||
context->uploadImage(m_fontImage,
|
||||
VkImageSubresourceLayers { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 },
|
||||
g_hudFont.texture, g_hudFont.width, g_hudFont.width * g_hudFont.height);
|
||||
|
||||
m_initialized = true;
|
||||
auto uploadSlice = uploadBuffer->getSliceHandle();
|
||||
auto fontSlice = m_fontBuffer->getSliceHandle();
|
||||
|
||||
VkImageMemoryBarrier2 imageBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 };
|
||||
imageBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
imageBarrier.dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
imageBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
imageBarrier.newLayout = m_fontTexture->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
imageBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
imageBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
imageBarrier.image = m_fontTexture->handle();
|
||||
imageBarrier.subresourceRange = m_fontTexture->getAvailableSubresources();
|
||||
|
||||
VkDependencyInfo depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };
|
||||
depInfo.imageMemoryBarrierCount = 1u;
|
||||
depInfo.pImageMemoryBarriers = &imageBarrier;
|
||||
|
||||
ctx.cmd->cmdPipelineBarrier(DxvkCmdBuffer::InitBuffer, &depInfo);
|
||||
|
||||
VkBufferCopy2 bufferRegion = { VK_STRUCTURE_TYPE_BUFFER_COPY_2 };
|
||||
bufferRegion.srcOffset = uploadSlice.offset;
|
||||
bufferRegion.dstOffset = fontSlice.offset;
|
||||
bufferRegion.size = bufferDataSize;
|
||||
|
||||
VkCopyBufferInfo2 bufferCopy = { VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2 };
|
||||
bufferCopy.srcBuffer = uploadSlice.handle;
|
||||
bufferCopy.dstBuffer = fontSlice.handle;
|
||||
bufferCopy.regionCount = 1;
|
||||
bufferCopy.pRegions = &bufferRegion;
|
||||
|
||||
ctx.cmd->cmdCopyBuffer(DxvkCmdBuffer::InitBuffer, &bufferCopy);
|
||||
|
||||
VkBufferImageCopy2 imageRegion = { VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2 };
|
||||
imageRegion.bufferOffset = uploadSlice.offset + bufferDataSize;
|
||||
imageRegion.imageExtent = m_fontTexture->info().extent;
|
||||
imageRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
imageRegion.imageSubresource.layerCount = 1u;
|
||||
|
||||
VkCopyBufferToImageInfo2 imageCopy = { VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2 };
|
||||
imageCopy.srcBuffer = uploadSlice.handle;
|
||||
imageCopy.dstImage = m_fontTexture->handle();
|
||||
imageCopy.dstImageLayout = m_fontTexture->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
imageCopy.regionCount = 1;
|
||||
imageCopy.pRegions = &imageRegion;
|
||||
|
||||
ctx.cmd->cmdCopyBufferToImage(DxvkCmdBuffer::InitBuffer, &imageCopy);
|
||||
|
||||
VkMemoryBarrier2 memoryBarrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 };
|
||||
memoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
memoryBarrier.srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
memoryBarrier.dstAccessMask = m_fontBuffer->info().access;
|
||||
memoryBarrier.dstStageMask = m_fontBuffer->info().stages;
|
||||
|
||||
imageBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 };
|
||||
imageBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
imageBarrier.srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
imageBarrier.dstAccessMask = m_fontTexture->info().access;
|
||||
imageBarrier.dstStageMask = m_fontTexture->info().stages;
|
||||
imageBarrier.oldLayout = m_fontTexture->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
imageBarrier.newLayout = m_fontTexture->info().layout;
|
||||
imageBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
imageBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
imageBarrier.image = m_fontTexture->handle();
|
||||
imageBarrier.subresourceRange = m_fontTexture->getAvailableSubresources();
|
||||
|
||||
depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };
|
||||
depInfo.memoryBarrierCount = 1u;
|
||||
depInfo.pMemoryBarriers = &memoryBarrier;
|
||||
depInfo.imageMemoryBarrierCount = 1u;
|
||||
depInfo.pImageMemoryBarriers = &imageBarrier;
|
||||
|
||||
ctx.cmd->cmdPipelineBarrier(DxvkCmdBuffer::InitBuffer, &depInfo);
|
||||
|
||||
ctx.cmd->trackResource<DxvkAccess::Read>(uploadBuffer->getAllocation());
|
||||
ctx.cmd->trackResource<DxvkAccess::Write>(m_fontBuffer->getAllocation());
|
||||
ctx.cmd->trackResource<DxvkAccess::Write>(m_fontTexture->getAllocation());
|
||||
}
|
||||
|
||||
|
||||
|
||||
VkDescriptorSetLayout HudRenderer::createSetLayout() {
|
||||
auto vk = m_device->vkd();
|
||||
|
||||
static const std::array<VkDescriptorSetLayoutBinding, 4> bindings = {{
|
||||
{ 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT },
|
||||
{ 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT },
|
||||
{ 2, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT },
|
||||
{ 3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT },
|
||||
}};
|
||||
|
||||
VkDescriptorSetLayoutCreateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
|
||||
info.bindingCount = bindings.size();
|
||||
info.pBindings = bindings.data();
|
||||
|
||||
VkDescriptorSetLayout layout = VK_NULL_HANDLE;
|
||||
VkResult vr = vk->vkCreateDescriptorSetLayout(vk->device(), &info, nullptr, &layout);
|
||||
|
||||
if (vr != VK_SUCCESS)
|
||||
throw DxvkError(str::format("Failed to create HUD descriptor set layout: ", vr));
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
|
||||
VkPipelineLayout HudRenderer::createPipelineLayout() {
|
||||
auto vk = m_device->vkd();
|
||||
|
||||
VkPushConstantRange pushConstantRange = { };
|
||||
pushConstantRange.offset = 0u;
|
||||
pushConstantRange.size = sizeof(HudPushConstants);
|
||||
pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
|
||||
VkPipelineLayoutCreateInfo info = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
|
||||
info.setLayoutCount = 1;
|
||||
info.pSetLayouts = &m_textSetLayout;
|
||||
info.pushConstantRangeCount = 1;
|
||||
info.pPushConstantRanges = &pushConstantRange;
|
||||
|
||||
VkPipelineLayout layout = VK_NULL_HANDLE;
|
||||
VkResult vr = vk->vkCreatePipelineLayout(vk->device(), &info, nullptr, &layout);
|
||||
|
||||
if (vr != VK_SUCCESS)
|
||||
throw DxvkError(str::format("Failed to create HUD descriptor set layout: ", vr));
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
|
||||
VkPipeline HudRenderer::createPipeline(
|
||||
const HudPipelineKey& key) {
|
||||
auto vk = m_device->vkd();
|
||||
|
||||
HudSpecConstants specConstants = getSpecConstants(key);
|
||||
VkSpecializationInfo specInfo = getSpecInfo(&specConstants);
|
||||
|
||||
std::array<VkPipelineShaderStageCreateInfo, 2> stages = { };
|
||||
stages[0] = m_textVs.stageInfo;
|
||||
stages[1] = m_textFs.stageInfo;
|
||||
stages[1].pSpecializationInfo = &specInfo;
|
||||
|
||||
VkPipelineRenderingCreateInfo rtInfo = { VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO };
|
||||
rtInfo.colorAttachmentCount = 1;
|
||||
rtInfo.pColorAttachmentFormats = &key.format;
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo viState = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo iaState = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
|
||||
iaState.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||
|
||||
VkPipelineViewportStateCreateInfo vpState = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };
|
||||
|
||||
VkPipelineRasterizationStateCreateInfo rsState = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
|
||||
rsState.cullMode = VK_CULL_MODE_NONE;
|
||||
rsState.polygonMode = VK_POLYGON_MODE_FILL;
|
||||
rsState.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
|
||||
rsState.lineWidth = 1.0f;
|
||||
|
||||
constexpr uint32_t sampleMask = 0x1;
|
||||
|
||||
VkPipelineMultisampleStateCreateInfo msState = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
|
||||
msState.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
msState.pSampleMask = &sampleMask;
|
||||
|
||||
VkPipelineColorBlendAttachmentState cbAttachment = { };
|
||||
cbAttachment.blendEnable = VK_TRUE;
|
||||
cbAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
cbAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
cbAttachment.colorBlendOp = VK_BLEND_OP_ADD;
|
||||
cbAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
cbAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
cbAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
|
||||
cbAttachment.colorWriteMask =
|
||||
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
|
||||
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
|
||||
|
||||
VkPipelineColorBlendStateCreateInfo cbOpaqueState = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO };
|
||||
cbOpaqueState.attachmentCount = 1;
|
||||
cbOpaqueState.pAttachments = &cbAttachment;
|
||||
|
||||
static const std::array<VkDynamicState, 2> dynStates = {
|
||||
VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT,
|
||||
VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT,
|
||||
};
|
||||
|
||||
VkPipelineDynamicStateCreateInfo dynState = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
|
||||
dynState.dynamicStateCount = dynStates.size();
|
||||
dynState.pDynamicStates = dynStates.data();
|
||||
|
||||
VkGraphicsPipelineCreateInfo blitInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, &rtInfo };
|
||||
blitInfo.stageCount = stages.size();
|
||||
blitInfo.pStages = stages.data();
|
||||
blitInfo.pVertexInputState = &viState;
|
||||
blitInfo.pInputAssemblyState = &iaState;
|
||||
blitInfo.pViewportState = &vpState;
|
||||
blitInfo.pRasterizationState = &rsState;
|
||||
blitInfo.pMultisampleState = &msState;
|
||||
blitInfo.pColorBlendState = &cbOpaqueState;
|
||||
blitInfo.pDynamicState = &dynState;
|
||||
blitInfo.layout = m_textPipelineLayout;
|
||||
blitInfo.basePipelineIndex = -1;
|
||||
|
||||
VkPipeline pipeline = VK_NULL_HANDLE;
|
||||
VkResult vr = vk->vkCreateGraphicsPipelines(vk->device(), VK_NULL_HANDLE,
|
||||
1, &blitInfo, nullptr, &pipeline);
|
||||
|
||||
if (vr != VK_SUCCESS)
|
||||
throw DxvkError(str::format("Failed to create swap chain blit pipeline: ", vr));
|
||||
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
|
||||
VkPipeline HudRenderer::getPipeline(
|
||||
const HudPipelineKey& key) {
|
||||
auto entry = m_textPipelines.find(key);
|
||||
|
||||
if (entry != m_textPipelines.end())
|
||||
return entry->second;
|
||||
|
||||
VkPipeline pipeline = createPipeline(key);
|
||||
m_textPipelines.insert({ key, pipeline });
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -5,7 +5,16 @@
|
||||
#include "dxvk_hud_font.h"
|
||||
|
||||
namespace dxvk::hud {
|
||||
|
||||
|
||||
/**
|
||||
* \brief HUD options
|
||||
*/
|
||||
struct HudOptions {
|
||||
float scale = 1.0f;
|
||||
float opacity = 1.0f;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief HUD coordinates
|
||||
*
|
||||
@ -13,82 +22,69 @@ namespace dxvk::hud {
|
||||
* corner of the swap image, in pixels.
|
||||
*/
|
||||
struct HudPos {
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Color
|
||||
*
|
||||
* SRGB color with alpha channel. The text
|
||||
* will use this color for the most part.
|
||||
*/
|
||||
struct HudColor {
|
||||
float r;
|
||||
float g;
|
||||
float b;
|
||||
float a;
|
||||
int32_t x = 0;
|
||||
int32_t y = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Normalized color
|
||||
*
|
||||
* SRGB color with alpha channel.
|
||||
*/
|
||||
struct HudNormColor {
|
||||
uint8_t a;
|
||||
uint8_t b;
|
||||
uint8_t g;
|
||||
uint8_t r;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Graph point with color
|
||||
*/
|
||||
struct HudGraphPoint {
|
||||
float value;
|
||||
HudNormColor color;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief HUD push constant data
|
||||
* \brief Draw parameters for text
|
||||
*/
|
||||
struct HudTextPushConstants {
|
||||
HudColor color;
|
||||
HudPos pos;
|
||||
uint32_t offset;
|
||||
float size;
|
||||
HudPos scale;
|
||||
struct HudTextDrawInfo {
|
||||
uint32_t textOffset = 0u;
|
||||
uint16_t textLength = 0u;
|
||||
uint16_t fontSize = 0u;
|
||||
int16_t posX = 0;
|
||||
int16_t posY = 0;
|
||||
uint32_t color = 0u;
|
||||
};
|
||||
|
||||
struct HudGraphPushConstants {
|
||||
uint32_t offset;
|
||||
uint32_t count;
|
||||
HudPos pos;
|
||||
HudPos size;
|
||||
HudPos scale;
|
||||
float opacity;
|
||||
|
||||
struct HudPushConstants {
|
||||
VkExtent2D surfaceSize;
|
||||
float opacity;
|
||||
float scale;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Glyph data
|
||||
* \brief Pipeline key
|
||||
*/
|
||||
struct HudGlyphGpuData {
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
int16_t w;
|
||||
int16_t h;
|
||||
int16_t originX;
|
||||
int16_t originY;
|
||||
struct HudPipelineKey {
|
||||
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||
VkColorSpaceKHR colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
||||
|
||||
size_t hash() const {
|
||||
DxvkHashState hash;
|
||||
hash.add(uint32_t(format));
|
||||
hash.add(uint32_t(colorSpace));
|
||||
return hash;
|
||||
}
|
||||
|
||||
bool eq(const HudPipelineKey& other) const {
|
||||
return format == other.format && colorSpace == other.colorSpace;
|
||||
}
|
||||
};
|
||||
|
||||
struct HudFontGpuData {
|
||||
float size;
|
||||
float advance;
|
||||
uint32_t padding[2];
|
||||
HudGlyphGpuData glyphs[256];
|
||||
|
||||
/**
|
||||
* \brief Specialization constants
|
||||
*/
|
||||
struct HudSpecConstants {
|
||||
VkColorSpaceKHR dstSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
||||
VkBool32 dstIsSrgb = VK_FALSE;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Shader module info
|
||||
*/
|
||||
struct HudShaderModule {
|
||||
VkShaderModuleCreateInfo moduleInfo = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
|
||||
VkPipelineShaderStageCreateInfo stageInfo = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO };
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Text renderer for the HUD
|
||||
*
|
||||
@ -103,99 +99,92 @@ namespace dxvk::hud {
|
||||
const Rc<DxvkDevice>& device);
|
||||
|
||||
~HudRenderer();
|
||||
|
||||
|
||||
void beginFrame(
|
||||
const Rc<DxvkContext>& context,
|
||||
VkExtent2D surfaceSize,
|
||||
float scale,
|
||||
float opacity);
|
||||
|
||||
const DxvkContextObjects& ctx,
|
||||
const Rc<DxvkImageView>& dstView,
|
||||
VkColorSpaceKHR dstColorSpace,
|
||||
const HudOptions& options);
|
||||
|
||||
void drawText(
|
||||
float size,
|
||||
HudPos pos,
|
||||
HudColor color,
|
||||
const std::string& text);
|
||||
|
||||
void drawGraph(
|
||||
HudPos pos,
|
||||
HudPos size,
|
||||
size_t pointCount,
|
||||
const HudGraphPoint* pointData);
|
||||
uint32_t size,
|
||||
HudPos pos,
|
||||
uint32_t color,
|
||||
const std::string& text);
|
||||
|
||||
DxvkContext* getContext() {
|
||||
m_mode = Mode::RenderNone;
|
||||
return m_context.ptr();
|
||||
}
|
||||
void drawTextIndirect(
|
||||
const DxvkContextObjects& ctx,
|
||||
const HudPipelineKey& key,
|
||||
const VkDescriptorBufferInfo& drawArgs,
|
||||
const VkDescriptorBufferInfo& drawInfos,
|
||||
VkBufferView text,
|
||||
uint32_t drawCount);
|
||||
|
||||
VkExtent2D surfaceSize() const {
|
||||
return m_surfaceSize;
|
||||
}
|
||||
void flushDraws(
|
||||
const DxvkContextObjects& ctx,
|
||||
const Rc<DxvkImageView>& dstView,
|
||||
VkColorSpaceKHR dstColorSpace,
|
||||
const HudOptions& options);
|
||||
|
||||
float scale() const {
|
||||
return m_scale;
|
||||
}
|
||||
HudPipelineKey getPipelineKey(
|
||||
const Rc<DxvkImageView>& dstView,
|
||||
VkColorSpaceKHR dstColorSpace) const;
|
||||
|
||||
HudSpecConstants getSpecConstants(
|
||||
const HudPipelineKey& key) const;
|
||||
|
||||
HudPushConstants getPushConstants() const;
|
||||
|
||||
VkSpecializationInfo getSpecInfo(
|
||||
const HudSpecConstants* constants) const;
|
||||
|
||||
void createShaderModule(
|
||||
HudShaderModule& shader,
|
||||
VkShaderStageFlagBits stage,
|
||||
size_t size,
|
||||
const uint32_t* code) const;
|
||||
|
||||
float opacity() const {
|
||||
return m_opacity;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
enum class Mode {
|
||||
RenderNone,
|
||||
RenderText,
|
||||
RenderGraph,
|
||||
};
|
||||
|
||||
struct ShaderPair {
|
||||
Rc<DxvkShader> vert;
|
||||
Rc<DxvkShader> frag;
|
||||
};
|
||||
|
||||
Mode m_mode;
|
||||
float m_scale;
|
||||
float m_opacity;
|
||||
VkExtent2D m_surfaceSize;
|
||||
Rc<DxvkDevice> m_device;
|
||||
|
||||
Rc<DxvkDevice> m_device;
|
||||
Rc<DxvkContext> m_context;
|
||||
|
||||
ShaderPair m_textShaders;
|
||||
ShaderPair m_graphShaders;
|
||||
|
||||
Rc<DxvkBuffer> m_dataBuffer;
|
||||
Rc<DxvkBufferView> m_dataView;
|
||||
VkDeviceSize m_dataOffset;
|
||||
Rc<DxvkBuffer> m_fontBuffer;
|
||||
Rc<DxvkImage> m_fontTexture;
|
||||
Rc<DxvkImageView> m_fontTextureView;
|
||||
Rc<DxvkSampler> m_fontSampler;
|
||||
|
||||
Rc<DxvkBuffer> m_fontBuffer;
|
||||
Rc<DxvkBufferView> m_fontBufferView;
|
||||
Rc<DxvkImage> m_fontImage;
|
||||
Rc<DxvkImageView> m_fontView;
|
||||
Rc<DxvkSampler> m_fontSampler;
|
||||
Rc<DxvkBuffer> m_textBuffer;
|
||||
Rc<DxvkBufferView> m_textBufferView;
|
||||
|
||||
bool m_initialized = false;
|
||||
std::vector<HudTextDrawInfo> m_textDraws;
|
||||
std::vector<char> m_textData;
|
||||
|
||||
void beginTextRendering();
|
||||
|
||||
void beginGraphRendering();
|
||||
HudShaderModule m_textVs;
|
||||
HudShaderModule m_textFs;
|
||||
|
||||
VkDeviceSize allocDataBuffer(VkDeviceSize size);
|
||||
VkDescriptorSetLayout m_textSetLayout = VK_NULL_HANDLE;
|
||||
VkPipelineLayout m_textPipelineLayout = VK_NULL_HANDLE;
|
||||
|
||||
ShaderPair createTextShaders();
|
||||
ShaderPair createGraphShaders();
|
||||
HudPushConstants m_pushConstants = { };
|
||||
|
||||
Rc<DxvkBuffer> createDataBuffer();
|
||||
Rc<DxvkBufferView> createDataView();
|
||||
std::unordered_map<HudPipelineKey,
|
||||
VkPipeline, DxvkHash, DxvkEq> m_textPipelines;
|
||||
|
||||
void createFontResources();
|
||||
|
||||
void uploadFontResources(
|
||||
const DxvkContextObjects& ctx);
|
||||
|
||||
VkDescriptorSetLayout createSetLayout();
|
||||
|
||||
VkPipelineLayout createPipelineLayout();
|
||||
|
||||
VkPipeline createPipeline(
|
||||
const HudPipelineKey& key);
|
||||
|
||||
VkPipeline getPipeline(
|
||||
const HudPipelineKey& key);
|
||||
|
||||
Rc<DxvkBuffer> createFontBuffer();
|
||||
Rc<DxvkBufferView> createFontBufferView();
|
||||
Rc<DxvkImage> createFontImage();
|
||||
Rc<DxvkImageView> createFontView();
|
||||
Rc<DxvkSampler> createFontSampler();
|
||||
|
||||
void initFontTexture(
|
||||
const Rc<DxvkContext>& context);
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -4,20 +4,17 @@
|
||||
|
||||
#include "hud_frag_common.glsl"
|
||||
|
||||
layout(location = 0) out vec4 o_color;
|
||||
|
||||
layout(push_constant)
|
||||
uniform push_data_t {
|
||||
vec2 pos;
|
||||
vec2 size;
|
||||
vec2 scale;
|
||||
uvec2 surface_size;
|
||||
float opacity;
|
||||
uint color;
|
||||
uint maskIndex;
|
||||
uint pageCount;
|
||||
float scale;
|
||||
};
|
||||
|
||||
layout(location = 0) out vec4 o_color;
|
||||
|
||||
void main() {
|
||||
vec4 rgba = unpackUnorm4x8(color);
|
||||
o_color = vec4(encodeOutput(rgba.rgb), rgba.a * opacity);
|
||||
o_color = vec4(0.0f, 0.0f, 0.0f, 0.75f);
|
||||
o_color.a *= opacity;
|
||||
o_color = linear_to_output(o_color);
|
||||
}
|
||||
|
@ -5,32 +5,31 @@
|
||||
#include "hud_frag_common.glsl"
|
||||
|
||||
layout(location = 0) in vec2 v_coord;
|
||||
|
||||
layout(location = 1, component = 0) flat in uint v_color;
|
||||
layout(location = 1, component = 1) flat in uint v_mask_index;
|
||||
layout(location = 1, component = 2) flat in uint v_page_count;
|
||||
|
||||
layout(location = 0) out vec4 o_color;
|
||||
|
||||
layout(binding = 0, std430)
|
||||
layout(binding = 1, std430)
|
||||
readonly buffer mask_data_t {
|
||||
uint masks[];
|
||||
};
|
||||
|
||||
layout(push_constant)
|
||||
uniform push_data_t {
|
||||
vec2 pos;
|
||||
vec2 size;
|
||||
vec2 scale;
|
||||
uvec2 surface_size;
|
||||
float opacity;
|
||||
uint color;
|
||||
uint maskIndex;
|
||||
uint pageCount;
|
||||
float scale;
|
||||
};
|
||||
|
||||
void main() {
|
||||
vec4 rgba = unpackUnorm4x8(color);
|
||||
|
||||
float dx = dFdx(v_coord.x * float(pageCount)) / 2.0f - 0.5f;
|
||||
float dx = dFdx(v_coord.x * float(v_page_count)) / 2.0f - 0.5f;
|
||||
|
||||
uvec2 pageRange = uvec2(clamp(
|
||||
(v_coord.xx * float(pageCount)) + vec2(-dx, dx),
|
||||
vec2(0.0), vec2(float(pageCount - 1u))));
|
||||
(v_coord.xx * float(v_page_count)) + vec2(-dx, dx),
|
||||
vec2(0.0), vec2(float(v_page_count - 1u))));
|
||||
|
||||
uint bitsTotal = max(pageRange.y - pageRange.x, 1u);
|
||||
uint bitsSet = 0u;
|
||||
@ -40,26 +39,28 @@ void main() {
|
||||
|
||||
if (shift + bitsTotal <= 32u) {
|
||||
bitsSet = bitCount(bitfieldExtract(
|
||||
masks[maskIndex + index], int(shift), int(bitsTotal)));
|
||||
masks[v_mask_index + index], int(shift), int(bitsTotal)));
|
||||
} else {
|
||||
bitsSet = bitCount(masks[maskIndex + (index++)] >> shift);
|
||||
bitsSet = bitCount(masks[v_mask_index + (index++)] >> shift);
|
||||
uint bitsCounted = 32u - shift;
|
||||
|
||||
while (bitsCounted + 32u <= bitsTotal) {
|
||||
bitsSet += bitCount(masks[maskIndex + (index++)]);
|
||||
bitsSet += bitCount(masks[v_mask_index + (index++)]);
|
||||
bitsCounted += 32u;
|
||||
}
|
||||
|
||||
if (bitsCounted < bitsTotal) {
|
||||
bitsSet += bitCount(bitfieldExtract(
|
||||
masks[maskIndex + (index++)], 0, int(bitsTotal - bitsCounted)));
|
||||
masks[v_mask_index + (index++)], 0, int(bitsTotal - bitsCounted)));
|
||||
}
|
||||
}
|
||||
|
||||
if (bitsSet == 0u)
|
||||
discard;
|
||||
|
||||
vec4 color = unpackUnorm4x8(v_color);
|
||||
|
||||
float blendFactor = 0.5f * float(bitsSet) / max(float(bitsTotal), 1.0f);
|
||||
o_color = vec4(mix(rgba.rgb, vec3(1.0f), blendFactor), rgba.a * opacity);
|
||||
o_color.rgb = encodeOutput(o_color.rgb);
|
||||
o_color = vec4(mix(color.rgb, vec3(1.0f), blendFactor), color.a * opacity);
|
||||
o_color = linear_to_output(o_color);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
47
src/dxvk/hud/shaders/hud_chunk_vert_background.vert
Normal file
47
src/dxvk/hud/shaders/hud_chunk_vert_background.vert
Normal 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);
|
||||
}
|
58
src/dxvk/hud/shaders/hud_chunk_vert_visualize.vert
Normal file
58
src/dxvk/hud/shaders/hud_chunk_vert_visualize.vert
Normal 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);
|
||||
}
|
@ -1,68 +1,27 @@
|
||||
#define VK_COLOR_SPACE_SRGB_NONLINEAR_KHR (0)
|
||||
#define VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT (1000104002)
|
||||
#define VK_COLOR_SPACE_HDR10_ST2084_EXT (1000104008)
|
||||
#define VK_COLOR_SPACE_PASS_THROUGH_EXT (1000104013)
|
||||
#include "../../shaders/dxvk_color_space.glsl"
|
||||
|
||||
#define HUD_NITS (203.0f)
|
||||
layout(constant_id = 0) const uint s_color_space = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
||||
layout(constant_id = 1) const bool s_srgb = false;
|
||||
|
||||
const mat3 rec709_to_xyz = mat3(
|
||||
0.4123908, 0.2126390, 0.0193308,
|
||||
0.3575843, 0.7151687, 0.1191948,
|
||||
0.1804808, 0.0721923, 0.9505322);
|
||||
vec4 linear_to_output(vec4 color) {
|
||||
switch (s_color_space) {
|
||||
case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: {
|
||||
if (!s_srgb)
|
||||
color.rgb = linear_to_srgb(color.rgb);
|
||||
|
||||
const mat3 xyz_to_rec2020 = mat3(
|
||||
1.7166512, -0.6666844, 0.0176399,
|
||||
-0.3556708, 1.6164812, -0.0427706,
|
||||
-0.2533663, 0.0157685, 0.9421031);
|
||||
|
||||
const mat3 rec709_to_rec2020 = xyz_to_rec2020 * rec709_to_xyz;
|
||||
|
||||
// Spec constants must always default to
|
||||
// zero for DXVK to handle them properly
|
||||
layout(constant_id = 0) const uint hud_color_space = 0;
|
||||
|
||||
vec3 encodeSrgb(vec3 linear) {
|
||||
bvec3 isLo = lessThanEqual(linear, vec3(0.0031308f));
|
||||
|
||||
vec3 loPart = linear * 12.92f;
|
||||
vec3 hiPart = pow(linear, vec3(5.0f / 12.0f)) * 1.055f - 0.055f;
|
||||
return mix(hiPart, loPart, isLo);
|
||||
}
|
||||
|
||||
vec3 encodePq(vec3 nits) {
|
||||
const float c1 = 0.8359375f;
|
||||
const float c2 = 18.8515625f;
|
||||
const float c3 = 18.6875f;
|
||||
const float m1 = 0.1593017578125f;
|
||||
const float m2 = 78.84375f;
|
||||
|
||||
vec3 y = clamp(nits / 10000.0f, vec3(0.0f), vec3(1.0f));
|
||||
vec3 y_m1 = pow(y, vec3(m1));
|
||||
|
||||
vec3 num = c1 + c2 * y_m1;
|
||||
vec3 den = 1.0f + c3 * y_m1;
|
||||
|
||||
return pow(num / den, vec3(m2));
|
||||
}
|
||||
|
||||
vec3 encodeScRgb(vec3 nits) {
|
||||
return nits / 80.0f;
|
||||
}
|
||||
|
||||
vec3 encodeOutput(vec3 linear) {
|
||||
switch (hud_color_space) {
|
||||
default:
|
||||
return linear;
|
||||
|
||||
case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR:
|
||||
return encodeSrgb(linear);
|
||||
|
||||
case VK_COLOR_SPACE_HDR10_ST2084_EXT: {
|
||||
vec3 rec2020 = rec709_to_rec2020 * linear;
|
||||
return encodePq(rec2020 * HUD_NITS);
|
||||
return color;
|
||||
}
|
||||
|
||||
case VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT:
|
||||
return encodeScRgb(linear * HUD_NITS);
|
||||
color.rgb = nits_to_sc_rgb(color.rgb * SDR_NITS);
|
||||
return color;
|
||||
|
||||
case VK_COLOR_SPACE_HDR10_ST2084_EXT:
|
||||
color.rgb = rec709_to_rec2020 * color.rgb;
|
||||
color.rgb = nits_to_pq(color.rgb * SDR_NITS);
|
||||
return color;
|
||||
|
||||
default: /* pass through */
|
||||
return color;
|
||||
}
|
||||
}
|
||||
|
197
src/dxvk/hud/shaders/hud_frame_time_eval.comp
Normal file
197
src/dxvk/hud/shaders/hud_frame_time_eval.comp
Normal 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));
|
||||
}
|
||||
}
|
@ -7,48 +7,80 @@
|
||||
layout(location = 0) in vec2 v_coord;
|
||||
layout(location = 0) out vec4 o_color;
|
||||
|
||||
struct line_point_t {
|
||||
float value;
|
||||
uint color;
|
||||
};
|
||||
#define NUM_FRAME_TIME_STAMPS (420)
|
||||
|
||||
layout(binding = 0, std430)
|
||||
readonly buffer line_data_t {
|
||||
line_point_t points[];
|
||||
readonly buffer timestamp_buffer_t {
|
||||
uvec2 frame_timestamps_raw[2u];
|
||||
float frame_interval_ms[NUM_FRAME_TIME_STAMPS];
|
||||
|
||||
float frame_time_avg_ms;
|
||||
float frame_time_min_ms;
|
||||
float frame_time_max_ms;
|
||||
};
|
||||
|
||||
layout(push_constant)
|
||||
uniform push_data_t {
|
||||
uint offset;
|
||||
uint count;
|
||||
vec2 pos;
|
||||
vec2 size;
|
||||
vec2 scale;
|
||||
uvec2 surface_size;
|
||||
float opacity;
|
||||
float scale;
|
||||
uint packed_xy;
|
||||
uint packed_wh;
|
||||
uint frame_index;
|
||||
};
|
||||
|
||||
void main() {
|
||||
float cx = v_coord.x * float(count);
|
||||
float fx = fract(cx);
|
||||
int max_index = NUM_FRAME_TIME_STAMPS - 1;
|
||||
|
||||
// We need to roll our own linear interpolation here
|
||||
uint p0_idx = min(uint(floor(cx)), count - 1) + offset;
|
||||
uint p1_idx = min(uint(ceil(cx)), count - 1) + offset;
|
||||
int compute_real_index(int local_index) {
|
||||
int real_index = int(frame_index) - local_index;
|
||||
|
||||
line_point_t p0 = points[p0_idx];
|
||||
line_point_t p1 = points[p1_idx];
|
||||
if (real_index < 0)
|
||||
real_index += NUM_FRAME_TIME_STAMPS;
|
||||
|
||||
float value = mix(p0.value, p1.value, fx);
|
||||
float alpha = value + v_coord.y - 1.0f;
|
||||
alpha = min(alpha / dFdy(v_coord.y), 1.0f);
|
||||
|
||||
if (alpha <= 0.0f)
|
||||
discard;
|
||||
|
||||
o_color = mix(
|
||||
unpackUnorm4x8(p0.color),
|
||||
unpackUnorm4x8(p1.color), fx);
|
||||
o_color *= alpha * opacity;
|
||||
|
||||
o_color.rgb = encodeOutput(o_color.rgb);
|
||||
return real_index;
|
||||
}
|
||||
|
||||
float sample_at(float position) {
|
||||
int local_index = int(position);
|
||||
|
||||
int lo_index = compute_real_index(clamp(local_index, 0, max_index));
|
||||
int hi_index = compute_real_index(clamp(local_index + 1, 0, max_index));
|
||||
|
||||
float lo_value = frame_interval_ms[lo_index];
|
||||
float hi_value = frame_interval_ms[hi_index];
|
||||
|
||||
return mix(lo_value, hi_value, fract(position));
|
||||
}
|
||||
|
||||
void main() {
|
||||
float x_pos = (1.0f - v_coord.x) * float(NUM_FRAME_TIME_STAMPS);
|
||||
|
||||
float x_delta = abs(dFdx(x_pos)) / 2.0f;
|
||||
float y_delta = abs(dFdy(v_coord.y));
|
||||
|
||||
float ms_l = sample_at(x_pos - x_delta);
|
||||
float ms_r = sample_at(x_pos + x_delta);
|
||||
|
||||
float ms_lo = min(ms_l, ms_r);
|
||||
float ms_hi = max(ms_l, ms_r);
|
||||
|
||||
float ms_max = clamp(max(frame_time_max_ms, frame_time_avg_ms * 2.0f), 20.0f, 200.0f);
|
||||
|
||||
float val_lo = min(ms_lo / ms_max, 1.0f) - y_delta;
|
||||
float val_hi = min(ms_hi / ms_max, 1.0f) - y_delta;
|
||||
|
||||
float diff_lo = min(v_coord.y - val_lo, 0.0f);
|
||||
float diff_hi = min(val_hi - v_coord.y, 0.0f);
|
||||
|
||||
float ms_y = ms_max * v_coord.y;
|
||||
|
||||
vec4 line_color = vec4(0.776f, 0.812f, 0.882f, 1.0f);
|
||||
vec4 bg_color = vec4(0.0f, 0.0f, 0.0f, 0.75f);
|
||||
|
||||
// Try to draw a somewhat defined line
|
||||
float diff = (diff_lo + diff_hi) + y_delta;
|
||||
o_color = mix(bg_color, line_color, clamp(diff / y_delta, 0.0f, 1.0f));
|
||||
|
||||
o_color = linear_to_output(o_color);
|
||||
o_color.a *= opacity;
|
||||
}
|
||||
|
@ -4,21 +4,35 @@ layout(location = 0) out vec2 o_coord;
|
||||
|
||||
layout(push_constant)
|
||||
uniform push_data_t {
|
||||
uint offset;
|
||||
uint count;
|
||||
vec2 pos;
|
||||
vec2 size;
|
||||
vec2 scale;
|
||||
uvec2 surface_size;
|
||||
float opacity;
|
||||
float scale;
|
||||
uint packed_xy;
|
||||
uint packed_wh;
|
||||
uint frame_index;
|
||||
};
|
||||
|
||||
vec2 unpack_u16(uint v) {
|
||||
// Inputs may be signed
|
||||
int hi = int(v);
|
||||
int lo = int(v << 16);
|
||||
return vec2(float(lo >> 16), float(hi >> 16));
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 coord = vec2(
|
||||
float(gl_VertexIndex & 1),
|
||||
float(gl_VertexIndex >> 1));
|
||||
o_coord = coord;
|
||||
o_coord = vec2(coord.x, 1.0f - coord.y);
|
||||
|
||||
vec2 surface_size_f = vec2(surface_size) / scale;
|
||||
|
||||
vec2 pos = unpack_u16(packed_xy);
|
||||
pos = mix(pos, surface_size_f + pos, lessThan(pos, vec2(0.0f)));
|
||||
|
||||
vec2 size = unpack_u16(packed_wh);
|
||||
|
||||
vec2 pixel_pos = pos + size * coord;
|
||||
vec2 scaled_pos = 2.0f * scale * pixel_pos - 1.0f;
|
||||
vec2 scaled_pos = 2.0f * (pixel_pos / surface_size_f) - 1.0f;
|
||||
gl_Position = vec4(scaled_pos, 0.0f, 1.0f);
|
||||
}
|
||||
|
@ -4,7 +4,14 @@
|
||||
|
||||
#include "hud_frag_common.glsl"
|
||||
|
||||
layout(binding = 2) uniform sampler2D s_font;
|
||||
layout(binding = 3) uniform sampler2D s_font;
|
||||
|
||||
layout(push_constant)
|
||||
uniform push_data_t {
|
||||
uvec2 surface_size;
|
||||
float opacity;
|
||||
float scale;
|
||||
};
|
||||
|
||||
layout(location = 0) in vec2 v_texcoord;
|
||||
layout(location = 1) in vec4 v_color;
|
||||
@ -25,8 +32,8 @@ void main() {
|
||||
vec3 r_shadow = vec3(0.0f, 0.0f, 0.0f);
|
||||
|
||||
o_color.rgb = mix(r_shadow, r_center, r_alpha_center);
|
||||
o_color.a = r_alpha_shadow * v_color.a;
|
||||
o_color.a = r_alpha_shadow * v_color.a * opacity;
|
||||
o_color.rgb *= o_color.a;
|
||||
|
||||
o_color.rgb = encodeOutput(o_color.rgb);
|
||||
o_color = linear_to_output(o_color);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
#version 450
|
||||
#version 460
|
||||
|
||||
struct font_info_t {
|
||||
float size;
|
||||
@ -18,15 +18,25 @@ readonly buffer font_buffer_t {
|
||||
glyph_info_t glyph_data[];
|
||||
};
|
||||
|
||||
layout(binding = 1) uniform usamplerBuffer text_buffer;
|
||||
struct draw_info_t {
|
||||
uint text_offset;
|
||||
uint text_length_and_size;
|
||||
uint packed_xy;
|
||||
uint color;
|
||||
};
|
||||
|
||||
layout(binding = 1, std430)
|
||||
readonly buffer draw_buffer_t {
|
||||
draw_info_t draw_infos[];
|
||||
};
|
||||
|
||||
layout(binding = 2) uniform usamplerBuffer text_buffer;
|
||||
|
||||
layout(push_constant)
|
||||
uniform push_data_t {
|
||||
vec4 text_color;
|
||||
vec2 text_pos;
|
||||
uint text_offset;
|
||||
float text_size;
|
||||
vec2 hud_scale;
|
||||
uvec2 surface_size;
|
||||
float opacity;
|
||||
float scale;
|
||||
};
|
||||
|
||||
layout(location = 0) out vec2 o_texcoord;
|
||||
@ -42,7 +52,8 @@ vec2 unpack_u16(uint v) {
|
||||
}
|
||||
|
||||
void main() {
|
||||
o_color = text_color;
|
||||
draw_info_t draw_info = draw_infos[gl_DrawID];
|
||||
o_color = unpackUnorm4x8(draw_info.color);
|
||||
|
||||
// Compute character index and vertex index for the current
|
||||
// character. We'll render two triangles per character.
|
||||
@ -50,7 +61,7 @@ void main() {
|
||||
uint vtx_idx = gl_VertexIndex - 6 * chr_idx;
|
||||
|
||||
// Load glyph info based on vertex index
|
||||
uint glyph_idx = texelFetch(text_buffer, int(text_offset + chr_idx)).x;
|
||||
uint glyph_idx = texelFetch(text_buffer, int(draw_info.text_offset + chr_idx)).x;
|
||||
glyph_info_t glyph_info = glyph_data[glyph_idx];
|
||||
|
||||
// Compute texture coordinate from glyph data
|
||||
@ -64,12 +75,18 @@ void main() {
|
||||
// Compute vertex position. We can easily do this here since our
|
||||
// font is a monospace font, otherwise we'd need to preprocess
|
||||
// the strings to render in a compute shader.
|
||||
float size_factor = text_size / font_data.size;
|
||||
uint text_size = bitfieldExtract(draw_info.text_length_and_size, 16, 16);
|
||||
float size_factor = float(text_size) / font_data.size;
|
||||
|
||||
vec2 surface_size_f = vec2(surface_size) / scale;
|
||||
|
||||
vec2 text_pos = unpack_u16(draw_info.packed_xy);
|
||||
text_pos = mix(text_pos, surface_size_f + text_pos, lessThan(text_pos, vec2(0.0f)));
|
||||
|
||||
vec2 local_pos = tex_wh * coord - unpack_u16(glyph_info.packed_origin)
|
||||
+ vec2(font_data.advance * float(chr_idx), 0.0f);
|
||||
vec2 pixel_pos = text_pos + size_factor * local_pos;
|
||||
vec2 scaled_pos = 2.0f * hud_scale * pixel_pos - 1.0f;
|
||||
vec2 scaled_pos = 2.0f * (pixel_pos / surface_size_f) - 1.0f;
|
||||
|
||||
gl_Position = vec4(scaled_pos, 0.0f, 1.0f);
|
||||
}
|
||||
|
@ -53,7 +53,10 @@ dxvk_shaders = files([
|
||||
|
||||
'hud/shaders/hud_chunk_frag_background.frag',
|
||||
'hud/shaders/hud_chunk_frag_visualize.frag',
|
||||
'hud/shaders/hud_chunk_vert.vert',
|
||||
'hud/shaders/hud_chunk_vert_background.vert',
|
||||
'hud/shaders/hud_chunk_vert_visualize.vert',
|
||||
|
||||
'hud/shaders/hud_frame_time_eval.comp',
|
||||
|
||||
'hud/shaders/hud_graph_frag.frag',
|
||||
'hud/shaders/hud_graph_vert.vert',
|
||||
|
Loading…
x
Reference in New Issue
Block a user