vulkanTutorial/VulkanTutorial.cpp

707 lines
20 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// VulkanTutorial.cpp: 定义应用程序的入口点。
//
#include "VulkanTutorial.h"
const int Width = 800;
const int Height = 600;
const std::vector<const char*> validationLayers = {
"VK_LAYER_KHRONOS_validation"
};
const std::vector<const char*> swapchainExtensions = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME
};
#ifdef NDEBUG
const bool enableValidationLayers = false;
#else
const bool enableValidationLayers = true;
#endif // NDEBUG
//window init
GLFWwindow* HelloTriangleApplication::initWindow(int Width, int Height) {
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
window = glfwCreateWindow(Width, Height, "vulkan", nullptr, nullptr);
return window;
}
// vulkan instace create
void HelloTriangleApplication::createInstance() {
// check validation layers
if (enableValidationLayers && ! 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();
createInfo.enabledExtensionCount = static_cast<uint32_t>(requiredExtensions.size());
createInfo.ppEnabledExtensionNames = requiredExtensions.data();
/* enable glfw extension in creatInfo
uint32_t glfwExtentionCount = 0;
const char** glfwExtensions;
glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtentionCount);
createInfo.enabledExtensionCount = glfwExtentionCount;
createInfo.ppEnabledExtensionNames = glfwExtensions;
*/
// enable validation layer if available in createInfo
VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{};
if (enableValidationLayers)
{
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");
}
}
void HelloTriangleApplication::initVulkan() {
createInstance();
showAvailableExtension();
setupDebugMessenger();
createSurface();
pickPhysicalDevice();
createLogicalDevice();
createSwapChain();
creatImageView();
createGraphicPipeline();
}
void HelloTriangleApplication::mainLoop(GLFWwindow* window){
while (!glfwWindowShouldClose(window))
{
glfwPollEvents();
}
}
void HelloTriangleApplication::cleanup(GLFWwindow* window) {
for (auto imageView : swapChainImageViews)
{
vkDestroyImageView(device, imageView, nullptr);
}
vkDestroySwapchainKHR(device, swapChain, nullptr);
vkDestroySurfaceKHR(instance, surface, nullptr);
if (enableValidationLayers)
{
DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
}
vkDestroyDevice(device, nullptr);
vkDestroyInstance(instance, nullptr);
glfwDestroyWindow(window);
glfwTerminate();
}
std::vector<const char*> HelloTriangleApplication::getRequiredExtensions() {
uint32_t glfwExtensionCount = 0;
const char** glfwExtensions;
glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
if (enableValidationLayers)
{
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
}
return extensions;
}
void HelloTriangleApplication::showAvailableExtension()
{
// show available extensions
uint32_t availableExtensionCount = 0;
vkEnumerateInstanceExtensionProperties(nullptr, &availableExtensionCount, nullptr);
std::vector<VkExtensionProperties> availableExtensions(availableExtensionCount);
vkEnumerateInstanceExtensionProperties(nullptr, &availableExtensionCount, availableExtensions.data());
std::cout << "available extensions :" << std::endl;
for (const auto& extension : availableExtensions)
{
std::cout << "\t" << extension.extensionName << " ver:" << extension.specVersion << std::endl;
}
}
VKAPI_ATTR VkBool32 VKAPI_CALL HelloTriangleApplication::debugCallback(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageTypes,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData)
{
std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl;
return VK_FALSE;
}
void HelloTriangleApplication::setupDebugMessenger()
{
if (! enableValidationLayers)
{
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 HelloTriangleApplication::populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo)
{
createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
createInfo.pfnUserCallback = &debugCallback;
createInfo.pUserData = nullptr;
}
void HelloTriangleApplication::createSurface()
{
if (glfwCreateWindowSurface(instance,window,nullptr,&surface) != VK_SUCCESS)
{
throw std::runtime_error("failed to create window surface in createSurface()");
}
}
bool HelloTriangleApplication::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();
}
void HelloTriangleApplication::pickPhysicalDevice()
{
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());
for (const auto& device : devices)
{
if (isDeviceSuitable(device))
{
physicalDevice = device;
break;
}
}
if (physicalDevice == VK_NULL_HANDLE)
{
throw std::runtime_error("failed to find a suitable GPU");
}
}
bool HelloTriangleApplication::isDeviceSuitable(VkPhysicalDevice device)
{
QueueFamilyIndices indices = findQueueFamilies(device);
bool extensionsSupported = checkDeviceExtensionSupport(device);
bool swapChainAdequate = false;
if (extensionsSupported)
{
SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device);
swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
}
return indices.isComplete() && extensionsSupported && swapChainAdequate;
}
HelloTriangleApplication::QueueFamilyIndices HelloTriangleApplication::findQueueFamilies(VkPhysicalDevice device)
{
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;
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
for (const auto& queueFamily : queueFamilies)
{
if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
indices.graphicsFamily = i;
}
if (indices.isComplete())
{
break;
}
if (presentSupport)
{
indices.presentFamily = i;
}
i++;
}
return indices;
}
void HelloTriangleApplication::createLogicalDevice()
{
QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
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);
}
/*
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;
*/
VkPhysicalDeviceFeatures deviceFeatures{};
VkDeviceCreateInfo deviceCreateInfo{};
deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
deviceCreateInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
deviceCreateInfo.pQueueCreateInfos = queueCreateInfos.data();
deviceCreateInfo.pEnabledFeatures = &deviceFeatures;
deviceCreateInfo.enabledExtensionCount = static_cast<uint32_t>(swapchainExtensions.size());
deviceCreateInfo.ppEnabledExtensionNames = swapchainExtensions.data();
//新版本vulkan已不对实例和设备特定验证层做区分此处保证兼容性
if (enableValidationLayers)
{
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);
vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
}
HelloTriangleApplication::SwapChainSupportDetails HelloTriangleApplication::querySwapChainSupport(VkPhysicalDevice device)
{
SwapChainSupportDetails details;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities);
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;
}
VkSurfaceFormatKHR HelloTriangleApplication::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 HelloTriangleApplication::chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes)
{
for (const auto& availablePresentMode : availablePresentModes) {
if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR)
{
return availablePresentMode;
}
}
return VK_PRESENT_MODE_FIFO_KHR;
}
VkExtent2D HelloTriangleApplication::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 HelloTriangleApplication::createSwapChain()
{
SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice);
VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats);
VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes);
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);
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;
}
void HelloTriangleApplication::creatImageView()
{
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");
}
}
}
std::vector<char> HelloTriangleApplication::readFile(const std::string& filename)
{
std::ifstream file(filename, std::ios::ate | std::ios::binary);
if (!file.is_open())
{
throw std::runtime_error("failed to open file!");
}
rsize_t fileSize = (rsize_t)file.tellg();
std::vector<char> buffer(fileSize);
file.seekg(0);
file.read(buffer.data(), fileSize);
file.close();
return buffer;
}
VkShaderModule HelloTriangleApplication::createShaderModule(const std::vector<char> code)
{
VkShaderModuleCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
createInfo.codeSize = code.size();
createInfo.pCode = reinterpret_cast<const uint32_t*>(code.data());
VkShaderModule shaderModeul;
if (vkCreateShaderModule(device,&createInfo,nullptr,&shaderModeul) != VK_SUCCESS)
{
throw std::runtime_error("failed to create shader module!");
}
return shaderModeul;
}
void HelloTriangleApplication::createGraphicPipeline()
{
auto vertShaderCode = readFile("../../../shaders/triangleVert.spv");
auto fragShaderCode = readFile("../../..//shaders/triangleFrag.spv");
std::cout << "vertShader size:" << vertShaderCode.size() << "\nfragShader size:" << fragShaderCode.size() << std::endl;
VkShaderModule vertShaderModule = createShaderModule(vertShaderCode);
VkShaderModule fragShaderModule = createShaderModule(fragShaderCode);
//指定管线vertex阶段
VkPipelineShaderStageCreateInfo vertShaderStageInfo{};
vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
//指定模块和入口点
vertShaderStageInfo.module = vertShaderModule;
vertShaderStageInfo.pName = "main";
//指定管线fragment阶段
VkPipelineShaderStageCreateInfo fragShaderStageInfo{};
fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
fragShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
fragShaderStageInfo.module = fragShaderModule;
fragShaderStageInfo.pName = "main";
VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo,fragShaderStageInfo };
//管道创建完成后销毁着色器模块
vkDestroyShaderModule(device, fragShaderModule, nullptr);
vkDestroyShaderModule(device, vertShaderModule, nullptr);
}
bool HelloTriangleApplication::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;
}
VkResult 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 DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator)
{
auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
if (func!=nullptr)
{
func(instance, debugMessenger, pAllocator);
}
}
int main()
{
HelloTriangleApplication app;
try
{
app.run(Width,Height);
}
catch (const std::exception& e)
{
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}