mirror of https://github.com/doitsujin/dxvk
375 lines
13 KiB
C++
375 lines
13 KiB
C++
#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()) {
|
|
|
|
}
|
|
|
|
|
|
HudRenderer::~HudRenderer() {
|
|
|
|
}
|
|
|
|
|
|
void HudRenderer::beginFrame(
|
|
const Rc<DxvkContext>& context,
|
|
VkExtent2D surfaceSize,
|
|
float scale,
|
|
float opacity) {
|
|
if (!m_initialized)
|
|
this->initFontTexture(context);
|
|
|
|
m_mode = Mode::RenderNone;
|
|
m_scale = scale;
|
|
m_opacity = opacity;
|
|
m_surfaceSize = surfaceSize;
|
|
m_context = context;
|
|
}
|
|
|
|
|
|
void HudRenderer::drawText(
|
|
float size,
|
|
HudPos pos,
|
|
HudColor color,
|
|
const std::string& text) {
|
|
if (text.empty())
|
|
return;
|
|
|
|
beginTextRendering();
|
|
|
|
// 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);
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
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);
|
|
m_context->setInputLayout(0, nullptr, 0, nullptr);
|
|
}
|
|
}
|
|
|
|
|
|
void HudRenderer::beginGraphRendering() {
|
|
if (m_mode != Mode::RenderGraph) {
|
|
m_mode = Mode::RenderGraph;
|
|
|
|
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));
|
|
|
|
static const DxvkInputAssemblyState iaState = {
|
|
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
|
|
VK_FALSE, 0 };
|
|
|
|
m_context->setInputAssemblyState(iaState);
|
|
m_context->setInputLayout(0, nullptr, 0, nullptr);
|
|
}
|
|
}
|
|
|
|
|
|
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() {
|
|
ShaderPair result;
|
|
|
|
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 },
|
|
}};
|
|
|
|
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 },
|
|
}};
|
|
|
|
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));
|
|
|
|
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));
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
Rc<DxvkBufferView> 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<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);
|
|
}
|
|
|
|
|
|
Rc<DxvkBufferView> HudRenderer::createFontBufferView() {
|
|
DxvkBufferViewCreateInfo info;
|
|
info.format = VK_FORMAT_UNDEFINED;
|
|
info.rangeOffset = 0;
|
|
info.rangeLength = m_fontBuffer->info().size;
|
|
|
|
return m_device->createBufferView(m_fontBuffer, info);
|
|
}
|
|
|
|
|
|
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() {
|
|
DxvkImageViewCreateInfo info;
|
|
info.type = VK_IMAGE_VIEW_TYPE_2D;
|
|
info.format = m_fontImage->info().format;
|
|
info.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
|
|
info.aspect = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
info.minLevel = 0;
|
|
info.numLevels = 1;
|
|
info.minLayer = 0;
|
|
info.numLayers = 1;
|
|
|
|
return m_device->createImageView(m_fontImage, info);
|
|
}
|
|
|
|
|
|
Rc<DxvkSampler> HudRenderer::createFontSampler() {
|
|
DxvkSamplerCreateInfo info;
|
|
info.magFilter = VK_FILTER_LINEAR;
|
|
info.minFilter = VK_FILTER_LINEAR;
|
|
info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
|
info.mipmapLodBias = 0.0f;
|
|
info.mipmapLodMin = 0.0f;
|
|
info.mipmapLodMax = 0.0f;
|
|
info.useAnisotropy = VK_FALSE;
|
|
info.maxAnisotropy = 1.0f;
|
|
info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
info.compareToDepth = VK_FALSE;
|
|
info.compareOp = VK_COMPARE_OP_NEVER;
|
|
info.reductionMode = VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE;
|
|
info.borderColor = VkClearColorValue();
|
|
info.usePixelCoord = VK_TRUE;
|
|
info.nonSeamless = VK_FALSE;
|
|
|
|
return m_device->createSampler(info);
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
context->uploadBuffer(m_fontBuffer, &gpuData);
|
|
|
|
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;
|
|
}
|
|
|
|
}
|