xo-imgui: + vulkan examples

This commit is contained in:
Roland Conybeare 2025-08-29 19:32:40 -04:00
commit fb7947a896
12 changed files with 5860 additions and 4 deletions

View file

@ -267,6 +267,11 @@ pkgs.mkShell {
pkgs.libGL
pkgs.stdenv.cc.cc.lib
pkgs.glibc
# try vulkan instead of opengl (at least on OSX)
pkgs.vulkan-loader
pkgs.vulkan-headers
pkgs.vulkan-validation-layers
pkgs.darwin.moltenvk
# fonts for imgui
pkgs.gucharmap
@ -297,6 +302,11 @@ pkgs.mkShell {
#export WSLG_FORCE_EGL=1
#export GLFW_USE_EGL=1
# for Vulkan on OSX
export VULKAN_SDK=${pkgs.vulkan-headers}
export VK_ICD_FILENAMES=${pkgs.darwin.moltenvk}/share/vulkan/icd.d/MoltenVK_icd.json
export VK_LAYER_PATH=${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d
# just for info
export mesa_drivers=${pkgs.mesa.drivers}

View file

@ -18,6 +18,7 @@ add_definitions(${PROJECT_CXX_FLAGS})
# ----------------------------------------------------------------
add_subdirectory(src/imgui)
add_subdirectory(example)
# add_subdirectory(utest)

View file

@ -1,2 +1,4 @@
add_subdirectory(ex1)
add_subdirectory(ex2)
add_subdirectory(ex3)
add_subdirectory(ex4)

View file

@ -22,6 +22,7 @@ if (XO_ENABLE_EXAMPLES)
${IMGUI_INCLUDE_DIR}/imgui/imgui_tables.cpp
${IMGUI_INCLUDE_DIR}/imgui/backends/imgui_impl_sdl2.cpp
${IMGUI_INCLUDE_DIR}/imgui/backends/imgui_impl_opengl3.cpp
#${IMGUI_INCLUDE_DIR}/imgui/backends/imgui_impl_vulkan.cpp
)
xo_include_options2(${SELF_EXE})
@ -36,11 +37,13 @@ if (XO_ENABLE_EXAMPLES)
# OPENGL_opengl_LIBRARY:FILEPATH=/path/to/libOpenGL.so
# OpenGL_DIR:PATH=OpenGL_DIR-NOTFOUND # no cmake config file
set(OpenGL_GL_PREFERENCE GLVND) # or LEGACY
# set(OpenGL_GL_PREFERENCE GLVND) # or LEGACY
find_package(OpenGL REQUIRED) # find_package(OpenGL CONFIG REQUIRE) won't work
target_link_libraries(${SELF_EXE} PUBLIC OpenGL::GL)
# find_package(Vulkan REQUIRED)
# target_link_libraries(${SELF_EXE} PUBLIC Vulkan::Vulkan)
# GLEW dependency
xo_external_pkgconfig_dependency(${SELF_EXE} GLEW glew)

View file

@ -1752,7 +1752,7 @@ int main(int, char **)
SDL_SetHint(SDL_HINT_VIDEO_X11_FORCE_EGL, "0");
SDL_Init(SDL_INIT_VIDEO);
SDL_Init(SDL_INIT_VIDEO);
SDL_version compiled;
SDL_VERSION(&compiled);
@ -1812,7 +1812,7 @@ int main(int, char **)
return -1;
}
SDL_GL_SetSwapInterval(1); // enable vsync
SDL_GL_SetSwapInterval(0); // disable vsync
GLenum glew_status = glewInit();
if (glew_status == GLEW_OK) {

View file

@ -0,0 +1,67 @@
if (XO_ENABLE_EXAMPLES)
# imgui dependency
find_path(IMGUI_INCLUDE_DIR
NAMES imgui/imgui.h
DOC "path to imgui header"
)
if (IMGUI_INCLUDE_DIR)
message(STATUS "found imgui/imgui.h in IMGUI_INCLUDE_DIR=[${IMGUI_INCLUDE_DIR}]")
else()
message(FATAL_ERROR "unable to find imgui.h")
endif()
# target executable
set(SELF_EXE imgui_ex3)
add_executable(${SELF_EXE} imgui_ex3.cpp
${IMGUI_INCLUDE_DIR}/imgui/imgui.cpp
${IMGUI_INCLUDE_DIR}/imgui/imgui_demo.cpp
${IMGUI_INCLUDE_DIR}/imgui/imgui_draw.cpp
${IMGUI_INCLUDE_DIR}/imgui/imgui_widgets.cpp
${IMGUI_INCLUDE_DIR}/imgui/imgui_tables.cpp
${IMGUI_INCLUDE_DIR}/imgui/backends/imgui_impl_sdl2.cpp
#${IMGUI_INCLUDE_DIR}/imgui/backends/imgui_impl_opengl3.cpp
#${IMGUI_INCLUDE_DIR}/imgui/backends/imgui_impl_glfw.cpp
${IMGUI_INCLUDE_DIR}/imgui/backends/imgui_impl_vulkan.cpp
)
xo_include_options2(${SELF_EXE})
# OpenGL dependency
# have to choose between
# libGL.so
# or
# libOpenGL.so + libGLX.so # GLVND = OpenGL Vendor Neutral Dispatch (e.g. mesa|nvda)
#
# expect in .build/CMakeCache.txt:
# OPENGL_opengl_LIBRARY:FILEPATH=/path/to/libOpenGL.so
# OpenGL_DIR:PATH=OpenGL_DIR-NOTFOUND # no cmake config file
# set(OpenGL_GL_PREFERENCE GLVND) # or LEGACY
# find_package(OpenGL REQUIRED) # find_package(OpenGL CONFIG REQUIRE) won't work
# target_link_libraries(${SELF_EXE} PUBLIC OpenGL::GL)
find_package(Vulkan REQUIRED)
target_link_libraries(${SELF_EXE} PUBLIC Vulkan::Vulkan)
# GLEW dependency
xo_external_pkgconfig_dependency(${SELF_EXE} GLEW glew)
# GLFW dependency
#find_package(glfw3 CONFIG REQUIRED)
#target_link_libraries(${SELF_EXE} PUBLIC glfw) # want -lglfw
# SDL2 dependency
xo_external_pkgconfig_dependency(${SELF_EXE} SDL2 sdl2)
# would prefer to use just IMGUI_INCLUDE_DIR,
# but imgui/backends/ .h files don't quote the imgui/ stem
#
target_include_directories(${SELF_EXE} PUBLIC ${IMGUI_INCLUDE_DIR}/imgui)
xo_dependency(${SELF_EXE} xo_object)
xo_dependency(${SELF_EXE} randomgen)
xo_dependency(${SELF_EXE} xo_flatstring)
xo_dependency(${SELF_EXE} xo_alloc)
endif()

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,68 @@
if (XO_ENABLE_EXAMPLES)
# imgui dependency
find_path(IMGUI_INCLUDE_DIR
NAMES imgui/imgui.h
DOC "path to imgui header"
)
if (IMGUI_INCLUDE_DIR)
message(STATUS "found imgui/imgui.h in IMGUI_INCLUDE_DIR=[${IMGUI_INCLUDE_DIR}]")
else()
message(FATAL_ERROR "unable to find imgui.h")
endif()
# target executable
set(SELF_EXE imgui_ex4)
add_executable(${SELF_EXE} imgui_ex4.cpp
${IMGUI_INCLUDE_DIR}/imgui/imgui.cpp
${IMGUI_INCLUDE_DIR}/imgui/imgui_demo.cpp
${IMGUI_INCLUDE_DIR}/imgui/imgui_draw.cpp
${IMGUI_INCLUDE_DIR}/imgui/imgui_widgets.cpp
${IMGUI_INCLUDE_DIR}/imgui/imgui_tables.cpp
${IMGUI_INCLUDE_DIR}/imgui/backends/imgui_impl_sdl2.cpp
#${IMGUI_INCLUDE_DIR}/imgui/backends/imgui_impl_opengl3.cpp
#${IMGUI_INCLUDE_DIR}/imgui/backends/imgui_impl_glfw.cpp
${IMGUI_INCLUDE_DIR}/imgui/backends/imgui_impl_vulkan.cpp
)
xo_include_options2(${SELF_EXE})
# OpenGL dependency
# have to choose between
# libGL.so
# or
# libOpenGL.so + libGLX.so # GLVND = OpenGL Vendor Neutral Dispatch (e.g. mesa|nvda)
#
# expect in .build/CMakeCache.txt:
# OPENGL_opengl_LIBRARY:FILEPATH=/path/to/libOpenGL.so
# OpenGL_DIR:PATH=OpenGL_DIR-NOTFOUND # no cmake config file
# set(OpenGL_GL_PREFERENCE GLVND) # or LEGACY
# find_package(OpenGL REQUIRED) # find_package(OpenGL CONFIG REQUIRE) won't work
# target_link_libraries(${SELF_EXE} PUBLIC OpenGL::GL)
find_package(Vulkan REQUIRED)
target_link_libraries(${SELF_EXE} PUBLIC Vulkan::Vulkan)
# GLEW dependency
xo_external_pkgconfig_dependency(${SELF_EXE} GLEW glew)
# GLFW dependency
#find_package(glfw3 CONFIG REQUIRED)
#target_link_libraries(${SELF_EXE} PUBLIC glfw) # want -lglfw
# SDL2 dependency
xo_external_pkgconfig_dependency(${SELF_EXE} SDL2 sdl2)
# would prefer to use just IMGUI_INCLUDE_DIR,
# but imgui/backends/ .h files don't quote the imgui/ stem
#
target_include_directories(${SELF_EXE} PUBLIC ${IMGUI_INCLUDE_DIR}/imgui)
xo_self_dependency(${SELF_EXE} xo_imgui)
xo_dependency(${SELF_EXE} xo_object)
xo_dependency(${SELF_EXE} randomgen)
xo_dependency(${SELF_EXE} xo_flatstring)
xo_dependency(${SELF_EXE} xo_alloc)
endif()

View file

@ -0,0 +1,19 @@
/* imgui_ex4.cpp */
#include "xo/imgui/VulkanApp.hpp"
#include <iostream>
int main() {
VulkanApp app;
try {
app.run();
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
/* end imgui_ex4.cpp */

View file

@ -0,0 +1,69 @@
/* file VulkanApp.hpp */
#pragma once
#include <vulkan/vulkan.h>
#include <SDL.h>
//#include <SDL_vulkan.h>
#include <vector>
class VulkanApp {
public:
void run();
private:
void init_window();
void init_vulkan();
void create_instance();
void create_surface();
void pick_physical_device();
void create_logical_device();
void create_swap_chain();
void create_image_views();
void create_render_pass();
void create_framebuffers();
void create_command_pool();
void create_command_buffers();
void create_sync_objects();
void create_descriptor_pool();
void init_imgui();
VkCommandBuffer begin_single_time_commands();
void end_single_time_commands(VkCommandBuffer commandBuffer);
void record_command_buffer(VkCommandBuffer commandBuffer,
uint32_t imageIndex);
void cleanup();
/** TODO: replace with some generic mechanism **/
void main_loop();
void draw_frame();
private:
SDL_Window* window = nullptr;
VkInstance instance;
VkPhysicalDevice physical_device_;
VkDevice device_;
VkQueue graphics_queue_;
VkSurfaceKHR surface_;
VkSwapchainKHR swapchain_;
VkFormat swapchain_image_format_;
VkExtent2D swapchain_extent_;
std::vector<VkImage> swapchainImages;
std::vector<VkImageView> swapchain_image_views_;
VkRenderPass render_pass_;
std::vector<VkFramebuffer> framebuffers_;
VkCommandPool command_pool_;
std::vector<VkCommandBuffer> command_buffers_;
/** one per frame (up to MAX_FRAMES_IN_FLIGHT) **/
std::vector<VkSemaphore> image_available_semaphores_;
/** one per frame (up to MAX_FRAMES_IN_FLIGHT) **/
std::vector<VkSemaphore> render_finished_semaphores_;
std::vector<VkFence> in_flight_fences_;
VkDescriptorPool descriptor_pool_;
/** image index of current frame **/
uint32_t current_frame_ = 0;
uint32_t graphics_queue_family_ = 0;
bool quit_ = false;
};
/* end VulkanApp.hpp */

View file

@ -0,0 +1,41 @@
# imgui/CMakeLists.txt
find_path(IMGUI_INCLUDE_DIR
NAMES imgui/imgui.h
DOC "path to imgui header")
if (IMGUI_INCLUDE_DIR)
message(STATUS
"found imgui/imgui.h in IMGUI_INCLUDE_DIR=[${IMGUI_INCLUDE_DIR}]")
else()
message(FATAL_ERROR "unable to find imgui.h")
endif()
set(SELF_LIB xo_imgui)
set(SELF_SRCS
VulkanApp.cpp
${IMGUI_INCLUDE_DIR}/imgui/imgui.cpp
${IMGUI_INCLUDE_DIR}/imgui/imgui_demo.cpp
${IMGUI_INCLUDE_DIR}/imgui/imgui_draw.cpp
${IMGUI_INCLUDE_DIR}/imgui/imgui_widgets.cpp
${IMGUI_INCLUDE_DIR}/imgui/imgui_tables.cpp
${IMGUI_INCLUDE_DIR}/imgui/backends/imgui_impl_sdl2.cpp
#${IMGUI_INCLUDE_DIR}/imgui/backends/imgui_impl_opengl3.cpp
#${IMGUI_INCLUDE_DIR}/imgui/backends/imgui_impl_glfw.cpp
${IMGUI_INCLUDE_DIR}/imgui/backends/imgui_impl_vulkan.cpp)
xo_add_shared_library4(
${SELF_LIB}
${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS}
)
find_package(Vulkan REQUIRED)
target_link_libraries(${SELF_LIB} PUBLIC Vulkan::Vulkan)
xo_external_pkgconfig_dependency(${SELF_LIB} SDL2 sdl2)
# NOTE: would prefer to use just IMGUI_INCLUDE_DIR,
# but imgui/backends/ .h files don't quote the imgui/ stem
#
target_include_directories(${SELF_LIB} PUBLIC ${IMGUI_INCLUDE_DIR}/imgui)
#xo_dependency(${SELF_LIB} reflect)

View file

@ -0,0 +1,662 @@
/* file VulkanApp.cpp */
#include "VulkanApp.hpp"
#include <SDL_vulkan.h>
#include <imgui.h>
#include <backends/imgui_impl_sdl2.h>
#include <backends/imgui_impl_vulkan.h>
#include <cstdint>
constexpr std::size_t c_max_frames_in_flight = 2;
void
VulkanApp::run() {
this->init_window();
this->init_vulkan();
this->init_imgui();
this->main_loop();
this->cleanup();
}
void
VulkanApp::init_window() {
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
throw std::runtime_error("Failed to initialize SDL!");
}
window = SDL_CreateWindow(
"Xo ImGui Vulkan SDL2 Frame",
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
800, 600,
SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE
);
if (!window) {
throw std::runtime_error("Failed to create SDL window!");
}
}
void
VulkanApp::init_vulkan()
{
this->create_instance();
this->create_surface();
this->pick_physical_device();
this->create_logical_device();
this->create_swap_chain();
this->create_image_views();
this->create_render_pass();
this->create_framebuffers();
this->create_command_pool();
this->create_command_buffers();
this->create_sync_objects();
this->create_descriptor_pool();
}
void
VulkanApp::create_instance()
{
VkApplicationInfo appInfo{};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pApplicationName = "ImGui Vulkan App";
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.pEngineName = "No Engine";
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.apiVersion = VK_API_VERSION_1_0;
VkInstanceCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.pApplicationInfo = &appInfo;
uint32_t extensionCount = 0;
if (!SDL_Vulkan_GetInstanceExtensions(window, &extensionCount, nullptr)) {
throw std::runtime_error("Failed to get SDL Vulkan extensions!");
}
std::vector<const char*> extensions(extensionCount);
if (!SDL_Vulkan_GetInstanceExtensions(window, &extensionCount, extensions.data())) {
throw std::runtime_error("Failed to get SDL Vulkan extensions!");
}
// Add portability extension for MoltenVK (macOS)
extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
createInfo.enabledExtensionCount = extensions.size();
createInfo.ppEnabledExtensionNames = extensions.data();
createInfo.enabledLayerCount = 0;
// CRITICAL: Enable portability enumeration flag for MoltenVK
createInfo.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
throw std::runtime_error("Failed to create instance!");
}
}
void
VulkanApp::create_surface() {
if (!SDL_Vulkan_CreateSurface(window, instance, &(this->surface_))) {
throw std::runtime_error("Failed to create SDL Vulkan surface!");
}
}
void
VulkanApp::pick_physical_device()
{
uint32_t deviceCount = 0;
vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
if (deviceCount == 0) {
throw std::runtime_error("Failed to find GPUs with Vulkan support!");
}
std::vector<VkPhysicalDevice> devices(deviceCount);
vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
this->physical_device_ = devices[0]; // Just pick the first one for simplicity
// Find graphics queue family
uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(physical_device_, &queueFamilyCount, nullptr);
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties
(physical_device_, &queueFamilyCount, queueFamilies.data());
for (uint32_t i = 0; i < queueFamilies.size(); i++) {
if (queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
VkBool32 presentSupport = false;
vkGetPhysicalDeviceSurfaceSupportKHR
(physical_device_, i, surface_, &presentSupport);
if (presentSupport) {
graphics_queue_family_ = i;
break;
}
}
}
}
void
VulkanApp::create_logical_device()
{
VkDeviceQueueCreateInfo queueCreateInfo{};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex = graphics_queue_family_;
queueCreateInfo.queueCount = 1;
float queuePriority = 1.0f;
queueCreateInfo.pQueuePriorities = &queuePriority;
VkPhysicalDeviceFeatures deviceFeatures{};
VkDeviceCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
createInfo.pQueueCreateInfos = &queueCreateInfo;
createInfo.queueCreateInfoCount = 1;
createInfo.pEnabledFeatures = &deviceFeatures;
const char* deviceExtensions[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
createInfo.enabledExtensionCount = 1;
createInfo.ppEnabledExtensionNames = deviceExtensions;
if (vkCreateDevice(physical_device_, &createInfo, nullptr, &device_) != VK_SUCCESS) {
throw std::runtime_error("Failed to create logical device!");
}
vkGetDeviceQueue(device_, graphics_queue_family_, 0, &graphics_queue_);
}
void
VulkanApp::create_swap_chain()
{
VkSurfaceCapabilitiesKHR capabilities;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR
(physical_device_, surface_, &capabilities);
uint32_t formatCount;
vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device_, surface_, &formatCount, nullptr);
std::vector<VkSurfaceFormatKHR> formats(formatCount);
vkGetPhysicalDeviceSurfaceFormatsKHR
(physical_device_, surface_, &formatCount, formats.data());
VkSurfaceFormatKHR surfaceFormat = formats[0];
swapchain_image_format_ = surfaceFormat.format;
int width, height;
SDL_Vulkan_GetDrawableSize(window, &width, &height);
swapchain_extent_ =
{
static_cast<uint32_t>(width),
static_cast<uint32_t>(height)
};
uint32_t imageCount = capabilities.minImageCount + 1;
if (capabilities.maxImageCount > 0 && imageCount > capabilities.maxImageCount) {
imageCount = capabilities.maxImageCount;
}
VkSwapchainCreateInfoKHR createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
createInfo.surface = surface_;
createInfo.minImageCount = imageCount;
createInfo.imageFormat = surfaceFormat.format;
createInfo.imageColorSpace = surfaceFormat.colorSpace;
createInfo.imageExtent = swapchain_extent_;
createInfo.imageArrayLayers = 1;
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
createInfo.preTransform = capabilities.currentTransform;
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
createInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR;
createInfo.clipped = VK_TRUE;
if (vkCreateSwapchainKHR
(device_, &createInfo, nullptr, &swapchain_) != VK_SUCCESS)
{
throw std::runtime_error("Failed to create swap chain!");
}
vkGetSwapchainImagesKHR(device_, swapchain_, &imageCount, nullptr);
swapchainImages.resize(imageCount);
vkGetSwapchainImagesKHR(device_, swapchain_, &imageCount, swapchainImages.data());
}
void
VulkanApp::create_image_views()
{
swapchain_image_views_.resize(swapchainImages.size());
for (size_t i = 0; i < swapchainImages.size(); i++) {
VkImageViewCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
createInfo.image = swapchainImages[i];
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
createInfo.format = swapchain_image_format_;
createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
createInfo.subresourceRange.baseMipLevel = 0;
createInfo.subresourceRange.levelCount = 1;
createInfo.subresourceRange.baseArrayLayer = 0;
createInfo.subresourceRange.layerCount = 1;
if (vkCreateImageView
(device_, &createInfo, nullptr,
&swapchain_image_views_[i]) != VK_SUCCESS)
{
throw std::runtime_error("Failed to create image views!");
}
}
}
void
VulkanApp::create_render_pass()
{
VkAttachmentDescription colorAttachment{};
colorAttachment.format = swapchain_image_format_;
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference colorAttachmentRef{};
colorAttachmentRef.attachment = 0;
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorAttachmentRef;
VkSubpassDependency dependency{};
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
dependency.dstSubpass = 0;
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.srcAccessMask = 0;
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
VkRenderPassCreateInfo renderPassInfo{};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = 1;
renderPassInfo.pAttachments = &colorAttachment;
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpass;
renderPassInfo.dependencyCount = 1;
renderPassInfo.pDependencies = &dependency;
if (vkCreateRenderPass
(device_, &renderPassInfo, nullptr, &render_pass_) != VK_SUCCESS)
{
throw std::runtime_error("Failed to create render pass!");
}
}
void
VulkanApp::create_framebuffers()
{
framebuffers_.resize(swapchain_image_views_.size());
for (size_t i = 0; i < swapchain_image_views_.size(); i++) {
VkImageView attachments[] = { swapchain_image_views_[i] };
VkFramebufferCreateInfo framebufferInfo{};
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferInfo.renderPass = render_pass_;
framebufferInfo.attachmentCount = 1;
framebufferInfo.pAttachments = attachments;
framebufferInfo.width = swapchain_extent_.width;
framebufferInfo.height = swapchain_extent_.height;
framebufferInfo.layers = 1;
if (vkCreateFramebuffer
(device_, &framebufferInfo, nullptr,
&framebuffers_[i]) != VK_SUCCESS)
{
throw std::runtime_error("Failed to create framebuffer!");
}
}
}
void
VulkanApp::create_command_pool()
{
VkCommandPoolCreateInfo poolInfo{};
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
poolInfo.queueFamilyIndex = graphics_queue_family_;
if (vkCreateCommandPool (device_, &poolInfo,
nullptr, &command_pool_) != VK_SUCCESS)
{
throw std::runtime_error("Failed to create command pool!");
}
}
void
VulkanApp::create_command_buffers()
{
this->command_buffers_.resize(c_max_frames_in_flight);
VkCommandBufferAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.commandPool = command_pool_;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = static_cast<uint32_t>(command_buffers_.size());
if (vkAllocateCommandBuffers
(device_, &allocInfo, command_buffers_.data()) != VK_SUCCESS)
{
throw std::runtime_error("Failed to allocate command buffers!");
}
}
void
VulkanApp::create_sync_objects()
{
this->image_available_semaphores_.resize(c_max_frames_in_flight);
this->render_finished_semaphores_.resize(c_max_frames_in_flight);
this->in_flight_fences_.resize(c_max_frames_in_flight);
VkSemaphoreCreateInfo semaphoreInfo{};
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
VkFenceCreateInfo fenceInfo{};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
for (std::size_t i = 0; i < c_max_frames_in_flight; i++) {
if ((vkCreateSemaphore(device_, &semaphoreInfo,
nullptr, &image_available_semaphores_[i]) != VK_SUCCESS)
|| (vkCreateSemaphore(device_, &semaphoreInfo,
nullptr, &render_finished_semaphores_[i]) != VK_SUCCESS)
|| (vkCreateFence(device_, &fenceInfo,
nullptr, &in_flight_fences_[i]) != VK_SUCCESS))
{
throw std::runtime_error("Failed to create synchronization objects!");
}
}
}
void
VulkanApp::create_descriptor_pool() {
VkDescriptorPoolSize pool_sizes[] = {
{ VK_DESCRIPTOR_TYPE_SAMPLER, 1000 },
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 },
{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 },
{ VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 },
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 },
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 },
{ VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 }
};
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 = 1000 * IM_ARRAYSIZE(pool_sizes);
pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes);
pool_info.pPoolSizes = pool_sizes;
if (vkCreateDescriptorPool(device_, &pool_info,
nullptr, &descriptor_pool_) != VK_SUCCESS)
{
throw std::runtime_error("Failed to create descriptor pool!");
}
}
void
VulkanApp::init_imgui()
{
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
// Setup Dear ImGui style
ImGui::StyleColorsDark();
// Setup Platform/Renderer backends
ImGui_ImplSDL2_InitForVulkan(window);
ImGui_ImplVulkan_InitInfo init_info = {};
init_info.Instance = instance;
init_info.PhysicalDevice = physical_device_;
init_info.Device = device_;
init_info.QueueFamily = graphics_queue_family_;
init_info.Queue = graphics_queue_;
init_info.PipelineCache = VK_NULL_HANDLE;
init_info.DescriptorPool = descriptor_pool_;
init_info.RenderPass = render_pass_;
init_info.Subpass = 0;
init_info.MinImageCount = c_max_frames_in_flight;
init_info.ImageCount = static_cast<uint32_t>(swapchainImages.size());
init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
init_info.Allocator = nullptr;
init_info.CheckVkResultFn = nullptr;
ImGui_ImplVulkan_Init(&init_info);
//ImGui_ImplVulkan_Init(&init_info, renderPass);
// Upload Fonts
VkCommandBuffer command_buffer = begin_single_time_commands();
ImGui_ImplVulkan_CreateFontsTexture();
this->end_single_time_commands(command_buffer);
//ImGui_ImplVulkan_DestroyFontUploadObjects();
} /*init_imgui*/
VkCommandBuffer
VulkanApp::begin_single_time_commands()
{
VkCommandBufferAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandPool = command_pool_;
allocInfo.commandBufferCount = 1;
VkCommandBuffer commandBuffer;
vkAllocateCommandBuffers(device_, &allocInfo, &commandBuffer);
VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
vkBeginCommandBuffer(commandBuffer, &beginInfo);
return commandBuffer;
}
void
VulkanApp::end_single_time_commands(VkCommandBuffer commandBuffer)
{
vkEndCommandBuffer(commandBuffer);
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffer;
vkQueueSubmit(graphics_queue_, 1, &submitInfo, VK_NULL_HANDLE);
vkQueueWaitIdle(graphics_queue_);
vkFreeCommandBuffers(device_, command_pool_, 1, &commandBuffer);
}
void
VulkanApp::main_loop()
{
SDL_Event event;
while (!quit_) {
while (SDL_PollEvent(&event)) {
ImGui_ImplSDL2_ProcessEvent(&event);
if (event.type == SDL_QUIT) {
this->quit_ = true;
}
}
this->draw_frame();
}
vkDeviceWaitIdle(device_);
}
void
VulkanApp::draw_frame()
{
vkWaitForFences(device_, 1,
&in_flight_fences_[current_frame_], VK_TRUE, UINT64_MAX);
uint32_t imageIndex;
vkAcquireNextImageKHR(device_, swapchain_, UINT64_MAX,
image_available_semaphores_[current_frame_],
VK_NULL_HANDLE, &imageIndex);
vkResetFences(device_, 1, &in_flight_fences_[current_frame_]);
vkResetCommandBuffer(command_buffers_[current_frame_], 0);
this->record_command_buffer(command_buffers_[current_frame_], imageIndex);
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
VkSemaphore waitSemaphores[] = {image_available_semaphores_[current_frame_]};
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = waitSemaphores;
submitInfo.pWaitDstStageMask = waitStages;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &command_buffers_[current_frame_];
VkSemaphore signalSemaphores[]
= {render_finished_semaphores_[current_frame_]};
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signalSemaphores;
if (vkQueueSubmit(graphics_queue_, 1,
&submitInfo,
in_flight_fences_[current_frame_]) != VK_SUCCESS)
{
throw std::runtime_error("Failed to submit draw command buffer!");
}
VkPresentInfoKHR presentInfo{};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = signalSemaphores;
VkSwapchainKHR swapChains[] = {swapchain_};
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = swapChains;
presentInfo.pImageIndices = &imageIndex;
vkQueuePresentKHR(graphics_queue_, &presentInfo);
current_frame_ = (current_frame_ + 1) % c_max_frames_in_flight;
} /*draw_frame*/
void
VulkanApp::record_command_buffer(VkCommandBuffer commandBuffer,
uint32_t imageIndex)
{
VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) {
throw std::runtime_error("Failed to begin recording command buffer!");
}
VkRenderPassBeginInfo renderPassInfo{};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
renderPassInfo.renderPass = render_pass_;
renderPassInfo.framebuffer = framebuffers_[imageIndex];
renderPassInfo.renderArea.offset = {0, 0};
renderPassInfo.renderArea.extent = swapchain_extent_;
VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
renderPassInfo.clearValueCount = 1;
renderPassInfo.pClearValues = &clearColor;
vkCmdBeginRenderPass(commandBuffer,
&renderPassInfo,
VK_SUBPASS_CONTENTS_INLINE);
// Start the Dear ImGui frame
ImGui_ImplVulkan_NewFrame();
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
// Create a simple ImGui window
ImGui::Begin("Hello, Vulkan + SDL2!");
ImGui::Text("This is a minimal ImGui + Vulkan + SDL2 example!");
static float f = 0.0f;
static int counter = 0;
ImGui::SliderFloat("float", &f, 0.0f, 1.0f);
if (ImGui::Button("Button"))
++counter;
ImGui::SameLine();
ImGui::Text("counter = %d", counter);
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)",
1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
ImGui::End();
// Rendering
ImGui::Render();
ImDrawData* draw_data = ImGui::GetDrawData();
ImGui_ImplVulkan_RenderDrawData(draw_data, commandBuffer);
vkCmdEndRenderPass(commandBuffer);
if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) {
throw std::runtime_error("Failed to record command buffer!");
}
}
void
VulkanApp::cleanup()
{
ImGui_ImplVulkan_Shutdown();
ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext();
for (size_t i = 0; i < c_max_frames_in_flight; ++i) {
vkDestroySemaphore(device_, render_finished_semaphores_[i], nullptr);
vkDestroySemaphore(device_, image_available_semaphores_[i], nullptr);
vkDestroyFence(device_, in_flight_fences_[i], nullptr);
}
vkDestroyCommandPool(device_, command_pool_, nullptr);
for (auto framebuffer : framebuffers_) {
vkDestroyFramebuffer(device_, framebuffer, nullptr);
}
vkDestroyRenderPass(device_, render_pass_, nullptr);
for (auto imageView : swapchain_image_views_) {
vkDestroyImageView(device_, imageView, nullptr);
}
vkDestroySwapchainKHR(device_, swapchain_, nullptr);
vkDestroyDescriptorPool(device_, descriptor_pool_, nullptr);
vkDestroyDevice(device_, nullptr);
vkDestroySurfaceKHR(instance, surface_, nullptr);
vkDestroyInstance(instance, nullptr);
SDL_DestroyWindow(window);
SDL_Quit();
}
/* end VulkanApp.cpp */