commit ac77711dd16331d9a8d38e763fee2bb4991262da Author: 54108 <3318195572@qq.com> Date: Mon Sep 16 01:26:08 2024 +0800 添加项目基础结构,包括CMake配置、头文件和实现文件,新增.gitignore和README文档 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d73e005 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.cache +build \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..108bd0b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,41 @@ +cmake_minimum_required(VERSION 3.5) +project(atom C CXX) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Debug CACHE STRING "" FORCE) +endif() + +set (CMAKE_CXX_STANDARD 23) +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DVK_PROTOTYPES") +set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DVK_PROTOTYPES") +set (CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# GLFW +set(GLFW_DIR glfw) # Set this to point to an up-to-date GLFW repo +option(GLFW_BUILD_EXAMPLES "Build the GLFW example programs" OFF) +option(GLFW_BUILD_TESTS "Build the GLFW test programs" OFF) +option(GLFW_BUILD_DOCS "Build the GLFW documentation" OFF) +option(GLFW_INSTALL "Generate installation target" OFF) +option(GLFW_DOCUMENT_INTERNALS "Include internals in documentation" OFF) +add_subdirectory(${GLFW_DIR} binary_dir EXCLUDE_FROM_ALL) +include_directories(${GLFW_DIR}/include) + +# Dear ImGui +set(IMGUI_DIR imgui) +include_directories(${IMGUI_DIR} ${IMGUI_DIR}/backends) + +# Libraries +find_package(Vulkan REQUIRED) +#find_library(VULKAN_LIBRARY + #NAMES vulkan vulkan-1) +#set(LIBRARIES "glfw;${VULKAN_LIBRARY}") +set(LIBRARIES "glfw;Vulkan::Vulkan") + +# Use vulkan headers from glfw: +include_directories(${GLFW_DIR}/deps) + +file(GLOB sources *.cpp) + +add_executable(atom ${sources} ${IMGUI_DIR}/backends/imgui_impl_glfw.cpp ${IMGUI_DIR}/backends/imgui_impl_vulkan.cpp ${IMGUI_DIR}/imgui.cpp ${IMGUI_DIR}/imgui_draw.cpp ${IMGUI_DIR}/imgui_demo.cpp ${IMGUI_DIR}/imgui_tables.cpp ${IMGUI_DIR}/imgui_widgets.cpp) +target_link_libraries(atom ${LIBRARIES}) +target_compile_definitions(atom PUBLIC -DImTextureID=ImU64) diff --git a/HYWenHei-85W.ttf b/HYWenHei-85W.ttf new file mode 100644 index 0000000..ebcab43 Binary files /dev/null and b/HYWenHei-85W.ttf differ diff --git a/atom.cpp b/atom.cpp new file mode 100644 index 0000000..a41d6b7 --- /dev/null +++ b/atom.cpp @@ -0,0 +1,77 @@ +#include "atom.h" +#include "imgui.h" + +void InitStyle(ImGuiStyle &style) +{ + ImVec4 *colors = style.Colors; + colors[ImGuiCol_FrameBg] = ImVec4(0.16f, 0.16f, 0.17f, 1.00f); + colors[ImGuiCol_FrameBgHovered] = ImVec4(0.37f, 0.36f, 0.36f, 102.00f); + colors[ImGuiCol_FrameBgActive] = ImVec4(0.10f, 0.10f, 0.10f, 171.00f); + colors[ImGuiCol_TitleBgActive] = ImVec4(0.20f, 0.20f, 0.20f, 255.00f); + colors[ImGuiCol_CheckMark] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f); + colors[ImGuiCol_SliderGrab] = ImVec4(0.64f, 0.64f, 0.64f, 1.00f); + colors[ImGuiCol_SliderGrabActive] = ImVec4(0.31f, 0.31f, 0.31f, 1.00f); + colors[ImGuiCol_Button] = ImVec4(0.22f, 0.22f, 0.22f, 0.40f); + colors[ImGuiCol_ButtonHovered] = ImVec4(0.29f, 0.29f, 0.29f, 1.00f); + colors[ImGuiCol_ButtonActive] = ImVec4(0.13f, 0.13f, 0.13f, 1.00f); + colors[ImGuiCol_Header] = ImVec4(0.45f, 0.45f, 0.45f, 0.31f); + colors[ImGuiCol_HeaderHovered] = ImVec4(0.55f, 0.55f, 0.55f, 0.80f); + colors[ImGuiCol_HeaderActive] = ImVec4(0.09f, 0.09f, 0.09f, 1.00f); + colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.20f); + colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.46f, 0.46f, 0.46f, 0.67f); + colors[ImGuiCol_ResizeGripActive] = ImVec4(0.17f, 0.17f, 0.17f, 0.95f); + colors[ImGuiCol_SeparatorActive] = ImVec4(0.42f, 0.42f, 0.42f, 1.00f); + colors[ImGuiCol_SeparatorHovered] = ImVec4(0.50f, 0.50f, 0.50f, 0.78f); + colors[ImGuiCol_TabHovered] = ImVec4(0.45f, 0.45f, 0.45f, 0.80f); + colors[ImGuiCol_TabActive] = ImVec4(0.28f, 0.28f, 0.28f, 1.00f); + colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.19f, 0.19f, 0.19f, 1.00f); + colors[ImGuiCol_DockingPreview] = ImVec4(0.51f, 0.51f, 0.51f, 0.70f); + colors[ImGuiCol_Tab] = ImVec4(0.21f, 0.21f, 0.21f, 0.86f); + colors[ImGuiCol_TabUnfocused] = ImVec4(0.15f, 0.15f, 0.15f, 0.97f); + colors[ImGuiCol_NavHighlight] = ImVec4(1.00f, 0.40f, 0.13f, 1.00f); + colors[ImGuiCol_TextSelectedBg] = ImVec4(0.45f, 1.00f, 0.85f, 0.35f); + + style.WindowRounding = 4; + style.FrameRounding = 4; + style.GrabRounding = 3; + style.ScrollbarSize = 7; + style.ScrollbarRounding = 0; +} + +void DrawTitleBar(const char *title, bool &p_open) +{ + ImGuiWindow *window = ImGui::GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiIO &io = ImGui::GetIO(); + (void)io; + // 绘制左上角的文字 + ImGui::Text("%s", title); + // ImGui::SameLine(); + // ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); + + // 计算关闭按钮位置 + float button_padding = 5.0f; // 关闭按钮距离边框的距离 + ImVec2 close_pos = ImVec2(window->Pos.x + window->Size.x - ImGui::GetStyle().FramePadding.x - ImGui::GetFrameHeight() - button_padding, window->Pos.y + ImGui::GetStyle().FramePadding.y + button_padding); + ImGui::SetCursorScreenPos(close_pos); + + // 绘制关闭按钮 + bool close_clicked = ImGui::InvisibleButton("##close_button", ImVec2(ImGui::GetFrameHeight(), ImGui::GetFrameHeight())); + if (ImGui::IsItemHovered()) + { + ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); // 更改鼠标光标 + ImGui::SetTooltip("关闭窗口"); + } + + // 绘制关闭按钮的图标(一个简单的'X') + ImDrawList *draw_list = ImGui::GetWindowDrawList(); + ImVec2 close_center = ImVec2(close_pos.x + ImGui::GetFrameHeight() * 0.5f, close_pos.y + ImGui::GetFrameHeight() * 0.5f); + float cross_extent = ImGui::GetFrameHeight() * 0.3f * 0.7071f; + draw_list->AddLine(ImVec2(close_center.x - cross_extent, close_center.y - cross_extent), ImVec2(close_center.x + cross_extent, close_center.y + cross_extent), IM_COL32(255, 255, 255, 255), 1.0f); + draw_list->AddLine(ImVec2(close_center.x + cross_extent, close_center.y - cross_extent), ImVec2(close_center.x - cross_extent, close_center.y + cross_extent), IM_COL32(255, 255, 255, 255), 1.0f); + + // 如果关闭按钮被点击,将布尔值设置为 false + if (close_clicked) + p_open = false; +} \ No newline at end of file diff --git a/atom.h b/atom.h new file mode 100644 index 0000000..aab8516 --- /dev/null +++ b/atom.h @@ -0,0 +1,20 @@ +#ifndef ATOM_H +#define ATOM_H + +#include "imgui.h" +#include "imgui_internal.h" +#include "imgui_impl_glfw.h" +#include "imgui_impl_vulkan.h" +#include "imstb_truetype.h" +#include <iostream> +#include <vector> +#include <cstdlib> +#define GLFW_INCLUDE_NONE +#define GLFW_INCLUDE_VULKAN +#include <GLFW/glfw3.h> + +void InitStyle(ImGuiStyle &style); + +void DrawTitleBar(const char *title, bool &p_open); + +#endif \ No newline at end of file diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..f3f9b13 --- /dev/null +++ b/main.cpp @@ -0,0 +1,661 @@ +// Dear ImGui: standalone example application for Glfw + Vulkan + +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +// Important note to the reader who wish to integrate imgui_impl_vulkan.cpp/.h in their own engine/app. +// - Common ImGui_ImplVulkan_XXX functions and structures are used to interface with imgui_impl_vulkan.cpp/.h. +// You will use those if you want to use this rendering backend in your engine/app. +// - Helper ImGui_ImplVulkanH_XXX functions and structures are only used by this example (main.cpp) and by +// the backend itself (imgui_impl_vulkan.cpp), but should PROBABLY NOT be used by your own engine/app code. +// Read comments in imgui_impl_vulkan.h. + +#include "atom.h" +#include "imgui.h" +#include <vector> + +// Volk headers +#ifdef IMGUI_IMPL_VULKAN_USE_VOLK +#define VOLK_IMPLEMENTATION +#include <volk.h> +#endif + +// [Win32] Our example includes a copy of glfw3.lib pre-compiled with VS2010 to maximize ease of testing and compatibility with old VS compilers. +// To link with VS2010-era libraries, VS2015+ requires linking with legacy_stdio_definitions.lib, which we do using this pragma. +// Your own project should not be affected, as you are likely to link with a newer binary of GLFW that is adequate for your version of Visual Studio. +#if defined(_MSC_VER) && (_MSC_VER >= 1900) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) +#pragma comment(lib, "legacy_stdio_definitions") +#endif + +// #define APP_USE_UNLIMITED_FRAME_RATE +#ifdef _DEBUG +#define APP_USE_VULKAN_DEBUG_REPORT +#endif + +// Data +static VkAllocationCallbacks *g_Allocator = nullptr; +static VkInstance g_Instance = VK_NULL_HANDLE; +static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE; +static VkDevice g_Device = VK_NULL_HANDLE; +static uint32_t g_QueueFamily = (uint32_t)-1; +static VkQueue g_Queue = VK_NULL_HANDLE; +static VkDebugReportCallbackEXT g_DebugReport = VK_NULL_HANDLE; +static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; +static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; + +static ImGui_ImplVulkanH_Window g_MainWindowData; +static int g_MinImageCount = 2; +static bool g_SwapChainRebuild = false; + +static void glfw_error_callback(int error, const char *description) +{ + fprintf(stderr, "GLFW Error %d: %s\n", error, description); +} +static void check_vk_result(VkResult err) +{ + if (err == 0) + return; + fprintf(stderr, "[vulkan] Error: VkResult = %d\n", err); + if (err < 0) + abort(); +} + +#ifdef APP_USE_VULKAN_DEBUG_REPORT +static VKAPI_ATTR VkBool32 VKAPI_CALL debug_report(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char *pLayerPrefix, const char *pMessage, void *pUserData) +{ + (void)flags; + (void)object; + (void)location; + (void)messageCode; + (void)pUserData; + (void)pLayerPrefix; // Unused arguments + fprintf(stderr, "[vulkan] Debug report from ObjectType: %i\nMessage: %s\n\n", objectType, pMessage); + return VK_FALSE; +} +#endif // APP_USE_VULKAN_DEBUG_REPORT + +static bool IsExtensionAvailable(const ImVector<VkExtensionProperties> &properties, const char *extension) +{ + for (const VkExtensionProperties &p : properties) + if (strcmp(p.extensionName, extension) == 0) + return true; + return false; +} + +static VkPhysicalDevice SetupVulkan_SelectPhysicalDevice() +{ + uint32_t gpu_count; + VkResult err = vkEnumeratePhysicalDevices(g_Instance, &gpu_count, nullptr); + check_vk_result(err); + IM_ASSERT(gpu_count > 0); + + ImVector<VkPhysicalDevice> gpus; + gpus.resize(gpu_count); + err = vkEnumeratePhysicalDevices(g_Instance, &gpu_count, gpus.Data); + check_vk_result(err); + + // If a number >1 of GPUs got reported, find discrete GPU if present, or use first one available. This covers + // most common cases (multi-gpu/integrated+dedicated graphics). Handling more complicated setups (multiple + // dedicated GPUs) is out of scope of this sample. + for (VkPhysicalDevice &device : gpus) + { + VkPhysicalDeviceProperties properties; + vkGetPhysicalDeviceProperties(device, &properties); + if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) + return device; + } + + // Use first GPU (Integrated) is a Discrete one is not available. + if (gpu_count > 0) + return gpus[0]; + return VK_NULL_HANDLE; +} + +static void SetupVulkan(ImVector<const char *> instance_extensions) +{ + VkResult err; +#ifdef IMGUI_IMPL_VULKAN_USE_VOLK + volkInitialize(); +#endif + + // Create Vulkan Instance + { + VkInstanceCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + + // Enumerate available extensions + uint32_t properties_count; + ImVector<VkExtensionProperties> properties; + vkEnumerateInstanceExtensionProperties(nullptr, &properties_count, nullptr); + properties.resize(properties_count); + err = vkEnumerateInstanceExtensionProperties(nullptr, &properties_count, properties.Data); + check_vk_result(err); + + // Enable required extensions + if (IsExtensionAvailable(properties, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) + instance_extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); +#ifdef VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME + if (IsExtensionAvailable(properties, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)) + { + instance_extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); + create_info.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; + } +#endif + + // Enabling validation layers +#ifdef APP_USE_VULKAN_DEBUG_REPORT + const char *layers[] = {"VK_LAYER_KHRONOS_validation"}; + create_info.enabledLayerCount = 1; + create_info.ppEnabledLayerNames = layers; + instance_extensions.push_back("VK_EXT_debug_report"); +#endif + + // Create Vulkan Instance + create_info.enabledExtensionCount = (uint32_t)instance_extensions.Size; + create_info.ppEnabledExtensionNames = instance_extensions.Data; + err = vkCreateInstance(&create_info, g_Allocator, &g_Instance); + check_vk_result(err); +#ifdef IMGUI_IMPL_VULKAN_USE_VOLK + volkLoadInstance(g_Instance); +#endif + + // Setup the debug report callback +#ifdef APP_USE_VULKAN_DEBUG_REPORT + auto f_vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkCreateDebugReportCallbackEXT"); + IM_ASSERT(f_vkCreateDebugReportCallbackEXT != nullptr); + VkDebugReportCallbackCreateInfoEXT debug_report_ci = {}; + debug_report_ci.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; + debug_report_ci.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; + debug_report_ci.pfnCallback = debug_report; + debug_report_ci.pUserData = nullptr; + err = f_vkCreateDebugReportCallbackEXT(g_Instance, &debug_report_ci, g_Allocator, &g_DebugReport); + check_vk_result(err); +#endif + } + + // Select Physical Device (GPU) + g_PhysicalDevice = SetupVulkan_SelectPhysicalDevice(); + + // Select graphics queue family + { + uint32_t count; + vkGetPhysicalDeviceQueueFamilyProperties(g_PhysicalDevice, &count, nullptr); + VkQueueFamilyProperties *queues = (VkQueueFamilyProperties *)malloc(sizeof(VkQueueFamilyProperties) * count); + vkGetPhysicalDeviceQueueFamilyProperties(g_PhysicalDevice, &count, queues); + for (uint32_t i = 0; i < count; i++) + if (queues[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) + { + g_QueueFamily = i; + break; + } + free(queues); + IM_ASSERT(g_QueueFamily != (uint32_t)-1); + } + + // Create Logical Device (with 1 queue) + { + ImVector<const char *> device_extensions; + device_extensions.push_back("VK_KHR_swapchain"); + + // Enumerate physical device extension + uint32_t properties_count; + ImVector<VkExtensionProperties> properties; + vkEnumerateDeviceExtensionProperties(g_PhysicalDevice, nullptr, &properties_count, nullptr); + properties.resize(properties_count); + vkEnumerateDeviceExtensionProperties(g_PhysicalDevice, nullptr, &properties_count, properties.Data); +#ifdef VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME + if (IsExtensionAvailable(properties, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME)) + device_extensions.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); +#endif + + const float queue_priority[] = {1.0f}; + VkDeviceQueueCreateInfo queue_info[1] = {}; + queue_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_info[0].queueFamilyIndex = g_QueueFamily; + queue_info[0].queueCount = 1; + queue_info[0].pQueuePriorities = queue_priority; + VkDeviceCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + create_info.queueCreateInfoCount = sizeof(queue_info) / sizeof(queue_info[0]); + create_info.pQueueCreateInfos = queue_info; + create_info.enabledExtensionCount = (uint32_t)device_extensions.Size; + create_info.ppEnabledExtensionNames = device_extensions.Data; + err = vkCreateDevice(g_PhysicalDevice, &create_info, g_Allocator, &g_Device); + check_vk_result(err); + vkGetDeviceQueue(g_Device, g_QueueFamily, 0, &g_Queue); + } + + // Create Descriptor Pool + // The example only requires a single combined image sampler descriptor for the font image and only uses one descriptor set (for that) + // If you wish to load e.g. additional textures you may need to alter pools sizes. + { + VkDescriptorPoolSize pool_sizes[] = + { + {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1}, + }; + VkDescriptorPoolCreateInfo pool_info = {}; + pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; + pool_info.maxSets = 1; + pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes); + pool_info.pPoolSizes = pool_sizes; + err = vkCreateDescriptorPool(g_Device, &pool_info, g_Allocator, &g_DescriptorPool); + check_vk_result(err); + } +} + +// All the ImGui_ImplVulkanH_XXX structures/functions are optional helpers used by the demo. +// Your real engine/app may not use them. +static void SetupVulkanWindow(ImGui_ImplVulkanH_Window *wd, VkSurfaceKHR surface, int width, int height) +{ + wd->Surface = surface; + + // Check for WSI support + VkBool32 res; + vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, wd->Surface, &res); + if (res != VK_TRUE) + { + fprintf(stderr, "Error no WSI support on physical device 0\n"); + exit(-1); + } + + // Select Surface Format + const VkFormat requestSurfaceImageFormat[] = {VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM}; + const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; + wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace); + + // Select Present Mode +#ifdef APP_USE_UNLIMITED_FRAME_RATE + VkPresentModeKHR present_modes[] = {VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR}; +#else + VkPresentModeKHR present_modes[] = {VK_PRESENT_MODE_FIFO_KHR}; +#endif + wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_modes[0], IM_ARRAYSIZE(present_modes)); + // printf("[vulkan] Selected PresentMode = %d\n", wd->PresentMode); + + // Create SwapChain, RenderPass, Framebuffer, etc. + IM_ASSERT(g_MinImageCount >= 2); + ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, width, height, g_MinImageCount); +} + +static void CleanupVulkan() +{ + vkDestroyDescriptorPool(g_Device, g_DescriptorPool, g_Allocator); + +#ifdef APP_USE_VULKAN_DEBUG_REPORT + // Remove the debug report callback + auto f_vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkDestroyDebugReportCallbackEXT"); + f_vkDestroyDebugReportCallbackEXT(g_Instance, g_DebugReport, g_Allocator); +#endif // APP_USE_VULKAN_DEBUG_REPORT + + vkDestroyDevice(g_Device, g_Allocator); + vkDestroyInstance(g_Instance, g_Allocator); +} + +static void CleanupVulkanWindow() +{ + ImGui_ImplVulkanH_DestroyWindow(g_Instance, g_Device, &g_MainWindowData, g_Allocator); +} + +static void FrameRender(ImGui_ImplVulkanH_Window *wd, ImDrawData *draw_data) +{ + VkResult err; + + VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore; + VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; + err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); + if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) + { + g_SwapChainRebuild = true; + return; + } + check_vk_result(err); + + ImGui_ImplVulkanH_Frame *fd = &wd->Frames[wd->FrameIndex]; + { + err = vkWaitForFences(g_Device, 1, &fd->Fence, VK_TRUE, UINT64_MAX); // wait indefinitely instead of periodically checking + check_vk_result(err); + + err = vkResetFences(g_Device, 1, &fd->Fence); + check_vk_result(err); + } + { + err = vkResetCommandPool(g_Device, fd->CommandPool, 0); + check_vk_result(err); + VkCommandBufferBeginInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + err = vkBeginCommandBuffer(fd->CommandBuffer, &info); + check_vk_result(err); + } + { + VkRenderPassBeginInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + info.renderPass = wd->RenderPass; + info.framebuffer = fd->Framebuffer; + info.renderArea.extent.width = wd->Width; + info.renderArea.extent.height = wd->Height; + info.clearValueCount = 1; + info.pClearValues = &wd->ClearValue; + vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE); + } + + // Record dear imgui primitives into command buffer + ImGui_ImplVulkan_RenderDrawData(draw_data, fd->CommandBuffer); + + // Submit command buffer + vkCmdEndRenderPass(fd->CommandBuffer); + { + VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkSubmitInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + info.waitSemaphoreCount = 1; + info.pWaitSemaphores = &image_acquired_semaphore; + info.pWaitDstStageMask = &wait_stage; + info.commandBufferCount = 1; + info.pCommandBuffers = &fd->CommandBuffer; + info.signalSemaphoreCount = 1; + info.pSignalSemaphores = &render_complete_semaphore; + + err = vkEndCommandBuffer(fd->CommandBuffer); + check_vk_result(err); + err = vkQueueSubmit(g_Queue, 1, &info, fd->Fence); + check_vk_result(err); + } +} + +static void +FramePresent(ImGui_ImplVulkanH_Window *wd) +{ + if (g_SwapChainRebuild) + return; + VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; + VkPresentInfoKHR info = {}; + info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + info.waitSemaphoreCount = 1; + info.pWaitSemaphores = &render_complete_semaphore; + info.swapchainCount = 1; + info.pSwapchains = &wd->Swapchain; + info.pImageIndices = &wd->FrameIndex; + VkResult err = vkQueuePresentKHR(g_Queue, &info); + if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) + { + g_SwapChainRebuild = true; + return; + } + check_vk_result(err); + wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->SemaphoreCount; // Now we can use the next set of semaphores +} + +// Main code +int main(int, char **) +{ + glfwSetErrorCallback(glfw_error_callback); + if (!glfwInit()) + return 1; + + // 设置 offscreen context 的标志位 + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + + // Create window with Vulkan context + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + GLFWwindow *window = glfwCreateWindow(1280, 720, "atom", nullptr, nullptr); + if (!glfwVulkanSupported()) + { + printf("GLFW: Vulkan Not Supported\n"); + return 1; + } + + ImVector<const char *> extensions; + uint32_t extensions_count = 0; + const char **glfw_extensions = glfwGetRequiredInstanceExtensions(&extensions_count); + for (uint32_t i = 0; i < extensions_count; i++) + extensions.push_back(glfw_extensions[i]); + SetupVulkan(extensions); + + // Create Window Surface + VkSurfaceKHR surface; + VkResult err = glfwCreateWindowSurface(g_Instance, window, g_Allocator, &surface); + check_vk_result(err); + + // Create Framebuffers + int w, h; + glfwGetFramebufferSize(window, &w, &h); + ImGui_ImplVulkanH_Window *wd = &g_MainWindowData; + SetupVulkanWindow(wd, surface, w, h); + + // Setup Dear ImGui context + 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; + + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + // ImGui::StyleColorsLight(); + + bool window_is_open = true; + + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle &style = ImGui::GetStyle(); + InitStyle(style); + + // Setup Platform/Renderer backends + ImGui_ImplGlfw_InitForVulkan(window, true); + ImGui_ImplVulkan_InitInfo init_info = {}; + init_info.Instance = g_Instance; + init_info.PhysicalDevice = g_PhysicalDevice; + init_info.Device = g_Device; + init_info.QueueFamily = g_QueueFamily; + init_info.Queue = g_Queue; + init_info.PipelineCache = g_PipelineCache; + init_info.DescriptorPool = g_DescriptorPool; + init_info.RenderPass = wd->RenderPass; + init_info.Subpass = 0; + init_info.MinImageCount = g_MinImageCount; + init_info.ImageCount = wd->ImageCount; + init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT; + init_info.Allocator = g_Allocator; + init_info.CheckVkResultFn = check_vk_result; + ImGui_ImplVulkan_Init(&init_info); + + // Load Fonts + io.Fonts->AddFontFromFileTTF("../HYWenHei-85W.ttf", 36.0, nullptr, io.Fonts->GetGlyphRangesChineseFull()); + + // Main loop + while (window_is_open) + { + // Poll and handle events (inputs, window resize, etc.) + // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. + // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. + // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. + // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. + glfwPollEvents(); + + // Resize swap chain? + int fb_width, fb_height; + glfwGetFramebufferSize(window, &fb_width, &fb_height); + if (fb_width > 0 && fb_height > 0 && (g_SwapChainRebuild || g_MainWindowData.Width != fb_width || g_MainWindowData.Height != fb_height)) + { + ImGui_ImplVulkan_SetMinImageCount(g_MinImageCount); + ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, &g_MainWindowData, g_QueueFamily, g_Allocator, fb_width, fb_height, g_MinImageCount); + g_MainWindowData.FrameIndex = 0; + g_SwapChainRebuild = false; + } + if (glfwGetWindowAttrib(window, GLFW_ICONIFIED) != 0) + { + ImGui_ImplGlfw_Sleep(10); + continue; + } + + // Start the Dear ImGui frame + ImGui_ImplVulkan_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); + // ImGui::DockSpaceOverViewport(); + + ImGuiWindowFlags window_flags = 0; + window_flags |= ImGuiWindowFlags_NoTitleBar; + + // 获取主显示器 + GLFWmonitor *primary_monitor = glfwGetPrimaryMonitor(); + if (primary_monitor) + { + // 获取显示器的工作区大小 + int workarea_x, workarea_y, workarea_width, workarea_height; + glfwGetMonitorWorkarea(primary_monitor, &workarea_x, &workarea_y, &workarea_width, &workarea_height); + + // 获取显示器的分辨率 + const GLFWvidmode *mode = glfwGetVideoMode(primary_monitor); + + // 设置窗口的最小和最大大小 + ImVec2 min_window_size = ImVec2(400, 300); // 最小窗口大小 + ImVec2 max_window_size = ImVec2(workarea_width * 0.8f, workarea_height * 0.8f); // 最大窗口大小 + ImGui::SetNextWindowSizeConstraints(min_window_size, max_window_size); + } + + // 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window. + { + ImGui::Begin("atom", nullptr, window_flags); // Create a window called "Hello, world!" and append into it. + auto str = "Atom"; + + DrawTitleBar(str, window_is_open); + + static float left_pane_width = 200.0f; // 初始左侧窗口宽度 + float min_pane_width = 100.0f; // 最小窗口宽度 + float max_pane_width = ImGui::GetContentRegionAvail().x - min_pane_width; // 最大窗口宽度 + + static float right_pane_height = 200.0f; // 初始右侧下窗口高度 + float min_pane_height = 100.0f; // 最小窗口高度 + float max_pane_height = ImGui::GetContentRegionAvail().y - min_pane_height; // 最大窗口高度 + + ImGui::BeginGroup(); // 开始一个组 + // 左侧的可滑动列表 + ImGui::BeginChild("Left Pane", ImVec2(left_pane_width, 0), true, ImGuiWindowFlags_NoMove); + static int selected_left = 0; + static std::vector<std::string> items_left = {"Item 1", "Item 2", "Item 3", "Item 4", "Item 5"}; + for (int i = 0; i < items_left.size(); i++) + { + if (ImGui::Selectable(items_left[i].c_str(), selected_left == i)) + selected_left = i; + } + // 添加一个加号按钮 + if (ImGui::Button("+", ImVec2(ImGui::GetContentRegionAvail().x, 0))) + { + items_left.push_back("New Item " + std::to_string(items_left.size() + 1)); + } + ImGui::EndChild(); + ImGui::EndGroup(); // 结束组 + + ImGui::SameLine(); + + ImGui::BeginGroup(); + // 分割器 + ImGui::InvisibleButton("splitter_vertical", ImVec2(1.0f, -1.0f)); + if (ImGui::IsItemHovered()) + { + ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); // 更改鼠标光标 + } + if (ImGui::IsItemActive()) + { + left_pane_width += ImGui::GetIO().MouseDelta.x; + if (left_pane_width < min_pane_width) + left_pane_width = min_pane_width; + if (left_pane_width > max_pane_width) + left_pane_width = max_pane_width; + } + ImGui::EndGroup(); + + ImGui::SameLine(); + + ImGui::BeginGroup(); // 开始一个组 + // 右侧的可滑动列表 + auto temp = ImGui::GetContentRegionAvail().y - right_pane_height - 12.0f; + ImGui::BeginChild("Right Pane", ImVec2(0, temp), true, ImGuiWindowFlags_NoMove); + static int selected_right = 0; + std::vector<std::string> items_right = {"Item A", "Item B", "Item C", "Item D", "Item E"}; + for (int i = 0; i < items_right.size(); i++) + { + if (ImGui::Selectable(items_right[i].c_str(), selected_right == i, ImGuiSelectableFlags_AllowDoubleClick)) + if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0)) + { + // Do stuff on Selectable() double click. + selected_right = i; + ImGui::SetClipboardText(items_right[i].c_str()); + selected_right = -1; + } + } + ImGui::EndChild(); + // 添加一个输入框 + + // 分割器 + ImGui::InvisibleButton("splitter_horizontal", ImVec2(-1.0f, 4.0f)); + if (ImGui::IsItemHovered()) + { + ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS); // 更改鼠标光标 + } + if (ImGui::IsItemActive()) + { + right_pane_height -= ImGui::GetIO().MouseDelta.y; + if (right_pane_height < min_pane_height) + right_pane_height = min_pane_height; + if (right_pane_height > max_pane_height) + right_pane_height = max_pane_height; + } + + // 设置输入框高度 + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.0f, right_pane_height / 2.0f - ImGui::GetFontSize() / 2.0f)); + + // 添加一个输入框 + static char input_text[1024] = ""; + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); // 设置输入框宽度 + ImGui::InputText("##input", input_text, IM_ARRAYSIZE(input_text), ImGuiInputTextFlags_EnterReturnsTrue); + ImGui::PopStyleVar(); // 恢复默认样式 + ImGui::EndGroup(); // 结束组 + + ImGui::End(); + } + + // Rendering + ImGui::Render(); + 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); + if (!main_is_minimized) + FrameRender(wd, main_draw_data); + + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } + + // Present Main Platform Window + if (!main_is_minimized) + FramePresent(wd); + } + + // Cleanup + err = vkDeviceWaitIdle(g_Device); + check_vk_result(err); + ImGui_ImplVulkan_Shutdown(); + ImGui_ImplGlfw_Shutdown(); + ImGui::DestroyContext(); + + CleanupVulkanWindow(); + CleanupVulkan(); + + glfwDestroyWindow(window); + glfwTerminate(); + + return 0; +} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..1d2f8d7 --- /dev/null +++ b/readme.md @@ -0,0 +1,5 @@ +''' +mkdir build && cd build +cmake .. +make +''' \ No newline at end of file