28std::vector<VkVertexInputAttributeDescription>
29VulkanRenderer::GetAttributeDescriptions<Vertex2D>();
32std::vector<VkVertexInputAttributeDescription>
33VulkanRenderer::GetAttributeDescriptions<VertexPositionColorTexture>();
36std::vector<VkVertexInputAttributeDescription>
37VulkanRenderer::GetAttributeDescriptions<VertexPositionColorTextureNormalTangent>();
41 mWindowSystem{windowSystem},
48 SetupDebugMessenger();
51 CreateLogicalDevice();
59 CreateColorResources();
60 CreateDepthResources();
64 CreateCommandBuffers();
65 CreateFrameSyncObjects();
66 CreateSwapchainSyncObjects();
71 vkWaitForFences(mLogicalDevice, 1, &mInFlightFences[mCurrentFrame], VK_TRUE, UINT64_MAX);
74 VkResult result = vkAcquireNextImageKHR(mLogicalDevice, mSwapChain, UINT64_MAX, mImageAvailableSemaphores[mCurrentFrame], VK_NULL_HANDLE, &imageIndex);
76 if (result == VK_ERROR_OUT_OF_DATE_KHR)
78 LOG_WARNING(
"Swapchain out of date. Triggering recreation.");
84 if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR)
86 LOG_ERROR(
"failed to acquire swap chain image!");
87 throw std::runtime_error(
"failed to acquire swap chain image!");
91 vkResetFences(mLogicalDevice, 1, &mInFlightFences[mCurrentFrame]);
93 vkResetCommandBuffer(mCommandBuffers[mCurrentFrame], 0);
94 RecordCommandBuffer(mCommandBuffers[mCurrentFrame], imageIndex);
96 VkSubmitInfo submitInfo{};
97 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
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;
105 submitInfo.commandBufferCount = 1;
106 submitInfo.pCommandBuffers = &mCommandBuffers[mCurrentFrame];
108 VkSemaphore signalSemaphores[] = { mRenderFinishedSemaphores[imageIndex] };
109 submitInfo.signalSemaphoreCount = 1;
110 submitInfo.pSignalSemaphores = signalSemaphores;
112 if (vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, mInFlightFences[mCurrentFrame]) != VK_SUCCESS)
114 LOG_ERROR(
"failed to submit draw command buffer!");
115 throw std::runtime_error(
"failed to submit draw command buffer!");
118 VkPresentInfoKHR presentInfo{};
119 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
121 presentInfo.waitSemaphoreCount = 1;
122 presentInfo.pWaitSemaphores = signalSemaphores;
124 VkSwapchainKHR swapChains[] = { mSwapChain };
125 presentInfo.swapchainCount = 1;
126 presentInfo.pSwapchains = swapChains;
128 presentInfo.pImageIndices = &imageIndex;
130 result = vkQueuePresentKHR(mPresentQueue, &presentInfo);
132 if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || mWindowSystem.
IsFramebufferResized())
139 if (result != VK_SUCCESS)
141 LOG_ERROR(
"failed to present swap chain image!");
142 throw std::runtime_error(
"failed to present swap chain image!");
151 vkWaitForFences(mLogicalDevice, 1, &mInFlightFences[mCurrentFrame], VK_TRUE, UINT64_MAX);
153 VkResult result = vkAcquireNextImageKHR(mLogicalDevice, mSwapChain, UINT64_MAX, mImageAvailableSemaphores[mCurrentFrame], VK_NULL_HANDLE, &mImageIndex);
155 if (result == VK_ERROR_OUT_OF_DATE_KHR)
162 if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR)
164 LOG_ERROR(
"failed to acquire swap chain image!");
165 throw std::runtime_error(
"failed to acquire swap chain image!");
169 if (mImagesInFlight[mImageIndex] != VK_NULL_HANDLE)
171 vkWaitForFences(mLogicalDevice, 1, &mImagesInFlight[mImageIndex], VK_TRUE, UINT64_MAX);
173 mImagesInFlight[mImageIndex] = mInFlightFences[mCurrentFrame];
175 vkResetFences(mLogicalDevice, 1, &mInFlightFences[mCurrentFrame]);
177 ProcessDeferredDestruction();
179 vkResetCommandBuffer(mCommandBuffers[mCurrentFrame], 0);
186 VkCommandBufferBeginInfo beginInfo{};
187 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
191 if (vkBeginCommandBuffer(mCommandBuffers[mCurrentFrame], &beginInfo) != VK_SUCCESS)
193 LOG_ERROR(
"failed to begin recording command buffer!");
194 throw std::runtime_error(
"failed to begin recording command buffer!");
197 VkRenderPassBeginInfo renderPassInfo{};
198 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
199 renderPassInfo.renderPass = mRenderPass;
200 renderPassInfo.framebuffer = mSwapChainFramebuffers[mImageIndex];
202 renderPassInfo.renderArea.offset = { 0, 0 };
203 renderPassInfo.renderArea.extent = mSwapChainExtent;
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();
211 vkCmdBeginRenderPass(mCommandBuffers[mCurrentFrame], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
216 vkCmdEndRenderPass(mCommandBuffers[mCurrentFrame]);
221 if (vkEndCommandBuffer(mCommandBuffers[mCurrentFrame]) != VK_SUCCESS)
223 LOG_ERROR(
"failed to record command buffer!");
224 throw std::runtime_error(
"failed to record command buffer!");
227 VkSubmitInfo submitInfo{};
228 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
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;
236 submitInfo.commandBufferCount = 1;
237 submitInfo.pCommandBuffers = &mCommandBuffers[mCurrentFrame];
239 VkSemaphore signalSemaphores[] = { mRenderFinishedSemaphores[mImageIndex] };
240 submitInfo.signalSemaphoreCount = 1;
241 submitInfo.pSignalSemaphores = signalSemaphores;
243 if (vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, mInFlightFences[mCurrentFrame]) != VK_SUCCESS)
245 LOG_ERROR(
"failed to submit draw command buffer!");
246 throw std::runtime_error(
"failed to submit draw command buffer!");
249 VkPresentInfoKHR presentInfo{};
250 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
252 presentInfo.waitSemaphoreCount = 1;
253 presentInfo.pWaitSemaphores = signalSemaphores;
255 VkSwapchainKHR swapChains[] = { mSwapChain };
256 presentInfo.swapchainCount = 1;
257 presentInfo.pSwapchains = swapChains;
259 presentInfo.pImageIndices = &mImageIndex;
261 VkResult result = vkQueuePresentKHR(mPresentQueue, &presentInfo);
263 if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || mWindowSystem.
IsFramebufferResized())
270 if (result != VK_SUCCESS)
272 LOG_ERROR(
"failed to present swap chain image!");
273 throw std::runtime_error(
"failed to present swap chain image!");
282 vkDeviceWaitIdle(mLogicalDevice);
287 LOG_INFO(
"Shutting down Vulkan renderer...");
288 vkDeviceWaitIdle(mLogicalDevice);
290 mFrameSerial = std::numeric_limits<uint64_t>::max();
291 ProcessDeferredDestruction();
297 vkDestroySemaphore(mLogicalDevice, mImageAvailableSemaphores[i],
nullptr);
298 vkDestroyFence(mLogicalDevice, mInFlightFences[i],
nullptr);
301 vkDestroyCommandPool(mLogicalDevice, mCommandPool,
nullptr);
303 vkDestroyDevice(mLogicalDevice,
nullptr);
306 DestroyDebugUtilsMessengerEXT(mInstance, mDebugMessenger,
nullptr);
308 vkDestroySurfaceKHR(mInstance, mSurface,
nullptr);
309 vkDestroyInstance(mInstance,
nullptr);
310 LOG_INFO(
"Vulkan renderer shutdown complete.");
315 if (std::find(mObservers.begin(), mObservers.end(), notifier) == mObservers.end())
317 mObservers.push_back(notifier);
323 mObservers.erase(std::remove(mObservers.begin(), mObservers.end(), notifier), mObservers.end());
343 mDefaultColor = glm::vec3(r, g, b);
351void VulkanRenderer::CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory)
353 VkBufferCreateInfo bufferInfo{};
354 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
355 bufferInfo.size = size;
357 bufferInfo.usage = usage;
358 bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
360 if (vkCreateBuffer(mLogicalDevice, &bufferInfo,
nullptr, &buffer) != VK_SUCCESS)
362 LOG_ERROR(
"failed to create vertex buffer!");
363 throw std::runtime_error(
"failed to create vertex buffer!");
366 VkMemoryRequirements memRequirements;
367 vkGetBufferMemoryRequirements(mLogicalDevice, buffer, &memRequirements);
369 VkMemoryAllocateInfo allocInfo{};
370 allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
371 allocInfo.allocationSize = memRequirements.size;
372 allocInfo.memoryTypeIndex =
FindMemoryType(memRequirements.memoryTypeBits, properties);
374 if (vkAllocateMemory(mLogicalDevice, &allocInfo,
nullptr, &bufferMemory) != VK_SUCCESS)
376 LOG_ERROR(
"failed to allocate vertex buffer memory!");
377 throw std::runtime_error(
"failed to allocate vertex buffer memory!");
380 vkBindBufferMemory(mLogicalDevice, buffer, bufferMemory, 0);
385 auto commandBuffer = BeginSingleTimeCommand();
387 VkBufferCopy copyRegion{};
388 copyRegion.srcOffset = 0;
389 copyRegion.dstOffset = 0;
390 copyRegion.size = size;
391 vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, ©Region);
393 EndSingleTimeCommand(commandBuffer);
398 return mLogicalDevice;
403 VkCommandBuffer commandBuffer = BeginSingleTimeCommand();
405 VkImageMemoryBarrier barrier{};
406 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
407 barrier.oldLayout = oldLayout;
408 barrier.newLayout = newLayout;
410 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
411 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
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;
420 VkPipelineStageFlags sourceStage;
421 VkPipelineStageFlags destinationStage;
423 if (newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL)
425 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
427 if (HasStencilComponent(format))
429 barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
434 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
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;
441 sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
442 destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
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;
448 sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
449 destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
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;
455 sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
456 destinationStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
459 throw std::invalid_argument(
"unsupported layout transition!");
462 vkCmdPipelineBarrier(
464 sourceStage, destinationStage,
471 EndSingleTimeCommand(commandBuffer);
476 VkCommandBuffer commandBuffer = BeginSingleTimeCommand();
478 VkBufferImageCopy region{};
479 region.bufferOffset = 0;
480 region.bufferRowLength = 0;
481 region.bufferImageHeight = 0;
483 region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
484 region.imageSubresource.mipLevel = 0;
485 region.imageSubresource.baseArrayLayer = 0;
486 region.imageSubresource.layerCount = 1;
488 region.imageOffset = { 0, 0, 0 };
489 region.imageExtent = {
495 vkCmdCopyBufferToImage(
499 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
504 EndSingleTimeCommand(commandBuffer);
510 VkFormatProperties formatProperties;
511 vkGetPhysicalDeviceFormatProperties(mPhysicalDevice, imageFormat, &formatProperties);
513 if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT))
515 LOG_ERROR(
"texture image format does not support linear blitting!");
516 throw std::runtime_error(
"texture image format does not support linear blitting!");
519 VkCommandBuffer commandBuffer = BeginSingleTimeCommand();
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;
531 int32_t mipWidth = texWidth;
532 int32_t mipHeight = texHeight;
534 for (uint32_t i = 1; i < mipLevels; i++)
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;
542 vkCmdPipelineBarrier(commandBuffer,
543 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
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;
562 vkCmdBlitImage(commandBuffer,
563 image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
564 image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
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;
573 vkCmdPipelineBarrier(commandBuffer,
574 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
579 if (mipWidth > 1) mipWidth /= 2;
580 if (mipHeight > 1) mipHeight /= 2;
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;
589 vkCmdPipelineBarrier(commandBuffer,
590 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
595 EndSingleTimeCommand(commandBuffer);
600 switch (deferredItem.
type)
608 mDeferredQueue.push_back(deferredItem);
611void VulkanRenderer::CreateInstance()
616 LOG_ERROR(
"validation layers requested, but not available!");
617 throw std::runtime_error(
"validation layers requested, but not available!");
620 VkApplicationInfo appInfo{};
621 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
623 appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
624 appInfo.pEngineName =
"Rendering Engine";
625 appInfo.engineVersion = VK_MAKE_VERSION(
626 RENDERING_ENGINE_VERSION_MAJOR,
627 RENDERING_ENGINE_VERSION_MINOR,
628 RENDERING_ENGINE_VERSION_PATCH
630 appInfo.apiVersion = VK_API_VERSION_1_0;
632 VkInstanceCreateInfo createInfo{};
633 createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
634 createInfo.pApplicationInfo = &appInfo;
636 auto extensionsNames = GetRequiredExtensions();
637 createInfo.enabledExtensionCount =
static_cast<uint32_t
>(extensionsNames.size());
638 createInfo.ppEnabledExtensionNames = extensionsNames.data();
640 VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{};
643 createInfo.enabledLayerCount =
static_cast<uint32_t
>(mValidationLayers.size());
644 createInfo.ppEnabledLayerNames = mValidationLayers.data();
646 PopulateDebugMessengerCreateInfo(debugCreateInfo);
647 createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*)&debugCreateInfo;
651 createInfo.enabledLayerCount = 0;
653 createInfo.pNext =
nullptr;
656 if (vkCreateInstance(&createInfo,
nullptr, &mInstance) != VK_SUCCESS)
659 throw std::runtime_error(
"failed to create instance!");
661 LOG_INFO(
"Vulkan instance created.");
664bool VulkanRenderer::CheckValidationLayerSupport()
667 vkEnumerateInstanceLayerProperties(&layerCount,
nullptr);
669 std::vector<VkLayerProperties> availableLayers(layerCount);
670 vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
672 for (
const char* layerName : mValidationLayers)
674 bool layerFound =
false;
676 for (
const auto& layerProperties : availableLayers)
678 if (strcmp(layerName, layerProperties.layerName) == 0)
694std::vector<const char*> VulkanRenderer::GetRequiredExtensions()
696 uint32_t glfwExtensionCount = 0;
697 const char** glfwExtensions;
698 glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
700 std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
704 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
710void VulkanRenderer::PopulateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo)
713 createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
714 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;
715 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;
716 createInfo.pfnUserCallback = DebugCallback;
719VKAPI_ATTR VkBool32 VKAPI_CALL VulkanRenderer::DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData)
721 if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
723 LOG_ERROR(std::string(
"Vulkan validation: ") + pCallbackData->pMessage);
727 if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT)
729 LOG_WARNING(std::string(
"Vulkan validation: ") + pCallbackData->pMessage);
733 LOG_DEBUG(std::string(
"Vulkan validation: ") + pCallbackData->pMessage);
740void VulkanRenderer::SetupDebugMessenger()
747 VkDebugUtilsMessengerCreateInfoEXT createInfo{};
748 PopulateDebugMessengerCreateInfo(createInfo);
750 if (CreateDebugUtilsMessengerEXT(mInstance, &createInfo,
nullptr, &mDebugMessenger) != VK_SUCCESS)
752 LOG_ERROR(
"failed to set up debug messenger!");
753 throw std::runtime_error(
"failed to set up debug messenger!");
757VkResult VulkanRenderer::CreateDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerCreateInfoEXT
const* pCreateInfo, VkAllocationCallbacks
const* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger)
759 auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance,
"vkCreateDebugUtilsMessengerEXT");
762 return func(instance, pCreateInfo, pAllocator, pDebugMessenger);
766 return VK_ERROR_EXTENSION_NOT_PRESENT;
770void VulkanRenderer::DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger,
const VkAllocationCallbacks* pAllocator)
772 auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance,
"vkDestroyDebugUtilsMessengerEXT");
775 func(instance, debugMessenger, pAllocator);
779void VulkanRenderer::CreateSurface()
781 GLFWwindow* window =
static_cast<GLFWwindow*
>(mWindowSystem.
GetNativeHandle());
782 if (glfwCreateWindowSurface(mInstance, window,
nullptr, &mSurface) != VK_SUCCESS)
784 LOG_ERROR(
"failed to create window surface!");
785 throw std::runtime_error(
"failed to create window surface!");
789void VulkanRenderer::PickPhysicalDevice()
791 uint32_t deviceCount = 0;
792 vkEnumeratePhysicalDevices(mInstance, &deviceCount,
nullptr);
794 if (deviceCount == 0)
796 LOG_ERROR(
"failed to find GPUs with Vulkan support!");
797 throw std::runtime_error(
"failed to find GPUs with Vulkan support!");
800 std::vector<VkPhysicalDevice> devices(deviceCount);
801 vkEnumeratePhysicalDevices(mInstance, &deviceCount, devices.data());
803 for (
const auto& device : devices)
805 if (IsDeviceSuitable(device)) {
806 mPhysicalDevice = device;
807 vkGetPhysicalDeviceFeatures(mPhysicalDevice, &mPhysDevSupportedFeatures);
808 vkGetPhysicalDeviceProperties(mPhysicalDevice, &mPhysicalDeviceProperties);
809 mMSAASamples = CheckMaxUsableSampleCount();
814 if (mPhysicalDevice == VK_NULL_HANDLE)
816 LOG_ERROR(
"Failed to find a suitable GPU!");
817 throw std::runtime_error(
"failed to find a suitable GPU!");
819 LOG_INFO(std::string(
"Selected GPU: ") +
820 mPhysicalDeviceProperties.deviceName);
823bool VulkanRenderer::IsDeviceSuitable(VkPhysicalDevice device)
836 QueueFamilyIndices indices = FindQueueFamilies(device);
838 bool extensionsSupported = CheckDeviceExtensionSupport(device);
840 bool swapChainAdequate =
false;
841 if (extensionsSupported)
843 auto const swapChainSupport = QuerySwapChainSupport(device);
844 swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
846 return indices.IsComplete() && extensionsSupported && swapChainAdequate;
849QueueFamilyIndices VulkanRenderer::FindQueueFamilies(VkPhysicalDevice device)
851 QueueFamilyIndices indices;
853 uint32_t queueFamilyCount = 0;
854 vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount,
nullptr);
856 std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
857 vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
860 for (
const auto& queueFamily : queueFamilies)
862 if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT)
864 indices.graphicsFamily = i;
867 VkBool32 presentSupport =
false;
868 vkGetPhysicalDeviceSurfaceSupportKHR(device, i, mSurface, &presentSupport);
871 indices.presentFamily = i;
873 if (indices.IsComplete())
883bool VulkanRenderer::CheckDeviceExtensionSupport(VkPhysicalDevice physicalDevice)
885 uint32_t extensionCount;
886 vkEnumerateDeviceExtensionProperties(physicalDevice,
nullptr, &extensionCount,
nullptr);
888 std::vector<VkExtensionProperties> availableExtensions(extensionCount);
889 vkEnumerateDeviceExtensionProperties(physicalDevice,
nullptr, &extensionCount, availableExtensions.data());
891 std::set<std::string> requiredExtensions(mDeviceExtensions.begin(), mDeviceExtensions.end());
893 for (
const auto& extension : availableExtensions)
895 requiredExtensions.erase(extension.extensionName);
898 return requiredExtensions.empty();
901SwapChainSupportDetails VulkanRenderer::QuerySwapChainSupport(VkPhysicalDevice device)
903 SwapChainSupportDetails scsDetails;
905 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, mSurface, &scsDetails.capabilities);
907 uint32_t formatCount;
908 vkGetPhysicalDeviceSurfaceFormatsKHR(device, mSurface, &formatCount,
nullptr);
910 if (formatCount != 0)
912 scsDetails.formats.resize(formatCount);
913 vkGetPhysicalDeviceSurfaceFormatsKHR(device, mSurface, &formatCount, scsDetails.formats.data());
916 uint32_t presentModeCount;
917 vkGetPhysicalDeviceSurfacePresentModesKHR(device, mSurface, &presentModeCount,
nullptr);
919 if (presentModeCount != 0)
921 scsDetails.presentModes.resize(presentModeCount);
922 vkGetPhysicalDeviceSurfacePresentModesKHR(device, mSurface, &presentModeCount, scsDetails.presentModes.data());
928VkSampleCountFlagBits VulkanRenderer::CheckMaxUsableSampleCount()
930 VkPhysicalDeviceProperties physicalDeviceProperties;
931 vkGetPhysicalDeviceProperties(mPhysicalDevice, &physicalDeviceProperties);
933 VkSampleCountFlags counts = physicalDeviceProperties.limits.framebufferColorSampleCounts & physicalDeviceProperties.limits.framebufferDepthSampleCounts;
934 if (counts & VK_SAMPLE_COUNT_64_BIT) {
return VK_SAMPLE_COUNT_64_BIT; }
935 if (counts & VK_SAMPLE_COUNT_32_BIT) {
return VK_SAMPLE_COUNT_32_BIT; }
936 if (counts & VK_SAMPLE_COUNT_16_BIT) {
return VK_SAMPLE_COUNT_16_BIT; }
937 if (counts & VK_SAMPLE_COUNT_8_BIT) {
return VK_SAMPLE_COUNT_8_BIT; }
938 if (counts & VK_SAMPLE_COUNT_4_BIT) {
return VK_SAMPLE_COUNT_4_BIT; }
939 if (counts & VK_SAMPLE_COUNT_2_BIT) {
return VK_SAMPLE_COUNT_2_BIT; }
941 return VK_SAMPLE_COUNT_1_BIT;
944void VulkanRenderer::CreateLogicalDevice()
946 QueueFamilyIndices indices = FindQueueFamilies(mPhysicalDevice);
948 std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
949 std::set<uint32_t> uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() };
951 float queuePriority = 1.0f;
952 for (uint32_t queueFamily : uniqueQueueFamilies)
954 VkDeviceQueueCreateInfo queueCreateInfo{};
955 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
956 queueCreateInfo.queueFamilyIndex = queueFamily;
957 queueCreateInfo.queueCount = 1;
958 queueCreateInfo.pQueuePriorities = &queuePriority;
959 queueCreateInfos.push_back(queueCreateInfo);
962 VkDeviceQueueCreateInfo queueCreateInfo{};
963 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
964 queueCreateInfo.queueFamilyIndex = indices.graphicsFamily.value();
965 queueCreateInfo.queueCount = 1;
967 queueCreateInfo.pQueuePriorities = &queuePriority;
969 VkPhysicalDeviceFeatures deviceFeatures{};
970 if (mPhysDevSupportedFeatures.samplerAnisotropy)
972 deviceFeatures.samplerAnisotropy = VK_TRUE;
976 deviceFeatures.samplerAnisotropy = VK_FALSE;
979 if (mPhysDevSupportedFeatures.sampleRateShading)
981 deviceFeatures.sampleRateShading = VK_TRUE;
985 deviceFeatures.sampleRateShading = VK_FALSE;
988 VkDeviceCreateInfo createInfo{};
989 createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
991 createInfo.pQueueCreateInfos = queueCreateInfos.data();
992 createInfo.queueCreateInfoCount =
static_cast<uint32_t
>(queueCreateInfos.size());
994 createInfo.pEnabledFeatures = &deviceFeatures;
996 createInfo.enabledExtensionCount =
static_cast<uint32_t
>(mDeviceExtensions.size());
997 createInfo.ppEnabledExtensionNames = mDeviceExtensions.data();
1001 createInfo.enabledLayerCount =
static_cast<uint32_t
>(mValidationLayers.size());
1002 createInfo.ppEnabledLayerNames = mValidationLayers.data();
1006 createInfo.enabledLayerCount = 0;
1009 if (vkCreateDevice(mPhysicalDevice, &createInfo,
nullptr, &mLogicalDevice) != VK_SUCCESS)
1011 LOG_ERROR(
"failed to create logical device!");
1012 throw std::runtime_error(
"failed to create logical device!");
1015 vkGetDeviceQueue(mLogicalDevice, indices.graphicsFamily.value(), 0, &mGraphicsQueue);
1016 vkGetDeviceQueue(mLogicalDevice, indices.presentFamily.value(), 0, &mPresentQueue);
1017 LOG_INFO(
"Logical device created.");
1020void VulkanRenderer::CreateSwapChain()
1022 SwapChainSupportDetails swapChainSupport = QuerySwapChainSupport(mPhysicalDevice);
1024 VkSurfaceFormatKHR surfaceFormat = ChooseSwapSurfaceFormat(swapChainSupport.formats);
1026 VkPresentModeKHR presentMode = ChooseSwapPresentMode(swapChainSupport.presentModes);
1027 VkExtent2D extent = ChooseSwapExtent(swapChainSupport.capabilities);
1029 uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
1030 if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount)
1032 imageCount = swapChainSupport.capabilities.maxImageCount;
1035 VkSwapchainCreateInfoKHR createInfo{};
1036 createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
1037 createInfo.surface = mSurface;
1039 createInfo.minImageCount = imageCount;
1040 createInfo.imageFormat = surfaceFormat.format;
1041 createInfo.imageColorSpace = surfaceFormat.colorSpace;
1042 createInfo.imageExtent = extent;
1043 createInfo.imageArrayLayers = 1;
1044 createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
1046 QueueFamilyIndices indices = FindQueueFamilies(mPhysicalDevice);
1047 uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() };
1049 if (indices.graphicsFamily != indices.presentFamily) {
1050 createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
1051 createInfo.queueFamilyIndexCount = 2;
1052 createInfo.pQueueFamilyIndices = queueFamilyIndices;
1056 createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
1057 createInfo.queueFamilyIndexCount = 0;
1058 createInfo.pQueueFamilyIndices =
nullptr;
1061 createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
1062 createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
1063 createInfo.presentMode = presentMode;
1064 createInfo.clipped = VK_TRUE;
1065 createInfo.oldSwapchain = VK_NULL_HANDLE;
1067 if (vkCreateSwapchainKHR(mLogicalDevice, &createInfo,
nullptr, &mSwapChain) != VK_SUCCESS)
1069 LOG_ERROR(
"failed to create swap chain!");
1070 throw std::runtime_error(
"failed to create swap chain!");
1073 vkGetSwapchainImagesKHR(mLogicalDevice, mSwapChain, &imageCount,
nullptr);
1074 mSwapChainImages.resize(imageCount);
1075 vkGetSwapchainImagesKHR(mLogicalDevice, mSwapChain, &imageCount, mSwapChainImages.data());
1077 mSwapChainImageFormat = surfaceFormat.format;
1078 mSwapChainExtent = extent;
1080 LOG_INFO(
"Swapchain created. Images: " +
1081 std::to_string(mSwapChainImages.size()) +
1083 std::to_string(mSwapChainExtent.width) +
"x" +
1084 std::to_string(mSwapChainExtent.height));
1087void VulkanRenderer::RecreateSwapChain()
1093 while (width == 0 || height == 0)
1095 GLFWwindow* window =
static_cast<GLFWwindow*
>(mWindowSystem.
GetNativeHandle());
1096 glfwGetFramebufferSize(window, &width, &height);
1099 vkDeviceWaitIdle(mLogicalDevice);
1103 for (
auto& observer : mObservers)
1105 observer->OnRenderResourcesRelease();
1112 CreateColorResources();
1113 CreateDepthResources();
1114 CreateFramebuffers();
1115 CreateCommandBuffers();
1117 CreateSwapchainSyncObjects();
1119 for (
auto& observer : mObservers)
1121 observer->OnRenderResourcesRebuild();
1123 LOG_INFO(
"Swapchain recreated successfully.");
1126VkSurfaceFormatKHR VulkanRenderer::ChooseSwapSurfaceFormat(std::vector<VkSurfaceFormatKHR>
const& availableFormats)
1128 for (
const auto& availableFormat : availableFormats)
1130 if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
1132 return availableFormat;
1136 return availableFormats[0];
1139VkPresentModeKHR VulkanRenderer::ChooseSwapPresentMode(std::vector<VkPresentModeKHR>
const& availablePresentModes)
1141 for (
const auto& availablePresentMode : availablePresentModes)
1143 if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR)
1145 return availablePresentMode;
1148 return VK_PRESENT_MODE_FIFO_KHR;
1151VkExtent2D VulkanRenderer::ChooseSwapExtent(VkSurfaceCapabilitiesKHR
const& capabilities)
1153 if (capabilities.currentExtent.width != UINT32_MAX)
1155 return capabilities.currentExtent;
1160 GLFWwindow* window =
static_cast<GLFWwindow*
>(mWindowSystem.
GetNativeHandle());
1161 glfwGetFramebufferSize(window, &width, &height);
1163 VkExtent2D actualExtent = {
1164 static_cast<uint32_t
>(width),
1165 static_cast<uint32_t
>(height)
1168 actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
1169 actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
1171 return actualExtent;
1175void VulkanRenderer::CreateImageViews()
1177 mSwapChainImageViews.resize(mSwapChainImages.size());
1179 for (uint32_t i = 0; i < mSwapChainImages.size(); i++)
1181 mSwapChainImageViews[i] = CreateImageView(mSwapChainImages[i], mSwapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT, 1);
1185VkImageView VulkanRenderer::CreateImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags, std::uint32_t mipmapLevels)
1187 VkImageViewCreateInfo viewInfo{};
1188 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1189 viewInfo.image = image;
1190 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
1191 viewInfo.format = format;
1192 viewInfo.subresourceRange.aspectMask = aspectFlags;
1193 viewInfo.subresourceRange.baseMipLevel = 0;
1194 viewInfo.subresourceRange.levelCount = mipmapLevels;
1195 viewInfo.subresourceRange.baseArrayLayer = 0;
1196 viewInfo.subresourceRange.layerCount = 1;
1198 VkImageView imageView;
1199 if (vkCreateImageView(mLogicalDevice, &viewInfo,
nullptr, &imageView) != VK_SUCCESS)
1201 LOG_ERROR(
"failed to create texture image view!");
1202 throw std::runtime_error(
"failed to create texture image view!");
1208void VulkanRenderer::CreateRenderPass()
1210 VkAttachmentDescription colorAttachment{};
1211 colorAttachment.format = mSwapChainImageFormat;
1212 colorAttachment.samples = mMSAASamples;
1213 colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1214 colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1215 colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1216 colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1217 colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1218 colorAttachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1220 VkAttachmentReference colorAttachmentRef{};
1221 colorAttachmentRef.attachment = 0;
1222 colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1224 VkAttachmentDescription depthAttachment{};
1225 depthAttachment.format = FindDepthFormat();
1226 depthAttachment.samples = mMSAASamples;
1227 depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1228 depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1229 depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1230 depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1231 depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1232 depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1234 VkAttachmentReference depthAttachmentRef{};
1235 depthAttachmentRef.attachment = 1;
1236 depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1238 VkAttachmentDescription colorAttachmentResolve{};
1239 colorAttachmentResolve.format = mSwapChainImageFormat;
1240 colorAttachmentResolve.samples = VK_SAMPLE_COUNT_1_BIT;
1241 colorAttachmentResolve.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1242 colorAttachmentResolve.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1243 colorAttachmentResolve.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1244 colorAttachmentResolve.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1245 colorAttachmentResolve.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1246 colorAttachmentResolve.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
1248 VkAttachmentReference colorAttachmentResolveRef{};
1249 colorAttachmentResolveRef.attachment = 2;
1250 colorAttachmentResolveRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1252 VkSubpassDescription subpass{};
1253 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
1254 subpass.colorAttachmentCount = 1;
1255 subpass.pColorAttachments = &colorAttachmentRef;
1256 subpass.pDepthStencilAttachment = &depthAttachmentRef;
1257 subpass.pResolveAttachments = &colorAttachmentResolveRef;
1259 VkSubpassDependency dependency{};
1260 dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
1261 dependency.dstSubpass = 0;
1262 dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
1263 dependency.srcAccessMask = 0;
1264 dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
1265 dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
1267 std::array<VkAttachmentDescription, 3> attachments = { colorAttachment, depthAttachment, colorAttachmentResolve };
1269 VkRenderPassCreateInfo renderPassInfo{};
1270 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
1271 renderPassInfo.attachmentCount =
static_cast<uint32_t
>(attachments.size());
1272 renderPassInfo.pAttachments = attachments.data();
1273 renderPassInfo.subpassCount = 1;
1274 renderPassInfo.pSubpasses = &subpass;
1275 renderPassInfo.dependencyCount = 1;
1276 renderPassInfo.pDependencies = &dependency;
1278 if (vkCreateRenderPass(mLogicalDevice, &renderPassInfo,
nullptr, &mRenderPass) != VK_SUCCESS)
1280 LOG_ERROR(
"failed to create render pass!");
1281 throw std::runtime_error(
"failed to create render pass!");
1285VkFormat VulkanRenderer::FindDepthFormat()
1287 return FindSupportedFormat(
1288 { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT },
1289 VK_IMAGE_TILING_OPTIMAL,
1290 VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
1294VkFormat VulkanRenderer::FindSupportedFormat(
const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features)
1296 for (VkFormat format : candidates)
1298 VkFormatProperties props;
1299 vkGetPhysicalDeviceFormatProperties(mPhysicalDevice, format, &props);
1301 if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features)
1305 else if (tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features) == features)
1310 LOG_ERROR(
"failed to find supported format!");
1311 throw std::runtime_error(
"failed to find supported format!");
1314void VulkanRenderer::CreateCommandPool()
1316 QueueFamilyIndices queueFamilyIndices = FindQueueFamilies(mPhysicalDevice);
1318 VkCommandPoolCreateInfo poolInfo{};
1319 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
1320 poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
1321 poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
1323 if (vkCreateCommandPool(mLogicalDevice, &poolInfo,
nullptr, &mCommandPool) != VK_SUCCESS)
1325 LOG_ERROR(
"failed to create command pool!");
1326 throw std::runtime_error(
"failed to create command pool!");
1330void VulkanRenderer::CreateColorResources()
1332 VkFormat colorFormat = mSwapChainImageFormat;
1334 CreateVulkanImage(mSwapChainExtent.width, mSwapChainExtent.height, 1, mMSAASamples, colorFormat, VK_IMAGE_TILING_OPTIMAL,
1335 VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
1336 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mColorImage, mColorImageMemory);
1337 mColorImageView = CreateImageView(mColorImage, colorFormat, VK_IMAGE_ASPECT_COLOR_BIT, 1);
1340void 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)
1342 VkImageCreateInfo imageInfo{};
1343 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
1344 imageInfo.imageType = VK_IMAGE_TYPE_2D;
1345 imageInfo.extent.width =
static_cast<uint32_t
>(width);
1346 imageInfo.extent.height =
static_cast<uint32_t
>(height);
1347 imageInfo.extent.depth = 1;
1348 imageInfo.mipLevels = mipmapLevels;
1349 imageInfo.arrayLayers = 1;
1351 imageInfo.format = format;
1352 imageInfo.tiling = tiling;
1354 imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1356 imageInfo.usage = usage;
1358 imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
1360 imageInfo.samples = numSamples;
1361 imageInfo.flags = 0;
1363 if (vkCreateImage(mLogicalDevice, &imageInfo,
nullptr, &image) != VK_SUCCESS)
1366 throw std::runtime_error(
"failed to create image!");
1369 VkMemoryRequirements memRequirements;
1370 vkGetImageMemoryRequirements(mLogicalDevice, image, &memRequirements);
1372 VkMemoryAllocateInfo allocInfo{};
1373 allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1374 allocInfo.allocationSize = memRequirements.size;
1375 allocInfo.memoryTypeIndex =
FindMemoryType(memRequirements.memoryTypeBits, properties);
1377 if (vkAllocateMemory(mLogicalDevice, &allocInfo,
nullptr, &imageMemory) != VK_SUCCESS) {
1378 LOG_ERROR(
"failed to allocate image memory!");
1379 throw std::runtime_error(
"failed to allocate image memory!");
1382 vkBindImageMemory(mLogicalDevice, image, imageMemory, 0);
1387 VkPhysicalDeviceMemoryProperties memProperties;
1388 vkGetPhysicalDeviceMemoryProperties(mPhysicalDevice, &memProperties);
1390 for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++)
1392 if (typeFilter & (1 << i) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties)
1397 LOG_ERROR(
"failed to find suitable memory type!");
1398 throw std::runtime_error(
"failed to find suitable memory type!");
1403 return mPhysicalDevice;
1408 return mPhysDevSupportedFeatures;
1413 std::uint32_t bindingNumber = 0;
1414 std::vector<VkDescriptorSetLayoutBinding> bindings;
1417 VkDescriptorSetLayoutBinding uboLayoutBinding{};
1418 uboLayoutBinding.binding = bindingNumber;
1419 uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
1420 uboLayoutBinding.descriptorCount = 1;
1421 uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
1422 uboLayoutBinding.pImmutableSamplers =
nullptr;
1424 bindings.push_back(uboLayoutBinding);
1427 if (!packedMaterialData.
buffer.empty())
1431 VkDescriptorSetLayoutBinding customMaterialVariables;
1432 customMaterialVariables.binding = bindingNumber;
1433 customMaterialVariables.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
1434 customMaterialVariables.descriptorCount = 1;
1435 customMaterialVariables.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
1436 customMaterialVariables.pImmutableSamplers =
nullptr;
1438 bindings.push_back(customMaterialVariables);
1441 for (
const auto& textureName : material->
GetTextures())
1445 VkDescriptorSetLayoutBinding samplerLayoutBinding{};
1446 samplerLayoutBinding.binding = bindingNumber;
1447 samplerLayoutBinding.descriptorCount = 1;
1448 samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1449 samplerLayoutBinding.pImmutableSamplers =
nullptr;
1450 samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
1452 bindings.push_back(samplerLayoutBinding);
1455 VkDescriptorSetLayoutCreateInfo layoutInfo{};
1456 layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
1457 layoutInfo.bindingCount =
static_cast<uint32_t
>(bindings.size());
1458 layoutInfo.pBindings = bindings.data();
1460 VkDescriptorSetLayout result;
1461 if (vkCreateDescriptorSetLayout(mLogicalDevice, &layoutInfo,
nullptr, &result) != VK_SUCCESS)
1463 LOG_ERROR(
"failed to create descriptor set layout!");
1464 throw std::runtime_error(
"failed to create descriptor set layout!");
1472 return mCommandBuffers;
1477 VkPipelineLayout pipelineLayout;
1478 VkPipeline pipeline;
1480 VkShaderModule vertShaderModule = CreateShaderModule(spvVertShaderCode);
1481 VkShaderModule fragShaderModule = CreateShaderModule(spvFragShaderCode);
1483 VkPipelineShaderStageCreateInfo vertShaderStageInfo{};
1484 vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
1485 vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
1487 vertShaderStageInfo.module = vertShaderModule;
1488 vertShaderStageInfo.pName =
"main";
1490 VkPipelineShaderStageCreateInfo fragShaderStageInfo{};
1491 fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
1492 fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
1493 fragShaderStageInfo.module = fragShaderModule;
1494 fragShaderStageInfo.pName =
"main";
1496 VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo };
1498 VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
1499 vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
1501 VkVertexInputBindingDescription bindingDescription;
1502 std::vector<VkVertexInputAttributeDescription> attributeDescriptions{};
1506 bindingDescription = GetBindingDescription<Vertex2D>();
1507 attributeDescriptions = GetAttributeDescriptions<Vertex2D>();
1511 bindingDescription = GetBindingDescription<VertexPositionColorTextureNormalTangent>();
1512 attributeDescriptions = GetAttributeDescriptions<VertexPositionColorTextureNormalTangent>();
1515 vertexInputInfo.vertexBindingDescriptionCount = 1;
1516 vertexInputInfo.vertexAttributeDescriptionCount =
static_cast<uint32_t
>(attributeDescriptions.size());
1518 vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
1519 vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();
1521 VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
1522 inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
1523 inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
1524 inputAssembly.primitiveRestartEnable = VK_FALSE;
1526 VkViewport viewport{};
1529 viewport.width = (float)mSwapChainExtent.width;
1530 viewport.height = (
float)mSwapChainExtent.height;
1531 viewport.minDepth = 0.0f;
1532 viewport.maxDepth = 1.0f;
1535 scissor.offset = { 0, 0 };
1536 scissor.extent = mSwapChainExtent;
1538 VkPipelineViewportStateCreateInfo viewportState{};
1539 viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
1540 viewportState.viewportCount = 1;
1541 viewportState.pViewports = &viewport;
1542 viewportState.scissorCount = 1;
1543 viewportState.pScissors = &scissor;
1545 VkPipelineRasterizationStateCreateInfo rasterizer{};
1546 rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
1547 rasterizer.depthClampEnable = VK_FALSE;
1548 rasterizer.rasterizerDiscardEnable = VK_FALSE;
1549 rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
1550 rasterizer.lineWidth = 1.0f;
1553 rasterizer.cullMode = VK_CULL_MODE_NONE;
1557 rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
1559 rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
1560 rasterizer.depthBiasEnable = VK_FALSE;
1561 rasterizer.depthBiasConstantFactor = 0.0f;
1562 rasterizer.depthBiasClamp = 0.0f;
1563 rasterizer.depthBiasSlopeFactor = 0.0f;
1565 VkPipelineMultisampleStateCreateInfo multisampling{};
1566 multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
1567 multisampling.sampleShadingEnable = VK_FALSE;
1568 multisampling.rasterizationSamples = mMSAASamples;
1569 multisampling.minSampleShading = 1.0f;
1570 multisampling.pSampleMask =
nullptr;
1571 multisampling.alphaToCoverageEnable = VK_FALSE;
1572 multisampling.alphaToOneEnable = VK_FALSE;
1574 VkPipelineColorBlendAttachmentState colorBlendAttachment{};
1575 colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
1577 colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
1578 colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
1579 colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
1580 colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
1581 colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
1582 colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
1584 VkPipelineColorBlendStateCreateInfo colorBlending{};
1585 colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
1586 colorBlending.logicOpEnable = VK_FALSE;
1587 colorBlending.logicOp = VK_LOGIC_OP_COPY;
1588 colorBlending.attachmentCount = 1;
1589 colorBlending.pAttachments = &colorBlendAttachment;
1590 colorBlending.blendConstants[0] = 0.0f;
1591 colorBlending.blendConstants[1] = 0.0f;
1592 colorBlending.blendConstants[2] = 0.0f;
1593 colorBlending.blendConstants[3] = 0.0f;
1596 std::vector<VkDynamicState> dynamicStates = {
1597 VK_DYNAMIC_STATE_VIEWPORT,
1598 VK_DYNAMIC_STATE_SCISSOR
1600 VkPipelineDynamicStateCreateInfo dynamicState{};
1601 dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
1602 dynamicState.dynamicStateCount =
static_cast<uint32_t
>(dynamicStates.size());
1603 dynamicState.pDynamicStates = dynamicStates.data();
1605 VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
1606 pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
1607 pipelineLayoutInfo.setLayoutCount = 1;
1608 pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout;
1610 if (vkCreatePipelineLayout(mLogicalDevice, &pipelineLayoutInfo,
nullptr, &pipelineLayout) != VK_SUCCESS)
1612 LOG_ERROR(
"failed to create pipeline layout!");
1613 throw std::runtime_error(
"failed to create pipeline layout!");
1616 VkPipelineDepthStencilStateCreateInfo depthStencil{};
1617 depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
1620 depthStencil.depthTestEnable = VK_FALSE;
1621 depthStencil.depthWriteEnable = VK_FALSE;
1625 depthStencil.depthTestEnable = VK_TRUE;
1628 depthStencil.depthCompareOp = VK_COMPARE_OP_LESS;
1629 depthStencil.depthBoundsTestEnable = VK_FALSE;
1632 depthStencil.stencilTestEnable = VK_FALSE;
1634 depthStencil.back = {};
1636 VkGraphicsPipelineCreateInfo pipelineInfo{};
1637 pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
1638 pipelineInfo.stageCount = 2;
1639 pipelineInfo.pStages = shaderStages;
1640 pipelineInfo.pVertexInputState = &vertexInputInfo;
1641 pipelineInfo.pInputAssemblyState = &inputAssembly;
1642 pipelineInfo.pViewportState = &viewportState;
1643 pipelineInfo.pRasterizationState = &rasterizer;
1644 pipelineInfo.pMultisampleState = &multisampling;
1645 pipelineInfo.pDepthStencilState = &depthStencil;
1646 pipelineInfo.pColorBlendState = &colorBlending;
1647 pipelineInfo.pDynamicState =
nullptr;
1648 pipelineInfo.layout = pipelineLayout;
1649 pipelineInfo.renderPass = mRenderPass;
1650 pipelineInfo.subpass = 0;
1651 pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
1652 pipelineInfo.basePipelineIndex = -1;
1655 if (vkCreateGraphicsPipelines(mLogicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo,
nullptr, &pipeline) != VK_SUCCESS)
1657 LOG_ERROR(
"failed to create graphics pipeline!");
1658 throw std::runtime_error(
"failed to create graphics pipeline!");
1660 vkDestroyShaderModule(mLogicalDevice, fragShaderModule,
nullptr);
1661 vkDestroyShaderModule(mLogicalDevice, vertShaderModule,
nullptr);
1663 return std::pair<VkPipelineLayout, VkPipeline>(pipelineLayout, pipeline);
1666void VulkanRenderer::CreateDepthResources()
1668 VkFormat depthFormat = FindDepthFormat();
1670 CreateVulkanImage(mSwapChainExtent.width, mSwapChainExtent.height, 1, mMSAASamples, depthFormat,
1671 VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
1672 mDepthImage, mDepthImageMemory);
1673 mDepthImageView = CreateImageView(mDepthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT, 1);
1676void VulkanRenderer::CreateFramebuffers()
1678 mSwapChainFramebuffers.resize(mSwapChainImageViews.size());
1680 for (
size_t i = 0; i < mSwapChainImageViews.size(); i++)
1682 std::array<VkImageView, 3> attachments = { mColorImageView, mDepthImageView, mSwapChainImageViews[i] };
1684 VkFramebufferCreateInfo framebufferInfo{};
1685 framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
1686 framebufferInfo.renderPass = mRenderPass;
1687 framebufferInfo.attachmentCount =
static_cast<uint32_t
>(attachments.size());
1688 framebufferInfo.pAttachments = attachments.data();
1689 framebufferInfo.width = mSwapChainExtent.width;
1690 framebufferInfo.height = mSwapChainExtent.height;
1691 framebufferInfo.layers = 1;
1693 if (vkCreateFramebuffer(mLogicalDevice, &framebufferInfo,
nullptr, &mSwapChainFramebuffers[i]) != VK_SUCCESS)
1695 LOG_ERROR(
"failed to create framebuffer!");
1696 throw std::runtime_error(
"failed to create framebuffer!");
1701void VulkanRenderer::CreateCommandBuffers()
1705 VkCommandBufferAllocateInfo allocInfo{};
1706 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1707 allocInfo.commandPool = mCommandPool;
1708 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1709 allocInfo.commandBufferCount = (uint32_t)mCommandBuffers.size();
1711 if (vkAllocateCommandBuffers(mLogicalDevice, &allocInfo, mCommandBuffers.data()) != VK_SUCCESS)
1713 LOG_ERROR(
"failed to allocate command buffers!");
1714 throw std::runtime_error(
"failed to allocate command buffers!");
1718void VulkanRenderer::CreateFrameSyncObjects()
1723 VkSemaphoreCreateInfo semaphoreInfo{ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
1724 VkFenceCreateInfo fenceInfo{ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
1725 fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
1729 if (vkCreateSemaphore(mLogicalDevice, &semaphoreInfo,
nullptr, &mImageAvailableSemaphores[i]) != VK_SUCCESS ||
1730 vkCreateFence(mLogicalDevice, &fenceInfo,
nullptr, &mInFlightFences[i]) != VK_SUCCESS)
1732 LOG_ERROR(
"failed to create per-frame sync objects!");
1733 throw std::runtime_error(
"failed to create per-frame sync objects!");
1738void VulkanRenderer::CreateSwapchainSyncObjects()
1740 mRenderFinishedSemaphores.resize(mSwapChainImages.size());
1741 mImagesInFlight.resize(mSwapChainImages.size(), VK_NULL_HANDLE);
1743 VkSemaphoreCreateInfo semaphoreInfo{ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
1745 for (
size_t i = 0; i < mSwapChainImages.size(); i++)
1747 if (vkCreateSemaphore(mLogicalDevice, &semaphoreInfo,
nullptr, &mRenderFinishedSemaphores[i]) != VK_SUCCESS)
1749 LOG_ERROR(
"failed to create per-image renderFinished semaphores!");
1750 throw std::runtime_error(
"failed to create per-image renderFinished semaphores!");
1755VkShaderModule VulkanRenderer::CreateShaderModule(std::vector<char>& code)
1757 VkShaderModuleCreateInfo createInfo{};
1758 createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
1759 createInfo.codeSize = code.size();
1760 createInfo.pCode =
reinterpret_cast<const uint32_t*
>(code.data());
1762 VkShaderModule shaderModule;
1763 if (vkCreateShaderModule(mLogicalDevice, &createInfo,
nullptr, &shaderModule) != VK_SUCCESS)
1765 LOG_ERROR(
"failed to create shader module!");
1766 throw std::runtime_error(
"failed to create shader module!");
1769 return shaderModule;
1772void VulkanRenderer::CleanupSwapChain()
1775 for (
auto sem : mRenderFinishedSemaphores)
1777 vkDestroySemaphore(mLogicalDevice, sem,
nullptr);
1779 mRenderFinishedSemaphores.clear();
1780 mImagesInFlight.clear();
1782 vkDestroyImageView(mLogicalDevice, mColorImageView,
nullptr);
1783 vkDestroyImage(mLogicalDevice, mColorImage,
nullptr);
1784 vkFreeMemory(mLogicalDevice, mColorImageMemory,
nullptr);
1786 vkDestroyImageView(mLogicalDevice, mDepthImageView,
nullptr);
1787 vkDestroyImage(mLogicalDevice, mDepthImage,
nullptr);
1788 vkFreeMemory(mLogicalDevice, mDepthImageMemory,
nullptr);
1789 for (
auto framebuffer : mSwapChainFramebuffers)
1791 vkDestroyFramebuffer(mLogicalDevice, framebuffer,
nullptr);
1794 vkFreeCommandBuffers(mLogicalDevice, mCommandPool,
static_cast<uint32_t
>(mCommandBuffers.size()), mCommandBuffers.data());
1796 vkDestroyRenderPass(mLogicalDevice, mRenderPass,
nullptr);
1797 for (
auto imageView : mSwapChainImageViews)
1799 vkDestroyImageView(mLogicalDevice, imageView,
nullptr);
1801 vkDestroySwapchainKHR(mLogicalDevice, mSwapChain,
nullptr);
1804void VulkanRenderer::RecordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex)
1806 VkCommandBufferBeginInfo beginInfo{};
1807 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1811 if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS)
1813 LOG_ERROR(
"failed to begin recording command buffer!");
1814 throw std::runtime_error(
"failed to begin recording command buffer!");
1817 VkRenderPassBeginInfo renderPassInfo{};
1818 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
1819 renderPassInfo.renderPass = mRenderPass;
1820 renderPassInfo.framebuffer = mSwapChainFramebuffers[imageIndex];
1822 renderPassInfo.renderArea.offset = { 0, 0 };
1823 renderPassInfo.renderArea.extent = mSwapChainExtent;
1825 std::array<VkClearValue, 2> clearValues{};
1826 clearValues[0].color = { {mDefaultColor.r, mDefaultColor.g, mDefaultColor.b, 1.0f} };
1827 clearValues[1].depthStencil = { 1.0f, 0 };
1828 renderPassInfo.clearValueCount =
static_cast<uint32_t
>(clearValues.size());
1829 renderPassInfo.pClearValues = clearValues.data();
1831 vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
1833 vkCmdEndRenderPass(commandBuffer);
1835 if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS)
1837 LOG_ERROR(
"failed to record command buffer!");
1838 throw std::runtime_error(
"failed to record command buffer!");
1842VkCommandBuffer VulkanRenderer::BeginSingleTimeCommand()
1844 VkCommandBufferAllocateInfo allocInfo{};
1845 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1846 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1847 allocInfo.commandPool = mCommandPool;
1848 allocInfo.commandBufferCount = 1;
1850 VkCommandBuffer commandBuffer;
1851 vkAllocateCommandBuffers(mLogicalDevice, &allocInfo, &commandBuffer);
1853 VkCommandBufferBeginInfo beginInfo{};
1854 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1855 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
1857 vkBeginCommandBuffer(commandBuffer, &beginInfo);
1859 return commandBuffer;
1862bool VulkanRenderer::HasStencilComponent(VkFormat format)
1864 return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT;
1867void VulkanRenderer::EndSingleTimeCommand(VkCommandBuffer commandBuffer)
1869 vkEndCommandBuffer(commandBuffer);
1871 VkSubmitInfo submitInfo{};
1872 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1873 submitInfo.commandBufferCount = 1;
1874 submitInfo.pCommandBuffers = &commandBuffer;
1876 vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
1877 vkQueueWaitIdle(mGraphicsQueue);
1879 vkFreeCommandBuffers(mLogicalDevice, mCommandPool, 1, &commandBuffer);
1882void VulkanRenderer::ProcessDeferredDestruction()
1884 std::vector<DeferredItem> ripe;
1886 while (!mDeferredQueue.empty() &&
1887 mDeferredQueue.front().retireFrame <= mFrameSerial)
1889 ripe.push_back(mDeferredQueue.front());
1890 mDeferredQueue.pop_front();
1894 for (
auto& o : ripe)
1896 vkDestroyDescriptorPool(mLogicalDevice, o.descriptorPool,
nullptr);
1898 for (
auto& o : ripe)
1900 vkDestroyBuffer(mLogicalDevice, o.buffer,
nullptr);
1902 for (
auto& o : ripe)
1904 vkFreeMemory(mLogicalDevice, o.memory,
nullptr);
1910 VkVertexInputBindingDescription binding{};
1911 binding.binding = 0;
1912 binding.stride =
sizeof(T);
1913 binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
1918std::vector<VkVertexInputAttributeDescription> VulkanRenderer::GetAttributeDescriptions<Vertex2D>()
1920 std::vector<VkVertexInputAttributeDescription> attributeDescriptions{};
1921 attributeDescriptions.resize(3);
1923 attributeDescriptions[0].binding = 0;
1924 attributeDescriptions[0].location = 0;
1925 attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT;
1926 attributeDescriptions[0].offset = offsetof(Vertex2D, position);
1928 attributeDescriptions[1].binding = 0;
1929 attributeDescriptions[1].location = 1;
1930 attributeDescriptions[1].format = VK_FORMAT_R32G32B32A32_SFLOAT;
1931 attributeDescriptions[1].offset = offsetof(Vertex2D, color);
1933 attributeDescriptions[2].binding = 0;
1934 attributeDescriptions[2].location = 2;
1935 attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
1936 attributeDescriptions[2].offset = offsetof(Vertex2D, textureCoordinates);
1938 return attributeDescriptions;
1942std::vector<VkVertexInputAttributeDescription> VulkanRenderer::GetAttributeDescriptions<VertexPositionColorTexture>()
1944 std::vector<VkVertexInputAttributeDescription> attributeDescriptions{};
1945 attributeDescriptions.resize(3);
1947 attributeDescriptions[0].binding = 0;
1948 attributeDescriptions[0].location = 0;
1949 attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
1950 attributeDescriptions[0].offset = offsetof(VertexPositionColorTexture, position);
1952 attributeDescriptions[1].binding = 0;
1953 attributeDescriptions[1].location = 1;
1954 attributeDescriptions[1].format = VK_FORMAT_R32G32B32A32_SFLOAT;
1955 attributeDescriptions[1].offset = offsetof(VertexPositionColorTexture, color);
1957 attributeDescriptions[2].binding = 0;
1958 attributeDescriptions[2].location = 2;
1959 attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
1960 attributeDescriptions[2].offset = offsetof(VertexPositionColorTexture, textureCoordinates);
1962 return attributeDescriptions;
1966std::vector<VkVertexInputAttributeDescription> VulkanRenderer::GetAttributeDescriptions<VertexPositionColorTextureNormalTangent>()
1968 std::vector<VkVertexInputAttributeDescription> attributeDescriptions{};
1969 attributeDescriptions.resize(5);
1971 attributeDescriptions[0].binding = 0;
1972 attributeDescriptions[0].location = 0;
1973 attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
1974 attributeDescriptions[0].offset = offsetof(VertexPositionColorTextureNormalTangent, position);
1976 attributeDescriptions[1].binding = 0;
1977 attributeDescriptions[1].location = 1;
1978 attributeDescriptions[1].format = VK_FORMAT_R32G32B32A32_SFLOAT;
1979 attributeDescriptions[1].offset = offsetof(VertexPositionColorTextureNormalTangent, color);
1981 attributeDescriptions[2].binding = 0;
1982 attributeDescriptions[2].location = 2;
1983 attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
1984 attributeDescriptions[2].offset = offsetof(VertexPositionColorTextureNormalTangent, textureCoordinates);
1986 attributeDescriptions[3].binding = 0;
1987 attributeDescriptions[3].location = 3;
1988 attributeDescriptions[3].format = VK_FORMAT_R32G32B32_SFLOAT;
1989 attributeDescriptions[3].offset = offsetof(VertexPositionColorTextureNormalTangent, normal);
1991 attributeDescriptions[4].binding = 0;
1992 attributeDescriptions[4].location = 4;
1993 attributeDescriptions[4].format = VK_FORMAT_R32G32B32_SFLOAT;
1994 attributeDescriptions[4].offset = offsetof(VertexPositionColorTextureNormalTangent, tangent);
1996 return attributeDescriptions;
virtual ScreenSettings GetScreenSettings() const =0
Retrieves the current screen or window settings.
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.
virtual void ResetFramebufferResizedFlag()=0
Resets the framebuffer resized flag after handling a resize event.
virtual void * GetNativeHandle() const =0
Returns a pointer to the underlying native window handle.
virtual const IApplication & GetApplication()=0
Retrieves a reference to the owning application instance.
virtual bool IsFramebufferResized() const =0
Checks if the framebuffer has been resized since the last frame.
Represents a material instance with parameter values, texture bindings, and rendering configuration.
std::vector< std::string > GetTextures() const
Returns the list of texture names used by this material.
PackedMaterialData PackMaterialParameters()
Packs the current float/vector parameters into a binary buffer and layout metadata.
const MaterialSettings GetMaterialSettings() const
Returns the material's static settings (domain, blend mode, shading model, etc.).
Vulkan-specific implementation of material render resources.
Vulkan implementation of the mesh GPU resource interface.
Vulkan implementation of IRenderResources for the Rendering Engine.
Vulkan-based implementation of the IRenderer interface.
bool BeginFrame() override
Begins frame rendering operations.
VulkanRenderer(IWindowSystem &windowSystem)
Constructs a VulkanRenderer bound to a specific window system.
ITextureRenderResources * ProvideTextureRenderResources() const override
Provides access to texture-related GPU resources.
VkPhysicalDeviceFeatures & GetPhysDevSupportedFeatures()
Returns reference to the physical device�s supported features.
void GenerateMipmaps(VkImage image, VkFormat imageFormat, int32_t texWidth, int32_t texHeight, uint32_t mipLevels)
Generates mipmaps for a given image resource.
void CopyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height)
Copies buffer data into an image (used for uploading texture data).
VkDevice & GetLogicalDevice()
Returns reference to the logical Vulkan device.
VkDescriptorSetLayout CreateDescriptorSetLayout(Material *material)
Creates a descriptor set layout corresponding to a given material.
IRenderResources * ProvideRenderResources() const override
Provides access to the general rendering resource manager.
void InitializeRenderer() override
Initializes all rendering subsystems and GPU resources.
void BeginRenderPass() override
Begins the active render pass for the current frame.
IMeshRenderResources * ProvideMeshRenderResources() const override
Provides access to mesh-related GPU resources.
void CopyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size)
Copies data from one buffer to another using a temporary command buffer.
void EndRenderPass() override
Ends the active render pass.
void EndFrame() override
Completes the current frame rendering and presents the result.
VkPhysicalDevice & GetPhysicalDevice()
Returns reference to the physical device.
void CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer &buffer, VkDeviceMemory &bufferMemory)
Creates a new Vulkan buffer with the specified usage and memory properties.
void UnregisterObserver(IRendererObserver *notifier) override
Unregisters a previously registered observer.
void WaitIdle() override
Waits until the GPU has finished all pending rendering operations.
std::pair< VkPipelineLayout, VkPipeline > CreateGraphicsPipeline(Material *material, VkDescriptorSetLayout &descriptorSetLayout, std::vector< char > &spvVertShaderCode, std::vector< char > &spvFragShaderCode)
Creates a Vulkan graphics pipeline based on material and shader inputs.
void DrawFrame() override
Executes a full frame rendering cycle.
static VkVertexInputBindingDescription GetBindingDescription()
Returns a vertex input binding description for a specific vertex type.
void AddDeferredDestroy(DeferredItem deferredItem)
uint32_t FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties)
Finds a suitable memory type for a given allocation.
void SetDefaultColor(float r, float g, float b) override
Sets the renderer clear color.
void TransitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, std::uint32_t mipmapLevels)
Transitions the image layout for a given Vulkan image.
void RegisterObserver(IRendererObserver *notifier) override
Registers an observer for rendering events.
void ShutdownRenderer() override
Destroys and cleans up all rendering resources.
std::vector< VkCommandBuffer > GetComandBuffers()
Returns the collection of command buffers used for rendering.
IMaterialRenderResources * ProvideMaterialRenderResources() const override
Provides access to material-related GPU resources.
Vulkan-specific implementation of ITextureRenderResources.
Engine-wide logging system for runtime diagnostics and performance tracking.
const bool enableValidationLayers
const int MAX_FRAMES_IN_FLIGHT
Number of frames that can be processed simultaneously (double buffering).
VkDescriptorPool descriptorPool
std::uint64_t retireFrame
MaterialDomain materialDomain
Contains the raw buffer data and layout metadata of packed material parameters.
std::vector< uint8_t > buffer
std::string name
The window or application name.