Rendering Engine 0.2.0
Modular Graphics Rendering Engine | v0.2.0
Loading...
Searching...
No Matches
vulkan_renderer.cpp
Go to the documentation of this file.
1#include "version.h"
2#include "vulkan_renderer.hpp"
8#include "utility.hpp"
9#include "material.hpp"
10#include "texture_cache.hpp"
11#include <stdexcept>
12#include <iostream>
13#include <set>
14#include <algorithm>
15#include <array>
16
17namespace rendering_engine
18{
19
20#if defined(__FreeBSD__) || defined(NDEBUG)
21 const bool enableValidationLayers = false;
22#else
23 const bool enableValidationLayers = true;
24#endif
25
26template<>
27std::vector<VkVertexInputAttributeDescription>
29
30template<>
31std::vector<VkVertexInputAttributeDescription>
33
34template<>
35std::vector<VkVertexInputAttributeDescription>
37
39 :
40 mWindowSystem{windowSystem},
41 mCurrentFrame{0}
42{}
43
45{
46 CreateInstance();
47 SetupDebugMessenger();
48 CreateSurface();
49 PickPhysicalDevice();
50 CreateLogicalDevice();
51
52 CreateSwapChain();
53 CreateImageViews();
54 CreateRenderPass();
55
56 CreateCommandPool();
57
58 CreateColorResources();
59 CreateDepthResources();
60
61 CreateFramebuffers();
62
63 CreateCommandBuffers();
64 CreateSyncObjects();
65}
66
68{
69 vkWaitForFences(mLogicalDevice, 1, &mInFlightFences[mCurrentFrame], VK_TRUE, UINT64_MAX);
70
71 uint32_t imageIndex;
72 VkResult result = vkAcquireNextImageKHR(mLogicalDevice, mSwapChain, UINT64_MAX, mImageAvailableSemaphores[mCurrentFrame], VK_NULL_HANDLE, &imageIndex);
73
74 if (result == VK_ERROR_OUT_OF_DATE_KHR)
75 {
76 RecreateSwapChain();
77 return;
78 }
79 else
80 {
81 if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR)
82 {
83 throw std::runtime_error("failed to acquire swap chain image!");
84 }
85 }
86
87 vkResetFences(mLogicalDevice, 1, &mInFlightFences[mCurrentFrame]);
88
89 vkResetCommandBuffer(mCommandBuffers[mCurrentFrame], /*VkCommandBufferResetFlagBits*/ 0);
90 RecordCommandBuffer(mCommandBuffers[mCurrentFrame], imageIndex);
91
92 VkSubmitInfo submitInfo{};
93 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
94
95 VkSemaphore waitSemaphores[] = { mImageAvailableSemaphores[mCurrentFrame] };
96 VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
97 submitInfo.waitSemaphoreCount = 1;
98 submitInfo.pWaitSemaphores = waitSemaphores;
99 submitInfo.pWaitDstStageMask = waitStages;
100
101 submitInfo.commandBufferCount = 1;
102 submitInfo.pCommandBuffers = &mCommandBuffers[mCurrentFrame];
103
104 VkSemaphore signalSemaphores[] = { mRenderFinishedSemaphores[mCurrentFrame] };
105 submitInfo.signalSemaphoreCount = 1;
106 submitInfo.pSignalSemaphores = signalSemaphores;
107
108 if (vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, mInFlightFences[mCurrentFrame]) != VK_SUCCESS)
109 {
110 throw std::runtime_error("failed to submit draw command buffer!");
111 }
112
113 VkPresentInfoKHR presentInfo{};
114 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
115
116 presentInfo.waitSemaphoreCount = 1;
117 presentInfo.pWaitSemaphores = signalSemaphores;
118
119 VkSwapchainKHR swapChains[] = { mSwapChain };
120 presentInfo.swapchainCount = 1;
121 presentInfo.pSwapchains = swapChains;
122
123 presentInfo.pImageIndices = &imageIndex;
124
125 result = vkQueuePresentKHR(mPresentQueue, &presentInfo);
126
127 if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || mWindowSystem.IsFramebufferResized())
128 {
129 mWindowSystem.ResetFramebufferResizedFlag();
130 RecreateSwapChain();
131 }
132 else
133 {
134 if (result != VK_SUCCESS)
135 {
136 throw std::runtime_error("failed to present swap chain image!");
137 }
138 }
139
140 mCurrentFrame = (mCurrentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
141}
142
144{
145 vkWaitForFences(mLogicalDevice, 1, &mInFlightFences[mCurrentFrame], VK_TRUE, UINT64_MAX);
146
147 VkResult result = vkAcquireNextImageKHR(mLogicalDevice, mSwapChain, UINT64_MAX, mImageAvailableSemaphores[mCurrentFrame], VK_NULL_HANDLE, &mImageIndex);
148
149 if (result == VK_ERROR_OUT_OF_DATE_KHR)
150 {
151 RecreateSwapChain();
152 return false;
153 }
154 else
155 {
156 if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR)
157 {
158 throw std::runtime_error("failed to acquire swap chain image!");
159 }
160 }
161
162 vkResetFences(mLogicalDevice, 1, &mInFlightFences[mCurrentFrame]);
163
164 vkResetCommandBuffer(mCommandBuffers[mCurrentFrame], /*VkCommandBufferResetFlagBits*/ 0);
165
166 return true;
167}
168
170{
171 VkCommandBufferBeginInfo beginInfo{};
172 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
173 //beginInfo.flags = 0; // Optional
174 //beginInfo.pInheritanceInfo = nullptr; // Optional
175
176 if (vkBeginCommandBuffer(mCommandBuffers[mCurrentFrame], &beginInfo) != VK_SUCCESS)
177 {
178 throw std::runtime_error("failed to begin recording command buffer!");
179 }
180
181 VkRenderPassBeginInfo renderPassInfo{};
182 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
183 renderPassInfo.renderPass = mRenderPass;
184 renderPassInfo.framebuffer = mSwapChainFramebuffers[mImageIndex];
185
186 renderPassInfo.renderArea.offset = { 0, 0 };
187 renderPassInfo.renderArea.extent = mSwapChainExtent;
188
189 std::array<VkClearValue, 2> clearValues{};
190 clearValues[0].color = { {0.0f, 0.0f, 0.0f, 1.0f} };
191 clearValues[1].depthStencil = { 1.0f, 0 };
192 renderPassInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
193 renderPassInfo.pClearValues = clearValues.data();
194
195 vkCmdBeginRenderPass(mCommandBuffers[mCurrentFrame], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
196}
197
199{
200 vkCmdEndRenderPass(mCommandBuffers[mCurrentFrame]);
201}
202
204{
205 if (vkEndCommandBuffer(mCommandBuffers[mCurrentFrame]) != VK_SUCCESS)
206 {
207 throw std::runtime_error("failed to record command buffer!");
208 }
209
210 VkSubmitInfo submitInfo{};
211 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
212
213 VkSemaphore waitSemaphores[] = { mImageAvailableSemaphores[mCurrentFrame] };
214 VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
215 submitInfo.waitSemaphoreCount = 1;
216 submitInfo.pWaitSemaphores = waitSemaphores;
217 submitInfo.pWaitDstStageMask = waitStages;
218
219 submitInfo.commandBufferCount = 1;
220 submitInfo.pCommandBuffers = &mCommandBuffers[mCurrentFrame];
221
222 VkSemaphore signalSemaphores[] = { mRenderFinishedSemaphores[mCurrentFrame] };
223 submitInfo.signalSemaphoreCount = 1;
224 submitInfo.pSignalSemaphores = signalSemaphores;
225
226 if (vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, mInFlightFences[mCurrentFrame]) != VK_SUCCESS)
227 {
228 throw std::runtime_error("failed to submit draw command buffer!");
229 }
230
231 VkPresentInfoKHR presentInfo{};
232 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
233
234 presentInfo.waitSemaphoreCount = 1;
235 presentInfo.pWaitSemaphores = signalSemaphores;
236
237 VkSwapchainKHR swapChains[] = { mSwapChain };
238 presentInfo.swapchainCount = 1;
239 presentInfo.pSwapchains = swapChains;
240
241 presentInfo.pImageIndices = &mImageIndex;
242
243 VkResult result = vkQueuePresentKHR(mPresentQueue, &presentInfo);
244
245 if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || mWindowSystem.IsFramebufferResized())
246 {
247 mWindowSystem.ResetFramebufferResizedFlag();
248 RecreateSwapChain();
249 }
250 else
251 {
252 if (result != VK_SUCCESS)
253 {
254 throw std::runtime_error("failed to present swap chain image!");
255 }
256 }
257
258 mCurrentFrame = (mCurrentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
259}
260
262{
263 vkDeviceWaitIdle(mLogicalDevice);
264}
265
267{
268 CleanupSwapChain();
269
270 for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
271 {
272 vkDestroySemaphore(mLogicalDevice, mRenderFinishedSemaphores[i], nullptr);
273 vkDestroySemaphore(mLogicalDevice, mImageAvailableSemaphores[i], nullptr);
274 vkDestroyFence(mLogicalDevice, mInFlightFences[i], nullptr);
275 }
276
277 vkDestroyCommandPool(mLogicalDevice, mCommandPool, nullptr);
278
279 vkDestroyDevice(mLogicalDevice, nullptr);
281 {
282 DestroyDebugUtilsMessengerEXT(mInstance, mDebugMessenger, nullptr);
283 }
284 vkDestroySurfaceKHR(mInstance, mSurface, nullptr);
285 vkDestroyInstance(mInstance, nullptr);
286}
287
289{
290 if (std::find(mObservers.begin(), mObservers.end(), notifier) == mObservers.end())
291 {
292 mObservers.push_back(notifier);
293 }
294}
295
297{
298 mObservers.erase(std::remove(mObservers.begin(), mObservers.end(), notifier), mObservers.end());
299}
300
305
310
315
320
321void VulkanRenderer::CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory)
322{
323 VkBufferCreateInfo bufferInfo{};
324 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
325 bufferInfo.size = size;
326
327 bufferInfo.usage = usage;
328 bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
329
330 if (vkCreateBuffer(mLogicalDevice, &bufferInfo, nullptr, &buffer) != VK_SUCCESS)
331 {
332 throw std::runtime_error("failed to create vertex buffer!");
333 }
334
335 VkMemoryRequirements memRequirements;
336 vkGetBufferMemoryRequirements(mLogicalDevice, buffer, &memRequirements);
337
338 VkMemoryAllocateInfo allocInfo{};
339 allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
340 allocInfo.allocationSize = memRequirements.size;
341 allocInfo.memoryTypeIndex = FindMemoryType(memRequirements.memoryTypeBits, properties);
342
343 if (vkAllocateMemory(mLogicalDevice, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS)
344 {
345 throw std::runtime_error("failed to allocate vertex buffer memory!");
346 }
347
348 vkBindBufferMemory(mLogicalDevice, buffer, bufferMemory, 0);
349}
350
351void VulkanRenderer::CopyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size)
352{
353 auto commandBuffer = BeginSingleTimeCommand();
354
355 VkBufferCopy copyRegion{};
356 copyRegion.srcOffset = 0; // Optional
357 copyRegion.dstOffset = 0; // Optional
358 copyRegion.size = size;
359 vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, &copyRegion);
360
361 EndSingleTimeCommand(commandBuffer);
362}
363
365{
366 return mLogicalDevice;
367}
368
369void VulkanRenderer::TransitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, std::uint32_t mipmapLevels)
370{
371 VkCommandBuffer commandBuffer = BeginSingleTimeCommand();
372
373 VkImageMemoryBarrier barrier{};
374 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
375 barrier.oldLayout = oldLayout;
376 barrier.newLayout = newLayout;
377
378 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
379 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
380
381 barrier.image = image;
382 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
383 barrier.subresourceRange.baseMipLevel = 0;
384 barrier.subresourceRange.levelCount = mipmapLevels;
385 barrier.subresourceRange.baseArrayLayer = 0;
386 barrier.subresourceRange.layerCount = 1;
387
388 VkPipelineStageFlags sourceStage;
389 VkPipelineStageFlags destinationStage;
390
391 if (newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL)
392 {
393 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
394
395 if (HasStencilComponent(format))
396 {
397 barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
398 }
399 }
400 else
401 {
402 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
403 }
404
405 if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
406 barrier.srcAccessMask = 0;
407 barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
408
409 sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
410 destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
411 }
412 else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
413 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
414 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
415
416 sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
417 destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
418 }
419 else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
420 barrier.srcAccessMask = 0;
421 barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
422
423 sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
424 destinationStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
425 }
426 else {
427 throw std::invalid_argument("unsupported layout transition!");
428 }
429
430 vkCmdPipelineBarrier(
431 commandBuffer,
432 sourceStage, destinationStage,
433 0,
434 0, nullptr,
435 0, nullptr,
436 1, &barrier
437 );
438
439 EndSingleTimeCommand(commandBuffer);
440}
441
442void VulkanRenderer::CopyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height)
443{
444 VkCommandBuffer commandBuffer = BeginSingleTimeCommand();
445
446 VkBufferImageCopy region{};
447 region.bufferOffset = 0;
448 region.bufferRowLength = 0;
449 region.bufferImageHeight = 0;
450
451 region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
452 region.imageSubresource.mipLevel = 0;
453 region.imageSubresource.baseArrayLayer = 0;
454 region.imageSubresource.layerCount = 1;
455
456 region.imageOffset = { 0, 0, 0 };
457 region.imageExtent = {
458 width,
459 height,
460 1
461 };
462
463 vkCmdCopyBufferToImage(
464 commandBuffer,
465 buffer,
466 image,
467 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
468 1,
469 &region
470 );
471
472 EndSingleTimeCommand(commandBuffer);
473}
474
475void VulkanRenderer::GenerateMipmaps(VkImage image, VkFormat imageFormat, int32_t texWidth, int32_t texHeight, uint32_t mipLevels)
476{
477 // Check if image format supports linear blitting
478 VkFormatProperties formatProperties;
479 vkGetPhysicalDeviceFormatProperties(mPhysicalDevice, imageFormat, &formatProperties);
480
481 if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT))
482 {
483 throw std::runtime_error("texture image format does not support linear blitting!");
484 }
485
486 VkCommandBuffer commandBuffer = BeginSingleTimeCommand();
487
488 VkImageMemoryBarrier barrier{};
489 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
490 barrier.image = image;
491 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
492 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
493 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
494 barrier.subresourceRange.baseArrayLayer = 0;
495 barrier.subresourceRange.layerCount = 1;
496 barrier.subresourceRange.levelCount = 1;
497
498 int32_t mipWidth = texWidth;
499 int32_t mipHeight = texHeight;
500
501 for (uint32_t i = 1; i < mipLevels; i++)
502 {
503 barrier.subresourceRange.baseMipLevel = i - 1;
504 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
505 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
506 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
507 barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
508
509 vkCmdPipelineBarrier(commandBuffer,
510 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
511 0, nullptr,
512 0, nullptr,
513 1, &barrier);
514
515 VkImageBlit blit{};
516 blit.srcOffsets[0] = { 0, 0, 0 };
517 blit.srcOffsets[1] = { mipWidth, mipHeight, 1 };
518 blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
519 blit.srcSubresource.mipLevel = i - 1;
520 blit.srcSubresource.baseArrayLayer = 0;
521 blit.srcSubresource.layerCount = 1;
522 blit.dstOffsets[0] = { 0, 0, 0 };
523 blit.dstOffsets[1] = { mipWidth > 1 ? mipWidth / 2 : 1, mipHeight > 1 ? mipHeight / 2 : 1, 1 };
524 blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
525 blit.dstSubresource.mipLevel = i;
526 blit.dstSubresource.baseArrayLayer = 0;
527 blit.dstSubresource.layerCount = 1;
528
529 vkCmdBlitImage(commandBuffer,
530 image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
531 image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
532 1, &blit,
533 VK_FILTER_LINEAR);
534
535 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
536 barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
537 barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
538 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
539
540 vkCmdPipelineBarrier(commandBuffer,
541 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
542 0, nullptr,
543 0, nullptr,
544 1, &barrier);
545
546 if (mipWidth > 1) mipWidth /= 2;
547 if (mipHeight > 1) mipHeight /= 2;
548 }
549
550 barrier.subresourceRange.baseMipLevel = mipLevels - 1;
551 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
552 barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
553 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
554 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
555
556 vkCmdPipelineBarrier(commandBuffer,
557 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
558 0, nullptr,
559 0, nullptr,
560 1, &barrier);
561
562 EndSingleTimeCommand(commandBuffer);
563}
564
565void VulkanRenderer::CreateInstance()
566{
567 //Check validation layers.
568 if (enableValidationLayers && !CheckValidationLayerSupport())
569 {
570 throw std::runtime_error("validation layers requested, but not available!");
571 }
572
573 VkApplicationInfo appInfo{};
574 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
575 appInfo.pApplicationName = mWindowSystem.GetApplication().GetScreenSettings().name.c_str();
576 appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
577 appInfo.pEngineName = "Rendering Engine";
578 appInfo.engineVersion = VK_MAKE_VERSION(
579 RENDERING_ENGINE_VERSION_MAJOR,
580 RENDERING_ENGINE_VERSION_MINOR,
581 RENDERING_ENGINE_VERSION_PATCH
582 );
583 appInfo.apiVersion = VK_API_VERSION_1_0;
584
585 VkInstanceCreateInfo createInfo{};
586 createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
587 createInfo.pApplicationInfo = &appInfo;
588
589 auto extensionsNames = GetRequiredExtensions();
590 createInfo.enabledExtensionCount = static_cast<uint32_t>(extensionsNames.size());
591 createInfo.ppEnabledExtensionNames = extensionsNames.data();
592
593 VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{};
595 {
596 createInfo.enabledLayerCount = static_cast<uint32_t>(mValidationLayers.size());
597 createInfo.ppEnabledLayerNames = mValidationLayers.data();
598
599 PopulateDebugMessengerCreateInfo(debugCreateInfo);
600 createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*)&debugCreateInfo;
601 }
602 else
603 {
604 createInfo.enabledLayerCount = 0;
605
606 createInfo.pNext = nullptr;
607 }
608
609 if (vkCreateInstance(&createInfo, nullptr, &mInstance) != VK_SUCCESS)
610 {
611 throw std::runtime_error("failed to create instance!");
612 }
613 std::cout << "Vulkan instance created" << std::endl;
614}
615
616bool VulkanRenderer::CheckValidationLayerSupport()
617{
618 uint32_t layerCount;
619 vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
620
621 std::vector<VkLayerProperties> availableLayers(layerCount);
622 vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
623
624 for (const char* layerName : mValidationLayers)
625 {
626 bool layerFound = false;
627
628 for (const auto& layerProperties : availableLayers)
629 {
630 if (strcmp(layerName, layerProperties.layerName) == 0)
631 {
632 layerFound = true;
633 break;
634 }
635 }
636
637 if (!layerFound)
638 {
639 return false;
640 }
641 }
642
643 return true;
644}
645
646std::vector<const char*> VulkanRenderer::GetRequiredExtensions()
647{
648 uint32_t glfwExtensionCount = 0;
649 const char** glfwExtensions;
650 glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
651
652 std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
653
655 {
656 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
657 }
658
659 return extensions;
660}
661
662void VulkanRenderer::PopulateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo)
663{
664 createInfo = {};
665 createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
666 createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
667 createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
668 createInfo.pfnUserCallback = DebugCallback;
669}
670
671VKAPI_ATTR VkBool32 VKAPI_CALL VulkanRenderer::DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData)
672{
673 std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl;
674 return VK_FALSE;
675}
676
677void VulkanRenderer::SetupDebugMessenger()
678{
680 {
681 return;
682 }
683
684 VkDebugUtilsMessengerCreateInfoEXT createInfo{};
685 PopulateDebugMessengerCreateInfo(createInfo);
686
687 if (CreateDebugUtilsMessengerEXT(mInstance, &createInfo, nullptr, &mDebugMessenger) != VK_SUCCESS)
688 {
689 throw std::runtime_error("failed to set up debug messenger!");
690 }
691}
692
693VkResult VulkanRenderer::CreateDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerCreateInfoEXT const* pCreateInfo, VkAllocationCallbacks const* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger)
694{
695 auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
696 if (func != nullptr)
697 {
698 return func(instance, pCreateInfo, pAllocator, pDebugMessenger);
699 }
700 else
701 {
702 return VK_ERROR_EXTENSION_NOT_PRESENT;
703 }
704}
705
706void VulkanRenderer::DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator)
707{
708 auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
709 if (func != nullptr)
710 {
711 func(instance, debugMessenger, pAllocator);
712 }
713}
714
715void VulkanRenderer::CreateSurface()
716{
717 GLFWwindow* window = static_cast<GLFWwindow*>(mWindowSystem.GetNativeHandle());
718 if (glfwCreateWindowSurface(mInstance, window, nullptr, &mSurface) != VK_SUCCESS)
719 {
720 throw std::runtime_error("failed to create window surface!");
721 }
722}
723
724void VulkanRenderer::PickPhysicalDevice()
725{
726 uint32_t deviceCount = 0;
727 vkEnumeratePhysicalDevices(mInstance, &deviceCount, nullptr);
728
729 if (deviceCount == 0)
730 {
731 throw std::runtime_error("failed to find GPUs with Vulkan support!");
732 }
733
734 std::vector<VkPhysicalDevice> devices(deviceCount);
735 vkEnumeratePhysicalDevices(mInstance, &deviceCount, devices.data());
736
737 for (const auto& device : devices)
738 {
739 if (IsDeviceSuitable(device)) {
740 mPhysicalDevice = device;
741 vkGetPhysicalDeviceFeatures(mPhysicalDevice, &mPhysDevSupportedFeatures);
742 vkGetPhysicalDeviceProperties(mPhysicalDevice, &mPhysicalDeviceProperties);
743 mMSAASamples = CheckMaxUsableSampleCount();
744 break;
745 }
746 }
747
748 if (mPhysicalDevice == VK_NULL_HANDLE)
749 {
750 throw std::runtime_error("failed to find a suitable GPU!");
751 }
752}
753
754bool VulkanRenderer::IsDeviceSuitable(VkPhysicalDevice device)
755{
756 // Next features could be used for rating and checkink proper device
757
758 //VkPhysicalDeviceProperties deviceProperties;
759 //vkGetPhysicalDeviceProperties(device, &deviceProperties);
760
761 // VkPhysicalDeviceFeatures supportedFeatures;
762 // vkGetPhysicalDeviceFeatures(device, &supportedFeatures);
763
764 //return deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU &&
765 // deviceFeatures.geometryShader;
766
767 QueueFamilyIndices indices = FindQueueFamilies(device);
768
769 bool extensionsSupported = CheckDeviceExtensionSupport(device);
770
771 bool swapChainAdequate = false;
772 if (extensionsSupported)
773 {
774 auto const swapChainSupport = QuerySwapChainSupport(device);
775 swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
776 }
777 return indices.IsComplete() && extensionsSupported && swapChainAdequate;
778}
779
780QueueFamilyIndices VulkanRenderer::FindQueueFamilies(VkPhysicalDevice device)
781{
782 QueueFamilyIndices indices;
783 // Logic to find queue family indices to populate struct with
784 uint32_t queueFamilyCount = 0;
785 vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
786
787 std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
788 vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
789
790 int i = 0;
791 for (const auto& queueFamily : queueFamilies)
792 {
793 if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT)
794 {
795 indices.graphicsFamily = i;
796 }
797
798 VkBool32 presentSupport = false;
799 vkGetPhysicalDeviceSurfaceSupportKHR(device, i, mSurface, &presentSupport);
800 if (presentSupport)
801 {
802 indices.presentFamily = i;
803 }
804 if (indices.IsComplete())
805 {
806 break;
807 }
808
809 i++;
810 }
811 return indices;
812}
813
814bool VulkanRenderer::CheckDeviceExtensionSupport(VkPhysicalDevice physicalDevice)
815{
816 uint32_t extensionCount;
817 vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, nullptr);
818
819 std::vector<VkExtensionProperties> availableExtensions(extensionCount);
820 vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, availableExtensions.data());
821
822 std::set<std::string> requiredExtensions(mDeviceExtensions.begin(), mDeviceExtensions.end());
823
824 for (const auto& extension : availableExtensions)
825 {
826 requiredExtensions.erase(extension.extensionName);
827 }
828
829 return requiredExtensions.empty();
830}
831
832SwapChainSupportDetails VulkanRenderer::QuerySwapChainSupport(VkPhysicalDevice device)
833{
834 SwapChainSupportDetails scsDetails;
835
836 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, mSurface, &scsDetails.capabilities);
837
838 uint32_t formatCount;
839 vkGetPhysicalDeviceSurfaceFormatsKHR(device, mSurface, &formatCount, nullptr);
840
841 if (formatCount != 0)
842 {
843 scsDetails.formats.resize(formatCount);
844 vkGetPhysicalDeviceSurfaceFormatsKHR(device, mSurface, &formatCount, scsDetails.formats.data());
845 }
846
847 uint32_t presentModeCount;
848 vkGetPhysicalDeviceSurfacePresentModesKHR(device, mSurface, &presentModeCount, nullptr);
849
850 if (presentModeCount != 0)
851 {
852 scsDetails.presentModes.resize(presentModeCount);
853 vkGetPhysicalDeviceSurfacePresentModesKHR(device, mSurface, &presentModeCount, scsDetails.presentModes.data());
854 }
855
856 return scsDetails;
857}
858
859VkSampleCountFlagBits VulkanRenderer::CheckMaxUsableSampleCount()
860{
861 VkPhysicalDeviceProperties physicalDeviceProperties;
862 vkGetPhysicalDeviceProperties(mPhysicalDevice, &physicalDeviceProperties);
863
864 VkSampleCountFlags counts = physicalDeviceProperties.limits.framebufferColorSampleCounts & physicalDeviceProperties.limits.framebufferDepthSampleCounts;
865 if (counts & VK_SAMPLE_COUNT_64_BIT) { return VK_SAMPLE_COUNT_64_BIT; }
866 if (counts & VK_SAMPLE_COUNT_32_BIT) { return VK_SAMPLE_COUNT_32_BIT; }
867 if (counts & VK_SAMPLE_COUNT_16_BIT) { return VK_SAMPLE_COUNT_16_BIT; }
868 if (counts & VK_SAMPLE_COUNT_8_BIT) { return VK_SAMPLE_COUNT_8_BIT; }
869 if (counts & VK_SAMPLE_COUNT_4_BIT) { return VK_SAMPLE_COUNT_4_BIT; }
870 if (counts & VK_SAMPLE_COUNT_2_BIT) { return VK_SAMPLE_COUNT_2_BIT; }
871
872 return VK_SAMPLE_COUNT_1_BIT;
873}
874
875void VulkanRenderer::CreateLogicalDevice()
876{
877 QueueFamilyIndices indices = FindQueueFamilies(mPhysicalDevice);
878
879 std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
880 std::set<uint32_t> uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() };
881
882 float queuePriority = 1.0f;
883 for (uint32_t queueFamily : uniqueQueueFamilies)
884 {
885 VkDeviceQueueCreateInfo queueCreateInfo{};
886 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
887 queueCreateInfo.queueFamilyIndex = queueFamily;
888 queueCreateInfo.queueCount = 1;
889 queueCreateInfo.pQueuePriorities = &queuePriority;
890 queueCreateInfos.push_back(queueCreateInfo);
891 }
892
893 VkDeviceQueueCreateInfo queueCreateInfo{};
894 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
895 queueCreateInfo.queueFamilyIndex = indices.graphicsFamily.value();
896 queueCreateInfo.queueCount = 1;
897
898 queueCreateInfo.pQueuePriorities = &queuePriority;
899
900 VkPhysicalDeviceFeatures deviceFeatures{};
901 if (mPhysDevSupportedFeatures.samplerAnisotropy)
902 {
903 deviceFeatures.samplerAnisotropy = VK_TRUE;
904 }
905 else
906 {
907 deviceFeatures.samplerAnisotropy = VK_FALSE;
908 }
909
910 if (mPhysDevSupportedFeatures.sampleRateShading)
911 {
912 deviceFeatures.sampleRateShading = VK_TRUE;
913 }
914 else
915 {
916 deviceFeatures.sampleRateShading = VK_FALSE;
917 }
918
919 VkDeviceCreateInfo createInfo{};
920 createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
921
922 createInfo.pQueueCreateInfos = queueCreateInfos.data();
923 createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
924
925 createInfo.pEnabledFeatures = &deviceFeatures;
926
927 createInfo.enabledExtensionCount = static_cast<uint32_t>(mDeviceExtensions.size());
928 createInfo.ppEnabledExtensionNames = mDeviceExtensions.data();
929
931 {
932 createInfo.enabledLayerCount = static_cast<uint32_t>(mValidationLayers.size());
933 createInfo.ppEnabledLayerNames = mValidationLayers.data();
934 }
935 else
936 {
937 createInfo.enabledLayerCount = 0;
938 }
939
940 if (vkCreateDevice(mPhysicalDevice, &createInfo, nullptr, &mLogicalDevice) != VK_SUCCESS)
941 {
942 throw std::runtime_error("failed to create logical device!");
943 }
944
945 vkGetDeviceQueue(mLogicalDevice, indices.graphicsFamily.value(), 0, &mGraphicsQueue);
946 vkGetDeviceQueue(mLogicalDevice, indices.presentFamily.value(), 0, &mPresentQueue);
947}
948
949void VulkanRenderer::CreateSwapChain()
950{
951 SwapChainSupportDetails swapChainSupport = QuerySwapChainSupport(mPhysicalDevice);
952
953 VkSurfaceFormatKHR surfaceFormat = ChooseSwapSurfaceFormat(swapChainSupport.formats);
954 VkPresentModeKHR presentMode = ChooseSwapPresentMode(swapChainSupport.presentModes);
955 VkExtent2D extent = ChooseSwapExtent(swapChainSupport.capabilities);
956
957 uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
958 if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount)
959 {
960 imageCount = swapChainSupport.capabilities.maxImageCount;
961 }
962
963 VkSwapchainCreateInfoKHR createInfo{};
964 createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
965 createInfo.surface = mSurface;
966
967 createInfo.minImageCount = imageCount;
968 createInfo.imageFormat = surfaceFormat.format;
969 createInfo.imageColorSpace = surfaceFormat.colorSpace;
970 createInfo.imageExtent = extent;
971 createInfo.imageArrayLayers = 1;
972 createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
973
974 QueueFamilyIndices indices = FindQueueFamilies(mPhysicalDevice);
975 uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() };
976
977 if (indices.graphicsFamily != indices.presentFamily) {
978 createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
979 createInfo.queueFamilyIndexCount = 2;
980 createInfo.pQueueFamilyIndices = queueFamilyIndices;
981 }
982 else
983 {
984 createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
985 createInfo.queueFamilyIndexCount = 0; // Optional
986 createInfo.pQueueFamilyIndices = nullptr; // Optional
987 }
988
989 createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
990 createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
991 createInfo.presentMode = presentMode;
992 createInfo.clipped = VK_TRUE;
993 createInfo.oldSwapchain = VK_NULL_HANDLE;
994
995 if (vkCreateSwapchainKHR(mLogicalDevice, &createInfo, nullptr, &mSwapChain) != VK_SUCCESS)
996 {
997 throw std::runtime_error("failed to create swap chain!");
998 }
999
1000 vkGetSwapchainImagesKHR(mLogicalDevice, mSwapChain, &imageCount, nullptr);
1001 mSwapChainImages.resize(imageCount);
1002 vkGetSwapchainImagesKHR(mLogicalDevice, mSwapChain, &imageCount, mSwapChainImages.data());
1003
1004 mSwapChainImageFormat = surfaceFormat.format;
1005 mSwapChainExtent = extent;
1006}
1007
1008void VulkanRenderer::RecreateSwapChain()
1009{
1010 int width = 0;
1011 int height = 0;
1012
1013 while (width == 0 || height == 0)
1014 {
1015 GLFWwindow* window = static_cast<GLFWwindow*>(mWindowSystem.GetNativeHandle());
1016 glfwGetFramebufferSize(window, &width, &height);
1017 glfwWaitEvents();
1018 }
1019 vkDeviceWaitIdle(mLogicalDevice);
1020
1021 CleanupSwapChain();
1022
1023 for (auto& observer : mObservers)
1024 {
1025 observer->OnRenderResourcesRelease();
1026 }
1027
1028 CreateSwapChain();
1029 CreateImageViews();
1030 CreateRenderPass();
1031
1032 CreateColorResources();
1033 CreateDepthResources();
1034 CreateFramebuffers();
1035 CreateCommandBuffers();
1036
1037 for (auto& observer : mObservers)
1038 {
1039 observer->OnRenderResourcesRebuild();
1040 }
1041}
1042
1043VkSurfaceFormatKHR VulkanRenderer::ChooseSwapSurfaceFormat(std::vector<VkSurfaceFormatKHR> const& availableFormats)
1044{
1045 for (const auto& availableFormat : availableFormats)
1046 {
1047 if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
1048 {
1049 return availableFormat;
1050 }
1051 }
1052
1053 return availableFormats[0];
1054}
1055
1056VkPresentModeKHR VulkanRenderer::ChooseSwapPresentMode(std::vector<VkPresentModeKHR>const& availablePresentModes)
1057{
1058 for (const auto& availablePresentMode : availablePresentModes)
1059 {
1060 if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR)
1061 {
1062 return availablePresentMode;
1063 }
1064 }
1065 return VK_PRESENT_MODE_FIFO_KHR;
1066}
1067
1068VkExtent2D VulkanRenderer::ChooseSwapExtent(VkSurfaceCapabilitiesKHR const& capabilities)
1069{
1070 if (capabilities.currentExtent.width != UINT32_MAX)
1071 {
1072 return capabilities.currentExtent;
1073 }
1074 else
1075 {
1076 int width, height;
1077 GLFWwindow* window = static_cast<GLFWwindow*>(mWindowSystem.GetNativeHandle());
1078 glfwGetFramebufferSize(window, &width, &height);
1079
1080 VkExtent2D actualExtent = {
1081 static_cast<uint32_t>(width),
1082 static_cast<uint32_t>(height)
1083 };
1084
1085 actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
1086 actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
1087
1088 return actualExtent;
1089 }
1090}
1091
1092void VulkanRenderer::CreateImageViews()
1093{
1094 mSwapChainImageViews.resize(mSwapChainImages.size());
1095
1096 for (uint32_t i = 0; i < mSwapChainImages.size(); i++)
1097 {
1098 mSwapChainImageViews[i] = CreateImageView(mSwapChainImages[i], mSwapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT, 1);
1099 }
1100}
1101
1102VkImageView VulkanRenderer::CreateImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags, std::uint32_t mipmapLevels)
1103{
1104 VkImageViewCreateInfo viewInfo{};
1105 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1106 viewInfo.image = image;
1107 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
1108 viewInfo.format = format;
1109 viewInfo.subresourceRange.aspectMask = aspectFlags;
1110 viewInfo.subresourceRange.baseMipLevel = 0;
1111 viewInfo.subresourceRange.levelCount = mipmapLevels;
1112 viewInfo.subresourceRange.baseArrayLayer = 0;
1113 viewInfo.subresourceRange.layerCount = 1;
1114
1115 VkImageView imageView;
1116 if (vkCreateImageView(mLogicalDevice, &viewInfo, nullptr, &imageView) != VK_SUCCESS)
1117 {
1118 throw std::runtime_error("failed to create texture image view!");
1119 }
1120
1121 return imageView;
1122}
1123
1124void VulkanRenderer::CreateRenderPass()
1125{
1126 VkAttachmentDescription colorAttachment{};
1127 colorAttachment.format = mSwapChainImageFormat;
1128 colorAttachment.samples = mMSAASamples;
1129 colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1130 colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1131 colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1132 colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1133 colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1134 colorAttachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1135
1136 VkAttachmentReference colorAttachmentRef{};
1137 colorAttachmentRef.attachment = 0;
1138 colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1139
1140 VkAttachmentDescription depthAttachment{};
1141 depthAttachment.format = FindDepthFormat();
1142 depthAttachment.samples = mMSAASamples;
1143 depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1144 depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1145 depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1146 depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1147 depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1148 depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1149
1150 VkAttachmentReference depthAttachmentRef{};
1151 depthAttachmentRef.attachment = 1;
1152 depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1153
1154 VkAttachmentDescription colorAttachmentResolve{};
1155 colorAttachmentResolve.format = mSwapChainImageFormat;
1156 colorAttachmentResolve.samples = VK_SAMPLE_COUNT_1_BIT;
1157 colorAttachmentResolve.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1158 colorAttachmentResolve.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1159 colorAttachmentResolve.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1160 colorAttachmentResolve.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1161 colorAttachmentResolve.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1162 colorAttachmentResolve.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
1163
1164 VkAttachmentReference colorAttachmentResolveRef{};
1165 colorAttachmentResolveRef.attachment = 2;
1166 colorAttachmentResolveRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1167
1168 VkSubpassDescription subpass{};
1169 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
1170 subpass.colorAttachmentCount = 1;
1171 subpass.pColorAttachments = &colorAttachmentRef;
1172 subpass.pDepthStencilAttachment = &depthAttachmentRef;
1173 subpass.pResolveAttachments = &colorAttachmentResolveRef;
1174
1175 VkSubpassDependency dependency{};
1176 dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
1177 dependency.dstSubpass = 0;
1178 dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
1179 dependency.srcAccessMask = 0;
1180 dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
1181 dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
1182
1183 std::array<VkAttachmentDescription, 3> attachments = { colorAttachment, depthAttachment, colorAttachmentResolve };
1184
1185 VkRenderPassCreateInfo renderPassInfo{};
1186 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
1187 renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
1188 renderPassInfo.pAttachments = attachments.data();
1189 renderPassInfo.subpassCount = 1;
1190 renderPassInfo.pSubpasses = &subpass;
1191 renderPassInfo.dependencyCount = 1;
1192 renderPassInfo.pDependencies = &dependency;
1193
1194 if (vkCreateRenderPass(mLogicalDevice, &renderPassInfo, nullptr, &mRenderPass) != VK_SUCCESS)
1195 {
1196 throw std::runtime_error("failed to create render pass!");
1197 }
1198}
1199
1200VkFormat VulkanRenderer::FindDepthFormat()
1201{
1202 return FindSupportedFormat(
1203 { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT },
1204 VK_IMAGE_TILING_OPTIMAL,
1205 VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
1206 );
1207}
1208
1209VkFormat VulkanRenderer::FindSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features)
1210{
1211 for (VkFormat format : candidates)
1212 {
1213 VkFormatProperties props;
1214 vkGetPhysicalDeviceFormatProperties(mPhysicalDevice, format, &props);
1215
1216 if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features)
1217 {
1218 return format;
1219 }
1220 else if (tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features) == features)
1221 {
1222 return format;
1223 }
1224 }
1225
1226 throw std::runtime_error("failed to find supported format!");
1227}
1228
1229void VulkanRenderer::CreateCommandPool()
1230{
1231 QueueFamilyIndices queueFamilyIndices = FindQueueFamilies(mPhysicalDevice);
1232
1233 VkCommandPoolCreateInfo poolInfo{};
1234 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
1235 poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
1236 poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
1237
1238 if (vkCreateCommandPool(mLogicalDevice, &poolInfo, nullptr, &mCommandPool) != VK_SUCCESS)
1239 {
1240 throw std::runtime_error("failed to create command pool!");
1241 }
1242}
1243
1244void VulkanRenderer::CreateColorResources()
1245{
1246 VkFormat colorFormat = mSwapChainImageFormat;
1247
1248 CreateVulkanImage(mSwapChainExtent.width, mSwapChainExtent.height, 1, mMSAASamples, colorFormat, VK_IMAGE_TILING_OPTIMAL,
1249 VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
1250 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mColorImage, mColorImageMemory);
1251 mColorImageView = CreateImageView(mColorImage, colorFormat, VK_IMAGE_ASPECT_COLOR_BIT, 1);
1252}
1253
1254void VulkanRenderer::CreateVulkanImage(uint32_t width, uint32_t height, std::uint32_t mipmapLevels, VkSampleCountFlagBits numSamples, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory)
1255{
1256 VkImageCreateInfo imageInfo{};
1257 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
1258 imageInfo.imageType = VK_IMAGE_TYPE_2D;
1259 imageInfo.extent.width = static_cast<uint32_t>(width);
1260 imageInfo.extent.height = static_cast<uint32_t>(height);
1261 imageInfo.extent.depth = 1;
1262 imageInfo.mipLevels = mipmapLevels;
1263 imageInfo.arrayLayers = 1;
1264
1265 imageInfo.format = format;
1266 imageInfo.tiling = tiling;
1267
1268 imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1269
1270 imageInfo.usage = usage;
1271
1272 imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
1273
1274 imageInfo.samples = numSamples;
1275 imageInfo.flags = 0; // Optional
1276
1277 if (vkCreateImage(mLogicalDevice, &imageInfo, nullptr, &image) != VK_SUCCESS)
1278 {
1279 throw std::runtime_error("failed to create image!");
1280 }
1281
1282 VkMemoryRequirements memRequirements;
1283 vkGetImageMemoryRequirements(mLogicalDevice, image, &memRequirements);
1284
1285 VkMemoryAllocateInfo allocInfo{};
1286 allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1287 allocInfo.allocationSize = memRequirements.size;
1288 allocInfo.memoryTypeIndex = FindMemoryType(memRequirements.memoryTypeBits, properties);
1289
1290 if (vkAllocateMemory(mLogicalDevice, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS) {
1291 throw std::runtime_error("failed to allocate image memory!");
1292 }
1293
1294 vkBindImageMemory(mLogicalDevice, image, imageMemory, 0);
1295}
1296
1297uint32_t VulkanRenderer::FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties)
1298{
1299 VkPhysicalDeviceMemoryProperties memProperties;
1300 vkGetPhysicalDeviceMemoryProperties(mPhysicalDevice, &memProperties);
1301
1302 for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++)
1303 {
1304 if (typeFilter & (1 << i) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties)
1305 {
1306 return i;
1307 }
1308 }
1309
1310 throw std::runtime_error("failed to find suitable memory type!");
1311}
1312
1314{
1315 return mPhysicalDevice;
1316}
1317
1319{
1320 return mPhysDevSupportedFeatures;
1321}
1322
1324{
1325 std::uint32_t bindingNumber = 0;
1326 std::vector<VkDescriptorSetLayoutBinding> bindings;
1327
1328 // Uniform buffer to transmit transformation matrix
1329 VkDescriptorSetLayoutBinding uboLayoutBinding{};
1330 uboLayoutBinding.binding = bindingNumber;
1331 uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
1332 uboLayoutBinding.descriptorCount = 1;
1333 uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
1334 uboLayoutBinding.pImmutableSamplers = nullptr; // Optional
1335
1336 bindings.push_back(uboLayoutBinding);
1337
1338 PackedMaterialData packedMaterialData = material->PackMaterialParameters();
1339 if (!packedMaterialData.buffer.empty())
1340 {
1341 ++bindingNumber;
1342 // Uniform buffer to transmit custom material variables
1343 VkDescriptorSetLayoutBinding customMaterialVariables;
1344 customMaterialVariables.binding = bindingNumber;
1345 customMaterialVariables.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
1346 customMaterialVariables.descriptorCount = 1;
1347 customMaterialVariables.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
1348 customMaterialVariables.pImmutableSamplers = nullptr; // Optional
1349
1350 bindings.push_back(customMaterialVariables);
1351 }
1352
1353 for (const auto& textureName : material->GetTextures())
1354 {
1355 (void)textureName;
1356 ++bindingNumber;
1357 VkDescriptorSetLayoutBinding samplerLayoutBinding{};
1358 samplerLayoutBinding.binding = bindingNumber;
1359 samplerLayoutBinding.descriptorCount = 1;
1360 samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1361 samplerLayoutBinding.pImmutableSamplers = nullptr;
1362 samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
1363
1364 bindings.push_back(samplerLayoutBinding);
1365 }
1366
1367 VkDescriptorSetLayoutCreateInfo layoutInfo{};
1368 layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
1369 layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
1370 layoutInfo.pBindings = bindings.data();
1371
1372 VkDescriptorSetLayout result;
1373 if (vkCreateDescriptorSetLayout(mLogicalDevice, &layoutInfo, nullptr, &result) != VK_SUCCESS)
1374 {
1375 throw std::runtime_error("failed to create descriptor set layout!");
1376 }
1377
1378 return result;
1379}
1380
1381std::vector<VkCommandBuffer> VulkanRenderer::GetComandBuffers()
1382{
1383 return mCommandBuffers;
1384}
1385
1386std::pair<VkPipelineLayout, VkPipeline> VulkanRenderer::CreateGraphicsPipeline(Material* material, VkDescriptorSetLayout& descriptorSetLayout, std::vector<char>& spvVertShaderCode, std::vector<char>& spvFragShaderCode)
1387{
1388 VkPipelineLayout pipelineLayout;
1389 VkPipeline pipeline;
1390
1391 VkShaderModule vertShaderModule = CreateShaderModule(spvVertShaderCode);
1392 VkShaderModule fragShaderModule = CreateShaderModule(spvFragShaderCode);
1393
1394 VkPipelineShaderStageCreateInfo vertShaderStageInfo{};
1395 vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
1396 vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
1397
1398 vertShaderStageInfo.module = vertShaderModule;
1399 vertShaderStageInfo.pName = "main";
1400
1401 VkPipelineShaderStageCreateInfo fragShaderStageInfo{};
1402 fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
1403 fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
1404 fragShaderStageInfo.module = fragShaderModule;
1405 fragShaderStageInfo.pName = "main";
1406
1407 VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo };
1408
1409 VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
1410 vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
1411
1412 VkVertexInputBindingDescription bindingDescription;
1413 std::vector<VkVertexInputAttributeDescription> attributeDescriptions{};
1414
1416 {
1417 bindingDescription = GetBindingDescription<Vertex2D>();
1418 attributeDescriptions = GetAttributeDescriptions<Vertex2D>();
1419 }
1421 {
1424 }
1425
1426 vertexInputInfo.vertexBindingDescriptionCount = 1;
1427 vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size());
1428
1429 vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
1430 vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();
1431
1432 VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
1433 inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
1434 inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
1435 inputAssembly.primitiveRestartEnable = VK_FALSE;
1436
1437 VkViewport viewport{};
1438 viewport.x = 0.0f;
1439 viewport.y = 0.0f;
1440 viewport.width = (float)mSwapChainExtent.width;
1441 viewport.height = (float)mSwapChainExtent.height;
1442 viewport.minDepth = 0.0f;
1443 viewport.maxDepth = 1.0f;
1444
1445 VkRect2D scissor{};
1446 scissor.offset = { 0, 0 };
1447 scissor.extent = mSwapChainExtent;
1448
1449 VkPipelineViewportStateCreateInfo viewportState{};
1450 viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
1451 viewportState.viewportCount = 1;
1452 viewportState.pViewports = &viewport;
1453 viewportState.scissorCount = 1;
1454 viewportState.pScissors = &scissor;
1455
1456 VkPipelineRasterizationStateCreateInfo rasterizer{};
1457 rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
1458 rasterizer.depthClampEnable = VK_FALSE;
1459 rasterizer.rasterizerDiscardEnable = VK_FALSE;
1460 rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
1461 rasterizer.lineWidth = 1.0f;
1463 {
1464 rasterizer.cullMode = VK_CULL_MODE_NONE;
1465 }
1466 else
1467 {
1468 rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
1469 }
1470 rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
1471 rasterizer.depthBiasEnable = VK_FALSE;
1472 rasterizer.depthBiasConstantFactor = 0.0f; // Optional
1473 rasterizer.depthBiasClamp = 0.0f; // Optional
1474 rasterizer.depthBiasSlopeFactor = 0.0f; // Optional
1475
1476 VkPipelineMultisampleStateCreateInfo multisampling{};
1477 multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
1478 multisampling.sampleShadingEnable = VK_FALSE;
1479 multisampling.rasterizationSamples = mMSAASamples;
1480 multisampling.minSampleShading = 1.0f; // Optional
1481 multisampling.pSampleMask = nullptr; // Optional
1482 multisampling.alphaToCoverageEnable = VK_FALSE; // Optional
1483 multisampling.alphaToOneEnable = VK_FALSE; // Optional
1484
1485 VkPipelineColorBlendAttachmentState colorBlendAttachment{};
1486 colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
1487 colorBlendAttachment.blendEnable = (material->GetMaterialSettings().blendMode == BlendMode::Translucent) ? VK_TRUE : VK_FALSE;
1488 colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
1489 colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
1490 colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; // Optional
1491 colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
1492 colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
1493 colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; // Optional
1494
1495 VkPipelineColorBlendStateCreateInfo colorBlending{};
1496 colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
1497 colorBlending.logicOpEnable = VK_FALSE;
1498 colorBlending.logicOp = VK_LOGIC_OP_COPY; // Optional
1499 colorBlending.attachmentCount = 1;
1500 colorBlending.pAttachments = &colorBlendAttachment;
1501 colorBlending.blendConstants[0] = 0.0f; // Optional
1502 colorBlending.blendConstants[1] = 0.0f; // Optional
1503 colorBlending.blendConstants[2] = 0.0f; // Optional
1504 colorBlending.blendConstants[3] = 0.0f; // Optional
1505
1506 // To use dynamicState, assign reference to it in pipelineInfo.pDynamicState.
1507 std::vector<VkDynamicState> dynamicStates = {
1508 VK_DYNAMIC_STATE_VIEWPORT,
1509 VK_DYNAMIC_STATE_SCISSOR
1510 };
1511 VkPipelineDynamicStateCreateInfo dynamicState{};
1512 dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
1513 dynamicState.dynamicStateCount = static_cast<uint32_t>(dynamicStates.size());
1514 dynamicState.pDynamicStates = dynamicStates.data();
1515
1516 VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
1517 pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
1518 pipelineLayoutInfo.setLayoutCount = 1;
1519 pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout;
1520
1521 if (vkCreatePipelineLayout(mLogicalDevice, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS)
1522 {
1523 throw std::runtime_error("failed to create pipeline layout!");
1524 }
1525
1526 VkPipelineDepthStencilStateCreateInfo depthStencil{};
1527 depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
1529 {
1530 depthStencil.depthTestEnable = VK_FALSE;
1531 depthStencil.depthWriteEnable = VK_FALSE;
1532 }
1533 else
1534 {
1535 depthStencil.depthTestEnable = VK_TRUE;
1536 depthStencil.depthWriteEnable = (material->GetMaterialSettings().blendMode == BlendMode::Opaque) ? VK_TRUE : VK_FALSE;
1537 }
1538 depthStencil.depthCompareOp = VK_COMPARE_OP_LESS;
1539 depthStencil.depthBoundsTestEnable = VK_FALSE;
1540 //depthStencil.minDepthBounds = 0.0f; // Optional
1541 //depthStencil.maxDepthBounds = 1.0f; // Optional
1542 depthStencil.stencilTestEnable = VK_FALSE;
1543 //depthStencil.front = {}; // Optional
1544 depthStencil.back = {}; // Optional
1545
1546 VkGraphicsPipelineCreateInfo pipelineInfo{};
1547 pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
1548 pipelineInfo.stageCount = 2;
1549 pipelineInfo.pStages = shaderStages;
1550 pipelineInfo.pVertexInputState = &vertexInputInfo;
1551 pipelineInfo.pInputAssemblyState = &inputAssembly;
1552 pipelineInfo.pViewportState = &viewportState;
1553 pipelineInfo.pRasterizationState = &rasterizer;
1554 pipelineInfo.pMultisampleState = &multisampling;
1555 pipelineInfo.pDepthStencilState = &depthStencil;
1556 pipelineInfo.pColorBlendState = &colorBlending;
1557 pipelineInfo.pDynamicState = nullptr;
1558 pipelineInfo.layout = pipelineLayout;
1559 pipelineInfo.renderPass = mRenderPass;
1560 pipelineInfo.subpass = 0;
1561 pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; // Optional
1562 pipelineInfo.basePipelineIndex = -1; // Optional
1563
1564
1565 if (vkCreateGraphicsPipelines(mLogicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline) != VK_SUCCESS)
1566 {
1567 throw std::runtime_error("failed to create graphics pipeline!");
1568 }
1569 vkDestroyShaderModule(mLogicalDevice, fragShaderModule, nullptr);
1570 vkDestroyShaderModule(mLogicalDevice, vertShaderModule, nullptr);
1571
1572 return std::pair<VkPipelineLayout, VkPipeline>(pipelineLayout, pipeline);
1573}
1574
1575void VulkanRenderer::CreateDepthResources()
1576{
1577 VkFormat depthFormat = FindDepthFormat();
1578
1579 CreateVulkanImage(mSwapChainExtent.width, mSwapChainExtent.height, 1, mMSAASamples, depthFormat,
1580 VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
1581 mDepthImage, mDepthImageMemory);
1582 mDepthImageView = CreateImageView(mDepthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT, 1);
1583}
1584
1585void VulkanRenderer::CreateFramebuffers()
1586{
1587 mSwapChainFramebuffers.resize(mSwapChainImageViews.size());
1588
1589 for (size_t i = 0; i < mSwapChainImageViews.size(); i++)
1590 {
1591 std::array<VkImageView, 3> attachments = { mColorImageView, mDepthImageView, mSwapChainImageViews[i] };
1592
1593 VkFramebufferCreateInfo framebufferInfo{};
1594 framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
1595 framebufferInfo.renderPass = mRenderPass;
1596 framebufferInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
1597 framebufferInfo.pAttachments = attachments.data();
1598 framebufferInfo.width = mSwapChainExtent.width;
1599 framebufferInfo.height = mSwapChainExtent.height;
1600 framebufferInfo.layers = 1;
1601
1602 if (vkCreateFramebuffer(mLogicalDevice, &framebufferInfo, nullptr, &mSwapChainFramebuffers[i]) != VK_SUCCESS)
1603 {
1604 throw std::runtime_error("failed to create framebuffer!");
1605 }
1606 }
1607}
1608
1609void VulkanRenderer::CreateCommandBuffers()
1610{
1611 mCommandBuffers.resize(MAX_FRAMES_IN_FLIGHT);
1612
1613 VkCommandBufferAllocateInfo allocInfo{};
1614 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1615 allocInfo.commandPool = mCommandPool;
1616 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1617 allocInfo.commandBufferCount = (uint32_t)mCommandBuffers.size();
1618
1619 if (vkAllocateCommandBuffers(mLogicalDevice, &allocInfo, mCommandBuffers.data()) != VK_SUCCESS)
1620 {
1621 throw std::runtime_error("failed to allocate command buffers!");
1622 }
1623}
1624
1625void VulkanRenderer::CreateSyncObjects()
1626{
1627 mImageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
1628 mRenderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
1629 mInFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
1630 mImagesInFlight.resize(mSwapChainImages.size(), VK_NULL_HANDLE);
1631
1632 VkSemaphoreCreateInfo semaphoreInfo{};
1633 semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
1634
1635 VkFenceCreateInfo fenceInfo{};
1636 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
1637 fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
1638
1639 for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
1640 {
1641 if (vkCreateSemaphore(mLogicalDevice, &semaphoreInfo, nullptr, &mImageAvailableSemaphores[i]) != VK_SUCCESS ||
1642 vkCreateSemaphore(mLogicalDevice, &semaphoreInfo, nullptr, &mRenderFinishedSemaphores[i]) != VK_SUCCESS ||
1643 vkCreateFence(mLogicalDevice, &fenceInfo, nullptr, &mInFlightFences[i]) != VK_SUCCESS)
1644 {
1645 throw std::runtime_error("failed to create synchronization objects for a frame!");
1646 }
1647 }
1648}
1649
1650VkShaderModule VulkanRenderer::CreateShaderModule(std::vector<char>& code)
1651{
1652 VkShaderModuleCreateInfo createInfo{};
1653 createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
1654 createInfo.codeSize = code.size();
1655 createInfo.pCode = reinterpret_cast<const uint32_t*>(code.data());
1656
1657 VkShaderModule shaderModule;
1658 if (vkCreateShaderModule(mLogicalDevice, &createInfo, nullptr, &shaderModule) != VK_SUCCESS)
1659 {
1660 throw std::runtime_error("failed to create shader module!");
1661 }
1662
1663 return shaderModule;
1664}
1665
1666void VulkanRenderer::CleanupSwapChain()
1667{
1668 vkDestroyImageView(mLogicalDevice, mColorImageView, nullptr);
1669 vkDestroyImage(mLogicalDevice, mColorImage, nullptr);
1670 vkFreeMemory(mLogicalDevice, mColorImageMemory, nullptr);
1671
1672 vkDestroyImageView(mLogicalDevice, mDepthImageView, nullptr);
1673 vkDestroyImage(mLogicalDevice, mDepthImage, nullptr);
1674 vkFreeMemory(mLogicalDevice, mDepthImageMemory, nullptr);
1675 for (auto framebuffer : mSwapChainFramebuffers)
1676 {
1677 vkDestroyFramebuffer(mLogicalDevice, framebuffer, nullptr);
1678 }
1679
1680 vkFreeCommandBuffers(mLogicalDevice, mCommandPool, static_cast<uint32_t>(mCommandBuffers.size()), mCommandBuffers.data());
1681
1682 vkDestroyRenderPass(mLogicalDevice, mRenderPass, nullptr);
1683 for (auto imageView : mSwapChainImageViews)
1684 {
1685 vkDestroyImageView(mLogicalDevice, imageView, nullptr);
1686 }
1687 vkDestroySwapchainKHR(mLogicalDevice, mSwapChain, nullptr);
1688}
1689
1690void VulkanRenderer::RecordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex)
1691{
1692 VkCommandBufferBeginInfo beginInfo{};
1693 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1694 //beginInfo.flags = 0; // Optional
1695 //beginInfo.pInheritanceInfo = nullptr; // Optional
1696
1697 if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS)
1698 {
1699 throw std::runtime_error("failed to begin recording command buffer!");
1700 }
1701
1702 VkRenderPassBeginInfo renderPassInfo{};
1703 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
1704 renderPassInfo.renderPass = mRenderPass;
1705 renderPassInfo.framebuffer = mSwapChainFramebuffers[imageIndex];
1706
1707 renderPassInfo.renderArea.offset = { 0, 0 };
1708 renderPassInfo.renderArea.extent = mSwapChainExtent;
1709
1710 std::array<VkClearValue, 2> clearValues{};
1711 clearValues[0].color = { {0.0f, 0.0f, 0.0f, 1.0f} };
1712 clearValues[1].depthStencil = { 1.0f, 0 };
1713 renderPassInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
1714 renderPassInfo.pClearValues = clearValues.data();
1715
1716 vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
1717
1718 vkCmdEndRenderPass(commandBuffer);
1719
1720 if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS)
1721 {
1722 throw std::runtime_error("failed to record command buffer!");
1723 }
1724}
1725
1726VkCommandBuffer VulkanRenderer::BeginSingleTimeCommand()
1727{
1728 VkCommandBufferAllocateInfo allocInfo{};
1729 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1730 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1731 allocInfo.commandPool = mCommandPool;
1732 allocInfo.commandBufferCount = 1;
1733
1734 VkCommandBuffer commandBuffer;
1735 vkAllocateCommandBuffers(mLogicalDevice, &allocInfo, &commandBuffer);
1736
1737 VkCommandBufferBeginInfo beginInfo{};
1738 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1739 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
1740
1741 vkBeginCommandBuffer(commandBuffer, &beginInfo);
1742
1743 return commandBuffer;
1744}
1745
1746bool VulkanRenderer::HasStencilComponent(VkFormat format)
1747{
1748 return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT;
1749}
1750
1751void VulkanRenderer::EndSingleTimeCommand(VkCommandBuffer commandBuffer)
1752{
1753 vkEndCommandBuffer(commandBuffer);
1754
1755 VkSubmitInfo submitInfo{};
1756 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1757 submitInfo.commandBufferCount = 1;
1758 submitInfo.pCommandBuffers = &commandBuffer;
1759
1760 vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
1761 vkQueueWaitIdle(mGraphicsQueue);
1762
1763 vkFreeCommandBuffers(mLogicalDevice, mCommandPool, 1, &commandBuffer);
1764}
1765
1766template<typename T>
1767VkVertexInputBindingDescription VulkanRenderer::GetBindingDescription()
1768{
1769 VkVertexInputBindingDescription binding{};
1770 binding.binding = 0;
1771 binding.stride = sizeof(T);
1772 binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
1773 return binding;
1774}
1775
1776template<>
1777std::vector<VkVertexInputAttributeDescription> VulkanRenderer::GetAttributeDescriptions<Vertex2D>()
1778{
1779 std::vector<VkVertexInputAttributeDescription> attributeDescriptions{};
1780 attributeDescriptions.resize(3);
1781
1782 attributeDescriptions[0].binding = 0;
1783 attributeDescriptions[0].location = 0;
1784 attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT;
1785 attributeDescriptions[0].offset = offsetof(Vertex2D, position);
1786
1787 attributeDescriptions[1].binding = 0;
1788 attributeDescriptions[1].location = 1;
1789 attributeDescriptions[1].format = VK_FORMAT_R32G32B32A32_SFLOAT;
1790 attributeDescriptions[1].offset = offsetof(Vertex2D, color);
1791
1792 attributeDescriptions[2].binding = 0;
1793 attributeDescriptions[2].location = 2;
1794 attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
1795 attributeDescriptions[2].offset = offsetof(Vertex2D, textureCoordinates);
1796
1797 return attributeDescriptions;
1798}
1799
1800template<>
1801std::vector<VkVertexInputAttributeDescription> VulkanRenderer::GetAttributeDescriptions<VertexPositionColorTexture>()
1802{
1803 std::vector<VkVertexInputAttributeDescription> attributeDescriptions{};
1804 attributeDescriptions.resize(3);
1805
1806 attributeDescriptions[0].binding = 0;
1807 attributeDescriptions[0].location = 0;
1808 attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
1809 attributeDescriptions[0].offset = offsetof(VertexPositionColorTexture, position);
1810
1811 attributeDescriptions[1].binding = 0;
1812 attributeDescriptions[1].location = 1;
1813 attributeDescriptions[1].format = VK_FORMAT_R32G32B32A32_SFLOAT;
1814 attributeDescriptions[1].offset = offsetof(VertexPositionColorTexture, color);
1815
1816 attributeDescriptions[2].binding = 0;
1817 attributeDescriptions[2].location = 2;
1818 attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
1819 attributeDescriptions[2].offset = offsetof(VertexPositionColorTexture, textureCoordinates);
1820
1821 return attributeDescriptions;
1822}
1823
1824template<>
1825std::vector<VkVertexInputAttributeDescription> VulkanRenderer::GetAttributeDescriptions<VertexPositionColorTextureNormalTangent>()
1826{
1827 std::vector<VkVertexInputAttributeDescription> attributeDescriptions{};
1828 attributeDescriptions.resize(5);
1829
1830 attributeDescriptions[0].binding = 0;
1831 attributeDescriptions[0].location = 0;
1832 attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
1833 attributeDescriptions[0].offset = offsetof(VertexPositionColorTextureNormalTangent, position);
1834
1835 attributeDescriptions[1].binding = 0;
1836 attributeDescriptions[1].location = 1;
1837 attributeDescriptions[1].format = VK_FORMAT_R32G32B32A32_SFLOAT;
1838 attributeDescriptions[1].offset = offsetof(VertexPositionColorTextureNormalTangent, color);
1839
1840 attributeDescriptions[2].binding = 0;
1841 attributeDescriptions[2].location = 2;
1842 attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
1843 attributeDescriptions[2].offset = offsetof(VertexPositionColorTextureNormalTangent, textureCoordinates);
1844
1845 attributeDescriptions[3].binding = 0;
1846 attributeDescriptions[3].location = 3;
1847 attributeDescriptions[3].format = VK_FORMAT_R32G32B32_SFLOAT;
1848 attributeDescriptions[3].offset = offsetof(VertexPositionColorTextureNormalTangent, normal);
1849
1850 attributeDescriptions[4].binding = 0;
1851 attributeDescriptions[4].location = 4;
1852 attributeDescriptions[4].format = VK_FORMAT_R32G32B32_SFLOAT;
1853 attributeDescriptions[4].offset = offsetof(VertexPositionColorTextureNormalTangent, tangent);
1854
1855 return attributeDescriptions;
1856}
1857
1858} //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.
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
std::vector< std::string > GetTextures() const
Returns the list of texture names used by this material.
Definition material.cpp:71
PackedMaterialData PackMaterialParameters()
Packs the current float/vector parameters into a binary buffer and layout metadata.
Definition material.cpp:31
const MaterialSettings GetMaterialSettings() const
Returns the material's static settings (domain, blend mode, shading model, etc.).
Definition material.cpp:16
Vulkan-specific implementation of material render resources.
Vulkan implementation of the mesh GPU resource interface.
Vulkan implementation of IRenderResources for the Rendering Engine.
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.
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.
Vulkan-specific implementation of ITextureRenderResources.
const bool enableValidationLayers
const int MAX_FRAMES_IN_FLIGHT
Number of frames that can be processed simultaneously (double buffering).
Contains the raw buffer data and layout metadata of packed material parameters.
Holds indices of Vulkan queue families supporting required operations.
Describes capabilities and available configurations for a physical device's swap chain.