Vulkan 学习笔记 02
上一篇文章主要介绍了在程序中编译shader的功能,这篇主要介绍一下如何集成imgui。主要参考官方的Vulkan例子。
在这里,我并没有使用官方提供的backend,算是从头自己按照官方代码实现的Vulkan渲染流程。在完成VulkanTutorial的例子后,我们开始添加imgui的功能。我选择的是imgui的docking分支,在实际使用中去掉了MultiViewPort的支持,因为该支持需要补全一些有关Vulkan的回调函数,不然在程序运行时会直接崩溃。
后续过程就是以现有的render pass为基础,创建一个新的pipeline,来绘制imgui的图像。具体初始化流程在initialize()函数中实现。开始运行后,main loop需要依次调用prepareContext、render以及在原本recoed command buffer时添加imgui的recordCommandBuffer函数调用。具体代码如下:
#pragma once
#include "runtime/core/math/math_type.h"
#include <volk.h>
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include <memory>
namespace ArchViz
{
class AssetManager;
class ConfigManager;
class VulkanInstance;
class VulkanDevice;
class VulkanTexture;
class VulkanShader;
class VulkanBuffer;
class VulkanUI
{
public:
void initialize();
void clear();
void prepareContext(float width, float height);
void render();
void recordCommandBuffer(VkCommandBuffer command_buffer, VkFramebuffer frame_buffer);
private:
void createRenderPass();
void createDescriptorSetLayout();
void createDescriptorSets();
void createPipelineLayout();
void createPipeline();
public:
struct PushConstantBlock
{
FVector2 scale;
FVector2 translate;
};
public:
GLFWwindow* m_window;
std::shared_ptr<AssetManager> m_asset_manager;
std::shared_ptr<ConfigManager> m_config_manager;
uint32_t m_image_count; // swap chain
VkFormat m_image_format;
std::shared_ptr<VulkanInstance> m_instance;
std::shared_ptr<VulkanDevice> m_device;
std::shared_ptr<VulkanTexture> m_font_texture;
std::shared_ptr<VulkanShader> m_shader;
std::shared_ptr<VulkanBuffer> m_vertex_buffer;
std::shared_ptr<VulkanBuffer> m_index_buffer;
uint32_t m_vertex_count;
uint32_t m_index_count;
VkPipelineCache m_pipeline_cache;
VkDescriptorPool m_descriptor_pool;
VkDescriptorSetLayout m_descriptor_set_layout;
VkDescriptorSet m_descriptor_set;
VkPipelineLayout m_pipeline_layout;
VkPipeline m_pipeline;
VkCommandPool m_command_pool;
VkCommandBuffer m_command_buffer;
VkRenderPass m_ui_pass;
PushConstantBlock m_push_const;
};
} // namespace ArchViz
#include "runtime/function/render/rhi/vulkan/vulkan_ui.h"
#include "runtime/function/render/rhi/vulkan/vulkan_buffer.h"
#include "runtime/function/render/rhi/vulkan/vulkan_buffer_utils.h"
#include "runtime/function/render/rhi/vulkan/vulkan_device.h"
#include "runtime/function/render/rhi/vulkan/vulkan_instance.h"
#include "runtime/function/render/rhi/vulkan/vulkan_shader.h"
#include "runtime/function/render/rhi/vulkan/vulkan_shader_utils.h"
#include "runtime/function/render/rhi/vulkan/vulkan_texture.h"
#include "runtime/function/render/rhi/vulkan/vulkan_texture_utils.h"
#include "runtime/function/render/rhi/vulkan/vulkan_utils.h"
#include "runtime/resource/asset_manager/asset_manager.h"
#include "runtime/resource/config_manager/config_manager.h"
#include "runtime/core/base/macro.h"
#include <GLFW/glfw3.h>
#define GLFW_INCLUDE_NONE
// #include "backends/imgui_impl_glfw.h"
// #include "backends/imgui_impl_vulkan.h"
#include "imgui.h"
#include <stdio.h>
#include <stdlib.h>
namespace ArchViz
{
void VulkanUI::initialize()
{
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
(void)io;
// io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
// io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
// io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows
// io.ConfigViewportsNoAutoMerge = true;
// io.ConfigViewportsNoTaskBarIcon = true;
// TODO : add multi viewport support
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset;
// Setup Dear ImGui style
ImGui::StyleColorsDark();
// ImGui::StyleColorsLight();
float scale_x, scale_y;
glfwGetWindowContentScale(m_window, &scale_x, &scale_y);
// When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.
ImGuiStyle& style = ImGui::GetStyle();
style.ScaleAllSizes(scale_x);
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
style.WindowRounding = 0.0f;
style.Colors[ImGuiCol_WindowBg].w = 1.0f;
}
// Load Fonts
// - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
// - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
// - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
// - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will
// call.
// - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering.
// - Read 'docs/FONTS.md' for more instructions and details.
// - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
auto font_path = m_config_manager->getRootFolder() / "asset-test/data/font/MiSans-Normal.ttf";
// io.Fonts->AddFontDefault();
ImFont* font = io.Fonts->AddFontFromFileTTF(font_path.generic_string().c_str(), 16.0f, nullptr, io.Fonts->GetGlyphRangesChineseSimplifiedCommon());
ASSERT(font);
unsigned char* font_data;
int tex_width, tex_height;
io.Fonts->GetTexDataAsRGBA32(&font_data, &tex_width, &tex_height);
VkDeviceSize imageSize = tex_width * tex_height * 4;
// Upload Fonts
m_font_texture = std::make_shared<VulkanTexture>();
m_font_texture->m_device = m_device;
m_font_texture->m_command_pool = m_command_pool;
m_font_texture->m_address_mode = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
m_font_texture->initialize(font_data, imageSize, VK_FORMAT_R8G8B8A8_UNORM, tex_width, tex_height);
m_vertex_buffer = std::make_shared<VulkanBuffer>();
m_index_buffer = std::make_shared<VulkanBuffer>();
m_vertex_buffer->device = m_device->m_device;
m_index_buffer->device = m_device->m_device;
// createRenderPass();
createDescriptorSetLayout();
createDescriptorSets();
createPipelineLayout();
createPipeline();
}
void VulkanUI::createRenderPass()
{
VkAttachmentDescription attachment {};
attachment.format = m_image_format;
attachment.samples = VK_SAMPLE_COUNT_1_BIT;
attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference color_attachment {};
color_attachment.attachment = 0;
color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &color_attachment;
VkSubpassDependency dependency {};
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
dependency.dstSubpass = 0;
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.srcAccessMask = 0;
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
VkRenderPassCreateInfo info {};
info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
info.attachmentCount = 1;
info.pAttachments = &attachment;
info.subpassCount = 1;
info.pSubpasses = &subpass;
info.dependencyCount = 1;
info.pDependencies = &dependency;
if (vkCreateRenderPass(m_device->m_device, &info, nullptr, &m_ui_pass) != VK_SUCCESS)
{
LOG_FATAL("failed to create ui pass!");
}
}
void VulkanUI::createDescriptorSetLayout()
{
VkDescriptorSetLayoutBinding sampler_layout_binding {};
sampler_layout_binding.binding = 0;
sampler_layout_binding.descriptorCount = 1;
sampler_layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
sampler_layout_binding.pImmutableSamplers = nullptr;
sampler_layout_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
std::array<VkDescriptorSetLayoutBinding, 1> bindings = {sampler_layout_binding};
VkDescriptorSetLayoutCreateInfo layout_info {};
layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layout_info.bindingCount = static_cast<uint32_t>(bindings.size());
layout_info.pBindings = bindings.data();
if (vkCreateDescriptorSetLayout(m_device->m_device, &layout_info, nullptr, &m_descriptor_set_layout) != VK_SUCCESS)
{
LOG_FATAL("failed to create descriptor set layout!");
}
}
void VulkanUI::createDescriptorSets()
{
VkDescriptorSetAllocateInfo alloc_info {};
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
alloc_info.descriptorPool = m_descriptor_pool;
alloc_info.descriptorSetCount = 1;
alloc_info.pSetLayouts = &m_descriptor_set_layout;
if (vkAllocateDescriptorSets(m_device->m_device, &alloc_info, &m_descriptor_set) != VK_SUCCESS)
{
LOG_FATAL("failed to allocate descriptor sets!");
}
VkDescriptorImageInfo image_info {};
image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
image_info.imageView = m_font_texture->m_view;
image_info.sampler = m_font_texture->m_sampler;
VkWriteDescriptorSet descriptor_write {};
descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptor_write.dstSet = m_descriptor_set;
descriptor_write.dstBinding = 0;
descriptor_write.descriptorCount = 1;
descriptor_write.pImageInfo = &image_info;
vkUpdateDescriptorSets(m_device->m_device, 1, &descriptor_write, 0, nullptr);
}
void VulkanUI::createPipelineLayout()
{
ShaderModuleConfig config;
config.m_vert_shader = "shader/glsl/imgui.vert";
config.m_frag_shader = "shader/glsl/imgui.frag";
m_shader = std::make_shared<VulkanShader>(config);
m_shader->m_device = m_device;
m_shader->m_config_manager = m_config_manager;
m_shader->m_asset_manager = m_asset_manager;
m_shader->initialize();
VkPushConstantRange push_constant_range {};
push_constant_range.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
push_constant_range.size = sizeof(PushConstantBlock);
push_constant_range.offset = 0;
VkPipelineLayoutCreateInfo pipeline_layout_create_info {};
pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipeline_layout_create_info.setLayoutCount = 1;
pipeline_layout_create_info.pSetLayouts = &m_descriptor_set_layout;
pipeline_layout_create_info.pushConstantRangeCount = 1;
pipeline_layout_create_info.pPushConstantRanges = &push_constant_range;
if (vkCreatePipelineLayout(m_device->m_device, &pipeline_layout_create_info, nullptr, &m_pipeline_layout) != VK_SUCCESS)
{
LOG_FATAL("failed to create ui pipeline layout");
}
}
void VulkanUI::createPipeline()
{
VkPipelineInputAssemblyStateCreateInfo input_assembly_state {};
input_assembly_state.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
input_assembly_state.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
input_assembly_state.flags = 0;
input_assembly_state.primitiveRestartEnable = VK_FALSE;
VkPipelineRasterizationStateCreateInfo rasterization {};
rasterization.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterization.polygonMode = VK_POLYGON_MODE_FILL;
rasterization.cullMode = VK_CULL_MODE_NONE;
rasterization.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rasterization.flags = 0;
rasterization.depthClampEnable = VK_FALSE;
rasterization.lineWidth = 1.0f;
VkPipelineColorBlendAttachmentState blend_attachment {};
blend_attachment.blendEnable = VK_TRUE;
blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
blend_attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
blend_attachment.colorBlendOp = VK_BLEND_OP_ADD;
blend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
blend_attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD;
VkPipelineColorBlendStateCreateInfo color_blend_state {};
color_blend_state.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
color_blend_state.attachmentCount = 1;
color_blend_state.pAttachments = &blend_attachment;
VkPipelineDepthStencilStateCreateInfo depth_stencil_state {};
depth_stencil_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
depth_stencil_state.depthTestEnable = VK_FALSE;
depth_stencil_state.depthWriteEnable = VK_FALSE;
depth_stencil_state.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
depth_stencil_state.back.compareOp = VK_COMPARE_OP_ALWAYS;
VkPipelineViewportStateCreateInfo viewport_state {};
viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewport_state.viewportCount = 1;
viewport_state.scissorCount = 1;
viewport_state.flags = 0;
VkPipelineMultisampleStateCreateInfo multisample_state {};
multisample_state.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisample_state.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
multisample_state.flags = 0;
std::vector<VkDynamicState> dynamic_state_enables = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
VkPipelineDynamicStateCreateInfo dynamic_state {};
dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamic_state.pDynamicStates = dynamic_state_enables.data();
dynamic_state.dynamicStateCount = static_cast<uint32_t>(dynamic_state_enables.size());
dynamic_state.flags = 0;
// std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages {};
VkVertexInputBindingDescription binding_description {};
binding_description.binding = 0;
binding_description.stride = sizeof(ImDrawVert);
binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
std::vector<VkVertexInputBindingDescription> vertex_input_bindings = {binding_description};
std::array<VkVertexInputAttributeDescription, 3> vertex_input_attributes {};
// Location 0: Position
vertex_input_attributes[0].binding = 0;
vertex_input_attributes[0].location = 0;
vertex_input_attributes[0].format = VK_FORMAT_R32G32_SFLOAT;
vertex_input_attributes[0].offset = offsetof(ImDrawVert, pos);
// Location 1: UV
vertex_input_attributes[1].binding = 0;
vertex_input_attributes[1].location = 1;
vertex_input_attributes[1].format = VK_FORMAT_R32G32_SFLOAT;
vertex_input_attributes[1].offset = offsetof(ImDrawVert, uv);
// Location 0: Color
vertex_input_attributes[2].binding = 0;
vertex_input_attributes[2].location = 2;
vertex_input_attributes[2].format = VK_FORMAT_R8G8B8A8_UNORM;
vertex_input_attributes[2].offset = offsetof(ImDrawVert, col);
VkPipelineVertexInputStateCreateInfo vertex_input_state {};
vertex_input_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertex_input_state.vertexBindingDescriptionCount = static_cast<uint32_t>(vertex_input_bindings.size());
vertex_input_state.pVertexBindingDescriptions = vertex_input_bindings.data();
vertex_input_state.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertex_input_attributes.size());
vertex_input_state.pVertexAttributeDescriptions = vertex_input_attributes.data();
VkGraphicsPipelineCreateInfo pipeline_create_info {};
pipeline_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipeline_create_info.layout = m_pipeline_layout;
pipeline_create_info.renderPass = m_ui_pass;
pipeline_create_info.flags = 0;
pipeline_create_info.basePipelineIndex = -1;
pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE;
pipeline_create_info.pInputAssemblyState = &input_assembly_state;
pipeline_create_info.pRasterizationState = &rasterization;
pipeline_create_info.pColorBlendState = &color_blend_state;
pipeline_create_info.pMultisampleState = &multisample_state;
pipeline_create_info.pViewportState = &viewport_state;
pipeline_create_info.pDepthStencilState = &depth_stencil_state;
pipeline_create_info.pDynamicState = &dynamic_state;
pipeline_create_info.stageCount = static_cast<uint32_t>(m_shader->m_stage_info.size());
pipeline_create_info.pStages = m_shader->m_stage_info.data();
pipeline_create_info.pVertexInputState = &vertex_input_state;
if (vkCreateGraphicsPipelines(m_device->m_device, m_pipeline_cache, 1, &pipeline_create_info, nullptr, &m_pipeline) != VK_SUCCESS)
{
LOG_FATAL("failed to create ui graphics pipeline");
}
}
void VulkanUI::prepareContext(float width, float height)
{
ImGuiIO& io = ImGui::GetIO();
#ifdef __MACH__
int w, h;
int fw, fh;
float scale_x, scale_y;
glfwGetWindowSize(m_window, &w, &h);
glfwGetFramebufferSize(m_window, &fw, &fh);
scale_x = (float)fw / (float)w;
scale_y = (float)fh / (float)h;
io.DisplaySize = ImVec2(width, height);
io.DisplayFramebufferScale = ImVec2(scale_x, scale_y);
int left = glfwGetMouseButton(m_window, GLFW_MOUSE_BUTTON_LEFT);
int right = glfwGetMouseButton(m_window, GLFW_MOUSE_BUTTON_RIGHT);
double x, y;
glfwGetCursorPos(m_window, &x, &y);
LOG_DEBUG("size: {}, {}, scale: {}, {}, pos: {}, {}", w, h, scale_x, scale_y, x, y);
io.MousePos = ImVec2(x * scale_x, y * scale_y);
io.MouseDown[0] = left;
io.MouseDown[1] = right;
#else
float scale_x, scale_y;
glfwGetWindowContentScale(m_window, &scale_x, &scale_y);
io.DisplaySize = ImVec2(width, height);
io.DisplayFramebufferScale = ImVec2(scale_x, scale_y);
int left = glfwGetMouseButton(m_window, GLFW_MOUSE_BUTTON_LEFT);
int right = glfwGetMouseButton(m_window, GLFW_MOUSE_BUTTON_RIGHT);
double x, y;
glfwGetCursorPos(m_window, &x, &y);
io.MousePos = ImVec2(x, y);
io.MouseDown[0] = left;
io.MouseDown[1] = right;
#endif
m_push_const.scale = {2.0f / io.DisplaySize.x, 2.0f / io.DisplaySize.y};
m_push_const.translate = {-1.0f, -1.0f};
ImGui::NewFrame();
}
void VulkanUI::render()
{
static bool opt_fullscreen = false;
static bool opt_padding = false;
static ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None;
dockspace_flags &= ~ImGuiDockNodeFlags_PassthruCentralNode;
ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking;
window_flags |= ImGuiWindowFlags_NoBackground;
ImGuiIO& io = ImGui::GetIO();
if (!opt_padding)
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::Begin("DockSpace Demo", nullptr, window_flags);
{
if (!opt_padding)
ImGui::PopStyleVar();
if (opt_fullscreen)
ImGui::PopStyleVar(2);
if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable)
{
ImGuiID dockspace_id = ImGui::GetID("MyDockSpace");
ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags);
}
if (ImGui::BeginMenuBar())
{
if (ImGui::BeginMenu("Options"))
{
// Disabling fullscreen would allow the window to be moved to the front of other windows,
// which we can't undo at the moment without finer window depth/z control.
// ImGui::MenuItem("Fullscreen", NULL, &opt_fullscreen);
ImGui::MenuItem("Padding", NULL, &opt_padding);
ImGui::Separator();
if (ImGui::MenuItem("Flag: NoSplit", "", (dockspace_flags & ImGuiDockNodeFlags_NoSplit) != 0))
{
dockspace_flags ^= ImGuiDockNodeFlags_NoSplit;
}
if (ImGui::MenuItem("Flag: NoResize", "", (dockspace_flags & ImGuiDockNodeFlags_NoResize) != 0))
{
dockspace_flags ^= ImGuiDockNodeFlags_NoResize;
}
if (ImGui::MenuItem("Flag: NoDockingInCentralNode", "", (dockspace_flags & ImGuiDockNodeFlags_NoDockingInCentralNode) != 0))
{
dockspace_flags ^= ImGuiDockNodeFlags_NoDockingInCentralNode;
}
if (ImGui::MenuItem("Flag: AutoHideTabBar", "", (dockspace_flags & ImGuiDockNodeFlags_AutoHideTabBar) != 0))
{
dockspace_flags ^= ImGuiDockNodeFlags_AutoHideTabBar;
}
if (ImGui::MenuItem("Flag: PassthruCentralNode", "", (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0, opt_fullscreen))
{
dockspace_flags ^= ImGuiDockNodeFlags_PassthruCentralNode;
}
ImGui::Separator();
ImGui::EndMenu();
}
ImGui::EndMenuBar();
}
}
ImGui::End();
// ImGui::DockSpaceOverViewport(ImGui::GetMainViewport());
ImGui::Begin("ArchViz");
{
ImGui::Text("Hello, world %d", 123);
}
ImGui::End();
ImGui::Begin(u8"ArchViz 测试");
{
ImGui::Text("Hello, world %d", 123);
}
ImGui::End();
ImGui::Render();
// Update and Render additional Platform Windows
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
}
}
void VulkanUI::recordCommandBuffer(VkCommandBuffer command_buffer, VkFramebuffer frame_buffer)
{
ImGuiIO& io = ImGui::GetIO();
ImDrawData* main_draw_data = ImGui::GetDrawData();
const bool main_is_minimized = (main_draw_data->DisplaySize.x <= 0.0f || main_draw_data->DisplaySize.y <= 0.0f);
// Note: Alignment is done inside buffer creation
VkDeviceSize vertex_buffer_size = main_draw_data->TotalVtxCount * sizeof(ImDrawVert);
VkDeviceSize index_buffer_size = main_draw_data->TotalIdxCount * sizeof(ImDrawIdx);
if ((vertex_buffer_size == 0) || (index_buffer_size == 0))
{
return;
}
// Vertex buffer
if ((m_vertex_buffer->buffer == VK_NULL_HANDLE) || (m_vertex_count != main_draw_data->TotalVtxCount))
{
// TODO : use fence or semphore to wait
m_device->wait();
m_vertex_buffer->unmap();
m_vertex_buffer->destroy();
m_vertex_buffer->size = vertex_buffer_size;
m_vertex_buffer->usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
m_vertex_buffer->property = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
VulkanBufferUtils::createBuffer(m_device, m_vertex_buffer->size, m_vertex_buffer->usage, m_vertex_buffer->property, m_vertex_buffer->buffer, m_vertex_buffer->memory);
// TODO : add alignment support
m_vertex_buffer->setupDescriptor();
m_vertex_count = main_draw_data->TotalVtxCount;
// m_vertex_buffer->map(vertex_buffer_size, 0);
m_vertex_buffer->map();
}
// Index buffer
if ((m_index_buffer->buffer == VK_NULL_HANDLE) || (m_index_count < main_draw_data->TotalIdxCount))
{
// TODO : use fence or semphore to wait
m_device->wait();
m_index_buffer->unmap();
m_index_buffer->destroy();
m_index_buffer->size = index_buffer_size;
m_index_buffer->usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
m_index_buffer->property = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
VulkanBufferUtils::createBuffer(m_device, m_index_buffer->size, m_index_buffer->usage, m_index_buffer->property, m_index_buffer->buffer, m_index_buffer->memory);
// TODO : add alignment support
m_index_buffer->setupDescriptor();
m_index_count = main_draw_data->TotalIdxCount;
// m_index_buffer->map(index_buffer_size, 0);
m_index_buffer->map();
}
// Upload data
ImDrawVert* vtx_dst = (ImDrawVert*)m_vertex_buffer->mapped;
ImDrawIdx* idx_dst = (ImDrawIdx*)m_index_buffer->mapped;
for (int n = 0; n < main_draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = main_draw_data->CmdLists[n];
size_t copy_vtx_size = cmd_list->VtxBuffer.Size * sizeof(ImDrawVert);
size_t copy_idx_size = cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx);
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, copy_vtx_size);
memcpy(idx_dst, cmd_list->IdxBuffer.Data, copy_idx_size);
vtx_dst += cmd_list->VtxBuffer.Size;
idx_dst += cmd_list->IdxBuffer.Size;
}
// Flush to make writes visible to GPU
m_vertex_buffer->flush();
m_index_buffer->flush();
// m_vertex_buffer->unmap();
// m_index_buffer->unmap();
// VkExtent2D extend {};
// extend.width = io.DisplaySize.x;
// extend.height = io.DisplaySize.y;
// VkRenderPassBeginInfo render_pass_info {};
// render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
// render_pass_info.renderPass = m_ui_pass;
// render_pass_info.framebuffer = frame_buffer;
// render_pass_info.renderArea.offset = {0, 0};
// render_pass_info.renderArea.extent = extend;
// std::array<VkClearValue, 2> clear_color {};
// clear_color[0].color = {{0.2f, 0.3f, 0.4f, 1.0f}};
// clear_color[1].depthStencil = {1.0f, 0};
// render_pass_info.clearValueCount = static_cast<uint32_t>(clear_color.size());
// render_pass_info.pClearValues = clear_color.data();
// vkCmdBeginRenderPass(command_buffer, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE);
{
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_layout, 0, 1, &m_descriptor_set, 0, nullptr);
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline);
VkViewport viewport {};
viewport.width = ImGui::GetIO().DisplaySize.x;
viewport.height = ImGui::GetIO().DisplaySize.y;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
vkCmdSetViewport(command_buffer, 0, 1, &viewport);
// UI scale and translate via push constants
m_push_const.scale = {2.0f / io.DisplaySize.x, 2.0f / io.DisplaySize.y};
m_push_const.translate = {-1.0f, -1.0f};
vkCmdPushConstants(command_buffer, m_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(PushConstantBlock), &m_push_const);
// Render commands
ImDrawData* main_draw_data = ImGui::GetDrawData();
int32_t vertex_offset = 0;
int32_t index_offset = 0;
if (main_draw_data->CmdListsCount > 0)
{
VkDeviceSize offsets[1] = {0};
vkCmdBindVertexBuffers(command_buffer, 0, 1, &m_vertex_buffer->buffer, offsets);
vkCmdBindIndexBuffer(command_buffer, m_index_buffer->buffer, 0, VK_INDEX_TYPE_UINT16);
for (int32_t i = 0; i < main_draw_data->CmdListsCount; i++)
{
const ImDrawList* cmd_list = main_draw_data->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(command_buffer, 0, 1, &scissorRect);
vkCmdDrawIndexed(command_buffer, pcmd->ElemCount, 1, index_offset, vertex_offset, 0);
index_offset += pcmd->ElemCount;
}
vertex_offset += cmd_list->VtxBuffer.Size;
}
}
}
// vkCmdEndRenderPass(command_buffer);
}
void VulkanUI::clear()
{
ImGui::DestroyContext();
vkDestroyPipeline(m_device->m_device, m_pipeline, nullptr);
vkDestroyPipelineLayout(m_device->m_device, m_pipeline_layout, nullptr);
vkDestroyDescriptorSetLayout(m_device->m_device, m_descriptor_set_layout, nullptr);
// vkDestroyRenderPass(m_device->m_device, m_ui_pass, nullptr);
m_index_buffer->destroy();
m_index_buffer.reset();
m_vertex_buffer->destroy();
m_vertex_buffer.reset();
m_shader->clear();
m_shader.reset();
m_font_texture->clear();
m_font_texture.reset();
}
} // namespace ArchViz