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 static_cast<void>(messageType);
722 static_cast<void>(pUserData);
724 if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
726 LOG_ERROR(std::string(
"Vulkan validation: ") + pCallbackData->pMessage);
730 if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT)
732 LOG_WARNING(std::string(
"Vulkan validation: ") + pCallbackData->pMessage);
736 LOG_DEBUG(std::string(
"Vulkan validation: ") + pCallbackData->pMessage);
743void VulkanRenderer::SetupDebugMessenger()
750 VkDebugUtilsMessengerCreateInfoEXT createInfo{};
751 PopulateDebugMessengerCreateInfo(createInfo);
753 if (CreateDebugUtilsMessengerEXT(mInstance, &createInfo,
nullptr, &mDebugMessenger) != VK_SUCCESS)
755 LOG_ERROR(
"failed to set up debug messenger!");
756 throw std::runtime_error(
"failed to set up debug messenger!");
760VkResult VulkanRenderer::CreateDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerCreateInfoEXT
const* pCreateInfo, VkAllocationCallbacks
const* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger)
762 auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance,
"vkCreateDebugUtilsMessengerEXT");
765 return func(instance, pCreateInfo, pAllocator, pDebugMessenger);
769 return VK_ERROR_EXTENSION_NOT_PRESENT;
773void VulkanRenderer::DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger,
const VkAllocationCallbacks* pAllocator)
775 auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance,
"vkDestroyDebugUtilsMessengerEXT");
778 func(instance, debugMessenger, pAllocator);
782void VulkanRenderer::CreateSurface()
784 GLFWwindow* window =
static_cast<GLFWwindow*
>(mWindowSystem.
GetNativeHandle());
785 if (glfwCreateWindowSurface(mInstance, window,
nullptr, &mSurface) != VK_SUCCESS)
787 LOG_ERROR(
"failed to create window surface!");
788 throw std::runtime_error(
"failed to create window surface!");
792void VulkanRenderer::PickPhysicalDevice()
794 uint32_t deviceCount = 0;
795 vkEnumeratePhysicalDevices(mInstance, &deviceCount,
nullptr);
797 if (deviceCount == 0)
799 LOG_ERROR(
"failed to find GPUs with Vulkan support!");
800 throw std::runtime_error(
"failed to find GPUs with Vulkan support!");
803 std::vector<VkPhysicalDevice> devices(deviceCount);
804 vkEnumeratePhysicalDevices(mInstance, &deviceCount, devices.data());
806 for (
const auto& device : devices)
808 if (IsDeviceSuitable(device)) {
809 mPhysicalDevice = device;
810 vkGetPhysicalDeviceFeatures(mPhysicalDevice, &mPhysDevSupportedFeatures);
811 vkGetPhysicalDeviceProperties(mPhysicalDevice, &mPhysicalDeviceProperties);
812 mMSAASamples = CheckMaxUsableSampleCount();
817 if (mPhysicalDevice == VK_NULL_HANDLE)
819 LOG_ERROR(
"Failed to find a suitable GPU!");
820 throw std::runtime_error(
"failed to find a suitable GPU!");
822 LOG_INFO(std::string(
"Selected GPU: ") +
823 mPhysicalDeviceProperties.deviceName);
826bool VulkanRenderer::IsDeviceSuitable(VkPhysicalDevice device)
839 QueueFamilyIndices indices = FindQueueFamilies(device);
841 bool extensionsSupported = CheckDeviceExtensionSupport(device);
843 bool swapChainAdequate =
false;
844 if (extensionsSupported)
846 auto const swapChainSupport = QuerySwapChainSupport(device);
847 swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
849 return indices.IsComplete() && extensionsSupported && swapChainAdequate;
852QueueFamilyIndices VulkanRenderer::FindQueueFamilies(VkPhysicalDevice device)
854 QueueFamilyIndices indices;
856 uint32_t queueFamilyCount = 0;
857 vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount,
nullptr);
859 std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
860 vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
863 for (
const auto& queueFamily : queueFamilies)
865 if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT)
867 indices.graphicsFamily = i;
870 VkBool32 presentSupport =
false;
871 vkGetPhysicalDeviceSurfaceSupportKHR(device, i, mSurface, &presentSupport);
874 indices.presentFamily = i;
876 if (indices.IsComplete())
886bool VulkanRenderer::CheckDeviceExtensionSupport(VkPhysicalDevice physicalDevice)
888 uint32_t extensionCount;
889 vkEnumerateDeviceExtensionProperties(physicalDevice,
nullptr, &extensionCount,
nullptr);
891 std::vector<VkExtensionProperties> availableExtensions(extensionCount);
892 vkEnumerateDeviceExtensionProperties(physicalDevice,
nullptr, &extensionCount, availableExtensions.data());
894 std::set<std::string> requiredExtensions(mDeviceExtensions.begin(), mDeviceExtensions.end());
896 for (
const auto& extension : availableExtensions)
898 requiredExtensions.erase(extension.extensionName);
901 return requiredExtensions.empty();
904SwapChainSupportDetails VulkanRenderer::QuerySwapChainSupport(VkPhysicalDevice device)
906 SwapChainSupportDetails scsDetails;
908 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, mSurface, &scsDetails.capabilities);
910 uint32_t formatCount;
911 vkGetPhysicalDeviceSurfaceFormatsKHR(device, mSurface, &formatCount,
nullptr);
913 if (formatCount != 0)
915 scsDetails.formats.resize(formatCount);
916 vkGetPhysicalDeviceSurfaceFormatsKHR(device, mSurface, &formatCount, scsDetails.formats.data());
919 uint32_t presentModeCount;
920 vkGetPhysicalDeviceSurfacePresentModesKHR(device, mSurface, &presentModeCount,
nullptr);
922 if (presentModeCount != 0)
924 scsDetails.presentModes.resize(presentModeCount);
925 vkGetPhysicalDeviceSurfacePresentModesKHR(device, mSurface, &presentModeCount, scsDetails.presentModes.data());
931VkSampleCountFlagBits VulkanRenderer::CheckMaxUsableSampleCount()
933 VkPhysicalDeviceProperties physicalDeviceProperties;
934 vkGetPhysicalDeviceProperties(mPhysicalDevice, &physicalDeviceProperties);
936 VkSampleCountFlags counts = physicalDeviceProperties.limits.framebufferColorSampleCounts & physicalDeviceProperties.limits.framebufferDepthSampleCounts;
937 if (counts & VK_SAMPLE_COUNT_64_BIT) {
return VK_SAMPLE_COUNT_64_BIT; }
938 if (counts & VK_SAMPLE_COUNT_32_BIT) {
return VK_SAMPLE_COUNT_32_BIT; }
939 if (counts & VK_SAMPLE_COUNT_16_BIT) {
return VK_SAMPLE_COUNT_16_BIT; }
940 if (counts & VK_SAMPLE_COUNT_8_BIT) {
return VK_SAMPLE_COUNT_8_BIT; }
941 if (counts & VK_SAMPLE_COUNT_4_BIT) {
return VK_SAMPLE_COUNT_4_BIT; }
942 if (counts & VK_SAMPLE_COUNT_2_BIT) {
return VK_SAMPLE_COUNT_2_BIT; }
944 return VK_SAMPLE_COUNT_1_BIT;
947void VulkanRenderer::CreateLogicalDevice()
949 QueueFamilyIndices indices = FindQueueFamilies(mPhysicalDevice);
951 std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
952 std::set<uint32_t> uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() };
954 float queuePriority = 1.0f;
955 for (uint32_t queueFamily : uniqueQueueFamilies)
957 VkDeviceQueueCreateInfo queueCreateInfo{};
958 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
959 queueCreateInfo.queueFamilyIndex = queueFamily;
960 queueCreateInfo.queueCount = 1;
961 queueCreateInfo.pQueuePriorities = &queuePriority;
962 queueCreateInfos.push_back(queueCreateInfo);
965 VkPhysicalDeviceFeatures deviceFeatures{};
966 if (mPhysDevSupportedFeatures.samplerAnisotropy)
968 deviceFeatures.samplerAnisotropy = VK_TRUE;
972 deviceFeatures.samplerAnisotropy = VK_FALSE;
975 if (mPhysDevSupportedFeatures.sampleRateShading)
977 deviceFeatures.sampleRateShading = VK_TRUE;
981 deviceFeatures.sampleRateShading = VK_FALSE;
984 VkDeviceCreateInfo createInfo{};
985 createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
987 createInfo.pQueueCreateInfos = queueCreateInfos.data();
988 createInfo.queueCreateInfoCount =
static_cast<uint32_t
>(queueCreateInfos.size());
990 createInfo.pEnabledFeatures = &deviceFeatures;
992 createInfo.enabledExtensionCount =
static_cast<uint32_t
>(mDeviceExtensions.size());
993 createInfo.ppEnabledExtensionNames = mDeviceExtensions.data();
997 createInfo.enabledLayerCount =
static_cast<uint32_t
>(mValidationLayers.size());
998 createInfo.ppEnabledLayerNames = mValidationLayers.data();
1002 createInfo.enabledLayerCount = 0;
1005 if (vkCreateDevice(mPhysicalDevice, &createInfo,
nullptr, &mLogicalDevice) != VK_SUCCESS)
1007 LOG_ERROR(
"failed to create logical device!");
1008 throw std::runtime_error(
"failed to create logical device!");
1011 vkGetDeviceQueue(mLogicalDevice, indices.graphicsFamily.value(), 0, &mGraphicsQueue);
1012 vkGetDeviceQueue(mLogicalDevice, indices.presentFamily.value(), 0, &mPresentQueue);
1013 LOG_INFO(
"Logical device created.");
1016void VulkanRenderer::CreateSwapChain()
1018 SwapChainSupportDetails swapChainSupport = QuerySwapChainSupport(mPhysicalDevice);
1020 VkSurfaceFormatKHR surfaceFormat = ChooseSwapSurfaceFormat(swapChainSupport.formats);
1022 VkPresentModeKHR presentMode = ChooseSwapPresentMode(swapChainSupport.presentModes);
1023 VkExtent2D extent = ChooseSwapExtent(swapChainSupport.capabilities);
1025 uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
1026 if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount)
1028 imageCount = swapChainSupport.capabilities.maxImageCount;
1031 VkSwapchainCreateInfoKHR createInfo{};
1032 createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
1033 createInfo.surface = mSurface;
1035 createInfo.minImageCount = imageCount;
1036 createInfo.imageFormat = surfaceFormat.format;
1037 createInfo.imageColorSpace = surfaceFormat.colorSpace;
1038 createInfo.imageExtent = extent;
1039 createInfo.imageArrayLayers = 1;
1040 createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
1042 QueueFamilyIndices indices = FindQueueFamilies(mPhysicalDevice);
1043 uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() };
1045 if (indices.graphicsFamily != indices.presentFamily) {
1046 createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
1047 createInfo.queueFamilyIndexCount = 2;
1048 createInfo.pQueueFamilyIndices = queueFamilyIndices;
1052 createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
1053 createInfo.queueFamilyIndexCount = 0;
1054 createInfo.pQueueFamilyIndices =
nullptr;
1057 createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
1058 createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
1059 createInfo.presentMode = presentMode;
1060 createInfo.clipped = VK_TRUE;
1061 createInfo.oldSwapchain = VK_NULL_HANDLE;
1063 if (vkCreateSwapchainKHR(mLogicalDevice, &createInfo,
nullptr, &mSwapChain) != VK_SUCCESS)
1065 LOG_ERROR(
"failed to create swap chain!");
1066 throw std::runtime_error(
"failed to create swap chain!");
1069 vkGetSwapchainImagesKHR(mLogicalDevice, mSwapChain, &imageCount,
nullptr);
1070 mSwapChainImages.resize(imageCount);
1071 vkGetSwapchainImagesKHR(mLogicalDevice, mSwapChain, &imageCount, mSwapChainImages.data());
1073 mSwapChainImageFormat = surfaceFormat.format;
1074 mSwapChainExtent = extent;
1076 LOG_INFO(
"Swapchain created. Images: " +
1077 std::to_string(mSwapChainImages.size()) +
1079 std::to_string(mSwapChainExtent.width) +
"x" +
1080 std::to_string(mSwapChainExtent.height));
1083void VulkanRenderer::RecreateSwapChain()
1089 while (width == 0 || height == 0)
1091 GLFWwindow* window =
static_cast<GLFWwindow*
>(mWindowSystem.
GetNativeHandle());
1092 glfwGetFramebufferSize(window, &width, &height);
1095 vkDeviceWaitIdle(mLogicalDevice);
1099 for (
auto& observer : mObservers)
1101 observer->OnRenderResourcesRelease();
1108 CreateColorResources();
1109 CreateDepthResources();
1110 CreateFramebuffers();
1111 CreateCommandBuffers();
1113 CreateSwapchainSyncObjects();
1115 for (
auto& observer : mObservers)
1117 observer->OnRenderResourcesRebuild();
1119 LOG_INFO(
"Swapchain recreated successfully.");
1122VkSurfaceFormatKHR VulkanRenderer::ChooseSwapSurfaceFormat(std::vector<VkSurfaceFormatKHR>
const& availableFormats)
1124 for (
const auto& availableFormat : availableFormats)
1126 if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
1128 return availableFormat;
1132 return availableFormats[0];
1135VkPresentModeKHR VulkanRenderer::ChooseSwapPresentMode(std::vector<VkPresentModeKHR>
const& availablePresentModes)
1137 for (
const auto& availablePresentMode : availablePresentModes)
1139 if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR)
1141 return availablePresentMode;
1144 return VK_PRESENT_MODE_FIFO_KHR;
1147VkExtent2D VulkanRenderer::ChooseSwapExtent(VkSurfaceCapabilitiesKHR
const& capabilities)
1149 if (capabilities.currentExtent.width != UINT32_MAX)
1151 return capabilities.currentExtent;
1156 GLFWwindow* window =
static_cast<GLFWwindow*
>(mWindowSystem.
GetNativeHandle());
1157 glfwGetFramebufferSize(window, &width, &height);
1159 VkExtent2D actualExtent = {
1160 static_cast<uint32_t
>(width),
1161 static_cast<uint32_t
>(height)
1164 actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
1165 actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
1167 return actualExtent;
1171void VulkanRenderer::CreateImageViews()
1173 mSwapChainImageViews.resize(mSwapChainImages.size());
1175 for (uint32_t i = 0; i < mSwapChainImages.size(); i++)
1177 mSwapChainImageViews[i] = CreateImageView(mSwapChainImages[i], mSwapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT, 1);
1181VkImageView VulkanRenderer::CreateImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags, std::uint32_t mipmapLevels)
1183 VkImageViewCreateInfo viewInfo{};
1184 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1185 viewInfo.image = image;
1186 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
1187 viewInfo.format = format;
1188 viewInfo.subresourceRange.aspectMask = aspectFlags;
1189 viewInfo.subresourceRange.baseMipLevel = 0;
1190 viewInfo.subresourceRange.levelCount = mipmapLevels;
1191 viewInfo.subresourceRange.baseArrayLayer = 0;
1192 viewInfo.subresourceRange.layerCount = 1;
1194 VkImageView imageView;
1195 if (vkCreateImageView(mLogicalDevice, &viewInfo,
nullptr, &imageView) != VK_SUCCESS)
1197 LOG_ERROR(
"failed to create texture image view!");
1198 throw std::runtime_error(
"failed to create texture image view!");
1204void VulkanRenderer::CreateRenderPass()
1206 VkAttachmentDescription colorAttachment{};
1207 colorAttachment.format = mSwapChainImageFormat;
1208 colorAttachment.samples = mMSAASamples;
1209 colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1210 colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1211 colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1212 colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1213 colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1214 colorAttachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1216 VkAttachmentReference colorAttachmentRef{};
1217 colorAttachmentRef.attachment = 0;
1218 colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1220 VkAttachmentDescription depthAttachment{};
1221 depthAttachment.format = FindDepthFormat();
1222 depthAttachment.samples = mMSAASamples;
1223 depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1224 depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1225 depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1226 depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1227 depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1228 depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1230 VkAttachmentReference depthAttachmentRef{};
1231 depthAttachmentRef.attachment = 1;
1232 depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1234 VkAttachmentDescription colorAttachmentResolve{};
1235 colorAttachmentResolve.format = mSwapChainImageFormat;
1236 colorAttachmentResolve.samples = VK_SAMPLE_COUNT_1_BIT;
1237 colorAttachmentResolve.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1238 colorAttachmentResolve.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1239 colorAttachmentResolve.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1240 colorAttachmentResolve.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1241 colorAttachmentResolve.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1242 colorAttachmentResolve.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
1244 VkAttachmentReference colorAttachmentResolveRef{};
1245 colorAttachmentResolveRef.attachment = 2;
1246 colorAttachmentResolveRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1248 VkSubpassDescription subpass{};
1249 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
1250 subpass.colorAttachmentCount = 1;
1251 subpass.pColorAttachments = &colorAttachmentRef;
1252 subpass.pDepthStencilAttachment = &depthAttachmentRef;
1253 subpass.pResolveAttachments = &colorAttachmentResolveRef;
1255 VkSubpassDependency dependency{};
1256 dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
1257 dependency.dstSubpass = 0;
1258 dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
1259 dependency.srcAccessMask = 0;
1260 dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
1261 dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
1263 std::array<VkAttachmentDescription, 3> attachments = { colorAttachment, depthAttachment, colorAttachmentResolve };
1265 VkRenderPassCreateInfo renderPassInfo{};
1266 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
1267 renderPassInfo.attachmentCount =
static_cast<uint32_t
>(attachments.size());
1268 renderPassInfo.pAttachments = attachments.data();
1269 renderPassInfo.subpassCount = 1;
1270 renderPassInfo.pSubpasses = &subpass;
1271 renderPassInfo.dependencyCount = 1;
1272 renderPassInfo.pDependencies = &dependency;
1274 if (vkCreateRenderPass(mLogicalDevice, &renderPassInfo,
nullptr, &mRenderPass) != VK_SUCCESS)
1276 LOG_ERROR(
"failed to create render pass!");
1277 throw std::runtime_error(
"failed to create render pass!");
1281VkFormat VulkanRenderer::FindDepthFormat()
1283 return FindSupportedFormat(
1284 { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT },
1285 VK_IMAGE_TILING_OPTIMAL,
1286 VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
1290VkFormat VulkanRenderer::FindSupportedFormat(
const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features)
1292 for (VkFormat format : candidates)
1294 VkFormatProperties props;
1295 vkGetPhysicalDeviceFormatProperties(mPhysicalDevice, format, &props);
1297 if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features)
1301 else if (tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features) == features)
1306 LOG_ERROR(
"failed to find supported format!");
1307 throw std::runtime_error(
"failed to find supported format!");
1310void VulkanRenderer::CreateCommandPool()
1312 QueueFamilyIndices queueFamilyIndices = FindQueueFamilies(mPhysicalDevice);
1314 VkCommandPoolCreateInfo poolInfo{};
1315 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
1316 poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
1317 poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
1319 if (vkCreateCommandPool(mLogicalDevice, &poolInfo,
nullptr, &mCommandPool) != VK_SUCCESS)
1321 LOG_ERROR(
"failed to create command pool!");
1322 throw std::runtime_error(
"failed to create command pool!");
1326void VulkanRenderer::CreateColorResources()
1328 VkFormat colorFormat = mSwapChainImageFormat;
1330 CreateVulkanImage(mSwapChainExtent.width, mSwapChainExtent.height, 1, mMSAASamples, colorFormat, VK_IMAGE_TILING_OPTIMAL,
1331 VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
1332 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mColorImage, mColorImageMemory);
1333 mColorImageView = CreateImageView(mColorImage, colorFormat, VK_IMAGE_ASPECT_COLOR_BIT, 1);
1336void 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)
1338 VkImageCreateInfo imageInfo{};
1339 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
1340 imageInfo.imageType = VK_IMAGE_TYPE_2D;
1341 imageInfo.extent.width =
static_cast<uint32_t
>(width);
1342 imageInfo.extent.height =
static_cast<uint32_t
>(height);
1343 imageInfo.extent.depth = 1;
1344 imageInfo.mipLevels = mipmapLevels;
1345 imageInfo.arrayLayers = 1;
1347 imageInfo.format = format;
1348 imageInfo.tiling = tiling;
1350 imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1352 imageInfo.usage = usage;
1354 imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
1356 imageInfo.samples = numSamples;
1357 imageInfo.flags = 0;
1359 if (vkCreateImage(mLogicalDevice, &imageInfo,
nullptr, &image) != VK_SUCCESS)
1362 throw std::runtime_error(
"failed to create image!");
1365 VkMemoryRequirements memRequirements;
1366 vkGetImageMemoryRequirements(mLogicalDevice, image, &memRequirements);
1368 VkMemoryAllocateInfo allocInfo{};
1369 allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1370 allocInfo.allocationSize = memRequirements.size;
1371 allocInfo.memoryTypeIndex =
FindMemoryType(memRequirements.memoryTypeBits, properties);
1373 if (vkAllocateMemory(mLogicalDevice, &allocInfo,
nullptr, &imageMemory) != VK_SUCCESS) {
1374 LOG_ERROR(
"failed to allocate image memory!");
1375 throw std::runtime_error(
"failed to allocate image memory!");
1378 vkBindImageMemory(mLogicalDevice, image, imageMemory, 0);
1383 VkPhysicalDeviceMemoryProperties memProperties;
1384 vkGetPhysicalDeviceMemoryProperties(mPhysicalDevice, &memProperties);
1386 for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++)
1388 if (typeFilter & (1 << i) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties)
1393 LOG_ERROR(
"failed to find suitable memory type!");
1394 throw std::runtime_error(
"failed to find suitable memory type!");
1399 return mPhysicalDevice;
1404 return mPhysDevSupportedFeatures;
1409 std::uint32_t bindingNumber = 0;
1410 std::vector<VkDescriptorSetLayoutBinding> bindings;
1413 VkDescriptorSetLayoutBinding uboLayoutBinding{};
1414 uboLayoutBinding.binding = bindingNumber;
1415 uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
1416 uboLayoutBinding.descriptorCount = 1;
1417 uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
1418 uboLayoutBinding.pImmutableSamplers =
nullptr;
1420 bindings.push_back(uboLayoutBinding);
1423 if (!packedMaterialData.
buffer.empty())
1427 VkDescriptorSetLayoutBinding customMaterialVariables;
1428 customMaterialVariables.binding = bindingNumber;
1429 customMaterialVariables.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
1430 customMaterialVariables.descriptorCount = 1;
1431 customMaterialVariables.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
1432 customMaterialVariables.pImmutableSamplers =
nullptr;
1434 bindings.push_back(customMaterialVariables);
1437 for (
const auto& textureName : material->
GetTextures())
1441 VkDescriptorSetLayoutBinding samplerLayoutBinding{};
1442 samplerLayoutBinding.binding = bindingNumber;
1443 samplerLayoutBinding.descriptorCount = 1;
1444 samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1445 samplerLayoutBinding.pImmutableSamplers =
nullptr;
1446 samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
1448 bindings.push_back(samplerLayoutBinding);
1451 VkDescriptorSetLayoutCreateInfo layoutInfo{};
1452 layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
1453 layoutInfo.bindingCount =
static_cast<uint32_t
>(bindings.size());
1454 layoutInfo.pBindings = bindings.data();
1456 VkDescriptorSetLayout result;
1457 if (vkCreateDescriptorSetLayout(mLogicalDevice, &layoutInfo,
nullptr, &result) != VK_SUCCESS)
1459 LOG_ERROR(
"failed to create descriptor set layout!");
1460 throw std::runtime_error(
"failed to create descriptor set layout!");
1468 return mCommandBuffers;
1473 VkPipelineLayout pipelineLayout;
1474 VkPipeline pipeline;
1476 VkShaderModule vertShaderModule = CreateShaderModule(spvVertShaderCode);
1477 VkShaderModule fragShaderModule = CreateShaderModule(spvFragShaderCode);
1479 VkPipelineShaderStageCreateInfo vertShaderStageInfo{};
1480 vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
1481 vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
1483 vertShaderStageInfo.module = vertShaderModule;
1484 vertShaderStageInfo.pName =
"main";
1486 VkPipelineShaderStageCreateInfo fragShaderStageInfo{};
1487 fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
1488 fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
1489 fragShaderStageInfo.module = fragShaderModule;
1490 fragShaderStageInfo.pName =
"main";
1492 VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo };
1494 VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
1495 vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
1497 VkVertexInputBindingDescription bindingDescription;
1498 std::vector<VkVertexInputAttributeDescription> attributeDescriptions{};
1502 bindingDescription = GetBindingDescription<Vertex2D>();
1503 attributeDescriptions = GetAttributeDescriptions<Vertex2D>();
1507 bindingDescription = GetBindingDescription<VertexPositionColorTextureNormalTangent>();
1508 attributeDescriptions = GetAttributeDescriptions<VertexPositionColorTextureNormalTangent>();
1511 vertexInputInfo.vertexBindingDescriptionCount = 1;
1512 vertexInputInfo.vertexAttributeDescriptionCount =
static_cast<uint32_t
>(attributeDescriptions.size());
1514 vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
1515 vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();
1517 VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
1518 inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
1519 inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
1520 inputAssembly.primitiveRestartEnable = VK_FALSE;
1522 VkViewport viewport{};
1525 viewport.width = (float)mSwapChainExtent.width;
1526 viewport.height = (float)mSwapChainExtent.height;
1527 viewport.minDepth = 0.0f;
1528 viewport.maxDepth = 1.0f;
1531 scissor.offset = { 0, 0 };
1532 scissor.extent = mSwapChainExtent;
1534 VkPipelineViewportStateCreateInfo viewportState{};
1535 viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
1536 viewportState.viewportCount = 1;
1537 viewportState.pViewports = &viewport;
1538 viewportState.scissorCount = 1;
1539 viewportState.pScissors = &scissor;
1541 VkPipelineRasterizationStateCreateInfo rasterizer{};
1542 rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
1543 rasterizer.depthClampEnable = VK_FALSE;
1544 rasterizer.rasterizerDiscardEnable = VK_FALSE;
1545 rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
1546 rasterizer.lineWidth = 1.0f;
1549 rasterizer.cullMode = VK_CULL_MODE_NONE;
1553 rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
1555 rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
1556 rasterizer.depthBiasEnable = VK_FALSE;
1557 rasterizer.depthBiasConstantFactor = 0.0f;
1558 rasterizer.depthBiasClamp = 0.0f;
1559 rasterizer.depthBiasSlopeFactor = 0.0f;
1561 VkPipelineMultisampleStateCreateInfo multisampling{};
1562 multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
1563 multisampling.sampleShadingEnable = VK_FALSE;
1564 multisampling.rasterizationSamples = mMSAASamples;
1565 multisampling.minSampleShading = 1.0f;
1566 multisampling.pSampleMask =
nullptr;
1567 multisampling.alphaToCoverageEnable = VK_FALSE;
1568 multisampling.alphaToOneEnable = VK_FALSE;
1570 VkPipelineColorBlendAttachmentState colorBlendAttachment{};
1571 colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
1573 colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
1574 colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
1575 colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
1576 colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
1577 colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
1578 colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
1580 VkPipelineColorBlendStateCreateInfo colorBlending{};
1581 colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
1582 colorBlending.logicOpEnable = VK_FALSE;
1583 colorBlending.logicOp = VK_LOGIC_OP_COPY;
1584 colorBlending.attachmentCount = 1;
1585 colorBlending.pAttachments = &colorBlendAttachment;
1586 colorBlending.blendConstants[0] = 0.0f;
1587 colorBlending.blendConstants[1] = 0.0f;
1588 colorBlending.blendConstants[2] = 0.0f;
1589 colorBlending.blendConstants[3] = 0.0f;
1591 VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
1592 pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
1593 pipelineLayoutInfo.setLayoutCount = 1;
1594 pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout;
1596 if (vkCreatePipelineLayout(mLogicalDevice, &pipelineLayoutInfo,
nullptr, &pipelineLayout) != VK_SUCCESS)
1598 LOG_ERROR(
"failed to create pipeline layout!");
1599 throw std::runtime_error(
"failed to create pipeline layout!");
1602 VkPipelineDepthStencilStateCreateInfo depthStencil{};
1603 depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
1606 depthStencil.depthTestEnable = VK_FALSE;
1607 depthStencil.depthWriteEnable = VK_FALSE;
1611 depthStencil.depthTestEnable = VK_TRUE;
1614 depthStencil.depthCompareOp = VK_COMPARE_OP_LESS;
1615 depthStencil.depthBoundsTestEnable = VK_FALSE;
1618 depthStencil.stencilTestEnable = VK_FALSE;
1620 depthStencil.back = {};
1622 VkGraphicsPipelineCreateInfo pipelineInfo{};
1623 pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
1624 pipelineInfo.stageCount = 2;
1625 pipelineInfo.pStages = shaderStages;
1626 pipelineInfo.pVertexInputState = &vertexInputInfo;
1627 pipelineInfo.pInputAssemblyState = &inputAssembly;
1628 pipelineInfo.pViewportState = &viewportState;
1629 pipelineInfo.pRasterizationState = &rasterizer;
1630 pipelineInfo.pMultisampleState = &multisampling;
1631 pipelineInfo.pDepthStencilState = &depthStencil;
1632 pipelineInfo.pColorBlendState = &colorBlending;
1644 pipelineInfo.pDynamicState =
nullptr;
1646 pipelineInfo.layout = pipelineLayout;
1647 pipelineInfo.renderPass = mRenderPass;
1648 pipelineInfo.subpass = 0;
1649 pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
1650 pipelineInfo.basePipelineIndex = -1;
1653 if (vkCreateGraphicsPipelines(mLogicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo,
nullptr, &pipeline) != VK_SUCCESS)
1655 LOG_ERROR(
"failed to create graphics pipeline!");
1656 throw std::runtime_error(
"failed to create graphics pipeline!");
1658 vkDestroyShaderModule(mLogicalDevice, fragShaderModule,
nullptr);
1659 vkDestroyShaderModule(mLogicalDevice, vertShaderModule,
nullptr);
1661 return std::pair<VkPipelineLayout, VkPipeline>(pipelineLayout, pipeline);
1664void VulkanRenderer::CreateDepthResources()
1666 VkFormat depthFormat = FindDepthFormat();
1668 CreateVulkanImage(mSwapChainExtent.width, mSwapChainExtent.height, 1, mMSAASamples, depthFormat,
1669 VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
1670 mDepthImage, mDepthImageMemory);
1671 mDepthImageView = CreateImageView(mDepthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT, 1);
1674void VulkanRenderer::CreateFramebuffers()
1676 mSwapChainFramebuffers.resize(mSwapChainImageViews.size());
1678 for (
size_t i = 0; i < mSwapChainImageViews.size(); i++)
1680 std::array<VkImageView, 3> attachments = { mColorImageView, mDepthImageView, mSwapChainImageViews[i] };
1682 VkFramebufferCreateInfo framebufferInfo{};
1683 framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
1684 framebufferInfo.renderPass = mRenderPass;
1685 framebufferInfo.attachmentCount =
static_cast<uint32_t
>(attachments.size());
1686 framebufferInfo.pAttachments = attachments.data();
1687 framebufferInfo.width = mSwapChainExtent.width;
1688 framebufferInfo.height = mSwapChainExtent.height;
1689 framebufferInfo.layers = 1;
1691 if (vkCreateFramebuffer(mLogicalDevice, &framebufferInfo,
nullptr, &mSwapChainFramebuffers[i]) != VK_SUCCESS)
1693 LOG_ERROR(
"failed to create framebuffer!");
1694 throw std::runtime_error(
"failed to create framebuffer!");
1699void VulkanRenderer::CreateCommandBuffers()
1703 VkCommandBufferAllocateInfo allocInfo{};
1704 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1705 allocInfo.commandPool = mCommandPool;
1706 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1707 allocInfo.commandBufferCount = (uint32_t)mCommandBuffers.size();
1709 if (vkAllocateCommandBuffers(mLogicalDevice, &allocInfo, mCommandBuffers.data()) != VK_SUCCESS)
1711 LOG_ERROR(
"failed to allocate command buffers!");
1712 throw std::runtime_error(
"failed to allocate command buffers!");
1716void VulkanRenderer::CreateFrameSyncObjects()
1721 VkSemaphoreCreateInfo semaphoreInfo{};
1722 semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
1723 VkFenceCreateInfo fenceInfo{};
1724 fenceInfo.sType = 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{};
1744 semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
1746 for (
size_t i = 0; i < mSwapChainImages.size(); i++)
1748 if (vkCreateSemaphore(mLogicalDevice, &semaphoreInfo,
nullptr, &mRenderFinishedSemaphores[i]) != VK_SUCCESS)
1750 LOG_ERROR(
"failed to create per-image renderFinished semaphores!");
1751 throw std::runtime_error(
"failed to create per-image renderFinished semaphores!");
1756VkShaderModule VulkanRenderer::CreateShaderModule(
const std::vector<char>& code)
1758 if (code.empty() || code.size() %
sizeof(std::uint32_t) != 0U)
1760 throw std::runtime_error(
"Invalid SPIR-V shader binary size.");
1763 std::vector<std::uint32_t> alignedCode(code.size() /
sizeof(std::uint32_t));
1765 std::memcpy(alignedCode.data(), code.data(), code.size());
1767 VkShaderModuleCreateInfo createInfo{};
1768 createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
1769 createInfo.codeSize = code.size();
1770 createInfo.pCode = alignedCode.data();
1772 VkShaderModule shaderModule;
1773 if (vkCreateShaderModule(
1777 &shaderModule) != VK_SUCCESS)
1779 LOG_ERROR(
"failed to create shader module!");
1780 throw std::runtime_error(
"failed to create shader module!");
1783 return shaderModule;
1786void VulkanRenderer::CleanupSwapChain()
1789 for (
auto sem : mRenderFinishedSemaphores)
1791 vkDestroySemaphore(mLogicalDevice, sem,
nullptr);
1793 mRenderFinishedSemaphores.clear();
1794 mImagesInFlight.clear();
1796 vkDestroyImageView(mLogicalDevice, mColorImageView,
nullptr);
1797 vkDestroyImage(mLogicalDevice, mColorImage,
nullptr);
1798 vkFreeMemory(mLogicalDevice, mColorImageMemory,
nullptr);
1800 vkDestroyImageView(mLogicalDevice, mDepthImageView,
nullptr);
1801 vkDestroyImage(mLogicalDevice, mDepthImage,
nullptr);
1802 vkFreeMemory(mLogicalDevice, mDepthImageMemory,
nullptr);
1803 for (
auto framebuffer : mSwapChainFramebuffers)
1805 vkDestroyFramebuffer(mLogicalDevice, framebuffer,
nullptr);
1808 vkFreeCommandBuffers(mLogicalDevice, mCommandPool,
static_cast<uint32_t
>(mCommandBuffers.size()), mCommandBuffers.data());
1810 vkDestroyRenderPass(mLogicalDevice, mRenderPass,
nullptr);
1811 for (
auto imageView : mSwapChainImageViews)
1813 vkDestroyImageView(mLogicalDevice, imageView,
nullptr);
1815 vkDestroySwapchainKHR(mLogicalDevice, mSwapChain,
nullptr);
1818void VulkanRenderer::RecordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex)
1820 VkCommandBufferBeginInfo beginInfo{};
1821 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1825 if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS)
1827 LOG_ERROR(
"failed to begin recording command buffer!");
1828 throw std::runtime_error(
"failed to begin recording command buffer!");
1831 VkRenderPassBeginInfo renderPassInfo{};
1832 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
1833 renderPassInfo.renderPass = mRenderPass;
1834 renderPassInfo.framebuffer = mSwapChainFramebuffers[imageIndex];
1836 renderPassInfo.renderArea.offset = { 0, 0 };
1837 renderPassInfo.renderArea.extent = mSwapChainExtent;
1839 std::array<VkClearValue, 2> clearValues{};
1840 clearValues[0].color = { {mDefaultColor.r, mDefaultColor.g, mDefaultColor.b, 1.0f} };
1841 clearValues[1].depthStencil = { 1.0f, 0 };
1842 renderPassInfo.clearValueCount =
static_cast<uint32_t
>(clearValues.size());
1843 renderPassInfo.pClearValues = clearValues.data();
1845 vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
1847 vkCmdEndRenderPass(commandBuffer);
1849 if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS)
1851 LOG_ERROR(
"failed to record command buffer!");
1852 throw std::runtime_error(
"failed to record command buffer!");
1856VkCommandBuffer VulkanRenderer::BeginSingleTimeCommand()
1858 VkCommandBufferAllocateInfo allocInfo{};
1859 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1860 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1861 allocInfo.commandPool = mCommandPool;
1862 allocInfo.commandBufferCount = 1;
1864 VkCommandBuffer commandBuffer;
1865 vkAllocateCommandBuffers(mLogicalDevice, &allocInfo, &commandBuffer);
1867 VkCommandBufferBeginInfo beginInfo{};
1868 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1869 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
1871 vkBeginCommandBuffer(commandBuffer, &beginInfo);
1873 return commandBuffer;
1876bool VulkanRenderer::HasStencilComponent(VkFormat format)
1878 return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT;
1881void VulkanRenderer::EndSingleTimeCommand(VkCommandBuffer commandBuffer)
1883 vkEndCommandBuffer(commandBuffer);
1885 VkSubmitInfo submitInfo{};
1886 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1887 submitInfo.commandBufferCount = 1;
1888 submitInfo.pCommandBuffers = &commandBuffer;
1890 vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
1891 vkQueueWaitIdle(mGraphicsQueue);
1893 vkFreeCommandBuffers(mLogicalDevice, mCommandPool, 1, &commandBuffer);
1896void VulkanRenderer::ProcessDeferredDestruction()
1898 std::vector<DeferredItem> ripe;
1900 while (!mDeferredQueue.empty() &&
1901 mDeferredQueue.front().retireFrame <= mFrameSerial)
1903 ripe.push_back(mDeferredQueue.front());
1904 mDeferredQueue.pop_front();
1908 for (
auto& o : ripe)
1910 vkDestroyDescriptorPool(mLogicalDevice, o.descriptorPool, nullptr);
1912 for (
auto& o : ripe)
1914 vkDestroyBuffer(mLogicalDevice, o.buffer, nullptr);
1916 for (
auto& o : ripe)
1918 vkFreeMemory(mLogicalDevice, o.memory, nullptr);
1924 VkVertexInputBindingDescription binding{};
1925 binding.binding = 0;
1926 binding.stride =
sizeof(T);
1927 binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
1932std::vector<VkVertexInputAttributeDescription> VulkanRenderer::GetAttributeDescriptions<Vertex2D>()
1934 std::vector<VkVertexInputAttributeDescription> attributeDescriptions{};
1935 attributeDescriptions.resize(3);
1937 attributeDescriptions[0].binding = 0;
1938 attributeDescriptions[0].location = 0;
1939 attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT;
1940 attributeDescriptions[0].offset = offsetof(Vertex2D, position);
1942 attributeDescriptions[1].binding = 0;
1943 attributeDescriptions[1].location = 1;
1944 attributeDescriptions[1].format = VK_FORMAT_R32G32B32A32_SFLOAT;
1945 attributeDescriptions[1].offset = offsetof(Vertex2D, color);
1947 attributeDescriptions[2].binding = 0;
1948 attributeDescriptions[2].location = 2;
1949 attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
1950 attributeDescriptions[2].offset = offsetof(Vertex2D, textureCoordinates);
1952 return attributeDescriptions;
1956std::vector<VkVertexInputAttributeDescription> VulkanRenderer::GetAttributeDescriptions<VertexPositionColorTexture>()
1958 std::vector<VkVertexInputAttributeDescription> attributeDescriptions{};
1959 attributeDescriptions.resize(3);
1961 attributeDescriptions[0].binding = 0;
1962 attributeDescriptions[0].location = 0;
1963 attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
1964 attributeDescriptions[0].offset = offsetof(VertexPositionColorTexture, position);
1966 attributeDescriptions[1].binding = 0;
1967 attributeDescriptions[1].location = 1;
1968 attributeDescriptions[1].format = VK_FORMAT_R32G32B32A32_SFLOAT;
1969 attributeDescriptions[1].offset = offsetof(VertexPositionColorTexture, color);
1971 attributeDescriptions[2].binding = 0;
1972 attributeDescriptions[2].location = 2;
1973 attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
1974 attributeDescriptions[2].offset = offsetof(VertexPositionColorTexture, textureCoordinates);
1976 return attributeDescriptions;
1980std::vector<VkVertexInputAttributeDescription> VulkanRenderer::GetAttributeDescriptions<VertexPositionColorTextureNormalTangent>()
1982 std::vector<VkVertexInputAttributeDescription> attributeDescriptions{};
1983 attributeDescriptions.resize(5);
1985 attributeDescriptions[0].binding = 0;
1986 attributeDescriptions[0].location = 0;
1987 attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
1988 attributeDescriptions[0].offset = offsetof(VertexPositionColorTextureNormalTangent, position);
1990 attributeDescriptions[1].binding = 0;
1991 attributeDescriptions[1].location = 1;
1992 attributeDescriptions[1].format = VK_FORMAT_R32G32B32A32_SFLOAT;
1993 attributeDescriptions[1].offset = offsetof(VertexPositionColorTextureNormalTangent, color);
1995 attributeDescriptions[2].binding = 0;
1996 attributeDescriptions[2].location = 2;
1997 attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
1998 attributeDescriptions[2].offset = offsetof(VertexPositionColorTextureNormalTangent, textureCoordinates);
2000 attributeDescriptions[3].binding = 0;
2001 attributeDescriptions[3].location = 3;
2002 attributeDescriptions[3].format = VK_FORMAT_R32G32B32_SFLOAT;
2003 attributeDescriptions[3].offset = offsetof(VertexPositionColorTextureNormalTangent, normal);
2005 attributeDescriptions[4].binding = 0;
2006 attributeDescriptions[4].location = 4;
2007 attributeDescriptions[4].format = VK_FORMAT_R32G32B32_SFLOAT;
2008 attributeDescriptions[4].offset = offsetof(VertexPositionColorTextureNormalTangent, tangent);
2010 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.