Rendering Engine 0.2.17
Modular Graphics Rendering Engine | v0.2.17
Loading...
Searching...
No Matches
rendering_engine::VulkanRenderer Class Reference

Vulkan-based implementation of the IRenderer interface. More...

#include <vulkan_renderer.hpp>

Inherits rendering_engine::IRenderer.

Public Member Functions

 VulkanRenderer (IWindowSystem &windowSystem)
 Constructs a VulkanRenderer bound to a specific window system.
 
void InitializeRenderer () override
 Initializes all rendering subsystems and GPU resources.
 
void DrawFrame () override
 Executes a full frame rendering cycle.
 
bool BeginFrame () override
 Begins frame rendering operations.
 
void BeginRenderPass () override
 Begins the active render pass for the current frame.
 
void EndRenderPass () override
 Ends the active render pass.
 
void EndFrame () override
 Completes the current frame rendering and presents the result.
 
void WaitIdle () override
 Waits until the GPU has finished all pending rendering operations.
 
void ShutdownRenderer () override
 Destroys and cleans up all rendering resources.
 
void RegisterObserver (IRendererObserver *notifier) override
 Registers an observer for rendering events.
 
void UnregisterObserver (IRendererObserver *notifier) override
 Unregisters a previously registered observer.
 
IRenderResources * ProvideRenderResources () const override
 Provides access to the general rendering resource manager.
 
ITextureRenderResources * ProvideTextureRenderResources () const override
 Provides access to texture-related GPU resources.
 
IMaterialRenderResources * ProvideMaterialRenderResources () const override
 Provides access to material-related GPU resources.
 
IMeshRenderResources * ProvideMeshRenderResources () const override
 Provides access to mesh-related GPU resources.
 
void SetDefaultColor (float r, float g, float b) override
 Sets the renderer clear color.
 
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 CopyBuffer (VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size)
 Copies data from one buffer to another using a temporary command buffer.
 
VkDevice & GetLogicalDevice ()
 Returns reference to the logical Vulkan device.
 
void TransitionImageLayout (VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, std::uint32_t mipmapLevels)
 Transitions the image layout for a given Vulkan image.
 
void CopyBufferToImage (VkBuffer buffer, VkImage image, uint32_t width, uint32_t height)
 Copies buffer data into an image (used for uploading texture data).
 
void GenerateMipmaps (VkImage image, VkFormat imageFormat, int32_t texWidth, int32_t texHeight, uint32_t mipLevels)
 Generates mipmaps for a given image resource.
 
uint32_t FindMemoryType (uint32_t typeFilter, VkMemoryPropertyFlags properties)
 Finds a suitable memory type for a given allocation.
 
VkPhysicalDevice & GetPhysicalDevice ()
 Returns reference to the physical device.
 
VkPhysicalDeviceFeatures & GetPhysDevSupportedFeatures ()
 Returns reference to the physical device�s supported features.
 
VkDescriptorSetLayout CreateDescriptorSetLayout (Material *material)
 Creates a descriptor set layout corresponding to a given material.
 
size_t GetCurrentFrame () const
 Returns the index of the currently active frame in flight.
 
std::vector< VkCommandBuffer > GetComandBuffers ()
 Returns the collection of command buffers used for rendering.
 
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 AddDeferredDestroy (DeferredItem deferredItem)
 
- Public Member Functions inherited from rendering_engine::IRenderer
virtual ~IRenderer ()=default
 Virtual destructor for safe polymorphic deletion.
 

Static Public Member Functions

template<typename T >
static VkVertexInputBindingDescription GetBindingDescription ()
 Returns a vertex input binding description for a specific vertex type.
 
template<typename T >
static std::vector< VkVertexInputAttributeDescription > GetAttributeDescriptions ()
 Returns vertex attribute descriptions for a specific vertex type.
 
template<>
std::vector< VkVertexInputAttributeDescription > GetAttributeDescriptions ()
 
template<>
std::vector< VkVertexInputAttributeDescription > GetAttributeDescriptions ()
 
template<>
std::vector< VkVertexInputAttributeDescription > GetAttributeDescriptions ()
 
template<>
std::vector< VkVertexInputAttributeDescription > GetAttributeDescriptions ()
 
template<>
std::vector< VkVertexInputAttributeDescription > GetAttributeDescriptions ()
 
template<>
std::vector< VkVertexInputAttributeDescription > GetAttributeDescriptions ()
 

Detailed Description

Vulkan-based implementation of the IRenderer interface.

This class encapsulates the initialization and management of Vulkan rendering resources, including device selection, swap chain management, render pass setup, frame synchronization, and command buffer submission. It serves as the core rendering backend for all graphics operations performed by the engine.

Definition at line 89 of file vulkan_renderer.hpp.

Constructor & Destructor Documentation

◆ VulkanRenderer()

rendering_engine::VulkanRenderer::VulkanRenderer ( IWindowSystem &  windowSystem)

Constructs a VulkanRenderer bound to a specific window system.

Parameters
windowSystemReference to the window system used for surface creation and event handling.

Definition at line 39 of file vulkan_renderer.cpp.

40 :
41 mWindowSystem{windowSystem},
42 mCurrentFrame{0}
43{}

Member Function Documentation

◆ AddDeferredDestroy()

void rendering_engine::VulkanRenderer::AddDeferredDestroy ( DeferredItem  deferredItem)

Definition at line 598 of file vulkan_renderer.cpp.

599{
600 switch (deferredItem.type)
601 {
602 case DeferredType::Buffer: if (deferredItem.buffer == VK_NULL_HANDLE) return; break;
603 case DeferredType::Memory: if (deferredItem.memory == VK_NULL_HANDLE) return; break;
604 case DeferredType::DescriptorPool: if (deferredItem.descriptorPool == VK_NULL_HANDLE) return; break;
605 default: break;
606 }
607 deferredItem.retireFrame = mFrameSerial + MAX_FRAMES_IN_FLIGHT;
608 mDeferredQueue.push_back(deferredItem);
609}
const int MAX_FRAMES_IN_FLIGHT
Number of frames that can be processed simultaneously (double buffering).

◆ BeginFrame()

bool rendering_engine::VulkanRenderer::BeginFrame ( )
overridevirtual

Begins frame rendering operations.

Returns
True if the frame can proceed.

Implements rendering_engine::IRenderer.

Definition at line 149 of file vulkan_renderer.cpp.

150{
151 vkWaitForFences(mLogicalDevice, 1, &mInFlightFences[mCurrentFrame], VK_TRUE, UINT64_MAX);
152
153 VkResult result = vkAcquireNextImageKHR(mLogicalDevice, mSwapChain, UINT64_MAX, mImageAvailableSemaphores[mCurrentFrame], VK_NULL_HANDLE, &mImageIndex);
154
155 if (result == VK_ERROR_OUT_OF_DATE_KHR)
156 {
157 RecreateSwapChain();
158 return false;
159 }
160 else
161 {
162 if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR)
163 {
164 LOG_ERROR("failed to acquire swap chain image!");
165 throw std::runtime_error("failed to acquire swap chain image!");
166 }
167 }
168
169 if (mImagesInFlight[mImageIndex] != VK_NULL_HANDLE)
170 {
171 vkWaitForFences(mLogicalDevice, 1, &mImagesInFlight[mImageIndex], VK_TRUE, UINT64_MAX);
172 }
173 mImagesInFlight[mImageIndex] = mInFlightFences[mCurrentFrame];
174
175 vkResetFences(mLogicalDevice, 1, &mInFlightFences[mCurrentFrame]);
176 ++mFrameSerial;
177 ProcessDeferredDestruction();
178
179 vkResetCommandBuffer(mCommandBuffers[mCurrentFrame], /*VkCommandBufferResetFlagBits*/ 0);
180
181 return true;
182}
#define LOG_ERROR(msg)
Definition logger.hpp:41

◆ BeginRenderPass()

void rendering_engine::VulkanRenderer::BeginRenderPass ( )
overridevirtual

Begins the active render pass for the current frame.

Implements rendering_engine::IRenderer.

Definition at line 184 of file vulkan_renderer.cpp.

185{
186 VkCommandBufferBeginInfo beginInfo{};
187 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
188 //beginInfo.flags = 0; // Optional
189 //beginInfo.pInheritanceInfo = nullptr; // Optional
190
191 if (vkBeginCommandBuffer(mCommandBuffers[mCurrentFrame], &beginInfo) != VK_SUCCESS)
192 {
193 LOG_ERROR("failed to begin recording command buffer!");
194 throw std::runtime_error("failed to begin recording command buffer!");
195 }
196
197 VkRenderPassBeginInfo renderPassInfo{};
198 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
199 renderPassInfo.renderPass = mRenderPass;
200 renderPassInfo.framebuffer = mSwapChainFramebuffers[mImageIndex];
201
202 renderPassInfo.renderArea.offset = { 0, 0 };
203 renderPassInfo.renderArea.extent = mSwapChainExtent;
204
205 std::array<VkClearValue, 2> clearValues{};
206 clearValues[0].color = { {mDefaultColor.r, mDefaultColor.g, mDefaultColor.b, 1.0f} };
207 clearValues[1].depthStencil = { 1.0f, 0 };
208 renderPassInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
209 renderPassInfo.pClearValues = clearValues.data();
210
211 vkCmdBeginRenderPass(mCommandBuffers[mCurrentFrame], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
212}

◆ CopyBuffer()

void rendering_engine::VulkanRenderer::CopyBuffer ( VkBuffer  srcBuffer,
VkBuffer  dstBuffer,
VkDeviceSize  size 
)

Copies data from one buffer to another using a temporary command buffer.

Parameters
srcBufferSource buffer.
dstBufferDestination buffer.
sizeSize of data to copy in bytes.

Definition at line 383 of file vulkan_renderer.cpp.

384{
385 auto commandBuffer = BeginSingleTimeCommand();
386
387 VkBufferCopy copyRegion{};
388 copyRegion.srcOffset = 0; // Optional
389 copyRegion.dstOffset = 0; // Optional
390 copyRegion.size = size;
391 vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, &copyRegion);
392
393 EndSingleTimeCommand(commandBuffer);
394}

◆ CopyBufferToImage()

void rendering_engine::VulkanRenderer::CopyBufferToImage ( VkBuffer  buffer,
VkImage  image,
uint32_t  width,
uint32_t  height 
)

Copies buffer data into an image (used for uploading texture data).

Parameters
bufferSource buffer containing pixel data.
imageDestination image.
widthImage width in pixels.
heightImage height in pixels.

Definition at line 474 of file vulkan_renderer.cpp.

475{
476 VkCommandBuffer commandBuffer = BeginSingleTimeCommand();
477
478 VkBufferImageCopy region{};
479 region.bufferOffset = 0;
480 region.bufferRowLength = 0;
481 region.bufferImageHeight = 0;
482
483 region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
484 region.imageSubresource.mipLevel = 0;
485 region.imageSubresource.baseArrayLayer = 0;
486 region.imageSubresource.layerCount = 1;
487
488 region.imageOffset = { 0, 0, 0 };
489 region.imageExtent = {
490 width,
491 height,
492 1
493 };
494
495 vkCmdCopyBufferToImage(
496 commandBuffer,
497 buffer,
498 image,
499 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
500 1,
501 &region
502 );
503
504 EndSingleTimeCommand(commandBuffer);
505}

◆ CreateBuffer()

void rendering_engine::VulkanRenderer::CreateBuffer ( VkDeviceSize  size,
VkBufferUsageFlags  usage,
VkMemoryPropertyFlags  properties,
VkBuffer &  buffer,
VkDeviceMemory &  bufferMemory 
)

Creates a new Vulkan buffer with the specified usage and memory properties.

Parameters
sizeSize of the buffer in bytes.
usageBitmask specifying intended buffer usage.
propertiesMemory property flags defining allocation type.
bufferOutput handle to the created buffer.
bufferMemoryOutput handle to the allocated buffer memory.

Definition at line 351 of file vulkan_renderer.cpp.

352{
353 VkBufferCreateInfo bufferInfo{};
354 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
355 bufferInfo.size = size;
356
357 bufferInfo.usage = usage;
358 bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
359
360 if (vkCreateBuffer(mLogicalDevice, &bufferInfo, nullptr, &buffer) != VK_SUCCESS)
361 {
362 LOG_ERROR("failed to create vertex buffer!");
363 throw std::runtime_error("failed to create vertex buffer!");
364 }
365
366 VkMemoryRequirements memRequirements;
367 vkGetBufferMemoryRequirements(mLogicalDevice, buffer, &memRequirements);
368
369 VkMemoryAllocateInfo allocInfo{};
370 allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
371 allocInfo.allocationSize = memRequirements.size;
372 allocInfo.memoryTypeIndex = FindMemoryType(memRequirements.memoryTypeBits, properties);
373
374 if (vkAllocateMemory(mLogicalDevice, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS)
375 {
376 LOG_ERROR("failed to allocate vertex buffer memory!");
377 throw std::runtime_error("failed to allocate vertex buffer memory!");
378 }
379
380 vkBindBufferMemory(mLogicalDevice, buffer, bufferMemory, 0);
381}
uint32_t FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties)
Finds a suitable memory type for a given allocation.

◆ CreateDescriptorSetLayout()

VkDescriptorSetLayout rendering_engine::VulkanRenderer::CreateDescriptorSetLayout ( Material *  material)

Creates a descriptor set layout corresponding to a given material.

The layout defines how shader resources (uniform buffers, custom parameters, textures) are bound to the pipeline. By the local convention of this engine:

  • Binding 0 � Transformation matrices (model, view, projection) depending on material domain (2D or 3D).
  • Binding 1 � Custom parameter variable block (if any). All variables are serialized and packed into a single binding.
  • Bindings 2..n � Texture samplers, one texture per binding.

This convention ensures consistent descriptor bindings across all shaders and materials.

Parameters
materialPointer to the material defining binding structure.
Returns
Handle to the created descriptor set layout.

Definition at line 1407 of file vulkan_renderer.cpp.

1408{
1409 std::uint32_t bindingNumber = 0;
1410 std::vector<VkDescriptorSetLayoutBinding> bindings;
1411
1412 // Uniform buffer to transmit transformation matrix
1413 VkDescriptorSetLayoutBinding uboLayoutBinding{};
1414 uboLayoutBinding.binding = bindingNumber;
1415 uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
1416 uboLayoutBinding.descriptorCount = 1;
1417 uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
1418 uboLayoutBinding.pImmutableSamplers = nullptr; // Optional
1419
1420 bindings.push_back(uboLayoutBinding);
1421
1422 PackedMaterialData packedMaterialData = material->PackMaterialParameters();
1423 if (!packedMaterialData.buffer.empty())
1424 {
1425 ++bindingNumber;
1426 // Uniform buffer to transmit custom material variables
1427 VkDescriptorSetLayoutBinding customMaterialVariables;
1428 customMaterialVariables.binding = bindingNumber;
1429 customMaterialVariables.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
1430 customMaterialVariables.descriptorCount = 1;
1431 customMaterialVariables.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
1432 customMaterialVariables.pImmutableSamplers = nullptr; // Optional
1433
1434 bindings.push_back(customMaterialVariables);
1435 }
1436
1437 for (const auto& textureName : material->GetTextures())
1438 {
1439 (void)textureName;
1440 ++bindingNumber;
1441 VkDescriptorSetLayoutBinding samplerLayoutBinding{};
1442 samplerLayoutBinding.binding = bindingNumber;
1443 samplerLayoutBinding.descriptorCount = 1;
1444 samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1445 samplerLayoutBinding.pImmutableSamplers = nullptr;
1446 samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
1447
1448 bindings.push_back(samplerLayoutBinding);
1449 }
1450
1451 VkDescriptorSetLayoutCreateInfo layoutInfo{};
1452 layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
1453 layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
1454 layoutInfo.pBindings = bindings.data();
1455
1456 VkDescriptorSetLayout result;
1457 if (vkCreateDescriptorSetLayout(mLogicalDevice, &layoutInfo, nullptr, &result) != VK_SUCCESS)
1458 {
1459 LOG_ERROR("failed to create descriptor set layout!");
1460 throw std::runtime_error("failed to create descriptor set layout!");
1461 }
1462
1463 return result;
1464}

◆ CreateGraphicsPipeline()

std::pair< VkPipelineLayout, VkPipeline > rendering_engine::VulkanRenderer::CreateGraphicsPipeline ( Material *  material,
VkDescriptorSetLayout &  descriptorSetLayout,
std::vector< char > &  spvVertShaderCode,
std::vector< char > &  spvFragShaderCode 
)

Creates a Vulkan graphics pipeline based on material and shader inputs.

Parameters
materialPointer to the material configuration.
descriptorSetLayoutDescriptor set layout used in the pipeline.
spvVertShaderCodeCompiled SPIR-V vertex shader bytecode.
spvFragShaderCodeCompiled SPIR-V fragment shader bytecode.
Returns
A pair containing pipeline layout and pipeline handle.

Definition at line 1471 of file vulkan_renderer.cpp.

1472{
1473 VkPipelineLayout pipelineLayout;
1474 VkPipeline pipeline;
1475
1476 VkShaderModule vertShaderModule = CreateShaderModule(spvVertShaderCode);
1477 VkShaderModule fragShaderModule = CreateShaderModule(spvFragShaderCode);
1478
1479 VkPipelineShaderStageCreateInfo vertShaderStageInfo{};
1480 vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
1481 vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
1482
1483 vertShaderStageInfo.module = vertShaderModule;
1484 vertShaderStageInfo.pName = "main";
1485
1486 VkPipelineShaderStageCreateInfo fragShaderStageInfo{};
1487 fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
1488 fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
1489 fragShaderStageInfo.module = fragShaderModule;
1490 fragShaderStageInfo.pName = "main";
1491
1492 VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo };
1493
1494 VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
1495 vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
1496
1497 VkVertexInputBindingDescription bindingDescription;
1498 std::vector<VkVertexInputAttributeDescription> attributeDescriptions{};
1499
1500 if (material->GetMaterialSettings().materialDomain == MaterialDomain::Sprite2D)
1501 {
1502 bindingDescription = GetBindingDescription<Vertex2D>();
1503 attributeDescriptions = GetAttributeDescriptions<Vertex2D>();
1504 }
1505 if (material->GetMaterialSettings().materialDomain == MaterialDomain::Surface3D)
1506 {
1507 bindingDescription = GetBindingDescription<VertexPositionColorTextureNormalTangent>();
1508 attributeDescriptions = GetAttributeDescriptions<VertexPositionColorTextureNormalTangent>();
1509 }
1510
1511 vertexInputInfo.vertexBindingDescriptionCount = 1;
1512 vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size());
1513
1514 vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
1515 vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();
1516
1517 VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
1518 inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
1519 inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
1520 inputAssembly.primitiveRestartEnable = VK_FALSE;
1521
1522 VkViewport viewport{};
1523 viewport.x = 0.0f;
1524 viewport.y = 0.0f;
1525 viewport.width = (float)mSwapChainExtent.width;
1526 viewport.height = (float)mSwapChainExtent.height;
1527 viewport.minDepth = 0.0f;
1528 viewport.maxDepth = 1.0f;
1529
1530 VkRect2D scissor{};
1531 scissor.offset = { 0, 0 };
1532 scissor.extent = mSwapChainExtent;
1533
1534 VkPipelineViewportStateCreateInfo viewportState{};
1535 viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
1536 viewportState.viewportCount = 1;
1537 viewportState.pViewports = &viewport;
1538 viewportState.scissorCount = 1;
1539 viewportState.pScissors = &scissor;
1540
1541 VkPipelineRasterizationStateCreateInfo rasterizer{};
1542 rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
1543 rasterizer.depthClampEnable = VK_FALSE;
1544 rasterizer.rasterizerDiscardEnable = VK_FALSE;
1545 rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
1546 rasterizer.lineWidth = 1.0f;
1547 if (material->GetMaterialSettings().materialDomain == MaterialDomain::Sprite2D)
1548 {
1549 rasterizer.cullMode = VK_CULL_MODE_NONE;
1550 }
1551 else
1552 {
1553 rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
1554 }
1555 rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
1556 rasterizer.depthBiasEnable = VK_FALSE;
1557 rasterizer.depthBiasConstantFactor = 0.0f; // Optional
1558 rasterizer.depthBiasClamp = 0.0f; // Optional
1559 rasterizer.depthBiasSlopeFactor = 0.0f; // Optional
1560
1561 VkPipelineMultisampleStateCreateInfo multisampling{};
1562 multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
1563 multisampling.sampleShadingEnable = VK_FALSE;
1564 multisampling.rasterizationSamples = mMSAASamples;
1565 multisampling.minSampleShading = 1.0f; // Optional
1566 multisampling.pSampleMask = nullptr; // Optional
1567 multisampling.alphaToCoverageEnable = VK_FALSE; // Optional
1568 multisampling.alphaToOneEnable = VK_FALSE; // Optional
1569
1570 VkPipelineColorBlendAttachmentState colorBlendAttachment{};
1571 colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
1572 colorBlendAttachment.blendEnable = (material->GetMaterialSettings().blendMode == BlendMode::Translucent) ? VK_TRUE : VK_FALSE;
1573 colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
1574 colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
1575 colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; // Optional
1576 colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
1577 colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
1578 colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; // Optional
1579
1580 VkPipelineColorBlendStateCreateInfo colorBlending{};
1581 colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
1582 colorBlending.logicOpEnable = VK_FALSE;
1583 colorBlending.logicOp = VK_LOGIC_OP_COPY; // Optional
1584 colorBlending.attachmentCount = 1;
1585 colorBlending.pAttachments = &colorBlendAttachment;
1586 colorBlending.blendConstants[0] = 0.0f; // Optional
1587 colorBlending.blendConstants[1] = 0.0f; // Optional
1588 colorBlending.blendConstants[2] = 0.0f; // Optional
1589 colorBlending.blendConstants[3] = 0.0f; // Optional
1590
1591 VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
1592 pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
1593 pipelineLayoutInfo.setLayoutCount = 1;
1594 pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout;
1595
1596 if (vkCreatePipelineLayout(mLogicalDevice, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS)
1597 {
1598 LOG_ERROR("failed to create pipeline layout!");
1599 throw std::runtime_error("failed to create pipeline layout!");
1600 }
1601
1602 VkPipelineDepthStencilStateCreateInfo depthStencil{};
1603 depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
1604 if (material->GetMaterialSettings().materialDomain == MaterialDomain::Sprite2D)
1605 {
1606 depthStencil.depthTestEnable = VK_FALSE;
1607 depthStencil.depthWriteEnable = VK_FALSE;
1608 }
1609 else
1610 {
1611 depthStencil.depthTestEnable = VK_TRUE;
1612 depthStencil.depthWriteEnable = (material->GetMaterialSettings().blendMode == BlendMode::Opaque) ? VK_TRUE : VK_FALSE;
1613 }
1614 depthStencil.depthCompareOp = VK_COMPARE_OP_LESS;
1615 depthStencil.depthBoundsTestEnable = VK_FALSE;
1616 //depthStencil.minDepthBounds = 0.0f; // Optional
1617 //depthStencil.maxDepthBounds = 1.0f; // Optional
1618 depthStencil.stencilTestEnable = VK_FALSE;
1619 //depthStencil.front = {}; // Optional
1620 depthStencil.back = {}; // Optional
1621
1622 VkGraphicsPipelineCreateInfo pipelineInfo{};
1623 pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
1624 pipelineInfo.stageCount = 2;
1625 pipelineInfo.pStages = shaderStages;
1626 pipelineInfo.pVertexInputState = &vertexInputInfo;
1627 pipelineInfo.pInputAssemblyState = &inputAssembly;
1628 pipelineInfo.pViewportState = &viewportState;
1629 pipelineInfo.pRasterizationState = &rasterizer;
1630 pipelineInfo.pMultisampleState = &multisampling;
1631 pipelineInfo.pDepthStencilState = &depthStencil;
1632 pipelineInfo.pColorBlendState = &colorBlending;
1633
1634 // To enable dynamic viewport/scissor state:
1635 // const std::vector<VkDynamicState> dynamicStates = {
1636 // VK_DYNAMIC_STATE_VIEWPORT,
1637 // VK_DYNAMIC_STATE_SCISSOR
1638 // };
1639 // VkPipelineDynamicStateCreateInfo dynamicState{};
1640 // dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
1641 // dynamicState.dynamicStateCount = static_cast<uint32_t>(dynamicStates.size());
1642 // dynamicState.pDynamicStates = dynamicStates.data();
1643 // Then set: pipelineInfo.pDynamicState = &dynamicState;
1644 pipelineInfo.pDynamicState = nullptr;
1645
1646 pipelineInfo.layout = pipelineLayout;
1647 pipelineInfo.renderPass = mRenderPass;
1648 pipelineInfo.subpass = 0;
1649 pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; // Optional
1650 pipelineInfo.basePipelineIndex = -1; // Optional
1651
1652
1653 if (vkCreateGraphicsPipelines(mLogicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline) != VK_SUCCESS)
1654 {
1655 LOG_ERROR("failed to create graphics pipeline!");
1656 throw std::runtime_error("failed to create graphics pipeline!");
1657 }
1658 vkDestroyShaderModule(mLogicalDevice, fragShaderModule, nullptr);
1659 vkDestroyShaderModule(mLogicalDevice, vertShaderModule, nullptr);
1660
1661 return std::pair<VkPipelineLayout, VkPipeline>(pipelineLayout, pipeline);
1662}

◆ DrawFrame()

void rendering_engine::VulkanRenderer::DrawFrame ( )
overridevirtual

Executes a full frame rendering cycle.

Implements rendering_engine::IRenderer.

Definition at line 69 of file vulkan_renderer.cpp.

70{
71 vkWaitForFences(mLogicalDevice, 1, &mInFlightFences[mCurrentFrame], VK_TRUE, UINT64_MAX);
72
73 uint32_t imageIndex;
74 VkResult result = vkAcquireNextImageKHR(mLogicalDevice, mSwapChain, UINT64_MAX, mImageAvailableSemaphores[mCurrentFrame], VK_NULL_HANDLE, &imageIndex);
75
76 if (result == VK_ERROR_OUT_OF_DATE_KHR)
77 {
78 LOG_WARNING("Swapchain out of date. Triggering recreation.");
79 RecreateSwapChain();
80 return;
81 }
82 else
83 {
84 if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR)
85 {
86 LOG_ERROR("failed to acquire swap chain image!");
87 throw std::runtime_error("failed to acquire swap chain image!");
88 }
89 }
90
91 vkResetFences(mLogicalDevice, 1, &mInFlightFences[mCurrentFrame]);
92
93 vkResetCommandBuffer(mCommandBuffers[mCurrentFrame], /*VkCommandBufferResetFlagBits*/ 0);
94 RecordCommandBuffer(mCommandBuffers[mCurrentFrame], imageIndex);
95
96 VkSubmitInfo submitInfo{};
97 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
98
99 VkSemaphore waitSemaphores[] = { mImageAvailableSemaphores[mCurrentFrame] };
100 VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
101 submitInfo.waitSemaphoreCount = 1;
102 submitInfo.pWaitSemaphores = waitSemaphores;
103 submitInfo.pWaitDstStageMask = waitStages;
104
105 submitInfo.commandBufferCount = 1;
106 submitInfo.pCommandBuffers = &mCommandBuffers[mCurrentFrame];
107
108 VkSemaphore signalSemaphores[] = { mRenderFinishedSemaphores[imageIndex] };
109 submitInfo.signalSemaphoreCount = 1;
110 submitInfo.pSignalSemaphores = signalSemaphores;
111
112 if (vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, mInFlightFences[mCurrentFrame]) != VK_SUCCESS)
113 {
114 LOG_ERROR("failed to submit draw command buffer!");
115 throw std::runtime_error("failed to submit draw command buffer!");
116 }
117
118 VkPresentInfoKHR presentInfo{};
119 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
120
121 presentInfo.waitSemaphoreCount = 1;
122 presentInfo.pWaitSemaphores = signalSemaphores;
123
124 VkSwapchainKHR swapChains[] = { mSwapChain };
125 presentInfo.swapchainCount = 1;
126 presentInfo.pSwapchains = swapChains;
127
128 presentInfo.pImageIndices = &imageIndex;
129
130 result = vkQueuePresentKHR(mPresentQueue, &presentInfo);
131
132 if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || mWindowSystem.IsFramebufferResized())
133 {
134 mWindowSystem.ResetFramebufferResizedFlag();
135 RecreateSwapChain();
136 }
137 else
138 {
139 if (result != VK_SUCCESS)
140 {
141 LOG_ERROR("failed to present swap chain image!");
142 throw std::runtime_error("failed to present swap chain image!");
143 }
144 }
145
146 mCurrentFrame = (mCurrentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
147}
virtual void ResetFramebufferResizedFlag()=0
Resets the framebuffer resized flag after handling a resize event.
virtual bool IsFramebufferResized() const =0
Checks if the framebuffer has been resized since the last frame.
#define LOG_WARNING(msg)
Definition logger.hpp:40

◆ EndFrame()

void rendering_engine::VulkanRenderer::EndFrame ( )
overridevirtual

Completes the current frame rendering and presents the result.

Implements rendering_engine::IRenderer.

Definition at line 219 of file vulkan_renderer.cpp.

220{
221 if (vkEndCommandBuffer(mCommandBuffers[mCurrentFrame]) != VK_SUCCESS)
222 {
223 LOG_ERROR("failed to record command buffer!");
224 throw std::runtime_error("failed to record command buffer!");
225 }
226
227 VkSubmitInfo submitInfo{};
228 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
229
230 VkSemaphore waitSemaphores[] = { mImageAvailableSemaphores[mCurrentFrame] };
231 VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
232 submitInfo.waitSemaphoreCount = 1;
233 submitInfo.pWaitSemaphores = waitSemaphores;
234 submitInfo.pWaitDstStageMask = waitStages;
235
236 submitInfo.commandBufferCount = 1;
237 submitInfo.pCommandBuffers = &mCommandBuffers[mCurrentFrame];
238
239 VkSemaphore signalSemaphores[] = { mRenderFinishedSemaphores[mImageIndex] };
240 submitInfo.signalSemaphoreCount = 1;
241 submitInfo.pSignalSemaphores = signalSemaphores;
242
243 if (vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, mInFlightFences[mCurrentFrame]) != VK_SUCCESS)
244 {
245 LOG_ERROR("failed to submit draw command buffer!");
246 throw std::runtime_error("failed to submit draw command buffer!");
247 }
248
249 VkPresentInfoKHR presentInfo{};
250 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
251
252 presentInfo.waitSemaphoreCount = 1;
253 presentInfo.pWaitSemaphores = signalSemaphores;
254
255 VkSwapchainKHR swapChains[] = { mSwapChain };
256 presentInfo.swapchainCount = 1;
257 presentInfo.pSwapchains = swapChains;
258
259 presentInfo.pImageIndices = &mImageIndex;
260
261 VkResult result = vkQueuePresentKHR(mPresentQueue, &presentInfo);
262
263 if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || mWindowSystem.IsFramebufferResized())
264 {
265 mWindowSystem.ResetFramebufferResizedFlag();
266 RecreateSwapChain();
267 }
268 else
269 {
270 if (result != VK_SUCCESS)
271 {
272 LOG_ERROR("failed to present swap chain image!");
273 throw std::runtime_error("failed to present swap chain image!");
274 }
275 }
276
277 mCurrentFrame = (mCurrentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
278}

◆ EndRenderPass()

void rendering_engine::VulkanRenderer::EndRenderPass ( )
overridevirtual

Ends the active render pass.

Implements rendering_engine::IRenderer.

Definition at line 214 of file vulkan_renderer.cpp.

215{
216 vkCmdEndRenderPass(mCommandBuffers[mCurrentFrame]);
217}

◆ FindMemoryType()

uint32_t rendering_engine::VulkanRenderer::FindMemoryType ( uint32_t  typeFilter,
VkMemoryPropertyFlags  properties 
)

Finds a suitable memory type for a given allocation.

Parameters
typeFilterBitmask of compatible memory types.
propertiesDesired memory property flags.
Returns
Index of the selected memory type.

Definition at line 1381 of file vulkan_renderer.cpp.

1382{
1383 VkPhysicalDeviceMemoryProperties memProperties;
1384 vkGetPhysicalDeviceMemoryProperties(mPhysicalDevice, &memProperties);
1385
1386 for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++)
1387 {
1388 if (typeFilter & (1 << i) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties)
1389 {
1390 return i;
1391 }
1392 }
1393 LOG_ERROR("failed to find suitable memory type!");
1394 throw std::runtime_error("failed to find suitable memory type!");
1395}

◆ GenerateMipmaps()

void rendering_engine::VulkanRenderer::GenerateMipmaps ( VkImage  image,
VkFormat  imageFormat,
int32_t  texWidth,
int32_t  texHeight,
uint32_t  mipLevels 
)

Generates mipmaps for a given image resource.

Parameters
imageVulkan image handle.
imageFormatImage format (must support linear blitting).
texWidthTexture width in pixels.
texHeightTexture height in pixels.
mipLevelsNumber of mipmap levels to generate.

Definition at line 507 of file vulkan_renderer.cpp.

508{
509 // Check if image format supports linear blitting
510 VkFormatProperties formatProperties;
511 vkGetPhysicalDeviceFormatProperties(mPhysicalDevice, imageFormat, &formatProperties);
512
513 if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT))
514 {
515 LOG_ERROR("texture image format does not support linear blitting!");
516 throw std::runtime_error("texture image format does not support linear blitting!");
517 }
518
519 VkCommandBuffer commandBuffer = BeginSingleTimeCommand();
520
521 VkImageMemoryBarrier barrier{};
522 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
523 barrier.image = image;
524 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
525 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
526 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
527 barrier.subresourceRange.baseArrayLayer = 0;
528 barrier.subresourceRange.layerCount = 1;
529 barrier.subresourceRange.levelCount = 1;
530
531 int32_t mipWidth = texWidth;
532 int32_t mipHeight = texHeight;
533
534 for (uint32_t i = 1; i < mipLevels; i++)
535 {
536 barrier.subresourceRange.baseMipLevel = i - 1;
537 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
538 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
539 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
540 barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
541
542 vkCmdPipelineBarrier(commandBuffer,
543 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
544 0, nullptr,
545 0, nullptr,
546 1, &barrier);
547
548 VkImageBlit blit{};
549 blit.srcOffsets[0] = { 0, 0, 0 };
550 blit.srcOffsets[1] = { mipWidth, mipHeight, 1 };
551 blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
552 blit.srcSubresource.mipLevel = i - 1;
553 blit.srcSubresource.baseArrayLayer = 0;
554 blit.srcSubresource.layerCount = 1;
555 blit.dstOffsets[0] = { 0, 0, 0 };
556 blit.dstOffsets[1] = { mipWidth > 1 ? mipWidth / 2 : 1, mipHeight > 1 ? mipHeight / 2 : 1, 1 };
557 blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
558 blit.dstSubresource.mipLevel = i;
559 blit.dstSubresource.baseArrayLayer = 0;
560 blit.dstSubresource.layerCount = 1;
561
562 vkCmdBlitImage(commandBuffer,
563 image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
564 image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
565 1, &blit,
566 VK_FILTER_LINEAR);
567
568 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
569 barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
570 barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
571 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
572
573 vkCmdPipelineBarrier(commandBuffer,
574 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
575 0, nullptr,
576 0, nullptr,
577 1, &barrier);
578
579 if (mipWidth > 1) mipWidth /= 2;
580 if (mipHeight > 1) mipHeight /= 2;
581 }
582
583 barrier.subresourceRange.baseMipLevel = mipLevels - 1;
584 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
585 barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
586 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
587 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
588
589 vkCmdPipelineBarrier(commandBuffer,
590 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
591 0, nullptr,
592 0, nullptr,
593 1, &barrier);
594
595 EndSingleTimeCommand(commandBuffer);
596}

◆ GetAttributeDescriptions() [1/7]

template<typename T >
static std::vector< VkVertexInputAttributeDescription > rendering_engine::VulkanRenderer::GetAttributeDescriptions ( )
static

Returns vertex attribute descriptions for a specific vertex type.

Template Parameters
TVertex type (e.g., Vertex2D, VertexPositionColorTexture, etc.).

◆ GetAttributeDescriptions() [2/7]

template<>
std::vector< VkVertexInputAttributeDescription > rendering_engine::VulkanRenderer::GetAttributeDescriptions ( )
static

◆ GetAttributeDescriptions() [3/7]

template<>
std::vector< VkVertexInputAttributeDescription > rendering_engine::VulkanRenderer::GetAttributeDescriptions ( )
static

◆ GetAttributeDescriptions() [4/7]

template<>
std::vector< VkVertexInputAttributeDescription > rendering_engine::VulkanRenderer::GetAttributeDescriptions ( )
static

◆ GetAttributeDescriptions() [5/7]

template<>
std::vector< VkVertexInputAttributeDescription > rendering_engine::VulkanRenderer::GetAttributeDescriptions ( )
static

Definition at line 1922 of file vulkan_renderer.cpp.

1933{
1934 std::vector<VkVertexInputAttributeDescription> attributeDescriptions{};
1935 attributeDescriptions.resize(3);
1936
1937 attributeDescriptions[0].binding = 0;
1938 attributeDescriptions[0].location = 0;
1939 attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT;
1940 attributeDescriptions[0].offset = offsetof(Vertex2D, position);
1941
1942 attributeDescriptions[1].binding = 0;
1943 attributeDescriptions[1].location = 1;
1944 attributeDescriptions[1].format = VK_FORMAT_R32G32B32A32_SFLOAT;
1945 attributeDescriptions[1].offset = offsetof(Vertex2D, color);
1946
1947 attributeDescriptions[2].binding = 0;
1948 attributeDescriptions[2].location = 2;
1949 attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
1950 attributeDescriptions[2].offset = offsetof(Vertex2D, textureCoordinates);
1951
1952 return attributeDescriptions;
1953}

◆ GetAttributeDescriptions() [6/7]

template<>
std::vector< VkVertexInputAttributeDescription > rendering_engine::VulkanRenderer::GetAttributeDescriptions ( )
static

Definition at line 1922 of file vulkan_renderer.cpp.

1957{
1958 std::vector<VkVertexInputAttributeDescription> attributeDescriptions{};
1959 attributeDescriptions.resize(3);
1960
1961 attributeDescriptions[0].binding = 0;
1962 attributeDescriptions[0].location = 0;
1963 attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
1964 attributeDescriptions[0].offset = offsetof(VertexPositionColorTexture, position);
1965
1966 attributeDescriptions[1].binding = 0;
1967 attributeDescriptions[1].location = 1;
1968 attributeDescriptions[1].format = VK_FORMAT_R32G32B32A32_SFLOAT;
1969 attributeDescriptions[1].offset = offsetof(VertexPositionColorTexture, color);
1970
1971 attributeDescriptions[2].binding = 0;
1972 attributeDescriptions[2].location = 2;
1973 attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
1974 attributeDescriptions[2].offset = offsetof(VertexPositionColorTexture, textureCoordinates);
1975
1976 return attributeDescriptions;
1977}

◆ GetAttributeDescriptions() [7/7]

template<>
std::vector< VkVertexInputAttributeDescription > rendering_engine::VulkanRenderer::GetAttributeDescriptions ( )
static

Definition at line 1922 of file vulkan_renderer.cpp.

1981{
1982 std::vector<VkVertexInputAttributeDescription> attributeDescriptions{};
1983 attributeDescriptions.resize(5);
1984
1985 attributeDescriptions[0].binding = 0;
1986 attributeDescriptions[0].location = 0;
1987 attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
1988 attributeDescriptions[0].offset = offsetof(VertexPositionColorTextureNormalTangent, position);
1989
1990 attributeDescriptions[1].binding = 0;
1991 attributeDescriptions[1].location = 1;
1992 attributeDescriptions[1].format = VK_FORMAT_R32G32B32A32_SFLOAT;
1993 attributeDescriptions[1].offset = offsetof(VertexPositionColorTextureNormalTangent, color);
1994
1995 attributeDescriptions[2].binding = 0;
1996 attributeDescriptions[2].location = 2;
1997 attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
1998 attributeDescriptions[2].offset = offsetof(VertexPositionColorTextureNormalTangent, textureCoordinates);
1999
2000 attributeDescriptions[3].binding = 0;
2001 attributeDescriptions[3].location = 3;
2002 attributeDescriptions[3].format = VK_FORMAT_R32G32B32_SFLOAT;
2003 attributeDescriptions[3].offset = offsetof(VertexPositionColorTextureNormalTangent, normal);
2004
2005 attributeDescriptions[4].binding = 0;
2006 attributeDescriptions[4].location = 4;
2007 attributeDescriptions[4].format = VK_FORMAT_R32G32B32_SFLOAT;
2008 attributeDescriptions[4].offset = offsetof(VertexPositionColorTextureNormalTangent, tangent);
2009
2010 return attributeDescriptions;
2011}

◆ GetBindingDescription()

template<typename T >
VkVertexInputBindingDescription rendering_engine::VulkanRenderer::GetBindingDescription ( )
static

Returns a vertex input binding description for a specific vertex type.

Template Parameters
TVertex type (e.g., Vertex2D, VertexPositionColorTexture, etc.).

Definition at line 1922 of file vulkan_renderer.cpp.

1923{
1924 VkVertexInputBindingDescription binding{};
1925 binding.binding = 0;
1926 binding.stride = sizeof(T);
1927 binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
1928 return binding;
1929}

◆ GetComandBuffers()

std::vector< VkCommandBuffer > rendering_engine::VulkanRenderer::GetComandBuffers ( )

Returns the collection of command buffers used for rendering.

Definition at line 1466 of file vulkan_renderer.cpp.

1467{
1468 return mCommandBuffers;
1469}

◆ GetCurrentFrame()

size_t rendering_engine::VulkanRenderer::GetCurrentFrame ( ) const
inline

Returns the index of the currently active frame in flight.

Definition at line 213 of file vulkan_renderer.hpp.

214 {
215 return mCurrentFrame;
216 }

◆ GetLogicalDevice()

VkDevice & rendering_engine::VulkanRenderer::GetLogicalDevice ( )

Returns reference to the logical Vulkan device.

Definition at line 396 of file vulkan_renderer.cpp.

397{
398 return mLogicalDevice;
399}

◆ GetPhysDevSupportedFeatures()

VkPhysicalDeviceFeatures & rendering_engine::VulkanRenderer::GetPhysDevSupportedFeatures ( )

Returns reference to the physical device�s supported features.

Definition at line 1402 of file vulkan_renderer.cpp.

1403{
1404 return mPhysDevSupportedFeatures;
1405}

◆ GetPhysicalDevice()

VkPhysicalDevice & rendering_engine::VulkanRenderer::GetPhysicalDevice ( )

Returns reference to the physical device.

Definition at line 1397 of file vulkan_renderer.cpp.

1398{
1399 return mPhysicalDevice;
1400}

◆ InitializeRenderer()

void rendering_engine::VulkanRenderer::InitializeRenderer ( )
overridevirtual

Initializes all rendering subsystems and GPU resources.

Implements rendering_engine::IRenderer.

Definition at line 45 of file vulkan_renderer.cpp.

46{
47 CreateInstance();
48 SetupDebugMessenger();
49 CreateSurface();
50 PickPhysicalDevice();
51 CreateLogicalDevice();
52
53 CreateSwapChain();
54 CreateImageViews();
55 CreateRenderPass();
56
57 CreateCommandPool();
58
59 CreateColorResources();
60 CreateDepthResources();
61
62 CreateFramebuffers();
63
64 CreateCommandBuffers();
65 CreateFrameSyncObjects();
66 CreateSwapchainSyncObjects();
67}

◆ ProvideMaterialRenderResources()

IMaterialRenderResources * rendering_engine::VulkanRenderer::ProvideMaterialRenderResources ( ) const
overridevirtual

Provides access to material-related GPU resources.

Returns
Pointer to the IMaterialRenderResources interface.

Implements rendering_engine::IRenderer.

Definition at line 331 of file vulkan_renderer.cpp.

332{
333 return new VulkanMaterialResources(const_cast<VulkanRenderer*>(this));
334}
VulkanRenderer(IWindowSystem &windowSystem)
Constructs a VulkanRenderer bound to a specific window system.

◆ ProvideMeshRenderResources()

IMeshRenderResources * rendering_engine::VulkanRenderer::ProvideMeshRenderResources ( ) const
overridevirtual

Provides access to mesh-related GPU resources.

Returns
Pointer to the IMeshRenderResources interface.

Implements rendering_engine::IRenderer.

Definition at line 336 of file vulkan_renderer.cpp.

337{
338 return new VulkanMeshResources(const_cast<VulkanRenderer*>(this));
339}

◆ ProvideRenderResources()

IRenderResources * rendering_engine::VulkanRenderer::ProvideRenderResources ( ) const
overridevirtual

Provides access to the general rendering resource manager.

Returns
Pointer to the IRenderResources interface.

Implements rendering_engine::IRenderer.

Definition at line 346 of file vulkan_renderer.cpp.

347{
348 return new VulkanRenderResources(const_cast<VulkanRenderer*>(this));
349}

◆ ProvideTextureRenderResources()

ITextureRenderResources * rendering_engine::VulkanRenderer::ProvideTextureRenderResources ( ) const
overridevirtual

Provides access to texture-related GPU resources.

Returns
Pointer to the ITextureRenderResources interface.

Implements rendering_engine::IRenderer.

Definition at line 326 of file vulkan_renderer.cpp.

327{
328 return new VulkanTextureResources(const_cast<VulkanRenderer*>(this));
329}

◆ RegisterObserver()

void rendering_engine::VulkanRenderer::RegisterObserver ( IRendererObserver *  notifier)
overridevirtual

Registers an observer for rendering events.

Parameters
notifierPointer to the observer to register.

Implements rendering_engine::IRenderer.

Definition at line 313 of file vulkan_renderer.cpp.

314{
315 if (std::find(mObservers.begin(), mObservers.end(), notifier) == mObservers.end())
316 {
317 mObservers.push_back(notifier);
318 }
319}

◆ SetDefaultColor()

void rendering_engine::VulkanRenderer::SetDefaultColor ( float  r,
float  g,
float  b 
)
overridevirtual

Sets the renderer clear color.

Parameters
rRed channel (linear space, [0.0, 1.0])
gGreen channel (linear space, [0.0, 1.0])
bBlue channel (linear space, [0.0, 1.0])

Implements rendering_engine::IRenderer.

Definition at line 341 of file vulkan_renderer.cpp.

342{
343 mDefaultColor = glm::vec3(r, g, b);
344}

◆ ShutdownRenderer()

void rendering_engine::VulkanRenderer::ShutdownRenderer ( )
overridevirtual

Destroys and cleans up all rendering resources.

Implements rendering_engine::IRenderer.

Definition at line 285 of file vulkan_renderer.cpp.

286{
287 LOG_INFO("Shutting down Vulkan renderer...");
288 vkDeviceWaitIdle(mLogicalDevice);
289
290 mFrameSerial = std::numeric_limits<uint64_t>::max();
291 ProcessDeferredDestruction();
292
293 CleanupSwapChain();
294
295 for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
296 {
297 vkDestroySemaphore(mLogicalDevice, mImageAvailableSemaphores[i], nullptr);
298 vkDestroyFence(mLogicalDevice, mInFlightFences[i], nullptr);
299 }
300
301 vkDestroyCommandPool(mLogicalDevice, mCommandPool, nullptr);
302
303 vkDestroyDevice(mLogicalDevice, nullptr);
305 {
306 DestroyDebugUtilsMessengerEXT(mInstance, mDebugMessenger, nullptr);
307 }
308 vkDestroySurfaceKHR(mInstance, mSurface, nullptr);
309 vkDestroyInstance(mInstance, nullptr);
310 LOG_INFO("Vulkan renderer shutdown complete.");
311}
#define LOG_INFO(msg)
Definition logger.hpp:39
const bool enableValidationLayers

◆ TransitionImageLayout()

void rendering_engine::VulkanRenderer::TransitionImageLayout ( VkImage  image,
VkFormat  format,
VkImageLayout  oldLayout,
VkImageLayout  newLayout,
std::uint32_t  mipmapLevels 
)

Transitions the image layout for a given Vulkan image.

Parameters
imageImage handle.
formatImage format.
oldLayoutCurrent image layout.
newLayoutDesired image layout.
mipmapLevelsNumber of mipmap levels to transition.

Definition at line 401 of file vulkan_renderer.cpp.

402{
403 VkCommandBuffer commandBuffer = BeginSingleTimeCommand();
404
405 VkImageMemoryBarrier barrier{};
406 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
407 barrier.oldLayout = oldLayout;
408 barrier.newLayout = newLayout;
409
410 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
411 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
412
413 barrier.image = image;
414 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
415 barrier.subresourceRange.baseMipLevel = 0;
416 barrier.subresourceRange.levelCount = mipmapLevels;
417 barrier.subresourceRange.baseArrayLayer = 0;
418 barrier.subresourceRange.layerCount = 1;
419
420 VkPipelineStageFlags sourceStage;
421 VkPipelineStageFlags destinationStage;
422
423 if (newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL)
424 {
425 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
426
427 if (HasStencilComponent(format))
428 {
429 barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
430 }
431 }
432 else
433 {
434 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
435 }
436
437 if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
438 barrier.srcAccessMask = 0;
439 barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
440
441 sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
442 destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
443 }
444 else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
445 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
446 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
447
448 sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
449 destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
450 }
451 else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
452 barrier.srcAccessMask = 0;
453 barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
454
455 sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
456 destinationStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
457 }
458 else {
459 throw std::invalid_argument("unsupported layout transition!");
460 }
461
462 vkCmdPipelineBarrier(
463 commandBuffer,
464 sourceStage, destinationStage,
465 0,
466 0, nullptr,
467 0, nullptr,
468 1, &barrier
469 );
470
471 EndSingleTimeCommand(commandBuffer);
472}

◆ UnregisterObserver()

void rendering_engine::VulkanRenderer::UnregisterObserver ( IRendererObserver *  notifier)
overridevirtual

Unregisters a previously registered observer.

Parameters
notifierPointer to the observer to remove.

Implements rendering_engine::IRenderer.

Definition at line 321 of file vulkan_renderer.cpp.

322{
323 mObservers.erase(std::remove(mObservers.begin(), mObservers.end(), notifier), mObservers.end());
324}

◆ WaitIdle()

void rendering_engine::VulkanRenderer::WaitIdle ( )
overridevirtual

Waits until the GPU has finished all pending rendering operations.

Implements rendering_engine::IRenderer.

Definition at line 280 of file vulkan_renderer.cpp.

281{
282 vkDeviceWaitIdle(mLogicalDevice);
283}

The documentation for this class was generated from the following files: