#include "vulkanFoundation.h" void VulkanBackend::VulkanFoundation::initVulkan(PlumageRender::Setter setter,Camera camera,PlumageRender::renderMain mainRender,UI* plumageGUI) { // 创建instance createInstance(setter); // 设置校验层消息回调 setupDebugMessager(setter); // 选择主机显卡 pickPhysicalDevice(setter); if (!setter.settings.headless) { // 实时显示结果的glfw surface createSurface(setter); } // 完成逻辑设备创建 createLogicalDevice(setter); // 创建交换链 createSwapChain(setter); // 创建交换链中的imageView createImageView(setter); // 创建renderpass createRenderPass(setter); // 创建资源描述符层级 createDescriptorSetLayout(); // 创建管线缓存 createPipelineCache(); // 创建图形管线 createGraphicPipeline(setter); // 创建主要帧缓冲区 createFramebuffer(setter); // 创建命令缓冲池 createCommandPool(setter); // 创建统一缓存区 createUniformBuffer(setter,camera,mainRender); // 创建资源描述存储池 createDescriptorPool(mainRender); // 创建资源描述符集 createDescriptorSets(setter); // 分配命令缓存区 allocateCommandBuffers(setter); // 创建命令缓存区 createCommandBuffer(setter,mainRender,plumageGUI); } void VulkanBackend::VulkanFoundation::createInstance(PlumageRender::Setter setter) { // check validation layers if (setter.settings.validation && !checkValidationLayerSupport()) { throw std::runtime_error("validation layers requsted,but not available"); } //setup appliaction info VkApplicationInfo appInfo{}; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; appInfo.pApplicationName = "Hello Triangle"; appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); appInfo.pEngineName = "No_Engine"; appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); appInfo.apiVersion = VK_API_VERSION_1_0; // setup createInfo VkInstanceCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; createInfo.pApplicationInfo = &appInfo; auto requiredExtensions = getRequiredExtensions(setter); createInfo.enabledExtensionCount = static_cast<uint32_t>(requiredExtensions.size()); createInfo.ppEnabledExtensionNames = requiredExtensions.data(); // enable validation layer if available in createInfo VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{}; if (setter.settings.validation) { createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); populateDebugMessengerCreateInfo(debugCreateInfo); createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*)&debugCreateInfo; } else { createInfo.enabledLayerCount = 0; createInfo.pNext = nullptr; } // throw error in creating instance if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance"); } } bool VulkanBackend::VulkanFoundation::checkValidationLayerSupport() { uint32_t layerCount; vkEnumerateInstanceLayerProperties(&layerCount, nullptr); std::vector<VkLayerProperties> availableLayers(layerCount); vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); // check if validationLayers can be found in availableLayers for (const char* layerName : validationLayers) { bool layerFound = false; for (const auto& layerProperties : availableLayers) { if (strcmp(layerName, layerProperties.layerName) == 0) { layerFound = true; break; } } if (!layerFound) { return false; } } return true; } std::vector<const char*> VulkanBackend::VulkanFoundation::getRequiredExtensions(PlumageRender::Setter setter) { std::vector<const char*> extensions; if (!setter.settings.headless) { uint32_t glfwExtensionCount = 0; const char** glfwExtensions; glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); } if (setter.settings.validation) { extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); } return extensions; } void VulkanBackend::VulkanFoundation::setupDebugMessager(PlumageRender::Setter setter) { if (!setter.settings.validation) { return; } VkDebugUtilsMessengerCreateInfoEXT createInfo{}; populateDebugMessengerCreateInfo(createInfo); if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) { throw std::runtime_error("failed to set up debug messenger in setupDebugMessenger"); } } void VulkanBackend::VulkanFoundation::populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& debugCreateInfo) { debugCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; debugCreateInfo.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; debugCreateInfo.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; debugCreateInfo.pfnUserCallback = &debugCallback; debugCreateInfo.pUserData = nullptr; } // debugCallback用于校验层 VKAPI_ATTR VkBool32 VKAPI_CALL VulkanBackend::VulkanFoundation::debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageTypes, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) { { std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl; return VK_FALSE; } } VkResult VulkanBackend::VulkanFoundation::CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger) { auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); if (func != nullptr) { return func(instance, pCreateInfo, pAllocator, pDebugMessenger); } else { return VK_ERROR_EXTENSION_NOT_PRESENT; } } void VulkanBackend::VulkanFoundation::createSurface(PlumageRender::Setter setter) { if (setter.settings.headless) { return; } if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { throw std::runtime_error("failed to create window surface in createSurface()"); } } void VulkanBackend::VulkanFoundation::pickPhysicalDevice(PlumageRender::Setter setter) { uint32_t deviceCount = 0; vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); if (deviceCount == 0) { throw std::runtime_error("failed to find GPUs with Vulkan support"); } std::vector<VkPhysicalDevice> devices(deviceCount); vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); if (setter.settings.selectedPhysicalDeviceIndex != NULL) { physicalDevice = devices[setter.settings.selectedPhysicalDeviceIndex]; } for (const auto& device : devices) { if (isDeviceSuitable(device,setter)) { physicalDevice = device; break; } } if (physicalDevice == VK_NULL_HANDLE) { throw std::runtime_error("failed to find a suitable GPU"); } } bool VulkanBackend::VulkanFoundation::isDeviceSuitable(VkPhysicalDevice device,PlumageRender::Setter setter) { if (setter.settings.headless) { bool extensionsSupported = checkDeviceExtensionSupport(device); return extensionsSupported; } else { // 非无头下在检查扩展支持的同时要检查swapchain bool extensionsSupported = checkDeviceExtensionSupport(device); bool swapChainAdequate = false; QueueFamilyIndices indices = findQueueFamilies(device,setter); if (extensionsSupported) { SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device); swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); } } } bool VulkanBackend::VulkanFoundation::checkDeviceExtensionSupport(VkPhysicalDevice device) { uint32_t extensionCount; vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); std::vector<VkExtensionProperties> availableExtensions(extensionCount); vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); std::set<std::string> requiredExtensions(swapchainExtensions.begin(), swapchainExtensions.end()); for (const auto& extension : availableExtensions) { requiredExtensions.erase(extension.extensionName); } return requiredExtensions.empty(); } VulkanBackend::VulkanFoundation::QueueFamilyIndices VulkanBackend::VulkanFoundation::findQueueFamilies(VkPhysicalDevice device,PlumageRender::Setter setter) { QueueFamilyIndices indices; uint32_t queueFamilyCount = 0; vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount); vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); VkBool32 presentSupport = false; // 检查显示队列支持 int i = 0; if (!setter.settings.headless) { vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); } for (const auto& queueFamily : queueFamilies) { if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { indices.graphicsFamily = i; } // 无头下不需要检查present queue if (setter.settings.headless) { if (indices.isGraphicsFamilyComplete()) { break; } } else { if (indices.isGraphicsFamilyComplete() && indices.isPresentFamilyComplete()) { break; } } if (presentSupport) { indices.presentFamily = i; } i++; } return indices; } VulkanBackend::VulkanFoundation::SwapChainSupportDetails VulkanBackend::VulkanFoundation::querySwapChainSupport(VkPhysicalDevice device) { // 获得surface细节 SwapChainSupportDetails details; vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities); // 检查格式支持(image) uint32_t formatCount; vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr); if (formatCount != 0) { details.formats.resize(formatCount); vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data()); } // 检查显示模式支持 uint32_t presentModeCount; vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr); if (presentModeCount != 0) { details.presentModes.resize(presentModeCount); vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data()); } return details; } void VulkanBackend::VulkanFoundation::createLogicalDevice(PlumageRender::Setter setter) { QueueFamilyIndices indices = findQueueFamilies(physicalDevice,setter); std::vector<VkDeviceQueueCreateInfo> queueCreateInfos; if (setter.settings.headless) { VkDeviceQueueCreateInfo queueCreateInfo{}; queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queueCreateInfo.queueFamilyIndex = indices.graphicsFamily.value(); queueCreateInfo.queueCount = 1; float queuePriority = 1.0f; queueCreateInfo.pQueuePriorities = &queuePriority; queueCreateInfos.push_back(queueCreateInfo); } else { std::set<uint32_t> uniqueQueueFamilies = { indices.graphicsFamily.value(),indices.presentFamily.value() }; float queuePriority = 1.0f; for (uint32_t queueFamily : uniqueQueueFamilies) { VkDeviceQueueCreateInfo queueCreateInfo{}; queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queueCreateInfo.queueFamilyIndex = queueFamily; queueCreateInfo.queueCount = 1; queueCreateInfo.pQueuePriorities = &queuePriority; queueCreateInfos.push_back(queueCreateInfo); } } VkPhysicalDeviceFeatures enableFeatures{}; if (deviceFeatures.samplerAnisotropy) { enableFeatures.samplerAnisotropy = VK_TRUE; } std::vector<const char*> enabledExtensions{}; if (!setter.settings.headless) { for (auto swapchainExtension: swapchainExtensions) { enabledExtensions.push_back(swapchainExtension); } } VkDeviceCreateInfo deviceCreateInfo{}; deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; deviceCreateInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size()); deviceCreateInfo.pQueueCreateInfos = queueCreateInfos.data(); deviceCreateInfo.pEnabledFeatures = &enableFeatures; deviceCreateInfo.enabledExtensionCount = static_cast<uint32_t>(enabledExtensions.size()); deviceCreateInfo.ppEnabledExtensionNames = enabledExtensions.data(); //新版本vulkan已不对实例和设备特定验证层做区分,此处保证兼容性 if (setter.settings.validation) { deviceCreateInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size()); deviceCreateInfo.ppEnabledLayerNames = validationLayers.data(); } else { deviceCreateInfo.enabledLayerCount = 0; } if (vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("failed to create logical device"); } vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicQueue); if (setter.settings.headless) { vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue); } } void VulkanBackend::VulkanFoundation::createSwapChain(PlumageRender::Setter setter) { if (setter.settings.headless) { return; } SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice); VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats); VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes,setter); VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities); uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) { imageCount = swapChainSupport.capabilities.maxImageCount; } VkSwapchainCreateInfoKHR createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; createInfo.surface = surface; createInfo.minImageCount = imageCount; createInfo.imageFormat = surfaceFormat.format; createInfo.imageColorSpace = surfaceFormat.colorSpace; createInfo.imageExtent = extent; createInfo.imageArrayLayers = 1; createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; QueueFamilyIndices indices = findQueueFamilies(physicalDevice,setter); uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(),indices.presentFamily.value() }; if (indices.graphicsFamily != indices.presentFamily) { createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; createInfo.queueFamilyIndexCount = 2; createInfo.pQueueFamilyIndices = queueFamilyIndices; } else { createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; createInfo.queueFamilyIndexCount = 0; createInfo.pQueueFamilyIndices = nullptr; } createInfo.preTransform = swapChainSupport.capabilities.currentTransform; createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; createInfo.presentMode = presentMode; createInfo.clipped = VK_TRUE; createInfo.oldSwapchain = VK_NULL_HANDLE; if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("failed to create swap chain !"); } vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); swapChainImages.resize(imageCount); vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); // store swap format and swap extent to member variable swapChainImageFormat = surfaceFormat.format; swapChainExtent = extent; } VkSurfaceFormatKHR VulkanBackend::VulkanFoundation::chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats) { for (const auto& availableFormat : availableFormats) { if (availableFormat.format == VK_FORMAT_B8G8R8_SRGB && availableFormat.colorSpace == VK_COLORSPACE_SRGB_NONLINEAR_KHR) { return availableFormat; } } return availableFormats[0]; } VkPresentModeKHR VulkanBackend::VulkanFoundation::chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes, PlumageRender::Setter setter) { // Get available present modes uint32_t presentModeCount; VK_CHECK_RESULT(fpGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, NULL)); assert(presentModeCount > 0); std::vector<VkPresentModeKHR> presentModes(presentModeCount); VK_CHECK_RESULT(fpGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, presentModes.data())); // The VK_PRESENT_MODE_FIFO_KHR mode must always be present as per spec // This mode waits for the vertical blank ("v-sync") // 垂直同步模式 VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR; // If v-sync is not requested, try to find a mailbox mode // It's the lowest latency non-tearing present mode available if (!setter.settings.vsync) { for (size_t i = 0; i < presentModeCount; i++) { if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR) { swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR; break; } if ((swapchainPresentMode != VK_PRESENT_MODE_MAILBOX_KHR) && (presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR)) { swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; } } } } VkExtent2D VulkanBackend::VulkanFoundation::chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) { if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) { return capabilities.currentExtent; } else { int width, height; glfwGetFramebufferSize(window, &width, &height); VkExtent2D actualExtent = { static_cast<uint32_t>(width), static_cast<uint32_t>(height) }; actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width); actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height); return actualExtent; } } void VulkanBackend::VulkanFoundation::createImageView(PlumageRender::Setter setter) { VkFormat colorAttachmentFormat = VK_FORMAT_R8G8B8A8_UNORM; VkFormat depthFormat = findDepthFormat(); if (setter.settings.headless) { if (setter.settings.multiSampling) { VkImageCreateInfo imageCI{}; imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageCI.imageType = VK_IMAGE_TYPE_2D; imageCI.format = colorAttachmentFormat; imageCI.extent.width = setter.settings.width; imageCI.extent.height = setter.settings.height; imageCI.extent.depth = 1; imageCI.mipLevels = 1; imageCI.arrayLayers = 1; imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageCI.tiling = VK_IMAGE_TILING_OPTIMAL; imageCI.samples = setter.settings.sampleCount; imageCI.usage = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; imageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; VK_CHECK_RESULT(vkCreateImage(device, &imageCI, nullptr, &multisampleTarget.colorAttachment.image)); VkMemoryRequirements memReqs; vkGetImageMemoryRequirements(device, multisampleTarget.colorAttachment.image, &memReqs); VkMemoryAllocateInfo memAllocInfo{}; memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; memAllocInfo.allocationSize = memReqs.size; VkBool32 lazyMemTypePresent; memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT, &lazyMemTypePresent); if (!lazyMemTypePresent) { memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); } VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &multisampleTarget.colorAttachment.memory)); vkBindImageMemory(device, multisampleTarget.colorAttachment.image, multisampleTarget.colorAttachment.memory, 0); // Create image view for the MSAA color image target VkImageViewCreateInfo imageViewCI{}; imageViewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; imageViewCI.image = multisampleTarget.colorAttachment.image; imageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D; imageViewCI.format = colorAttachmentFormat; imageViewCI.components.r = VK_COMPONENT_SWIZZLE_R; imageViewCI.components.g = VK_COMPONENT_SWIZZLE_G; imageViewCI.components.b = VK_COMPONENT_SWIZZLE_B; imageViewCI.components.a = VK_COMPONENT_SWIZZLE_A; imageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageViewCI.subresourceRange.levelCount = 1; imageViewCI.subresourceRange.layerCount = 1; VK_CHECK_RESULT(vkCreateImageView(device, &imageViewCI, nullptr, &multisampleTarget.colorAttachment.view)); // Depth target imageCI.imageType = VK_IMAGE_TYPE_2D; imageCI.format = depthFormat; imageCI.extent.width =setter.settings.width; imageCI.extent.height =setter.settings.height; imageCI.extent.depth = 1; imageCI.mipLevels = 1; imageCI.arrayLayers = 1; imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageCI.tiling = VK_IMAGE_TILING_OPTIMAL; imageCI.samples =setter.settings.sampleCount; imageCI.usage = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; imageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; VK_CHECK_RESULT(vkCreateImage(device, &imageCI, nullptr, &multisampleTarget.depthAttachment.image)); vkGetImageMemoryRequirements(device, multisampleTarget.depthAttachment.image, &memReqs); memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; memAllocInfo.allocationSize = memReqs.size; memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT, &lazyMemTypePresent); if (!lazyMemTypePresent) { memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); } VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &multisampleTarget.depthAttachment.memory)); vkBindImageMemory(device, multisampleTarget.depthAttachment.image, multisampleTarget.depthAttachment.memory, 0); // Create image view for the MSAA target imageViewCI.image = multisampleTarget.depthAttachment.image; imageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D; imageViewCI.format = depthFormat; imageViewCI.components.r = VK_COMPONENT_SWIZZLE_R; imageViewCI.components.g = VK_COMPONENT_SWIZZLE_G; imageViewCI.components.b = VK_COMPONENT_SWIZZLE_B; imageViewCI.components.a = VK_COMPONENT_SWIZZLE_A; imageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; imageViewCI.subresourceRange.levelCount = 1; imageViewCI.subresourceRange.layerCount = 1; VK_CHECK_RESULT(vkCreateImageView(device, &imageViewCI, nullptr, &multisampleTarget.depthAttachment.view)); } // creat color image VkImageCreateInfo imageCI{}; imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageCI.imageType = VK_IMAGE_TYPE_2D; imageCI.format = colorAttachmentFormat; imageCI.extent.width =setter.settings.width; imageCI.extent.height =setter.settings.height; imageCI.extent.depth = 1; imageCI.mipLevels = 1; imageCI.arrayLayers = 1; imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageCI.tiling = VK_IMAGE_TILING_OPTIMAL; imageCI.samples = VK_SAMPLE_COUNT_1_BIT; imageCI.usage = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; imageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; VK_CHECK_RESULT(vkCreateImage(device, &imageCI, nullptr, &colorAttachment.image)); VkMemoryRequirements memReqs; vkGetImageMemoryRequirements(device,colorAttachment.image, &memReqs); VkMemoryAllocateInfo memAllocInfo{}; memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; memAllocInfo.allocationSize = memReqs.size; VkBool32 lazyMemTypePresent; memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT, &lazyMemTypePresent); if (!lazyMemTypePresent) { memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); } VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &colorAttachment.memory)); vkBindImageMemory(device, multisampleTarget.colorAttachment.image, colorAttachment.memory, 0); // Create image view for the color image VkImageViewCreateInfo imageViewCI{}; imageViewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; imageViewCI.image = colorAttachment.image; imageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D; imageViewCI.format = colorAttachmentFormat; imageViewCI.components.r = VK_COMPONENT_SWIZZLE_R; imageViewCI.components.g = VK_COMPONENT_SWIZZLE_G; imageViewCI.components.b = VK_COMPONENT_SWIZZLE_B; imageViewCI.components.a = VK_COMPONENT_SWIZZLE_A; imageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageViewCI.subresourceRange.levelCount = 1; imageViewCI.subresourceRange.layerCount = 1; VK_CHECK_RESULT(vkCreateImageView(device, &imageViewCI, nullptr, &colorAttachment.view)); // create image for the depth image VkImageCreateInfo image = {}; image.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; image.pNext = NULL; image.imageType = VK_IMAGE_TYPE_2D; image.format = depthFormat; image.extent.width =setter.settings.width; image.extent.height =setter.settings.height; image.extent.depth = 1; image.mipLevels = 1; image.arrayLayers = 1; image.samples = VK_SAMPLE_COUNT_1_BIT; image.tiling = VK_IMAGE_TILING_OPTIMAL; image.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; image.flags = 0; VK_CHECK_RESULT(vkCreateImage(device, &image, nullptr, &depthAttachment.image)); VkMemoryAllocateInfo memAlloc = {}; memAlloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; memAlloc.pNext = NULL; memAlloc.allocationSize = 0; memAlloc.memoryTypeIndex = 0; VkMemoryRequirements memReqs; vkGetImageMemoryRequirements(device, depthAttachment.image, &memReqs); memAlloc.allocationSize = memReqs.size; memAlloc.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VK_CHECK_RESULT(vkAllocateMemory(device, &memAlloc, nullptr, &depthAttachment.memory)); VK_CHECK_RESULT(vkBindImageMemory(device, depthAttachment.image, depthAttachment.memory, 0)); // create image view for depth image VkImageViewCreateInfo depthStencilView = {}; depthStencilView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; depthStencilView.pNext = NULL; depthStencilView.viewType = VK_IMAGE_VIEW_TYPE_2D; depthStencilView.format = depthFormat; depthStencilView.image = depthAttachment.image; depthStencilView.flags = 0; depthStencilView.subresourceRange = {}; depthStencilView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; if (depthFormat >= VK_FORMAT_D16_UNORM_S8_UINT) { depthStencilView.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; } depthStencilView.subresourceRange.baseMipLevel = 0; depthStencilView.subresourceRange.levelCount = 1; depthStencilView.subresourceRange.baseArrayLayer = 0; depthStencilView.subresourceRange.layerCount = 1; VK_CHECK_RESULT(vkCreateImageView(device, &depthStencilView, nullptr, &depthAttachment.view)); } else { swapChainImageViews.resize(swapChainImages.size()); for (size_t i = 0; i < swapChainImages.size(); i++) { VkImageViewCreateInfo creatInfo{}; creatInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; creatInfo.image = swapChainImages[i]; creatInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; creatInfo.format = swapChainImageFormat; creatInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; creatInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; creatInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; creatInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; creatInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; creatInfo.subresourceRange.baseMipLevel = 0; creatInfo.subresourceRange.levelCount = 1; creatInfo.subresourceRange.baseArrayLayer = 0; creatInfo.subresourceRange.layerCount = 1; if (vkCreateImageView(device, &creatInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { throw std::runtime_error("failed to creat image view"); } } } } void VulkanBackend::VulkanFoundation::createRenderPass(PlumageRender::Setter setter) { VkFormat colorAttachmentFormat; VkFormat depthAttachmentFormat = findDepthFormat(); VkImageLayout colorAttachmentFinallayout; if (setter.settings.headless) { colorAttachmentFormat = VK_FORMAT_R8G8B8A8_UNORM; colorAttachmentFinallayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; } else { colorAttachmentFormat = swapChainImageFormat; colorAttachmentFinallayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; } if (setter.settings.multiSampling) { std::array<VkAttachmentDescription, 4> attachments = {}; // Multisampled attachment that we render to attachments[0].format = colorAttachmentFormat; attachments[0].samples =setter.settings.sampleCount; attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // This is the frame buffer attachment to where the multisampled image // will be resolved to and which will be presented to the swapchain attachments[1].format = colorAttachmentFormat; attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; attachments[1].finalLayout = colorAttachmentFinallayout; // Multisampled depth attachment we render to attachments[2].format = depthAttachmentFormat; attachments[2].samples =setter.settings.sampleCount; attachments[2].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; attachments[2].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachments[2].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachments[2].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachments[2].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; attachments[2].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; // Depth resolve attachment attachments[3].format = depthAttachmentFormat; attachments[3].samples = VK_SAMPLE_COUNT_1_BIT; attachments[3].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachments[3].storeOp = VK_ATTACHMENT_STORE_OP_STORE; attachments[3].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachments[3].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachments[3].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; attachments[3].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; VkAttachmentReference colorReference = {}; colorReference.attachment = 0; colorReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; VkAttachmentReference depthReference = {}; depthReference.attachment = 2; depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; // Resolve attachment reference for the color attachment VkAttachmentReference resolveReference = {}; resolveReference.attachment = 1; resolveReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; VkSubpassDescription subpass = {}; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &colorReference; // Pass our resolve attachments to the sub pass subpass.pResolveAttachments = &resolveReference; subpass.pDepthStencilAttachment = &depthReference; std::array<VkSubpassDependency, 2> dependencies; dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; dependencies[0].dstSubpass = 0; dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; dependencies[1].srcSubpass = 0; dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; VkRenderPassCreateInfo renderPassCI = {}; renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; renderPassCI.attachmentCount = static_cast<uint32_t>(attachments.size()); renderPassCI.pAttachments = attachments.data(); renderPassCI.subpassCount = 1; renderPassCI.pSubpasses = &subpass; renderPassCI.dependencyCount = 2; renderPassCI.pDependencies = dependencies.data(); VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassCI, nullptr, &renderPass)); } else { std::array<VkAttachmentDescription, 2> attachments = {}; // Color attachment attachments[0].format = colorAttachmentFormat; attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; attachments[0].finalLayout = colorAttachmentFinallayout; // Depth attachment attachments[1].format = depthAttachmentFormat; attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; VkAttachmentReference colorReference = {}; colorReference.attachment = 0; colorReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; VkAttachmentReference depthReference = {}; depthReference.attachment = 1; depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; VkSubpassDescription subpassDescription = {}; subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpassDescription.colorAttachmentCount = 1; subpassDescription.pColorAttachments = &colorReference; subpassDescription.pDepthStencilAttachment = &depthReference; subpassDescription.inputAttachmentCount = 0; subpassDescription.pInputAttachments = nullptr; subpassDescription.preserveAttachmentCount = 0; subpassDescription.pPreserveAttachments = nullptr; subpassDescription.pResolveAttachments = nullptr; // Subpass dependencies for layout transitions std::array<VkSubpassDependency, 2> dependencies; dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; dependencies[0].dstSubpass = 0; dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; dependencies[1].srcSubpass = 0; dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; VkRenderPassCreateInfo renderPassCI{}; renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; renderPassCI.attachmentCount = static_cast<uint32_t>(attachments.size()); renderPassCI.pAttachments = attachments.data(); renderPassCI.subpassCount = 1; renderPassCI.pSubpasses = &subpassDescription; renderPassCI.dependencyCount = static_cast<uint32_t>(dependencies.size()); renderPassCI.pDependencies = dependencies.data(); VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassCI, nullptr, &renderPass)); } } VkFormat VulkanBackend::VulkanFoundation::findDepthFormat() { return findSupportedFormat( { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT }, VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT ); } VkFormat VulkanBackend::VulkanFoundation::findSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) { for (VkFormat format : candidates) { VkFormatProperties props; vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &props); if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features) { return format; } else if(tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features) == features) { return format; } } throw std::runtime_error("failed to find supported format"); } bool VulkanBackend::VulkanFoundation::hasStencilComponent(VkFormat format) { return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT; } void VulkanBackend::VulkanFoundation::createDescriptorSetLayout() { createSceneDescriptorSetLayout(); createMaterialDescriptorSetLayout(); createNodeDescriptorSetLayout(); } void VulkanBackend::VulkanFoundation::createSceneDescriptorSetLayout() { // scene,场景的资源描述符 std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = { { 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, nullptr }, { 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr }, { 2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr }, { 3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr }, { 4, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr }, }; VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI{}; descriptorSetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; descriptorSetLayoutCI.pBindings = setLayoutBindings.data(); descriptorSetLayoutCI.bindingCount = static_cast<uint32_t>(setLayoutBindings.size()); VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &descriptorSetLayouts.scene)); } void VulkanBackend::VulkanFoundation::createMaterialDescriptorSetLayout() { // 材质或材质采样器的资源描述符 std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = { { 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr }, { 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr }, { 2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr }, { 3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr }, { 4, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr }, }; VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI{}; descriptorSetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; descriptorSetLayoutCI.pBindings = setLayoutBindings.data(); descriptorSetLayoutCI.bindingCount = static_cast<uint32_t>(setLayoutBindings.size()); VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &descriptorSetLayouts.material)); } void VulkanBackend::VulkanFoundation::createNodeDescriptorSetLayout() { // 模型结点或矩阵的资源描述符 std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = { { 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT, nullptr }, }; VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI{}; descriptorSetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; descriptorSetLayoutCI.pBindings = setLayoutBindings.data(); descriptorSetLayoutCI.bindingCount = static_cast<uint32_t>(setLayoutBindings.size()); VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &descriptorSetLayouts.node)); } void VulkanBackend::VulkanFoundation::createPipelineCache() { VkPipelineCacheCreateInfo pipelineCacheCreateInfo{}; pipelineCacheCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; VK_CHECK_RESULT(vkCreatePipelineCache(device, &pipelineCacheCreateInfo, nullptr, &pipelineCache)); } void VulkanBackend::VulkanFoundation::createGraphicPipeline(PlumageRender::Setter setter) { VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI{}; inputAssemblyStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; inputAssemblyStateCI.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; VkPipelineRasterizationStateCreateInfo rasterizationStateCI{}; rasterizationStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; rasterizationStateCI.polygonMode = VK_POLYGON_MODE_FILL; rasterizationStateCI.cullMode = VK_CULL_MODE_BACK_BIT; rasterizationStateCI.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; rasterizationStateCI.lineWidth = 1.0f; VkPipelineColorBlendAttachmentState blendAttachmentState{}; blendAttachmentState.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; blendAttachmentState.blendEnable = VK_FALSE; VkPipelineColorBlendStateCreateInfo colorBlendStateCI{}; colorBlendStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; colorBlendStateCI.attachmentCount = 1; colorBlendStateCI.pAttachments = &blendAttachmentState; VkPipelineDepthStencilStateCreateInfo depthStencilStateCI{}; depthStencilStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; depthStencilStateCI.depthTestEnable = VK_FALSE; depthStencilStateCI.depthWriteEnable = VK_FALSE; depthStencilStateCI.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; depthStencilStateCI.front = depthStencilStateCI.back; depthStencilStateCI.back.compareOp = VK_COMPARE_OP_ALWAYS; VkPipelineViewportStateCreateInfo viewportStateCI{}; viewportStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; viewportStateCI.viewportCount = 1; viewportStateCI.scissorCount = 1; VkPipelineMultisampleStateCreateInfo multisampleStateCI{}; multisampleStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; if (setter.settings.multiSampling) { multisampleStateCI.rasterizationSamples =setter.settings.sampleCount; } std::vector<VkDynamicState> dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; VkPipelineDynamicStateCreateInfo dynamicStateCI{}; dynamicStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; dynamicStateCI.pDynamicStates = dynamicStateEnables.data(); dynamicStateCI.dynamicStateCount = static_cast<uint32_t>(dynamicStateEnables.size()); // Pipeline layout const std::vector<VkDescriptorSetLayout> setLayouts = { descriptorSetLayouts.scene, descriptorSetLayouts.material, descriptorSetLayouts.node }; VkPipelineLayoutCreateInfo pipelineLayoutCI{}; pipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipelineLayoutCI.setLayoutCount = static_cast<uint32_t>(setLayouts.size()); pipelineLayoutCI.pSetLayouts = setLayouts.data(); VkPushConstantRange pushConstantRange{}; pushConstantRange.size = sizeof(PBR::Material::pushConstBlockMaterial); pushConstantRange.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; pipelineLayoutCI.pushConstantRangeCount = 1; pipelineLayoutCI.pPushConstantRanges = &pushConstantRange; VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelineLayout)); // Vertex bindings an attributes VkVertexInputBindingDescription vertexInputBinding = { 0, sizeof(glTFModel::Model::Vertex), VK_VERTEX_INPUT_RATE_VERTEX }; std::vector<VkVertexInputAttributeDescription> vertexInputAttributes = { { 0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0 }, { 1, 0, VK_FORMAT_R32G32B32_SFLOAT, sizeof(float) * 3 }, { 2, 0, VK_FORMAT_R32G32_SFLOAT, sizeof(float) * 6 }, { 3, 0, VK_FORMAT_R32G32_SFLOAT, sizeof(float) * 8 }, { 4, 0, VK_FORMAT_R32G32B32A32_SFLOAT, sizeof(float) * 10 }, { 5, 0, VK_FORMAT_R32G32B32A32_SFLOAT, sizeof(float) * 14 }, { 6, 0, VK_FORMAT_R32G32B32A32_SFLOAT, sizeof(float) * 18 } }; VkPipelineVertexInputStateCreateInfo vertexInputStateCI{}; vertexInputStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; vertexInputStateCI.vertexBindingDescriptionCount = 1; vertexInputStateCI.pVertexBindingDescriptions = &vertexInputBinding; vertexInputStateCI.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertexInputAttributes.size()); vertexInputStateCI.pVertexAttributeDescriptions = vertexInputAttributes.data(); // Pipelines std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages; VkGraphicsPipelineCreateInfo pipelineCI{}; pipelineCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; pipelineCI.layout = pipelineLayout; pipelineCI.renderPass = renderPass; pipelineCI.pInputAssemblyState = &inputAssemblyStateCI; pipelineCI.pVertexInputState = &vertexInputStateCI; pipelineCI.pRasterizationState = &rasterizationStateCI; pipelineCI.pColorBlendState = &colorBlendStateCI; pipelineCI.pMultisampleState = &multisampleStateCI; pipelineCI.pViewportState = &viewportStateCI; pipelineCI.pDepthStencilState = &depthStencilStateCI; pipelineCI.pDynamicState = &dynamicStateCI; pipelineCI.stageCount = static_cast<uint32_t>(shaderStages.size()); pipelineCI.pStages = shaderStages.data(); if (setter.settings.multiSampling) { multisampleStateCI.rasterizationSamples =setter.settings.sampleCount; } // Skybox pipeline (background cube) shaderStages = { loadShader(device,setter.filePath.skyboxVertShaderPath, VK_SHADER_STAGE_VERTEX_BIT), loadShader(device,setter.filePath.skyboxFragShaderPath, VK_SHADER_STAGE_FRAGMENT_BIT) }; VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.skybox)); for (auto shaderStage : shaderStages) { vkDestroyShaderModule(device, shaderStage.module, nullptr); } // PBR pipeline shaderStages = { loadShader(device,setter.filePath.pbrVertShaderPath, VK_SHADER_STAGE_VERTEX_BIT), loadShader(device,setter.filePath.pbrFragShaderPath, VK_SHADER_STAGE_FRAGMENT_BIT) }; depthStencilStateCI.depthWriteEnable = VK_TRUE; depthStencilStateCI.depthTestEnable = VK_TRUE; VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.pbr)); rasterizationStateCI.cullMode = VK_CULL_MODE_NONE; VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.pbrDoubleSided)); rasterizationStateCI.cullMode = VK_CULL_MODE_NONE; blendAttachmentState.blendEnable = VK_TRUE; blendAttachmentState.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; blendAttachmentState.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; blendAttachmentState.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; blendAttachmentState.colorBlendOp = VK_BLEND_OP_ADD; blendAttachmentState.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; blendAttachmentState.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; blendAttachmentState.alphaBlendOp = VK_BLEND_OP_ADD; VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.pbrAlphaBlend)); for (auto shaderStage : shaderStages) { vkDestroyShaderModule(device, shaderStage.module, nullptr); } //Create Tone Mapping render pipeline //CreateToneMappingPipeline(); } void VulkanBackend::VulkanFoundation::createFramebuffer(PlumageRender::Setter setter) { if (setter.settings.headless) { auto frameRange = setter.settings.endFrameIndex - setter.settings.startFrameCount; if (setter.settings.multiSampling) { for (int i = 0; i < frameRange; i++) { VkImageView attachments[4]; attachments[0] = multisampleTarget.colorAttachment.view; attachments[1] = multisampleTarget.depthAttachment.view; attachments[2] = depthAttachment.view; attachments[3] = colorAttachment.view; VkFramebufferCreateInfo framebufferCreateInfo = vks::initializers::framebufferCreateInfo(); framebufferCreateInfo.renderPass = renderPass; framebufferCreateInfo.attachmentCount = 4; framebufferCreateInfo.pAttachments = attachments; framebufferCreateInfo.width =setter.settings.width; framebufferCreateInfo.height =setter.settings.height; framebufferCreateInfo.layers = 1; VK_CHECK_RESULT(vkCreateFramebuffer(device, &framebufferCreateInfo, nullptr, &framebuffers[i])); } } else { for (int i = 0; i < frameRange; i++) { VkImageView attachments[2]; attachments[0] = colorAttachment.view; attachments[1] = depthAttachment.view; VkFramebufferCreateInfo framebufferCreateInfo = vks::initializers::framebufferCreateInfo(); framebufferCreateInfo.renderPass = renderPass; framebufferCreateInfo.attachmentCount = 2; framebufferCreateInfo.pAttachments = attachments; framebufferCreateInfo.width =setter.settings.width; framebufferCreateInfo.height =setter.settings.height; framebufferCreateInfo.layers = 1; VK_CHECK_RESULT(vkCreateFramebuffer(device, &framebufferCreateInfo, nullptr, &framebuffers[i])); } } } else { createSwapChainFramebuffer(setter); } } void VulkanBackend::VulkanFoundation::createSwapChainFramebuffer(PlumageRender::Setter setter) { uint32_t attachmentCount; VkImageView attachments[attachmentCount]; if (setter.settings.multiSampling) { attachmentCount = 4; attachments[0] = multisampleTarget.colorAttachment.view; attachments[1] = multisampleTarget.depthAttachment.view; attachments[2] = depthAttachment.view; } else { attachmentCount = 2; attachments[1] = depthAttachment.view; } VkFramebufferCreateInfo frameBufferCI{}; frameBufferCI.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; frameBufferCI.pNext = NULL; frameBufferCI.renderPass = renderPass; frameBufferCI.attachmentCount = attachmentCount; frameBufferCI.pAttachments = attachments; frameBufferCI.width =setter.settings.width; frameBufferCI.height =setter.settings.height; frameBufferCI.layers = 1; // Create frame buffers for every swap chain image framebuffers.resize(swapChainImageViews.size()); for (uint32_t i = 0; i < swapChainImageViews.size(); i++) { if (setter.settings.multiSampling) { attachments[3] = swapChainImageViews[i]; } else { attachments[0] = swapChainImageViews[i]; } VK_CHECK_RESULT(vkCreateFramebuffer(device, &frameBufferCI, nullptr, &framebuffers[i])); } } void VulkanBackend::VulkanFoundation::createCommandPool(PlumageRender::Setter setter) { QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice,setter); VkCommandPoolCreateInfo poolInfo{}; poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value(); if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { throw std::runtime_error("failed to create command pool in createCommandpool"); } } void VulkanBackend::VulkanFoundation::createUniformBuffer(PlumageRender::Setter setter,Camera camera,PlumageRender::renderMain mainRender) { if (setter.settings.headless) { auto frameRange = setter.settings.endFrameIndex - setter.settings.startFrameCount; uniformBuffers.resize(frameRange); } else { uniformBuffers.resize(swapChainImages.size()); } for (auto& uniformBuffer : uniformBuffers) { uniformBuffer.scene.create(vulkanDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(shaderDataScene)); uniformBuffer.skybox.create(vulkanDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(shaderDataSkybox)); uniformBuffer.params.create(vulkanDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(PBR::Material::shaderData)); } updateUniformBuffers(camera,mainRender); } void VulkanBackend::VulkanFoundation::updateUniformBuffers(Camera camera, PlumageRender::renderMain mainRender) { // Scene shaderDataScene.projection = camera.matrices.perspective; shaderDataScene.view = camera.matrices.view; // Center and scale model float scale = (1.0f / std::max(mainRender.models.scene.aabb[0][0], std::max(mainRender.models.scene.aabb[1][1], mainRender.models.scene.aabb[2][2]))) * 0.5f; glm::vec3 translate = -glm::vec3(mainRender.models.scene.aabb[3][0], mainRender.models.scene.aabb[3][1], mainRender.models.scene.aabb[3][2]); translate += -0.5f * glm::vec3(mainRender.models.scene.aabb[0][0], mainRender.models.scene.aabb[1][1], mainRender.models.scene.aabb[2][2]); shaderDataScene.model = glm::mat4(1.0f); shaderDataScene.model[0][0] = scale; shaderDataScene.model[1][1] = scale; shaderDataScene.model[2][2] = scale; shaderDataScene.model = glm::translate(shaderDataScene.model, translate); shaderDataScene.camPos = glm::vec3( -camera.position.z * sin(glm::radians(camera.rotation.y)) * cos(glm::radians(camera.rotation.x)), -camera.position.z * sin(glm::radians(camera.rotation.x)), camera.position.z * cos(glm::radians(camera.rotation.y)) * cos(glm::radians(camera.rotation.x)) ); // Skybox shaderDataSkybox.projection = camera.matrices.perspective; shaderDataSkybox.view = camera.matrices.view; shaderDataSkybox.model = glm::mat4(glm::mat3(camera.matrices.view)); } void VulkanBackend::VulkanFoundation::createDescriptorPool(PlumageRender::renderMain mainRender) { /* Descriptor Pool */ uint32_t imageSamplerCount = 0; uint32_t materialCount = 0; uint32_t meshCount = 0; // Environment samplers (radiance, irradiance, brdflut) imageSamplerCount += 3; std::vector<glTFModel::Model*> modellist = { &mainRender.models.skybox, &mainRender.models.scene }; for (auto& model : modellist) { for (auto& material : model->materials) { imageSamplerCount += 5; materialCount++; } for (auto node : model->linearNodes) { if (node->mesh) { meshCount++; } } } std::vector<VkDescriptorPoolSize> poolSizes = { { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, (4 + meshCount) * static_cast<uint32_t>(MAX_FRAME_IN_FLIGHT)}, { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, imageSamplerCount * static_cast<uint32_t>(MAX_FRAME_IN_FLIGHT) } }; VkDescriptorPoolCreateInfo descriptorPoolCI{}; descriptorPoolCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; descriptorPoolCI.poolSizeCount = 2; descriptorPoolCI.pPoolSizes = poolSizes.data(); descriptorPoolCI.maxSets = (2 + materialCount + meshCount) * static_cast<uint32_t>(MAX_FRAME_IN_FLIGHT); VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolCI, nullptr, &descriptorPool)); } void VulkanBackend::VulkanFoundation::createDescriptorSets(PlumageRender::Setter setter) { if (setter.settings.headless) { auto frameRange = setter.settings.endFrameIndex - setter.settings.startFrameCount; descriptorSets.resize(frameRange); } else { descriptorSets.resize(swapChainImages.size()); } createSceneDescriptorSets(); createMaterialDescriptorSets(); createModelNodeDescriptorSets(); createSkyboxDescriptorSets(); } // Scene (matrices and environment maps) void VulkanBackend::VulkanFoundation::createSceneDescriptorSets() { for (auto i = 0; i < descriptorSets.size(); i++) { VkDescriptorSetAllocateInfo descriptorSetAllocInfo{}; descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; descriptorSetAllocInfo.descriptorPool = descriptorPool; descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayouts.scene; descriptorSetAllocInfo.descriptorSetCount = 1; VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &descriptorSets[i].scene)); std::array<VkWriteDescriptorSet, 5> writeDescriptorSets{}; writeDescriptorSets[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeDescriptorSets[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; writeDescriptorSets[0].descriptorCount = 1; writeDescriptorSets[0].dstSet = descriptorSets[i].scene; writeDescriptorSets[0].dstBinding = 0; writeDescriptorSets[0].pBufferInfo = &uniformBuffers[i].scene.descriptor; writeDescriptorSets[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeDescriptorSets[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; writeDescriptorSets[1].descriptorCount = 1; writeDescriptorSets[1].dstSet = descriptorSets[i].scene; writeDescriptorSets[1].dstBinding = 1; writeDescriptorSets[1].pBufferInfo = &uniformBuffers[i].params.descriptor; writeDescriptorSets[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeDescriptorSets[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writeDescriptorSets[2].descriptorCount = 1; writeDescriptorSets[2].dstSet = descriptorSets[i].scene; writeDescriptorSets[2].dstBinding = 2; writeDescriptorSets[2].pImageInfo = &PBR::Material::textures.irradianceCube.descriptor; writeDescriptorSets[3].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeDescriptorSets[3].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writeDescriptorSets[3].descriptorCount = 1; writeDescriptorSets[3].dstSet = descriptorSets[i].scene; writeDescriptorSets[3].dstBinding = 3; writeDescriptorSets[3].pImageInfo = &PBR::Material::textures.prefilteredCube.descriptor; writeDescriptorSets[4].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeDescriptorSets[4].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writeDescriptorSets[4].descriptorCount = 1; writeDescriptorSets[4].dstSet = descriptorSets[i].scene; writeDescriptorSets[4].dstBinding = 4; writeDescriptorSets[4].pImageInfo = &PBR::Material::textures.lutBrdf.descriptor; vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL); } } // Per-Material descriptor sets void VulkanBackend::VulkanFoundation::createMaterialDescriptorSets() { for (auto& material : PlumageRender::renderMain::models.scene.materials) { VkDescriptorSetAllocateInfo descriptorSetAllocInfo{}; descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; descriptorSetAllocInfo.descriptorPool = descriptorPool; descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayouts.material; descriptorSetAllocInfo.descriptorSetCount = 1; VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &material.descriptorSet)); std::vector<VkDescriptorImageInfo> imageDescriptors = { PBR::Material::textures.empty.descriptor, PBR::Material::textures.empty.descriptor, material.normalTexture ? material.normalTexture->descriptor : PBR::Material::textures.empty.descriptor, material.occlusionTexture ? material.occlusionTexture->descriptor : PBR::Material::textures.empty.descriptor, material.emissiveTexture ? material.emissiveTexture->descriptor : PBR::Material::textures.empty.descriptor }; if (material.pbrWorkflows.metallicRoughness) { if (material.baseColorTexture) { imageDescriptors[0] = material.baseColorTexture->descriptor; } if (material.metallicRoughnessTexture) { imageDescriptors[1] = material.metallicRoughnessTexture->descriptor; } } if (material.pbrWorkflows.specularGlossiness) { if (material.extension.diffuseTexture) { imageDescriptors[0] = material.extension.diffuseTexture->descriptor; } if (material.extension.specularGlossinessTexture) { imageDescriptors[1] = material.extension.specularGlossinessTexture->descriptor; } } std::array<VkWriteDescriptorSet, 5> writeDescriptorSets{}; for (size_t i = 0; i < imageDescriptors.size(); i++) { writeDescriptorSets[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeDescriptorSets[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writeDescriptorSets[i].descriptorCount = 1; writeDescriptorSets[i].dstSet = material.descriptorSet; writeDescriptorSets[i].dstBinding = static_cast<uint32_t>(i); writeDescriptorSets[i].pImageInfo = &imageDescriptors[i]; } vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL); } } // Model node (matrices) void VulkanBackend::VulkanFoundation::createModelNodeDescriptorSets() { // Per-Node descriptor set for (auto& node : PlumageRender::renderMain::models.scene.nodes) { setupglTFNodeDescriptorSet(node); } } // attention: gltf-spec void VulkanBackend::VulkanFoundation::setupglTFNodeDescriptorSet(glTFModel::Node* node) { if (node->mesh) { VkDescriptorSetAllocateInfo descriptorSetAllocInfo{}; descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; descriptorSetAllocInfo.descriptorPool = descriptorPool; descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayouts.node; descriptorSetAllocInfo.descriptorSetCount = 1; VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &node->mesh->uniformBuffer.descriptorSet)); VkWriteDescriptorSet writeDescriptorSet{}; writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; writeDescriptorSet.descriptorCount = 1; writeDescriptorSet.dstSet = node->mesh->uniformBuffer.descriptorSet; writeDescriptorSet.dstBinding = 0; writeDescriptorSet.pBufferInfo = &node->mesh->uniformBuffer.descriptor; vkUpdateDescriptorSets(device, 1, &writeDescriptorSet, 0, nullptr); } for (auto& child : node->children) { setupglTFNodeDescriptorSet(child); } } void VulkanBackend::VulkanFoundation::createSkyboxDescriptorSets() { // Skybox (fixed set) for (auto i = 0; i < uniformBuffers.size(); i++) { VkDescriptorSetAllocateInfo descriptorSetAllocInfo{}; descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; descriptorSetAllocInfo.descriptorPool = descriptorPool; descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayouts.scene; descriptorSetAllocInfo.descriptorSetCount = 1; VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &descriptorSets[i].skybox)); std::array<VkWriteDescriptorSet, 3> writeDescriptorSets{}; writeDescriptorSets[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeDescriptorSets[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; writeDescriptorSets[0].descriptorCount = 1; writeDescriptorSets[0].dstSet = descriptorSets[i].skybox; writeDescriptorSets[0].dstBinding = 0; writeDescriptorSets[0].pBufferInfo = &uniformBuffers[i].skybox.descriptor; writeDescriptorSets[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeDescriptorSets[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; writeDescriptorSets[1].descriptorCount = 1; writeDescriptorSets[1].dstSet = descriptorSets[i].skybox; writeDescriptorSets[1].dstBinding = 1; writeDescriptorSets[1].pBufferInfo = &uniformBuffers[i].params.descriptor; writeDescriptorSets[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeDescriptorSets[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writeDescriptorSets[2].descriptorCount = 1; writeDescriptorSets[2].dstSet = descriptorSets[i].skybox; writeDescriptorSets[2].dstBinding = 2; writeDescriptorSets[2].pImageInfo = &PBR::Material::textures.prefilteredCube.descriptor; vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr); } } void VulkanBackend::VulkanFoundation::allocateCommandBuffers(PlumageRender::Setter setter) { // resize if (setter.settings.headless) { auto frameRange = setter.getFrameRange(); commandbuffers.resize(frameRange); } else { commandbuffers.resize(swapChainImages.size()); } // allocate VkCommandBufferAllocateInfo cmdBufAllocateInfo{}; cmdBufAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; cmdBufAllocateInfo.commandPool = commandPool; cmdBufAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; cmdBufAllocateInfo.commandBufferCount = static_cast<uint32_t>(commandbuffers.size()); VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, commandbuffers.data())); } void VulkanBackend::VulkanFoundation::cleanupSwapChain() { for (auto framebuffer : framebuffers) { vkDestroyFramebuffer(device, framebuffer, nullptr); } for (auto imageView : swapChainImageViews) { vkDestroyImageView(device, imageView, nullptr); } vkDestroySwapchainKHR(device, swapChain, nullptr); } void VulkanBackend::VulkanFoundation::createCommandBuffer(PlumageRender::Setter setter,PlumageRender::renderMain mainRender,UI* plumageGUI) { VkCommandBufferBeginInfo cmdBufferBeginInfo{}; cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; VkClearValue clearValues[3]; if (setter.settings.multiSampling) { clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 1.0f } }; clearValues[1].color = { { 0.0f, 0.0f, 0.0f, 1.0f } }; clearValues[2].depthStencil = { 1.0f, 0 }; } else { clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 1.0f } }; clearValues[1].depthStencil = { 1.0f, 0 }; } VkRenderPassBeginInfo renderPassBeginInfo{}; renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; renderPassBeginInfo.renderPass = renderPass; renderPassBeginInfo.renderArea.offset.x = 0; renderPassBeginInfo.renderArea.offset.y = 0; renderPassBeginInfo.renderArea.extent.width =setter.settings.width; renderPassBeginInfo.renderArea.extent.height =setter.settings.height; renderPassBeginInfo.clearValueCount =setter.settings.multiSampling ? 3 : 2; renderPassBeginInfo.pClearValues = clearValues; for (uint32_t i = 0; i < commandbuffers.size(); ++i) { renderPassBeginInfo.framebuffer = framebuffers[i]; VkCommandBuffer currentCB = commandbuffers[i]; VK_CHECK_RESULT(vkBeginCommandBuffer(currentCB, &cmdBufferBeginInfo)); vkCmdBeginRenderPass(currentCB, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); VkViewport viewport{}; viewport.width = (float)setter.settings.width; viewport.height = (float)setter.settings.height; viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; vkCmdSetViewport(currentCB, 0, 1, &viewport); VkRect2D scissor{}; scissor.extent = {setter.settings.width,setter.settings.height }; vkCmdSetScissor(currentCB, 0, 1, &scissor); VkDeviceSize offsets[1] = { 0 }; if (setter.settings.displayBackground) { vkCmdBindDescriptorSets(currentCB, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets[i].skybox, 0, nullptr); vkCmdBindPipeline(currentCB, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.skybox); mainRender.models.skybox.draw(currentCB); } glTFModel::Model& model = mainRender.models.scene; vkCmdBindVertexBuffers(currentCB, 0, 1, &model.vertices.buffer, offsets); if (model.indices.buffer != VK_NULL_HANDLE) { vkCmdBindIndexBuffer(currentCB, model.indices.buffer, 0, VK_INDEX_TYPE_UINT32); } boundPipeline = VK_NULL_HANDLE; // Opaque primitives first for (auto node : model.nodes) { createglTFNodeCommandBuffer(node, i, glTFModel::Material::ALPHAMODE_OPAQUE); } // Alpha masked primitives for (auto node : model.nodes) { createglTFNodeCommandBuffer(node, i, glTFModel::Material::ALPHAMODE_MASK); } // Transparent primitives // TODO: Correct depth sorting for (auto node : model.nodes) { createglTFNodeCommandBuffer(node, i, glTFModel::Material::ALPHAMODE_BLEND); } // User interface if (!setter.settings.headless) { plumageGUI->draw(currentCB); } vkCmdEndRenderPass(currentCB); VK_CHECK_RESULT(vkEndCommandBuffer(currentCB)); } } void VulkanBackend::VulkanFoundation::createglTFNodeCommandBuffer(glTFModel::Node* node, uint32_t cbIndex, glTFModel::Material::AlphaMode alphaMode) { if (node->mesh) { // Render mesh primitives for (glTFModel::Primitive* primitive : node->mesh->primitives) { if (primitive->material.alphaMode == alphaMode) { VkPipeline pipeline = VK_NULL_HANDLE; switch (alphaMode) { case glTFModel::Material::ALPHAMODE_OPAQUE: case glTFModel::Material::ALPHAMODE_MASK: pipeline = primitive->material.doubleSided ? pipelines.pbrDoubleSided : pipelines.pbr; break; case glTFModel::Material::ALPHAMODE_BLEND: pipeline = pipelines.pbrAlphaBlend; break; } if (pipeline != boundPipeline) { vkCmdBindPipeline(commandbuffers[cbIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); boundPipeline = pipeline; } const std::vector<VkDescriptorSet> descriptorsets = { descriptorSets[cbIndex].scene, primitive->material.descriptorSet, node->mesh->uniformBuffer.descriptorSet, }; vkCmdBindDescriptorSets(commandbuffers[cbIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, static_cast<uint32_t>(descriptorsets.size()), descriptorsets.data(), 0, NULL); // Pass material parameters as push constants PBR::Material::PushConstBlockMaterial pushConstBlockMaterial{}; pushConstBlockMaterial.emissiveFactor = primitive->material.emissiveFactor; // To save push constant space, availabilty and texture coordiante set are combined // -1 = texture not used for this material, >= 0 texture used and index of texture coordinate set pushConstBlockMaterial.colorTextureSet = primitive->material.baseColorTexture != nullptr ? primitive->material.texCoordSets.baseColor : -1; pushConstBlockMaterial.normalTextureSet = primitive->material.normalTexture != nullptr ? primitive->material.texCoordSets.normal : -1; pushConstBlockMaterial.occlusionTextureSet = primitive->material.occlusionTexture != nullptr ? primitive->material.texCoordSets.occlusion : -1; pushConstBlockMaterial.emissiveTextureSet = primitive->material.emissiveTexture != nullptr ? primitive->material.texCoordSets.emissive : -1; pushConstBlockMaterial.alphaMask = static_cast<float>(primitive->material.alphaMode == glTFModel::Material::ALPHAMODE_MASK); pushConstBlockMaterial.alphaMaskCutoff = primitive->material.alphaCutoff; // TODO: glTF specs states that metallic roughness should be preferred, even if specular glosiness is present if (primitive->material.pbrWorkflows.metallicRoughness) { // Metallic roughness workflow pushConstBlockMaterial.workflow = static_cast<float>(PBR::Material::PBRWorkflows::PBR_WORKFLOW_METALLIC_ROUGHNESS); pushConstBlockMaterial.baseColorFactor = primitive->material.baseColorFactor; pushConstBlockMaterial.metallicFactor = primitive->material.metallicFactor; pushConstBlockMaterial.roughnessFactor = primitive->material.roughnessFactor; pushConstBlockMaterial.PhysicalDescriptorTextureSet = primitive->material.metallicRoughnessTexture != nullptr ? primitive->material.texCoordSets.metallicRoughness : -1; pushConstBlockMaterial.colorTextureSet = primitive->material.baseColorTexture != nullptr ? primitive->material.texCoordSets.baseColor : -1; } if (primitive->material.pbrWorkflows.specularGlossiness) { // Specular glossiness workflow pushConstBlockMaterial.workflow = static_cast<float>(PBR::Material::PBRWorkflows::PBR_WORKFLOW_SPECULAR_GLOSINESS); pushConstBlockMaterial.PhysicalDescriptorTextureSet = primitive->material.extension.specularGlossinessTexture != nullptr ? primitive->material.texCoordSets.specularGlossiness : -1; pushConstBlockMaterial.colorTextureSet = primitive->material.extension.diffuseTexture != nullptr ? primitive->material.texCoordSets.baseColor : -1; pushConstBlockMaterial.diffuseFactor = primitive->material.extension.diffuseFactor; pushConstBlockMaterial.specularFactor = glm::vec4(primitive->material.extension.specularFactor, 1.0f); } vkCmdPushConstants(commandbuffers[cbIndex], pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PBR::Material::PushConstBlockMaterial), &pushConstBlockMaterial); if (primitive->hasIndices) { vkCmdDrawIndexed(commandbuffers[cbIndex], primitive->indexCount, 1, primitive->firstIndex, 0, 0); } else { vkCmdDraw(commandbuffers[cbIndex], primitive->vertexCount, 1, 0, 0); } } } }; for (auto child : node->children) { createglTFNodeCommandBuffer(child, cbIndex, alphaMode); } } void VulkanBackend::VulkanFoundation::createFenceAndSemaphore(PlumageRender::Setter setter) { waitFences.resize(setter.settings.MaxFrameInFlight); presentCompleteSemaphores.resize(setter.settings.MaxFrameInFlight); renderCompleteSemaphores.resize(setter.settings.MaxFrameInFlight); // Command buffer execution fences for (auto& waitFence : waitFences) { VkFenceCreateInfo fenceCI{ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, nullptr, VK_FENCE_CREATE_SIGNALED_BIT }; VK_CHECK_RESULT(vkCreateFence(device, &fenceCI, nullptr, &waitFence)); } if (!setter.settings.headless) { for (auto& semaphore : presentCompleteSemaphores) { VkSemaphoreCreateInfo semaphoreCI{ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, nullptr, 0 }; VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCI, nullptr, &semaphore)); } } // Queue ordering semaphores for (auto& semaphore : renderCompleteSemaphores) { VkSemaphoreCreateInfo semaphoreCI{ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, nullptr, 0 }; VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCI, nullptr, &semaphore)); } } void VulkanBackend::VulkanFoundation::DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator) { auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); if (func != nullptr) { func(instance, debugMessenger, pAllocator); } } bool VulkanBackend::VulkanFoundation::acqureNextSwapchainImage(bool framebuffeerResized,uint32_t imageIndex,uint32_t frameIndex,PlumageRender::Setter setter) { VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, renderCompleteSemaphores[frameIndex], VK_NULL_HANDLE, &imageIndex); if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebuffeerResized) { framebuffeerResized = false; recreateSwapChain(setter); return framebuffeerResized; } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { throw std::runtime_error("failed to acquire swap chain image in drawFrame"); } } void VulkanBackend::VulkanFoundation::submitToGraphicQueue(uint32_t frameIndex, uint32_t currentBuffer) { const VkPipelineStageFlags waitDstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkSubmitInfo submitInfo{}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.pWaitDstStageMask = &waitDstStageMask; submitInfo.pWaitSemaphores = &presentCompleteSemaphores[frameIndex]; submitInfo.waitSemaphoreCount = 1; submitInfo.pSignalSemaphores = &renderCompleteSemaphores[frameIndex]; submitInfo.signalSemaphoreCount = 1; submitInfo.pCommandBuffers = &commandbuffers[currentBuffer]; submitInfo.commandBufferCount = 1; VK_CHECK_RESULT(vkQueueSubmit(graphicQueue, 1, &submitInfo, waitFences[frameIndex])); } void VulkanBackend::VulkanFoundation::imageToQueuePresent(uint32_t frameIndex,uint32_t imageIndex,bool framebufferResized,PlumageRender::Setter setter) { //显示队列 VkSemaphore signalSemaphores[] = { renderCompleteSemaphores[frameIndex] }; VkPresentInfoKHR presentInfo{}; presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; presentInfo.waitSemaphoreCount = 1; presentInfo.pWaitSemaphores = signalSemaphores; VkSwapchainKHR swapChains[] = { swapChain }; presentInfo.swapchainCount = 1; presentInfo.pSwapchains = swapChains; presentInfo.pImageIndices = &imageIndex; presentInfo.pResults = nullptr; VkResult result = vkQueuePresentKHR(presentQueue, &presentInfo); if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { framebufferResized = false; recreateSwapChain(setter); } else if (result != VK_SUCCESS) { throw std::runtime_error("failed to present swap chain image in drawFrame"); } } void VulkanBackend::VulkanFoundation::destroyVulkanBackend(PlumageRender::Setter setter) { // Clean up Vulkan resources cleanupSwapChain(); vkDestroyDescriptorPool(device, descriptorPool, nullptr); vkDestroyRenderPass(device, renderPass, nullptr); for (uint32_t i = 0; i < framebuffers.size(); i++) { vkDestroyFramebuffer(device, framebuffers[i], nullptr); } vkDestroyImageView(device, colorAttachment.view, nullptr); vkDestroyImage(device, colorAttachment.image, nullptr); vkFreeMemory(device, colorAttachment.memory, nullptr); vkDestroyImageView(device, depthAttachment.view, nullptr); vkDestroyImage(device, depthAttachment.image, nullptr); vkFreeMemory(device, depthAttachment.memory, nullptr); if (setter.settings.multiSampling) { vkDestroyImage(device, multisampleTarget.colorAttachment.image, nullptr); vkDestroyImageView(device, multisampleTarget.colorAttachment.view, nullptr); vkFreeMemory(device, multisampleTarget.colorAttachment.memory, nullptr); vkDestroyImage(device, multisampleTarget.depthAttachment.image, nullptr); vkDestroyImageView(device, multisampleTarget.depthAttachment.view, nullptr); vkFreeMemory(device, multisampleTarget.depthAttachment.memory, nullptr); } // Clean up used Vulkan resources // Note : Inherited destructor cleans up resources stored in base class vkDestroyPipeline(device, pipelines.skybox, nullptr); vkDestroyPipeline(device, pipelines.pbr, nullptr); vkDestroyPipeline(device, pipelines.pbrAlphaBlend, nullptr); vkDestroyPipelineCache(device, pipelineCache, nullptr); vkDestroyCommandPool(device, commandPool, nullptr); vkDestroyPipelineLayout(device, pipelineLayout, nullptr); vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.scene, nullptr); vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.material, nullptr); vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.node, nullptr); for (auto buffer : uniformBuffers) { buffer.params.destroy(); buffer.scene.destroy(); buffer.skybox.destroy(); } for (auto fence : waitFences) { vkDestroyFence(device, fence, nullptr); } for (auto semaphore : renderCompleteSemaphores) { vkDestroySemaphore(device, semaphore, nullptr); } for (auto semaphore : presentCompleteSemaphores) { vkDestroySemaphore(device, semaphore, nullptr); } delete vulkanDevice; if (setter.settings.validation) { DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr); } vkDestroyInstance(instance, nullptr); } void VulkanBackend::VulkanFoundation::updateShaderData() { PBR::Material::shaderData.lightDir = glm::vec4( sin(glm::radians(lightSource.rotation.x)) * cos(glm::radians(lightSource.rotation.y)), sin(glm::radians(lightSource.rotation.y)), cos(glm::radians(lightSource.rotation.x)) * cos(glm::radians(lightSource.rotation.y)), 0.0f); } void VulkanBackend::VulkanFoundation::recreateSwapChain(PlumageRender::Setter setter) { int width = 0, height = 0; glfwGetFramebufferSize(window, &width, &height); while (width == 0 || height == 0) { if (glfwWindowShouldClose(window)) { return; } glfwGetFramebufferSize(window, &width, &height); glfwWaitEvents(); } vkDeviceWaitIdle(device); cleanupSwapChain(); createSwapChain(setter); createImageView(setter); createFramebuffer(setter); }