Replaced text overlay with proper (imgui based) UI overlay class
This commit is contained in:
parent
5107cf3ed0
commit
f6af0bde03
10 changed files with 743 additions and 807 deletions
555
base/VulkanUIOverlay.cpp
Normal file
555
base/VulkanUIOverlay.cpp
Normal file
|
|
@ -0,0 +1,555 @@
|
|||
/*
|
||||
* UI overlay class using ImGui
|
||||
*
|
||||
* Copyright (C) 2017 by Sascha Willems - www.saschawillems.de
|
||||
*
|
||||
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
|
||||
*/
|
||||
|
||||
#include "VulkanUIOverlay.h"
|
||||
|
||||
namespace vks
|
||||
{
|
||||
UIOverlay::UIOverlay(vks::VulkanDevice *vulkanDevice, VkQueue copyQueue, std::vector<VkFramebuffer> &framebuffers, VkFormat colorformat, VkFormat depthformat, uint32_t *framebufferwidth, uint32_t *framebufferheight, std::vector<VkPipelineShaderStageCreateInfo> shaderstages)
|
||||
{
|
||||
this->device = vulkanDevice;
|
||||
this->copyQueue = copyQueue;
|
||||
this->colorFormat = colorformat;
|
||||
this->depthFormat = depthformat;
|
||||
|
||||
this->frameBuffers.resize(framebuffers.size());
|
||||
for (uint32_t i = 0; i < framebuffers.size(); i++) {
|
||||
this->frameBuffers[i] = &framebuffers[i];
|
||||
}
|
||||
|
||||
this->shaderStages = shaderstages;
|
||||
|
||||
this->frameBufferWidth = framebufferwidth;
|
||||
this->frameBufferHeight = framebufferheight;
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
// Scale text on Android devices with high DPI
|
||||
if (vks::android::screenDensity >= ACONFIGURATION_DENSITY_XXHIGH) {
|
||||
scale = 2.0f;
|
||||
}
|
||||
else if (vks::android::screenDensity >= ACONFIGURATION_DENSITY_XHIGH) {
|
||||
scale = 1.5f;
|
||||
}
|
||||
else if (vks::android::screenDensity >= ACONFIGURATION_DENSITY_HIGH) {
|
||||
scale = 1.25f;
|
||||
};
|
||||
#endif
|
||||
|
||||
// Init ImGui
|
||||
// Color scheme
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
style.Colors[ImGuiCol_TitleBg] = ImVec4(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
style.Colors[ImGuiCol_TitleBgActive] = ImVec4(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
style.Colors[ImGuiCol_TitleBgCollapsed] = ImVec4(1.0f, 0.0f, 0.0f, 0.1f);
|
||||
style.Colors[ImGuiCol_MenuBarBg] = ImVec4(1.0f, 0.0f, 0.0f, 0.4f);
|
||||
style.Colors[ImGuiCol_Header] = ImVec4(0.8f, 0.0f, 0.0f, 0.4f);
|
||||
style.Colors[ImGuiCol_HeaderActive] = ImVec4(1.0f, 0.0f, 0.0f, 0.4f);
|
||||
style.Colors[ImGuiCol_HeaderHovered] = ImVec4(1.0f, 0.0f, 0.0f, 0.4f);
|
||||
style.Colors[ImGuiCol_CheckMark] = ImVec4(1.0f, 0.0f, 0.0f, 0.8f);
|
||||
// Dimensions
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.DisplaySize = ImVec2((float)(*framebufferwidth), (float)(*framebufferheight));
|
||||
io.DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
|
||||
|
||||
cmdBuffers.resize(framebuffers.size());
|
||||
prepareResources();
|
||||
prepareRenderPass();
|
||||
preparePipeline();
|
||||
}
|
||||
|
||||
/** Free up all Vulkan resources acquired by the UI overlay */
|
||||
UIOverlay::~UIOverlay()
|
||||
{
|
||||
vertexBuffer.destroy();
|
||||
vkDestroySampler(device->logicalDevice, sampler, nullptr);
|
||||
vkDestroyDescriptorSetLayout(device->logicalDevice, descriptorSetLayout, nullptr);
|
||||
vkDestroyDescriptorPool(device->logicalDevice, descriptorPool, nullptr);
|
||||
vkDestroyPipelineLayout(device->logicalDevice, pipelineLayout, nullptr);
|
||||
vkDestroyPipelineCache(device->logicalDevice, pipelineCache, nullptr);
|
||||
vkDestroyPipeline(device->logicalDevice, pipeline, nullptr);
|
||||
vkDestroyRenderPass(device->logicalDevice, renderPass, nullptr);
|
||||
vkFreeCommandBuffers(device->logicalDevice, commandPool, static_cast<uint32_t>(cmdBuffers.size()), cmdBuffers.data());
|
||||
vkDestroyCommandPool(device->logicalDevice, commandPool, nullptr);
|
||||
vkDestroyFence(device->logicalDevice, fence, nullptr);
|
||||
}
|
||||
|
||||
/** Prepare all vulkan resources required to render the UI overlay */
|
||||
void UIOverlay::prepareResources()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
// Create font texture
|
||||
unsigned char* fontData;
|
||||
int texWidth, texHeight;
|
||||
io.Fonts->GetTexDataAsRGBA32(&fontData, &texWidth, &texHeight);
|
||||
VkDeviceSize uploadSize = texWidth*texHeight * 4 * sizeof(char);
|
||||
|
||||
// Create target image for copy
|
||||
VkImageCreateInfo imageInfo = vks::initializers::imageCreateInfo();
|
||||
imageInfo.imageType = VK_IMAGE_TYPE_2D;
|
||||
imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
imageInfo.extent.width = texWidth;
|
||||
imageInfo.extent.height = texHeight;
|
||||
imageInfo.extent.depth = 1;
|
||||
imageInfo.mipLevels = 1;
|
||||
imageInfo.arrayLayers = 1;
|
||||
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
imageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
VK_CHECK_RESULT(vkCreateImage(device->logicalDevice, &imageInfo, nullptr, &fontImage));
|
||||
VkMemoryRequirements memReqs;
|
||||
vkGetImageMemoryRequirements(device->logicalDevice, fontImage, &memReqs);
|
||||
VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo();
|
||||
memAllocInfo.allocationSize = memReqs.size;
|
||||
memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &fontMemory));
|
||||
VK_CHECK_RESULT(vkBindImageMemory(device->logicalDevice, fontImage, fontMemory, 0));
|
||||
|
||||
// Image view
|
||||
VkImageViewCreateInfo viewInfo = vks::initializers::imageViewCreateInfo();
|
||||
viewInfo.image = fontImage;
|
||||
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
viewInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
viewInfo.subresourceRange.levelCount = 1;
|
||||
viewInfo.subresourceRange.layerCount = 1;
|
||||
VK_CHECK_RESULT(vkCreateImageView(device->logicalDevice, &viewInfo, nullptr, &fontView));
|
||||
|
||||
// Staging buffers for font data upload
|
||||
vks::Buffer stagingBuffer;
|
||||
|
||||
VK_CHECK_RESULT(device->createBuffer(
|
||||
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
||||
&stagingBuffer,
|
||||
uploadSize));
|
||||
|
||||
stagingBuffer.map();
|
||||
memcpy(stagingBuffer.mapped, fontData, uploadSize);
|
||||
stagingBuffer.unmap();
|
||||
|
||||
// Copy buffer data to font image
|
||||
VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
|
||||
|
||||
// Prepare for transfer
|
||||
vks::tools::setImageLayout(
|
||||
copyCmd,
|
||||
fontImage,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_PIPELINE_STAGE_HOST_BIT,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT);
|
||||
|
||||
// Copy
|
||||
VkBufferImageCopy bufferCopyRegion = {};
|
||||
bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
bufferCopyRegion.imageSubresource.layerCount = 1;
|
||||
bufferCopyRegion.imageExtent.width = texWidth;
|
||||
bufferCopyRegion.imageExtent.height = texHeight;
|
||||
bufferCopyRegion.imageExtent.depth = 1;
|
||||
|
||||
vkCmdCopyBufferToImage(
|
||||
copyCmd,
|
||||
stagingBuffer.buffer,
|
||||
fontImage,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
1,
|
||||
&bufferCopyRegion
|
||||
);
|
||||
|
||||
// Prepare for shader read
|
||||
vks::tools::setImageLayout(
|
||||
copyCmd,
|
||||
fontImage,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
|
||||
|
||||
device->flushCommandBuffer(copyCmd, copyQueue, true);
|
||||
|
||||
stagingBuffer.destroy();
|
||||
|
||||
// Font texture Sampler
|
||||
VkSamplerCreateInfo samplerInfo = vks::initializers::samplerCreateInfo();
|
||||
samplerInfo.magFilter = VK_FILTER_LINEAR;
|
||||
samplerInfo.minFilter = VK_FILTER_LINEAR;
|
||||
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
||||
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
|
||||
VK_CHECK_RESULT(vkCreateSampler(device->logicalDevice, &samplerInfo, nullptr, &sampler));
|
||||
|
||||
// Command buffer
|
||||
VkCommandPoolCreateInfo cmdPoolInfo = {};
|
||||
cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
||||
cmdPoolInfo.queueFamilyIndex = device->queueFamilyIndices.graphics;
|
||||
cmdPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
||||
VK_CHECK_RESULT(vkCreateCommandPool(device->logicalDevice, &cmdPoolInfo, nullptr, &commandPool));
|
||||
|
||||
VkCommandBufferAllocateInfo cmdBufAllocateInfo =
|
||||
vks::initializers::commandBufferAllocateInfo(commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, static_cast<uint32_t>(cmdBuffers.size()));
|
||||
VK_CHECK_RESULT(vkAllocateCommandBuffers(device->logicalDevice, &cmdBufAllocateInfo, cmdBuffers.data()));
|
||||
|
||||
// Descriptor pool
|
||||
std::vector<VkDescriptorPoolSize> poolSizes = {
|
||||
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1)
|
||||
};
|
||||
VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 2);
|
||||
VK_CHECK_RESULT(vkCreateDescriptorPool(device->logicalDevice, &descriptorPoolInfo, nullptr, &descriptorPool));
|
||||
|
||||
// Descriptor set layout
|
||||
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
|
||||
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 0),
|
||||
};
|
||||
VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings);
|
||||
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device->logicalDevice, &descriptorLayout, nullptr, &descriptorSetLayout));
|
||||
|
||||
// Descriptor set
|
||||
VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1);
|
||||
VK_CHECK_RESULT(vkAllocateDescriptorSets(device->logicalDevice, &allocInfo, &descriptorSet));
|
||||
VkDescriptorImageInfo fontDescriptor = vks::initializers::descriptorImageInfo(
|
||||
sampler,
|
||||
fontView,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
|
||||
);
|
||||
std::vector<VkWriteDescriptorSet> writeDescriptorSets = {
|
||||
vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0, &fontDescriptor)
|
||||
};
|
||||
vkUpdateDescriptorSets(device->logicalDevice, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);
|
||||
|
||||
// Pipeline cache
|
||||
VkPipelineCacheCreateInfo pipelineCacheCreateInfo = {};
|
||||
pipelineCacheCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
|
||||
VK_CHECK_RESULT(vkCreatePipelineCache(device->logicalDevice, &pipelineCacheCreateInfo, nullptr, &pipelineCache));
|
||||
|
||||
// Pipeline layout
|
||||
// Push constants for UI rendering parameters
|
||||
VkPushConstantRange pushConstantRange = vks::initializers::pushConstantRange(VK_SHADER_STAGE_VERTEX_BIT, sizeof(PushConstBlock), 0);
|
||||
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1);
|
||||
pipelineLayoutCreateInfo.pushConstantRangeCount = 1;
|
||||
pipelineLayoutCreateInfo.pPushConstantRanges = &pushConstantRange;
|
||||
VK_CHECK_RESULT(vkCreatePipelineLayout(device->logicalDevice, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout));
|
||||
|
||||
// Command buffer execution fence
|
||||
VkFenceCreateInfo fenceCreateInfo = vks::initializers::fenceCreateInfo();
|
||||
VK_CHECK_RESULT(vkCreateFence(device->logicalDevice, &fenceCreateInfo, nullptr, &fence));
|
||||
}
|
||||
|
||||
/** Prepare a separate pipeline for the UI overlay rendering decoupled from the main application */
|
||||
void UIOverlay::preparePipeline()
|
||||
{
|
||||
// Setup graphics pipeline for UI rendering
|
||||
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState =
|
||||
vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE);
|
||||
|
||||
VkPipelineRasterizationStateCreateInfo rasterizationState =
|
||||
vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_COUNTER_CLOCKWISE);
|
||||
|
||||
// Enable blending
|
||||
VkPipelineColorBlendAttachmentState blendAttachmentState{};
|
||||
blendAttachmentState.blendEnable = VK_TRUE;
|
||||
blendAttachmentState.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
|
||||
blendAttachmentState.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
||||
blendAttachmentState.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
blendAttachmentState.colorBlendOp = VK_BLEND_OP_ADD;
|
||||
blendAttachmentState.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
blendAttachmentState.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
blendAttachmentState.alphaBlendOp = VK_BLEND_OP_ADD;
|
||||
|
||||
VkPipelineColorBlendStateCreateInfo colorBlendState =
|
||||
vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentState);
|
||||
|
||||
VkPipelineDepthStencilStateCreateInfo depthStencilState =
|
||||
vks::initializers::pipelineDepthStencilStateCreateInfo(VK_FALSE, VK_FALSE, VK_COMPARE_OP_LESS_OR_EQUAL);
|
||||
|
||||
VkPipelineViewportStateCreateInfo viewportState =
|
||||
vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0);
|
||||
|
||||
VkPipelineMultisampleStateCreateInfo multisampleState =
|
||||
vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT);
|
||||
|
||||
std::vector<VkDynamicState> dynamicStateEnables = {
|
||||
VK_DYNAMIC_STATE_VIEWPORT,
|
||||
VK_DYNAMIC_STATE_SCISSOR
|
||||
};
|
||||
VkPipelineDynamicStateCreateInfo dynamicState =
|
||||
vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables);
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipelineCreateInfo = vks::initializers::pipelineCreateInfo(pipelineLayout, renderPass);
|
||||
|
||||
pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState;
|
||||
pipelineCreateInfo.pRasterizationState = &rasterizationState;
|
||||
pipelineCreateInfo.pColorBlendState = &colorBlendState;
|
||||
pipelineCreateInfo.pMultisampleState = &multisampleState;
|
||||
pipelineCreateInfo.pViewportState = &viewportState;
|
||||
pipelineCreateInfo.pDepthStencilState = &depthStencilState;
|
||||
pipelineCreateInfo.pDynamicState = &dynamicState;
|
||||
pipelineCreateInfo.stageCount = static_cast<uint32_t>(shaderStages.size());
|
||||
pipelineCreateInfo.pStages = shaderStages.data();
|
||||
|
||||
// Vertex bindings an attributes based on ImGui vertex definition
|
||||
std::vector<VkVertexInputBindingDescription> vertexInputBindings = {
|
||||
vks::initializers::vertexInputBindingDescription(0, sizeof(ImDrawVert), VK_VERTEX_INPUT_RATE_VERTEX),
|
||||
};
|
||||
std::vector<VkVertexInputAttributeDescription> vertexInputAttributes = {
|
||||
vks::initializers::vertexInputAttributeDescription(0, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(ImDrawVert, pos)), // Location 0: Position
|
||||
vks::initializers::vertexInputAttributeDescription(0, 1, VK_FORMAT_R32G32_SFLOAT, offsetof(ImDrawVert, uv)), // Location 1: UV
|
||||
vks::initializers::vertexInputAttributeDescription(0, 2, VK_FORMAT_R8G8B8A8_UNORM, offsetof(ImDrawVert, col)), // Location 0: Color
|
||||
};
|
||||
VkPipelineVertexInputStateCreateInfo vertexInputState = vks::initializers::pipelineVertexInputStateCreateInfo();
|
||||
vertexInputState.vertexBindingDescriptionCount = static_cast<uint32_t>(vertexInputBindings.size());
|
||||
vertexInputState.pVertexBindingDescriptions = vertexInputBindings.data();
|
||||
vertexInputState.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertexInputAttributes.size());
|
||||
vertexInputState.pVertexAttributeDescriptions = vertexInputAttributes.data();
|
||||
|
||||
pipelineCreateInfo.pVertexInputState = &vertexInputState;
|
||||
|
||||
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device->logicalDevice, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipeline));
|
||||
}
|
||||
|
||||
/** Prepare a separate render pass for rendering the text as an overlay */
|
||||
void UIOverlay::prepareRenderPass()
|
||||
{
|
||||
VkAttachmentDescription attachments[2] = {};
|
||||
|
||||
// Color attachment
|
||||
attachments[0].format = colorFormat;
|
||||
attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
// Don't clear the framebuffer (like the renderpass from the example does)
|
||||
attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
|
||||
// Depth attachment
|
||||
attachments[1].format = depthFormat;
|
||||
attachments[1].samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
|
||||
VkAttachmentReference colorReference = {};
|
||||
colorReference.attachment = 0;
|
||||
colorReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
|
||||
VkAttachmentReference depthReference = {};
|
||||
depthReference.attachment = 1;
|
||||
depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
|
||||
VkSubpassDependency subpassDependencies[2] = {};
|
||||
|
||||
// Transition from final to initial (VK_SUBPASS_EXTERNAL refers to all commmands executed outside of the actual renderpass)
|
||||
subpassDependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
|
||||
subpassDependencies[0].dstSubpass = 0;
|
||||
subpassDependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
||||
subpassDependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
subpassDependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
|
||||
subpassDependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
subpassDependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
|
||||
|
||||
// Transition from initial to final
|
||||
subpassDependencies[1].srcSubpass = 0;
|
||||
subpassDependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
|
||||
subpassDependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
subpassDependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
||||
subpassDependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
subpassDependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
|
||||
subpassDependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
|
||||
|
||||
VkSubpassDescription subpassDescription = {};
|
||||
subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
subpassDescription.flags = 0;
|
||||
subpassDescription.inputAttachmentCount = 0;
|
||||
subpassDescription.pInputAttachments = NULL;
|
||||
subpassDescription.colorAttachmentCount = 1;
|
||||
subpassDescription.pColorAttachments = &colorReference;
|
||||
subpassDescription.pResolveAttachments = NULL;
|
||||
subpassDescription.pDepthStencilAttachment = &depthReference;
|
||||
subpassDescription.preserveAttachmentCount = 0;
|
||||
subpassDescription.pPreserveAttachments = NULL;
|
||||
|
||||
VkRenderPassCreateInfo renderPassInfo = {};
|
||||
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
||||
renderPassInfo.pNext = NULL;
|
||||
renderPassInfo.attachmentCount = 2;
|
||||
renderPassInfo.pAttachments = attachments;
|
||||
renderPassInfo.subpassCount = 1;
|
||||
renderPassInfo.pSubpasses = &subpassDescription;
|
||||
renderPassInfo.dependencyCount = 2;
|
||||
renderPassInfo.pDependencies = subpassDependencies;
|
||||
|
||||
VK_CHECK_RESULT(vkCreateRenderPass(device->logicalDevice, &renderPassInfo, nullptr, &renderPass));
|
||||
}
|
||||
|
||||
/** Update the command buffers to reflect text changes */
|
||||
void UIOverlay::updateCommandBuffers()
|
||||
{
|
||||
VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo();
|
||||
|
||||
VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo();
|
||||
renderPassBeginInfo.renderPass = renderPass;
|
||||
renderPassBeginInfo.renderArea.extent.width = *frameBufferWidth;
|
||||
renderPassBeginInfo.renderArea.extent.height = *frameBufferHeight;
|
||||
// None of the attachments will be cleared
|
||||
renderPassBeginInfo.clearValueCount = 0;
|
||||
renderPassBeginInfo.pClearValues = nullptr;
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
for (size_t i = 0; i < cmdBuffers.size(); ++i) {
|
||||
renderPassBeginInfo.framebuffer = *frameBuffers[i];
|
||||
|
||||
VK_CHECK_RESULT(vkBeginCommandBuffer(cmdBuffers[i], &cmdBufInfo));
|
||||
|
||||
if (vks::debugmarker::active) {
|
||||
vks::debugmarker::beginRegion(cmdBuffers[i], "UI overlay", glm::vec4(1.0f, 0.94f, 0.3f, 1.0f));
|
||||
}
|
||||
|
||||
vkCmdBeginRenderPass(cmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
vkCmdBindPipeline(cmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
vkCmdBindDescriptorSets(cmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL);
|
||||
|
||||
VkDeviceSize offsets[1] = { 0 };
|
||||
vkCmdBindVertexBuffers(cmdBuffers[i], 0, 1, &vertexBuffer.buffer, offsets);
|
||||
vkCmdBindIndexBuffer(cmdBuffers[i], indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT16);
|
||||
|
||||
VkViewport viewport = vks::initializers::viewport(ImGui::GetIO().DisplaySize.x, ImGui::GetIO().DisplaySize.y, 0.0f, 1.0f);
|
||||
vkCmdSetViewport(cmdBuffers[i], 0, 1, &viewport);
|
||||
|
||||
VkRect2D scissor = vks::initializers::rect2D((int32_t)ImGui::GetIO().DisplaySize.x, (int32_t)ImGui::GetIO().DisplaySize.y, 0, 0);
|
||||
vkCmdSetScissor(cmdBuffers[i], 0, 1, &scissor);
|
||||
|
||||
// UI scale and translate via push constants
|
||||
pushConstBlock.scale = glm::vec2(2.0f / io.DisplaySize.x, 2.0f / io.DisplaySize.y);
|
||||
pushConstBlock.translate = glm::vec2(-1.0f);
|
||||
vkCmdPushConstants(cmdBuffers[i], pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(PushConstBlock), &pushConstBlock);
|
||||
|
||||
// Render commands
|
||||
ImDrawData* imDrawData = ImGui::GetDrawData();
|
||||
int32_t vertexOffset = 0;
|
||||
int32_t indexOffset = 0;
|
||||
for (int32_t j = 0; j < imDrawData->CmdListsCount; j++) {
|
||||
const ImDrawList* cmd_list = imDrawData->CmdLists[j];
|
||||
for (int32_t k = 0; k < cmd_list->CmdBuffer.Size; k++) {
|
||||
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[k];
|
||||
VkRect2D scissorRect;
|
||||
scissorRect.offset.x = std::max((int32_t)(pcmd->ClipRect.x), 0);
|
||||
scissorRect.offset.y = std::max((int32_t)(pcmd->ClipRect.y), 0);
|
||||
scissorRect.extent.width = (uint32_t)(pcmd->ClipRect.z - pcmd->ClipRect.x);
|
||||
scissorRect.extent.height = (uint32_t)(pcmd->ClipRect.w - pcmd->ClipRect.y);
|
||||
vkCmdSetScissor(cmdBuffers[i], 0, 1, &scissorRect);
|
||||
vkCmdDrawIndexed(cmdBuffers[i], pcmd->ElemCount, 1, indexOffset, vertexOffset, 0);
|
||||
indexOffset += pcmd->ElemCount;
|
||||
}
|
||||
vertexOffset += cmd_list->VtxBuffer.Size;
|
||||
}
|
||||
|
||||
vkCmdEndRenderPass(cmdBuffers[i]);
|
||||
|
||||
if (vks::debugmarker::active) {
|
||||
vks::debugmarker::endRegion(cmdBuffers[i]);
|
||||
}
|
||||
|
||||
VK_CHECK_RESULT(vkEndCommandBuffer(cmdBuffers[i]));
|
||||
}
|
||||
}
|
||||
|
||||
/** Update vertex and index buffer containing the imGui elements when required */
|
||||
void UIOverlay::update()
|
||||
{
|
||||
ImDrawData* imDrawData = ImGui::GetDrawData();
|
||||
bool updateCmdBuffers = false;
|
||||
|
||||
if (!imDrawData) { return; };
|
||||
|
||||
// Note: Alignment is done inside buffer creation
|
||||
VkDeviceSize vertexBufferSize = imDrawData->TotalVtxCount * sizeof(ImDrawVert);
|
||||
VkDeviceSize indexBufferSize = imDrawData->TotalIdxCount * sizeof(ImDrawIdx);
|
||||
|
||||
// Update buffers only if vertex or index count has been changed compared to current buffer size
|
||||
|
||||
// Vertex buffer
|
||||
if ((vertexBuffer.buffer == VK_NULL_HANDLE) || (vertexCount != imDrawData->TotalVtxCount)) {
|
||||
vertexBuffer.unmap();
|
||||
vertexBuffer.destroy();
|
||||
VK_CHECK_RESULT(device->createBuffer(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &vertexBuffer, vertexBufferSize));
|
||||
vertexCount = imDrawData->TotalVtxCount;
|
||||
vertexBuffer.unmap();
|
||||
vertexBuffer.map();
|
||||
updateCmdBuffers = true;
|
||||
}
|
||||
|
||||
// Index buffer
|
||||
VkDeviceSize indexSize = imDrawData->TotalIdxCount * sizeof(ImDrawIdx);
|
||||
if ((indexBuffer.buffer == VK_NULL_HANDLE) || (indexCount < imDrawData->TotalIdxCount)) {
|
||||
indexBuffer.unmap();
|
||||
indexBuffer.destroy();
|
||||
VK_CHECK_RESULT(device->createBuffer(VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &indexBuffer, indexBufferSize));
|
||||
indexCount = imDrawData->TotalIdxCount;
|
||||
indexBuffer.map();
|
||||
updateCmdBuffers = true;
|
||||
}
|
||||
|
||||
// Upload data
|
||||
ImDrawVert* vtxDst = (ImDrawVert*)vertexBuffer.mapped;
|
||||
ImDrawIdx* idxDst = (ImDrawIdx*)indexBuffer.mapped;
|
||||
|
||||
for (int n = 0; n < imDrawData->CmdListsCount; n++) {
|
||||
const ImDrawList* cmd_list = imDrawData->CmdLists[n];
|
||||
memcpy(vtxDst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
|
||||
memcpy(idxDst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
|
||||
vtxDst += cmd_list->VtxBuffer.Size;
|
||||
idxDst += cmd_list->IdxBuffer.Size;
|
||||
}
|
||||
|
||||
// Flush to make writes visible to GPU
|
||||
vertexBuffer.flush();
|
||||
indexBuffer.flush();
|
||||
|
||||
if (updateCmdBuffers) {
|
||||
updateCommandBuffers();
|
||||
}
|
||||
}
|
||||
|
||||
/** Submit the overlay command buffers to a queue */
|
||||
void UIOverlay::submit(VkQueue queue, uint32_t bufferindex, VkSubmitInfo submitInfo)
|
||||
{
|
||||
if (!visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
submitInfo.pCommandBuffers = &cmdBuffers[bufferindex];
|
||||
submitInfo.commandBufferCount = 1;
|
||||
|
||||
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, fence));
|
||||
|
||||
VK_CHECK_RESULT(vkWaitForFences(device->logicalDevice, 1, &fence, VK_TRUE, UINT64_MAX));
|
||||
VK_CHECK_RESULT(vkResetFences(device->logicalDevice, 1, &fence));
|
||||
}
|
||||
|
||||
/** Reallocate command buffers for the text overlay */
|
||||
void UIOverlay::reallocateCommandBuffers()
|
||||
{
|
||||
vkFreeCommandBuffers(device->logicalDevice, commandPool, static_cast<uint32_t>(cmdBuffers.size()), cmdBuffers.data());
|
||||
VkCommandBufferAllocateInfo cmdBufAllocateInfo =
|
||||
vks::initializers::commandBufferAllocateInfo(commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, static_cast<uint32_t>(cmdBuffers.size()));
|
||||
VK_CHECK_RESULT(vkAllocateCommandBuffers(device->logicalDevice, &cmdBufAllocateInfo, cmdBuffers.data()));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue