From 0709c5f5c7e4f5f25e92e7cef263bc2edf9128b4 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sun, 20 Feb 2022 07:25:47 +0100 Subject: [PATCH] [hud] Greatly simplify text rendering in the HUD --- src/dxvk/hud/dxvk_hud_renderer.cpp | 213 ++++++++++++------------ src/dxvk/hud/dxvk_hud_renderer.h | 79 +++++---- src/dxvk/hud/shaders/hud_text_frag.frag | 2 +- src/dxvk/hud/shaders/hud_text_vert.vert | 77 ++++++++- 4 files changed, 226 insertions(+), 145 deletions(-) diff --git a/src/dxvk/hud/dxvk_hud_renderer.cpp b/src/dxvk/hud/dxvk_hud_renderer.cpp index 1c549dd6..a6ee6258 100644 --- a/src/dxvk/hud/dxvk_hud_renderer.cpp +++ b/src/dxvk/hud/dxvk_hud_renderer.cpp @@ -15,11 +15,15 @@ namespace dxvk::hud { m_device (device), m_textShaders (createTextShaders()), m_lineShaders (createLineShaders()), + m_dataBuffer (createDataBuffer()), + m_dataView (createDataView()), + m_dataOffset (0ull), + m_fontBuffer (createFontBuffer()), m_fontImage (createFontImage()), m_fontView (createFontView()), m_fontSampler (createFontSampler()), m_vertexBuffer (createVertexBuffer()) { - this->initCharMap(); + } @@ -32,9 +36,6 @@ namespace dxvk::hud { if (!m_initialized) this->initFontTexture(context); - context->bindResourceSampler(0, m_fontSampler); - context->bindResourceView (0, m_fontView, nullptr); - m_mode = Mode::RenderNone; m_scale = scale; m_surfaceSize = surfaceSize; @@ -49,66 +50,32 @@ namespace dxvk::hud { HudPos pos, HudColor color, const std::string& text) { + if (text.empty()) + return; + beginTextRendering(); - const float xscale = m_scale / std::max(float(m_surfaceSize.width), 1.0f); - const float yscale = m_scale / std::max(float(m_surfaceSize.height), 1.0f); + // 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), ' '); - uint32_t vertexCount = 6 * text.size(); + VkDeviceSize offset = allocDataBuffer(textCopy.size()); + std::memcpy(m_dataBuffer->mapPtr(offset), textCopy.data(), textCopy.size()); - if (m_currTextVertex + vertexCount > MaxTextVertexCount - || m_currTextInstance + 1 > MaxTextInstanceCount) - allocVertexBufferSlice(); + // 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->draw(vertexCount, 1, m_currTextVertex, m_currTextInstance); + m_context->pushConstants(0, sizeof(pushData), &pushData); - const float sizeFactor = size / float(g_hudFont.size); - - for (size_t i = 0; i < text.size(); i++) { - const HudGlyph& glyph = g_hudFont.glyphs[ - m_charMap[uint8_t(text[i])]]; - - HudPos size = { - sizeFactor * float(glyph.w), - sizeFactor * float(glyph.h) }; - - HudPos origin = { - pos.x - sizeFactor * float(glyph.originX), - pos.y - sizeFactor * float(glyph.originY) }; - - HudPos posTl = { xscale * (origin.x), yscale * (origin.y) }; - HudPos posBr = { xscale * (origin.x + size.x), yscale * (origin.y + size.y) }; - - HudTexCoord texTl = { uint32_t(glyph.x), uint32_t(glyph.y) }; - HudTexCoord texBr = { uint32_t(glyph.x + glyph.w), uint32_t(glyph.y + glyph.h) }; - - uint32_t idx = 6 * i + m_currTextVertex; - - m_vertexData->textVertices[idx + 0].position = { posTl.x, posTl.y }; - m_vertexData->textVertices[idx + 0].texcoord = { texTl.u, texTl.v }; - - m_vertexData->textVertices[idx + 1].position = { posBr.x, posTl.y }; - m_vertexData->textVertices[idx + 1].texcoord = { texBr.u, texTl.v }; - - m_vertexData->textVertices[idx + 2].position = { posTl.x, posBr.y }; - m_vertexData->textVertices[idx + 2].texcoord = { texTl.u, texBr.v }; - - m_vertexData->textVertices[idx + 3].position = { posBr.x, posBr.y }; - m_vertexData->textVertices[idx + 3].texcoord = { texBr.u, texBr.v }; - - m_vertexData->textVertices[idx + 4].position = { posTl.x, posBr.y }; - m_vertexData->textVertices[idx + 4].texcoord = { texTl.u, texBr.v }; - - m_vertexData->textVertices[idx + 5].position = { posBr.x, posTl.y }; - m_vertexData->textVertices[idx + 5].texcoord = { texBr.u, texTl.v }; - - pos.x += sizeFactor * static_cast(g_hudFont.advance); - } - - m_vertexData->textColors[m_currTextInstance] = color; - - m_currTextVertex += vertexCount; - m_currTextInstance += 1; + // Draw with orignal vertex count + m_context->draw(6 * text.size(), 1, 0, 0); } @@ -142,8 +109,6 @@ namespace dxvk::hud { auto vertexSlice = m_vertexBuffer->allocSlice(); m_context->invalidateBuffer(m_vertexBuffer, vertexSlice); - m_currTextVertex = 0; - m_currTextInstance = 0; m_currLineVertex = 0; m_vertexData = reinterpret_cast(vertexSlice.mapPtr); @@ -154,33 +119,20 @@ namespace dxvk::hud { if (m_mode != Mode::RenderText) { m_mode = Mode::RenderText; - m_context->bindVertexBuffer(0, DxvkBufferSlice(m_vertexBuffer, offsetof(VertexBufferData, textVertices), sizeof(HudTextVertex) * MaxTextVertexCount), sizeof(HudTextVertex)); - m_context->bindVertexBuffer(1, DxvkBufferSlice(m_vertexBuffer, offsetof(VertexBufferData, textColors), sizeof(HudColor) * MaxTextInstanceCount), sizeof(HudColor)); - m_context->bindShader(VK_SHADER_STAGE_VERTEX_BIT, m_textShaders.vert); m_context->bindShader(VK_SHADER_STAGE_FRAGMENT_BIT, m_textShaders.frag); + m_context->bindResourceBuffer (0, DxvkBufferSlice(m_fontBuffer)); + m_context->bindResourceView (1, nullptr, m_dataView); + m_context->bindResourceSampler(2, m_fontSampler); + m_context->bindResourceView (2, m_fontView, nullptr); + static const DxvkInputAssemblyState iaState = { VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, VK_FALSE, 0 }; - - static const std::array ilAttributes = {{ - { 0, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(HudTextVertex, position) }, - { 1, 0, VK_FORMAT_R32G32_UINT, offsetof(HudTextVertex, texcoord) }, - { 2, 1, VK_FORMAT_R32G32B32A32_SFLOAT, 0 }, - }}; - - static const std::array ilBindings = {{ - { 0, 0, VK_VERTEX_INPUT_RATE_VERTEX }, - { 1, 1, VK_VERTEX_INPUT_RATE_INSTANCE }, - }}; m_context->setInputAssemblyState(iaState); - m_context->setInputLayout( - ilAttributes.size(), - ilAttributes.data(), - ilBindings.size(), - ilBindings.data()); + m_context->setInputLayout(0, nullptr, 0, nullptr); } } @@ -215,6 +167,18 @@ namespace dxvk::hud { ilBindings.data()); } } + + + VkDeviceSize HudRenderer::allocDataBuffer(VkDeviceSize size) { + if (m_dataOffset + size > m_dataBuffer->info().size) { + m_context->invalidateBuffer(m_dataBuffer, m_dataBuffer->allocSlice()); + m_dataOffset = 0; + } + + VkDeviceSize offset = m_dataOffset; + m_dataOffset = align(offset + size, 64); + return offset; + } HudRenderer::ShaderPair HudRenderer::createTextShaders() { @@ -223,14 +187,21 @@ namespace dxvk::hud { const SpirvCodeBuffer vsCode(hud_text_vert); const SpirvCodeBuffer fsCode(hud_text_frag); - // Two shader resources: Font texture and sampler + const std::array vsResources = {{ + { 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_IMAGE_VIEW_TYPE_MAX_ENUM }, + { 1, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, VK_IMAGE_VIEW_TYPE_MAX_ENUM }, + }}; + const std::array fsResources = {{ - { 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_IMAGE_VIEW_TYPE_2D }, + { 2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_IMAGE_VIEW_TYPE_2D }, }}; result.vert = m_device->createShader( VK_SHADER_STAGE_VERTEX_BIT, - 0, nullptr, { 0x7, 0x3 }, vsCode); + vsResources.size(), + vsResources.data(), + { 0x0, 0x3, 0, sizeof(HudTextPushConstants) }, + vsCode); result.frag = m_device->createShader( VK_SHADER_STAGE_FRAGMENT_BIT, @@ -261,6 +232,46 @@ namespace dxvk::hud { } + Rc HudRenderer::createDataBuffer() { + DxvkBufferCreateInfo info; + info.size = DataBufferSize; + info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT + | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT; + info.stages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT + | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + info.access = VK_ACCESS_SHADER_READ_BIT; + + return m_device->createBuffer(info, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + } + + + Rc HudRenderer::createDataView() { + DxvkBufferViewCreateInfo info; + info.format = VK_FORMAT_R8_UINT; + info.rangeOffset = 0; + info.rangeLength = m_dataBuffer->info().size; + + return m_device->createBufferView(m_dataBuffer, info); + } + + + Rc 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); + } + + Rc HudRenderer::createFontImage() { DxvkImageCreateInfo info; info.type = VK_IMAGE_TYPE_2D; @@ -336,31 +347,29 @@ namespace dxvk::hud { void HudRenderer::initFontTexture( const Rc& context) { - DxvkBufferCreateInfo bufferInfo; - bufferInfo.size = g_hudFont.width * g_hudFont.height; - bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - bufferInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT; - bufferInfo.access = VK_ACCESS_TRANSFER_READ_BIT; + HudFontGpuData gpuData = { }; + gpuData.size = float(g_hudFont.size); + gpuData.advance = float(g_hudFont.advance); - auto stagingBuffer = m_device->createBuffer(bufferInfo, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); - std::memcpy(stagingBuffer->mapPtr(0), g_hudFont.texture, bufferInfo.size); + for (uint32_t i = 0; i < g_hudFont.charCount; i++) { + auto src = &g_hudFont.glyphs[i]; + auto dst = &gpuData.glyphs[src->codePoint]; - context->copyBufferToImage(m_fontImage, + 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); + + context->uploadImage(m_fontImage, VkImageSubresourceLayers { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 }, - VkOffset3D { 0, 0, 0 }, - VkExtent3D { g_hudFont.width, g_hudFont.height, 1 }, - stagingBuffer, 0, 0, 0); + g_hudFont.texture, g_hudFont.width, g_hudFont.width * g_hudFont.height); m_initialized = true; } - - void HudRenderer::initCharMap() { - std::fill(m_charMap.begin(), m_charMap.end(), 0); - - for (uint32_t i = 0; i < g_hudFont.charCount; i++) - m_charMap.at(g_hudFont.glyphs[i].codePoint) = i; - } - } diff --git a/src/dxvk/hud/dxvk_hud_renderer.h b/src/dxvk/hud/dxvk_hud_renderer.h index 8d9aa6b1..e118e0ea 100644 --- a/src/dxvk/hud/dxvk_hud_renderer.h +++ b/src/dxvk/hud/dxvk_hud_renderer.h @@ -17,17 +17,6 @@ namespace dxvk::hud { float y; }; - /** - * \brief Texture coordinates - * - * Absolute texture coordinates that are used - * to pick letters in the font texture. - */ - struct HudTexCoord { - uint32_t u; - uint32_t v; - }; - /** * \brief Color * @@ -52,14 +41,6 @@ namespace dxvk::hud { uint8_t a; }; - /** - * \brief Text vertex and texture coordinates - */ - struct HudTextVertex { - HudPos position; - HudTexCoord texcoord; - }; - /** * \brief Line vertex and color */ @@ -67,7 +48,37 @@ namespace dxvk::hud { HudPos position; HudNormColor color; }; - + + /** + * \brief HUD push constant data + */ + struct HudTextPushConstants { + HudColor color; + HudPos pos; + uint32_t offset; + float size; + HudPos scale; + }; + + /** + * \brief Glyph data + */ + 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]; + }; + /** * \brief Text renderer for the HUD * @@ -75,14 +86,11 @@ namespace dxvk::hud { * display performance and driver information. */ class HudRenderer { - constexpr static uint32_t MaxTextVertexCount = 512 * 6; - constexpr static uint32_t MaxTextInstanceCount = 64; + constexpr static VkDeviceSize DataBufferSize = 16384; constexpr static uint32_t MaxLineVertexCount = 1024; struct VertexBufferData { - HudColor textColors[MaxTextInstanceCount]; - HudTextVertex textVertices[MaxTextVertexCount]; - HudLineVertex lineVertices[MaxLineVertexCount]; + HudLineVertex lineVertices[MaxLineVertexCount]; }; public: @@ -127,8 +135,6 @@ namespace dxvk::hud { Rc frag; }; - std::array m_charMap; - Mode m_mode; float m_scale; VkExtent2D m_surfaceSize; @@ -139,6 +145,11 @@ namespace dxvk::hud { ShaderPair m_textShaders; ShaderPair m_lineShaders; + Rc m_dataBuffer; + Rc m_dataView; + VkDeviceSize m_dataOffset; + + Rc m_fontBuffer; Rc m_fontImage; Rc m_fontView; Rc m_fontSampler; @@ -146,8 +157,6 @@ namespace dxvk::hud { Rc m_vertexBuffer; VertexBufferData* m_vertexData = nullptr; - uint32_t m_currTextVertex = 0; - uint32_t m_currTextInstance = 0; uint32_t m_currLineVertex = 0; bool m_initialized = false; @@ -157,21 +166,25 @@ namespace dxvk::hud { void beginTextRendering(); void beginLineRendering(); - + + VkDeviceSize allocDataBuffer(VkDeviceSize size); + ShaderPair createTextShaders(); ShaderPair createLineShaders(); + Rc createDataBuffer(); + Rc createDataView(); + + Rc createFontBuffer(); Rc createFontImage(); Rc createFontView(); - Rc createFontSampler(); + Rc createVertexBuffer(); void initFontTexture( const Rc& context); - void initCharMap(); - }; } \ No newline at end of file diff --git a/src/dxvk/hud/shaders/hud_text_frag.frag b/src/dxvk/hud/shaders/hud_text_frag.frag index e6f91bf6..e1278fab 100644 --- a/src/dxvk/hud/shaders/hud_text_frag.frag +++ b/src/dxvk/hud/shaders/hud_text_frag.frag @@ -2,7 +2,7 @@ layout(constant_id = 1225) const bool srgbSwapchain = false; -layout(set = 0, binding = 0) uniform sampler2D s_font; +layout(binding = 2) uniform sampler2D s_font; layout(location = 0) in vec2 v_texcoord; layout(location = 1) in vec4 v_color; diff --git a/src/dxvk/hud/shaders/hud_text_vert.vert b/src/dxvk/hud/shaders/hud_text_vert.vert index 7b866704..c0b0f175 100644 --- a/src/dxvk/hud/shaders/hud_text_vert.vert +++ b/src/dxvk/hud/shaders/hud_text_vert.vert @@ -1,16 +1,75 @@ #version 450 -layout(location = 0) in vec2 v_position; -layout(location = 1) in uvec2 v_texcoord; -layout(location = 2) in vec4 v_color; +struct font_info_t { + float size; + float advance; + uvec2 padding; +}; + +struct glyph_info_t { + uint packed_xy; + uint packed_wh; + uint packed_origin; +}; + +layout(binding = 0, std430) +readonly buffer font_buffer_t { + font_info_t font_data; + glyph_info_t glyph_data[]; +}; + +layout(binding = 1) 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; +}; layout(location = 0) out vec2 o_texcoord; layout(location = 1) out vec4 o_color; +const uvec2 coord_mask = uvec2(0x2a, 0x1c); + +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() { - o_texcoord = vec2(v_texcoord); - o_color = v_color; - - vec2 pos = 2.0f * v_position - 1.0f; - gl_Position = vec4(pos, 0.0f, 1.0f); -} \ No newline at end of file + o_color = text_color; + + // Compute character index and vertex index for the current + // character. We'll render two triangles per character. + uint chr_idx = gl_VertexIndex / 6; + 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; + glyph_info_t glyph_info = glyph_data[glyph_idx]; + + // Compute texture coordinate from glyph data + vec2 coord = vec2((coord_mask >> vtx_idx) & 0x1); + + vec2 tex_xy = unpack_u16(glyph_info.packed_xy); + vec2 tex_wh = unpack_u16(glyph_info.packed_wh); + + o_texcoord = tex_xy + coord * tex_wh; + + // 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; + + 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; + + gl_Position = vec4(scaled_pos, 0.0f, 1.0f); +}