2085 lines
79 KiB
C++
2085 lines
79 KiB
C++
#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);
|
||
}
|
||
|
||
|
||
|
||
|