Application methods enabled (uncommented)
| # | Call | File | Status |
|---|---|---|---|
| 1 | createInstance(app->doApiDump) | vkapp_init.cpp | done |
| 2 | createPhysicalDevice() | vkapp_init.cpp | done |
| 3 | chooseQueueIndex() | vkapp_init.cpp | done |
| 4 | createDevice() | vkapp_init.cpp | done |
| 5 | getCommandQueue() | vkapp_init.cpp | done |
| 6 | loadExtensions() | extensions_vk.cpp | done |
| 7 | getSurface() | vkapp_init.cpp | done |
| 8 | createSwapchain() | vkapp_init.cpp | done |
| 9 | createCommandPool() | vkapp_init.cpp | done |
| 10 | createDepthResource() | vkapp_postProcess.cpp | done |
| — | createScRenderTarget() | vkapp_postProcess.cpp | left commented (project 2) |
| — | createPostDescriptor() | vkapp_postProcess.cpp | left commented (project 2) |
| 11 | createPostPipeline() | vkapp_postProcess.cpp | done |
| 12 | drawFrame(): all four phases | vkapp_runtime.cpp | done |
| 13 | renderFrame(): postProcess() only | vkapp_runtime.cpp | done |
| 14 | 3 fence/idle lines in destroyAllVulkanResources() | vkapp.cpp | done |
Each section below comes from an incremental build, one step at a time up to the full build that produces the required output of the post-process pipeline. The log from that full run is at the end, under Validation-layer verification.
Initialization
The three-step enumeration
Understand the following three step process for requesting a variable length list from Vulkan. You will use this many more times.
Every list retrieved from Vulkan, from instance layers to surface formats, follows the same three step process:
- Ask Vulkan for the count only, passing
nullptrfor the data pointer. - Create a
std::vector<...>to that count. - Call Vulkan again with the vector’s data pointer to have it filled.
Rather than write it out nine times, it is wrapped once in a VK_ENUM_TO_VECTOR
macro that takes the element type, the enumerating function, and any leading
arguments, and returns the filled vector:
std::vector<VkLayerProperties> availableLayers =
VK_ENUM_TO_VECTOR(VkLayerProperties, vkEnumerateInstanceLayerProperties);
std::vector<VkSurfaceFormatKHR> formats =
VK_ENUM_TO_VECTOR(VkSurfaceFormatKHR, vkGetPhysicalDeviceSurfaceFormatsKHR,
m_physicalDevice, m_surface);
Both calls in the pair are checked for a successful call output, which is why the log entries show each enumerating function succeeding twice in a row: once for the count, once for the data.
createInstance
(a) GLFW-required instance extensions, appended to reqInstanceExtensions:
[12:26:51.454] info ---- GLFW required extensions [count : 2] ------------------------------
[12:26:51.454] info VK_KHR_surface
[12:26:51.454] info VK_KHR_win32_surface
(b) Available instance layers (vkEnumerateInstanceLayerProperties):
[12:27:45.968] info ---- InstanceLayers Info [count: 24 ] ----------------------------------
[12:27:45.969] info VK_LAYER_NV_optimus
[12:27:45.969] info VK_LAYER_NV_present
[12:27:45.969] info VK_LAYER_AMD_switchable_graphics
[12:27:45.969] info GalaxyOverlayVkLayer
[12:27:45.969] info GalaxyOverlayVkLayer_VERBOSE
[12:27:45.969] info GalaxyOverlayVkLayer_DEBUG
[12:27:45.969] info VK_LAYER_EOS_Overlay
[12:27:45.969] info VK_LAYER_NV_nomad_release_public_2025_4_1
[12:27:45.969] info VK_LAYER_NV_GPU_Trace_release_public_2025_4_1
[12:27:45.969] info VK_LAYER_NV_ngfx_capture_release_public_2025_4_1
[12:27:45.970] info VK_LAYER_NV_shader_debugger_release_public_2025_4_1
[12:27:45.970] info VK_LAYER_OBS_HOOK
[12:27:45.970] info VK_LAYER_VALVE_steam_overlay
[12:27:45.970] info VK_LAYER_VALVE_steam_fossilize
[12:27:45.970] info VK_LAYER_ROCKSTAR_GAMES_social_club
[12:27:45.970] info VK_LAYER_LUNARG_api_dump
[12:27:45.970] info VK_LAYER_LUNARG_gfxreconstruct
[12:27:45.970] info VK_LAYER_KHRONOS_synchronization2
[12:27:45.970] info VK_LAYER_KHRONOS_validation
[12:27:45.970] info VK_LAYER_LUNARG_monitor
[12:27:45.970] info VK_LAYER_LUNARG_screenshot
[12:27:45.970] info VK_LAYER_KHRONOS_profiles
[12:27:45.971] info VK_LAYER_KHRONOS_shader_object
[12:27:45.971] info VK_LAYER_LUNARG_crash_diagnostic
VK_LAYER_KHRONOS_validation found in the instance layer info: yes.
(c) Available instance extensions (vkEnumerateInstanceExtensionProperties):
[12:34:43.985] info ---- InstanceExtensions Info [count: 20 ] ------------------------------
[12:34:43.986] info VK_KHR_device_group_creation
[12:34:43.986] info VK_KHR_display
[12:34:43.986] info VK_KHR_external_fence_capabilities
[12:34:43.986] info VK_KHR_external_memory_capabilities
[12:34:43.987] info VK_KHR_external_semaphore_capabilities
[12:34:43.987] info VK_KHR_get_display_properties2
[12:34:43.987] info VK_KHR_get_physical_device_properties2
[12:34:43.987] info VK_KHR_get_surface_capabilities2
[12:34:43.987] info VK_KHR_surface
[12:34:43.988] info VK_KHR_surface_maintenance1
[12:34:43.988] info VK_KHR_surface_protected_capabilities
[12:34:43.988] info VK_KHR_win32_surface
[12:34:43.988] info VK_EXT_debug_report
[12:34:43.988] info VK_EXT_debug_utils
[12:34:43.989] info VK_EXT_direct_mode_display
[12:34:43.989] info VK_EXT_surface_maintenance1
[12:34:43.989] info VK_EXT_swapchain_colorspace
[12:34:43.989] info VK_NV_external_memory_capabilities
[12:34:43.989] info VK_KHR_portability_enumeration
[12:34:43.990] info VK_LUNARG_direct_driver_loading
- Result checked:
vkCreateInstanceagainstVK_SUCCESS, throwingstd::runtime_erroron failure. Verified via the trace log below. - Destroy registered:
vkDestroyInstance
[12:48:02.018] trace vkCheck successful vkCreateInstance(&instanceCreateInfo, nullptr, &m_instance)
createPhysicalDevice: chosen and rejected GPUs
This code is in a loop iterating variable physicalDevice through a list of all physicalDevices (GPUs) … and here we judge if the physicalDevice is compatible with our requirements.
| GPU | deviceType | All reqDeviceExtensions present? | Verdict |
|---|---|---|---|
| NVIDIA GeForce RTX 4090 | DISCRETE_GPU | yes | chosen |
| AMD Radeon(TM) Graphics | INTEGRATED_GPU | n/a | rejected, not discrete |
[13:25:51.060] info ---- Phyiscal Devices Info [count : 2] --------------------------------
[13:25:51.060] info Checking GPU : NVIDIA GeForce RTX 4090
[13:25:51.060] info Found discrete GPU
[13:25:51.061] trace Checking extensions
[13:25:51.061] info All required extensions found
[13:25:51.061] info Checking GPU : AMD Radeon(TM) Graphics
[13:25:51.061] info Rejecting GPU since its non discrete : [deviceType : VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU]
Selection rule used: a device is compatible iff it is
VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU and every entry of
reqDeviceExtensions appears in its extension list.
Iterated using an unordered set of available extension names to compare against the required extensions; only one discrete GPU was found.
chooseQueueIndex: queue families
How many queue families does your Vulkan offer? Which of the three flags does each offer? Which of them, by index, has the above three required flags?
Families reported: 6.
| Index | GRAPHICS | COMPUTE | TRANSFER | Queue count | Other flags |
|---|---|---|---|---|---|
| 0 | yes | yes | yes | 4 | VK_QUEUE_SPARSE_BINDING_BIT |
| 1 | yes | 2 | VK_QUEUE_SPARSE_BINDING_BIT | ||
| 2 | yes | yes | 3 | VK_QUEUE_SPARSE_BINDING_BIT | |
| 3 | yes | 3 | VK_QUEUE_SPARSE_BINDING_BIT, VK_QUEUE_VIDEO_DECODE_BIT_KHR | ||
| 4 | yes | 3 | VK_QUEUE_SPARSE_BINDING_BIT, VK_QUEUE_VIDEO_ENCODE_BIT_KHR | ||
| 5 | yes | 3 | VK_QUEUE_SPARSE_BINDING_BIT, VK_QUEUE_OPTICAL_FLOW_BIT_NV |
[13:39:39.795] info ---- Queue Properties Info : [queue family count : 6] ------------------
[13:39:39.795] info Required queue flags found at queueindex : 0, full flag list : VK_QUEUE_GRAPHICS_BIT|VK_QUEUE_COMPUTE_BIT|VK_QUEUE_TRANSFER_BIT|VK_QUEUE_SPARSE_BINDING_BIT
[13:39:39.795] info Required queue flags not found for queueIndex : 1 , found : VK_QUEUE_TRANSFER_BIT|VK_QUEUE_SPARSE_BINDING_BIT
[13:39:39.795] info Required queue flags not found for queueIndex : 2 , found : VK_QUEUE_COMPUTE_BIT|VK_QUEUE_TRANSFER_BIT|VK_QUEUE_SPARSE_BINDING_BIT
[13:39:39.796] info Required queue flags not found for queueIndex : 3 , found : VK_QUEUE_TRANSFER_BIT|VK_QUEUE_SPARSE_BINDING_BIT|VK_QUEUE_VIDEO_DECODE_BIT_KHR
[13:39:39.796] info Required queue flags not found for queueIndex : 4 , found : VK_QUEUE_TRANSFER_BIT|VK_QUEUE_SPARSE_BINDING_BIT|VK_QUEUE_VIDEO_ENCODE_BIT_KHR
[13:39:39.796] info Required queue flags not found for queueIndex : 5 , found : VK_QUEUE_TRANSFER_BIT|VK_QUEUE_SPARSE_BINDING_BIT|VK_QUEUE_OPTICAL_FLOW_BIT_NV
m_graphicsQueueIndex = 0, the first family with
GRAPHICS | COMPUTE | TRANSFER set.
createDevice
The pNext chain is built bottom-up, each structure’s second initializer
pointing at the one below it, so features2 ends up as the head of the whole
chain:
features2 → features11 → features12 → features13 → accelFeature → rtPipelineFeature → NULL
VkPhysicalDeviceRayTracingPipelineFeaturesKHR rtPipelineFeatureVkPhysicalDeviceAccelerationStructureFeaturesKHR accelFeatureVkPhysicalDeviceVulkan13Features features13VkPhysicalDeviceVulkan12Features features12VkPhysicalDeviceVulkan11Features features11VkPhysicalDeviceFeatures2 features2, passed asdeviceCreateInfo.pNext
vkGetPhysicalDeviceFeatures2 fills all six in one call, and
features13.dynamicRendering == VK_TRUE is asserted before proceeding.
- Result checked:
vkCreateDevice - Destroy registered:
vkDestroyDevice
[13:48:07.395] trace vkCheck successful vkCreateDevice(m_physicalDevice, &deviceCreateInfo, nullptr, &m_device)
getCommandQueue / loadExtensions
Neither step produces any output that needs checking:
getCommandQueue:vkGetDeviceQueuereturnsvoid, so there is nothing to verify or destroy, since the queue is owned by the deviceloadExtensions: callsload_VK_EXTENSIONS, a generated function that loads the symbols for every registered extension. No resource is created
Application launch successful, no validation errors.
getSurface
- Results checked (three):
glfwCreateWindowSurface,vkGetPhysicalDeviceSurfaceSupportKHR, andisSupported == VK_TRUE - Destroy registered:
vkDestroySurfaceKHR
[13:54:48.995] trace vkCheck successful glfwCreateWindowSurface(m_instance, app->GLFW_window, nullptr, &m_surface)
[13:54:48.997] trace vkCheck successful vkGetPhysicalDeviceSurfaceSupportKHR(m_physicalDevice, m_graphicsQueueIndex, m_surface, &isSupported)
[13:54:48.997] info Vulkan supports presenting on GLFW window surface
createSwapchain: present modes and surface formats
Document your PRESENT MODEs. I especially want to know if your system offers VK_PRESENT_MODE_MAILBOX_KHR mode. My high-end windows desktop does; My higher-end Linux laptop doesn’t.
[16:02:15.539] info ---- Present Mode Info : [count : 5] -----------------------------------
[16:02:15.540] info VK_PRESENT_MODE_FIFO_KHR
[16:02:15.540] info VK_PRESENT_MODE_FIFO_RELAXED_KHR
[16:02:15.540] info Mailbox present mode support found. Mailbox mode will be preferred.
[16:02:15.540] info VK_PRESENT_MODE_MAILBOX_KHR
[16:02:15.540] info VK_PRESENT_MODE_IMMEDIATE_KHR
[16:02:15.540] info VK_PRESENT_MODE_FIFO_LATEST_READY_KHR
VK_PRESENT_MODE_MAILBOX_KHR offered: yes, so MAILBOX is the mode selected.
Then search the formats to choose format VK_FORMAT_B8G8R8A8_UNORM (and its color space) if such exists. Document your list of formats/color-spaces, and your particular choice.
| # | VkFormat | VkColorSpaceKHR |
|---|---|---|
| 0 | VK_FORMAT_B8G8R8A8_UNORM | VK_COLOR_SPACE_SRGB_NONLINEAR_KHR |
| 1 | VK_FORMAT_B8G8R8A8_SRGB | VK_COLOR_SPACE_SRGB_NONLINEAR_KHR |
| 2 | VK_FORMAT_R8G8B8A8_UNORM | VK_COLOR_SPACE_SRGB_NONLINEAR_KHR |
| 3 | VK_FORMAT_R8G8B8A8_SRGB | VK_COLOR_SPACE_SRGB_NONLINEAR_KHR |
| 4 | VK_FORMAT_R16G16B16A16_SFLOAT | VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT |
| 5 | VK_FORMAT_A2B10G10R10_UNORM_PACK32 | VK_COLOR_SPACE_HDR10_ST2084_EXT |
| 6 | VK_FORMAT_A2B10G10R10_UNORM_PACK32 | VK_COLOR_SPACE_SRGB_NONLINEAR_KHR |
[16:02:15.548] info ---- Surface Format Info : [count : 7] ---------------------------------
[16:02:15.549] info VK_FORMAT_B8G8R8A8_UNORM
[16:02:15.549] info VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
[16:02:15.549] info VK_FORMAT_B8G8R8A8_SRGB
[16:02:15.550] info VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
[16:02:15.550] info VK_FORMAT_R8G8B8A8_UNORM
[16:02:15.551] info VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
[16:02:15.551] info VK_FORMAT_R8G8B8A8_SRGB
[16:02:15.551] info VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
[16:02:15.552] info VK_FORMAT_R16G16B16A16_SFLOAT
[16:02:15.552] info VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT
[16:02:15.552] info VK_FORMAT_A2B10G10R10_UNORM_PACK32
[16:02:15.552] info VK_COLOR_SPACE_HDR10_ST2084_EXT
[16:02:15.553] info VK_FORMAT_A2B10G10R10_UNORM_PACK32
[16:02:15.553] info VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
Chosen: VK_FORMAT_B8G8R8A8_UNORM / VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, found
directly in the list (entry 0), so no fallback to formats[0] is required.
B8G8R8A8 is the most broadly supported native swapchain layout, and the
UNORM (rather than _SRGB) variant provides manual control of gamma/tone-mapping
in postProcess.frag, avoiding a double gamma-correction when that shader applies its own curve.
The HDR-capable entries
(R16G16B16A16_SFLOAT/EXTENDED_SRGB_LINEAR_EXT and
A2B10G10R10_UNORM_PACK32/HDR10_ST2084_EXT) are available on this display
but kept out of scope for Project 1. They will be considered either as a toggle or as an
auto detect mechanism from project 2 onwards.
Other swapchain facts:
m_imageCount = capabilities.minImageCount = 2.
Window extent taken from capabilities.currentExtent.
[16:02:15.535] info ---- Capabilites Info --------------------------------------------------
[16:02:15.536] info Min Image Count : 2
[16:02:15.536] info Window Extent : Width 1280 , Height 768
- Results checked:
vkCreateSwapchainKHRandvkGetSwapchainImagesKHR, after retrieving the swapchain image list
[16:02:15.847] trace vkCheck successful vkCreateSwapchainKHR(m_device, &createInfo, nullptr, &m_swapchain)
[16:02:15.848] trace vkCheck successful vkGetSwapchainImagesKHR
- Destroy registered:
destroySwapchain(), which takes the per-image views and the three sync objects in a0..m_imageCountloop, thenvkDestroySwapchainKHR
createCommandPool
- Results checked:
vkCreateCommandPoolandvkAllocateCommandBuffers - Destroy registered:
vkDestroyCommandPool, which frees the command buffer with it
[16:07:24.183] trace vkCheck successful vkCreateCommandPool(m_device, &poolCreateInfo, nullptr, &m_cmdPool)
[16:07:24.187] trace vkCheck successful vkAllocateCommandBuffers(m_device, &allocateInfo, m_commandBuffers.data())
createDepthResource
m_depthImage is an ImageWrap (a small wrapper bundling the image, its
memory, view and sampler) built at the current window extent:
| Parameter | Value |
|---|---|
| Extent | m_windowSize |
| Format | VK_FORMAT_D32_SFLOAT |
| Usage | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT |
| Memory | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
| Aspect | VK_IMAGE_ASPECT_DEPTH_BIT |
| Layout | VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL |
| Mip levels | 1 |
- Destroy registered:
m_depthImage.destroy(m_device) - Resize: destroyed at the top of
recreateSizedVulkanResourcesand recreated at the bottom, since its extent is the window extent
createPostPipeline
Project 1: The best we can do for now … Projects 2 and beyond: What we eventually need.
Per the @@ comment Project 1 uses the first of the two pipeline layouts, because
createPostDescriptor() is still commented out and there is no descriptor set
layout to point at yet:
// Project 1: the best we can do for now
createInfo.setLayoutCount = 0;
createInfo.pSetLayouts = nullptr;
// Projects 2 and beyond: what we eventually need
//createInfo.setLayoutCount = 1;
//createInfo.pSetLayouts = &m_postDesc.descSetLayout;
The push constant range is used in the vertex and fragment stages with the
PushConstantPost struct, which has the window attributes width and height. It has been
extended to add a time attribute, obtained via glfwGetTime(), for use with the Mandelbulb
fractal in the shader.
- Built with: dynamic rendering, so there is no
VkRenderPass - Destroy registered:
vkDestroyPipeline+vkDestroyPipelineLayout - Resize: both destroyed and recreated in
recreateSizedVulkanResources
Runtime
drawFrame(): all four phases uncommented, in the orderacquireFrame(),renderGui(),renderFrame(),submitFrame()renderFrame(): records exactly one command this project,postProcess(). TheupdateCameraBuffer/raytrace/denoise/rasterizelines stay commented outsubmitFrame():vkQueueSubmitandvkQueuePresentKHRresults checked- Resize: the
throw std::runtime_errorat the top ofrecreateSizedVulkanResourceswas removed and the destroy/recreate pairs added. Resizing rebuilds the swapchain, depth resource and post pipeline against the new extent; the log in the final section shows two such rebuilds (1280×768 → 2560×1369 → 1280×768) with no validation messages.VK_ERROR_OUT_OF_DATE_KHRfromvkAcquireNextImageKHRalso has been added to the same set of checks.
Debugging note: swapchain acquire timeout and image/semaphore indexing
Two related validation-layer issues surfaced while wiring up acquireFrame() /
submitFrame().
(a) vkAcquireNextImageKHR timeout. By default the acquire call was written with a
hard-coded 1000000 ns (1 ms) timeout in place of UINT64_MAX:
VkResult result = vkAcquireNextImageKHR(m_device, m_swapchain, 1000000,//UINT64_MAX,
m_imageAvailableSemaphores[currentFrame], ...);
Under normal FIFO/vsync timing an image is frequently not ready within 1 ms, so
vkAcquireNextImageKHR returned VK_TIMEOUT.
Per the Vulkan spec, the semaphore is not
signaled on VK_TIMEOUT, so the subsequent vkQueueSubmit waited on a semaphore
that had no pending signal operation:
Validation Error: [ VUID-vkQueueSubmit-pWaitSemaphores-03238 ]
vkQueueSubmit(): pSubmits[0].pWaitSemaphores[0] queue ... is waiting on semaphore
(VkSemaphore ...[m_imageAvailableSemaphore]) that has no way to be signaled.
Fix: restored UINT64_MAX so acquireFrame() blocks until an image is available.
(b) currentFrame used in place of the acquired image index. After fixing
(a), a second pair of errors appeared, this time only under
VK_PRESENT_MODE_MAILBOX_KHR:
Validation Error: [ UNASSIGNED-non-acquired-swapchain-image-used ]
vkQueueSubmit(): pSubmits[0] performs a layout transition on presentable VkImage ...,
but the image has not been acquired from VkSwapchainKHR ... (either never or since
the last present operation).
Validation Error: [ VUID-vkQueueSubmit-pSignalSemaphores-00067 ]
vkQueueSubmit(): pSubmits[0].pSignalSemaphores[0] (VkSemaphore ...[m_renderFinishedSemaphore])
is being signaled ..., but it may still be in use by VkSwapchainKHR ...
Most recently acquired image indices: 0, 1, 0, 1, 0, [1], 0, 0.
Swapchain image 1 was presented but was not re-acquired, so VkSemaphore
...[m_renderFinishedSemaphore] may still be in use and cannot be safely reused
with image index 0.
Root cause: postProcess() selected the swapchain image/view by
currentFrame (a free-running CPU “frame-in-flight” counter,
(currentFrame+1) % m_imageCount) instead of m_swapchainIndex (the actual
index returned by vkAcquireNextImageKHR).
The Vulkan spec does not guarantee
acquired images come back in round-robin order for any present mode: FIFO
only guarantees presentation order, not acquire order. Most drivers seem to
behave like round-robin under FIFO with a small image count, which coincidentally
kept currentFrame == m_swapchainIndex and masked the bug.
Under MAILBOX the presentation engine is more likely to return images in a different order
from the one it was handed (it can drop a
queued but unshown image and recycle that slot early), which is what exposed the
drift. The same submission then also signaled
m_renderFinishedSemaphores[currentFrame] rather than the slot tied to the
image actually being presented (represented by m_swapchainIndex), tripping the semaphore reuse check.
Fix: index everything that identifies the physical swapchain image by
m_swapchainIndex instead of currentFrame:
vkapp_postProcess.cpp:colorAttachment.imageViewand bothWrapPipelineBarriercalls now usem_imageViews[m_swapchainIndex]/m_swapchainImages[m_swapchainIndex].vkapp_runtime.cppsubmitFrame():pSignalSemaphoresandpWaitSemaphoresnow usem_renderFinishedSemaphores[m_swapchainIndex].
m_imageAvailableSemaphores[currentFrame], m_inFlightFences[currentFrame], and
m_commandBuffers[currentFrame] are left indexed by currentFrame. That
semaphore is handed to vkAcquireNextImageKHR before the image index is known,
so it must stay tied to a rotating frame-in-flight slot rather than image
identity, and the fence and command buffer selection are CPU side pacing concerns,
not a matter of image identity.
Cleanup and resize
destroyAllVulkanResources
The three fence/idle lines at the top were uncommented as directed: wait on the
in-flight fence, reset it, then vkDeviceWaitIdle, so that nothing is still
executing when the scope reaches the destroy call for the objects below.
Destruction runs in reverse creation order, and every device-owned object is
gone before m_device itself:
vkDestroyPipeline(m_device, m_postPipeline, nullptr);
vkDestroyPipelineLayout(m_device, m_postPipelineLayout, nullptr);
m_depthImage.destroy(m_device);
vkDestroyCommandPool(m_device, m_cmdPool, nullptr);
destroySwapchain();
vkDestroySurfaceKHR(m_instance, m_surface, nullptr);
vkDestroyDevice(m_device, nullptr);
vkDestroyInstance(m_instance, nullptr);
m_surface is destroyed with m_instance as its parent, not m_device, which
is why it is added below the device-owned group and above vkDestroyDevice.
destroySwapchain() is its own procedure because it has to loop
0..m_imageCount over the per-image views and the three synchronization objects
before destroying the swapchain itself:
for (int idx = 0; idx < m_imageCount; ++idx) {
vkDestroyImageView(m_device, m_imageViews[idx], nullptr);
vkDestroyFence(m_device, m_inFlightFences[idx], nullptr);
vkDestroySemaphore(m_device, m_imageAvailableSemaphores[idx], nullptr);
vkDestroySemaphore(m_device, m_renderFinishedSemaphores[idx], nullptr);
}
vkDestroySwapchainKHR(m_device, m_swapchain, nullptr);
recreateSizedVulkanResources
Each resized resource needs two entries: a destroy near the top and a recreate
near the bottom, on either side of the glfwGetFramebufferSize call that provides the
new extent. Three resources qualify for the destroy-recreate pattern, all of them dependent on
the window size:
- Destroyed at the top:
m_postPipeline,m_postPipelineLayout,m_depthImage, thendestroySwapchain() - Recreated at the bottom:
createSwapchain(),createDepthResource(),createPostPipeline()
Result
The stock black/red/green/yellow gradient was replaced with a
Mandelbulb fractal ported from Shadertoy. The source was adapted to the Vulkan
pipeline by extending the existing push constants to carry time from
glfwGetTime, alongside the width and height already passed.
Shadertoy source: shadertoy.com/view/3tXXzX
Validation-layer verification
VK_LAYER_KHRONOS_validation was requested in reqInstanceLayers and confirmed
present in the layer list above. The full session, from startup through the draw loop, two
window resizes, and a clean shutdown via Esc, produced no validation-layer
messages.
Complete console output of that run:
[18:57:50.510] info Vulkan SDK version 1.4.357
[18:57:50.511] info ---- GLFW required extensions [count : 2] ------------------------------
[18:57:50.512] info VK_KHR_surface
[18:57:50.512] info VK_KHR_win32_surface
[18:57:50.520] trace vkCheck successful vkEnumerateInstanceLayerProperties
[18:57:50.526] trace vkCheck successful vkEnumerateInstanceLayerProperties
[18:57:50.526] info ---- InstanceLayers Info [count: 24] -----------------------------------
[18:57:50.527] info VK_LAYER_NV_optimus
[18:57:50.527] info VK_LAYER_NV_present
[18:57:50.527] info VK_LAYER_AMD_switchable_graphics
[18:57:50.527] info GalaxyOverlayVkLayer
[18:57:50.527] info GalaxyOverlayVkLayer_VERBOSE
[18:57:50.527] info GalaxyOverlayVkLayer_DEBUG
[18:57:50.527] info VK_LAYER_EOS_Overlay
[18:57:50.528] info VK_LAYER_NV_nomad_release_public_2025_4_1
[18:57:50.529] info VK_LAYER_NV_GPU_Trace_release_public_2025_4_1
[18:57:50.529] info VK_LAYER_NV_ngfx_capture_release_public_2025_4_1
[18:57:50.529] info VK_LAYER_NV_shader_debugger_release_public_2025_4_1
[18:57:50.529] info VK_LAYER_OBS_HOOK
[18:57:50.529] info VK_LAYER_VALVE_steam_overlay
[18:57:50.529] info VK_LAYER_VALVE_steam_fossilize
[18:57:50.530] info VK_LAYER_ROCKSTAR_GAMES_social_club
[18:57:50.530] info VK_LAYER_LUNARG_api_dump
[18:57:50.530] info VK_LAYER_LUNARG_gfxreconstruct
[18:57:50.530] info VK_LAYER_KHRONOS_synchronization2
[18:57:50.530] info VK_LAYER_KHRONOS_validation
[18:57:50.530] info VK_LAYER_LUNARG_monitor
[18:57:50.530] info VK_LAYER_LUNARG_screenshot
[18:57:50.531] info VK_LAYER_KHRONOS_profiles
[18:57:50.531] info VK_LAYER_KHRONOS_shader_object
[18:57:50.531] info VK_LAYER_LUNARG_crash_diagnostic
[18:57:50.534] trace vkCheck successful vkEnumerateInstanceExtensionProperties
[18:57:50.537] trace vkCheck successful vkEnumerateInstanceExtensionProperties
[18:57:50.537] info ---- InstanceExtensions Info [count: 20] -------------------------------
[18:57:50.537] info VK_KHR_device_group_creation
[18:57:50.537] info VK_KHR_display
[18:57:50.538] info VK_KHR_external_fence_capabilities
[18:57:50.538] info VK_KHR_external_memory_capabilities
[18:57:50.538] info VK_KHR_external_semaphore_capabilities
[18:57:50.538] info VK_KHR_get_display_properties2
[18:57:50.538] info VK_KHR_get_physical_device_properties2
[18:57:50.539] info VK_KHR_get_surface_capabilities2
[18:57:50.539] info VK_KHR_surface
[18:57:50.539] info VK_KHR_surface_maintenance1
[18:57:50.540] info VK_KHR_surface_protected_capabilities
[18:57:50.540] info VK_KHR_win32_surface
[18:57:50.540] info VK_EXT_debug_report
[18:57:50.540] info VK_EXT_debug_utils
[18:57:50.541] info VK_EXT_direct_mode_display
[18:57:50.541] info VK_EXT_surface_maintenance1
[18:57:50.541] info VK_EXT_swapchain_colorspace
[18:57:50.541] info VK_NV_external_memory_capabilities
[18:57:50.542] info VK_KHR_portability_enumeration
[18:57:50.542] info VK_LUNARG_direct_driver_loading
[18:57:50.656] trace vkCheck successful vkCreateInstance(&instanceCreateInfo, nullptr, &m_instance)
[18:57:50.658] trace vkCheck successful vkEnumeratePhysicalDevices
[18:57:50.660] trace vkCheck successful vkEnumeratePhysicalDevices
[18:57:50.660] info ---- Phyiscal Devices Info [count : 2] --------------------------------
[18:57:50.661] trace vkCheck successful vkEnumerateDeviceExtensionProperties
[18:57:50.661] trace vkCheck successful vkEnumerateDeviceExtensionProperties
[18:57:50.661] info Checking GPU : NVIDIA GeForce RTX 4090
[18:57:50.661] info Found discrete GPU
[18:57:50.662] trace Checking extensions
[18:57:50.662] info All required extensions found
[18:57:50.662] trace vkCheck successful vkEnumerateDeviceExtensionProperties
[18:57:50.662] trace vkCheck successful vkEnumerateDeviceExtensionProperties
[18:57:50.662] info Checking GPU : AMD Radeon(TM) Graphics
[18:57:50.663] info Rejecting GPU since its non discrete : [deviceType : VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU]
[18:57:50.663] info ---- Queue Properties Info : [queue family count : 6] ------------------
[18:57:50.663] info Required queue flags found at queueindex : 0, full flag list : VK_QUEUE_GRAPHICS_BIT|VK_QUEUE_COMPUTE_BIT|VK_QUEUE_TRANSFER_BIT|VK_QUEUE_SPARSE_BINDING_BIT
[18:57:50.663] info Required queue flags not found for queueIndex : 1 , found : VK_QUEUE_TRANSFER_BIT|VK_QUEUE_SPARSE_BINDING_BIT
[18:57:50.663] info Required queue flags not found for queueIndex : 2 , found : VK_QUEUE_COMPUTE_BIT|VK_QUEUE_TRANSFER_BIT|VK_QUEUE_SPARSE_BINDING_BIT
[18:57:50.664] info Required queue flags not found for queueIndex : 3 , found : VK_QUEUE_TRANSFER_BIT|VK_QUEUE_SPARSE_BINDING_BIT|VK_QUEUE_VIDEO_DECODE_BIT_KHR
[18:57:50.664] info Required queue flags not found for queueIndex : 4 , found : VK_QUEUE_TRANSFER_BIT|VK_QUEUE_SPARSE_BINDING_BIT|VK_QUEUE_VIDEO_ENCODE_BIT_KHR
[18:57:50.664] info Required queue flags not found for queueIndex : 5 , found : VK_QUEUE_TRANSFER_BIT|VK_QUEUE_SPARSE_BINDING_BIT|VK_QUEUE_OPTICAL_FLOW_BIT_NV
[18:57:50.766] trace vkCheck successful vkCreateDevice(m_physicalDevice, &deviceCreateInfo, nullptr, &m_device)
[18:57:50.767] trace vkCheck successful glfwCreateWindowSurface(m_instance, app->GLFW_window, nullptr, &m_surface)
[18:57:50.767] trace vkCheck successful vkGetPhysicalDeviceSurfaceSupportKHR(m_physicalDevice, m_graphicsQueueIndex, m_surface, &isSupported)
[18:57:50.767] info Vulkan supports presenting on GLFW window surface
[18:57:50.767] info ---- Capabilites Info --------------------------------------------------
[18:57:50.768] info Min Image Count : 2
[18:57:50.768] info Window Extent : Width 1280 , Height 768
[18:57:50.770] trace vkCheck successful vkGetPhysicalDeviceSurfacePresentModesKHR
[18:57:50.771] trace vkCheck successful vkGetPhysicalDeviceSurfacePresentModesKHR
[18:57:50.771] info ---- Present Mode Info : [count : 5] -----------------------------------
[18:57:50.771] info VK_PRESENT_MODE_FIFO_KHR
[18:57:50.771] info VK_PRESENT_MODE_FIFO_RELAXED_KHR
[18:57:50.771] info Mailbox present mode support found. Mailbox mode will be preferred.
[18:57:50.772] info VK_PRESENT_MODE_MAILBOX_KHR
[18:57:50.772] info VK_PRESENT_MODE_IMMEDIATE_KHR
[18:57:50.772] info VK_PRESENT_MODE_FIFO_LATEST_READY_KHR
[18:57:50.777] trace vkCheck successful vkGetPhysicalDeviceSurfaceFormatsKHR
[18:57:50.779] trace vkCheck successful vkGetPhysicalDeviceSurfaceFormatsKHR
[18:57:50.780] info ---- Surface Format Info : [count : 7] ---------------------------------
[18:57:50.780] info VK_FORMAT_B8G8R8A8_UNORM
[18:57:50.780] info VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
[18:57:50.780] info VK_FORMAT_B8G8R8A8_SRGB
[18:57:50.781] info VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
[18:57:50.782] info VK_FORMAT_R8G8B8A8_UNORM
[18:57:50.782] info VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
[18:57:50.782] info VK_FORMAT_R8G8B8A8_SRGB
[18:57:50.783] info VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
[18:57:50.783] info VK_FORMAT_R16G16B16A16_SFLOAT
[18:57:50.783] info VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT
[18:57:50.784] info VK_FORMAT_A2B10G10R10_UNORM_PACK32
[18:57:50.784] info VK_COLOR_SPACE_HDR10_ST2084_EXT
[18:57:50.784] info VK_FORMAT_A2B10G10R10_UNORM_PACK32
[18:57:50.785] info VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
[18:57:51.035] trace vkCheck successful vkCreateSwapchainKHR(m_device, &createInfo, nullptr, &m_swapchain)
[18:57:51.036] trace vkCheck successful vkGetSwapchainImagesKHR
[18:57:51.037] trace vkCheck successful vkGetSwapchainImagesKHR
[18:57:51.037] trace vkCheck successful vkCreateCommandPool(m_device, &poolCreateInfo, nullptr, &m_cmdPool)
[18:57:51.038] trace vkCheck successful vkAllocateCommandBuffers(m_device, &allocateInfo, m_commandBuffers.data())
[18:57:51.042] info ---- Draw loop ---------------------------------------------------------
[18:57:53.670] info ---- Capabilites Info --------------------------------------------------
[18:57:53.670] info Min Image Count : 2
[18:57:53.670] info Window Extent : Width 2560 , Height 1369
[18:57:53.671] trace vkCheck successful vkGetPhysicalDeviceSurfacePresentModesKHR
[18:57:53.671] trace vkCheck successful vkGetPhysicalDeviceSurfacePresentModesKHR
[18:57:53.671] info ---- Present Mode Info : [count : 5] -----------------------------------
[18:57:53.671] info VK_PRESENT_MODE_FIFO_KHR
[18:57:53.671] info VK_PRESENT_MODE_FIFO_RELAXED_KHR
[18:57:53.672] info Mailbox present mode support found. Mailbox mode will be preferred.
[18:57:53.672] info VK_PRESENT_MODE_MAILBOX_KHR
[18:57:53.672] info VK_PRESENT_MODE_IMMEDIATE_KHR
[18:57:53.672] info VK_PRESENT_MODE_FIFO_LATEST_READY_KHR
[18:57:53.674] trace vkCheck successful vkGetPhysicalDeviceSurfaceFormatsKHR
[18:57:53.676] trace vkCheck successful vkGetPhysicalDeviceSurfaceFormatsKHR
[18:57:53.676] info ---- Surface Format Info : [count : 7] ---------------------------------
[18:57:53.677] info VK_FORMAT_B8G8R8A8_UNORM
[18:57:53.677] info VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
[18:57:53.677] info VK_FORMAT_B8G8R8A8_SRGB
[18:57:53.677] info VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
[18:57:53.677] info VK_FORMAT_R8G8B8A8_UNORM
[18:57:53.678] info VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
[18:57:53.678] info VK_FORMAT_R8G8B8A8_SRGB
[18:57:53.678] info VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
[18:57:53.679] info VK_FORMAT_R16G16B16A16_SFLOAT
[18:57:53.679] info VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT
[18:57:53.679] info VK_FORMAT_A2B10G10R10_UNORM_PACK32
[18:57:53.679] info VK_COLOR_SPACE_HDR10_ST2084_EXT
[18:57:53.679] info VK_FORMAT_A2B10G10R10_UNORM_PACK32
[18:57:53.680] info VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
[18:57:53.695] trace vkCheck successful vkCreateSwapchainKHR(m_device, &createInfo, nullptr, &m_swapchain)
[18:57:53.695] trace vkCheck successful vkGetSwapchainImagesKHR
[18:57:53.695] trace vkCheck successful vkGetSwapchainImagesKHR
[18:57:55.772] info ---- Capabilites Info --------------------------------------------------
[18:57:55.772] info Min Image Count : 2
[18:57:55.772] info Window Extent : Width 1280 , Height 768
[18:57:55.772] trace vkCheck successful vkGetPhysicalDeviceSurfacePresentModesKHR
[18:57:55.773] trace vkCheck successful vkGetPhysicalDeviceSurfacePresentModesKHR
[18:57:55.773] info ---- Present Mode Info : [count : 5] -----------------------------------
[18:57:55.773] info VK_PRESENT_MODE_FIFO_KHR
[18:57:55.773] info VK_PRESENT_MODE_FIFO_RELAXED_KHR
[18:57:55.773] info Mailbox present mode support found. Mailbox mode will be preferred.
[18:57:55.774] info VK_PRESENT_MODE_MAILBOX_KHR
[18:57:55.774] info VK_PRESENT_MODE_IMMEDIATE_KHR
[18:57:55.774] info VK_PRESENT_MODE_FIFO_LATEST_READY_KHR
[18:57:55.776] trace vkCheck successful vkGetPhysicalDeviceSurfaceFormatsKHR
[18:57:55.777] trace vkCheck successful vkGetPhysicalDeviceSurfaceFormatsKHR
[18:57:55.777] info ---- Surface Format Info : [count : 7] ---------------------------------
[18:57:55.778] info VK_FORMAT_B8G8R8A8_UNORM
[18:57:55.778] info VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
[18:57:55.778] info VK_FORMAT_B8G8R8A8_SRGB
[18:57:55.778] info VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
[18:57:55.778] info VK_FORMAT_R8G8B8A8_UNORM
[18:57:55.778] info VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
[18:57:55.779] info VK_FORMAT_R8G8B8A8_SRGB
[18:57:55.779] info VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
[18:57:55.779] info VK_FORMAT_R16G16B16A16_SFLOAT
[18:57:55.779] info VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT
[18:57:55.779] info VK_FORMAT_A2B10G10R10_UNORM_PACK32
[18:57:55.779] info VK_COLOR_SPACE_HDR10_ST2084_EXT
[18:57:55.780] info VK_FORMAT_A2B10G10R10_UNORM_PACK32
[18:57:55.780] info VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
[18:57:55.788] trace vkCheck successful vkCreateSwapchainKHR(m_device, &createInfo, nullptr, &m_swapchain)
[18:57:55.788] trace vkCheck successful vkGetSwapchainImagesKHR
[18:57:55.788] trace vkCheck successful vkGetSwapchainImagesKHR
[18:57:57.964] info ---- Cleanup -----------------------------------------------------------
Appendix A: where each @@ comment is addressed
@@ request | File | Answered in |
|---|---|---|
| Understand the three-step list process | vkapp_init.cpp | The three-step enumeration |
| Append GLFW required extensions | vkapp_init.cpp | createInstance (a) |
| Print out the availableLayers | vkapp_init.cpp | createInstance (b) |
| Print out the availableExtensions | vkapp_init.cpp | createInstance (c) |
Verify success of vkCreateInstance | vkapp_init.cpp | createInstance |
| Judge each physicalDevice for compatibility | vkapp_init.cpp | createPhysicalDevice |
| How many queue families / which flags / which index | vkapp_init.cpp | chooseQueueIndex |
| Build a pNext chain of the six feature structures | vkapp_init.cpp | createDevice |
Verify success of vkCreateDevice | vkapp_init.cpp | createDevice |
Verify glfwCreateWindowSurface, surface support, isSupported | vkapp_init.cpp | getSurface |
| Document your PRESENT MODEs (and MAILBOX) | vkapp_init.cpp | createSwapchain |
| Choose MAILBOX if present | vkapp_init.cpp | createSwapchain |
| Document the format / color-space list and your choice | vkapp_init.cpp | createSwapchain |
Verify vkCreateSwapchainKHR and vkGetSwapchainImagesKHR | vkapp_init.cpp | createSwapchain |
| Destroy the swapchain in its own procedure | vkapp_init.cpp | destroyAllVulkanResources |
Verify vkCreateCommandPool / vkAllocateCommandBuffers | vkapp_init.cpp | createCommandPool |
| Project 1 vs. projects 2+ pipeline layout | vkapp_postProcess.cpp | createPostPipeline |
Uncomment the four drawFrame phases | vkapp_runtime.cpp | Runtime |
Uncomment postProcess() in renderFrame | vkapp_runtime.cpp | Runtime |
| Uncomment the three fence/idle lines | vkapp.cpp | destroyAllVulkanResources |
Delete the resize throw; destroy/recreate pairs | vkapp.cpp | recreateSizedVulkanResources |
| Screen capture of the final result | 1-first-light.pdf | Result: capture of the Mandelbulb replacement |
| No validation-layer messages | 1-first-light.pdf | Validation-layer verification |
Appendix B: destroy / resize registry
All resources, with whether they are destroyed and whether they need recreating on a resize.
| Resource | Destroyed | Resized |
|---|---|---|
m_instance | yes | — |
m_device | yes | — |
m_surface | yes | — |
m_swapchain (+ image views, semaphores, fences) | yes | yes |
m_cmdPool | yes | — |
m_depthImage | yes | yes |
m_postPipelineLayout / m_postPipeline | yes | yes |