### Vulkan RAII Application Example Source: https://docs.vulkan.org/tutorial/latest/_attachments/04_logical_device.cpp A complete C++ example demonstrating a Vulkan application using the RAII wrapper. It includes instance creation, debug callback setup, and the main application run loop. Ensure Vulkan headers and RAII wrappers are correctly included. ```cpp #include extensions.push_back(vk::EXTDebugUtilsExtensionName); } return extensions; } static VKAPI_ATTR vk::Bool32 VKAPI_CALL debugCallback(vk::DebugUtilsMessageSeverityFlagBitsEXT severity, vk::DebugUtilsMessageTypeFlagsEXT type, const vk::DebugUtilsMessengerCallbackDataEXT *pCallbackData, void *) { if (severity == vk::DebugUtilsMessageSeverityFlagBitsEXT::eError || severity == vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning) { std::cerr << "validation layer: type " << to_string(type) << " msg: " << pCallbackData->pMessage << std::endl; } return vk::False; } }; int main() { try { HelloTriangleApplication app; app.run(); } catch (const std::exception &e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } ``` -------------------------------- ### Install Linux Dependencies Source: https://docs.vulkan.org/samples/latest/docs/build.html Installs necessary dependencies for building Vulkan samples on Linux using apt-get. ```bash sudo apt-get install cmake g++ xorg-dev glu1-mesa-dev libwayland-dev libxkbcommon-dev ``` -------------------------------- ### Vulkan RAII Initialization Example Source: https://docs.vulkan.org/tutorial/latest/_attachments/29_mipmapping.cpp Demonstrates the core initialization steps for a Vulkan application using RAII wrappers. This includes setting up the window, Vulkan instance, physical device, logical device, and swap chain. ```cpp #include #define GLFW_INCLUDE_VULKAN #include #include #include #include #define STB_IMAGE_IMPLEMENTATION #include #define TINYOBJLOADER_IMPLEMENTATION #include constexpr uint32_t WIDTH = 800; constexpr uint32_t HEIGHT = 600; const std::string MODEL_PATH = "models/viking_room.obj"; const std::string TEXTURE_PATH = "textures/viking_room.png"; constexpr int MAX_FRAMES_IN_FLIGHT = 2; const std::vector validationLayers = { "VK_LAYER_KHRONOS_validation"}; #ifdef NDEBUG constexpr bool enableValidationLayers = false; #else constexpr bool enableValidationLayers = true; #endif struct Vertex { glm::vec3 pos; glm::vec3 color; glm::vec2 texCoord; static vk::VertexInputBindingDescription getBindingDescription() { return {0, sizeof(Vertex), vk::VertexInputRate::eVertex}; } static std::array getAttributeDescriptions() { return { vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, pos)), vk::VertexInputAttributeDescription(1, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, color)), vk::VertexInputAttributeDescription(2, 0, vk::Format::eR32G32Sfloat, offsetof(Vertex, texCoord)) }; } bool operator==(const Vertex &other) const { return pos == other.pos && color == other.color && texCoord == other.texCoord; } }; template <> struct std::hash { size_t operator()(Vertex const &vertex) const noexcept { return ((hash()(vertex.pos) ^ (hash()(vertex.color) << 1)) >> 1) ^ (hash()(vertex.texCoord) << 1); } }; struct UniformBufferObject { alignas(16) glm::mat4 model; alignas(16) glm::mat4 view; alignas(16) glm::mat4 proj; }; class HelloTriangleApplication { public: void run() { initWindow(); initVulkan(); mainLoop(); cleanup(); } private: GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; vk::raii::DebugUtilsMessengerEXT debugMessenger = nullptr; vk::raii::SurfaceKHR surface = nullptr; vk::raii::PhysicalDevice physicalDevice = nullptr; vk::raii::Device device = nullptr; uint32_t queueIndex = ~0; vk::raii::Queue queue = nullptr; vk::raii::SwapchainKHR swapChain = nullptr; std::vector swapChainImages; vk::SurfaceFormatKHR swapChainSurfaceFormat; vk::Extent2D swapChainExtent; std::vector swapChainImageViews; vk::raii::DescriptorSetLayout descriptorSetLayout = nullptr; vk::raii::PipelineLayout pipelineLayout = nullptr; vk::raii::Pipeline graphicsPipeline = nullptr; vk::raii::Image depthImage = nullptr; vk::raii::DeviceMemory depthImageMemory = nullptr; vk::raii::ImageView depthImageView = nullptr; uint32_t mipLevels = 0; vk::raii::Image textureImage = nullptr; vk::raii::DeviceMemory textureImageMemory = nullptr; vk::raii::ImageView textureImageView = nullptr; vk::raii::Sampler textureSampler = nullptr; std::vector vertices; std::vector indices; vk::raii::Buffer vertexBuffer = nullptr; vk::raii::DeviceMemory vertexBufferMemory = nullptr; vk::raii::Buffer indexBuffer = nullptr; vk::raii::DeviceMemory indexBufferMemory = nullptr; std::vector uniformBuffers; std::vector uniformBuffersMemory; std::vector uniformBuffersMapped; vk::raii::DescriptorPool descriptorPool = nullptr; std::vector descriptorSets; vk::raii::CommandPool commandPool = nullptr; std::vector commandBuffers; std::vector presentCompleteSemaphores; std::vector renderFinishedSemaphores; std::vector inFlightFences; uint32_t frameIndex = 0; bool framebufferResized = false; std::vector requiredDeviceExtension = { vk::KHRSwapchainExtensionName}; void initWindow() { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); glfwSetWindowUserPointer(window, this); glfwSetFramebufferSizeCallback(window, framebufferResizeCallback); } void initVulkan() { // RAII wrappers for Vulkan objects are automatically managed. // Initialization logic for instance, device, swap chain, etc. goes here. } void mainLoop() { // Main rendering loop } void cleanup() { // RAII wrappers handle cleanup automatically. glfwDestroyWindow(window); glfwTerminate(); } static void framebufferResizeCallback(GLFWwindow* window, int width, int height) { auto app = reinterpret_cast(glfwGetWindowUserPointer(window)); app->framebufferResized = true; } }; ``` -------------------------------- ### Sub-Array Extraction Example Source: https://docs.vulkan.org/features/latest/features/proposals/VK_QCOM_cooperative_matrix_conversion.html Example demonstrating the usage of extractSubArrayQCOM for slicing arrays, including handling different start indices. ```glsl float32_t uvecAcc[32]; float32_t uvecA[8]; extractSubArrayQCOM(uvecAcc, 3, uvecA); ... extractSubArrayQCOM(uvecAcc, 24, uvecA); ``` -------------------------------- ### Run Swapchain Images Sample Source: https://docs.vulkan.org/samples/latest/README.html Execute the 'swapchain_images' sample. This demonstrates basic swapchain functionality. ```bash vulkan_samples sample swapchain_images ``` -------------------------------- ### Get Vulkan SDK path Source: https://docs.vulkan.org/tutorial/latest/02_Development_environment.html Prints the current value of the VULKAN_SDK environment variable to find the exact installation path. ```shell echo $VULKAN_SDK ``` -------------------------------- ### Begin Rendering with Dynamic Rendering Dependencies Source: https://docs.vulkan.org/features/latest/features/proposals/VK_KHR_dynamic_rendering_local_read.html This code illustrates the setup for dynamic rendering, showing how to use pipeline barriers to manage dependencies between draw calls. ```c // Write the setup code vkCmdBeginRendering(...); vkCmdDraw(...); vkCmdPipelineBarrier(...); vkCmdDraw(...); vkCmdEndRendering(...); ``` -------------------------------- ### Vulkan Initialization Steps Source: https://docs.vulkan.org/tutorial/latest/_attachments/26_texture_mapping.cpp Outlines the sequence of Vulkan initialization functions called within the `run` method, including window creation, Vulkan setup, and resource initialization. ```cpp void initWindow() { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); glfwSetWindowUserPointer(window, this); glfwSetFramebufferSizeCallback(window, framebufferResizeCallback); } static void framebufferResizeCallback(GLFWwindow *window, int width, int height) { auto app = reinterpret_cast(glfwGetWindowUserPointer(window)); app->framebufferResized = true; } void initVulkan() { createInstance(); setupDebugMessenger(); createSurface(); pickPhysicalDevice(); createLogicalDevice(); createSwapChain(); createImageViews(); createDescriptorSetLayout(); createGraphicsPipeline(); createCommandPool(); createTextureImage(); createTextureImageView(); createTextureSampler(); createVertexBuffer(); createIndexBuffer(); } ``` -------------------------------- ### Submit Command Buffer with Keyed Mutex Source: https://docs.vulkan.org/refpages/latest/refpages/source/VK_NV_win32_keyed_mutex.html Submits a command buffer to a queue. This example assumes the necessary setup for keyed mutex synchronization has been performed. ```c++ submit_info.pCommandBuffers = &cmd_buf; vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE); ``` -------------------------------- ### Initialize Performance API, Create Query Pool, Record Queries, and Get Results Source: https://docs.vulkan.org/spec/latest/appendices/extensions.html This comprehensive example demonstrates the workflow for using the INTEL Performance Query extension. It covers initializing the API, setting up a query pool for performance queries, recording commands to measure performance, acquiring and applying a performance configuration, and finally retrieving the query results. Ensure necessary setup like device and queue creation, and command buffer submission are handled prior to and after this snippet. ```c // A previously created device VkDevice device; // A queue derived from the device VkQueue queue; VkInitializePerformanceApiInfoINTEL performanceApiInfoIntel = { VK_STRUCTURE_TYPE_INITIALIZE_PERFORMANCE_API_INFO_INTEL, NULL, NULL }; vkInitializePerformanceApiINTEL( device, &performanceApiInfoIntel); VkQueryPoolPerformanceQueryCreateInfoINTEL queryPoolIntel = { VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO_INTEL, NULL, VK_QUERY_POOL_SAMPLING_MODE_MANUAL_INTEL, }; VkQueryPoolCreateInfo queryPoolCreateInfo = { VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO, &queryPoolIntel, 0, VK_QUERY_TYPE_PERFORMANCE_QUERY_INTEL, 1, 0 }; VkQueryPool queryPool; VkResult result = vkCreateQueryPool( device, &queryPoolCreateInfo, NULL, &queryPool); assert(VK_SUCCESS == result); // A command buffer we want to record counters on VkCommandBuffer commandBuffer; VkCommandBufferBeginInfo commandBufferBeginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, NULL }; result = vkBeginCommandBuffer(commandBuffer, &commandBufferBeginInfo); assert(VK_SUCCESS == result); vkCmdResetQueryPool( commandBuffer, queryPool, 0, 1); vkCmdBeginQuery( commandBuffer, queryPool, 0, 0); // Perform the commands you want to get performance information on // ... // Perform a barrier to ensure all previous commands were complete before // ending the query vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0, NULL, 0, NULL); vkCmdEndQuery( commandBuffer, queryPool, 0); result = vkEndCommandBuffer(commandBuffer); assert(VK_SUCCESS == result); VkPerformanceConfigurationAcquireInfoINTEL performanceConfigurationAcquireInfo = { VK_STRUCTURE_TYPE_PERFORMANCE_CONFIGURATION_ACQUIRE_INFO_INTEL, NULL, VK_PERFORMANCE_CONFIGURATION_TYPE_COMMAND_QUEUE_METRICS_DISCOVERY_ACTIVATED_INTEL }; VkPerformanceConfigurationINTEL performanceConfigurationIntel; result = vkAcquirePerformanceConfigurationINTEL( device, &performanceConfigurationAcquireInfo, &performanceConfigurationIntel); vkQueueSetPerformanceConfigurationINTEL(queue, performanceConfigurationIntel); assert(VK_SUCCESS == result); // Submit the command buffer and wait for its completion // ... result = vkReleasePerformanceConfigurationINTEL( device, performanceConfigurationIntel); assert(VK_SUCCESS == result); // Get the report size from metrics-discovery's QueryReportSize result = vkGetQueryPoolResults( device, queryPool, 0, 1, QueryReportSize, data, QueryReportSize, 0); assert(VK_SUCCESS == result); // The data can then be passed back to metrics-discovery from which // human readable values can be queried. ``` -------------------------------- ### Vulkan Instance Creation with Exception Handling Source: https://docs.vulkan.org/tutorial/latest/03_Drawing_a_triangle/00_Setup/01_Instance.html Demonstrates creating a Vulkan context, instance, physical device, and device using RAII wrappers and catching potential Vulkan or standard exceptions. ```cpp try { vk::raii::Context context; vk::raii::Instance instance(context, vk::InstanceCreateInfo{}); vk::raii::PhysicalDevice physicalDevice = instance.enumeratePhysicalDevices().front(); vk::raii::Device device(physicalDevice, vk::DeviceCreateInfo{}); // Use Vulkan objects vk::raii::Buffer buffer(device, vk::BufferCreateInfo{}); } catch (const vk::SystemError& err) { std::cerr << "Vulkan error: " << err.what() << std::endl; return 1; } catch (const std::exception& err) { std::cerr << "Error: " << err.what() << std::endl; return 1; } ``` -------------------------------- ### Example: Enumerate Available Performance Counters Source: https://docs.vulkan.org/features/latest/features/proposals/VK_ARM_performance_counters_by_region.html Demonstrates the two-step process for enumerating performance counters: first to get the count, then to retrieve the actual counter data. ```c /* Retrieve the number of counters */ vkEnumeratePhysicalDeviceQueueFamilyPerformanceCountersByRegionARM(physicalDevice, queueFamilyIndex, &counterCount, NULL, NULL); /* Allocate memory and retrieve the counter IDs and descriptions); counters = malloc(counterCount * sizeof(VkPerformanceCounterARM)); descriptions = malloc(counterCount * sizeof(VkPerformanceCounterDescriptionARM)); vkEnumeratePhysicalDeviceQueueFamilyPerformanceCountersByRegionARM(physicalDevice, queueFamilyIndex, &counterCount, counters, descriptions); ``` -------------------------------- ### Setting up and Using Dynamic Rendering Source: https://docs.vulkan.org/features/latest/features/proposals/VK_KHR_dynamic_rendering.html Demonstrates how to configure and use dynamic rendering by defining attachment information and issuing rendering commands. This approach bypasses the need for explicit VkRenderPass objects. ```c VkRenderingAttachmentInfoKHR colorAttachments[2] = { { .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR .pNext = NULL, .imageView = colorImageViews[0], .imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR, .resolveMode = VK_RESOLVE_MODE_AVERAGE_BIT, .resolveImageView = resolveColorImageView, .resolveImageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR, .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, .clearValue = {.color = {.float32 = {0.0f,0.0f,0.0f,0.0f} } } }, { .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR .pNext = NULL, .imageView = colorImageViews[1], .imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR, .resolveMode = VK_RESOLVE_MODE_NONE, .loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, .storeOp = VK_ATTACHMENT_STORE_OP_STORE } }; // A single depth stencil attachment info can be used, but they can also be specified separately. // When both are specified separately, the only requirement is that the image view is identical. VkRenderingAttachmentInfoKHR depthStencilAttachment = { .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR .pNext = NULL, .imageView = depthStencilStencilImageView, .imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR, .resolveMode = VK_RESOLVE_MODE_NONE, .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, .clearValue = {.depthStencil = {.depth = 0.0f, .stencil = 0 } } }; VkRenderingInfoKHR renderingInfo = { .sType = VK_STRUCTURE_TYPE_RENDERING_INFO_KHR, .pNext = NULL, .flags = 0, .renderArea = { ... }, .layerCount = 1, .colorAttachmentCount = 2, .pColorAttachments = colorAttachments, .pDepthAttachment = &depthStencilAttachment, .pStencilAttachment = &depthStencilAttachment }; vkCmdBeginRenderingKHR(commandBuffer, &renderingInfo); vkCmdDraw(commandBuffer, ...); ... vkCmdDraw(commandBuffer, ...); vkCmdEndRenderingKHR(commandBuffer); ``` -------------------------------- ### Record Command Buffer for Dynamic Rendering Source: https://docs.vulkan.org/tutorial/latest/_attachments/38_ray_tracing.cpp Records commands into a command buffer, including image layout transitions and setting up attachments for dynamic rendering. This example shows the setup for color and depth attachments. ```cpp void recordCommandBuffer(uint32_t imageIndex) { auto &commandBuffer = commandBuffers[frameIndex]; commandBuffer.begin({}); // Before starting rendering, transition the swapchain image to COLOR_ATTACHMENT_OPTIMAL transition_image_layout( swapChainImages[imageIndex], vk::ImageLayout::eUndefined, vk::ImageLayout::eColorAttachmentOptimal, {}, // srcAccessMask (no need to wait for previous operations) vk::AccessFlagBits2::eColorAttachmentWrite, // dstAccessMask vk::PipelineStageFlagBits2::eColorAttachmentOutput, // srcStage vk::PipelineStageFlagBits2::eColorAttachmentOutput, // dstStage vk::ImageAspectFlagBits::eColor); // Transition depth image to depth attachment optimal layout transition_image_layout( *depthImage, vk::ImageLayout::eUndefined, vk::ImageLayout::eDepthAttachmentOptimal, vk::AccessFlagBits2::eDepthStencilAttachmentWrite, vk::AccessFlagBits2::eDepthStencilAttachmentWrite, vk::PipelineStageFlagBits2::eEarlyFragmentTests | vk::PipelineStageFlagBits2::eLateFragmentTests, vk::PipelineStageFlagBits2::eEarlyFragmentTests | vk::PipelineStageFlagBits2::eLateFragmentTests, vk::ImageAspectFlagBits::eDepth); vk::ClearValue clearColor = vk::ClearColorValue(0.0f, 0.0f, 0.0f, 1.0f); vk::ClearValue clearDepth = vk::ClearDepthStencilValue(1.0f, 0); /* TASK01: Check the setup for dynamic rendering * * With dynamic rendering, we specify the image view and load/store operations directly * in the vk::RenderingAttachmentInfo structure. * This approach eliminates the need for explicit render pass and framebuffer objects, * simplifying the code and providing flexibility to change attachments at runtime. */ vk::RenderingAttachmentInfo colorAttachmentInfo = { .imageView = swapChainImageViews[imageIndex], .imageLayout = vk::ImageLayout::eColorAttachmentOptimal, .loadOp = vk::AttachmentLoadOp::eClear, .storeOp = vk::AttachmentStoreOp::eStore, .clearValue = clearColor}; vk::RenderingAttachmentInfo depthAttachmentInfo = { .imageView = depthImageView, .imageLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal, .loadOp = vk::AttachmentLoadOp::eClear, .storeOp = vk::AttachmentStoreOp::eDontCare, .clearValue = clearDepth}; } ``` -------------------------------- ### Vulkan Ray Tracing Application Setup Source: https://docs.vulkan.org/tutorial/latest/_attachments/38_ray_tracing.cpp Initializes Vulkan resources, including window creation, device setup, swap chain, pipelines, and ray tracing specific structures. ```cpp #define LAB_TASK_LEVEL 1 vk::raii::Buffer instanceLUTBuffer = nullptr; vk::raii::DeviceMemory instanceLUTBufferMemory = nullptr; UniformBufferObject ubo{}; std::vector uniformBuffers; std::vector uniformBuffersMemory; std::vector uniformBuffersMapped; struct SubMesh { uint32_t indexOffset; uint32_t indexCount; int materialID; uint32_t firstVertex; uint32_t maxVertex; bool alphaCut; bool reflective; }; std::vector submeshes; std::vector materials; vk::raii::DescriptorPool descriptorPool = nullptr; std::vector globalDescriptorSets; std::vector materialDescriptorSets; vk::raii::CommandPool commandPool = nullptr; std::vector commandBuffers; uint32_t graphicsIndex = 0; std::vector presentCompleteSemaphores; std::vector renderFinishedSemaphores; std::vector inFlightFences; uint32_t frameIndex = 0; bool framebufferResized = false; std::vector requiredDeviceExtension = { vk::KHRSwapchainExtensionName, vk::KHRSpirv14ExtensionName, vk::KHRSynchronization2ExtensionName, vk::KHRCreateRenderpass2ExtensionName, vk::KHRAccelerationStructureExtensionName, vk::KHRBufferDeviceAddressExtensionName, vk::KHRDeferredHostOperationsExtensionName, vk::KHRRayQueryExtensionName}; void initWindow() { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); glfwSetWindowUserPointer(window, this); glfwSetFramebufferSizeCallback(window, framebufferResizeCallback); } static void framebufferResizeCallback(GLFWwindow *window, int width, int height) { auto app = static_cast(glfwGetWindowUserPointer(window)); app->framebufferResized = true; } void initVulkan() { createInstance(); setupDebugMessenger(); createSurface(); pickPhysicalDevice(); createLogicalDevice(); createSwapChain(); createImageViews(); createCommandPool(); loadModel(); createDescriptorSetLayout(); createGraphicsPipeline(); createDepthResources(); createTextureSampler(); createVertexBuffer(); createIndexBuffer(); createUVBuffer(); createAccelerationStructures(); createInstanceLUTBuffer(); createUniformBuffers(); createDescriptorPool(); createDescriptorSets(); createCommandBuffers(); createSyncObjects(); } void mainLoop() { while (!glfwWindowClose(window)) { glfwPollEvents(); drawFrame(); } device.waitIdle(); } void cleanupSwapChain() const { swapChainImageViews.clear(); swapChain = nullptr; } void cleanup() const { glfwDestroyWindow(window); glfwTerminate(); } void recreateSwapChain() { int width = 0, height = 0; glfwGetFramebufferSize(window, &width, &height); while (width == 0 || height == 0) { glfwGetFramebufferSize(window, &width, &height); glfwWaitEvents(); } device.waitIdle(); cleanupSwapChain(); createSwapChain(); createImageViews(); createDepthResources(); } ``` -------------------------------- ### Setting Up SDL2 Window with Vulkan Support Source: https://docs.vulkan.org/guide/latest/windowing_audio_input.html Initializes SDL, creates a window with Vulkan support, and retrieves necessary Vulkan extensions. This is the starting point for Vulkan applications using SDL2. ```cpp #include #include #include #include #include int main() { // Initialize SDL if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) != 0) { std::cerr << "SDL_Init Error: " << SDL_GetError() << std::endl; return -1; } // Create window with Vulkan support SDL_Window* window = SDL_CreateWindow( "Vulkan SDL2 Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_VULKAN | SDL_WINDOW_SHOWN ); if (!window) { std::cerr << "SDL_CreateWindow Error: " << SDL_GetError() << std::endl; SDL_Quit(); return -1; } // Get required Vulkan extensions for SDL unsigned int extensionCount; if (!SDL_Vulkan_GetInstanceExtensions(window, &extensionCount, nullptr)) { std::cerr << "Failed to get Vulkan extension count" << std::endl; return -1; } std::vector extensions(extensionCount); if (!SDL_Vulkan_GetInstanceExtensions(window, &extensionCount, extensions.data())) { std::cerr << "Failed to get Vulkan extensions" << std::endl; return -1; } // Create Vulkan instance (not shown) VkInstance instance = VK_NULL_HANDLE; // ... create instance with extensions ... // Create Vulkan surface VkSurfaceKHR surface; if (!SDL_Vulkan_CreateSurface(window, instance, &surface)) { std::cerr << "Failed to create Vulkan surface" << std::endl; return -1; } // Main loop bool running = true; SDL_Event event; while (running) { while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { running = false; } } // Render with Vulkan (not shown) } // Cleanup vkDestroySurfaceKHR(instance, surface, nullptr); SDL_DestroyWindow(window); SDL_Quit(); return 0; } ``` -------------------------------- ### Mipmap Generation Loop Setup Source: https://docs.vulkan.org/tutorial/latest/09_Generating_Mipmaps.html Initializes mipmap dimensions and sets up a loop to iterate through each mip level that needs to be generated. The loop starts from mip level 1, as level 0 is the source data. ```c++ int32_t mipWidth = texWidth; int32_t mipHeight = texHeight; for (uint32_t i = 1; i < mipLevels; i++) { } ``` -------------------------------- ### Creating a Render Pass with Multisampled-to-Single-Sampled Subpass Source: https://docs.vulkan.org/features/latest/features/proposals/VK_EXT_multisampled_render_to_single_sampled.html This example demonstrates creating a Vulkan render pass with a subpass configured for multisampled rendering to a single-sampled attachment. It includes setup for multiple attachments with varying sample counts and defines depth/stencil resolve modes. ```c // Render pass attachments with mixed sample count VkAttachmentDescription2 attachmentDescs[3] = { [0] = { .sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR, .format = ..., .samples = 1, .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, .storeOp = VK_ATTACHMENT_STORE_OP_STORE, .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, .initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, .finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, }, [1] = { .sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR, .format = ..., .samples = 4, .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, .initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, .finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, }, [2] = { .sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR, .format = ..., .samples = 1, .loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, .storeOp = VK_ATTACHMENT_STORE_OP_STORE, .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD, .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, .initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, .finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, }, }; // Subpass attachment references VkAttachmentReference2 colorAttachments[2] = { [0] = { .sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2, .attachment = 0, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, }, [1] = { .sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2, .attachment = 1, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, }, }; VkAttachmentReference2 depthStencilAttachment = { .sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2, .attachment = 0, .layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, .aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, }; // Multisampled-render-to-single-sampling info. Rendering at 4xMSAA. VkMultisampledRenderToSingleSampledInfoEXT msrtss = { .sType = VK_STRUCTURE_TYPE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_INFO_EXT, .multisampledRenderToSingleSampledEnable = VK_TRUE, .rasterizationSamples = 4, }; // Resolve modes for depth/stencil VkSubpassDescriptionDepthStencilResolve depthStencilResolve = { .sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE, .pNext = &msrtss, .depthResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT, .stencilResolveMode = VK_RESOLVE_MODE_NONE, }; // The subpass description where multisampled-render-to-single-sampled rendering is enabled. VkSubpassDescription2 subpassDescription = { .sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR, .pNext = &depthStencilResolve, .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, .colorAttachmentCount = 2, .pColorAttachments = colorAttachments, .pDepthStencilAttachment = &depthStencilAttachment, }; // The render pass creation. VkRenderPassCreateInfo2KHR renderPassInfo = { .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR, .attachmentCount = 3, .pAttachments = attachmentDescs, .subpassCount = 1, .pSubpasses = &subpassDescription, }; VkRenderPass renderPass; vkCreateRenderPass2(device, &renderPassInfo, NULL, &renderPass); ``` -------------------------------- ### Creating VkDevice with Queue Families Source: https://docs.vulkan.org/guide/latest/queues.html This example shows how to configure and create a Vulkan logical device, specifying the queue families and the number of queues to create from each. It prepares VkDeviceQueueCreateInfo structures for transfer and graphics queues. ```cpp VkDeviceQueueCreateInfo queueCreateInfo[2]; queueCreateInfo[0].queueFamilyIndex = 0; // Transfer queueCreateInfo[0].queueCount = 1; queueCreateInfo[1].queueFamilyIndex = 1; // Graphics queueCreateInfo[1].queueCount = 2; VkDeviceCreateInfo deviceCreateInfo = {}; deviceCreateInfo.pQueueCreateInfos = queueCreateInfo; deviceCreateInfo.queueCreateInfoCount = 2; vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &device); ``` -------------------------------- ### Dynamic Vertex Input State Setup Source: https://docs.vulkan.org/samples/latest/samples/extensions/vertex_dynamic_state/README.html Demonstrates setting up a graphics pipeline with a null vertex input state and then dynamically configuring vertex input using the VK_EXT_vertex_input_dynamic_state extension. ```cpp VkVertexInputBindingDescription2EXT vertex_bindings_description_ext = { vkb::initializers::vertex_input_binding_description2ext( 0, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX, 1); } VkVertexInputAttributeDescription2EXT vertex_attribute_description_ext[2] = { vkb::initializers::vertex_input_attribute_description2ext( 0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, pos)), vkb::initializers::vertex_input_attribute_description2ext( 0, 1, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, normal); } graphics_create_info.pVertexInputState = nullptr; vkCreateGraphicsPipelines(get_device().get_handle(), VK_NULL_HANDLE, 1, &graphics_create_info, VK_NULL_HANDLE, &pipeline); /* First set of vertex input dynamic data (Vertex structure) */ vertex_bindings_description_ext[0].stride = sizeof(Vertex); vertex_attribute_description_ext[1].offset = offsetof(Vertex, normal); vkCmdSetVertexInputEXT(draw_cmd_buffer, ``` -------------------------------- ### Basic Vertex Shader Module Source: https://docs.vulkan.org/tutorial/latest/03_Drawing_a_triangle/02_Graphics_pipeline_basics/01_Shader_modules.html This vertex shader defines vertex positions directly within the shader code and outputs them as clip coordinates. It uses SV_VertexID to index into a hardcoded array of positions. Suitable for simple examples where vertex buffer setup is deferred. ```slang static float2 positions[3] = float2[]( float2(0.0, -0.5), float2(0.5, 0.5), float2(-0.5, 0.5) ); struct VertexOutput { float4 sv_position : SV_Position; }; [shader("vertex")] VertexOutput vertMain(uint vid : SV_VertexID) { VertexOutput output; output.sv_position = float4(positions[vid], 0.0, 1.0); return output; } ``` -------------------------------- ### Vulkan RAII Initialization and Cleanup Source: https://docs.vulkan.org/tutorial/latest/_attachments/07_image_views.cpp Initializes the Vulkan window and RAII context, sets up the Vulkan instance, debug messenger, surface, picks a physical device, creates a logical device, swap chain, and image views. Cleans up resources afterwards. ```cpp #include #else import vulkan_hpp; #endif #define GLFW_INCLUDE_VULKAN // REQUIRED only for GLFW CreateWindowSurface. #include constexpr uint32_t WIDTH = 800; constexpr uint32_t HEIGHT = 600; const std::vector validationLayers = { "VK_LAYER_KHRONOS_validation"}; #ifdef NDEBUG constexpr bool enableValidationLayers = false; #else constexpr bool enableValidationLayers = true; #endif class HelloTriangleApplication { public: void run() { initWindow(); initVulkan(); mainLoop(); cleanup(); } private: GLFWwindow *window = nullptr; vk::raii::Context context; vk::raii::Instance instance = nullptr; vk::raii::DebugUtilsMessengerEXT debugMessenger = nullptr; vk::raii::SurfaceKHR surface = nullptr; vk::raii::PhysicalDevice physicalDevice = nullptr; vk::raii::Device device = nullptr; vk::raii::Queue queue = nullptr; vk::raii::SwapchainKHR swapChain = nullptr; std::vector swapChainImages; vk::SurfaceFormatKHR swapChainSurfaceFormat; vk::Extent2D swapChainExtent; std::vector swapChainImageViews; std::vector requiredDeviceExtension = { vk::KHRSwapchainExtensionName}; void initWindow() { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); } void initVulkan() { createInstance(); setupDebugMessenger(); createSurface(); pickPhysicalDevice(); createLogicalDevice(); createSwapChain(); createImageViews(); } void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); } } void cleanup() { glfwDestroyWindow(window); glfwTerminate(); } void createInstance() { constexpr vk::ApplicationInfo appInfo{.pApplicationName = "Hello Triangle", .applicationVersion = VK_MAKE_VERSION(1, 0, 0), .pEngineName = "No Engine", .engineVersion = VK_MAKE_VERSION(1, 0, 0), .apiVersion = vk::ApiVersion14}; // Get the required layers std::vector requiredLayers; if (enableValidationLayers) { requiredLayers.assign(validationLayers.begin(), validationLayers.end()); } // Check if the required layers are supported by the Vulkan implementation. auto layerProperties = context.enumerateInstanceLayerProperties(); auto unsupportedLayerIt = std::ranges::find_if(requiredLayers, [&layerProperties](auto const &requiredLayer) { return std::ranges::none_of(layerProperties, [requiredLayer](auto const &layerProperty) { return strcmp(layerProperty.layerName, requiredLayer) == 0; }); }); if (unsupportedLayerIt != requiredLayers.end()) { throw std::runtime_error("Required layer not supported: " + std::string(*unsupportedLayerIt)); } // Get the required extensions. auto requiredExtensions = getRequiredInstanceExtensions(); // Check if the required extensions are supported by the Vulkan implementation. auto extensionProperties = context.enumerateInstanceExtensionProperties(); auto unsupportedPropertyIt = std::ranges::find_if(requiredExtensions, [&extensionProperties](auto const &requiredExtension) { return std::ranges::none_of(extensionProperties, [requiredExtension](auto const &extensionProperty) { return strcmp(extensionProperty.extensionName, requiredExtension) == 0; }); }); if (unsupportedPropertyIt != requiredExtensions.end()) { throw std::runtime_error("Required extension not supported: " + std::string(*unsupportedPropertyIt)); } vk::InstanceCreateInfo createInfo{.pApplicationInfo = &appInfo, .enabledLayerCount = static_cast(requiredLayers.size()), .ppEnabledLayerNames = requiredLayers.data(), .enabledExtensionCount = static_cast(requiredExtensions.size()), .ppEnabledExtensionNames = requiredExtensions.data()}; instance = vk::raii::Instance(context, createInfo); } void setupDebugMessenger() { if (!enableValidationLayers) return; vk::DebugUtilsMessageSeverityFlagsEXT severityFlags(vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | ``` -------------------------------- ### Record Decode Operation (Video Session without DPB Slots) Source: https://docs.vulkan.org/features/latest/features/proposals/VK_KHR_video_decode_queue.html Records a video decode operation using vkCmdDecodeVideoKHR. This example demonstrates the setup for a decode operation that does not utilize DPB slots, specifying the output picture resource and the source bitstream buffer. ```c vkCmdBeginVideoCodingKHR(commandBuffer, ...); VkVideoPictureResourceInfoKHR decodeOutputPictureResource = { .sType = VK_STRUCTURE_TYPE_VIDEO_PICTURE_RESOURCE_INFO_KHR, .pNext = NULL, .codedOffset = ... // offset within the image subresource (typically { 0, 0 }) .codedExtent = ... // extent of decoded picture (typically the video frame size) .baseArrayLayer = 0, .imageViewBinding = outputImageView }; VkVideoDecodeInfoKHR decodeInfo = { .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_INFO_KHR, .pNext = ... // pointer to codec-specific picture information structure .flags = 0, .srcBuffer = bitstreamBuffer, .srcBufferOffset = ... // offset of picture data in the video bitstream buffer .srcBufferRange = ... // size of picture data in the video bitstream buffer .dstPictureResource = decodeOutputPictureResource, .pSetupReferenceSlot = NULL, .referenceSlotCount = 0, .pReferenceSlots = NULL }; vkCmdDecodeVideoKHR(commandBuffer, &decodeInfo); vkCmdEndVideoCodingKHR(commandBuffer, ...); ``` -------------------------------- ### Install GLM on Arch Linux Source: https://docs.vulkan.org/tutorial/latest/02_Development_environment.html Installs the GLM header-only library on Arch Linux. ```bash sudo pacman -S glm ```