Using Vulkan C API

This commit is contained in:
mklefrancois 2021-06-07 14:02:45 +02:00
parent b3e6d84807
commit e642e9dc3a
83 changed files with 8015 additions and 8163 deletions

View file

@ -7,7 +7,7 @@ cmake_minimum_required(VERSION 3.9.6 FATAL_ERROR)
#--------------------------------------------------------------------------------------------------
# Project setting
get_filename_component(PROJNAME ${CMAKE_CURRENT_SOURCE_DIR} NAME)
SET(PROJNAME vk_${PROJNAME}_KHR)
set(PROJNAME vk_${PROJNAME}_KHR)
project(${PROJNAME} LANGUAGES C CXX)
message(STATUS "-------------------------------")
message(STATUS "Processing Project ${PROJNAME}:")

View file

@ -63,39 +63,50 @@ executeCallableEXT(pushC.lightType, 0);
In `HelloVulkan::createRtPipeline()`, immediately after adding the closest-hit shader, we will add
3 callable shaders, for each type of light.
First create the shader modules
~~~~ C++
enum StageIndices
{
eRaygen,
eMiss,
eMiss2,
eClosestHit,
eCall0,
eCall1,
eCall2,
eShaderGroupCount
};
...
// Call0
stage.module = nvvk::createShaderModule(m_device, nvh::loadFile("spv/light_point.rcall.spv", true, defaultSearchPaths, true));
stage.stage = VK_SHADER_STAGE_CALLABLE_BIT_KHR;
stages[eCall0] = stage;
// Call1
stage.module = nvvk::createShaderModule(m_device, nvh::loadFile("spv/light_spot.rcall.spv", true, defaultSearchPaths, true));
stage.stage = VK_SHADER_STAGE_CALLABLE_BIT_KHR;
stages[eCall1] = stage;
// Call2
stage.module = nvvk::createShaderModule(m_device, nvh::loadFile("spv/light_inf.rcall.spv", true, defaultSearchPaths, true));
stage.stage = VK_SHADER_STAGE_CALLABLE_BIT_KHR;
stages[eCall2] = stage;
~~~~
Then 3 groups of callable shaders and the stages that goes with it.
~~~~ C++
// Callable shaders
vk::RayTracingShaderGroupCreateInfoKHR callGroup{vk::RayTracingShaderGroupTypeKHR::eGeneral,
VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR,
VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR};
vk::ShaderModule call0 =
nvvk::createShaderModule(m_device,
nvh::loadFile("shaders/light_point.rcall.spv", true, paths));
vk::ShaderModule call1 =
nvvk::createShaderModule(m_device,
nvh::loadFile("shaders/light_spot.rcall.spv", true, paths));
vk::ShaderModule call2 =
nvvk::createShaderModule(m_device, nvh::loadFile("shaders/light_inf.rcall.spv", true, paths));
callGroup.setGeneralShader(static_cast<uint32_t>(stages.size()));
stages.push_back({{}, vk::ShaderStageFlagBits::eCallableKHR, call0, "main"});
m_rtShaderGroups.push_back(callGroup);
callGroup.setGeneralShader(static_cast<uint32_t>(stages.size()));
stages.push_back({{}, vk::ShaderStageFlagBits::eCallableKHR, call1, "main"});
m_rtShaderGroups.push_back(callGroup);
callGroup.setGeneralShader(static_cast<uint32_t>(stages.size()));
stages.push_back({{}, vk::ShaderStageFlagBits::eCallableKHR, call2, "main"});
m_rtShaderGroups.push_back(callGroup);
group.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR;
group.closestHitShader = VK_SHADER_UNUSED_KHR;
group.generalShader = eCall0;
m_rtShaderGroups.push_back(group);
group.generalShader = eCall1;
m_rtShaderGroups.push_back(group);
group.generalShader = eCall2;
m_rtShaderGroups.push_back(group);
~~~~
And at the end of the function, delete the shaders.
~~~~ C++
m_device.destroy(call0);
m_device.destroy(call1);
m_device.destroy(call2);
~~~~
#### Shaders
@ -106,29 +117,44 @@ Here are the source of all shaders
* [light_inf.rcall](shaders/light_inf.rcall)
### Passing Callable to traceRaysKHR
### Shading Binding Table
In this example, we will use the `nvvk::SBTWrapper`. It is using the information to create the ray tracing pipeline, to
create the buffers for the shading binding table.
In the `hello_vulkan.h` header, include the wrapper and add a new member.
~~~~C
#include "nvvk/sbtwrapper_vk.hpp"
...
nvvk::SBTWrapper m_sbtWrapper;
~~~~
In `HelloVulkan::initRayTracing()`, initialize it the following way.
~~~~C
m_sbtWrapper.setup(m_device, m_graphicsQueueIndex, &m_alloc, m_rtProperties);
~~~~
In `HelloVulkan::createRtPipeline()`, immediately after creating the pipeline call to `vkCreateRayTracingPipelinesKHR()`,
create the SBT with the following command.
~~~~C
m_sbtWrapper.create(m_rtPipeline, rayPipelineInfo);
~~~~
In `HelloVulkan::raytrace()`, we have to tell where the callable shader starts. Since they were added after the hit shader, we have in the SBT the following.
![SBT](images/sbt.png)
Therefore, the callable starts at `4 * progSize`
The SBT wrapper class give back the information we need. So instead of computing the various offsets, we can get directly the
`VkStridedDeviceAddressRegionKHR` for each group type.
~~~~ C++
std::array<stride, 4> strideAddresses{
stride{sbtAddress + 0u * progSize, progSize, progSize * 1}, // raygen
stride{sbtAddress + 1u * progSize, progSize, progSize * 2}, // miss
stride{sbtAddress + 3u * progSize, progSize, progSize * 1}, // hit
stride{sbtAddress + 4u * progSize, progSize, progSize * 1}}; // callable
~~~~
Then we can call `traceRaysKHR`
~~~~ C++
cmdBuf.traceRaysKHR(&strideAddresses[0], &strideAddresses[1], &strideAddresses[2],
&strideAddresses[3], //
m_size.width, m_size.height, 1); //
auto& regions = m_sbtWrapper.getRegions();
vkCmdTraceRaysKHR(cmdBuf, &regions[0], &regions[1], &regions[2], &regions[3], m_size.width, m_size.height, 1);
~~~~
## Calling the Callable Shaders

File diff suppressed because it is too large Load diff

View file

@ -19,17 +19,16 @@
#pragma once
#include "nvvk/appbase_vkpp.hpp"
#include "nvvk/appbase_vk.hpp"
#include "nvvk/debug_util_vk.hpp"
#include "nvvk/descriptorsets_vk.hpp"
#include "nvvk/memallocator_dma_vk.hpp"
#include "nvvk/resourceallocator_vk.hpp"
// #VKRay
#include "nvvk/raytraceKHR_vk.hpp"
#include "nvvk/sbtwrapper_vk.hpp"
//--------------------------------------------------------------------------------------------------
// Simple rasterizer of OBJ objects
// - Each OBJ loaded are stored in an `ObjModel` and referenced by a `ObjInstance`
@ -37,25 +36,21 @@
// - Rendering is done in an offscreen framebuffer
// - The image of the framebuffer is displayed in post-process in a full-screen quad
//
class HelloVulkan : public nvvk::AppBase
class HelloVulkan : public nvvk::AppBaseVk
{
public:
void setup(const vk::Instance& instance,
const vk::Device& device,
const vk::PhysicalDevice& physicalDevice,
uint32_t queueFamily) override;
void setup(const VkInstance& instance, const VkDevice& device, const VkPhysicalDevice& physicalDevice, uint32_t queueFamily) override;
void createDescriptorSetLayout();
void createGraphicsPipeline();
void loadModel(const std::string& filename, nvmath::mat4f transform = nvmath::mat4f(1));
void updateDescriptorSet();
void createUniformBuffer();
void createSceneDescriptionBuffer();
void createTextureImages(const vk::CommandBuffer& cmdBuf,
const std::vector<std::string>& textures);
void updateUniformBuffer(const vk::CommandBuffer& cmdBuf);
void createTextureImages(const VkCommandBuffer& cmdBuf, const std::vector<std::string>& textures);
void updateUniformBuffer(const VkCommandBuffer& cmdBuf);
void onResize(int /*w*/, int /*h*/) override;
void destroyResources();
void rasterize(const vk::CommandBuffer& cmdBuff);
void rasterize(const VkCommandBuffer& cmdBuff);
// The OBJ model
struct ObjModel
@ -95,39 +90,41 @@ public:
std::vector<ObjInstance> m_objInstance;
// Graphic pipeline
vk::PipelineLayout m_pipelineLayout;
vk::Pipeline m_graphicsPipeline;
VkPipelineLayout m_pipelineLayout;
VkPipeline m_graphicsPipeline;
nvvk::DescriptorSetBindings m_descSetLayoutBind;
vk::DescriptorPool m_descPool;
vk::DescriptorSetLayout m_descSetLayout;
vk::DescriptorSet m_descSet;
VkDescriptorPool m_descPool;
VkDescriptorSetLayout m_descSetLayout;
VkDescriptorSet m_descSet;
nvvk::Buffer m_cameraMat; // Device-Host of the camera matrices
nvvk::Buffer m_sceneDesc; // Device buffer of the OBJ instances
std::vector<nvvk::Texture> m_textures; // vector of all textures of the scene
nvvk::ResourceAllocatorDma m_alloc; // Allocator for buffer, images, acceleration structures
nvvk::DebugUtil m_debug; // Utility to name objects
// #Post
void createOffscreenRender();
void createPostPipeline();
void createPostDescriptor();
void updatePostDescriptorSet();
void drawPost(vk::CommandBuffer cmdBuf);
void drawPost(VkCommandBuffer cmdBuf);
nvvk::DescriptorSetBindings m_postDescSetLayoutBind;
vk::DescriptorPool m_postDescPool;
vk::DescriptorSetLayout m_postDescSetLayout;
vk::DescriptorSet m_postDescSet;
vk::Pipeline m_postPipeline;
vk::PipelineLayout m_postPipelineLayout;
vk::RenderPass m_offscreenRenderPass;
vk::Framebuffer m_offscreenFramebuffer;
VkDescriptorPool m_postDescPool{VK_NULL_HANDLE};
VkDescriptorSetLayout m_postDescSetLayout{VK_NULL_HANDLE};
VkDescriptorSet m_postDescSet{VK_NULL_HANDLE};
VkPipeline m_postPipeline{VK_NULL_HANDLE};
VkPipelineLayout m_postPipelineLayout{VK_NULL_HANDLE};
VkRenderPass m_offscreenRenderPass{VK_NULL_HANDLE};
VkFramebuffer m_offscreenFramebuffer{VK_NULL_HANDLE};
nvvk::Texture m_offscreenColor;
vk::Format m_offscreenColorFormat{vk::Format::eR32G32B32A32Sfloat};
nvvk::Texture m_offscreenDepth;
vk::Format m_offscreenDepthFormat{vk::Format::eX8D24UnormPack32};
VkFormat m_offscreenColorFormat{VK_FORMAT_R32G32B32A32_SFLOAT};
VkFormat m_offscreenDepthFormat{VK_FORMAT_X8_D24_UNORM_PACK32};
// #VKRay
void initRayTracing();
@ -137,19 +134,18 @@ public:
void createRtDescriptorSet();
void updateRtDescriptorSet();
void createRtPipeline();
void raytrace(const vk::CommandBuffer& cmdBuf, const nvmath::vec4f& clearColor);
void raytrace(const VkCommandBuffer& cmdBuf, const nvmath::vec4f& clearColor);
vk::PhysicalDeviceRayTracingPipelinePropertiesKHR m_rtProperties;
nvvk::RaytracingBuilderKHR m_rtBuilder;
nvvk::DescriptorSetBindings m_rtDescSetLayoutBind;
vk::DescriptorPool m_rtDescPool;
vk::DescriptorSetLayout m_rtDescSetLayout;
vk::DescriptorSet m_rtDescSet;
std::vector<vk::RayTracingShaderGroupCreateInfoKHR> m_rtShaderGroups;
vk::PipelineLayout m_rtPipelineLayout;
vk::Pipeline m_rtPipeline;
nvvk::Buffer m_rtSBTBuffer;
VkPhysicalDeviceRayTracingPipelinePropertiesKHR m_rtProperties{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR};
nvvk::RaytracingBuilderKHR m_rtBuilder;
nvvk::DescriptorSetBindings m_rtDescSetLayoutBind;
VkDescriptorPool m_rtDescPool;
VkDescriptorSetLayout m_rtDescSetLayout;
VkDescriptorSet m_rtDescSet;
std::vector<VkRayTracingShaderGroupCreateInfoKHR> m_rtShaderGroups;
VkPipelineLayout m_rtPipelineLayout;
VkPipeline m_rtPipeline;
struct RtPushConstant
{

View file

@ -23,18 +23,15 @@
// at the top of imgui.cpp.
#include <array>
#include <vulkan/vulkan.hpp>
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
#include "backends/imgui_impl_glfw.h"
#include "hello_vulkan.h"
#include "imgui.h"
#include "hello_vulkan.h"
#include "imgui/imgui_camera_widget.h"
#include "nvh/cameramanipulator.hpp"
#include "nvh/fileoperations.hpp"
#include "nvpsystem.hpp"
#include "nvvk/appbase_vkpp.hpp"
#include "nvvk/commands_vk.hpp"
#include "nvvk/context_vk.hpp"
@ -46,6 +43,7 @@ VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
// Default search path for shaders
std::vector<std::string> defaultSearchPaths;
// GLFW Callback functions
static void onErrorCallback(int error, const char* description)
{
@ -56,7 +54,7 @@ static void onErrorCallback(int error, const char* description)
void renderUI(HelloVulkan& helloVk)
{
ImGuiH::CameraWidget();
if(ImGui::CollapsingHeader("Light", ImGuiTreeNodeFlags_DefaultOpen))
if(ImGui::CollapsingHeader("Light"))
{
ImGui::RadioButton("Point", &helloVk.m_pushConstant.lightType, 0);
ImGui::SameLine();
@ -104,8 +102,7 @@ int main(int argc, char** argv)
return 1;
}
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
GLFWwindow* window =
glfwCreateWindow(SAMPLE_WIDTH, SAMPLE_HEIGHT, PROJECT_NAME, nullptr, nullptr);
GLFWwindow* window = glfwCreateWindow(SAMPLE_WIDTH, SAMPLE_HEIGHT, PROJECT_NAME, nullptr, nullptr);
// Setup camera
CameraManip.setWindowSize(SAMPLE_WIDTH, SAMPLE_HEIGHT);
@ -132,12 +129,13 @@ int main(int argc, char** argv)
nvvk::ContextCreateInfo contextInfo(true);
contextInfo.setVersion(1, 2);
contextInfo.addInstanceLayer("VK_LAYER_LUNARG_monitor", true);
contextInfo.addInstanceExtension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, true);
contextInfo.addInstanceExtension(VK_KHR_SURFACE_EXTENSION_NAME);
#ifdef _WIN32
contextInfo.addInstanceExtension(VK_KHR_WIN32_SURFACE_EXTENSION_NAME, false);
contextInfo.addInstanceExtension(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
#else
contextInfo.addInstanceExtension(VK_KHR_XLIB_SURFACE_EXTENSION_NAME, false);
contextInfo.addInstanceExtension(VK_KHR_XCB_SURFACE_EXTENSION_NAME, false);
contextInfo.addInstanceExtension(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
contextInfo.addInstanceExtension(VK_KHR_XCB_SURFACE_EXTENSION_NAME);
#endif
contextInfo.addInstanceExtension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
contextInfo.addDeviceExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
@ -145,18 +143,16 @@ int main(int argc, char** argv)
contextInfo.addDeviceExtension(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
contextInfo.addDeviceExtension(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME);
contextInfo.addDeviceExtension(VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME);
// #VKRay: Activate the ray tracing extension
VkPhysicalDeviceAccelerationStructureFeaturesKHR accelFeature{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR};
contextInfo.addDeviceExtension(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME, false, &accelFeature);
VkPhysicalDeviceRayTracingPipelineFeaturesKHR rtPipelineFeature{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR};
contextInfo.addDeviceExtension(VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME, false, &rtPipelineFeature);
contextInfo.addDeviceExtension(VK_KHR_MAINTENANCE3_EXTENSION_NAME);
contextInfo.addDeviceExtension(VK_KHR_PIPELINE_LIBRARY_EXTENSION_NAME);
contextInfo.addDeviceExtension(VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME);
contextInfo.addDeviceExtension(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME);
vk::PhysicalDeviceAccelerationStructureFeaturesKHR accelFeature;
contextInfo.addDeviceExtension(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME, false,
&accelFeature);
vk::PhysicalDeviceRayTracingPipelineFeaturesKHR rtPipelineFeature;
contextInfo.addDeviceExtension(VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME, false,
&rtPipelineFeature);
// Creating Vulkan base application
nvvk::Context vkctx{};
@ -171,11 +167,10 @@ int main(int argc, char** argv)
HelloVulkan helloVk;
// Window need to be opened to get the surface on which to draw
const vk::SurfaceKHR surface = helloVk.getVkSurface(vkctx.m_instance, window);
const VkSurfaceKHR surface = helloVk.getVkSurface(vkctx.m_instance, window);
vkctx.setGCTQueueWithPresent(surface);
helloVk.setup(vkctx.m_instance, vkctx.m_device, vkctx.m_physicalDevice,
vkctx.m_queueGCT.familyIndex);
helloVk.setup(vkctx.m_instance, vkctx.m_device, vkctx.m_physicalDevice, vkctx.m_queueGCT.familyIndex);
helloVk.createSwapchain(surface, SAMPLE_WIDTH, SAMPLE_HEIGHT);
helloVk.createDepthBuffer();
helloVk.createRenderPass();
@ -234,8 +229,7 @@ int main(int argc, char** argv)
ImGui::Checkbox("Ray Tracer mode", &useRaytracer); // Switch between raster and ray tracing
renderUI(helloVk);
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)",
1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
ImGuiH::Control::Info("", "", "(F10) Toggle Pane", ImGuiH::Control::Flags::Disabled);
ImGuiH::Panel::End();
}
@ -244,28 +238,29 @@ int main(int argc, char** argv)
helloVk.prepareFrame();
// Start command buffer of this frame
auto curFrame = helloVk.getCurFrame();
const vk::CommandBuffer& cmdBuf = helloVk.getCommandBuffers()[curFrame];
auto curFrame = helloVk.getCurFrame();
const VkCommandBuffer& cmdBuf = helloVk.getCommandBuffers()[curFrame];
cmdBuf.begin({vk::CommandBufferUsageFlagBits::eOneTimeSubmit});
VkCommandBufferBeginInfo beginInfo{VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
vkBeginCommandBuffer(cmdBuf, &beginInfo);
// Updating camera buffer
helloVk.updateUniformBuffer(cmdBuf);
// Clearing screen
std::array<vk::ClearValue, 2> clearValues;
clearValues[0].setColor(
std::array<float, 4>({clearColor[0], clearColor[1], clearColor[2], clearColor[3]}));
clearValues[1].setDepthStencil({1.0f, 0});
std::array<VkClearValue, 2> clearValues{};
clearValues[0].color = {{clearColor[0], clearColor[1], clearColor[2], clearColor[3]}};
clearValues[1].depthStencil = {1.0f, 0};
// Offscreen render pass
{
vk::RenderPassBeginInfo offscreenRenderPassBeginInfo;
offscreenRenderPassBeginInfo.setClearValueCount(2);
offscreenRenderPassBeginInfo.setPClearValues(clearValues.data());
offscreenRenderPassBeginInfo.setRenderPass(helloVk.m_offscreenRenderPass);
offscreenRenderPassBeginInfo.setFramebuffer(helloVk.m_offscreenFramebuffer);
offscreenRenderPassBeginInfo.setRenderArea({{}, helloVk.getSize()});
VkRenderPassBeginInfo offscreenRenderPassBeginInfo{VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO};
offscreenRenderPassBeginInfo.clearValueCount = 2;
offscreenRenderPassBeginInfo.pClearValues = clearValues.data();
offscreenRenderPassBeginInfo.renderPass = helloVk.m_offscreenRenderPass;
offscreenRenderPassBeginInfo.framebuffer = helloVk.m_offscreenFramebuffer;
offscreenRenderPassBeginInfo.renderArea = {{0, 0}, helloVk.getSize()};
// Rendering Scene
if(useRaytracer)
@ -274,40 +269,40 @@ int main(int argc, char** argv)
}
else
{
cmdBuf.beginRenderPass(offscreenRenderPassBeginInfo, vk::SubpassContents::eInline);
vkCmdBeginRenderPass(cmdBuf, &offscreenRenderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
helloVk.rasterize(cmdBuf);
cmdBuf.endRenderPass();
vkCmdEndRenderPass(cmdBuf);
}
}
// 2nd rendering pass: tone mapper, UI
{
vk::RenderPassBeginInfo postRenderPassBeginInfo;
postRenderPassBeginInfo.setClearValueCount(2);
postRenderPassBeginInfo.setPClearValues(clearValues.data());
postRenderPassBeginInfo.setRenderPass(helloVk.getRenderPass());
postRenderPassBeginInfo.setFramebuffer(helloVk.getFramebuffers()[curFrame]);
postRenderPassBeginInfo.setRenderArea({{}, helloVk.getSize()});
VkRenderPassBeginInfo postRenderPassBeginInfo{VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO};
postRenderPassBeginInfo.clearValueCount = 2;
postRenderPassBeginInfo.pClearValues = clearValues.data();
postRenderPassBeginInfo.renderPass = helloVk.getRenderPass();
postRenderPassBeginInfo.framebuffer = helloVk.getFramebuffers()[curFrame];
postRenderPassBeginInfo.renderArea = {{0, 0}, helloVk.getSize()};
cmdBuf.beginRenderPass(postRenderPassBeginInfo, vk::SubpassContents::eInline);
// Rendering tonemapper
vkCmdBeginRenderPass(cmdBuf, &postRenderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
helloVk.drawPost(cmdBuf);
// Rendering UI
ImGui::Render();
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmdBuf);
cmdBuf.endRenderPass();
vkCmdEndRenderPass(cmdBuf);
}
// Submit for display
cmdBuf.end();
vkEndCommandBuffer(cmdBuf);
helloVk.submitFrame();
}
// Cleanup
helloVk.getDevice().waitIdle();
vkDeviceWaitIdle(helloVk.getDevice());
helloVk.destroyResources();
helloVk.destroy();
vkctx.deinit();
glfwDestroyWindow(window);