Reworked UI overlay class (no longer using separate render pass + submits)
Updated to support ImGui 1.62 Refs #496
This commit is contained in:
parent
3bbf0d8f29
commit
8a61105ec6
4 changed files with 76 additions and 288 deletions
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue