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)
{
this->createInfo = createInfo;
this->renderPass = createInfo.renderPass;
#if defined(__ANDROID__)
if (vks::android::screenDensity >= ACONFIGURATION_DENSITY_XXHIGH) {
@ -28,6 +27,7 @@ namespace vks
#endif
// Init ImGui
ImGui::CreateContext();
// Color scheme
ImGuiStyle& style = ImGui::GetStyle();
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.FontGlobalScale = scale;
cmdBuffers.resize(createInfo.framebuffers.size());
prepareResources();
if (createInfo.renderPass == VK_NULL_HANDLE) {
prepareRenderPass();
}
preparePipeline();
}
/** Free up all Vulkan resources acquired by the UI overlay */
UIOverlay::~UIOverlay()
{
ImGui::DestroyContext();
vertexBuffer.destroy();
indexBuffer.destroy();
vkDestroyImageView(createInfo.device->logicalDevice, fontView, nullptr);
@ -63,14 +59,7 @@ namespace vks
vkDestroyDescriptorSetLayout(createInfo.device->logicalDevice, descriptorSetLayout, nullptr);
vkDestroyDescriptorPool(createInfo.device->logicalDevice, descriptorPool, nullptr);
vkDestroyPipelineLayout(createInfo.device->logicalDevice, pipelineLayout, nullptr);
vkDestroyPipelineCache(createInfo.device->logicalDevice, pipelineCache, 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 */
@ -185,17 +174,6 @@ namespace vks
samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
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
std::vector<VkDescriptorPoolSize> poolSizes = {
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)
};
vkUpdateDescriptorSets(createInfo.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(createInfo.device->logicalDevice, &pipelineCacheCreateInfo, nullptr, &pipelineCache));
/** Prepare a separate pipeline for the UI overlay rendering decoupled from the main application */
void UIOverlay::preparePipeline(const VkPipelineCache pipelineCache, const VkRenderPass renderPass)
{
// Pipeline layout
// Push constants for UI rendering parameters
VkPushConstantRange pushConstantRange = vks::initializers::pushConstantRange(VK_SHADER_STAGE_VERTEX_BIT, sizeof(PushConstBlock), 0);
@ -236,14 +213,6 @@ namespace vks
pipelineLayoutCreateInfo.pPushConstantRanges = &pushConstantRange;
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
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState =
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());
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 =
vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0);
@ -326,183 +295,22 @@ namespace vks
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 */
void UIOverlay::update()
bool UIOverlay::update()
{
ImDrawData* imDrawData = ImGui::GetDrawData();
bool updateCmdBuffers = false;
if (!imDrawData) { return; };
if (!imDrawData) { return false; };
// 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
if ((vertexBufferSize == 0) || (indexBufferSize == 0)) {
return false;
}
// Vertex buffer
if ((vertexBuffer.buffer == VK_NULL_HANDLE) || (vertexCount != imDrawData->TotalVtxCount)) {
@ -542,35 +350,55 @@ namespace vks
vertexBuffer.flush();
indexBuffer.flush();
if (updateCmdBuffers) {
updateCommandBuffers();
}
return updateCmdBuffers;
}
void UIOverlay::resize(uint32_t width, uint32_t height, std::vector<VkFramebuffer> framebuffers)
void UIOverlay::draw(const VkCommandBuffer commandBuffer)
{
ImGuiIO& io = ImGui::GetIO();
io.DisplaySize = ImVec2((float)(width), (float)(height));
createInfo.width = width;
createInfo.height = height;
createInfo.framebuffers = framebuffers;
updateCommandBuffers();
}
ImDrawData* imDrawData = ImGui::GetDrawData();
int32_t vertexOffset = 0;
int32_t indexOffset = 0;
/** Submit the overlay command buffers to a queue */
void UIOverlay::submit(VkQueue queue, uint32_t bufferindex, VkSubmitInfo submitInfo)
{
if (!visible) {
if ((!imDrawData) || (imDrawData->CmdListsCount == 0)) {
return;
}
submitInfo.pCommandBuffers = &cmdBuffers[bufferindex];
submitInfo.commandBufferCount = 1;
ImGuiIO& io = ImGui::GetIO();
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));
VK_CHECK_RESULT(vkResetFences(createInfo.device->logicalDevice, 1, &fence));
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
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)

View file

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

View file

@ -199,9 +199,6 @@ void VulkanExampleBase::prepare()
// Setup default overlay creation info
overlayCreateInfo.device = vulkanDevice;
overlayCreateInfo.copyQueue = queue;
overlayCreateInfo.framebuffers = frameBuffers;
overlayCreateInfo.colorformat = swapChain.colorFormat;
overlayCreateInfo.depthformat = depthFormat;
overlayCreateInfo.width = width;
overlayCreateInfo.height = height;
// Virtual function call for example to customize overlay creation
@ -214,6 +211,7 @@ void VulkanExampleBase::prepare()
};
}
UIOverlay = new vks::UIOverlay(overlayCreateInfo);
UIOverlay->preparePipeline(pipelineCache, renderPass);
updateOverlay();
}
}
@ -603,7 +601,9 @@ void VulkanExampleBase::updateOverlay()
ImGui::PopStyleVar();
ImGui::Render();
UIOverlay->update();
if (UIOverlay->update()) {
buildCommandBuffers();
}
#if defined(VK_USE_PLATFORM_ANDROID_KHR)
if (mouseButtons.left) {
@ -612,6 +612,13 @@ void VulkanExampleBase::updateOverlay()
#endif
}
void VulkanExampleBase::drawUI(const VkCommandBuffer commandBuffer)
{
if (settings.overlay) {
UIOverlay->draw(commandBuffer);
}
}
void VulkanExampleBase::prepareFrame()
{
// Acquire the next image from the swap chain
@ -627,38 +634,7 @@ void VulkanExampleBase::prepareFrame()
void VulkanExampleBase::submitFrame()
{
bool submitOverlay = settings.overlay && UIOverlay->visible;
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);
VkResult res = swapChain.queuePresent(queue, currentBuffer, semaphores.renderComplete);
if (!((res == VK_SUCCESS) || (res == VK_SUBOPTIMAL_KHR))) {
if (res == VK_ERROR_OUT_OF_DATE_KHR) {
// 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(vkQueueWaitIdle(queue));
}
@ -809,7 +784,6 @@ VulkanExampleBase::~VulkanExampleBase()
vkDestroySemaphore(device, semaphores.presentComplete, nullptr);
vkDestroySemaphore(device, semaphores.renderComplete, nullptr);
vkDestroySemaphore(device, semaphores.overlayComplete, nullptr);
for (auto& fence : waitFences) {
vkDestroyFence(device, fence, nullptr);
}
@ -981,10 +955,6 @@ bool VulkanExampleBase::initVulkan()
// Create a semaphore used to synchronize command submission
// Ensures that the image is not presented until all commands have been sumbitted and executed
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
// Semaphores will stay the same during application lifetime
@ -2166,6 +2136,12 @@ void VulkanExampleBase::windowResize()
}
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
// references to the recreated frame buffer
destroyCommandBuffers();
@ -2175,9 +2151,6 @@ void VulkanExampleBase::windowResize()
vkDeviceWaitIdle(device);
if ((width > 0.0f) && (height > 0.0f)) {
if (settings.overlay) {
UIOverlay->resize(width, height, frameBuffers);
}
camera.updateAspectRatio((float)width / (float)height);
}

View file

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