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