Use compute shader for text, use Titillium Web font

This commit is contained in:
synchromach 2025-02-07 10:17:16 +07:00
parent 9664e0b850
commit 5cb9805ab3
11 changed files with 8964 additions and 1939 deletions

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <unordered_map>
namespace dxvk::hud { namespace dxvk::hud {
@ -12,6 +13,7 @@ namespace dxvk::hud {
int32_t h; int32_t h;
int32_t originX; int32_t originX;
int32_t originY; int32_t originY;
int32_t advance;
}; };
struct HudFont { struct HudFont {
@ -19,7 +21,6 @@ namespace dxvk::hud {
uint32_t width; uint32_t width;
uint32_t height; uint32_t height;
uint32_t falloff; uint32_t falloff;
uint32_t advance;
uint32_t charCount; uint32_t charCount;
const HudGlyph* glyphs; const HudGlyph* glyphs;
@ -27,5 +28,4 @@ namespace dxvk::hud {
}; };
extern const HudFont g_hudFont; extern const HudFont g_hudFont;
}
}

View file

@ -320,6 +320,9 @@ namespace dxvk::hud {
VkDescriptorBufferInfo frameTimeBuffer = m_gpuBuffer->getDescriptor( VkDescriptorBufferInfo frameTimeBuffer = m_gpuBuffer->getDescriptor(
0, bufferLayout.timestampSize).buffer; 0, bufferLayout.timestampSize).buffer;
VkDescriptorBufferInfo charInfoBuffer = m_gpuBuffer->getDescriptor(
bufferLayout.charInfoOffset, bufferLayout.charInfoSize).buffer;
VkDescriptorBufferInfo drawInfoBuffer = m_gpuBuffer->getDescriptor( VkDescriptorBufferInfo drawInfoBuffer = m_gpuBuffer->getDescriptor(
bufferLayout.drawInfoOffset, bufferLayout.drawInfoSize).buffer; bufferLayout.drawInfoOffset, bufferLayout.drawInfoSize).buffer;
@ -328,15 +331,17 @@ namespace dxvk::hud {
VkBufferView textBufferView = m_textView->handle(); VkBufferView textBufferView = m_textView->handle();
std::array<VkWriteDescriptorSet, 4> descriptorWrites = {{ std::array<VkWriteDescriptorSet, 5> descriptorWrites = {{
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
set, 0, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &frameTimeBuffer }, set, 0, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &frameTimeBuffer },
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
set, 1, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &drawParamBuffer }, set, 1, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &charInfoBuffer },
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
set, 2, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &drawInfoBuffer }, set, 2, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &drawParamBuffer },
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
set, 3, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, nullptr, nullptr, &textBufferView }, set, 3, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &drawInfoBuffer },
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
set, 4, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, nullptr, nullptr, &textBufferView },
}}; }};
ctx.cmd->updateDescriptorSets( ctx.cmd->updateDescriptorSets(
@ -376,8 +381,8 @@ namespace dxvk::hud {
renderer.drawText(12, minPos, 0xff4040ff, "min:"); renderer.drawText(12, minPos, 0xff4040ff, "min:");
renderer.drawText(12, maxPos, 0xff4040ff, "max:"); renderer.drawText(12, maxPos, 0xff4040ff, "max:");
renderer.drawTextIndirect(ctx, key, drawParamBuffer, renderer.drawTextIndirect(ctx, key, charInfoBuffer,
drawInfoBuffer, textBufferView, 2u); drawParamBuffer, drawInfoBuffer, textBufferView, 2u);
if (unlikely(m_device->isDebugEnabled())) if (unlikely(m_device->isDebugEnabled()))
ctx.cmd->cmdEndDebugUtilsLabel(DxvkCmdBuffer::InitBuffer); ctx.cmd->cmdEndDebugUtilsLabel(DxvkCmdBuffer::InitBuffer);
@ -494,11 +499,12 @@ namespace dxvk::hud {
HudRenderer& renderer) { HudRenderer& renderer) {
auto vk = m_device->vkd(); auto vk = m_device->vkd();
std::array<VkDescriptorSetLayoutBinding, 4> bindings = {{ std::array<VkDescriptorSetLayoutBinding, 5> bindings = {{
{ 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT }, { 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT },
{ 1, 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 }, { 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 }, { 3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT },
{ 4, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT },
}}; }};
VkDescriptorSetLayoutCreateInfo setLayoutInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; VkDescriptorSetLayoutCreateInfo setLayoutInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
@ -704,7 +710,9 @@ namespace dxvk::hud {
BufferLayout result = { }; BufferLayout result = { };
result.timestampSize = align(sizeof(ComputeTimestampBuffer), 256u); result.timestampSize = align(sizeof(ComputeTimestampBuffer), 256u);
result.drawInfoOffset = result.timestampSize; result.charInfoOffset = result.timestampSize;
result.charInfoSize = align(sizeof(HudCharInfo) * 256u, 256u);
result.drawInfoOffset = result.charInfoOffset + result.charInfoSize;
result.drawInfoSize = align(sizeof(HudTextDrawInfo) * NumTextDraws, 256u); result.drawInfoSize = align(sizeof(HudTextDrawInfo) * NumTextDraws, 256u);
result.drawParamOffset = result.drawInfoOffset + result.drawInfoSize; result.drawParamOffset = result.drawInfoOffset + result.drawInfoSize;
result.drawParamSize = align(sizeof(VkDrawIndirectCommand) * NumTextDraws, 256u); result.drawParamSize = align(sizeof(VkDrawIndirectCommand) * NumTextDraws, 256u);

View file

@ -287,6 +287,8 @@ namespace dxvk::hud {
struct BufferLayout { struct BufferLayout {
size_t timestampSize; size_t timestampSize;
size_t charInfoOffset;
size_t charInfoSize;
size_t drawInfoOffset; size_t drawInfoOffset;
size_t drawInfoSize; size_t drawInfoSize;
size_t drawParamOffset; size_t drawParamOffset;
@ -774,4 +776,4 @@ namespace dxvk::hud {
}; };
} }

View file

@ -1,5 +1,6 @@
#include "dxvk_hud_renderer.h" #include "dxvk_hud_renderer.h"
#include <hud_text_comp.h>
#include <hud_text_frag.h> #include <hud_text_frag.h>
#include <hud_text_vert.h> #include <hud_text_vert.h>
@ -12,17 +13,22 @@ namespace dxvk::hud {
int16_t h; int16_t h;
int16_t originX; int16_t originX;
int16_t originY; int16_t originY;
int16_t advance;
int16_t padding[1];
}; };
struct HudFontGpuData { struct HudFontGpuData {
float size; float size;
float advance; uint32_t padding[3];
uint32_t padding[2];
HudGlyphGpuData glyphs[256]; HudGlyphGpuData glyphs[256];
}; };
struct ComputePushConstants {
uint32_t drawCount;
};
static const std::array<VkSpecializationMapEntry, 2> HudSpecConstantMap = {{ static const std::array<VkSpecializationMapEntry, 2> HudSpecConstantMap = {{
{ 0, offsetof(HudSpecConstants, dstSpace), sizeof(VkColorSpaceKHR) }, { 0, offsetof(HudSpecConstants, dstSpace), sizeof(VkColorSpaceKHR) },
@ -32,9 +38,12 @@ namespace dxvk::hud {
HudRenderer::HudRenderer(const Rc<DxvkDevice>& device) HudRenderer::HudRenderer(const Rc<DxvkDevice>& device)
: m_device (device), : m_device (device),
m_textSetLayout (createSetLayout()), m_textGfxSetLayout (createGfxSetLayout()),
m_textPipelineLayout (createPipelineLayout()) { m_textPipelineLayout (createPipelineLayout()) {
createComputePipeline();
// createShaderModule(m_textCs, VK_SHADER_STAGE_COMPUTE_BIT, sizeof(hud_text_comp), hud_text_comp);
createShaderModule(m_textVs, VK_SHADER_STAGE_VERTEX_BIT, sizeof(hud_text_vert), hud_text_vert); 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); createShaderModule(m_textFs, VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(hud_text_frag), hud_text_frag);
} }
@ -46,11 +55,16 @@ namespace dxvk::hud {
for (const auto& p : m_textPipelines) for (const auto& p : m_textPipelines)
vk->vkDestroyPipeline(vk->device(), p.second, nullptr); vk->vkDestroyPipeline(vk->device(), p.second, nullptr);
// vk->vkDestroyShaderModule(vk->device(), m_textCs.stageInfo.module, nullptr);
vk->vkDestroyShaderModule(vk->device(), m_textVs.stageInfo.module, nullptr); vk->vkDestroyShaderModule(vk->device(), m_textVs.stageInfo.module, nullptr);
vk->vkDestroyShaderModule(vk->device(), m_textFs.stageInfo.module, nullptr); vk->vkDestroyShaderModule(vk->device(), m_textFs.stageInfo.module, nullptr);
vk->vkDestroyPipeline(vk->device(), m_computePipeline, nullptr);
vk->vkDestroyPipelineLayout(vk->device(), m_computePipelineLayout, nullptr);
vk->vkDestroyDescriptorSetLayout(vk->device(), m_textComputeSetLayout, nullptr);
vk->vkDestroyPipelineLayout(vk->device(), m_textPipelineLayout, nullptr); vk->vkDestroyPipelineLayout(vk->device(), m_textPipelineLayout, nullptr);
vk->vkDestroyDescriptorSetLayout(vk->device(), m_textSetLayout, nullptr); vk->vkDestroyDescriptorSetLayout(vk->device(), m_textGfxSetLayout, nullptr);
} }
@ -134,20 +148,26 @@ namespace dxvk::hud {
// We'll use indirect draws and then just use aligned subsections // We'll use indirect draws and then just use aligned subsections
// of the data buffer to write our draw parameters // of the data buffer to write our draw parameters
size_t charInfoSize = align(m_textData.size() * sizeof(HudCharInfo), 256u);
size_t drawInfoSize = align(m_textDraws.size() * sizeof(HudTextDrawInfo), 256u); size_t drawInfoSize = align(m_textDraws.size() * sizeof(HudTextDrawInfo), 256u);
size_t drawArgsSize = align(m_textDraws.size() * sizeof(VkDrawIndirectCommand), 256u); size_t drawArgsSize = align(m_textDraws.size() * sizeof(VkDrawIndirectCommand), 256u);
// m_textBuffer will be our buffer where we put everything that the shaders will need:
// text data | character x-coordinates | draw infos | draw args
// Align buffer size to something large so we don't recreate it all the time // Align buffer size to something large so we don't recreate it all the time
size_t bufferSize = align(textSizeAligned + drawInfoSize + drawArgsSize, 2048u); size_t bufferSize = align(textSizeAligned + charInfoSize + drawInfoSize + drawArgsSize, 2048u);
if (!m_textBuffer || m_textBuffer->info().size < bufferSize) { if (!m_textBuffer || m_textBuffer->info().size < bufferSize) {
DxvkBufferCreateInfo textBufferInfo = { }; DxvkBufferCreateInfo textBufferInfo = { };
textBufferInfo.size = bufferSize; textBufferInfo.size = bufferSize;
textBufferInfo.usage = VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT textBufferInfo.usage = VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT
| VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
| VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT; | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT
| VK_BUFFER_USAGE_TRANSFER_DST_BIT;
textBufferInfo.stages = VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT textBufferInfo.stages = VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT
| VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT
| VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
textBufferInfo.access = VK_ACCESS_SHADER_READ_BIT textBufferInfo.access = VK_ACCESS_SHADER_READ_BIT
| VK_ACCESS_INDIRECT_COMMAND_READ_BIT; | VK_ACCESS_INDIRECT_COMMAND_READ_BIT;
textBufferInfo.debugName = "HUD text buffer"; textBufferInfo.debugName = "HUD text buffer";
@ -172,20 +192,23 @@ namespace dxvk::hud {
// Upload aligned text data in such a way that we write full cache lines // Upload aligned text data in such a way that we write full cache lines
std::memcpy(m_textBuffer->mapPtr(0), m_textData.data(), textSizeAligned); std::memcpy(m_textBuffer->mapPtr(0), m_textData.data(), textSizeAligned);
// Upload zeroes to character info to initialize
std::memset(m_textBuffer->mapPtr(textSizeAligned), 65, charInfoSize);
// Upload draw parameters and pad aligned region with zeroes // Upload draw parameters and pad aligned region with zeroes
size_t drawInfoCopySize = m_textDraws.size() * sizeof(HudTextDrawInfo); size_t drawInfoCopySize = m_textDraws.size() * sizeof(HudTextDrawInfo);
std::memcpy(m_textBuffer->mapPtr(textSizeAligned), m_textDraws.data(), drawInfoCopySize); std::memcpy(m_textBuffer->mapPtr(textSizeAligned + charInfoSize), m_textDraws.data(), drawInfoCopySize);
std::memset(m_textBuffer->mapPtr(textSizeAligned + drawInfoCopySize), 0, drawInfoSize - drawInfoCopySize); std::memset(m_textBuffer->mapPtr(textSizeAligned + charInfoSize + drawInfoCopySize), 0, drawInfoSize - drawInfoCopySize);
// Emit indirect draw parameters // Emit indirect draw parameters
size_t drawArgWriteSize = m_textDraws.size() * sizeof(VkDrawIndirectCommand); size_t drawArgWriteSize = m_textDraws.size() * sizeof(VkDrawIndirectCommand);
size_t drawArgOffset = textSizeAligned + drawInfoSize; size_t drawArgOffset = textSizeAligned + charInfoSize + drawInfoSize;
auto drawArgs = reinterpret_cast<VkDrawIndirectCommand*>(m_textBuffer->mapPtr(drawArgOffset)); auto drawArgs = reinterpret_cast<VkDrawIndirectCommand*>(m_textBuffer->mapPtr(drawArgOffset));
for (size_t i = 0; i < m_textDraws.size(); i++) { for (size_t i = 0; i < m_textDraws.size(); i++) {
drawArgs[i].vertexCount = 6u * m_textDraws[i].textLength; drawArgs[i].vertexCount = 12u * m_textDraws[i].textLength;
drawArgs[i].instanceCount = 1u; drawArgs[i].instanceCount = 1u;
drawArgs[i].firstVertex = 0u; drawArgs[i].firstVertex = 0u;
drawArgs[i].firstInstance = 0u; drawArgs[i].firstInstance = 0u;
@ -194,11 +217,12 @@ namespace dxvk::hud {
std::memset(m_textBuffer->mapPtr(drawArgOffset + drawArgWriteSize), 0, drawArgsSize - drawArgWriteSize); std::memset(m_textBuffer->mapPtr(drawArgOffset + drawArgWriteSize), 0, drawArgsSize - drawArgWriteSize);
// Draw the actual text // Draw the actual text
VkDescriptorBufferInfo textBufferDescriptor = m_textBuffer->getDescriptor(textSizeAligned, drawInfoSize).buffer; VkDescriptorBufferInfo charInfosDescriptor = m_textBuffer->getDescriptor(textSizeAligned, charInfoSize).buffer;
VkDescriptorBufferInfo drawBufferDescriptor = m_textBuffer->getDescriptor(drawArgOffset, drawArgWriteSize).buffer; VkDescriptorBufferInfo drawInfosDescriptor = m_textBuffer->getDescriptor(textSizeAligned + charInfoSize, drawInfoSize).buffer;
VkDescriptorBufferInfo drawArgsDescriptor = m_textBuffer->getDescriptor(drawArgOffset, drawArgWriteSize).buffer;
drawTextIndirect(ctx, getPipelineKey(dstView), drawTextIndirect(ctx, getPipelineKey(dstView), charInfosDescriptor,
drawBufferDescriptor, textBufferDescriptor, drawArgsDescriptor, drawInfosDescriptor,
m_textBufferView->handle(), m_textDraws.size()); m_textBufferView->handle(), m_textDraws.size());
// Ensure all used resources are kept alive // Ensure all used resources are kept alive
@ -216,18 +240,12 @@ namespace dxvk::hud {
void HudRenderer::drawTextIndirect( void HudRenderer::drawTextIndirect(
const DxvkContextObjects& ctx, const DxvkContextObjects& ctx,
const HudPipelineKey& key, const HudPipelineKey& key,
const VkDescriptorBufferInfo& charInfos,
const VkDescriptorBufferInfo& drawArgs, const VkDescriptorBufferInfo& drawArgs,
const VkDescriptorBufferInfo& drawInfos, const VkDescriptorBufferInfo& drawInfos,
VkBufferView text, VkBufferView text,
uint32_t drawCount) { uint32_t drawCount) {
// Bind the correct pipeline for the swap chain // Prepare font buffer, needed by both compute and gfx.
VkPipeline pipeline = getPipeline(key);
ctx.cmd->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,
VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
// Bind resources
VkDescriptorSet set = ctx.descriptorPool->alloc(m_textSetLayout);
VkDescriptorBufferInfo fontBufferDescriptor = m_fontBuffer->getDescriptor(0, m_fontBuffer->info().size).buffer; VkDescriptorBufferInfo fontBufferDescriptor = m_fontBuffer->getDescriptor(0, m_fontBuffer->info().size).buffer;
@ -235,25 +253,87 @@ namespace dxvk::hud {
fontTextureDescriptor.sampler = m_fontSampler->handle(); fontTextureDescriptor.sampler = m_fontSampler->handle();
fontTextureDescriptor.imageView = m_fontTextureView->handle(); fontTextureDescriptor.imageView = m_fontTextureView->handle();
fontTextureDescriptor.imageLayout = m_fontTexture->info().layout; fontTextureDescriptor.imageLayout = m_fontTexture->info().layout;
std::array<VkWriteDescriptorSet, 4> descriptorWrites = {{ // -- Compute part
VkDescriptorSet computeSet = ctx.descriptorPool->alloc(m_textComputeSetLayout);
std::array<VkWriteDescriptorSet, 4> computeDescriptorWrites = {{
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
set, 0, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &fontBufferDescriptor }, computeSet, 0, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &fontBufferDescriptor },
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
set, 1, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &drawInfos }, computeSet, 1, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &charInfos },
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
set, 2, 0, 1, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, nullptr, nullptr, &text }, computeSet, 2, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &drawInfos },
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
set, 3, 0, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &fontTextureDescriptor }, computeSet, 3, 0, 1, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, nullptr, nullptr, &text },
}}; }};
ctx.cmd->updateDescriptorSets( ctx.cmd->updateDescriptorSets(
descriptorWrites.size(), computeDescriptorWrites.size(),
descriptorWrites.data()); computeDescriptorWrites.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,
computeSet, 0, nullptr);
ComputePushConstants pushConstants = { };
pushConstants.drawCount = drawCount;
ctx.cmd->cmdPushConstants(DxvkCmdBuffer::InitBuffer,
m_computePipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT,
0, sizeof(pushConstants), &pushConstants);
static const int textComputeWorkgroupSize = 256;
ctx.cmd->cmdDispatch(DxvkCmdBuffer::InitBuffer,
(drawCount + textComputeWorkgroupSize - 1) / textComputeWorkgroupSize, 1, 1);
VkMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 };
barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
barrier.srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
barrier.dstStageMask = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
VkDependencyInfo depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };
depInfo.memoryBarrierCount = 1u;
depInfo.pMemoryBarriers = &barrier;
ctx.cmd->cmdPipelineBarrier(DxvkCmdBuffer::InitBuffer, &depInfo);
// -- Graphics part
// Bind the correct pipeline for the swap chain
VkPipeline pipeline = getPipeline(key);
ctx.cmd->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,
VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
// Bind resources
VkDescriptorSet gfxSet = ctx.descriptorPool->alloc(m_textGfxSetLayout);
std::array<VkWriteDescriptorSet, 5> gfxDescriptorWrites = {{
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
gfxSet, 0, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &fontBufferDescriptor },
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
gfxSet, 1, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &charInfos },
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
gfxSet, 2, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &drawInfos },
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
gfxSet, 3, 0, 1, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, nullptr, nullptr, &text },
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
gfxSet, 4, 0, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &fontTextureDescriptor },
}};
ctx.cmd->updateDescriptorSets(
gfxDescriptorWrites.size(),
gfxDescriptorWrites.data());
ctx.cmd->cmdBindDescriptorSet(DxvkCmdBuffer::ExecBuffer, ctx.cmd->cmdBindDescriptorSet(DxvkCmdBuffer::ExecBuffer,
VK_PIPELINE_BIND_POINT_GRAPHICS, m_textPipelineLayout, VK_PIPELINE_BIND_POINT_GRAPHICS, m_textPipelineLayout,
set, 0, nullptr); gfxSet, 0, nullptr);
ctx.cmd->cmdPushConstants(DxvkCmdBuffer::ExecBuffer, m_textPipelineLayout, ctx.cmd->cmdPushConstants(DxvkCmdBuffer::ExecBuffer, m_textPipelineLayout,
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
@ -404,7 +484,6 @@ namespace dxvk::hud {
HudFontGpuData glyphData = { }; HudFontGpuData glyphData = { };
glyphData.size = float(g_hudFont.size); glyphData.size = float(g_hudFont.size);
glyphData.advance = float(g_hudFont.advance);
for (size_t i = 0; i < g_hudFont.charCount; i++) { for (size_t i = 0; i < g_hudFont.charCount; i++) {
auto& src = g_hudFont.glyphs[i]; auto& src = g_hudFont.glyphs[i];
@ -416,6 +495,7 @@ namespace dxvk::hud {
dst.h = src.h; dst.h = src.h;
dst.originX = src.originX; dst.originX = src.originX;
dst.originY = src.originY; dst.originY = src.originY;
dst.advance = src.advance;
} }
std::memcpy(uploadBuffer->mapPtr(0), &glyphData, bufferDataSize); std::memcpy(uploadBuffer->mapPtr(0), &glyphData, bufferDataSize);
@ -501,14 +581,70 @@ namespace dxvk::hud {
} }
VkDescriptorSetLayout HudRenderer::createSetLayout() { void HudRenderer::createComputePipeline() {
auto vk = m_device->vkd(); auto vk = m_device->vkd();
static const std::array<VkDescriptorSetLayoutBinding, 4> bindings = {{ static const 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_UNIFORM_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_textComputeSetLayout);
if (vr != VK_SUCCESS)
throw DxvkError(str::format("Failed to create HUD text 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_textComputeSetLayout;
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 HUD compute pipeline layout: ", vr));
HudShaderModule shader = { };
createShaderModule(shader, VK_SHADER_STAGE_COMPUTE_BIT, sizeof(hud_text_comp), hud_text_comp);
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 HUD compute pipeline: ", vr));
vk->vkDestroyShaderModule(vk->device(), shader.stageInfo.module, nullptr);
}
VkDescriptorSetLayout HudRenderer::createGfxSetLayout() {
auto vk = m_device->vkd();
static const std::array<VkDescriptorSetLayoutBinding, 5> bindings = {{
{ 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT }, { 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT },
{ 1, 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 }, { 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT },
{ 3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT }, { 3, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT },
{ 4, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT },
}}; }};
VkDescriptorSetLayoutCreateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; VkDescriptorSetLayoutCreateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
@ -535,7 +671,7 @@ namespace dxvk::hud {
VkPipelineLayoutCreateInfo info = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; VkPipelineLayoutCreateInfo info = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
info.setLayoutCount = 1; info.setLayoutCount = 1;
info.pSetLayouts = &m_textSetLayout; info.pSetLayouts = &m_textGfxSetLayout;
info.pushConstantRangeCount = 1; info.pushConstantRangeCount = 1;
info.pPushConstantRanges = &pushConstantRange; info.pPushConstantRanges = &pushConstantRange;

View file

@ -40,6 +40,11 @@ namespace dxvk::hud {
}; };
struct HudCharInfo {
float x;
};
struct HudPushConstants { struct HudPushConstants {
VkExtent2D surfaceSize; VkExtent2D surfaceSize;
float opacity; float opacity;
@ -117,6 +122,7 @@ namespace dxvk::hud {
void drawTextIndirect( void drawTextIndirect(
const DxvkContextObjects& ctx, const DxvkContextObjects& ctx,
const HudPipelineKey& key, const HudPipelineKey& key,
const VkDescriptorBufferInfo& charInfos,
const VkDescriptorBufferInfo& drawArgs, const VkDescriptorBufferInfo& drawArgs,
const VkDescriptorBufferInfo& drawInfos, const VkDescriptorBufferInfo& drawInfos,
VkBufferView text, VkBufferView text,
@ -159,10 +165,15 @@ namespace dxvk::hud {
std::vector<HudTextDrawInfo> m_textDraws; std::vector<HudTextDrawInfo> m_textDraws;
std::vector<char> m_textData; std::vector<char> m_textData;
// HudShaderModule m_textCs;
HudShaderModule m_textVs; HudShaderModule m_textVs;
HudShaderModule m_textFs; HudShaderModule m_textFs;
VkDescriptorSetLayout m_textComputeSetLayout = VK_NULL_HANDLE;
VkPipelineLayout m_computePipelineLayout = VK_NULL_HANDLE;
VkPipeline m_computePipeline = VK_NULL_HANDLE;
VkDescriptorSetLayout m_textSetLayout = VK_NULL_HANDLE; VkDescriptorSetLayout m_textGfxSetLayout = VK_NULL_HANDLE;
VkPipelineLayout m_textPipelineLayout = VK_NULL_HANDLE; VkPipelineLayout m_textPipelineLayout = VK_NULL_HANDLE;
HudPushConstants m_pushConstants = { }; HudPushConstants m_pushConstants = { };
@ -174,8 +185,10 @@ namespace dxvk::hud {
void uploadFontResources( void uploadFontResources(
const DxvkContextObjects& ctx); const DxvkContextObjects& ctx);
void createComputePipeline();
VkDescriptorSetLayout createSetLayout(); VkDescriptorSetLayout createGfxSetLayout();
VkPipelineLayout createPipelineLayout(); VkPipelineLayout createPipelineLayout();
@ -187,4 +200,4 @@ namespace dxvk::hud {
}; };
} }

View file

@ -31,17 +31,17 @@ workgroupcoherent buffer timestamp_buffer_t {
float frame_time_max_ms; float frame_time_max_ms;
}; };
layout(binding = 1, std430) layout(binding = 2, std430)
writeonly buffer draw_param_buffer_t { writeonly buffer draw_param_buffer_t {
draw_param_t draw_params[]; draw_param_t draw_params[];
}; };
layout(binding = 2, std430) layout(binding = 3, std430)
writeonly buffer draw_info_buffer_t { writeonly buffer draw_info_buffer_t {
draw_info_t draw_infos[]; draw_info_t draw_infos[];
}; };
layout(binding = 3) layout(binding = 4)
uniform writeonly uimageBuffer text_buffer; uniform writeonly uimageBuffer text_buffer;
layout(push_constant) layout(push_constant)
@ -170,7 +170,7 @@ void main() {
draw_infos[tid].color = 0xffffffffu; draw_infos[tid].color = 0xffffffffu;
// Emit indirect draw parameters // Emit indirect draw parameters
draw_params[tid].vertex_count = 6u * len; draw_params[tid].vertex_count = 12u * len;
draw_params[tid].instance_count = 1u; draw_params[tid].instance_count = 1u;
draw_params[tid].first_vertex = 0u; draw_params[tid].first_vertex = 0u;
draw_params[tid].first_instance = 0u; draw_params[tid].first_instance = 0u;

View file

@ -0,0 +1,73 @@
#version 460
struct font_info_t {
float size;
float padding1;
uvec2 padding2;
};
struct glyph_info_t {
uint packed_xy;
uint packed_wh;
uint packed_origin;
uint packed_advance_padding;
};
layout(binding = 0, std430)
readonly buffer font_buffer_t {
font_info_t font_data;
glyph_info_t glyph_data[];
};
struct char_info_t {
float x;
};
layout(binding = 1, std430)
writeonly buffer char_info_buffer_t {
char_info_t char_infos[];
};
struct draw_info_t {
uint text_offset;
uint text_length_and_size;
uint packed_xy;
uint color;
};
layout(binding = 2, std430)
readonly buffer draw_buffer_t {
draw_info_t draw_infos[];
};
layout(binding = 3) uniform usamplerBuffer text_buffer;
layout(push_constant)
uniform push_data_t {
uint draw_count;
};
layout(local_size_x = 256) in;
void main() {
uint draw_index = gl_GlobalInvocationID.x;
if (draw_index < draw_count) {
draw_info_t draw_info = draw_infos[draw_index];
uint text_length = bitfieldExtract(draw_info.text_length_and_size, 0, 16);
uint text_size = bitfieldExtract(draw_info.text_length_and_size, 16, 16);
float size_factor = float(text_size) / font_data.size;
float total_x = 0.0f;
for (uint i = 0; i < text_length; ++i) {
uint glyph_idx = texelFetch(text_buffer, int(draw_info.text_offset + i)).x;
glyph_info_t glyph_info = glyph_data[glyph_idx];
char_infos[draw_info.text_offset + i].x = total_x;
uint advance = bitfieldExtract(glyph_info.packed_advance_padding, 0, 16);
total_x += advance;
}
}
}

View file

@ -4,7 +4,7 @@
#include "hud_frag_common.glsl" #include "hud_frag_common.glsl"
layout(binding = 3) uniform sampler2D s_font; layout(binding = 4) uniform sampler2D s_font;
layout(push_constant) layout(push_constant)
uniform push_data_t { uniform push_data_t {
@ -15,6 +15,8 @@ uniform push_data_t {
layout(location = 0) in vec2 v_texcoord; layout(location = 0) in vec2 v_texcoord;
layout(location = 1) in vec4 v_color; layout(location = 1) in vec4 v_color;
layout(location = 2) in float v_bias;
layout(location = 3) in float v_range;
layout(location = 0) out vec4 o_color; layout(location = 0) out vec4 o_color;
@ -25,14 +27,10 @@ float sampleAlpha(float alpha_bias, float dist_range) {
} }
void main() { void main() {
float r_alpha_center = sampleAlpha(0.0f, 5.0f); float r_alpha = sampleAlpha(v_bias, v_range);
float r_alpha_shadow = sampleAlpha(0.3f, 5.0f);
vec3 r_center = v_color.rgb; o_color = v_color;
vec3 r_shadow = vec3(0.0f, 0.0f, 0.0f); o_color.a *= r_alpha;
o_color.rgb = mix(r_shadow, r_center, r_alpha_center);
o_color.a = r_alpha_shadow * v_color.a * opacity;
o_color.rgb *= o_color.a; o_color.rgb *= o_color.a;
o_color = linear_to_output(o_color); o_color = linear_to_output(o_color);

View file

@ -2,14 +2,15 @@
struct font_info_t { struct font_info_t {
float size; float size;
float advance; float padding1;
uvec2 padding; uvec2 padding2;
}; };
struct glyph_info_t { struct glyph_info_t {
uint packed_xy; uint packed_xy;
uint packed_wh; uint packed_wh;
uint packed_origin; uint packed_origin;
uint packed_advance_padding;
}; };
layout(binding = 0, std430) layout(binding = 0, std430)
@ -18,6 +19,15 @@ readonly buffer font_buffer_t {
glyph_info_t glyph_data[]; glyph_info_t glyph_data[];
}; };
struct char_info_t {
float x;
};
layout(binding = 1, std430)
readonly buffer char_info_buffer_t {
char_info_t char_infos[];
};
struct draw_info_t { struct draw_info_t {
uint text_offset; uint text_offset;
uint text_length_and_size; uint text_length_and_size;
@ -25,12 +35,12 @@ struct draw_info_t {
uint color; uint color;
}; };
layout(binding = 1, std430) layout(binding = 2, std430)
readonly buffer draw_buffer_t { readonly buffer draw_buffer_t {
draw_info_t draw_infos[]; draw_info_t draw_infos[];
}; };
layout(binding = 2) uniform usamplerBuffer text_buffer; layout(binding = 3) uniform usamplerBuffer text_buffer;
layout(push_constant) layout(push_constant)
uniform push_data_t { uniform push_data_t {
@ -41,6 +51,8 @@ uniform push_data_t {
layout(location = 0) out vec2 o_texcoord; layout(location = 0) out vec2 o_texcoord;
layout(location = 1) out vec4 o_color; layout(location = 1) out vec4 o_color;
layout(location = 2) out float o_bias;
layout(location = 3) out float o_range;
const uvec2 coord_mask = uvec2(0x2a, 0x1c); const uvec2 coord_mask = uvec2(0x2a, 0x1c);
@ -51,18 +63,45 @@ vec2 unpack_u16(uint v) {
return vec2(float(lo >> 16), float(hi >> 16)); return vec2(float(lo >> 16), float(hi >> 16));
} }
uvec2 unpack_uvec16(uint v) {
// Inputs may be signed
uint hi = uint(v);
uint lo = uint(v << 16);
return uvec2(lo >> 16, hi >> 16);
}
void main() { void main() {
draw_info_t draw_info = draw_infos[gl_DrawID]; draw_info_t draw_info = draw_infos[gl_DrawID];
o_color = unpackUnorm4x8(draw_info.color);
// Compute character index and vertex index for the current // Compute character index and vertex index for the current
// character. We'll render two triangles per character. // character. We'll render two triangles per character.
uint chr_idx = gl_VertexIndex / 6; uint chr_idx = gl_VertexIndex / 6;
uint vtx_idx = gl_VertexIndex - 6 * chr_idx; uint vtx_idx = gl_VertexIndex - 6 * chr_idx;
// Extract text size.
uint text_length = bitfieldExtract(draw_info.text_length_and_size, 0, 16);
uint text_size = bitfieldExtract(draw_info.text_length_and_size, 16, 16);
float size_factor = float(text_size) / font_data.size;
// Set color and bias accordingly.
vec4 text_color = unpackUnorm4x8(draw_info.color);
if (chr_idx < text_length) {
o_bias = min(text_size / 5.6f, 0.35f);
o_range = 8.0f;
o_color = vec4(0.0f, 0.0f, 0.0f, text_color.a);
} else {
o_bias = 0.0f;
o_range = 10.0f;
o_color = text_color;
chr_idx -= text_length;
}
// Load glyph info based on vertex index // Load glyph info based on vertex index
uint glyph_idx = texelFetch(text_buffer, int(draw_info.text_offset + chr_idx)).x; uint glyph_idx = texelFetch(text_buffer, int(draw_info.text_offset + chr_idx)).x;
glyph_info_t glyph_info = glyph_data[glyph_idx]; glyph_info_t glyph_info = glyph_data[glyph_idx];
// Load character info (only the X coordinate for now) from compute shader
char_info_t char_info = char_infos[draw_info.text_offset + chr_idx];
// Compute texture coordinate from glyph data // Compute texture coordinate from glyph data
vec2 coord = vec2((coord_mask >> vtx_idx) & 0x1); vec2 coord = vec2((coord_mask >> vtx_idx) & 0x1);
@ -72,19 +111,14 @@ void main() {
o_texcoord = tex_xy + coord * tex_wh; o_texcoord = tex_xy + coord * tex_wh;
// Compute vertex position. We can easily do this here since our // Compute vertex position.
// font is a monospace font, otherwise we'd need to preprocess
// the strings to render in a compute shader.
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 surface_size_f = vec2(surface_size) / scale;
vec2 text_pos = unpack_u16(draw_info.packed_xy); 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))); text_pos = mix(text_pos, surface_size_f + text_pos, lessThan(text_pos, vec2(0.0f)));
vec2 local_pos = tex_wh * coord - unpack_u16(glyph_info.packed_origin) vec2 local_pos = tex_wh * coord - unpack_u16(glyph_info.packed_origin)
+ vec2(font_data.advance * float(chr_idx), 0.0f); + vec2(char_info.x, 0.0f);
vec2 pixel_pos = text_pos + size_factor * local_pos; vec2 pixel_pos = text_pos + size_factor * local_pos;
vec2 scaled_pos = 2.0f * (pixel_pos / surface_size_f) - 1.0f; vec2 scaled_pos = 2.0f * (pixel_pos / surface_size_f) - 1.0f;

View file

@ -64,6 +64,7 @@ dxvk_shaders = files([
'hud/shaders/hud_graph_frag.frag', 'hud/shaders/hud_graph_frag.frag',
'hud/shaders/hud_graph_vert.vert', 'hud/shaders/hud_graph_vert.vert',
'hud/shaders/hud_text_comp.comp',
'hud/shaders/hud_text_frag.frag', 'hud/shaders/hud_text_frag.frag',
'hud/shaders/hud_text_vert.vert', 'hud/shaders/hud_text_vert.vert',
]) ])