#include #include #include #include "vulkan/vulkan.h" #include "imgui/imgui.h" #include "VulkanDevice.hpp" #include "VulkanUtils.hpp" #include "VulkanTexture.hpp" #if defined(__ANDROID__) #include #endif #define GLM_FORCE_RADIANS #define GLM_FORCE_DEPTH_ZERO_TO_ONE #include #include struct UI { private: VkDevice device; public: Buffer vertexBuffer, indexBuffer; vks::Texture2D fontTexture; VkPipelineLayout pipelineLayout; VkPipeline pipeline; VkDescriptorPool descriptorPool; VkDescriptorSetLayout descriptorSetLayout; VkDescriptorSet descriptorSet; struct PushConstBlock { glm::vec2 scale; glm::vec2 translate; } pushConstBlock; UI(vks::VulkanDevice *vulkanDevice, VkRenderPass renderPass, VkQueue queue, VkPipelineCache pipelineCache, VkSampleCountFlagBits multiSampleCount) { this->device = vulkanDevice->logicalDevice; ImGui::CreateContext(); /* Font texture loading */ ImGuiIO& io = ImGui::GetIO(); unsigned char* fontData; int texWidth, texHeight; #if defined(__ANDROID__) float scale = (float)vks::android::screenDensity / (float)ACONFIGURATION_DENSITY_MEDIUM; AAsset* asset = AAssetManager_open(androidApp->activity->assetManager, "Roboto-Medium.ttf", AASSET_MODE_STREAMING); assert(asset); size_t size = AAsset_getLength(asset); assert(size > 0); char *fontAsset = new char[size]; AAsset_read(asset, fontAsset, size); AAsset_close(asset); io.Fonts->AddFontFromMemoryTTF(fontAsset, size, 14.0f * scale); delete[] fontAsset; #else std::string ttfFilePath = getAssetPath() + "/STXINWEI.TTF"; io.Fonts->AddFontFromFileTTF(ttfFilePath.data(), 16.0f,NULL, io.Fonts->GetGlyphRangesChineseSimplifiedCommon()); #endif io.Fonts->GetTexDataAsRGBA32(&fontData, &texWidth, &texHeight); fontTexture.loadFromBuffer(fontData, texWidth * texHeight * 4 * sizeof(char), VK_FORMAT_R8G8B8A8_UNORM, texWidth, texHeight, vulkanDevice, queue); /* Setup */ ImGuiStyle& style = ImGui::GetStyle(); style.FrameBorderSize = 0.0f; style.WindowBorderSize = 0.0f; /* Descriptor pool */ std::vector poolSizes = { { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1 } }; VkDescriptorPoolCreateInfo descriptorPoolCI{}; descriptorPoolCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; descriptorPoolCI.poolSizeCount = 1; descriptorPoolCI.pPoolSizes = poolSizes.data(); descriptorPoolCI.maxSets = 1; VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolCI, nullptr, &descriptorPool)); /* Descriptor set layout */ VkDescriptorSetLayoutBinding setLayoutBinding{ 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr }; VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI{}; descriptorSetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; descriptorSetLayoutCI.pBindings = &setLayoutBinding; descriptorSetLayoutCI.bindingCount = 1; VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &descriptorSetLayout)); /* Descriptor set */ VkDescriptorSetAllocateInfo descriptorSetAllocInfo{}; descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; descriptorSetAllocInfo.descriptorPool = descriptorPool; descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayout; descriptorSetAllocInfo.descriptorSetCount = 1; VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &descriptorSet)); VkWriteDescriptorSet writeDescriptorSet{}; writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writeDescriptorSet.descriptorCount = 1; writeDescriptorSet.dstSet = descriptorSet; writeDescriptorSet.dstBinding = 0; writeDescriptorSet.pImageInfo = &fontTexture.descriptor; vkUpdateDescriptorSets(device, 1, &writeDescriptorSet, 0, nullptr); /* Pipeline layout */ VkPushConstantRange pushConstantRange{ VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(PushConstBlock) }; VkPipelineLayoutCreateInfo pipelineLayoutCI{}; pipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipelineLayoutCI.pushConstantRangeCount = 1; pipelineLayoutCI.pPushConstantRanges = &pushConstantRange; pipelineLayoutCI.setLayoutCount = 1; pipelineLayoutCI.pSetLayouts = &descriptorSetLayout; pipelineLayoutCI.pushConstantRangeCount = 1; pipelineLayoutCI.pPushConstantRanges = &pushConstantRange; VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelineLayout)); /* Pipeline */ VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI{}; inputAssemblyStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; inputAssemblyStateCI.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; VkPipelineRasterizationStateCreateInfo rasterizationStateCI{}; rasterizationStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; rasterizationStateCI.polygonMode = VK_POLYGON_MODE_FILL; rasterizationStateCI.cullMode = VK_CULL_MODE_NONE; rasterizationStateCI.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; rasterizationStateCI.lineWidth = 1.0f; VkPipelineColorBlendAttachmentState blendAttachmentState{}; blendAttachmentState.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; blendAttachmentState.blendEnable = VK_TRUE; 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 colorBlendStateCI{}; colorBlendStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; colorBlendStateCI.attachmentCount = 1; colorBlendStateCI.pAttachments = &blendAttachmentState; VkPipelineDepthStencilStateCreateInfo depthStencilStateCI{}; depthStencilStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; depthStencilStateCI.depthTestEnable = VK_FALSE; depthStencilStateCI.depthWriteEnable = VK_FALSE; depthStencilStateCI.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; depthStencilStateCI.front = depthStencilStateCI.back; depthStencilStateCI.back.compareOp = VK_COMPARE_OP_ALWAYS; VkPipelineViewportStateCreateInfo viewportStateCI{}; viewportStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; viewportStateCI.viewportCount = 1; viewportStateCI.scissorCount = 1; VkPipelineMultisampleStateCreateInfo multisampleStateCI{}; multisampleStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; if (multiSampleCount >= VK_SAMPLE_COUNT_1_BIT) { multisampleStateCI.rasterizationSamples = multiSampleCount; } std::vector dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; VkPipelineDynamicStateCreateInfo dynamicStateCI{}; dynamicStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; dynamicStateCI.pDynamicStates = dynamicStateEnables.data(); dynamicStateCI.dynamicStateCount = static_cast(dynamicStateEnables.size()); VkVertexInputBindingDescription vertexInputBinding = { 0, 20, VK_VERTEX_INPUT_RATE_VERTEX }; std::vector vertexInputAttributes = { { 0, 0, VK_FORMAT_R32G32_SFLOAT, 0 }, { 1, 0, VK_FORMAT_R32G32_SFLOAT, sizeof(float) * 2 }, { 2, 0, VK_FORMAT_R8G8B8A8_UNORM, sizeof(float) * 4 }, }; VkPipelineVertexInputStateCreateInfo vertexInputStateCI{}; vertexInputStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; vertexInputStateCI.vertexBindingDescriptionCount = 1; vertexInputStateCI.pVertexBindingDescriptions = &vertexInputBinding; vertexInputStateCI.vertexAttributeDescriptionCount = static_cast(vertexInputAttributes.size()); vertexInputStateCI.pVertexAttributeDescriptions = vertexInputAttributes.data(); std::array shaderStages; VkGraphicsPipelineCreateInfo pipelineCI{}; pipelineCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; pipelineCI.layout = pipelineLayout; pipelineCI.renderPass = renderPass; pipelineCI.pInputAssemblyState = &inputAssemblyStateCI; pipelineCI.pVertexInputState = &vertexInputStateCI; pipelineCI.pRasterizationState = &rasterizationStateCI; pipelineCI.pColorBlendState = &colorBlendStateCI; pipelineCI.pMultisampleState = &multisampleStateCI; pipelineCI.pViewportState = &viewportStateCI; pipelineCI.pDepthStencilState = &depthStencilStateCI; pipelineCI.pDynamicState = &dynamicStateCI; pipelineCI.stageCount = static_cast(shaderStages.size()); pipelineCI.pStages = shaderStages.data(); pipelineCI.layout = pipelineLayout; std::string uiVertShaderPath = getAssetPath() + "shaders/ui.vert.spv"; std::string uiFragShaderPath = getAssetPath() + "shaders/ui.frag.spv"; shaderStages = { loadShader(device, uiVertShaderPath.data(), VK_SHADER_STAGE_VERTEX_BIT), loadShader(device, uiFragShaderPath.data(), VK_SHADER_STAGE_FRAGMENT_BIT) }; VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline)); for (auto shaderStage : shaderStages) { vkDestroyShaderModule(device, shaderStage.module, nullptr); } } ~UI() { ImGui::DestroyContext(); vertexBuffer.destroy(); indexBuffer.destroy(); vkDestroyPipeline(device, pipeline, nullptr); vkDestroyPipelineLayout(device, pipelineLayout, nullptr); vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); vkDestroyDescriptorPool(device, descriptorPool, nullptr); } void draw(VkCommandBuffer cmdBuffer) { vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); vkCmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); const VkDeviceSize offsets[1] = { 0 }; vkCmdBindVertexBuffers(cmdBuffer, 0, 1, &vertexBuffer.buffer, offsets); vkCmdBindIndexBuffer(cmdBuffer, indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT16); vkCmdPushConstants(cmdBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(UI::PushConstBlock), &pushConstBlock); 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(cmdBuffer, 0, 1, &scissorRect); vkCmdDrawIndexed(cmdBuffer, pcmd->ElemCount, 1, indexOffset, vertexOffset, 0); indexOffset += pcmd->ElemCount; } vertexOffset += cmd_list->VtxBuffer.Size; } } template bool checkbox(const char* caption, T *value) { bool val = (*value == 1); bool res = ImGui::Checkbox(caption, &val); *value = val; return res; } bool header(const char *caption) { return ImGui::CollapsingHeader(caption, ImGuiTreeNodeFlags_DefaultOpen); } bool slider(const char* caption, float* value, float min, float max) { return ImGui::SliderFloat(caption, value, min, max); } bool combo(const char *caption, int32_t *itemindex, std::vector items) { if (items.empty()) { return false; } std::vector charitems; charitems.reserve(items.size()); for (size_t i = 0; i < items.size(); i++) { charitems.push_back(items[i].c_str()); } uint32_t itemCount = static_cast(charitems.size()); return ImGui::Combo(caption, itemindex, &charitems[0], itemCount, itemCount); } bool combo(const char *caption, std::string &selectedkey, std::map items) { bool selectionChanged = false; if (ImGui::BeginCombo(caption, selectedkey.c_str())) { for (auto it = items.begin(); it != items.end(); ++it) { const bool isSelected = it->first == selectedkey; if (ImGui::Selectable(it->first.c_str(), isSelected)) { selectionChanged = it->first != selectedkey; selectedkey = it->first; } if (isSelected) { ImGui::SetItemDefaultFocus(); } } ImGui::EndCombo(); } return selectionChanged; } bool button(const char *caption) { return ImGui::Button(caption); } bool beginChild(const char* caption, ImVec2 size, bool border) { return ImGui::BeginChild(caption, size, border); } void endChild() { return ImGui::EndChild(); } // menu GUI bool beginMainMenuBar() { return ImGui::BeginMainMenuBar(); } bool beginMenu(const char* caption) { return ImGui::BeginMenu(caption); } bool menuItem(const char* caption) { return ImGui::MenuItem(caption); } void endMenu() { return ImGui::EndMenu(); } void endMainMenuBar() { return ImGui::EndMainMenuBar(); } void text(const char *formatstr, ...) { va_list args; va_start(args, formatstr); ImGui::TextV(formatstr, args); va_end(args); } };