Brendan Galea's Vulkan C++ [Youtube Series]
3:40minute video 💁♀️
Alternatively:- https://paminerva.github.io/docs/LearnVulkan/01.A-Hello-Window
1 - Introduction part from here only 😊 [untill 1.2. Why Vulkan? end]Alternatively:- you can give this page a try too:-
1️⃣ What is Vulkan? ....🤔.... Why Vulkan?
p.a.minerva2️⃣ Why should 'you' learn/use Vulkan?
3️⃣ Why is this Important?
OpenGL is fine enough
Shader Enthusiast:- https://www.shadertoy.com/
Making an App/UI :- doing everything with OpenGL -> would be just fine
4️⃣ When will 'you' need vulkan?
vulkan for even that last 5-10% performance5️⃣ How does vulkan work?
vulkan-sdk, cmake, amGHOST if you don't have vscode & C++ Compiler
📥 https://vulkan.lunarg.com/sdk/home
VULKAN_SDK & VK_SDK_PATH environment variables are set🎓 Intro/Tutorials
OR: Watch 6/7 videos from this playlist:-
restart vscode after installing
📜 REY_DOCsamGHOST, will need to have these commandscmake_minimum_required(VERSION 3.25 FATAL_ERROR)
project("idk_PROJECT" VERSION 0.1)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# --------------------
set(SRC
"main.cpp"
)
set(INC
${CMAKE_CURRENT_SOURCE_DIR}
)
# --------------------
# --------------------
# set_source_files_properties(main.cpp PROPERTIES COMPILE_FLAGS "/P /C")
# Output Preprocessed File
add_executable (idk ${SRC})
target_include_directories (idk PUBLIC ${INC})
# ------amGHOST-------
add_subdirectory (amGHOST)
target_link_libraries (idk PUBLIC amGHOST)
# ------install-------
install(TARGETS idk
DESTINATION ${CMAKE_CURRENT_SOURCE_DIR})
amGHOST
blender's GHOST xP 😜]git clone -b win32-intro https://github.com/REYNEP/amGHOSTF1 --> CMake: ConfigureF1 --> CMake: BuildF1 --> CMake: Install --> .insall diramGHOST/README.md
Option 1:- use cmake for your project too.... using add_subdirectory(amGHOST)Option 2:- use libamGHOST.lib after installing & #include amGHOST/<header>main.cpp for your program#include "amGHOST/amGHOST_System.hh"
int main(int argumentCount, char* argumentVector[])
{
amGHOST_System::create_system(); // initializes amG_HEART
amGHOST_Window* W = amG_HEART->new_window_interface();
W->create(L"Whatever", 0, 0, 500, 600);
REY::cin.get(); // wait for terminal input
W->destroy();
}
readme ex. 1]Viewing these readmes in a Nice Way
vscode extension:- shd101wyy.markdown-preview-enhancedscoop install princexmlvscode F1:- Markdown Preview Enhanced:- Customize CSS (Global)style-bkup.lessvscode F1:- Markdown Preview Enhanced:- Open Preview 💁♀️VkInstance
amVK wrap 🌯 #include "amVK_Instance.hh"
// TwT
amVK_Instance::AppInfo // VkApplicationInfo [public]
amVK_Instance::CI // VkInstanceCreateInfo [public]
// You can modify these as you wish 😊
amVK_Instance::CreateInstance(); // initializes amVK_HEART
Notes 

https://vkdoc.net/man/VkApplicationInfo
.sType:-
VkStruct is gonna have this field/member 💁♀️VK_STRUCTURE_TYPE_APPLICATION_INFO for VkApplicationInfoVK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO for VkInstanceCreateInfoVK_STRUCTURE_TYPE_DEVICE_CREATE_INFO for VkDeviceCreateInfo.pNext:-
VkStruct is gonna have this field/member 💁♀️NULL 💁♀️VkStructEXT when you need those Extension features 💁♀️.pApplicationName --> null-terminated UTF-8 string
.applicationVersion --> uint32
.pEngineName --> null-terminated UTF-8 string
.engineVersion --> uint32
.apiVersion --> uint32
Valid Usage section 😉There's a alternative to vkdoc.net
vscode --> ivirtex.vulkan-hover-docsSymbols
VkApplicationInfo https://vkdoc.net/man/VkApplicationInfo
.sType 🟪 VK_STRUCTURE_TYPE_APPLICATION_INFO.pNext 🟪 NULL.pApplicationName --> null-terminated UTF-8 string.applicationVersion 🏷️ uint32.pEngineName --> null-terminated UTF-8 string.engineVersion 🏷️ uint32.apiVersion 🏷️ uint32.apiVersion
lowest Vulkan API version Your APP "can run" on.
.engineVersion
version of the engine (if any) used to create "Your APP".vulkan driver implementations to perform "ad-hoc" optimizations.
Unreal Engine Version 4.1.smth idk 🤷♀️yes, what are you waiting for 🤷♀️ go go, shooo.... (🤭)
#include <vulkan/vulkan.h>Struct -> Fill it up [😉][have the vkdoc.net as assist]VkInstanceCreateInfo .sType 🟪 VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO.pNext 🪐 NULL
.flags 🏳️ VkInstanceCreateFlagBits
.pApplicationInfo 🟪 💁♀️ Duh!.ppEnabledLayerNames 🟨 ChapterZZZ.ppEnabledExtensionNames 🟨 Chapter4.2
EnabledLayer & EnabledExtensions right now
.md file for that thingyvscode / visual-studio extension if you want 💁♀️ vscode extension name --> ivirtex.vulkan-hover-docs
VkInstance m_instance = nullptr; vkCreateInstance(CI, nullptr, &m_instance) https://vkdoc.net/man/vkCreateInstance
param pCreateInfo 🟪 💁♀️ Duh!param pAllocator 🟪 nullptrparam pInstance 🟪 &m_instanceparam pAllocator
VkAllocationCallbacks 🟨 ChapterZZZ
amVK_log.hh
stackTracer() that i basically stripped from blender3D codebase 🥴📽️ So far, The result :- 4.guide.chapter1.hh vkEnumerateInstanceExtensionProperties() --> 🟨 Chapter4.2
Add_InstanceEXT_ToEnable(const char* extName) --> 🟨 Chapter4.2
VkDevice 
Take a look into this awesome slide from slide-26 onwards
...to understand what each of these steps above "feel like"/mean/"how to imagine them".
*slide = Vulkanised 2023 Tutorial Part 1
amVK wrap 🌯 #include "amVK_Instance.hh"
#include "amVK_DeviceQueues.hh"
#include "amVK_Device.hh"
// TwT
REY_LOG("");
amVK_Instance::EnumeratePhysicalDevices();
amVK_GPUProps *GPUProps = amVK_InstanceProps::GetARandom_GPU();
GPUProps->GetPhysicalDeviceQueueFamilyProperties();
GPUProps->REY_CategorizeQueueFamilies();
amVK_Device* D = new amVK_Device(GPUProps);
D->CI // VkDeviceCreateInfo [public]
D->Queues // amVK_DeviceQueues [public] [take a look inside 😜]
D->add_1D_QFAMs_QCount_USER() // amVK_DeviceQueues
D->CreateDevice(1); // param1 = GraphicsQueueCount =
D->GetDeviceQueues(); // see:- Queues.TheArrays 😜
D->Queues.GraphicsQ(0) // returns Queues.TheArrays.Graphics[0]
vkCreateDevice() physicalDevice 🟪 HardwareGPU_List[0] / amVK_InstanceProps::GetARandom_GPU()
pCreateInfo 🟪💁♀️
pAllocator 🟨 ChapterZZZpDevice ↩️📦 &m_Device
📽️ So far, The result:-VkDeviceCreateInfo https://vkdoc.net/man/VkDeviceCreateInfo
.sType 🟪 VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO
.pNext 🪐 nullptr
VkDeviceCI 💁♀️.flags 🏴 0
VkSpecs Says:- reserved for future use.pQueueCreateInfos 🔗 SubChapter 5
.ppEnabledLayerNames ⚠️ deprecated [by Vulkan]
.ppEnabledExtensionNames 🟨 Chapter4.2
.pEnabledFeatures 🟨 ChapterZZZ
.pQueueCreateInfos -> yes, you 'can' pass multiple 😉.zzzCreateInfoCount & .pZZZCreateInfos
📽️ So far, The result:-vkEnumeratePhysicalDevices() </> TheCodeuint32_t deviceCount = 0;
// [implicit valid usage]:- must be 0 [if 3rd-param = nullptr]
vkEnumeratePhysicalDevices(m_instance, &deviceCount, nullptr);
// it's kinda like the function is 'output-ing into' deviceCount
std::vector<VkPhysicalDevice> HardwareGPU_List(gpuCount);
// best to save this as a class member variable
vkEnumeratePhysicalDevices(m_instance, &deviceCount, HardwareGPU_List.data());
// note: it does return VkResult return_code
👀 Visualization / [See it] / JSON Printing:- 4.guide.chapter2.1.json.hh📽️ So far, The result:- 4.guide.chapter2.1.midway.hh🔗 GitHub:- amVK_GPUProps.hhamVK_InstanceProps::GetARandom_GPU() </> TheCode 🔗 GITHUB amVK_InstanceProps.hh#L39
VkDeviceQueueCreateInfo - 'The Real Deal' https://vkdoc.net/man/VkDeviceQueueCreateInfo
.sType 🟪 VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO
.pNext 🪐 nullptr
.flags 🏳️ 0
VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT [Protected Queue].queueFamilyIndex 🔗 Next 3 SubChapters
vkGetPhysicalDeviceQueueFamilyProperties() --> look for a QueueFamily that supports VK_QUEUE_GRAPHICS_BIT.queueCount 🟪 1 [Specify, how many you need 💁♀️]
.pQueuePriorities --> yes, this can be multiple "Priorities" 🥴 [idk yet why tho]
📽️ So far, The result:-GITHUBvkGetPhysicalDeviceQueueFamilyProperties() https://vkdoc.net/man/vkGetPhysicalDeviceQueueFamilyProperties
QueueFamily might support VK_QUEUE_GRAPHICS_BITQueueFamily might support VK_QUEUE_COMPUTE_BITQueueFamily might support VK_QUEUE_TRANSFER_BITQueueFamily might support VK_QUEUE_VIDEO_ENCODE_BIT_KHRQueueFamily might support a-mixture of multiple</> TheCode [OldWay]#define GPUs amVK_InstanceProps::s_HardwareGPU_List
#define amVK_2D_GPUs_QFAMs amVK_Instance::s_HardwareGPU_QFamProps_List2D
static inline REY_Array<REY_Array<VkQueueFamilyProperties>> s_HardwareGPU_QFamProps_List2D;
// REY_Array --> "REY_LoggerNUtils/REY_Utils.hh" 😄
// 1 System/PC
// multiple GPU
// multiple QFamProps
static inline void GetPhysicalDeviceQueueFamilyProperties(void) {
amVK_2D_GPUs_QFAMs.reserve(GPUs.n); // malloc using "new" keyword
for ( uint32_t k = 0; k < GPUs.n; k++ ) // for each GPU
{
REY_Array<VkQueueFamilyProperties> *k_QFamProps = &amVK_2D_GPUs_QFAMs.data[k];
uint32_t QFamCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(GPUs[k], &QFamCount, nullptr);
k_QFamProps->n = QFamCount;
k_QFamProps->data = new VkQueueFamilyProperties[QFamCount];
vkGetPhysicalDeviceQueueFamilyProperties(GPUs[k], &k_QFamProps->n, k_QFamProps->data);
}
#undef GPUs
}
👀 Visualization / [See it] / JSON Printing:- 4.guide.chapter2.5.json.hh
📽️ So far, The result:- [OldWay] 4.guide.chapter2.5.amVK_Instance.hh
2DArray_QFAM_Props part & below were added only compared to Chapter2.1.📽️ So far, The result:- 🔗 GITHUB [NewWay]VkQueueFamilyProperties .queueFlags
QCI.queueFamilyIndex based on these flagsQueueFamily that supports VK_QUEUE_GRAPHICS_BITVK_QUEUE_COMPUTE_BITVK_QUEUE_TRANSFER_BITVK_QUEUE_VIDEO_ENCODE_BIT_KHR.queueCount
Queues we are allowed to work with' 🥴.timestampValidBits.minImageTransferGranularityVkDeviceQCI.queueFamilyIndex [OldWay] 🎯 Task
QueueFamily that supports VK_QUEUE_GRAPHICS_BIT 😉</> amVK_Device.hhvoid amVK_Device::Select_QFAM_GRAPHICS(void) {
if (!amVK_Instance::called_GetPhysicalDeviceQueueFamilyProperties) {
amVK_Instance::EnumeratePhysicalDevices();
}
if (!amVK_Instance::called_GetPhysicalDeviceQueueFamilyProperties) {
amVK_Instance::GetPhysicalDeviceQueueFamilyProperties();
}
amVK_Instance::amVK_PhysicalDevice_Index index = amVK_HEART->GetARandom_PhysicalDevice_amVK_Index();
this->QCI.Default.queueFamilyIndex = amVK_Instance::ChooseAQueueFamily(VK_QUEUE_GRAPHICS_BIT, index);
// If you wanna see the implementation for this function
}
📽️ So far, The result:- OldWay (Don't spend time inside this, more than 1 minute)
📽️ So far, The result:- NewWay 🔗 GITHUB (NewWay is like 10x more organized and easier to understand)
REY_CategorizeQueueFamilies() [NewWay] </> TheCode 🔗 GITHUB
amVK_GPUProps.hh#L50
amVK_GPUProps.cpp#L260
vkCreateDevice() finally calling it 😊 </> main.cppamVK_Device* D = new amVK_Device(GPUProps);
D->CI // VkDeviceCreateInfo [public]
D->Queues // amVK_DeviceQueues [public] [take a look inside 😜]
D->add_1D_QFAMs_QCount_USER() // amVK_DeviceQueues
D->CreateDevice(1); // param1 = GraphicsQueueCount = 1
D->GetDeviceQueues(); // see:- Queues.TheArrays 😜
D->Queues.GraphicsQ(0) // returns Queues.TheArrays.Graphics[0]
CreateInfo => By default has initial values inside amVK_DeviceamVK_DeviceQueues VkDeviceCreateInfo.pQueueCreateInfos The .queueFamilyIndex member of each element of .pQueueCreateInfos must be unique 💁♀️
So, randomly push_back()ing without any kinda safety ➡️ kinda feels absurd. 💁♀️ doesn't it? .... e.g.
/* ============ REY_LoggerNUtils::REY_Utils.hh ============ */
REY_ArrayDYN<VkDeviceQueueCreateInfo> Array = REY_ArrayDYN<VkDeviceQueueCreateInfo>(2);
REY_ARRAY_PUSH_BACK(Array) = this->Default_QCI;
REY_ARRAY_PUSH_BACK(Array) = Your_QCI;
So what i did is:- to introduce a QCount array as per QFamily 💁♀️
& then have a function for the user to increase the QCount
GITHUB_WIP --> amVK_Device::add_1D_QFAMs_QCount_USER()March, 2025class amVK_InstanceProps
EnumeratePhysicalDevices()GetPhysicalDeviceQueueFamilyProperties()(Don't spend time inside this, more than 1 minute)
https://github.com/REYNEP/amGHOST/tree/3e44b982902a3f3fa4ac584aefb19da3d4cdfcc6
May, 2025GITHUB (NewWay is like 10x more organized and easier to understand)
vkGetPhysicalDeviceProperties() 🟨 Chapter11
GetFeatures 🟨 Chapter11
MemoryTypes 🟨 Chapter11
Guide on amVK_Array 🟨 Chapter6.6
Object Vk VkInstance
Types Vk VkInstanceCreateInfo
Funcs vk vkCreateInstance()
Enums VK_ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO
Extensions
KHR:- Khronos authored,
EXT:- multi-company authored
Creating "VkZZZ" object
1. take `VkZZZCreateInfo` --> fill it up
2. call `vkCreateZZZ()`
3. also `vkDestroyZZZ()` before closing your app
4. Some objects get "allocated" rather than "created"
`VkZZZAllocateInfo` --> `vkAllocateZZZ` --> `vkFreeZZZ`
5. Sometimes there will be `.zzzCreateInfoCount` & `.pZZZCreateInfos`
e.g. `.queueCreateInfoCount` & `.pQueueCreateInfos``
-> So you could like pass in an array/vector
-> You will see this in lots of other places
Getting List/Properties
6. vkEnumerateZZZ() --> \see `[Chapter2.1.] vkEnumeratePhysicalDevices()` example
-- | -- | -- | ----------------------------------------------------------------------------
🟪 almost every VkStruct is gonna have these 3 field/member 💁♀️
sType:-
vulkan-loader and actual gpu-driver-implementations to know what type of structure was passed in through pNext.pNext:-
vulkan-loader, debugging-validation-layers, and gpu-driver-implementations.
pNext->stype field to know what's ahead in the linked list.flags:-
0.pQueueCreateInfos:- yes, you 'can' pass multiple 😉
.zzzCreateInfoCount & .pZZZCreateInfos
-- | -- | -- | ----------------------------------------------------------------------------
VkRenderPassCreateInfo CI = {
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.pNext = nullptr,
.flags = 0
};
10. Do remember to check the `Valid Usage` section within each manual-page
uint32_t deviceCount = 0;
// [implicit valid usage]:- must be 0 [if 3rd-param = nullptr]
vkEnumeratePhysicalDevices(m_instance, &deviceCount, nullptr);
// it's kinda like the function is 'output-ing into' deviceCount
std::vector<VkPhysicalDevice> HardwareGPU_List(gpuCount);
// best to save this as a class member variable
vkEnumeratePhysicalDevices(m_instance, &deviceCount, HardwareGPU_List.data());
// note: it does return VkResult return_code
-- | -- | -- | ----------------------------------------------------------------------------
Symbols:-
🟪:- kinda means nothing
🟨:- "Yellow Card"
1. ChapterZZZ => Unknown WIP/TBD Chapter
2. Chapter2.4 =>
If LATER-CHAPTER => Dont hesitate right now, Do this when you each that LATER-Chapter
If PREV-CHAPTER => You can go back and check 😛
🔗 `SurfCAP.currentTransform`
🔗 Chapter2.4
🟧:- "Orange Card"
🪐: "Extensions"
🔠: "Options"
🏳️: "I Lose, You Win!" / General Flag Icon / Sometimes means -> "Lots of Flags" / IDK / Didn't check [IDC]
🎌: "Nice/Important Flags"
🚩: "One Flag" [IDC]
🏴: "No Flag" [IDC]
⚠️: "Deprecated Feature" / "Other Kinds of Warnings" / I will try to name when using this emoji/sign
🏷️: "Type"
🟨 ChapterZZZ
🔗 Chapter2.1
🔗 GITHUB_WIP
📋🔄 Chapter2.1
vkEnumeratePhysicalDevices()ℹ️: "Create Info"
🌯: amVK_Wrap
↩️📦: "Object Getting return by Vulkan Function"
📜 REY_DOCs
</> TheCode
📽️ So far, The result
👀 Visualization / [See it] / JSON Printing
🔬🛠️ 2DriverIMPL
-- | -- | -- | ----------------------------------------------------------------------------
GITHUB_WIP</> TheCode</> main.cpp📽️ So far, The result🔬🛠️ 2DriverIMPL👀 Visualization / [See it] / JSON Printinghttps://vkdoc.net/man/VkGraphicsPipelineCreateInfo
.sType 🟪 VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO.pNext 🪐 nullptr.flags 🏳️ 0
https://vkdoc.net/man/VkGraphicsPipelineCreateInfo
.sType 🟪 VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO
.pNext 🪐 nullptr
.flags 🏳️ VkBufferCreateFlagBits
SPARSE 🟨 ChapterZZZ.pSwapchains 🟪💁♀️
.pNext 🪐 nullptr
VkDeviceGroupCommandBufferBeginInfo.flags 🔠 VkCommandBufferUsageFlagBits
ONE_TIME_SUBMITRENDER_PASS_CONTINUE [secondary command buffer]SIMULTANEOUS_USE📟📇
🎚️🎌🏳️
🔌🚀
🚫📜
⚠️🧓
☢️🧨
☢️💀
⚠️🏚️
1️⃣
2️⃣
3️⃣
4️⃣
5️⃣
6️⃣
7️⃣
8️⃣
9️⃣
🔟
1️⃣1️⃣
1️⃣2️⃣
1️⃣3️⃣
1️⃣4️⃣
1️⃣5️⃣
1️⃣6️⃣
1️⃣7️⃣
1️⃣8️⃣
1️⃣9️⃣
2️⃣0️⃣
1. query_SurfCap 🕵️♂️
2. update_SurfCap 🔄
3. load_SurfCap 📥
4. acquire_SurfCap 🔗
5. get_SurfCap 📤
6. grab_SurfCap 👐
7. snag_SurfCap 🎣 (Quick pull)
8. pluck_SurfCap ✂️ (Precision)
9. selected_gpu_surfCap 🎯 (Targeted) Emphasizes the GPU_Index selection.
10. current_surfCap ⏳ (Stateful)
11. yoink_SurfCap 🦄 (Playful) VkSurfaceCapabilitiesKHR* cap = yoink_SurfCap();
12. procure_SurfCap 🕴️ (Formal) procure_SurfCap() → Sounds like a business transaction!
13. obtain_SurfCap 🏆 (Success)
14. collect_SurfCap 📚 (Gathering)
15. retrieve_SurfCap 🎯 (Accuracy)
16. sync_SurfCap 🔄 (Sync State)
17. pull_SurfCap 🪢 (Tug-of-war)
18. refresh_SurfCap 💫 (Update)
19. reload_SurfCap ♻️ (Reload)
20. populate_SurfCap 🌱 (Fill Data)
21. enumerate_SurfCap 📇 (Listing)
22. summon_SurfCap 🧙♂️ (Magic)
23. harvest_SurfCap 🌾 (Farm)
24. fish_SurfCap 🎣 (Fishing)
25. dial in 🎛️ (Precision)
26. shape up 🌟 (Polishing)
27. rig 🛠️ (Hacky)
28. tailor 👗 (Custom-fit)
29. access_SurfCap 🔍
30. craft 🧙♂️ (Artisan)
31. surfCap 📋 (property-style)
32. surfCap_ptr 🎯 (or surfCapRef)
#!/usr/bin/env python3
# 🎮 Ultimate Vulkan Emoji Guide (1-35)
vulkan_steps = [
# Core Setup (Original 1-5)
"1. 🌍 Instance Creation",
"2. 🖥️ Physical Device Selection",
"3. ⚙️ Logical Device Setup",
"4. 🎨 Graphics Pipeline",
"5. 🖼️ SwapChain Initialization",
# Resource Management (Original 6-10)
"6. 🗄️ Buffer Allocation",
"7. 🧠 Memory Binding",
"8. 🖌️ Descriptor Sets",
"9. 📦 Image Creation",
"10. 🎮 Command Pools",
# Execution Flow (Original 11-12)
"11. 📜 Command Buffers",
"12. ⏱️ Synchronization",
# Debugging (Original 13-14)
"13. 🔍 Validation Layers",
"14. 🐛 Debug Messenger",
# Advanced Features (Original 15-17)
"15. 🌌 Ray Tracing",
"16. 🤖 Compute Pipeline",
"17. 🧵 Multi-Threading",
# Cleanup (Original 18-20)
"18. 🧹 Resource Destruction",
"19. 💥 Device Cleanup",
"20. 🚀 Instance Shutdown",
# New Additions (21-35)
"21. 🧊 Device Memory",
"22. 🔄 Memory Barriers",
"23. 📊 Buffer Views",
"24. 🎛️ Pipeline Layout",
"25. 🔮 Shader Modules",
"26. 🧩 Pipeline Cache",
"27. 🎆 Render Passes",
"28. 🖌️ Dynamic Rendering",
"29. 🌐 Multi-View Rendering",
"30. ⏳ Timeline Semaphores",
"31. 🚦 Fences",
"32. 📡 Debug Markers",
"33. 📈 Performance Queries",
"34. 🌀 Compute Dispatches",
"35. 🚀 Acceleration Structures"
]
#!/usr/bin/env python3
# 🏆 Ultimate Vulkan Emoji Cheatsheet (50+ Concepts)
vulkan_concepts = {
# === Core Setup ===
"🌍": "Instance Creation (vkCreateInstance)",
"🖥️": "Physical Device Selection (vkEnumeratePhysicalDevices)",
"⚙️": "Logical Device (vkCreateDevice)",
"📜": "Extensions/Layers (ppEnabledExtensionNames)",
# === Resources ===
"🗄️": "Buffers (vkCreateBuffer)",
"🧊": "Device Memory (vkAllocateMemory)",
"📦": "Images (vkCreateImage)",
"🔄": "Memory Barriers (vkCmdPipelineBarrier)",
"🧶": "Image Views (vkCreateImageView)",
"🧩": "Sparse Resources (VkSparseImageMemoryBind)",
# === Pipeline ===
"🎨": "Graphics Pipeline (vkCreateGraphicsPipelines)",
"🤖": "Compute Pipeline (vkCreateComputePipelines)",
"🔮": "Shader Modules (vkCreateShaderModule)",
"🎛️": "Pipeline Layout (vkCreatePipelineLayout)",
"🧪": "Pipeline Cache (vkCreatePipelineCache)",
# === Descriptors ===
"🖌️": "Descriptor Sets (vkAllocateDescriptorSets)",
"📇": "Descriptor Pool (vkCreateDescriptorPool)",
"📊": "Descriptor Set Layout (vkCreateDescriptorSetLayout)",
# === Rendering ===
"🎆": "Render Passes (vkCreateRenderPass)",
"🖼️": "Framebuffers (vkCreateFramebuffer)",
"🎚️": "Dynamic Rendering (VK_KHR_dynamic_rendering)",
"👁️": "Multi-View (VK_KHR_multiview)",
# === Commands ===
"🎮": "Command Pools (vkCreateCommandPool)",
"📜": "Command Buffers (vkAllocateCommandBuffers)",
"⏱️": "Queue Submission (vkQueueSubmit)",
# === Synchronization ===
"🚦": "Fences (vkCreateFence)",
"⏳": "Timeline Semaphores (VK_KHR_timeline_semaphore)",
"🤝": "Events (vkCreateEvent)",
# === Advanced ===
"🌌": "Ray Tracing (VK_KHR_ray_tracing_pipeline)",
"🚀": "Acceleration Structures (vkCreateAccelerationStructureKHR)",
"🌀": "Mesh Shading (VK_EXT_mesh_shader)",
"💫": "Task Shaders (VK_EXT_mesh_shader)",
# === Debugging ===
"🔍": "Validation Layers (VK_LAYER_KHRONOS_validation)",
"🐛": "Debug Utils (vkCreateDebugUtilsMessengerEXT)",
"📡": "Debug Markers (vkCmdDebugMarkerBeginEXT)",
"📈": "Performance Queries (VK_QUERY_TYPE_PERFORMANCE_QUERY_KHR)",
# === Cleanup ===
"🧹": "Resource Destruction (vkDestroy*)",
"💥": "Device Cleanup (vkDestroyDevice)",
"🚀": "Instance Shutdown (vkDestroyInstance)",
# === New Additions ===
"🔄": "Push Constants (vkCmdPushConstants)",
"🎚️": "Dynamic States (VkPipelineDynamicStateCreateInfo)",
"🧠": "Pipeline Derivatives (VK_PIPELINE_CREATE_DERIVATIVE_BIT)",
"📌": "Specialization Constants (VkSpecializationInfo)",
"🌐": "External Memory (VK_KHR_external_memory)",
"🔗": "Linked GPUs (VK_KHR_device_group)"
}
#!/usr/bin/env python3
# 🏆 Ultimate Vulkan Cheatsheet (70+ Concepts) 🏆
vulkan_steps = [
# === Core Setup (1-8) ===
"1. 🌍 Instance Creation",
"2. 🖥️ Physical Device Selection",
"3. ⚙️ Logical Device Setup",
"4. 🔌 Device Features",
"5. 📜 Extensions/Layers",
"6. 🖼️ SwapChain Initialization",
"7. 🌐 Surface Creation",
"8. 🧭 Queue Families",
# === Resources (9-24) ===
"9. 🗄️ Buffer Allocation",
"10. 🧊 Device Memory",
"11. 📦 Image Creation",
"12. 🧶 Image Views",
"13. 🔄 Memory Barriers",
"14. 🧩 Sparse Resources",
"15. 📊 Buffer Views",
"16. 🧵 Host-Coherent Memory",
"17. 🚚 Memory Transfers",
"18. 🧠 Staging Buffers",
"19. 🔗 External Memory",
"20. 🧿 Protected Memory",
"21. 💿 Buffer Device Address",
"22. 🏷️ Resource Naming",
"23. 📏 Memory Requirements",
"24. 🧑🔧 Memory Budget",
# === Pipeline (25-40) ===
"25. 🎨 Graphics Pipeline",
"26. 🤖 Compute Pipeline",
"27. 🔮 Shader Modules",
"28. 🎛️ Pipeline Layout",
"29. 🧪 Pipeline Cache",
"30. 🔄 Push Constants",
"31. 🎚️ Dynamic States",
"32. 📌 Specialization Constants",
"33. 🧠 Pipeline Derivatives",
"34. 💾 Pipeline Libraries",
"35. 🌀 Tessellation",
"36. 💫 Geometry Shaders",
"37. 🧪 Subpasses",
"38. ✂️ Depth/Stencil",
"39. 🌈 Blend States",
"40. 🧵 Multiview Rendering",
# === Commands (41-50) ===
"41. 🎮 Command Pools",
"42. 📜 Command Buffers",
"43. ⏱️ Queue Submission",
"44. 🔁 Secondary Command Buffers",
"45. 🧑🍳 Indirect Commands",
"46. 🎛️ Device Groups",
"47. 🤝 Queue Priorities",
"48. ⏳ Timeline Semaphores",
"49. 🚦 Fences",
"50. 🤝 Events",
# === Advanced (51-70) ===
"51. 🌌 Ray Tracing",
"52. 🚀 Acceleration Structures",
"53. 🌀 Mesh Shading",
"54. 💫 Task Shading",
"55. 📡 Debug Markers",
"56. 📈 Performance Queries",
"57. 🕵️♀️ Object Tracking",
"58. 🧩 Bindless Resources",
"59. 🚧 Pipeline Barriers",
"60. 💾 Pipeline Statistics",
"61. 🌐 External Semaphores",
"62. 🔄 Present Modes",
"63. 🖌️ Dynamic Rendering",
"64. 🧶 Fragment Density Maps",
"65. 🌀 Variable Rate Shading",
"66. 🧿 Protected Swapchains",
"67. 📜 Shader Printf",
"68. 🧪 Pipeline Robusness",
"69. 🛡️ Validation Features",
"70. 🧹 Resource Cleanup"
]
-- | -- | -- | ----------------------------------------------------------------------------
VkSwapchainKHR 🪟 VkSwapchainCreateInfoKHR ℹ️ .sType 🟪 VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO.pNext 🪐 nullptr.flags 🏳️ ChapterZZZ.surface 🏄♀️ Chapter4.2.minImageCount.imageFormat 🤭.imageColorSpace 🤭.imageExtent 😊.imageArrayLayers.imageUsage.imageSharingMode 🟪 EXCLUSIVE/CONCURRENT [Toggle]VK_SHARING_MODE_CONCURRENT 🟨 ChapterZZZ
.queueFamilyIndexCount --> if using, must be greated than 1.pQueueFamilyIndices --> These two are used only if .imageSharingMode = CONCURRENT iguess.preTransform:- VkSurfaceTransformFlagBitsKHR.compositeAlpha:- VkCompositeAlphaFlagBitsKHR.presentMode:- VkPresentModeKHR.clipped:- VkBool32.oldSwapchain 🟨 ChapterZZZ
amVK wrap 🌯 Part I #include "amGHOST_VkSurfaceKHR.hh"
// TwT
REY_LOG("");
amVK_Instance::EnumerateInstanceExtensions();
amVK_Instance::addTo_1D_Instance_EXTs_Enabled("VK_KHR_surface");
amVK_Instance::addTo_1D_Instance_EXTs_Enabled(amGHOST_System::get_vulkan_os_surface_ext_name());
// amGHOST_VkSurfaceKHR::create_surface() needs that extension enabled
amVK_Instance::CreateInstance();
REY_LOG("");
VkSurfaceKHR VK_S = amGHOST_VkSurfaceKHR::create_surface(W, amVK_Instance::vk_Instance);
// another 🌯 amVK_Wrap, at the end of this file
VkSurfaceKHR 🏄♀️ VK_KHR_surface Vulkan Extension https://vkdoc.net/man/VkSurfaceKHR
https://vkdoc.net/extensions/VK_KHR_surface
Yaaaay, we have reached our first extension to enable
we need to enable it back in vkCreateInstance() from 🔗 Chapter1.2
vkEnumerateInstanceExtensionProperties()IS_InstanceEXT_Available(const char* extName)bool amVK_InstanceProps::IS_InstanceEXT_Available(const char *extName) {
for (uint32_t k = 0, lim = amVK_EXT_PROPs.n; k < lim; k++) {
if (strcmp(amVK_EXT_PROPs[k].extensionName, extName) == 0) { // <cstring>
return true;
}
}
return false;
}
Add_InstanceEXT_ToEnable(const char* extName)static inline REY_ArrayDYN<char*> s_Enabled_EXTs = REY_ArrayDYN<char*>(nullptr, 0, 0);
// It will be automatically allocated, resize, as we keep adding 😊
#include <string.h>
void amVK_Instance::Add_InstanceEXT_ToEnable(const char* extName)
{
if (!amVK_InstanceProps::called_EnumerateInstanceExtensions) {
amVK_InstanceProps::EnumerateInstanceExtensions();
}
if (amVK_InstanceProps::IS_InstanceEXT_Available(extName)) {
char *dont_lose = new char[strlen(extName)];
strcpy(dont_lose, extName);
s_Enabled_EXTs.push_back(dont_lose);
amVK_Instance::CI.enabledExtensionCount = s_Enabled_EXTs.neXt;
amVK_Instance::CI.ppEnabledExtensionNames = s_Enabled_EXTs.data;
}
else {
REY_LOG_notfound("Vulkan Extension:- " << extName);
}
}
amVK_Instance::Add_InstanceEXT_ToEnable(amGHOST_System::get_vulkan_os_surface_ext_name());
// or
amVK_Instance::Add_InstanceEXT_ToEnable("VK_KHR_win32_surface");
// or some other surface name
Win32SurfaceCI
vkCreateWin32SurfaceKHR()
</> TheCodepure-virtual VkSurfaceKHR amGHOST_VkSurfaceKHR_WIN32::create(VkInstance I)
{
amGHOST_SystemWIN32 *heart_win32 = (amGHOST_SystemWIN32 *) amGHOST_System::heart;
VkWin32SurfaceCreateInfoKHR CI = {
.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR,
.pNext = NULL,
.flags = 0,
.hinstance = heart_win32->_hInstance,
.hwnd = this->W->m_hwnd
// W = amGHOST_WindowWIN32
};
VkSurfaceKHR S = nullptr;
VkResult return_code = vkCreateWin32SurfaceKHR(I, &CI, nullptr, &S);
amVK_return_code_log( "vkCreateWin32SurfaceKHR()" );
return S;
}
VkXlibSurfaceCreateInfoKHR & vkCreateXlibSurfaceKHR() 🛠️ [wip]amGHOST_VkSurfaceKHR::create_surface() 😉📽️ So far, The resultVkSurfaceKHR VK_S = amGHOST_VkSurfaceKHR::create_surface(amG_WindowOBJ, amVK_Instance::s_vk);
Naming Patterns ✏️ example naming patterns for storing all these data.... cz it's gonna get overwhelming pretty soon, pretty fast
Arraysclass amVK_InstanceProps {
public:
// Array of `HardWare amVK_1D_GPUs` connected to motherboard
static inline REY_Array<VkPhysicalDevice> amVK_1D_GPUs;
static inline REY_Array<REY_Array<VkQueueFamilyProperties>> amVK_2D_GPUs_QFAMs;
static inline REY_Array<VkExtensionProperties> amVK_1D_InstanceEXTs;
static inline REY_ArrayDYN<char*> amVK_1D_InstanceEXTs_Enabled;
static inline REY_ArrayDYN<SurfaceInfo> amVK_1D_SurfaceInfos; // See Below
static inline REY_Array<REY_Array<VkExtensionProperties>> amVK_2D_GPUs_EXTs;
// REY_Array doesn't allocate any memory by default
#define amVK_LOOP_GPUs(_var_) \
for (uint32_t _var_ = 0, lim = amVK_1D_GPUs.n; _var_ < lim; _var_++)
#define amVK_LOOP_QFAMs(_k_, _var_) \
for (uint32_t _var_ = 0, lim = amVK_2D_GPUs_QFAMs[_k_].n; _var_ < lim; _var_++)
};
ChildrenStructsclass amVK_InstanceProps {
class SurfaceInfo {
public:
VkSurfaceKHR S = nullptr;
SurfaceInfo(void) {}
SurfaceInfo(VkSurfaceKHR pS) {this-> S = pS;}
REY_Array<REY_Array<VkSurfaceFormatKHR>> amVK_2D_GPUs_ImageFMTs;
bool called_GetPhysicalDeviceSurfaceFormatsKHR = false;
void GetPhysicalDeviceSurfaceFormatsKHR(void); // amVK_2D_GPUs_ImageFMTs
};
};
VkFuncCallsclass amVK_InstanceProps {
public:
static inline bool called_EnumeratePhysicalDevices = false;
static inline bool called_GetPhysicalDeviceQueueFamilyProperties = false;
static inline bool called_EnumerateInstanceExtensions = false;
public:
static void EnumeratePhysicalDevices(void); // amVK_1D_GPUs
static void GetPhysicalDeviceQueueFamilyProperties(void); // amVK_2D_GPUs_QFAMs
static void EnumerateInstanceExtensions(void); // amVK_1D_InstanceEXTs
};
📜 REY_DOCsamVK_InstanceProps.hh📽️ So far, The result:-SwapChain Image Options 🖼️ .imageFormat + .imageColorSpace
vkGetPhysicalDeviceSurfaceFormatsKHR()
param surfaceFormats might be a bit different as per VkSurfaceKHR📽️ So far, The result:- 4.guide.chapter4.4.5.midway.cppVkSurfaceFormatKHR 📺||| .format 🖼️🔢 ImageFormat.colorSpace 🖼️🌈 ImageColorSpaceImageFormat & 🖼️🌈 ColorSpace
VkSwapchainCreateInfoKHR. instead of mumbo-jumbo-ing & mixing random stufs alltogether....Life is Hard without Images/Visualization
.minImageCount
🖼️ + .imageExtent + .imageArrayLayers + .imageUsage
🧙♂️ .compositeAlpha + .preTransform
VkSurfaceCapabilitiesKHR https://vkdoc.net/man/VkSurfaceCapabilitiesKHR
.minImageCount.currentExtent
SurfCaps also changevkGetPhysicalDeviceSurfaceCapabilitiesKHR() to get updated WindowSize / SurfCaps.maxImageArrayLayers.supportedUsageFlags.supportedTransforms.supportedCompositeAlpha
ALPHA-Blending/Transparency/GlassEffect:- you'd have to enable blending/transparency @ OS-Level first, iguess 🤔🔬🛠️ 2DriverIMPL.minImageCount:- must be at least 1.maxImageArrayLayers:- must be at least 1.supportedTransforms:- at least 1 bit must be set..supportedUsageFlags:-
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT must be included in the set.vkGetPhysicalDeviceSurfaceCapabilitiesKHR()📽️ So far, The result:- 4.guide.chapter4.4.5.midway.cppLife is Hard without Images/Visualization 2
.imageSharingMode
VkSharingMode
EXCLUSIVE/CONCURRENT📽️ So far, The result:-
amVK_SwapChain *SC = new amVK_SwapChain(VK_Surface);
SC->CI.imageFormat = VK_FORMAT_B8G8R8A8_UNORM;
SC->CI.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
SC->CI.minImageCount = amVK_InstanceProps::amVK_1D_SurfaceInfos[0].amVK_1D_GPUs_SurfCAP[0].minImageCount;
SC->CI.imageExtent = amVK_InstanceProps::amVK_1D_SurfaceInfos[0].amVK_1D_GPUs_SurfCAP[0].currentExtent;
SC->CI.imageArrayLayers = amVK_InstanceProps::amVK_1D_SurfaceInfos[0].amVK_1D_GPUs_SurfCAP[0].maxImageArrayLayers;
// You can just use "1" too, which is guranteed by DRIVER_IMPLEMENTATION [2DriverIMPL]
SC->CI.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
// `EXCLUSIVE/CONCURRENT` [Toggle]
SC->CI.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
// 2DriverIMPL:- VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT is guranteed to be supported by SurfCAP
Abbreviations
PD -> PhysicalDeviceGPUs -> PhysicalDevicesCI -> CreateInfoQCI -> QueueCreateInfoQFAM -> QueueFamilySurfCAP -> https://vkdoc.net/man/VkSurfaceCapabilitiesKHRSurfFMT -> https://vkdoc.net/man/VkSurfaceFormatKHRSC -> SwapChainVkSwapchainCreateInfoKHR
.flags 🏳️ ChapterZZZ.surface 🏄♀️ Chapter4.2.minImageCount = 🟪😉 SurfCAP.minImageCount.imageFormat = 🟪😉 SurfFMT[x].format.imageColorSpace = 🟪🤭 SurfFMT[x].colorSpace
.imageExtent = 🟪😊 SurfCAP.minImageCount.imageArrayLayers = 🟪 1
.imageUsage -> VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
.imageSharingMode = 🟪 EXCLUSIVE/CONCURRENT [Toggle]
VK_SHARING_MODE_CONCURRENT 🟨 ChapterZZZ
.queueFamilyIndexCount -> 0.pQueueFamilyIndices -> nullptrSwapChain Compositing Options 🧙♂️ .compositeAlphaOptions:- Don't use / Pre-multiplied / Post-multiplied / inherit from OS-native window systemRequirement:-
vkGetPhysicalDeviceSurfaceCapabilitiesKHR()
SurfCAP.supportedCompositeAlpha will changeVK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR.preTransformSurfCAP.currentTransformcurrentTransform isn't
VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR.clippedVK_TRUE allows the implementation to discard rendering outside of the surface area.presentMode 🏷️ VkPresentModeKHROptions:- IMMEDIATE / MAILBOX / FirstInFirstOut / FIFO_Relaxed.oldSwapChainSo far, The resultamVK_SwapChain *SC = new amVK_SwapChain(VK_Surface);
... Image Stuffs
SC->CI.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
SC->CI.preTransform = amVK_InstanceProps::amVK_1D_SurfaceInfos[0].amVK_1D_GPUs_SurfCAP[0].currentTransform;
SC->CI.clipped = VK_TRUE;
SC->CI.presentMode = VK_PRESENT_MODE_FIFO_KHR;
SC->CI.oldSwapchain = nullptr;
SwapChain Extension Enabling 🧩 [VK_KHR_swapchain] vkEnumerateDeviceExtensionProperties()vkEnumeratePhysicalDeviceExtensionProperties()VkDeviceVkPhysicalDeviceclass amVK_InstanceProps {
...
static inline bool called_EnumerateDeviceExtensionProperties = false;
static void EnumerateDeviceExtensionProperties(void); // amVK_2D_GPUs_EXTs
static inline REY_Array<REY_Array<VkExtensionProperties>> amVK_2D_GPUs_EXTs;
#define amVK_LOOP_GPU_EXTs(_k_, _var_) for (uint32_t _var_ = 0, lim = amVK_2D_GPUs_EXTs[_k_].n; _var_ < lim; _var_++)
static bool IS_GPU_EXT_Available(PD_Index GPU_k, const char *extName); // amVK_2D_GPUs_EXTs
// kinda copy of IS_InstanceEXT_Available
...
};
amVK_Device::Add_GPU_EXT_ToEnable(const char* extName)class amVK_Device {
...
REY_ArrayDYN<char*> amVK_1D_DeviceEXTs_Enabled;
void Log_GPU_EXTs_Enabled(VkResult ret);
void Add_GPU_EXT_ToEnable(const char* extName);
// Copy of `amVK_InstanceProps::Add_InstanceEXT_ToEnable()` -> but not static anymore.... 🤷♀️
};
So far, The resultvkCreateSwapchainKHR() amVK wrap 🌯 Part II amVK_InstanceProps::EnumerateDeviceExtensionProperties();
amVK_Device* D = new amVK_Device(amVK_InstanceProps::GetARandom_GPU());
D->select_QFAM_Graphics();
D->Add_GPU_EXT_ToEnable("VK_KHR_swapchain");
D->CreateDevice();
amVK wrap 🌯 Part III #include "amVK_Surface.hh"
#include "amVK_SwapChain.hh"
// TwT
REY_LOG("")
amVK_Surface *S = new amVK_Surface(VK_S);
amVK_SurfacePresenter *PR = S->PR;
PR->bind_Surface(S);
PR->bind_Device(D);
PR->create_SwapChain_interface();
// This amVK_SwapChain is Bound to this amVK_Surface
amVK_SwapChain *SC = PR->SC;
SC->konf_ImageSharingMode(VK_SHARING_MODE_EXCLUSIVE);
SC->konf_Images(
amVK_IF::RGBA_8bpc_UNORM, // VK_FORMAT_R8G8B8A8_UNORM
amVK_CS::sRGB, // VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
amVK_IU::Color_Display // VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
);
SC->konf_Compositing(
amVK_PM::FIFO, // VK_PRESENT_MODE_FIFO_KHR
amVK_CC::YES, // Clipping:- VK_TRUE
amVK_TA::Opaque // VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR
);
SC->sync_SurfCaps(); // refresh/fetch & set/sync ---> latest SurfCaps
SC->CI.oldSwapchain = nullptr;
SC->CreateSwapChain();
🧙♂️ Part 2: The True Arcane Secrets of
RenderPass
(SubPass + Image Layer Transition) & FrameBuffers
Welcome to the inner sanctum where GPU gods decide how fast your pixels live or die.
- ChatGPT
RenderPass 🥪 "
subpassesare the soul of RenderPass! . But it's not just aboutsubpassesonly...." - ChatGPT
RenderPass? "This is one of the most convoluted parts of the Vulkan specification, especially for those who are just starting out." - P.A. Minerva
ex. 1:- PostProcessing Effects
RenderPass:
- color attachment
- depth attachment
subpasses:
- Subpass 0: render geometry
- Subpass 1: post-process effects
// multiple rendering steps without switching FrameBuffers/AttachMents
// All defined in ONE render pass
ex. 2:- Deferred Shading
attachments:
- position: offscreen image
- normal: offscreen image
- albedo: offscreen image
- depth: depth image
- finalColor: swapchain image
subpasses:
- Subpass 0: G-buffer generation (write position, normal, albedo)
- Subpass 1: Lighting pass (read G-buffers, write to finalColor)
subpasses, you'd need to switch framebuffers (expensive!).subpasses, Vulkan can optimize this by keeping data in GPU memory (especially tile-based GPUs).ex. 3:- Post-Processing Chain
attachments:
- scene: offscreen image
- postProcessOut: swapchain image
subpasses:
- Subpass 0: scene render → scene
- Subpass 1: post-process → postProcessOut
COLOR_ATTACHMENT_OPTIMAL → SHADER_READ_ONLY_OPTIMAL)ex. 4:- Shadow Map Pass / Render from light's POV, to a depth-only image
attachments:
- depth: depth image
subpasses:
- Subpass 0: write to depth only (no color)
ex. 5:- 3D Scene -> Depth Testing
attachments:
- color: swapchain image
- depth: depth image
subpasses:
- Subpass 0:
- color attachment: color
- depth attachment: depth
RenderPass? 🥪 RenderPass is designed around subpasses.The core purpose of a RenderPass is to tell Vulkan:
“Hey, I’m going to do these rendering stages (
subpasses), in this order, using these attachments.”
So yeah, subpasses are the main reason for a RenderPass to exist. subpasses are the soul of RenderPass!
But it's not just about subpasses only:-
🌟 Load/Store Ops — "What should I do with the image before & after rendering?"
LOAD: Keep whatever was already in the attachment.
CLEAR: Wipe it to a specific value (e.g., clear color to black).
DONT_CARE: Vulkan can throw away old contents (faster, if you don’t care).
STORE: Save the result (e.g., to present to the screen or use later).
DONT_CARE: Vulkan can discard the result (like shadow maps or intermediate stuff you don't need to read later).
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
// Meaning: Clear the image before rendering, and store the result so we can present it.
🎯 Image Layout Transitions — "How should the GPU access this image during the pass?"
[ VK_IMAGE_LAYOUT_UNDEFINED ]
👇 clear
[ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL ]
👇 render
[ VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ]
👇 post-process
[ VK_IMAGE_LAYOUT_PRESENT_SRC_KHR ]
📜 Attachments — "What images are we using?"
RenderPass Attachment is not an actual thing!RenderPass Attachment Description/Descriptor is a thing!
Attachments right here, as we send the AttachmentDescriptions -> RenderPassRenderPass Attachment != FrameBuffer AttachmentFrameBuffer Attachment
VkImageViews of SwapChain ImagesImage Layout Transitions🏭 1. Different hardware units = different memory access patterns
GPU Unit Access Pattern
------------------ -----------------------
Fragment Shader Texture-like (random)
Render Output Unit Tiled or linear (write-heavy)
Compute Shader Raw buffer-style
Display Engine Linear format
- for ex.
garbagehuge perf penalty as the driver does conversion.... (every single time you access a single pixel) (a single pixel would = an element in an 2D Array) (Texture might have Millions of Pixel)🧱 Physical Layout in VRAM (Tiles vs Linear)
finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
“Yo, I’m done rendering — please un-tile this, decompress it, and arrange it in scanlines for display.”
🔄 Transitions let the driver do reordering, compression, or memory reallocation
// When you declare:-
finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
// you are not just giving a hint....
// ---- you are saying:-
“After rendering, I'm going to sample this as a texture — so prepare it.”
This allows the GPU driver to:
- flush caches
- Decompress the image (some GPUs compress attachments during render!)
- Move memory or restructure tiles
- Or even alias memory with another attachment in a single memory block
- In modern GPUs, there's hardware image compression, like:-
- ARM's AFBC (Arm Frame Buffer Compression)
- AMD's DCC (Delta Color Compression)
- NVIDIA has their own secret sauce too
🌀 Aliasing & Tile Memory Reuse [ImageLayouts + Barriers]
“When does this memory stop being ‘render target’ and start being ‘texture’ or ‘compute input’?”
Layouts + barriers = safe aliasing.
Drivers can now:
- Use the same memory pool
- Skip clearing
- Not double allocate
You become a GPU memory ninja 💨🗡️
📜 Predictability = Performance
Explicit layouts give Vulkan this power:
- It knows exactly when and how you are going to use an image.
- So it can avoid runtime guessing, which causes:
- CPU stalls
- Cache flushes
- Sync fences
- Or even full GPU pipeline bubbles 😵💫
- Compared to OpenGL or DirectX11, where the driver had to guess what you meant and do hidden magic — Vulkan is like:
“If you don’t tell me what layout you want, I’ll trip and fall flat on my face 😭”
👻 You can skip transitions altogether if you do it right
VK_ATTACHMENT_LOAD_OP_DONT_CARE and reusing image layouts cleverly — you can avoid layout transitions entirely.
🎮 Analogy: Baking Cookies 🍪
Let’s say you're:
- Baking cookies (rendering)
- Then you plate them for display (presenting)
- Later you want to show them off or decorate them (sample in shaders)
Vulkan Image Layout Cookie Stage
------------------------ ---------------------------------
UNDEFINED Empty tray, nothing on it yet
COLOR_ATTACHMENT_OPTIMAL You're baking the cookies 🔥
SHADER_READ_ONLY_OPTIMAL You’ve finished baking and wanna decorate (like sampling in a post-process shader) 🎨
PRESENT_SRC_KHR You’re plating the cookies to serve 🥂 (sending to the screen)
“Please transition between layouts, so I know what stage your cookie is in — and I’ll move it to the right place, with oven mitts, spatulas, etc.”
🧼 Why does this matter?
You may try to grab a cookie off a 200°C tray and get burned (💀 invalid reads)
The cookies may not be fully baked (💀 undefined writes)
Or worse: you show your customer an empty plate because Vulkan never moved them to the PRESENT_SRC_KHR plate 😭
🚀 What makes Vulkan powerful?
You get to say:
1. "Bake in tray A"
2. "Decorate using buffer B"
3. "Present from plate C"
But you must tell Vulkan when to move cookies from one surface to another.
Layouts = telling Vulkan exactly thaaat!
🧪 Subpass Optimization (Tile-Based GPUs)
On tile-based GPUs (like PowerVR or Mali):
- Entire framebuffers live on-chip, in tiles
- You can run all subpasses without touching VRAM!
But it only works if Vulkan knows:
- The image will stay in the same layout
- No unnecessary STORE or layout transitions
By carefully using:
layout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL;
loadOp = DONT_CARE;
storeOp = DONT_CARE;
You unlock 🥷 zero-cost rendering.
🏁 TL;DR: Image Layout Transitions Aren’t Just Bureaucracy
🎯 They are literal instructions to the driver:
- "Where this image lives"
- "How it’s structured"
- "What GPU unit will touch it next"
- "Whether you need to prepare, flush, decompress, or alias it"
🎯 And by explicitly telling the GPU, you:
- Avoid expensive guesses
- Skip hidden memory ops
- Unlock mobile-level optimizations
- Prevent subtle bugs and undefined behavior
📜 RenderPass Attachments Desc.
RenderPass Attachment is not an actual thing!RenderPass Attachment Description/Descriptor is a thing!
Attachments right here, as we send the AttachmentDescriptions -> RenderPassRenderPass Attachment Description/Descriptors are not actual images — they’re a template for what the RenderPass expects!
RenderPass exactly with thatRenderPass Attachment != FrameBuffer Attachment| RenderPass Attachments | Framebuffers |
|---|---|
| Define what is needed | Provide which resources to use |
| Abstract (format, usage, layout) | Concrete (image views) |
| Reusable across Framebuffers | Swapchain-dependent (often 1:1) |
Think of it like a Socket & Plug
- `RenderPass` 🥪 = The RenderPass defines the socket (shape, voltage).
- `Framebuffer` 📦 = The Framebuffer provides the plug (actual wires) that fits the socket.
📦 FrameBuffer Attachment
VkImageViewImage Views (VkImageView):
Handles to specific images (e.g., swapchain images, depth textures).
Compatibility:
Must match the RenderPass’s attachment definitions (format, sample count, size).
Swapchain Link:
Typically, one Framebuffer per swapchain image.
🛒 FrameBuffers [🍞🍅🥚🍗]
ImageViews (e.g., SwapChain Images, Depth Textures) to the attachments defined in the RenderPass.RenderPass’s Attachment Descriptions (format, size, sample count).SwapChain-dependent (e.g., each SwapChainImage typically has its own Framebuffer).- `RenderPass` 🥪 = A recipe requiring "2 eggs and 1 cup of flour" (attachments).
- `Framebuffer` 🛒 = The actual eggs and flour (image views) you use to bake a cake (render a frame).
📦 Attachments
RenderPass Attachments (Blueprint) → Framebuffer (Instance)
┌-------------------------------┐ ┌-----------------------------┐
│ Attachment 0: Color (SRGB) │ │ Image View 0: Swapchain IMG │
│ Attachment 1: Depth (D32_SF) │ │ Image View 1: Depth Texture │
└-------------------------------┘ └-----------------------------┘
Each attachment you declare includes:
- Format (VK_FORMAT_B8G8R8A8_SRGB, etc.)
- Sample count (for MSAA)
- Load/store ops
- Layouts (see above)
"From all the attachments I’ve declared, I’m gonna use these ones in this subpass."
attachments[0] = colorAttachment; // swapchain image
attachments[1] = depthAttachment; // depth image
subpass.colorAttachment = &attachments[0];
subpass.depthAttachment = &attachments[1];
So even if your RenderPass only has one subpass, the Vulkan driver still wants to know:
- How many attachments
- What to do with them (clear/store?)
- What layouts they go into and come out as
🛒 FrameBuffers v/s 📦 Attachments:- The Last Fight, (If Above stuffs got you confused):-
| Aspect | Attachments (RenderPass) ✖ | Framebuffers 🖼 |
|---|---|---|
| Purpose | Define what resources are needed (format, usage, layout transitions) 📝 | Specify which actual images (image views) to use for those resources ✏️ |
| Concrete/Abstract | Abstract (blueprint) 📒 | Concrete (instance) 🏗 |
| Lifetime | Long-lived (reused across frames) ♻️ | Short-lived (often recreated with swapchain) ↻ |
| Dependencies | Independent of images/swapchain ⛔ 🖼 | Tied to swapchain images or specific textures 🔗 |
| Example | "Need a color attachment (SRGB) and depth attachment (D32_SFLOAT)" 🎨➕🌑 | "Use this swapchain image and that depth texture" 🖼 1️⃣ + 🖼 2️⃣ |
Lifecycle Flowchart
RenderPass Creation 🏗️
│
├── Define Attachments 📄 (format, load/store ops, layouts)
│ └── "I need a color slot (B8G8R8A8_SRGB) and depth slot (D32_SFLOAT)" 🎨🌑
│
└── Define Subpasses 🔗 (how attachments are used in rendering steps)
↓
Framebuffer Creation 🛠️
│
├── Bind Image Views to Attachment Slots 🔌
│ └── "Slot 0 = Swapchain Image View 🖼️, Slot 1 = Depth Texture View 🧊"
│
└── Validate Compatibility ✅ (size, format, sample count)
| Scenario | Attachments (RenderPass) 🧩 | Framebuffers 🖼️ |
|---|---|---|
| Swapchain Rendering | Define color/depth formats and layouts. 🎨🔄🌑 | Bind swapchain images + depth texture. 🖼️🔗🧊 |
| Deferred Rendering | Define G-Buffer attachments (Albedo, Normal, Position). 📦📦📦 | Bind actual G-Buffer image views. 🖼️1️⃣🖼️2️⃣🖼️3️⃣ |
| Post-Processing | Define input (e.g., HDR color) + output (e.g., SRGB). 🌟➡️🎨 | Bind input texture + swapchain image. 🧩🖼️ |
Key Interactions
RenderPass Begin Command 🚦
│
├── Uses RenderPass Attachments 🧩 (format, load/store rules)
│
└── Uses Framebuffer 🖼️ (actual images to write to)
↓
GPU Renders 🎮
│
├── Reads/Writes to Framebuffer’s Image Views 📊
│
└── Follows Attachment Rules (clearing, layout transitions) 🔄
Emoji Analogy Time! 🤯
Next Chapter will be on 🛒 FrameBuffers!!!! 🤭
amVK Wrap 😊 #include "amVK_RenderPass.hh
// TwT
SC->GetSwapChainImagesKHR();
SC->CreateSwapChainImageViews();
amVK_RenderPass *RP = PR->create_RenderPass_interface();
vkCreateRenderPass() amVK_SwapChain.hh Current Implementation & Change it as needed
VkRenderPassCreateInfo() .flags 🚩 Only Option:- used for Qualcom Extension.pAttachments 🔗 this->SubChapter4.pSubpasses 🔗 this->SubChapter5.pDependencies 🔗 this->SubChapter6ImageViews vkGetSwapchainImagesKHR()
vkGetPhysicalDeviceQueueFamilyProperties()class amVK_SwapChain {
...
public:
amVK_Device *D = nullptr;
VkSwapchainKHR SC = nullptr;
REY_Array<VkImage> amVK_1D_SC_IMGs;
REY_Array<amVK_Image> amVK_1D_SC_IMGs_amVK_WRAP;
bool called_GetSwapchainImagesKHR = false;
public:
...
vkCreateImageView()
void CreateSwapChainImageViews(void) {
REY_Array_LOOP(amVK_1D_SC_IMGs_amVK_WRAP, i) {
amVK_1D_SC_IMGs_amVK_WRAP[i].createImageView();
}
}
amVK_Image.hh:- 4.guide.chapter5.3.2.Image.hhVkImageViewCreateInfo
void amVK_SwapChain::CreateSwapChainImageViews(void) {
REY_Array_LOOP(amVK_1D_SC_IMGs_amVK_WRAP, i) {
// ViewCI.image
// ViewCI.format
// should be set inside amVK_SwapChain::GetSwapchainImagesKHR()
amVK_1D_SC_IMGs_amVK_WRAP[i].ViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D;
amVK_1D_SC_IMGs_amVK_WRAP[i].ViewCI.components = { // Equivalent to:
VK_COMPONENT_SWIZZLE_R, // VK_COMPONENT_SWIZZLE_IDENTITY
VK_COMPONENT_SWIZZLE_G, // VK_COMPONENT_SWIZZLE_IDENTITY
VK_COMPONENT_SWIZZLE_B, // VK_COMPONENT_SWIZZLE_IDENTITY
VK_COMPONENT_SWIZZLE_A // VK_COMPONENT_SWIZZLE_IDENTITY
};
amVK_1D_SC_IMGs_amVK_WRAP[i].ViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
amVK_1D_SC_IMGs_amVK_WRAP[i].ViewCI.subresourceRange.baseMipLevel = 0;
amVK_1D_SC_IMGs_amVK_WRAP[i].ViewCI.subresourceRange.levelCount = 1;
amVK_1D_SC_IMGs_amVK_WRAP[i].ViewCI.subresourceRange.baseArrayLayer = 0;
amVK_1D_SC_IMGs_amVK_WRAP[i].ViewCI.subresourceRange.layerCount = 1;
amVK_1D_SC_IMGs_amVK_WRAP[i].createImageView();
}
}
VkAttachmentDescription VkSubpassDescription VkSubpassDependency class amVK_RenderPass {
public:
REY_ArrayDYN<VkAttachmentDescription> attachments;
REY_ArrayDYN<VkSubpassDescription> subpasses;
REY_ArrayDYN<VkSubpassDependency> dependencies;
void set_attachments_subpasses_dependencies(void);
}
amVK_RenderPass.hh [Full Implementation]:- 4.guide.chapter5.8.RenderPass.hhamVK_RenderPass *RP = new amVK_RenderPass(D);
RP->attachments.push_back({
.format = SC->CI.imageFormat, // Use the color format selected by the swapchain
.samples = VK_SAMPLE_COUNT_1_BIT, // We don't use multi sampling in this example
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, // Clear this attachment at the start of the render pass
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
// Keep its contents after the render pass is finished (for displaying it)
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
// Similar to loadOp, but for stenciling (we don't use stencil here)
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
// Similar to storeOp, but for stenciling (we don't use stencil here)
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
// Layout at render pass start. Initial doesn't matter, so we use undefined
.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
// Layout to which the attachment is transitioned when the render pass is finished
// As we want to present the color attachment, we transition to PRESENT_KHR
});
VkAttachmentReference colorReference = {
.attachment = 0,
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
};
RP->subpasses.push_back({
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
.inputAttachmentCount = 0,
// Input attachments can be used to sample from contents of a previous subpass
.pInputAttachments = nullptr, // (Input attachments not used by this example)
.colorAttachmentCount = 1, // Subpass uses one color attachment
.pColorAttachments = &colorReference, // Reference to the color attachment in slot 0
.pResolveAttachments = nullptr,
// Resolve attachments are resolved at the end of a sub pass and can be used for e.g. multi sampling
.pDepthStencilAttachment = nullptr, // (Depth attachments not used by this sample)
.preserveAttachmentCount = 0,
// Preserved attachments can be used to loop (and preserve) attachments through subpasses
.pPreserveAttachments = nullptr // (Preserve attachments not used by this example)
});
RP->dependencies.push_back({
// Setup dependency and add implicit layout transition from final to initial layout for the color attachment.
// (The actual usage layout is preserved through the layout specified in the attachment reference).
.srcSubpass = VK_SUBPASS_EXTERNAL,
.dstSubpass = 0,
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.srcAccessMask = VK_ACCESS_NONE,
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT,
});
RP->set_attachments_subpasses_dependencies();
RP->createRenderPass();
------------------------------------- Made with help from P.A.Minerva Vulkan Guide -------------------------------------
https://paminerva.github.io/docs/LearnVulkan/01.A-Hello-Window#416---creating-a-render-pass
main.cpp [Full Implementation]:- 4.guide.chapter5.8.main.cppamVK_Surface amVK_Surface.hh [Full Implementation]:- 4.guide.chapter5.9.Surface.hhamVK_ColorSpace.hh, amVK_Surface, amVK_SurfacePresenter, Renaming Things in amVK
amVK_ColorSpace.hh /**
* ex. 1 amVK_IF::RGBA_8bpc_UNORM
*/
namespace amVK_ImageFormat {
// 8bpc = 8-bits per channel
inline constexpr VkFormat RGBA_8bpc_UNORM = VK_FORMAT_R8G8B8A8_UNORM; // 37
inline constexpr VkFormat RGBA_8bpc_SNORM = VK_FORMAT_R8G8B8A8_SNORM; // 38
inline constexpr VkFormat RGBA_8bpc_USCALED = VK_FORMAT_R8G8B8A8_USCALED; // 39
inline constexpr VkFormat RGBA_8bpc_SSCALED = VK_FORMAT_R8G8B8A8_SSCALED; // 40
inline constexpr VkFormat RGBA_8bpc_UINT = VK_FORMAT_R8G8B8A8_UINT; // 41
inline constexpr VkFormat RGBA_8bpc_SINT = VK_FORMAT_R8G8B8A8_SINT; // 42
inline constexpr VkFormat RGBA_8bpc_SRGB = VK_FORMAT_R8G8B8A8_SRGB; // 43
// Common Depth/Stencil Formats
inline constexpr VkFormat D32_SFLOAT = VK_FORMAT_D32_SFLOAT;
inline constexpr VkFormat D24_UNORM_S8_UINT = VK_FORMAT_D24_UNORM_S8_UINT;
}
#define amVK_IF amVK_ImageFormat
#define amVK_PF amVK_ImageFormat
#define amVK_PixelFormat amVK_ImageFormat
amVK_Surface /**
* VULKAN-EXT:- `VK_KHR_surface`
* IMPL:- `amVK_1D_SurfaceInfos`
*/
class amVK_Surface {
public:
VkSurfaceKHR S = nullptr; // Set in CONSTRUCTOR
amVK_SurfacePresenter *PR = nullptr; // Set in CONSTRUCTOR
amVK_Surface(void) {}
amVK_Surface(VkSurfaceKHR pS);
REY_Array<REY_Array<VkSurfaceFormatKHR>> amVK_2D_GPUs_ImageFMTs;
REY_Array<VkSurfaceCapabilitiesKHR> amVK_1D_GPUs_SurfCAP;
bool called_GetPhysicalDeviceSurfaceFormatsKHR = false;
bool called_GetPhysicalDeviceSurfaceCapabilitiesKHR = false;
void GetPhysicalDeviceSurfaceInfo(void);
void GetPhysicalDeviceSurfaceCapabilitiesKHR(void);
};
amVK_SurfacePresenter class amVK_SurfacePresenter {
public:
amVK_Surface *S = nullptr;
amVK_SwapChain *SC = nullptr;
amVK_RenderPass *RP = nullptr;
// SC.VkDevice = RP.VkDevice
amVK_Device *D = nullptr;
VkPhysicalDevice GPU = nullptr;
// amVK_Device.m_PD = this->GPU;
amVK_GPU_Index GPU_Index = 0;
public:
void bind_Device(amVK_Device *D);
amVK_SurfacePresenter (amVK_Surface* pS) {this->S = pS;}
public:
amVK_SwapChain* create_SwapChain(void);
amVK_RenderPass* create_RenderPass(void);
// Defined currently inside amVK_SwapChain.cpp
void refresh_SurfCaps(void) { this->S->GetPhysicalDeviceSurfaceCapabilitiesKHR(); }
VkSurfaceCapabilitiesKHR* fetched_SurfCaps(void) {
return &( this->S->amVK_1D_GPUs_SurfCAP[this->GPU_Index] );
}
};
amVK Naming Conventions 😊 Calling Vulkan Library Functions:-
bool called_GetPhysicalDeviceSurfaceFormatsKHR = false;
bool called_GetPhysicalDeviceSurfaceCapabilitiesKHR = false;
void GetPhysicalDeviceSurfaceInfo(void);
void GetPhysicalDeviceSurfaceCapabilitiesKHR(void);
vkCreateZZZ() wrappers
amVK_SwapChain {
void CreateSwapChain(void) {
VkResult return_code = vkCreateSwapchainKHR(this->D->m_device, &CI, nullptr, &this->SC);
amVK_return_code_log( "vkCreateSwapchainKHR()" ); // above variable "return_code" can nott be named smth else
}
}
amVK_Object/Instance-Creation
amVK_SwapChain* amVK_SurfacePresenter::create_SwapChain(void);
amVK_Object::Functions()
amVK_SwapChain* create_SwapChain(void); // Creates amVK_Object
amVK_RenderPass* create_RenderPass(void); // Creates amVK_Object
void refresh_SurfCaps(void); // SurfCapabilities changes if Window is Resized
VkSurfaceCapabilitiesKHR* fetched_SurfCaps(void); // Returns the REFRESHED/FETCHED element
void amVK_SwapChain::sync_SurfCaps(void);/** Refreshes & Syncs `SurfaceCapabilites` */
void amVK_SwapChain::konf_Images(
VkFormat IF,
VkColorSpaceKHR CS,
VkImageUsageFlagBits IU,
bool autoFallBack = true
)
void amVK_SwapChain::konf_Compositing(
VkPresentModeKHR PM,
amVK_CompositeClipping CC,
VkCompositeAlphaFlagBitsKHR CA
);
void amVK_SwapChain::konf_ImageSharingMode(VkSharingMode ISM);
VkFormat amVK_SwapChain::active_PixelFormat(void) {return CI.imageFormat;}
VkColorSpaceKHR amVK_SwapChain::active_ColorSpace (void) {return CI.imageColorSpace;}
VkObject Variables
class amVK_Image {
public:
amVK_Device *D = nullptr;
VkImage vk_Image = nullptr;
VkImageView vk_ImageView = nullptr;
};
class amVK_FrameBuffer {
public:
amVK_SurfacePresenter *PR = nullptr; // Basically, Parent Pointer
VkFramebuffer vk_FrameBuffer = nullptr;
};
class amVK_RenderPass {
public:
amVK_SurfacePresenter *PR = nullptr; // Basically, Parent Pointer
VkRenderPass vk_RenderPass = nullptr;
};
class amVK_Surface {
public:
amVK_SurfacePresenter *PR = nullptr; // Created in CONSTRUCTOR
VkSurfaceKHR vk_SurfaceKHR = nullptr; // Set in CONSTRUCTOR
}
amVK_RenderPass_Descriptors.hh namespace amVK_RP_AttachmentDescription
{
// Change .format before using
inline VkAttachmentDescription ColorPresentation = {
.format = VK_FORMAT_UNDEFINED, // you should use the ImageFormat selected by the swapchain
.samples = VK_SAMPLE_COUNT_1_BIT, // We don't use multi sampling in this example
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, // Clear this attachment at the start of the render pass
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
// Keep its contents after the render pass is finished (for displaying it)
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
// Similar to loadOp, but for stenciling (we don't use stencil here)
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
// Similar to storeOp, but for stenciling (we don't use stencil here)
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
// Layout at render pass start. Initial doesn't matter, so we use undefined
.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
// Layout to which the attachment is transitioned when the render pass is finished
// As we want to present the color attachment, we transition to PRESENT_KHR
};
};
#define amVK_RPADes amVK_RP_AttachmentDescription
#define amVK_RPARef amVK_RP_AttachmentReference
#define amVK_RPSDes amVK_RP_SubpassDescription
#define amVK_RPSDep amVK_RP_SubpassDependency
amVK_RenderPass_Descriptors.hh file yourself 🤭amVK_RenderPass *RP = PR->create_RenderPass_interface();
amVK_RPADes::ColorPresentation.format = SC->CI.imageFormat;
RP->AttachmentInfos .push_back(amVK_RPADes::ColorPresentation);
RP->SubpassInfos .push_back(amVK_RPSDes::ColorPresentation);
RP->Dependencies .push_back(amVK_RPSDep::ColorPresentation);
RP->sync_Attachments_Subpasses_Dependencies();
RP->CreateRenderPass();
REY_Utils.hh REY_ArrayREY_ArrayDYN<VkDeviceQueueCreateInfo> Array = REY_ArrayDYN<VkDeviceQueueCreateInfo>(nullptr, 0, 0);
// No MemoryAllocation by default 😊
// 1. REY_ArrayDYN.initialize(10)
// 2. REY_ARRAY_PUSH_BACK(Array) = your_QueueCI; [not a function. but rather a preprocessor macro]
FrameBuffer [🍞🍅🥚🍗] vkCreateFramebuffer() amVK_RenderPass.hh Current Implementation & Change it as needed
VkFramebufferCreateInfo() https://vkdoc.net/man/VkFramebufferCreateInfo
.flags 🏳️ 0
VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT [ImageLess FrameBuffer].renderPass 🟪💁♀️.pAttachments 🔗 SubChapter 3.width.height.layersamVK_RenderPass.hh:-📽️ So far, The result amVK_RenderPassFBs.hhVkImageView .pAttachments </> TheCodevoid amVK_RenderPassFBs::CreateFrameBuffers(void) {
if (this->SC_IMGs->called_GetSwapChainImagesKHR == false) {
this->SC_IMGs-> GetSwapChainImagesKHR(); }
if (this->SC_IMGs->called_CreateSwapChainImageViews == false) {
this->SC_IMGs-> CreateSwapChainImageViews(); }
VkExtent2D imageExtent = this->SC_IMGs->active_ImageExtent();
this->CI.width = imageExtent.width;
this->CI.height = imageExtent.height;
this->amVK_1D_RP_FBs.reserve(this->SC_IMGs->amVK_1D_SC_IMGs.n);
REY_Array_LOOP(this->amVK_1D_RP_FBs, k) {
this->CI.attachmentCount = 1;
this->CI.pAttachments = &(this->SC_IMGs->amVK_1D_SC_IMGViews[k]);
#define VK_DEVICE this->RP->D->vk_Device
VkResult return_code = vkCreateFramebuffer(VK_DEVICE, &CI, nullptr, &this->amVK_1D_RP_FBs[k]);
amVK_return_code_log( "vkCreateFramebuffer()" );
}
}
📽️ So far, The result amVK_RenderPass.cpp#L34-L55CommandBuffer 📝 Rendering commands have to be Recorded in a CommandBuffer.
Only then the GPU can work on it 💁♀️.
That's the idea, since decades ago, so yeah, xD.
amVK wrap 🌯 #include "amVK_Synchronization.hh"
#include "amVK_CommandPoolMAN.hh"
// TwT
REY_LOG("");
#define amVK_S amVK_Sync
#define CPCF CommandPoolCreateFlags
amVK_CommandPoolMAN*CPMAN = new amVK_CommandPoolMAN(D);
CPMAN->init_CMDPool_Graphics(amVK_S::CPCF::RecordBuffer_MoreThanOnce);
CPMAN->CreateCommandPool_Graphics(flags);
CPMAN->AllocateCommandBuffers1_Graphics(1);
amVK_CommandBufferPrimary *CB = new amVK_CommandBufferPrimary(CPMAN->BUFFs1.Graphics[0]);
VkCommandPool VkCommandPoolCreateInfo .sType 🟪 VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO
.pNext 🟪 NULL
.flags 🏳️ VkCommandPoolCreateFlagBits
TRANSIENTRESET_COMMAND_BUFFER:- Lets you call vkBeginCommandBuffer() on same CMDBUF more than oncePROTECTED0:- Can't call vkBeginCommandBuffer() more than once on the same CMDBUF.queueFamilyIndex
as per queueFamilyvkCreateCommandPool() .device 🟪 💁♀️.pCreateInfo 🟪 💁♀️.pAllocator 🟨 ChapterZZZ.pSemaphore ↩️📦amVK_FrameBuffer.hh Current Implementation & Change it as needed
amVK 🔗 amVK_CommandPoolMAN.hh VkCommandBuffer VkCommandBufferAllocateInfo .sType 🟪 VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO.pNext 🟪 NULL.commandPool 🟪 💁♀️.level 🔀 PRIMARY/SECONDARY [Toggle].commandBufferCount 🟪 💁♀️vkAllocateCommandBuffers() .device.pAllocateInfo 🟪 💁♀️pCommandBuffers ↩️📦amVK 🔗 amVK_CommandPoolMAN.hh#L63 01 May, 2025
amVK_CommandPoolCATs (Categories) e.g. Graphics/Compute 💁♀️EntryNotFound (FileSystemError): Error: ENOENT: no such file or directory, open 'c:\Users\REY\Desktop\idk\amGHOST\amVK_Guide\P1\CH9.md'
So far, The result #include "amGHOST_System.hh"
#include "amVK_Instance.hh"
#include "amVK_Device.hh"
#include "amGHOST_VkSurfaceKHR.hh"
#include "amVK_Surface.hh"
#include "amVK_SwapChain.hh"
#include "amVK_ColorSpace.hh"
#include "amVK_RenderPass.hh"
#include "amVK_RenderPass_Descriptors.hh"
#include "amVK_CommandPoolMAN.hh"
int main(int argumentCount, char* argumentVector[]) {
REY::cout << "\n";
// ------------------------- amGHOST ----------------------------
amGHOST_System::create_system();
amGHOST_Window *W = amGHOST_System::heart->new_window_interface();
W->create(L"Whatever", 0, 0, 500, 600);
// ------------------------- amGHOST ----------------------------
REY_LOG("");
REY_LOG("");
// --------------------------- amVK -----------------------------
REY_LOG("");
amVK_Instance::EnumerateInstanceExtensions();
amVK_Instance::EnumerateInstanceLayerProperties();
amVK_Instance::addTo_1D_Instance_Layers_Enabled("VK_LAYER_KHRONOS_validation");
amVK_Instance::addTo_1D_Instance_EXTs_Enabled("VK_KHR_surface");
amVK_Instance::addTo_1D_Instance_EXTs_Enabled(amGHOST_System::get_vulkan_os_surface_ext_name());
amVK_Instance::CreateInstance();
REY_LOG("");
VkSurfaceKHR VK_S = amGHOST_VkSurfaceKHR::create_surface(W, amVK_Instance::vk_Instance);
REY_LOG("");
amVK_Instance::EnumeratePhysicalDevices();
amVK_GPUProps *GPUProps = amVK_InstanceProps::GetARandom_GPU();
GPUProps->GetPhysicalDeviceQueueFamilyProperties();
GPUProps->EnumerateDeviceExtensionProperties();
GPUProps->REY_CategorizeQueueFamilies();
amVK_Device* D = new amVK_Device(GPUProps);
D->addTo_1D_GPU_EXTs_Enabled("VK_KHR_swapchain");
D->CreateDevice(1);
D->GetDeviceQueues();
REY_LOG("")
amVK_Surface *S = new amVK_Surface(VK_S);
S->GetPhysicalDeviceSurfaceInfo();
S->GetPhysicalDeviceSurfaceCapabilitiesKHR();
// --------------------------- SwapChain, RenderPass, FrameBuffers -----------------------------
REY_LOG("")
amVK_SwapChain *SC = new amVK_SwapChain(this->S, this->D);;
SC->konf_ImageSharingMode(VK_SHARING_MODE_EXCLUSIVE);
SC->konf_Images(
amVK_IF::RGBA_8bpc_UNORM, // VK_FORMAT_R8G8B8A8_UNORM
amVK_CS::sRGB, // VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
amVK_IU::Color_Display // VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
);
SC->konf_Compositing(
amVK_PM::FIFO, // VK_PRESENT_MODE_FIFO_KHR
amVK_CC::YES, // Clipping:- VK_TRUE
amVK_TA::Opaque // VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR
);
SC->sync_SurfCaps(); // refresh/fetch & set/sync ---> latest SurfCaps
SC->CI.oldSwapchain = nullptr;
SC->CreateSwapChain();
amVK_SwapChainIMGs *SC_IMGs = new amVK_SwapChainIMGs(this->SC);
SC_IMGs-> GetSwapChainImagesKHR();
SC_IMGs->CreateSwapChainImageViews();
amVK_RenderPass *RP = new amVK_RenderPass(this->D);
amVK_RPADes::ColorPresentation.format = SC->CI.imageFormat;
RP->AttachmentInfos.push_back(amVK_RPADes::ColorPresentation);
RP->SubpassInfos .push_back(amVK_RPSDes::ColorPresentation);
RP->Dependencies .push_back(amVK_RPSDep::ColorPresentation);
RP->sync_Attachments_Subpasses_Dependencies();
RP->CreateRenderPass();
amVK_RenderPassFBs *RP_FBs = PR->create_FrameBuffers_interface();
RP_FBs->CreateFrameBuffers();
// --------------------------- SwapChain, RenderPass, FrameBuffers -----------------------------
amVK_CommandPoolMAN *CPMAN = PR->create_CommandPoolMAN_interface();
CPMAN->init_CMDPool_Graphics();
CPMAN->CreateCommandPool_Graphics(amVK_Sync::CommandPoolCreateFlags::RecordBuffer_MoreThanOnce);
CPMAN->AllocateCommandBuffers1_Graphics(1);
amVK_CommandBufferPrimary *CB = new amVK_CommandBufferPrimary(CPMAN->BUFFs1.Graphics[0]);
// --------------------------- amVK -----------------------------
REY_LOG("");
REY_LOG("");
// ------------------------- CleanUp & ExportJSON ----------------------------
REY::cin.get(); // wait for terminal input
amVK_InstancePropsEXPORT::Export_nilohmannJSON_EXT();
destroy_everything_serially(); // Last Chapter, copy code from there
W->m_amGHOST_VkSurface->destroy();
amVK_Instance::DestroyInstance();
W->destroy();
// ------------------------- CleanUp & ExportJSON ----------------------------
REY::cout << "\n";
}
Shader #version 450
layout(location = 0) out vec3 fragColor;
vec2 positions[3] = vec2[](
vec2(0.0, -0.5),
vec2(0.5, 0.5),
vec2(-0.5, 0.5)
);
vec3 colors[3] = vec3[](
vec3(1.0, 0.0, 0.0),
vec3(0.0, 1.0, 0.0),
vec3(0.0, 0.0, 1.0)
);
void main() {
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
fragColor = colors[gl_VertexIndex];
}
#version 450
layout (location = 0) in vec3 fragColor;
layout (location = 0) out vec4 outColor;
void main() {
outColor = vec4(fragColor, 1.0);
}
glslangValidator -V triangle.vert -o triangle.vert.spv
glslangValidator -V triangle.frag -o triangle.frag.spv
Pipeline VkGraphicsPipelineCreateInfo .sType 🟪 VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO
.pNext 🪐 nullptr
.flags 🏳️ ChapterZZZ
.stageCount 🏷️ uint32_t
.pStages 🔮 Shaders 🏷️ VkPipelineShaderStageCreateInfo
🔀 Pipeline States / Stages
.pVertexInputState 🤭.pInputAssemblyState.pTessellationState.pViewportState.pRasterizationState.pMultisampleState.pDepthStencilState.pColorBlendState.pDynamicState.layout 🔗 SubChapter 2
.renderPass 🥪
.subpass 🟪 0
.basePipelineHandle 🟪VK_NULL_HANDLE
.basePipelineIndex 🟪 INT32_MIN
amVK wrap 🌯 amVK_PipelineGRAPHICS* PLG = new amVK_PipelineGRAPHICS(RP_FBs);
PLG->CreateGraphicsPipeline();
Pipeline Objects 🔗 amVK_Vertex.hh
🔗 amVK_GeoMetry.hh
🔗 amVK_PipelineGRAPHICS.cpp
VkPipelineLayout ℹ️ VkPipelineLayoutCreateInfo
.sType 🟪 VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO
.pNext 🪐 nullptr
.flags 🏳️ 0
.pSetLayouts 🟪 nullptr 🏷️ VkDescriptorSetLayout 🟨 ChapterZZZ
.pPushConstantRanges 🟪 nullptr 🏷️ VkPushConstantRange 🟨 ChapterZZZ
📦 vkCreatePipelineLayout()
RenderLoop 🖥️🎨 Vulkan wrap 🌯 while(true) {
vkAcquireNextImageKHR();
vkResetCommandBuffer(); // req:- VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT
vkBeginCommandBuffer();
vkCmdBeginRenderPass();
vkCmdSetViewport();
vkCmdSetScissor();
vkCmdBindPipeline();
vkCmdDraw();
vkCmdEndRenderPass();
vkEndCommandBuffer();
vkQueueSubmit();
vkQueuePresentKHR();
vkQueueWaitIdle();
PROCESS_InputEvents();
REY_NoobTimer::wait(10); // wait 10ms
}
amVK wrap 🌯 while(true) {
W->dispatch_events_with_OSModalLoops();
RP_FBs->RPBI_AcquireNextFrameBuffer();
CB->BeginCommandBuffer(amVK_Sync::CommandBufferUsageFlags::Submit_Once);
// ------------------------- CommandBufferRecording ----------------------------
RP_FBs->CMDBeginRenderPass(CB->vk_CommandBuffer);
RP_FBs->CMDSetViewport_n_Scissor(CB->vk_CommandBuffer);
PLG->CMDBindPipeline(CB->vk_CommandBuffer);
VB.CMDDraw(CB->vk_CommandBuffer);
RP_FBs->CMDEndRenderPass(CB->vk_CommandBuffer);
// ------------------------- CommandBufferRecording ----------------------------
CB->EndCommandBuffer();
// This was Done Before in CH4 : SwapChain
amVK_SurfacePresenter *PR = new amVK_SurfacePresenter();
PR->bind_Surface(S);
PR->bind_Device(D);
// This was Done Before in CH4 : SwapChain
PR->set_CommandBuffer(CB->vk_CommandBuffer);
PR->submit_CMDBUF(D->Queues.GraphicsQ(0));
PR->Present(D->Queues.GraphicsQ(0));
vkQueueWaitIdle(D->Queues.GraphicsQ(0));
REY_NoobTimer::wait(10); // wait 10ms
}
vkAcquireNextImageKHR() https://vkdoc.net/man/vkAcquireNextImageKHR
.device 🟪 Same as SwapChain 💁♀️.swapchain 🟪💁♀️.timeout ⏱️⚡ nanoseconds
nanoseconds, if no image is available.uint64_t ns_per_second = 1'000'000'000;
.semaphore 🔗 SubChapter 2.fench 🟨 ChapterZZZ.pImageIndex ↩️📦
VkImage but an index to it 💁♀️VK_SUBOPTIMAL_KHR
GPU-DriverImplementation/PresentationEngine is still able to scale the presented images to the new size to produce valid surface updates.VK_ERROR_OUT_OF_DATE_KHR
AcquireNextImage() from SwapChain1 SwapChainIMG? 💁♀️ 1 IMG Modes
VK_PRESENT_MODE_IMMEDIATE_KHR
vkAcquireNextImageKHR():- Returns VK_SUCCESS immediately (no synchronization).VK_PRESENT_MODE_FIFO_RELAXED_KHR
Multi IMG Modes
VK_PRESENT_MODE_FIFO_KHR a.k.a VSync :- Needs ≥2 images (front + back buffer).VK_PRESENT_MODE_MAILBOX_KHR (Triple buffering) :- Needs ≥3 images.
GPU Driver:-
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(...);
// caps.minImageCount is often 2+ (driver may ignore your request when creating SwapChain)
vkQueueWaitIdle() VkCommandPoolCreateInfo.flags 🏳️ VkCommandPoolCreateFlagBits
TRANSIENTRESET_COMMAND_BUFFER:- Lets you call vkBeginCommandBuffer() on same CMDBUF more than oncePROTECTED0:- Can't call vkBeginCommandBuffer() more than once on the same CMDBUFVkCommandBufferBeginInfo
https://vkdoc.net/man/VkCommandBufferBeginInfo
.sType 🟪 VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO.pNext 🟪 nullptr
VkDeviceGroupCommandBufferBeginInfo.flags 🔠 VkCommandBufferUsageFlagBits
SIMULTANEOUS_USERENDER_PASS_CONTINUE [secondary command buffer]ONE_TIME_SUBMIT:- usually coupled with 🏳️ RESET_COMMAND_BUFFER0:- Now you can submit this command buffer multiple times.pInheritanceInfo 🪐 [secondary command buffer]CMDBUFRecording a VkCommandBuffer only Once
Recording a VkCommandBuffer more than Once
CMDBUF to be reset by vkResetCommandBuffer() before recording again
vkBeginCommandBuffer() also does do an implicit resetVkCommandPoolCreateInfo.flags 🟪 VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BITSubmitting a VkCommandBuffer only Once
VkCommandBufferBeginInfo.flags 🟪 VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BITSUBMIT_ONCE with a RESET_CMDBUFImplementing Synchronization features is so fked up. After Present Image, call vkQueueWaitIdle()
VkRenderPassBeginInfo
https://vkdoc.net/man/VkRenderPassBeginInfo
.sType 🟪 VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO.pNext 🪐 nullptr.renderPass 🟪💁♀️.framebuffer 🟪💁♀️.renderArea ➡️ https://vkdoc.net/man/VkRect2D.pClearValues ➡️ https://vkdoc.net/man/VkClearValueRecordCommandBuffer()BeginRenderPass()DepthPass, NormalPass, ShadowPass, AlbedoPass, other necessary subpassesvkBeginCommandBuffer()
.commandBuffer 🟪 💁♀️.pBeginInfo 🟪 💁♀️</> TheCodeamVK_CommandPool {
public:
REY_Array<VkCommandBuffer> vk_CommandBuffers;
REY_Array<VkCommandBuffer> AllocateCommandBuffers(void);
public:
VkCommandBufferBeginInfo BI = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.pNext = 0,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
// People usually uses `SUBMIT_ONCE` with a `RESET_CMDBUF` & records CMDBUF every frame
.pInheritanceInfo = nullptr
};
void BeginCommandBuffer(uint32_t CMDBUF_Index) {
VkResult return_code = vkBeginCommandBuffer(vk_CommandBuffers[CMDBUF_Index], &BI);
amVK_return_code_log( "vkBeginCommandBuffer()" );
}
}
vkCmdBeginRenderPass()
.commandBuffer 🟪 💁♀️.pRenderPassBegin 🟪 💁♀️.contents 🔠 VK_SUBPASS_CONTENTS_INLINE
INLINESECONDARY_COMMAND_BUFFERS [secondary command buffer]INLINE_AND_SECONDARY_COMMAND_BUFFERS_KHR [VK_KHR_maintenance7]INLINE_AND_SECONDARY_COMMAND_BUFFERS_EXT [VK_EXT_nested_command_buffer]vkCmdSetViewport()
https://vkdoc.net/man/vkCmdSetViewport
.commandBuffer 🟪 💁♀️.firstViewport 🟪 0.viewportCount 🟪 1.pViewports 🟪 VkViewport
vkCmdSetScissor()
https://vkdoc.net/man/vkCmdSetScissor
.pScissors 🟪 VkRect2D
vkBindPipeline()
https://vkdoc.net/man/vkBindPipeline
.commandBuffer 🟪 💁♀️Draw()VertexBuffers
vkCmdDraw()
vkCmdEndRenderPass()
.commandBuffer 🟪 💁♀️vkEndCommandBuffer()
.commandBuffer 🟪 💁♀️Submit Command Buffer & Present VkSubmitInfo
.sType 🟪 VK_STRUCTURE_TYPE_SUBMIT_INFO.pNext 🪐 NULL.pWaitSemaphores 🔗 Chapter9.1
amVK_SwapChain::AcquireNextImage_SemaPhore.pWaitDstStageMask 🟪 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT.pCommandBuffers 🟪 💁♀️.pSignalSemaphores
amVK_SurfacePresenter::RenderingFinished_SemaPhorevkQueueSubmit()
.queue 🟪 GraphicsQueue.submitCount 🟪 1.pSubmits 🟪 💁♀️.fench 🟪 VK_NULL_HANDLEvkGetDeviceQueue()
.device.queueFamilyindex 🔗 Chapter2.7
amVK_Device::amVK_1D_QCIs::select_QFAM_Graphics().queueIndex 🔗 Chapter2.4
VkDeviceQueueCreateInfo.queueCount.pQueue ↩️📦
VkPresentInfoKHR
.sType 🟪 VK_STRUCTURE_TYPE_PRESENT_INFO_KHR.pNext 🪐 NULL
.pWaitSemaphores 🔗 Chapter9.6
amVK_SwapChain::RenderingFinished_SemaPhore.pSwapchains 🟪 💁♀️.pImageIndices.pResultsvkQueuePresentKHR()
.queue 🟪 💁♀️.pPresentInfo 🟪 💁♀️📽️ So far, The result 🔗 GITHUBamVK wrap 🌯 while(true) {
W->dispatch_events_with_OSModalLoops(); // No way to disable ModalLoop so far in win32
RP_FBs->RPBI_AcquireNextFrameBuffer();
CB->BeginCommandBuffer(amVK_Sync::CommandBufferUsageFlags::Submit_Once);
// ------------------------- CommandBufferRecording ----------------------------
RP_FBs->CMDBeginRenderPass(CB->vk_CommandBuffer);
RP_FBs->CMDSetViewport_n_Scissor(CB->vk_CommandBuffer);
PLG->CMDBindPipeline(CB->vk_CommandBuffer);
VB.CMDDraw(CB->vk_CommandBuffer);
RP_FBs->CMDEndRenderPass(CB->vk_CommandBuffer);
// ------------------------- CommandBufferRecording ----------------------------
CB->EndCommandBuffer();
// This was Done Before in CH4 : SwapChain
amVK_SurfacePresenter *PR = new amVK_SurfacePresenter();
PR->bind_Surface(S);
PR->bind_Device(D);
// This was Done Before in CH4 : SwapChain
PR->set_CommandBuffer(CB->vk_CommandBuffer);
PR->submit_CMDBUF(D->Queues.GraphicsQ(0));
PR->Present(D->Queues.GraphicsQ(0));
vkQueueWaitIdle(D->Queues.GraphicsQ(0));
REY_NoobTimer::wait(10); // wait 10ms
}
OS::ProcessEvents()amGHOST exists 💁♀️VkSemaphore 🟨 ChapterZZZ SemaPhore will be used to synchronize the rendering and presentation of imagesVkSemaphoreCreateInfo
.sType 🟪 VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO.pNext 🪐 NULL.flags 🏳️ 0vkCreateSemaphore
.device.pCreateInfo 🟪 💁♀️.pAllocator 🟨 ChapterZZZ.pSemaphore ↩️📦we are gonna take an backwards unfolding approach here
amGHOST StackTrace [Surface ---> Deep]amGHOST:- lets your headache dissappear about OS::Stuffs/Functions
1. amGHOST_Window ::dispatch_events_with_OSModalLoops() 🔗[github][1]
2. amGHOST_WindowWIN32::dispatch_events_with_OSModalLoops() 🔗[github][2]
3. amGHOST_System ::dispatch_events_with_OSModalLoops() 🔗[github][3]
4. amGHOST_SystemWIN32::dispatch_events_with_OSModalLoops() 🔗[github][4]
5. amGHOST_SystemWIN32.cpp::actual_implementaion 🔗[github][5]
win32 / xlib / x11 / wayland / macOS sections below1. ::PeekMessage() or ::GetMessage()
2. ::TranslateMessage()
3. ::DispatchMessage() ----> WndProc()
4. static WndProc() -------> ::DefWindowProcessor()
5. ::DefWindowProcessor() -> ModalLoop
6. ModalLoop
::PeekMessage()
::TranslateMessage()
::DispatchMessage() --> WndProc
WndProc
static WndProc()
::DefWindowProcessor()
WM_SYSCOMMANDwin32 🖥️ ::CreateWindowA()
WNDCLASSA during ::CreateWindowA() 🌱 \::Peek/Get/DispatchMessage()
WNDCLASSA.lpfnWndProc 🔗
static WndProc() ⚙️static WndProc()
staticMouseButtonDown 🖱️MouseButtonReleasedMouseButtonReleased 🖱️Threadwin32 ModalLoop Under The Hood:-dispatch_events() implementation.MouseButtonReleased is receivedwhile(true) {
::GetMessage() // Blocks Thread if no message is there till smth arrives
::TranslateMessage()
::DispatchMessage()
calls -> static WndProc() [userProvided]
if (msg == WM_EXITSIZEMOVE) {break;}
// Obviously there will be a heck ton other extra processing going on, but you get the idea
}
win32 Sample Implementation (odin32):-Exactly where does the ModalLoop gets Trigger? 🎬
WM_SYSCOMMAND to ::DefWindowProc()WM_LBUTTONDOWN vs WM_NCLBUTTONDOWN 🖱️
WM_NCLBUTTONDOWN (not WM_LBUTTONDOWN)What if we ignore WM_NCLBUTTONDOWN? ⛔
WM_SYSCOMMAND won’t generate!::DefWindowProc() on WM_NCLBUTTONDOWN ✅::DefWindowProc() is exactly how the OS internally keeps track of MouseButtonDown 💁♀️When does WM_ENTERSIZEMOVE occur? 🔄
::DefWindowProc() with WM_SYSCOMMANDModalLoop starts on passing WM_SYSCOMMAND or WM_ENTERSIZEMOVE? 🤔
WM_SYSCOMMAND [gotta test 🧪]If, WM_SYSCOMMAND starts the ModalLoop, why'd we even catch WM_ENTERSIZEMOVE in our static WndProc()?
win32 wanted us to catch all those events, but still wanted us to call ::DefWindowProc() on thosexlib X11 / XCB / Wayland [Linux] & [MacOS]summary Loop when MouseButton is PressedDownModalLoopModalLoop doesn't return/break till MouseButton is Released
WM_SYSCOMMANDWM_NCLBUTTONDOWN --> ::DefWindowProcessor()WM_SYSCOMMAND --> ::DefWindowProcessor()[REPEATED]:- WM_NCMOUSEMOVE --> WM_NCHITTEST --> WM_SETCURSOR
[REY_MODAL_LOOP]:- WM_NCLBUTTONDOWN --> Entering: DefWindowProc
[REY_MODAL_LOOP]:- WM_SYSCOMMAND --> Entering: DefWindowProc
[Win32GUI]:- WM_GETMINMAXINFO
[Win32GUI]:- WM_ENTERSIZEMOVE
[Win32GUI]:- WM_NCMOUSELEAVE
[Win32GUI]:- WM_CAPTURECHANGED
[Win32GUI]:- WM_WINDOWPOSCHANGING
[Win32GUI]:- WM_GETMINMAXINFO
[Win32GUI]:- WM_EXITSIZEMOVE
[REY_MODAL_LOOP]:- WM_SYSCOMMAND --> Returned: DefWindowProc
[REY_MODAL_LOOP]:- WM_NCLBUTTONDOWN --> Returned: DefWindowProc
[REPEATED]:- WM_NCMOUSEMOVE --> WM_NCHITTEST --> WM_SETCURSOR
modalLoopResizing & SwapChain Recreation Vulkan wrap 🌯 void reSize(void) {
RP_FBs->DestroyFrameBuffers();
SC_IMGs->DestroySwapChainImageViews();
SC->reCreateSwapChain(); // calls --> sync_SurfCaps();
SC_IMGs->GetSwapChainImagesKHR();
SC_IMGs->CreateSwapChainImageViews();
RP_FBs->CreateFrameBuffers();
}
amVK wrap 🌯 amGHOST_SwapChainResizer* SC_Resizer = new amGHOST_SwapChainResizer(RP_FBs, W);
amVK wrap 🌯 // ------------------------- Render Loop -------------------------
amTHREAD phoenix;
phoenix.run([&]() {
REY_LOG("Thread started.");
while(true) {
RP_FBs->RPBI_AcquireNextFrameBuffer();
// ------------------------- CommandBufferRecording -------------------------
// ------------------------- Submit & Present -------------------------
vkQueueWaitIdle(D->Queues.GraphicsQ(0));
REY_NoobTimer::wait(10); // wait 10ms
}
REY_LOG("Thread finished.");
});
while(true) {
W->dispatch_events_with_OSModalLoops(); // dispatch events
REY_NoobTimer::wait(1); // wait 100ms
}
// ------------------------- Render Loop -------------------------
Vertex 📍 & VertexBuffer 🗄️ Mesh/Vertices amVK_Vertex
struct amVK_Vertex {
float position[3];
float color[4];
};
Vertex Buffer
VkBufferCreateInfo
.sType 🟪 VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO.pNext 🪐 nullptr.flags 🪐 VkBufferCreateFlagBits
SPARSE 🟨 ChapterZZZ.size 🟪 sizeof(amVK_Vertex) * N.usage 🟪 VK_BUFFER_USAGE_VERTEX_BUFFER_BIT.sharingMode 🟧 ChapterZZZ
.queueFamilyIndexCount.pQueueFamilyIndexvkCreateBuffer()
.device 🟪💁♀️.pCreateInfo 🟪💁♀️.pAllocator.pBuffer ↩️📦📽️ So far, The result:- CH11.1.VertexBuffer.hh
A lesson in Memory https://www.youtube.com/watch?v=uXgKXfVMeFw
(obviously i am not talking about Vulkan / Implementation Programming)
(i am talking about Algorithms/CP/CodeForces/MIT6.046)
vkGetBufferMemoryRequirements()
.device 🟪💁♀️.buffer 🟪💁♀️.pMemoryRequirements ↩️📦VkMemoryRequirements
.size ➡️ VkMemoryAllocateInfo.allocationSize.alignment.memoryTypeBits.memoryTypeIndex | VkPhysicalDeviceMemoryProperties
https://vkdoc.net/man/VkPhysicalDeviceMemoryProperties
VkMemoryType memoryTypes[VK_MAX_MEMORY_TYPES];VkMemoryHeap memoryHeaps[VK_MAX_MEMORY_HEAPS];VkMemoryType
.propertyFlags 🏷️ VkMemoryPropertyFlags
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BITVK_MEMORY_PROPERTY_HOST_VISIBLE_BITVK_MEMORY_PROPERTY_HOST_COHERENT_BITVK_MEMORY_PROPERTY_HOST_CACHED_BITVK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT.heapIndex 🏷️ uint32_tVkmemoryHeap
.size 🏷️ VkDeviceSize.flags 🏷️ VkMemoryHeapFlags
VK_MEMORY_HEAP_DEVICE_LOCAL_BITVK_MEMORY_HEAP_MULTI_INSTANCE_BITVK_MEMORY_HEAP_TILE_MEMORY_BIT_QCOMVK_MEMORY_HEAP_MULTI_INSTANCE_BIT_KHRvkGetPhysicalDeviceMemoryProperties()
.physicalDevice 🟪💁♀️.pFeatures ↩️📦VkPhysicalDeviceFeatures
VkBool32
ShadersTexuresSparsevkGetPhysicalDeviceFeatures()
.physicalDevice 🟪💁♀️.pMemoryProperties ↩️📦📽️ So far, The result
class amVK_InstanceProps {
static void GetPhysicalDeviceFeatures(void); // amVK_1D_GPUs_Features
static void GetPhysicalDeviceMemoryProperties(void); // amVK_1D_GPUs_MEMProps
static inline REY_Array<VkPhysicalDeviceFeatures> amVK_1D_GPUs_Features;
static inline REY_Array<VkPhysicalDeviceMemoryProperties> amVK_1D_GPUs_MEMProps;
}
// The other one is copy of this one
void amVK_InstanceProps::GetPhysicalDeviceFeatures(void) {
amVK_1D_GPUs_Features.reserve(amVK_1D_GPUs.n);
amVK_LOOP_GPUs(k) {
vkGetPhysicalDeviceFeatures(amVK_1D_GPUs[k], &amVK_1D_GPUs_Features[k]);
}
called_GetPhysicalDeviceFeatures = true;
}
👀 Visualization / [See it] / JSON Printing:- 🔗 GITHUB amVK_InstancePropsExport_nlohmann.cpp#L1-L117
REY_CategorizeMemoryHeaps() 🔗 GITHUB amVK_GPUProps.cpp#L56-264
Refactoring is pretty smooth now, I did it again, in this commit 🥴 🔗 GITHUB
VkMemoryAllocateInfo
.sType 🟪 VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO.pNext 🪐 nullptr
.allocationSize 🟪 VkMemoryRequirements.size.memoryTypeIndex 🏷️ uint32_tvkAllocateMemory()
.device.pAllocateInfo.pAllocator.pMemory</> TheCode
void amVK_VertexBuffer::AllocateMemory(void) {
if(called_GetBufferMemoryRequirements == false) {
this->GetBufferMemoryRequirements();
}
if (this->D->GPU_Props->called_REY_CategorizeMemoryHeaps == false) {
this->D->GPU_Props-> REY_CategorizeMemoryHeaps();
}
AI.allocationSize = vk_MemoryReq.size;
AI.memoryTypeIndex = this->D->GPU_Props->MEMTypeID.CPU_GPU_Synced;
VkResult return_code = vkAllocateMemory(this->D->vk_Device, &AI, nullptr, &this->vk_DeviceMemory);
amVK_return_code_log( "vkAllocateMemory()" );
}
vkMapMemory()
vkUnmapMemory()
vkBindBufferMemory()
</> TheCode
void amVK_VertexBuffer::MapMemory(void) {
VkResult return_code = vkMapMemory(D->vk_Device, vk_DeviceMemory, 0, vk_MemoryReq.size, 0, &vk_MappedMemoryData);
amVK_return_code_log( "vkMapMemory()" );
}
void amVK_VertexBuffer::CopyIntoMemory(void) {
REY_memcpy(vk_MappedMemoryData, Vertices.data, CI.size);
}
void amVK_VertexBuffer::UnMapMemory(void) {
vkUnmapMemory(D->vk_Device, vk_DeviceMemory);
}
void amVK_VertexBuffer::BindBufferMemory(void) {
VkResult return_code = vkBindBufferMemory(D->vk_Device, vk_Buffer, vk_DeviceMemory, 0);
amVK_return_code_log( "vkBindBufferMemory()" );
}
// WIP //
amVK_SurfacePresenter Can't have everything scatterred now, everything is getting too much sophisticating.... 🤔 🤦♀️ must Refactor....
Major Decision Change
Right now, amVK_Surface::CTOR creates amVK_SurfacePresenter. & SwapChain, RenderPass, CommandPool are supposed to be created from amVK_SurfacePresenter.
class amVK_Surface
amVK_SurfacePresenter {
create_SwapChain_interface()
new amVK_SwapChain(this)
this->CI.surface = PR->S->vk_SurfaceKHR;
// later amVK_SwapChain::CreateSwapChain(void) uses this->PR->D->vk_Device
create_RenderPass_interface()
new amVK_RenderPass(this)
this->PR = PR;
create_CommandPool_interface()
new amVK_CommandPool(this)
this->CI.queueFamilyIndex = this->PR->D->amVK_1D_QCIs.ptr_Default()->queueFamilyIndex;
create_FrameBuffers()
new amVK_FrameBuffer(this)
this->CI.renderPass = this->PR->RP->vk_RenderPass;
Problem #1:- I think this is just a little too much deep to handle....
Problem #2:- if amVK_SwapChain.hh included amVK_SurfacePresenter.hh, then the reverse can't happen. 💁♀️
Thus a lot of 1-liner functions would have to be put inside .cpp even tho i don't want it to.
Problem #2:- in DetailsC1:- Don't include amVK_SurfacePresenter.hh in amVK_SwapChain.hh but rather inside amVK_SwapChain.cppC2:- Don't include amVK_SwapChain.hh in amVK_SurfacePresenter.hh but rather inside amVK_SurfacePresenter.cppCase 1:-
amVK_SwapChain::CONSTRUCTORsync_SurfCaps()amVK_SwapChain.cppCase 2:-
amVK_SurfacePresenter::sync_SC_SurfCaps()amVK_SurfacePresenter::synced_ImageExtent()amVK_SurfacePresenter.cppamVK_SurfacePresenter anyway 💁♀️amVK_SurfacePresenter Optional
amVK_SurfacePresenter Code part
Before Commit:- https://github.com/REYNEP/amGHOST/blob/9cec3e58db123144bd8d88363ccf9a4a7ffc9edc/amVK/amVK_Surface.hh
Middle (Discarded) Commit:- https://github.com/REYNEP/amGHOST/blob/3be7cfcd154b383cd98783d302468f63fda0618b/amVK/amVK_SurfacePresenter.hh
Final Commit:- https://github.com/REYNEP/amGHOST/blob/7376cdb5c2c6eee19655dae436e6cf8edd02e1d5/amVK/amVK_SurfacePresenter.hh
📽️ So far, The result [🔗 GITHUB]
|
|
Windows WndProc 
| Feature | WM_PAINT | WM_PRINT |
|---|---|---|
| Purpose | Sent by the system to request that a window redraw its client area. | Sent by an application to request that a window draw itself into a specified device context (e.g., for printing or capturing). |
| Trigger | Automatically triggered by the system when the client area becomes invalid (e.g., resizing, minimizing). | Explicitly sent by an application using SendMessage to request the window to draw itself. |
| Message ID | 0x800F | 0x0317 |
| Who Sends It | Sent by the system. | Sent by the application (e.g., using SendMessage(hwnd, WM_PRINT, ...)). |
| Default Behavior | Calls the window's WndProc to handle the redraw. | Calls the window's WndProc to handle the drawing into the specified device context. |
| Device Context | Uses the device context provided by BeginPaint and EndPaint. | Uses the device context passed in wParam. |
| Use Case | Used for normal window redrawing (e.g., after invalidation or resizing). | Used for off-screen rendering, printing, or capturing the window's content. |
| System-Generated | Yes, automatically generated when the client area is invalid. | No, must be explicitly sent by the application. |
| Parameters | - wParam: Not used. - lParam: Not used. |
- wParam: Handle to the device context (HDC). - lParam: Flags specifying what to draw. |
| Flags in lParam | Not applicable. | Flags include: - PRF_CHECKVISIBLE: Only draw if the window is visible. - PRF_CHILDREN: Draw child windows. - PRF_CLIENT: Draw the client area. - PRF_NONCLIENT: Draw the non-client area. - PRF_ERASEBKGND: Erase the background. |
| Child Windows | Does not automatically draw child windows. | Can optionally draw child windows if the PRF_CHILDREN flag is set. |
| Non-Client Area | Does not draw the non-client area (e.g., title bar, borders). | Can optionally draw the non-client area if the PRF_NONCLIENT flag is set. |
| Example Usage | Used in the WndProc to handle normal window painting. | Used for capturing the window's content into a bitmap or for printing. |


