Reworked UI overlay class (no longer using separate render pass + submits)

Updated to support ImGui 1.62
Refs #496
This commit is contained in:
saschawillems 2018-08-29 20:49:13 +02:00
parent 3bbf0d8f29
commit 8a61105ec6
4 changed files with 76 additions and 288 deletions

View file

@ -13,7 +13,6 @@ namespace vks
UIOverlay::UIOverlay(vks::UIOverlayCreateInfo createInfo) UIOverlay::UIOverlay(vks::UIOverlayCreateInfo createInfo)
{ {
this->createInfo = createInfo; this->createInfo = createInfo;
this->renderPass = createInfo.renderPass;
#if defined(__ANDROID__) #if defined(__ANDROID__)
if (vks::android::screenDensity >= ACONFIGURATION_DENSITY_XXHIGH) { if (vks::android::screenDensity >= ACONFIGURATION_DENSITY_XXHIGH) {
@ -28,6 +27,7 @@ namespace vks
#endif #endif
// Init ImGui // Init ImGui
ImGui::CreateContext();
// Color scheme // Color scheme
ImGuiStyle& style = ImGui::GetStyle(); ImGuiStyle& style = ImGui::GetStyle();
style.Colors[ImGuiCol_TitleBg] = ImVec4(1.0f, 0.0f, 0.0f, 1.0f); style.Colors[ImGuiCol_TitleBg] = ImVec4(1.0f, 0.0f, 0.0f, 1.0f);
@ -43,17 +43,13 @@ namespace vks
io.DisplaySize = ImVec2((float)(createInfo.width), (float)(createInfo.height)); io.DisplaySize = ImVec2((float)(createInfo.width), (float)(createInfo.height));
io.FontGlobalScale = scale; io.FontGlobalScale = scale;
cmdBuffers.resize(createInfo.framebuffers.size());
prepareResources(); prepareResources();
if (createInfo.renderPass == VK_NULL_HANDLE) {
prepareRenderPass();
}
preparePipeline();
} }
/** Free up all Vulkan resources acquired by the UI overlay */ /** Free up all Vulkan resources acquired by the UI overlay */
UIOverlay::~UIOverlay() UIOverlay::~UIOverlay()
{ {
ImGui::DestroyContext();
vertexBuffer.destroy(); vertexBuffer.destroy();
indexBuffer.destroy(); indexBuffer.destroy();
vkDestroyImageView(createInfo.device->logicalDevice, fontView, nullptr); vkDestroyImageView(createInfo.device->logicalDevice, fontView, nullptr);
@ -63,14 +59,7 @@ namespace vks
vkDestroyDescriptorSetLayout(createInfo.device->logicalDevice, descriptorSetLayout, nullptr); vkDestroyDescriptorSetLayout(createInfo.device->logicalDevice, descriptorSetLayout, nullptr);
vkDestroyDescriptorPool(createInfo.device->logicalDevice, descriptorPool, nullptr); vkDestroyDescriptorPool(createInfo.device->logicalDevice, descriptorPool, nullptr);
vkDestroyPipelineLayout(createInfo.device->logicalDevice, pipelineLayout, nullptr); vkDestroyPipelineLayout(createInfo.device->logicalDevice, pipelineLayout, nullptr);
vkDestroyPipelineCache(createInfo.device->logicalDevice, pipelineCache, nullptr);
vkDestroyPipeline(createInfo.device->logicalDevice, pipeline, nullptr); vkDestroyPipeline(createInfo.device->logicalDevice, pipeline, nullptr);
if (createInfo.renderPass == VK_NULL_HANDLE) {
vkDestroyRenderPass(createInfo.device->logicalDevice, renderPass, nullptr);
}
vkFreeCommandBuffers(createInfo.device->logicalDevice, commandPool, static_cast<uint32_t>(cmdBuffers.size()), cmdBuffers.data());
vkDestroyCommandPool(createInfo.device->logicalDevice, commandPool, nullptr);
vkDestroyFence(createInfo.device->logicalDevice, fence, nullptr);
} }
/** Prepare all vulkan resources required to render the UI overlay */ /** Prepare all vulkan resources required to render the UI overlay */
@ -185,17 +174,6 @@ namespace vks
samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
VK_CHECK_RESULT(vkCreateSampler(createInfo.device->logicalDevice, &samplerInfo, nullptr, &sampler)); VK_CHECK_RESULT(vkCreateSampler(createInfo.device->logicalDevice, &samplerInfo, nullptr, &sampler));
// Command buffer
VkCommandPoolCreateInfo cmdPoolInfo = {};
cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
cmdPoolInfo.queueFamilyIndex = createInfo.device->queueFamilyIndices.graphics;
cmdPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
VK_CHECK_RESULT(vkCreateCommandPool(createInfo.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(createInfo.device->logicalDevice, &cmdBufAllocateInfo, cmdBuffers.data()));
// Descriptor pool // Descriptor pool
std::vector<VkDescriptorPoolSize> poolSizes = { std::vector<VkDescriptorPoolSize> poolSizes = {
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1) vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1)
@ -222,12 +200,11 @@ namespace vks
vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0, &fontDescriptor) vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0, &fontDescriptor)
}; };
vkUpdateDescriptorSets(createInfo.device->logicalDevice, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr); vkUpdateDescriptorSets(createInfo.device->logicalDevice, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);
}
// Pipeline cache /** Prepare a separate pipeline for the UI overlay rendering decoupled from the main application */
VkPipelineCacheCreateInfo pipelineCacheCreateInfo = {}; void UIOverlay::preparePipeline(const VkPipelineCache pipelineCache, const VkRenderPass renderPass)
pipelineCacheCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; {
VK_CHECK_RESULT(vkCreatePipelineCache(createInfo.device->logicalDevice, &pipelineCacheCreateInfo, nullptr, &pipelineCache));
// Pipeline layout // Pipeline layout
// Push constants for UI rendering parameters // Push constants for UI rendering parameters
VkPushConstantRange pushConstantRange = vks::initializers::pushConstantRange(VK_SHADER_STAGE_VERTEX_BIT, sizeof(PushConstBlock), 0); VkPushConstantRange pushConstantRange = vks::initializers::pushConstantRange(VK_SHADER_STAGE_VERTEX_BIT, sizeof(PushConstBlock), 0);
@ -236,14 +213,6 @@ namespace vks
pipelineLayoutCreateInfo.pPushConstantRanges = &pushConstantRange; pipelineLayoutCreateInfo.pPushConstantRanges = &pushConstantRange;
VK_CHECK_RESULT(vkCreatePipelineLayout(createInfo.device->logicalDevice, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); VK_CHECK_RESULT(vkCreatePipelineLayout(createInfo.device->logicalDevice, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout));
// Command buffer execution fence
VkFenceCreateInfo fenceCreateInfo = vks::initializers::fenceCreateInfo();
VK_CHECK_RESULT(vkCreateFence(createInfo.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 // Setup graphics pipeline for UI rendering
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = VkPipelineInputAssemblyStateCreateInfo inputAssemblyState =
vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE);
@ -278,7 +247,7 @@ namespace vks
vks::initializers::pipelineColorBlendStateCreateInfo(static_cast<uint32_t>(blendStates.size()), blendStates.data()); vks::initializers::pipelineColorBlendStateCreateInfo(static_cast<uint32_t>(blendStates.size()), blendStates.data());
VkPipelineDepthStencilStateCreateInfo depthStencilState = VkPipelineDepthStencilStateCreateInfo depthStencilState =
vks::initializers::pipelineDepthStencilStateCreateInfo(VK_FALSE, VK_FALSE, VK_COMPARE_OP_LESS_OR_EQUAL); vks::initializers::pipelineDepthStencilStateCreateInfo(VK_FALSE, VK_FALSE, VK_COMPARE_OP_ALWAYS);
VkPipelineViewportStateCreateInfo viewportState = VkPipelineViewportStateCreateInfo viewportState =
vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0); vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0);
@ -326,183 +295,22 @@ namespace vks
VK_CHECK_RESULT(vkCreateGraphicsPipelines(createInfo.device->logicalDevice, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipeline)); VK_CHECK_RESULT(vkCreateGraphicsPipelines(createInfo.device->logicalDevice, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipeline));
} }
/** Prepare a separate render pass for rendering the UI as an overlay */
void UIOverlay::prepareRenderPass()
{
VkAttachmentDescription attachments[2] = {};
// Color attachment
attachments[0].format = createInfo.colorformat;
attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
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_PRESENT_SRC_KHR;
attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
// Depth attachment
attachments[1].format = createInfo.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(createInfo.device->logicalDevice, &renderPassInfo, nullptr, &renderPass));
}
/** Update the command buffers to reflect UI changes */
void UIOverlay::updateCommandBuffers()
{
VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo();
VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo();
renderPassBeginInfo.renderPass = renderPass;
renderPassBeginInfo.renderArea.extent.width = createInfo.width;
renderPassBeginInfo.renderArea.extent.height = createInfo.height;
renderPassBeginInfo.clearValueCount = static_cast<uint32_t>(createInfo.clearValues.size());
renderPassBeginInfo.pClearValues = createInfo.clearValues.data();
ImGuiIO& io = ImGui::GetIO();
for (size_t i = 0; i < cmdBuffers.size(); ++i) {
renderPassBeginInfo.framebuffer = createInfo.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);
if (createInfo.targetSubpass > 0) {
for (uint32_t j = 0; j < createInfo.targetSubpass; j++) {
vkCmdNextSubpass(cmdBuffers[i], 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;
}
// Add empty subpasses if requested
if (createInfo.subpassCount > 1) {
for (uint32_t j = createInfo.targetSubpass+1; j < createInfo.subpassCount; j++) {
vkCmdNextSubpass(cmdBuffers[i], VK_SUBPASS_CONTENTS_INLINE);
}
}
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 */ /** Update vertex and index buffer containing the imGui elements when required */
void UIOverlay::update() bool UIOverlay::update()
{ {
ImDrawData* imDrawData = ImGui::GetDrawData(); ImDrawData* imDrawData = ImGui::GetDrawData();
bool updateCmdBuffers = false; bool updateCmdBuffers = false;
if (!imDrawData) { return; }; if (!imDrawData) { return false; };
// Note: Alignment is done inside buffer creation // Note: Alignment is done inside buffer creation
VkDeviceSize vertexBufferSize = imDrawData->TotalVtxCount * sizeof(ImDrawVert); VkDeviceSize vertexBufferSize = imDrawData->TotalVtxCount * sizeof(ImDrawVert);
VkDeviceSize indexBufferSize = imDrawData->TotalIdxCount * sizeof(ImDrawIdx); VkDeviceSize indexBufferSize = imDrawData->TotalIdxCount * sizeof(ImDrawIdx);
// Update buffers only if vertex or index count has been changed compared to current buffer size // Update buffers only if vertex or index count has been changed compared to current buffer size
if ((vertexBufferSize == 0) || (indexBufferSize == 0)) {
return false;
}
// Vertex buffer // Vertex buffer
if ((vertexBuffer.buffer == VK_NULL_HANDLE) || (vertexCount != imDrawData->TotalVtxCount)) { if ((vertexBuffer.buffer == VK_NULL_HANDLE) || (vertexCount != imDrawData->TotalVtxCount)) {
@ -542,35 +350,55 @@ namespace vks
vertexBuffer.flush(); vertexBuffer.flush();
indexBuffer.flush(); indexBuffer.flush();
if (updateCmdBuffers) { return updateCmdBuffers;
updateCommandBuffers();
}
} }
void UIOverlay::resize(uint32_t width, uint32_t height, std::vector<VkFramebuffer> framebuffers) void UIOverlay::draw(const VkCommandBuffer commandBuffer)
{ {
ImGuiIO& io = ImGui::GetIO(); ImDrawData* imDrawData = ImGui::GetDrawData();
io.DisplaySize = ImVec2((float)(width), (float)(height)); int32_t vertexOffset = 0;
createInfo.width = width; int32_t indexOffset = 0;
createInfo.height = height;
createInfo.framebuffers = framebuffers;
updateCommandBuffers();
}
/** Submit the overlay command buffers to a queue */ if ((!imDrawData) || (imDrawData->CmdListsCount == 0)) {
void UIOverlay::submit(VkQueue queue, uint32_t bufferindex, VkSubmitInfo submitInfo)
{
if (!visible) {
return; return;
} }
submitInfo.pCommandBuffers = &cmdBuffers[bufferindex]; ImGuiIO& io = ImGui::GetIO();
submitInfo.commandBufferCount = 1;
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, fence)); pushConstBlock.scale = glm::vec2(2.0f / io.DisplaySize.x, 2.0f / io.DisplaySize.y);
pushConstBlock.translate = glm::vec2(-1.0f);
vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(PushConstBlock), &pushConstBlock);
VK_CHECK_RESULT(vkWaitForFences(createInfo.device->logicalDevice, 1, &fence, VK_TRUE, UINT64_MAX)); vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
VK_CHECK_RESULT(vkResetFences(createInfo.device->logicalDevice, 1, &fence)); vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL);
VkDeviceSize offsets[1] = { 0 };
vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertexBuffer.buffer, offsets);
vkCmdBindIndexBuffer(commandBuffer, indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT16);
for (int32_t i = 0; i < imDrawData->CmdListsCount; i++)
{
const ImDrawList* cmd_list = imDrawData->CmdLists[i];
for (int32_t j = 0; j < cmd_list->CmdBuffer.Size; j++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[j];
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(commandBuffer, 0, 1, &scissorRect);
vkCmdDrawIndexed(commandBuffer, pcmd->ElemCount, 1, indexOffset, vertexOffset, 0);
indexOffset += pcmd->ElemCount;
}
vertexOffset += cmd_list->VtxBuffer.Size;
}
}
void UIOverlay::resize(uint32_t width, uint32_t height)
{
ImGuiIO& io = ImGui::GetIO();
io.DisplaySize = ImVec2((float)(width), (float)(height));
} }
bool UIOverlay::header(const char *caption) bool UIOverlay::header(const char *caption)

View file

@ -34,10 +34,6 @@ namespace vks
{ {
vks::VulkanDevice *device; vks::VulkanDevice *device;
VkQueue copyQueue; VkQueue copyQueue;
VkRenderPass renderPass;
std::vector<VkFramebuffer> framebuffers;
VkFormat colorformat;
VkFormat depthformat;
uint32_t width; uint32_t width;
uint32_t height; uint32_t height;
std::vector<VkPipelineShaderStageCreateInfo> shaders; std::vector<VkPipelineShaderStageCreateInfo> shaders;
@ -50,7 +46,7 @@ namespace vks
class UIOverlay class UIOverlay
{ {
private: public:
vks::Buffer vertexBuffer; vks::Buffer vertexBuffer;
vks::Buffer indexBuffer; vks::Buffer indexBuffer;
int32_t vertexCount = 0; int32_t vertexCount = 0;
@ -60,11 +56,7 @@ namespace vks
VkDescriptorSetLayout descriptorSetLayout; VkDescriptorSetLayout descriptorSetLayout;
VkDescriptorSet descriptorSet; VkDescriptorSet descriptorSet;
VkPipelineLayout pipelineLayout; VkPipelineLayout pipelineLayout;
VkPipelineCache pipelineCache;
VkPipeline pipeline; VkPipeline pipeline;
VkRenderPass renderPass;
VkCommandPool commandPool;
VkFence fence;
VkDeviceMemory fontMemory = VK_NULL_HANDLE; VkDeviceMemory fontMemory = VK_NULL_HANDLE;
VkImage fontImage = VK_NULL_HANDLE; VkImage fontImage = VK_NULL_HANDLE;
@ -79,22 +71,18 @@ namespace vks
UIOverlayCreateInfo createInfo = {}; UIOverlayCreateInfo createInfo = {};
void prepareResources(); void prepareResources();
void preparePipeline();
void prepareRenderPass();
void updateCommandBuffers();
public: public:
bool visible = true; bool visible = true;
float scale = 1.0f; float scale = 1.0f;
std::vector<VkCommandBuffer> cmdBuffers;
UIOverlay(vks::UIOverlayCreateInfo createInfo); UIOverlay(vks::UIOverlayCreateInfo createInfo);
~UIOverlay(); ~UIOverlay();
void update(); void preparePipeline(const VkPipelineCache pipelineCache, const VkRenderPass renderPass);
void resize(uint32_t width, uint32_t height, std::vector<VkFramebuffer> framebuffers);
void submit(VkQueue queue, uint32_t bufferindex, VkSubmitInfo submitInfo); bool update();
void draw(const VkCommandBuffer commandBuffer);
void resize(uint32_t width, uint32_t height);
bool header(const char* caption); bool header(const char* caption);
bool checkBox(const char* caption, bool* value); bool checkBox(const char* caption, bool* value);

View file

@ -199,9 +199,6 @@ void VulkanExampleBase::prepare()
// Setup default overlay creation info // Setup default overlay creation info
overlayCreateInfo.device = vulkanDevice; overlayCreateInfo.device = vulkanDevice;
overlayCreateInfo.copyQueue = queue; overlayCreateInfo.copyQueue = queue;
overlayCreateInfo.framebuffers = frameBuffers;
overlayCreateInfo.colorformat = swapChain.colorFormat;
overlayCreateInfo.depthformat = depthFormat;
overlayCreateInfo.width = width; overlayCreateInfo.width = width;
overlayCreateInfo.height = height; overlayCreateInfo.height = height;
// Virtual function call for example to customize overlay creation // Virtual function call for example to customize overlay creation
@ -214,6 +211,7 @@ void VulkanExampleBase::prepare()
}; };
} }
UIOverlay = new vks::UIOverlay(overlayCreateInfo); UIOverlay = new vks::UIOverlay(overlayCreateInfo);
UIOverlay->preparePipeline(pipelineCache, renderPass);
updateOverlay(); updateOverlay();
} }
} }
@ -603,7 +601,9 @@ void VulkanExampleBase::updateOverlay()
ImGui::PopStyleVar(); ImGui::PopStyleVar();
ImGui::Render(); ImGui::Render();
UIOverlay->update(); if (UIOverlay->update()) {
buildCommandBuffers();
}
#if defined(VK_USE_PLATFORM_ANDROID_KHR) #if defined(VK_USE_PLATFORM_ANDROID_KHR)
if (mouseButtons.left) { if (mouseButtons.left) {
@ -612,6 +612,13 @@ void VulkanExampleBase::updateOverlay()
#endif #endif
} }
void VulkanExampleBase::drawUI(const VkCommandBuffer commandBuffer)
{
if (settings.overlay) {
UIOverlay->draw(commandBuffer);
}
}
void VulkanExampleBase::prepareFrame() void VulkanExampleBase::prepareFrame()
{ {
// Acquire the next image from the swap chain // Acquire the next image from the swap chain
@ -627,38 +634,7 @@ void VulkanExampleBase::prepareFrame()
void VulkanExampleBase::submitFrame() void VulkanExampleBase::submitFrame()
{ {
bool submitOverlay = settings.overlay && UIOverlay->visible; VkResult res = swapChain.queuePresent(queue, currentBuffer, semaphores.renderComplete);
if (submitOverlay) {
// Wait for color attachment output to finish before rendering the text overlay
VkPipelineStageFlags stageFlags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
submitInfo.pWaitDstStageMask = &stageFlags;
// Set semaphores
// Wait for render complete semaphore
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = &semaphores.renderComplete;
// Signal ready with UI overlay complete semaphpre
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = &semaphores.overlayComplete;
// Submit current UI overlay command buffer
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &UIOverlay->cmdBuffers[currentBuffer];
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
// Reset stage mask
submitInfo.pWaitDstStageMask = &submitPipelineStages;
// Reset wait and signal semaphores for rendering next frame
// Wait for swap chain presentation to finish
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = &semaphores.presentComplete;
// Signal ready with offscreen semaphore
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = &semaphores.renderComplete;
}
VkResult res = swapChain.queuePresent(queue, currentBuffer, submitOverlay ? semaphores.overlayComplete : semaphores.renderComplete);
if (!((res == VK_SUCCESS) || (res == VK_SUBOPTIMAL_KHR))) { if (!((res == VK_SUCCESS) || (res == VK_SUBOPTIMAL_KHR))) {
if (res == VK_ERROR_OUT_OF_DATE_KHR) { if (res == VK_ERROR_OUT_OF_DATE_KHR) {
// Swap chain is no longer compatible with the surface and needs to be recreated // Swap chain is no longer compatible with the surface and needs to be recreated
@ -668,7 +644,6 @@ void VulkanExampleBase::submitFrame()
VK_CHECK_RESULT(res); VK_CHECK_RESULT(res);
} }
} }
VK_CHECK_RESULT(vkQueueWaitIdle(queue)); VK_CHECK_RESULT(vkQueueWaitIdle(queue));
} }
@ -809,7 +784,6 @@ VulkanExampleBase::~VulkanExampleBase()
vkDestroySemaphore(device, semaphores.presentComplete, nullptr); vkDestroySemaphore(device, semaphores.presentComplete, nullptr);
vkDestroySemaphore(device, semaphores.renderComplete, nullptr); vkDestroySemaphore(device, semaphores.renderComplete, nullptr);
vkDestroySemaphore(device, semaphores.overlayComplete, nullptr);
for (auto& fence : waitFences) { for (auto& fence : waitFences) {
vkDestroyFence(device, fence, nullptr); vkDestroyFence(device, fence, nullptr);
} }
@ -981,10 +955,6 @@ bool VulkanExampleBase::initVulkan()
// Create a semaphore used to synchronize command submission // Create a semaphore used to synchronize command submission
// Ensures that the image is not presented until all commands have been sumbitted and executed // Ensures that the image is not presented until all commands have been sumbitted and executed
VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &semaphores.renderComplete)); VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &semaphores.renderComplete));
// Create a semaphore used to synchronize command submission
// Ensures that the image is not presented until all commands for the UI overlay have been sumbitted and executed
// Will be inserted after the render complete semaphore if the UI overlay is enabled
VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &semaphores.overlayComplete));
// Set up submit info structure // Set up submit info structure
// Semaphores will stay the same during application lifetime // Semaphores will stay the same during application lifetime
@ -2166,6 +2136,12 @@ void VulkanExampleBase::windowResize()
} }
setupFrameBuffer(); setupFrameBuffer();
if ((width > 0.0f) && (height > 0.0f)) {
if (settings.overlay) {
UIOverlay->resize(width, height);
}
}
// Command buffers need to be recreated as they may store // Command buffers need to be recreated as they may store
// references to the recreated frame buffer // references to the recreated frame buffer
destroyCommandBuffers(); destroyCommandBuffers();
@ -2175,9 +2151,6 @@ void VulkanExampleBase::windowResize()
vkDeviceWaitIdle(device); vkDeviceWaitIdle(device);
if ((width > 0.0f) && (height > 0.0f)) { if ((width > 0.0f) && (height > 0.0f)) {
if (settings.overlay) {
UIOverlay->resize(width, height, frameBuffers);
}
camera.updateAspectRatio((float)width / (float)height); camera.updateAspectRatio((float)width / (float)height);
} }

View file

@ -128,8 +128,6 @@ protected:
VkSemaphore presentComplete; VkSemaphore presentComplete;
// Command buffer submission and execution // Command buffer submission and execution
VkSemaphore renderComplete; VkSemaphore renderComplete;
// UI overlay submission and execution
VkSemaphore overlayComplete;
} semaphores; } semaphores;
std::vector<VkFence> waitFences; std::vector<VkFence> waitFences;
public: public:
@ -391,6 +389,7 @@ public:
void renderFrame(); void renderFrame();
void updateOverlay(); void updateOverlay();
void drawUI(const VkCommandBuffer commandBuffer);
// Prepare the frame for workload submission // Prepare the frame for workload submission
// - Acquires the next image from the swap chain // - Acquires the next image from the swap chain