Rendering Engine 0.2.9
Modular Graphics Rendering Engine | v0.2.9
vulkan_renderer.hpp
Go to the documentation of this file.
1// This file is part of the Rendering Engine project.
2// Author: Alexander Obzherin <alexanderobzherin@gmail.com>
3// Copyright (c) 2026 Alexander Obzherin
4// Distributed under the terms of the zlib License. See LICENSE.md for details.
5#pragma once
6
7#include "i_renderer.hpp"
8#include "i_window_system.hpp"
10#include <optional>
11#include <vector>
12#include <unordered_map>
13#include <deque>
14
15#define GLFW_INCLUDE_VULKAN
16#include <GLFW/glfw3.h>
17#include <glm/glm.hpp>
18
19namespace rendering_engine
20{
21/** @brief Number of frames that can be processed simultaneously (double buffering). */
23/**
24 * @struct QueueFamilyIndices
25 * @brief Holds indices of Vulkan queue families supporting required operations.
26 */
28{
29 std::optional<uint32_t> graphicsFamily;
30 std::optional<uint32_t> presentFamily;
31 /** @brief Checks if both required queue families are available. */
33 {
34 return graphicsFamily.has_value() && presentFamily.has_value();
35 }
36};
37/**
38 * @struct SwapChainSupportDetails
39 * @brief Describes capabilities and available configurations for a physical device's swap chain.
40 */
42{
43 VkSurfaceCapabilitiesKHR capabilities;
44 std::vector<VkSurfaceFormatKHR> formats;
45 std::vector<VkPresentModeKHR> presentModes;
46};
47
48enum class DeferredType
49{
50 Buffer,
51 Memory,
53 Image,
55 Sampler,
58};
59
61{
63union {
64 VkBuffer buffer;
65 VkDeviceMemory memory;
66 VkDescriptorPool descriptorPool;
67 VkImage image;
68 VkImageView imageView;
69 VkSampler sampler;
70 VkPipeline pipeline;
71 VkPipelineLayout pipelineLayout;
72};
73std::uint64_t retireFrame = 0;
74DeferredItem() { buffer = VK_NULL_HANDLE; }
75};
76
77class Material;
78class TextureCache;
79
80/**
81 * @class VulkanRenderer
82 * @brief Vulkan-based implementation of the IRenderer interface.
83 *
84 * This class encapsulates the initialization and management of Vulkan rendering resources,
85 * including device selection, swap chain management, render pass setup, frame synchronization,
86 * and command buffer submission. It serves as the core rendering backend for all graphics
87 * operations performed by the engine.
88 */
90{
91public:
92 /**
93 * @brief Constructs a VulkanRenderer bound to a specific window system.
94 * @param windowSystem Reference to the window system used for surface creation and event handling.
95 */
96 VulkanRenderer(IWindowSystem& windowSystem);
97 /** @copydoc IRenderer::InitializeRenderer */
98 void InitializeRenderer() override;
99 /** @copydoc IRenderer::DrawFrame */
100 void DrawFrame() override;
101 /** @copydoc IRenderer::BeginFrame */
102 bool BeginFrame() override;
103 /** @copydoc IRenderer::BeginRenderPass */
104 void BeginRenderPass() override;
105 /** @copydoc IRenderer::EndRenderPass */
106 void EndRenderPass() override;
107 /** @copydoc IRenderer::EndFrame */
108 void EndFrame() override;
109 /** @copydoc IRenderer::WaitIdle */
110 void WaitIdle() override;
111 /** @copydoc IRenderer::ShutdownRenderer */
112 void ShutdownRenderer() override;
113 /** @copydoc IRenderer::RegisterObserver */
114 void RegisterObserver(IRendererObserver* notifier) override;
115 /** @copydoc IRenderer::UnregisterObserver */
116 void UnregisterObserver(IRendererObserver* notifier) override;
117
118 /** @copydoc IRenderer::ProvideRenderResources */
120 /** @copydoc IRenderer::ProvideTextureRenderResources */
122 /** @copydoc IRenderer::ProvideMaterialRenderResources */
124 /** @copydoc IRenderer::ProvideMeshRenderResources */
126 /** @copydoc IRenderer::SetDefaultColor */
127 void SetDefaultColor(float r, float g, float b) override;
128
129 /**
130 * @brief Creates a new Vulkan buffer with the specified usage and memory properties.
131 * @param size Size of the buffer in bytes.
132 * @param usage Bitmask specifying intended buffer usage.
133 * @param properties Memory property flags defining allocation type.
134 * @param buffer Output handle to the created buffer.
135 * @param bufferMemory Output handle to the allocated buffer memory.
136 */
137 void CreateBuffer(VkDeviceSize size,
138 VkBufferUsageFlags usage,
139 VkMemoryPropertyFlags properties,
140 VkBuffer& buffer,
141 VkDeviceMemory& bufferMemory);
142 /**
143 * @brief Copies data from one buffer to another using a temporary command buffer.
144 * @param srcBuffer Source buffer.
145 * @param dstBuffer Destination buffer.
146 * @param size Size of data to copy in bytes.
147 */
148 void CopyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size);
149 /** @brief Returns reference to the logical Vulkan device. */
150 VkDevice& GetLogicalDevice();
151 /**
152 * @brief Transitions the image layout for a given Vulkan image.
153 * @param image Image handle.
154 * @param format Image format.
155 * @param oldLayout Current image layout.
156 * @param newLayout Desired image layout.
157 * @param mipmapLevels Number of mipmap levels to transition.
158 */
159 void TransitionImageLayout(VkImage image,
160 VkFormat format,
161 VkImageLayout oldLayout,
162 VkImageLayout newLayout,
163 std::uint32_t mipmapLevels);
164 /**
165 * @brief Copies buffer data into an image (used for uploading texture data).
166 * @param buffer Source buffer containing pixel data.
167 * @param image Destination image.
168 * @param width Image width in pixels.
169 * @param height Image height in pixels.
170 */
171 void CopyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height);
172 /**
173 * @brief Generates mipmaps for a given image resource.
174 * @param image Vulkan image handle.
175 * @param imageFormat Image format (must support linear blitting).
176 * @param texWidth Texture width in pixels.
177 * @param texHeight Texture height in pixels.
178 * @param mipLevels Number of mipmap levels to generate.
179 */
180 void GenerateMipmaps(VkImage image,
181 VkFormat imageFormat,
182 int32_t texWidth,
183 int32_t texHeight,
184 uint32_t mipLevels);
185 /**
186 * @brief Finds a suitable memory type for a given allocation.
187 * @param typeFilter Bitmask of compatible memory types.
188 * @param properties Desired memory property flags.
189 * @return Index of the selected memory type.
190 */
191 uint32_t FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties);
192 /** @brief Returns reference to the physical device. */
193 VkPhysicalDevice& GetPhysicalDevice();
194 /** @brief Returns reference to the physical device�s supported features. */
195 VkPhysicalDeviceFeatures& GetPhysDevSupportedFeatures();
196
197 /**
198 * @brief Creates a descriptor set layout corresponding to a given material.
199 *
200 * The layout defines how shader resources (uniform buffers, custom parameters, textures)
201 * are bound to the pipeline. By the local convention of this engine:
202 * - **Binding 0** � Transformation matrices (model, view, projection) depending on material domain (2D or 3D).
203 * - **Binding 1** � Custom parameter variable block (if any). All variables are serialized and packed into a single binding.
204 * - **Bindings 2..n** � Texture samplers, one texture per binding.
205 *
206 * This convention ensures consistent descriptor bindings across all shaders and materials.
207 *
208 * @param material Pointer to the material defining binding structure.
209 * @return Handle to the created descriptor set layout.
210 */
211 VkDescriptorSetLayout CreateDescriptorSetLayout(Material* material);
212 /** @brief Returns the index of the currently active frame in flight. */
213 inline size_t GetCurrentFrame() const
214 {
215 return mCurrentFrame;
216 }
217 /** @brief Returns the collection of command buffers used for rendering. */
218 std::vector<VkCommandBuffer> GetComandBuffers();
219 /**
220 * @brief Creates a Vulkan graphics pipeline based on material and shader inputs.
221 * @param material Pointer to the material configuration.
222 * @param descriptorSetLayout Descriptor set layout used in the pipeline.
223 * @param spvVertShaderCode Compiled SPIR-V vertex shader bytecode.
224 * @param spvFragShaderCode Compiled SPIR-V fragment shader bytecode.
225 * @return A pair containing pipeline layout and pipeline handle.
226 */
227 std::pair<VkPipelineLayout, VkPipeline> CreateGraphicsPipeline(Material* material,
228 VkDescriptorSetLayout& descriptorSetLayout,
229 std::vector<char>& spvVertShaderCode,
230 std::vector<char>& spvFragShaderCode);
231
232 /**
233 * @brief Returns a vertex input binding description for a specific vertex type.
234 * @tparam T Vertex type (e.g., Vertex2D, VertexPositionColorTexture, etc.).
235 */
236 template <typename T>
237 static VkVertexInputBindingDescription GetBindingDescription();
238 /**
239 * @brief Returns vertex attribute descriptions for a specific vertex type.
240 * @tparam T Vertex type (e.g., Vertex2D, VertexPositionColorTexture, etc.).
241 */
242 template <typename T>
243 static std::vector<VkVertexInputAttributeDescription> GetAttributeDescriptions();
244
245 void AddDeferredDestroy(DeferredItem deferredItem);
246
247private:
248 void CreateInstance();
249 bool CheckValidationLayerSupport();
250 std::vector<const char*> GetRequiredExtensions();
251 void PopulateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo);
252 static VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback(
253 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
254 VkDebugUtilsMessageTypeFlagsEXT messageType,
255 const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
256 void* pUserData);
257 void SetupDebugMessenger();
258 VkResult CreateDebugUtilsMessengerEXT(VkInstance instance,
259 VkDebugUtilsMessengerCreateInfoEXT const* pCreateInfo,
260 VkAllocationCallbacks const* pAllocator,
261 VkDebugUtilsMessengerEXT* pDebugMessenger);
262 void DestroyDebugUtilsMessengerEXT(VkInstance instance,
263 VkDebugUtilsMessengerEXT debugMessenger,
264 const VkAllocationCallbacks* pAllocator);
265 void CreateSurface();
266
267 void PickPhysicalDevice();
268 bool IsDeviceSuitable(VkPhysicalDevice device);
269 QueueFamilyIndices FindQueueFamilies(VkPhysicalDevice device);
270 bool CheckDeviceExtensionSupport(VkPhysicalDevice physicalDevice);
271 SwapChainSupportDetails QuerySwapChainSupport(VkPhysicalDevice device);
272 VkSampleCountFlagBits CheckMaxUsableSampleCount();
273
274 void CreateLogicalDevice();
275
276 void CreateSwapChain();
277 VkSurfaceFormatKHR ChooseSwapSurfaceFormat(std::vector<VkSurfaceFormatKHR> const& availableFormats);
278 VkPresentModeKHR ChooseSwapPresentMode(std::vector<VkPresentModeKHR>const& availablePresentModes);
279 VkExtent2D ChooseSwapExtent(VkSurfaceCapabilitiesKHR const& capabilities);
280 void CleanupSwapChain();
281 void RecreateSwapChain();
282
283 void CreateImageViews();
284 VkImageView CreateImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags, std::uint32_t mipmapLevels);
285
286 void CreateRenderPass();
287 VkFormat FindDepthFormat();
288 VkFormat FindSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features);
289
290
291 void CreateCommandPool();
292
293 void CreateColorResources();
294
295 void CreateVulkanImage(uint32_t width, uint32_t height, std::uint32_t mipmapLevels,
296 VkSampleCountFlagBits numSamples, VkFormat format, VkImageTiling tiling,
297 VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory);
298
299 void CreateDepthResources();
300
301 void CreateFramebuffers();
302
303 void CreateCommandBuffers();
304
305 void CreateFrameSyncObjects();
306 void CreateSwapchainSyncObjects();
307
308 VkShaderModule CreateShaderModule(std::vector<char>& code);
309
310 void RecordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex);
311
312 VkCommandBuffer BeginSingleTimeCommand();
313 bool HasStencilComponent(VkFormat format);
314 void EndSingleTimeCommand(VkCommandBuffer commandBuffer);
315
316 void ProcessDeferredDestruction();
317
318private:
319 IWindowSystem& mWindowSystem;
320 size_t mCurrentFrame;
321
322 VkInstance mInstance;
323 VkSurfaceKHR mSurface;
324 std::vector<const char*> const mValidationLayers = { "VK_LAYER_KHRONOS_validation" };
325 VkDebugUtilsMessengerEXT mDebugMessenger;
326 std::vector<const char*> const mDeviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
327 VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE;
328 VkPhysicalDeviceProperties mPhysicalDeviceProperties;
329 VkPhysicalDeviceFeatures mPhysDevSupportedFeatures;
330
331 VkSampleCountFlagBits mMSAASamples;
332
333 VkDevice mLogicalDevice;
334 VkQueue mGraphicsQueue;
335 VkQueue mPresentQueue;
336
337 VkSwapchainKHR mSwapChain;
338 std::vector<VkImage> mSwapChainImages;
339 VkFormat mSwapChainImageFormat;
340 VkExtent2D mSwapChainExtent;
341
342 std::vector<VkImageView> mSwapChainImageViews;
343 std::vector<VkFramebuffer> mSwapChainFramebuffers;
344
345 VkRenderPass mRenderPass;
346
347 VkCommandPool mCommandPool;
348 std::vector<VkCommandBuffer> mCommandBuffers;
349 uint32_t mImageIndex = 0;
350
351 VkImage mColorImage;
352 VkDeviceMemory mColorImageMemory;
353 VkImageView mColorImageView;
354
355 VkImage mDepthImage;
356 VkDeviceMemory mDepthImageMemory;
357 VkImageView mDepthImageView;
358
359 std::vector<VkSemaphore> mImageAvailableSemaphores;
360 std::vector<VkSemaphore> mRenderFinishedSemaphores;
361 std::vector<VkFence> mInFlightFences;
362 std::vector<VkFence> mImagesInFlight;
363 std::vector<IRendererObserver*> mObservers;
364
365 std::uint64_t mFrameSerial = 0;
366 std::deque<DeferredItem> mDeferredQueue;
367
368 glm::vec3 mDefaultColor = {0.0f, 0.0f, 0.0f};
369};
370
371} // rendering_engine
Interface for backend-specific material GPU resources.
Interface for GPU mesh resource management.
Interface for rendering backend resource aggregation and submission.
Interface for observing renderer resource lifecycle events.
Defines an abstract interface for rendering backends.
Definition: i_renderer.hpp:29
Interface for backend-specific GPU texture resource management.
Abstract interface for platform-specific window management.
Represents a material instance with parameter values, texture bindings, and rendering configuration.
Definition: material.hpp:30
Vulkan-based implementation of the IRenderer interface.
bool BeginFrame() override
Begins frame rendering operations.
VulkanRenderer(IWindowSystem &windowSystem)
Constructs a VulkanRenderer bound to a specific window system.
ITextureRenderResources * ProvideTextureRenderResources() const override
Provides access to texture-related GPU resources.
VkPhysicalDeviceFeatures & GetPhysDevSupportedFeatures()
Returns reference to the physical device�s supported features.
void GenerateMipmaps(VkImage image, VkFormat imageFormat, int32_t texWidth, int32_t texHeight, uint32_t mipLevels)
Generates mipmaps for a given image resource.
void CopyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height)
Copies buffer data into an image (used for uploading texture data).
static std::vector< VkVertexInputAttributeDescription > GetAttributeDescriptions()
Returns vertex attribute descriptions for a specific vertex type.
VkDevice & GetLogicalDevice()
Returns reference to the logical Vulkan device.
VkDescriptorSetLayout CreateDescriptorSetLayout(Material *material)
Creates a descriptor set layout corresponding to a given material.
IRenderResources * ProvideRenderResources() const override
Provides access to the general rendering resource manager.
void InitializeRenderer() override
Initializes all rendering subsystems and GPU resources.
void BeginRenderPass() override
Begins the active render pass for the current frame.
IMeshRenderResources * ProvideMeshRenderResources() const override
Provides access to mesh-related GPU resources.
void CopyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size)
Copies data from one buffer to another using a temporary command buffer.
void EndRenderPass() override
Ends the active render pass.
void EndFrame() override
Completes the current frame rendering and presents the result.
VkPhysicalDevice & GetPhysicalDevice()
Returns reference to the physical device.
void CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer &buffer, VkDeviceMemory &bufferMemory)
Creates a new Vulkan buffer with the specified usage and memory properties.
void UnregisterObserver(IRendererObserver *notifier) override
Unregisters a previously registered observer.
void WaitIdle() override
Waits until the GPU has finished all pending rendering operations.
std::pair< VkPipelineLayout, VkPipeline > CreateGraphicsPipeline(Material *material, VkDescriptorSetLayout &descriptorSetLayout, std::vector< char > &spvVertShaderCode, std::vector< char > &spvFragShaderCode)
Creates a Vulkan graphics pipeline based on material and shader inputs.
void DrawFrame() override
Executes a full frame rendering cycle.
static VkVertexInputBindingDescription GetBindingDescription()
Returns a vertex input binding description for a specific vertex type.
void AddDeferredDestroy(DeferredItem deferredItem)
size_t GetCurrentFrame() const
Returns the index of the currently active frame in flight.
uint32_t FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties)
Finds a suitable memory type for a given allocation.
void SetDefaultColor(float r, float g, float b) override
Sets the renderer clear color.
void TransitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, std::uint32_t mipmapLevels)
Transitions the image layout for a given Vulkan image.
void RegisterObserver(IRendererObserver *notifier) override
Registers an observer for rendering events.
void ShutdownRenderer() override
Destroys and cleans up all rendering resources.
std::vector< VkCommandBuffer > GetComandBuffers()
Returns the collection of command buffers used for rendering.
IMaterialRenderResources * ProvideMaterialRenderResources() const override
Provides access to material-related GPU resources.
const int MAX_FRAMES_IN_FLIGHT
Number of frames that can be processed simultaneously (double buffering).
Holds indices of Vulkan queue families supporting required operations.
bool IsComplete()
Checks if both required queue families are available.
std::optional< uint32_t > presentFamily
std::optional< uint32_t > graphicsFamily
Describes capabilities and available configurations for a physical device's swap chain.
std::vector< VkPresentModeKHR > presentModes
std::vector< VkSurfaceFormatKHR > formats