目录
Swap Chain Recreation
Recreating the swap chain
当窗口的大小发生变化时,会导致swap chain与重置窗口不兼容,因此需要捕捉这些事件并重新创建swap chain。
void recreateSwapChain() {
//[等待资源完成使用。
vkDeviceWaitIdle(device);
cleanupSwapChain();
createSwapChain();
createImageViews();
createRenderPass();
createGraphicsPipeline();
createFramebuffers();
createCommandBuffers();
}
void cleanupSwapChain() {
for (size_t i = 0; i < swapChainFramebuffers.size(); i++) {
vkDestroyFramebuffer(device, swapChainFramebuffers[i], nullptr);
}
//可以重新创建Command池,但相当浪费的。相反,使用vkfreecandbuffers函数清理现有CommandBuffer。就可以重用现有的池来分配新的CommandBuffer。
vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
vkDestroyPipeline(device, graphicsPipeline, nullptr);
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
vkDestroyRenderPass(device, renderPass, nullptr);
for (size_t i = 0; i < swapChainImageViews.size(); i++) {
vkDestroyImageView(device, swapChainImageViews[i], nullptr);
}
vkDestroySwapchainKHR(device, swapChain, nullptr);
}
该方法的缺点是,我们需要在创建新的swap chain之前停止所有渲染任务
Suboptimal or out-of-date swap chain
现在需要弄清楚什么时候需要重新创建Swap chain并调用 recreateSwapChain 函数。Vulkan 通常只会在显示期间告诉我们Swap chain不再适用。 vkAcquireNextImageKHR 和 vkQueuePresentKHR 函数可以返回以下特殊值来表明这一点
- VK_ERROR_OUT_OF_DATE_KHR:Swap chain与surface不兼容,无法再用于渲染。 通常发生在窗口调整大小之后
- VK_SUBOPTIMAL_KHR:Swap chain仍可用于成功呈现到surface,但surface属性不再完全匹配
VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
if (result == VK_ERROR_OUT_OF_DATE_KHR) {
//如果SwapChain在尝试获取图像时已过时,则无法再显示给它。因此,我们应该立即重新创建SwapChain,并在下一个drawFrame调用中重试。
recreateSwapChain();
return;
}
//如果SwapChain是次优的,可以调用recreateSwapChain,但还是选择继续,因为我们已经获得了一个图像。VK_SUCCESS和VK_SUBOPTIMAL_KHR都被认为是“成功” 。
else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
throw std::runtime_error("failed to acquire swap chain image!");
}
//vkQueuePresentKHR 函数返回具有相同vkAcquireNextImageKHR返回值含义相同的值。
//在这种情况下,如果SwapChain不是最优的,我们也会重新创建它,因为我们想要最好的结果。
result = vkQueuePresentKHR(presentQueue, &presentInfo);
//添加 framebufferResized 确保semaphores处于一致状态,否则可能永远不会正确等待发出信号的semaphores。
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR ) {
recreateSwapChain();
}
else if (result != VK_SUCCESS) {
throw std::runtime_error("failed to present swap chain image!");
}
Handling resizes explicitly
许多驱动程序和平台在调整窗口大小后会自动触发VK_ERROR_OUT_OF_DATE_KHR,但并不保证一定会触发。要添加一些额外的代码来显式处理大小调整。
bool framebufferResized = false;
----------------------------------
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) {
framebufferResized = false;
recreateSwapChain();
}
else if (result != VK_SUCCESS) {
throw std::runtime_error("failed to present swap chain image!");
}
检测窗体实际大小,我们可以使用 GLFW 框架中的 glfwSetFramebufferSizeCallback 函数来设置回调
//[19]
static void framebufferResizeCallback(GLFWwindow* window, int width, int height) {
//[19-2]当前对象指针赋值
auto app = reinterpret_cast<Application*>(glfwGetWindowUserPointer(window));
app->framebufferResized = true;
}
----------------------------------------------
//储存当前对象指针
glfwSetWindowUserPointer(window, this);
//检测窗体实际大小,我们可以使用 GLFW 框架中的 glfwSetFramebufferSizeCallback 函数来设置回调:
glfwSetFramebufferSizeCallback(window, framebufferResizeCallback);
Handling minimization
//处理最小化
int width = 0, height = 0;
glfwGetFramebufferSize(window, &width, &height);
while (width == 0 || height == 0) {
glfwGetFramebufferSize(window, &width, &height);
glfwWaitEvents();
}
Code
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <iostream>
#include <stdexcept>
#include <cstdlib>
#include <vector>
#include <map>
#include <optional>
#include <set>
#include <fstream>
//[2]验证层Debug时开启
#ifdef NDEBUG
const bool enableValidationLayers = false;
#else
const bool enableValidationLayers = true;
#endif
//[2]所有有用的标准验证都捆绑到SDK的一个层中,称为VK_LAYER_KHRONOS_validation层。
const std::vector<const char*> validationLayers = {
"VK_LAYER_KHRONOS_validation"
};
//[5]
struct QueueFamilyIndices {
std::optional<uint32_t> graphicsFamily;
std::optional<uint32_t> presentFamily;
//[8]为了方便起见,我们还将向结构本身添加一个泛型检查
bool isComplete() {
return graphicsFamily.has_value() && presentFamily.has_value();
}
};
//[5]
struct SwapChainSupportDetails {
VkSurfaceCapabilitiesKHR capabilities;
std::vector<VkSurfaceFormatKHR> formats;
std::vector<VkPresentModeKHR> presentModes;
};
//[5]
const std::vector<const char*> deviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
//[7]
const int WIDTH = 800;
//[7]
const int HEIGHT = 600;
//[10]ate: Start reading at the end of the file
//[10]binary: Read the file as binary file (avoid text transformations)
static std::vector<char> 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!");
}
//[10]ate 的优势是,可以获取文件的大小
size_t fileSize = (size_t)file.tellg();
std::vector<char> buffer(fileSize);
//[10]指针跳到头
file.seekg(0);
file.read(buffer.data(), fileSize);
file.close();
return buffer;
}
//[14]
const int MAX_FRAMES_IN_FLIGHT = 2;
class Application {
public:
void run() {
//[1]
initWindow();
initVulkan();
mainLoop();
cleanup();
}
public:
//[1]
GLFWwindow* window;
//[2]
VkInstance instance;
//[3]
VkSurfaceKHR surface;
//[4]
VkDebugUtilsMessengerEXT debugMessenger;
//[5]当vkinInstance被销毁时,该对象将被隐式销毁,因此不需要在cleanup函数中执行销毁。
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
//[6]
VkDevice device;
//[6]
VkQueue graphicsQueue;
//[6]
VkQueue presentQueue;
//[7]
VkSwapchainKHR swapChain;
//[7]
std::vector<VkImage> swapChainImages;
//[7]
VkFormat swapChainImageFormat;
//[7]
VkExtent2D swapChainExtent;
//[8]
std::vector<VkImageView> swapChainImageViews;
//[9]
VkRenderPass renderPass;
//[10]
VkPipelineLayout pipelineLayout;
//[10]
VkPipeline graphicsPipeline;
//[11]
std::vector<VkFramebuffer> swapChainFramebuffers;
//[12]
VkCommandPool commandPool;
//[13] Command buffers will be automatically freed when their command pool is destroyed
std::vector<VkCommandBuffer> commandBuffers;
//[14]
std::vector<VkSemaphore> imageAvailableSemaphores;
//[14]
std::vector<VkSemaphore> renderFinishedSemaphores;
//[14]
std::vector<VkFence> inFlightFences;
//[14]-[15]是否需要释放?
std::vector<VkFence> imagesInFlight;
//[15]
size_t currentFrame = 0;
//[19]
bool framebufferResized = false;
//[1]
void initWindow() {
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
//glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);
//[19-1]储存当前对象指针
glfwSetWindowUserPointer(window, this);
//[19] 检测窗体实际大小,我们可以使用 GLFW 框架中的 glfwSetFramebufferSizeCallback 函数来设置回调:
glfwSetFramebufferSizeCallback(window, framebufferResizeCallback);
}
void initVulkan() {
//[2]
createInstance();
//[3]
createSurface();
//[4]
setupDebugMessenger();
//[5]
pickPhysicalDevice();
//[6]
createLogicalDevice();
//[7]
createSwapChain();
//[8]
createImageViews();
//[9]
createRenderPass();
//[10]
createGraphicsPipeline();
//[11]
createFramebuffers();
//[12]
createCommandPool();
//[13]
createCommandBuffers();
//[14]
createSemaphores();
}
void mainLoop() {
//[1]
while (!glfwWindowShouldClose(window))
{
glfwPollEvents();
//[15]
drawFrame();
}
//[16]可以用作执行同步的基本的方法.
vkDeviceWaitIdle(device);
}
void cleanup() {
//[17]
cleanupSwapChain();
//[14]
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr);
vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr);
vkDestroyFence(device, inFlightFences[i], nullptr);
}
//17将这些释放资源的函数移动到cleanupSwapChain中
[12]
//vkDestroyCommandPool(device, commandPool, nullptr);
[11]
//for (auto framebuffer : swapChainFramebuffers) {
// vkDestroyFramebuffer(device, framebuffer, nullptr);
//}
[10]
//vkDestroyPipeline(device, graphicsPipeline, nullptr);
[10]
//vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
[9]
//vkDestroyRenderPass(device, renderPass, nullptr);
[8]
//for (auto imageView : swapChainImageViews) {
// vkDestroyImageView(device, imageView, nullptr);
//}
[7]
//vkDestroySwapchainKHR(device, swapChain, nullptr);
//[6]
vkDestroyDevice(device, nullptr);
//[4]
if (enableValidationLayers) {
DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
}
//[3]
vkDestroySurfaceKHR(instance, surface, nullptr);
//[2]
vkDestroyInstance(instance, nullptr);
//[1]
glfwDestroyWindow(window);
glfwTerminate();
}
//[2]--------------------------------------------------------------------------------------------------------
void createInstance() {
//[2]创建实例时检测是否启用验证层
if (enableValidationLayers && !checkValidationLayerSupport()) {
throw std::runtime_error("validation layers requested, but not available!");
}
//[2]well-known graphics engine
VkApplicationInfo appInfo = {};
//[2]结构体必须指明类型,pNext指向拓展信息
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;
//[2]Vulkan驱动程序使用哪些全局扩展和验证,后续后详细说明
VkInstanceCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.pApplicationInfo = &appInfo;
//[2]指定全局扩展
uint32_t glfwExtensionCount = 0;
const char** glfwExtensions;
glfwExtensions =
glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
createInfo.enabledExtensionCount = glfwExtensionCount;
createInfo.ppEnabledExtensionNames = glfwExtensions;
//[2]the global validation layers to enable
createInfo.enabledLayerCount = 0; //后续有说明
//[2]验证层信息
//[2]如果检查成功,那么vkCreateInstance不会返回VK_ERROR_LAYER_NOT_PRESENT错误
if (enableValidationLayers) {
createInfo.enabledLayerCount =
static_cast<uint32_t>(validationLayers.size());
createInfo.ppEnabledLayerNames = validationLayers.data();
}
else {
createInfo.enabledLayerCount = 0;
}
//[2]GLFW
auto extensions = getRequiredExtensions();
createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
createInfo.ppEnabledExtensionNames = extensions.data();
//[2]重用
//[2]通过该方式创建一个额外的调试信息,它将在vkCreateInstance和vkDestroyInstance期间自动创建和销毁
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;
}
//[2]or
/*if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
throw std::runtime_error("failed to set up debug messenger!");
}*/
//[2] VK_SUCCESS or Error Code
//[2]VkResult result = vkCreateInstance(&createInfo, nullptr, &instance);
//[2]or
//[2]创建实例
if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS)
{
throw std::runtime_error("failed to create instance!");
/*
* //[2]验证层说明,Vulkan每次调用都会进行相应的验证,通过返回值判定函数是否执行成功
VkResult vkCreateInstance(
const VkInstanceCreateInfo * pCreateInfo,
const VkAllocationCallbacks * pAllocator,
VkInstance * instance) {
if (pCreateInfo == nullptr || instance == nullptr) {
log("Null pointer passed to required parameter!");
return VK_ERROR_INITIALIZATION_FAILED;
}
return real_vkCreateInstance(pCreateInfo, pAllocator, instance);
}
*/
}
//[2]the number of extensions
//[2]支持扩展的数量
uint32_t extensionCount = 0;
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
//[2]an array of VkExtensionProperties to store details of the extensions.
//[2]an array to hold the extension details
//[2]支持的扩展详细信息
std::vector<VkExtensionProperties> extensionsProperties(extensionCount);
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensionsProperties.data());
//[2]query the extension details
//[2]Each VkExtensionProperties struct contains the name and version of an extension.
//[2]查询扩展的详细信息
std::cout << "available extensions:" << std::endl;
for (const auto& extension : extensionsProperties) {
std::cout << "\t" << extension.extensionName << std::endl;
}
}
//[2]list all of the available layers
//[2]列出所有验证层的信息
bool checkValidationLayerSupport() {
uint32_t layerCount;
vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
std::vector<VkLayerProperties> availableLayers(layerCount);
vkEnumerateInstanceLayerProperties(&layerCount,
availableLayers.data());
//[2]查询是否存在验证层信息 layerName = VK_LAYER_KHRONOS_validation
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;
}
//[2]we have to set up a debug messenger with a callback using the VK_EXT_debug_utils extension.
//[2]我们必须使用VK_EXT_debug_utils扩展,设置一个带有回调的debug messenger。
std::vector<const char*> getRequiredExtensions() {
//[5]指定GLFW扩展,但是debug messenger 扩展是有条件添加的
uint32_t glfwExtensionCount = 0;
const char** glfwExtensions;
glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
if (enableValidationLayers) {
//[2]在这里使用VK_EXT_DEBUG_UTILS_EXTENSION_NAME宏,它等于字符串“VK_EXT_debug_utils”。
//[2]使用此宏可以避免输入错误
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
}
return extensions;
}
//[2]仔细阅读扩展文档,就会发现有一种方法可以专门为这两个函数调用创建单独的 debug utils messenger.
//[2]它要求您只需在VkInstanceCreateInfo的pNext扩展字段中
//[2]传递一个指向VkDebugUtilsMessengerCreateInfoEXT结构的指针。
//[2]首先将messenger创建信息的填充提取到单独的函数中:
void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT&
createInfo) {
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;
}
//[2]Add a new static member function called debugCallback with
//[2] the PFN_vkDebugUtilsMessengerCallbackEXT prototype.
//[2]使用PFN_vkDebugUtilsMessengerCallbackEXT属性添加一个静态函数
//[2]The VKAPI_ATTR and VKAPI_CALL ensure that the function has the
//[2] right signature for Vulkan to call it.
//[2]使用VKAPI_ATTR和VKAPI_CALL 确保函数具有正确的签名,以便Vulkan调用它
static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
//[2]VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT 诊断信息
//[2]VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT 信息性消息,如资源的创建
//[2]关于行为的消息,其不一定是错误,但很可能是应用程序中的BUG
//[2]VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
//[2]关于无效且可能导致崩溃的行为的消息
//[2]VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT
//[2]可以使用比较操作来检查消息是否与某个严重性级别相等或更差,例如:
//[2]if (messageSeverity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
//[2] // Message is important enough to show
//[2]}
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
//[2]VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT 发生了一些与规范或性能无关的事件
//[2]VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT 发生了违反规范或一些可能显示的错误
//[2]VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT 非最优的方式使用Vulkan
VkDebugUtilsMessageTypeFlagsEXT messageType,
//[2]消息本身的详细信息, 包括其重要成员:
//[2]pMessage 以null结尾的调试消息字符串
//[2]pObjects 与消息相关的Vulkan对象句柄数组
//[2]objectCount 数组中的对象数
//[2]pUserData 包含回调指定的指针,允许将自己设置的数据传递给它。
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData) {
std::cerr << "validation layer: " << pCallbackData->pMessage <<
std::endl;
return VK_FALSE;
}
//[2]--------------------------------------------------------------------------------------------------------
//[3]--------------------------------------------------------------------------------------------------------
void createSurface() {
Windows的创建方法
//VkWin32SurfaceCreateInfoKHR createInfo = {};
//createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
//createInfo.hwnd = glfwGetWin32Window(window);
//createInfo.hinstance = GetModuleHandle(nullptr);
//if (vkCreateWin32SurfaceKHR(instance, &createInfo, nullptr, &surface) != VK_SUCCESS) {
// throw std::runtime_error("failed to create window surface!");
//}
Linux的创建方法与上面类似 vkCreateXcbSurfaceKHR
//[10]使用GLFWWindow surface
if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) {
throw std::runtime_error("failed to create window surface!");
}
}
//[3]--------------------------------------------------------------------------------------------------------
//[4]--------------------------------------------------------------------------------------------------------
void setupDebugMessenger() {
if (!enableValidationLayers) return;
VkDebugUtilsMessengerCreateInfoEXT createInfo = {};
populateDebugMessengerCreateInfo(createInfo);
//[4] messenger 创建信息的填充提取到单独的函数中
if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr,
&debugMessenger) != VK_SUCCESS) {
throw std::runtime_error("failed to set up debug messenger!");
}
//[4]or
// createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
//[4]指定希望调用回调严重性类型
//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;
//[4]滤回调通知的消息类型
//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;
//[4]指定指向回调函数的指针
//createInfo.pfnUserCallback = debugCallback;
//[4]返回的回调函数
//createInfo.pUserData = nullptr;
}
//[4]创建代理函数
VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const
VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const
VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT*
pDebugMessenger) {
auto func = (PFN_vkCreateDebugUtilsMessengerEXT)
//[4]如果无法加载,函数将返回nullptr。
vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
if (func != nullptr) {
return func(instance, pCreateInfo, pAllocator, pDebugMessenger);
}
else {
return VK_ERROR_EXTENSION_NOT_PRESENT;
}
}
//[4]创建代理函数 销毁CreateDebugUtilsMessengerEXT
void DestroyDebugUtilsMessengerEXT(VkInstance instance,
VkDebugUtilsMessengerEXT debugMessenger, const
VkAllocationCallbacks* pAllocator) {
auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)
vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
if (func != nullptr) {
func(instance, debugMessenger, pAllocator);
}
}
//[4]--------------------------------------------------------------------------------------------------------
//[5]--------------------------------------------------------------------------------------------------------
void pickPhysicalDevice() {
//[5]查询GPU数量
uint32_t deviceCount = 0;
vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
if (deviceCount == 0) {
throw std::runtime_error("failed to find GPUs with Vulkan support!");
}
//[5]获取驱动信息
std::vector<VkPhysicalDevice> devices(deviceCount);
vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
//[5]选择适合该程序的GPU
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!");
}
//or
//[5]使用有序Map,通过分数自动对显卡排序
std::multimap<int, VkPhysicalDevice> candidates;
for (const auto& device : devices) {
int score = rateDeviceSuitability(device);
candidates.insert(std::make_pair(score, device));
}
//[5]Check if the best candidate is suitable at all
if (candidates.rbegin()->first > 0) {
physicalDevice = candidates.rbegin()->second;
}
else {
throw std::runtime_error("failed to find a suitable GPU!");
}
}
//[5]GPU是否适合该程序的
bool isDeviceSuitable(VkPhysicalDevice device) {
//[5]查询显卡属性,包括:名称,支持Vulkan的版本号
//VkPhysicalDeviceProperties deviceProperties;
//vkGetPhysicalDeviceProperties(device, &deviceProperties);
//[5]扩展支持
bool extensionsSupported = checkDeviceExtensionSupport(device);
//[5]swap chain support
bool swapChainAdequate = false;
if (extensionsSupported) {
SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device);
swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
}
//[5]查询显卡特性,包括:纹理压缩,64位浮点述。多视口渲染(VR)
//VkPhysicalDeviceFeatures deviceFeatures;
//vkGetPhysicalDeviceFeatures(device, &deviceFeatures);
//[5]是否为专业显卡(a dedicated graphics card )(独显),是否支持几何着色器
//return deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU && deviceFeatures.geometryShader;
//or
QueueFamilyIndices indices = findQueueFamilies(device);
//return indices.graphicsFamily.has_value();
//or
return indices.isComplete() && extensionsSupported && swapChainAdequate;
}
SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) {
SwapChainSupportDetails details;
//[5]basic surface capabilities 基本性能
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities);
//[5]the supported surface formats
uint32_t formatCount;
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr);
if (formatCount != 0) {
details.formats.resize(formatCount);
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data());
}
//[5]the supported presentation modes
uint32_t presentModeCount;
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr);
if (presentModeCount != 0) {
details.presentModes.resize(presentModeCount);
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data());
}
return details;
}
QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) {
//[5]Logic to find graphics queue family
QueueFamilyIndices indices;
//[5]Logic to find queue family indices to populate struct with
//[5]C++ 17引入了optional数据结构来区分存在或不存在的值的情况。
//[5]std::optional<uint32_t> graphicsFamily;
//[5]std::cout << std::boolalpha << graphicsFamily.has_value() <<std::endl; // false
//[5]graphicsFamily = 0;
//[5]std::cout << std::boolalpha << graphicsFamily.has_value() << std::endl; // true
uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
//我们需要找到至少一个支持VK_QUEUE_GRAPHICS_BIT的族。
int i = 0;
for (const auto& queueFamily : queueFamilies) {
//[5]寻找一个队列族,它能够链接window
VkBool32 presentSupport = false;
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
if (presentSupport) {
indices.presentFamily = i;
}
if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
indices.graphicsFamily = i;
if (indices.isComplete())
break;
}
i++;
}
return indices;
}
//[5]
bool 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(deviceExtensions.begin(), deviceExtensions.end());
for (const auto& extension : availableExtensions) {
requiredExtensions.erase(extension.extensionName);
}
return requiredExtensions.empty();
}
int rateDeviceSuitability(VkPhysicalDevice device) {
//[5]查询显卡属性,包括:名称,支持Vulkan的版本号
VkPhysicalDeviceProperties deviceProperties;
vkGetPhysicalDeviceProperties(device, &deviceProperties);
int score = 0;
//[5]离散GPU具有显著的性能优势
if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
score += 1000;
}
//[5]支持纹理的最大值,影响图形质量
score += deviceProperties.limits.maxImageDimension2D;
//[5]查询显卡特性,包括:纹理压缩,64位浮点述。多视口渲染(VR)
VkPhysicalDeviceFeatures deviceFeatures;
vkGetPhysicalDeviceFeatures(device, &deviceFeatures);
//[5]不支持几何着色器
if (!deviceFeatures.geometryShader) {
return 0;
}
return score;
}
//[5]--------------------------------------------------------------------------------------------------------
//[6]--------------------------------------------------------------------------------------------------------
void createLogicalDevice() {
QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
VkDeviceQueueCreateInfo queueCreateInfo = {};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex = indices.graphicsFamily.value();
queueCreateInfo.queueCount = 1;
//[6]Vulkan使用0.0到1.0之间的浮点数为队列分配优先级, 来进行缓冲区执行的调度。即使只有一个队列,这也是必需的:
float queuePriority = 1.0f;
queueCreateInfo.pQueuePriorities = &queuePriority;
//[6]device features
VkPhysicalDeviceFeatures deviceFeatures = {};
VkDeviceCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
createInfo.pQueueCreateInfos = &queueCreateInfo;
createInfo.queueCreateInfoCount = 1;
createInfo.pEnabledFeatures = &deviceFeatures;
//[6]VK_KHR_swapchain 将该设备的渲染图像显示到windows
//[6]之前版本Vulkan实现对实例和设备特定的验证层进行了区分,但现在情况不再如此。
//[6]这意味着VkDeviceCreateInfo的enabledLayerCount和ppEnabledLayerNames字段被最新的实现忽略。不过,还是应该将它们设置为与较旧的实现兼容:
createInfo.enabledExtensionCount = 0;
if (enableValidationLayers) {
createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
createInfo.ppEnabledLayerNames = validationLayers.data();
}
else {
createInfo.enabledLayerCount = 0;
}
//[6]create a queue from both families
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
std::set<uint32_t> uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() };
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);
}
//[6]将队列信息加入驱动info
createInfo.queueCreateInfoCount =
static_cast<uint32_t>(queueCreateInfos.size());
createInfo.pQueueCreateInfos = queueCreateInfos.data();
//[6]开启扩展支持
createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
createInfo.ppEnabledExtensionNames = deviceExtensions.data();
//[6]创建驱动
if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
throw std::runtime_error("failed to create logical device!");
}
//[6]获取驱动队列
//[6]因为我们只从这个族中创建一个队列,所以我们只使用索引0
vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
//[6]显示队列
vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
}
//[6]--------------------------------------------------------------------------------------------------------
//[7]--------------------------------------------------------------------------------------------------------
void createSwapChain() {
SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice);
VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats);
VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes);
VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities);
//[7]然而,简单地坚持这个最小值意味着我们有时可能需要等待驱动程序完成内部操作,
//[7]然后才能获取另一个要渲染的图像。因此,建议请求至少比最小值多一个图像:
//[7]uint32_t imageCount = swapChainSupport.capabilities.minImageCount;
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;
//[7]imageArrayLayers指定每个图像包含的层的数量。除非您正在开发3D应用程序,否则该值始终为1。
createInfo.imageArrayLayers = 1;
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
//[7]两种方法可以处理从多个队列访问的图像:
//[7]VK_SHARING_MODE_CONCURRENT
//[7]VK_SHARING_MODE_EXCLUSIVE
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; // Optional
createInfo.pQueueFamilyIndices = nullptr; // Optional
}
//[7]指定对交换链中的图像应用某种变换
createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
//[7]alpha channel should be used for blending
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
createInfo.presentMode = presentMode;
createInfo.clipped = VK_TRUE;
//[7]窗口重置是取缓存区图像方式
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());
swapChainImageFormat = surfaceFormat.format;
swapChainExtent = extent;
}
//[7]
VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats) {
for (const auto& availableFormat : availableFormats) {
if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB
&& availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
return availableFormat;
}
}
//[12]如果查询失败返回第一个
return availableFormats[0];
}
//[7]VK_PRESENT_MODE_IMMEDIATE_KHR
//[7]VK_PRESENT_MODE_FIFO_KHR
//[7]VK_PRESENT_MODE_FIFO_RELAXED_KHR
//[7]VK_PRESENT_MODE_MAILBOX_KHR
VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes) {
//[7]三级缓存更好,如果有就开启
for (const auto& availablePresentMode : availablePresentModes) {
if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
return availablePresentMode;
}
}
return VK_PRESENT_MODE_FIFO_KHR;
}
//[7]在minImageExtent和maxImageExtent内选择与窗口最匹配的分辨率
VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) {
if (capabilities.currentExtent.width != UINT32_MAX) {
return capabilities.currentExtent;
}
else {
//[18]为了正确地处理窗口大小调整,还需要查询帧缓冲区的当前窗口大小,以确保交swap chain(new)中图像大小正确。
int width, height;
glfwGetFramebufferSize(window, &width, &height);
VkExtent2D actualExtent = { static_cast<uint32_t>(width), static_cast<uint32_t>(height) };
//VkExtent2D actualExtent = { WIDTH, HEIGHT };
actualExtent.width = std::max(capabilities.minImageExtent.width,
std::min(capabilities.maxImageExtent.width, actualExtent.width));
actualExtent.height = std::max(capabilities.minImageExtent.height,
std::min(capabilities.maxImageExtent.height, actualExtent.height));
return actualExtent;
}
}
//[7]--------------------------------------------------------------------------------------------------------
//[8]--------------------------------------------------------------------------------------------------------
void createImageViews() {
swapChainImageViews.resize(swapChainImages.size());
for (size_t i = 0; i < swapChainImages.size(); i++) {
VkImageViewCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
createInfo.image = swapChainImages[i];
//[8]选择视图类型 1D 2D 3D
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
createInfo.format = swapChainImageFormat;
//[8]components字段允许旋转颜色通道。
createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; //default
createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
//[8]描述图像的用途以及应访问图像的哪个部分。
createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
createInfo.subresourceRange.baseMipLevel = 0;
createInfo.subresourceRange.levelCount = 1;
createInfo.subresourceRange.baseArrayLayer = 0;
createInfo.subresourceRange.layerCount = 1;
if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) {
throw std::runtime_error("failed to create image views!");
}
}
}
//[8]--------------------------------------------------------------------------------------------------------
//[9]--------------------------------------------------------------------------------------------------------
void createRenderPass() {
VkAttachmentDescription colorAttachment = {};
//[9]colorAttachment的format应与swapChain图像的格式匹配
colorAttachment.format = swapChainImageFormat;
//[9]没有使用多重采样(multisampling),将使用1个样本。
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
//[9]clear the framebuffer to black before drawing a new frame
//[9]VK_ATTACHMENT_LOAD_OP_LOAD 保留Attachment的现有内容
//[9]VK_ATTACHMENT_LOAD_OP_CLEAR 开始时将值初始化为常数
//[9]VK_ATTACHMENT_LOAD_OP_DONT_CARE 现有内容未定义; 忽略
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
//[9]color and depth data
//[9]VK_ATTACHMENT_STORE_OP_STORE 渲染的内容将存储在内存中,以后可以读取
//[9]VK_ATTACHMENT_STORE_OP_DONT_CARE 渲染操作后,帧缓冲区的内容将是未定义的
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
//[9]stencil data
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
//[9]Textures and framebuffers
//[9]指定在渲染开始之前图像将具有的布局。
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
//[9]指定在渲染完成时自动过渡到的布局。
//[9]VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL used as color attachment
//[9]VK_IMAGE_LAYOUT_PRESENT_SRC_KHR Images the swap chain 中要显示的图像
//[9]VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL 用作存储器复制操作目的图像
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
//[9]引用Attachment
VkAttachmentReference colorAttachmentRef = {};
//[9]attachment参数通过attachment描述数组中的索引指定要引用的attachment
colorAttachmentRef.attachment = 0;
//[9]布局指定了我们希望attachment在使用此引用的subpass中具有哪种布局。
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
//[9]Vulkan may also support compute subpasses in the future
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
//[9]引用colorAttachment
//[9]数组中attachment的索引是直接从fragment shader 引用的(location = 0)out vec4 outColor指令!
//[9]subpass可以引用以下其他类型的attachment
//[9]pInputAttachments read from a shader
//[9]pResolveAttachments used for multisampling attachments
//[9]pDepthStencilAttachment depth and stencil data
//[9]pPreserveAttachments not used by this subpass but for which the data must be preserved(保存)
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorAttachmentRef;
//[9]
VkRenderPassCreateInfo renderPassInfo = {};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = 1;
renderPassInfo.pAttachments = &colorAttachment;
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpass;
if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS)
{
throw std::runtime_error("failed to create render pass!");
}
}
//[9]--------------------------------------------------------------------------------------------------------
//[10]--------------------------------------------------------------------------------------------------------
void createGraphicsPipeline() {
//[10]
auto vertShaderCode = readFile("shaders/vert.spv");
auto fragShaderCode = readFile("shaders/frag.spv");
//[10]
VkShaderModule vertShaderModule = createShaderModule(vertShaderCode);
VkShaderModule fragShaderModule = createShaderModule(fragShaderCode);
//[10]可以将多个着色器组合到一个ShaderModule中,并使用不同的entry points来区分它们的行为。
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";
//[10]指定着色器常量的值。
//[10]单个着色器模块,在管道中创建常量,给予不同的值来配置其行为。
//[10]比在渲染时使用变量配置着色器更为有效, 编译器可以进行优化,例如消除依赖于这些值的if语句
//[10]如果没有这样的常量,则可以将成员设置为nullptr,初始化会自动执行该操作。
//[10]pSpecializationInfo
//[10]
VkPipelineShaderStageCreateInfo fragShaderStageInfo = {};
fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fragShaderStageInfo.module = fragShaderModule;
fragShaderStageInfo.pName = "main";
//[10]
VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo };
//[10]
VkPipelineVertexInputStateCreateInfo vertexInputInfo = {};
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputInfo.vertexBindingDescriptionCount = 0;
//[10]描述上述用于加载顶点数据的详细信息
vertexInputInfo.pVertexBindingDescriptions = nullptr;
vertexInputInfo.vertexAttributeDescriptionCount = 0;
//[10]描述上述用于加载顶点数据的详细信息
vertexInputInfo.pVertexAttributeDescriptions = nullptr;
//[10]
VkPipelineInputAssemblyStateCreateInfo inputAssembly = {};
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
inputAssembly.primitiveRestartEnable = VK_FALSE;
//[10]viewport
VkViewport viewport = {};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = (float)swapChainExtent.width;
viewport.height = (float)swapChainExtent.height;
//[10]minDepth和maxDepth 在0.0f到1.0f之间
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
//[10]裁剪矩形定义哪些区域像素被存储
VkRect2D scissor = {};
scissor.offset = { 0, 0 };
scissor.extent = swapChainExtent;
//[10]viewport 和scissor可以有多个
VkPipelineViewportStateCreateInfo viewportState = {};
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState.viewportCount = 1;
viewportState.pViewports = &viewport;
viewportState.scissorCount = 1;
viewportState.pScissors = &scissor;
VkPipelineRasterizationStateCreateInfo rasterizer = {};
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
//[10]保留近平面和远平面之外的片元
rasterizer.depthClampEnable = VK_FALSE;
//[10]true 几何图形不会通过光栅化阶段。 禁用对帧缓冲区的任何输出
rasterizer.rasterizerDiscardEnable = VK_FALSE;
//[10]VK_POLYGON_MODE_FILL 用片段填充多边形区域
//[10]VK_POLYGON_MODE_LINE 多边形边缘绘制为线
//[10]VK_POLYGON_MODE_POINT 多边形顶点绘制为点
rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
//[10]支持的最大线宽取决于硬件,任何比1.0f粗的线都需要启用wideLines。
rasterizer.lineWidth = 1.0f;
//[10]确定要使用的面部剔除类型。
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
//[10]指定面片视为正面的顶点顺序,可以是顺时针或逆时针
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
//[10]Rasterization可以通过添加常量或基于片段的斜率对深度值进行偏置来更改深度值。
//[10]多用于阴影贴图,如不需要将depthBiasEnable设置为VK_FALSE。
rasterizer.depthBiasEnable = VK_FALSE;
rasterizer.depthBiasConstantFactor = 0.0f;
rasterizer.depthBiasClamp = 0.0f;
rasterizer.depthBiasSlopeFactor = 0.0f;
//[10]
VkPipelineMultisampleStateCreateInfo multisampling = {};
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampling.sampleShadingEnable = VK_FALSE;
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
multisampling.minSampleShading = 1.0f; // Optional
multisampling.pSampleMask = nullptr; // Optional
multisampling.alphaToCoverageEnable = VK_FALSE; // Optional
multisampling.alphaToOneEnable = VK_FALSE; // Optional
//[10]specification详见说明文档
//[10]每个附加的帧缓冲区的混合规则
VkPipelineColorBlendAttachmentState colorBlendAttachment = {};
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT |
VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT |
VK_COLOR_COMPONENT_A_BIT;
//[6]片段着色器的颜色直接输出
colorBlendAttachment.blendEnable = VK_FALSE;
colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
/*
if (blendEnable) {
finalColor.rgb = (srcColorBlendFactor * newColor.rgb)
< colorBlendOp > (dstColorBlendFactor * oldColor.rgb);
finalColor.a = (srcAlphaBlendFactor * newColor.a) < alphaBlendOp >
(dstAlphaBlendFactor * oldColor.a);
}
else {
finalColor = newColor;
}
finalColor = finalColor & colorWriteMask;
*/
//[10]透明混合
/*
finalColor.rgb = newAlpha * newColor + (1 - newAlpha) * oldColor;
finalColor.a = newAlpha.a;
*/
//[10]VK_TRUE
colorBlendAttachment.blendEnable = VK_TRUE;
colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
//[10]全局颜色混合设置。
VkPipelineColorBlendStateCreateInfo colorBlending = {};
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
//[10]bitwise combination 请注意,这将自动禁用第一种方法
colorBlending.logicOpEnable = VK_FALSE;
colorBlending.logicOp = VK_LOGIC_OP_COPY; // Optional
colorBlending.attachmentCount = 1;
colorBlending.pAttachments = &colorBlendAttachment;
colorBlending.blendConstants[0] = 0.0f; // Optional
colorBlending.blendConstants[1] = 0.0f; // Optional
colorBlending.blendConstants[2] = 0.0f; // Optional
colorBlending.blendConstants[3] = 0.0f; // Optional
//[10]
VkDynamicState dynamicStates[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_LINE_WIDTH
};
//[10]
VkPipelineDynamicStateCreateInfo dynamicState = {};
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicState.dynamicStateCount = 2;
dynamicState.pDynamicStates = dynamicStates;
//[10]
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 0; // Optional
pipelineLayoutInfo.pSetLayouts = nullptr; // Optional
pipelineLayoutInfo.pushConstantRangeCount = 0; // Optional
pipelineLayoutInfo.pPushConstantRanges = nullptr; // Optional
//[10]
if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) {
throw std::runtime_error("failed to create pipeline layout!");
}
//[10]
VkGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = 2;
pipelineInfo.pStages = shaderStages;
pipelineInfo.pVertexInputState = &vertexInputInfo;
pipelineInfo.pInputAssemblyState = &inputAssembly;
pipelineInfo.pViewportState = &viewportState;
pipelineInfo.pRasterizationState = &rasterizer;
pipelineInfo.pMultisampleState = &multisampling;
pipelineInfo.pDepthStencilState = nullptr; // Optional
pipelineInfo.pColorBlendState = &colorBlending;
pipelineInfo.pDynamicState = nullptr; // Optional
pipelineInfo.layout = pipelineLayout;
//[10]引用将使用图形管线的renderPass和subpass索引。
//[10]也可以使用其他渲染管线,但是它们必须与renderPass兼容
pipelineInfo.renderPass = renderPass;
pipelineInfo.subpass = 0;
//[10]Vulkan允许通过从现有管线中派生来创建新的图形管线。
//[10]管线派生的思想是,当管线具有与现有管线共有的许多功能时,建立管线的成本较低,
//[10]并且可以更快地完成同一父管线之间的切换。
//[10]可以使用basePipelineHandle指定现有管线的句柄,也可以使用basePipelineIndex引用索引创建的另一个管线。
//[10]现在只有一个管线,因此我们只需指定一个空句柄和一个无效索引。
//[10]仅当在VkGraphicsPipelineCreateInfo的flags字段中还指定了VK_PIPELINE_CREATE_DERIVATIVE_BIT标志时,才使用这些值。
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; // Optional
pipelineInfo.basePipelineIndex = -1; // Optional
//[10]第二个参数VK_NULL_HANDLE引用了一个可选的VkPipelineCache对象。
//[10]管线Cache可用于存储和重用管线创建相关的数据
//[10]可以加快管线的创建速度,后有详解
if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) {
throw std::runtime_error("failed to create graphics pipeline!");
}
//[10]
//在创建图形管线之前,不会将SPIR-V字节码编译并链接到机器代码以供GPU执行。
//这意味着在管道创建完成后,就可以立即销毁ShaderModule
vkDestroyShaderModule(device, fragShaderModule, nullptr);
vkDestroyShaderModule(device, vertShaderModule, nullptr);
}
VkShaderModule 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 shaderModule;
if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) {
throw std::runtime_error("failed to create shader module!");
}
return shaderModule;
}
//[10]--------------------------------------------------------------------------------------------------------
//[11]--------------------------------------------------------------------------------------------------------
void createFramebuffers()
{
//[11]调整容器大小以容纳所有帧缓冲区
swapChainFramebuffers.resize(swapChainImageViews.size());
//[11] create framebuffers
for (size_t i = 0; i < swapChainImageViews.size(); i++) {
VkImageView attachments[] = { swapChainImageViews[i] };
VkFramebufferCreateInfo framebufferInfo = {};
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferInfo.renderPass = renderPass;
framebufferInfo.attachmentCount = 1;
framebufferInfo.pAttachments = attachments;
framebufferInfo.width = swapChainExtent.width;
framebufferInfo.height = swapChainExtent.height;
framebufferInfo.layers = 1;
if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) {
throw std::runtime_error("failed to create framebuffer!");
}
}
}
//[11]--------------------------------------------------------------------------------------------------------
//[12]--------------------------------------------------------------------------------------------------------
void createCommandPool() {
QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice);
VkCommandPoolCreateInfo poolInfo = {};
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
//[12]There are two possible flags for command pools
//[12]VK_COMMAND_POOL_CREATE_TRANSIENT_BIT 提示CommandPool经常用新命令重新记录(可能会改变内存分配行为)
//[12]VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT 允许单独重新记录命CommandPool,否则都必须一起重置
poolInfo.flags = 0; //不使用flag
if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {
throw std::runtime_error("failed to create command pool!");
}
}
//[13]--------------------------------------------------------------------------------------------------------
void createCommandBuffers() {
commandBuffers.resize(swapChainFramebuffers.size());
VkCommandBufferAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.commandPool = commandPool;
//[13]指定primary 或 secondary command buffers.
//[13]VK_COMMAND_BUFFER_LEVEL_PRIMARY 可以提交到队列执行,但不能从其他命令缓冲区调用。
//[13]VK_COMMAND_BUFFER_LEVEL_SECONDARY 不能直接提交,但可以从主命令缓冲区调用
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = (uint32_t)commandBuffers.size();
if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) {
throw std::runtime_error("failed to allocate command buffers!");
}
//[13]recording a command buffer
for (size_t i = 0; i < commandBuffers.size(); i++) {
VkCommandBufferBeginInfo beginInfo = {};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
//[13]指定指定我们将如何使用command buffers.
//[13]VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT 在执行一次后立即rerecord。
//[13]VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT 这是一个辅助command buffers, 将在单个渲染通道中使用
//[13] VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT command buffer 可以在已经挂起执行时重新提交。
beginInfo.flags = 0; // Optional
//[13]仅与secondary command buffers 相关。 它指定从primarycommand buffers 继承哪些状态。
beginInfo.pInheritanceInfo = nullptr; // Optional
if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) {
throw std::runtime_error("failed to begin recording command buffer!");
}
//[13]Starting a render pass
VkRenderPassBeginInfo renderPassInfo = {};
//[13]the render pass itself and the attachments to bind
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
renderPassInfo.renderPass = renderPass;
renderPassInfo.framebuffer = swapChainFramebuffers[i];
//[13]the size of the render area
renderPassInfo.renderArea.offset = { 0, 0 }; //It should match the size of the attachments for best performance
renderPassInfo.renderArea.extent = swapChainExtent;
VkClearValue clearColor = { 0.0f, 0.0f, 0.0f, 1.0f };
renderPassInfo.clearValueCount = 1;
renderPassInfo.pClearValues = &clearColor;
//[13]最后一个参数:how the drawing commands within the render pass will be provided
//[13]VK_SUBPASS_CONTENTS_INLINE render pass commands 将嵌入 primary command buffer, 并且不会执行 secondary command buffers
//[13]VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERSrender pass commands 将从 secondary command buffers 执行
vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
//[13]Basic drawing commands
//[13]第二个参数指定管线对象是图形管线还是计算管线。
vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
//[13]vertexCount 即使没有顶点缓冲区,从技术上讲,仍然有3个顶点要绘制
//[13]instanceCount 用于实例化渲染,如果不进行实例化使用1
//[13]firstVertex 顶点缓冲区的偏移量,定义gl_VertexIndex的最小值
//[13]firstInstance 用作实例渲染的偏移量,定义gl_InstanceIndex的最小值
vkCmdDraw(commandBuffers[i], 3, 1, 0, 0);
//[13]The render pass can now be ended
if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) {
throw std::runtime_error("failed to record command buffer!");
}
}
}
//[13]--------------------------------------------------------------------------------------------------------
//[14]--------------------------------------------------------------------------------------------------------
void createSemaphores() {
//[14]
imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
//[14]
inFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
//[15]
imagesInFlight.resize(swapChainImages.size(), VK_NULL_HANDLE);
//[14] Vulkan API 会扩展 flags 和 pNext 参数。
VkSemaphoreCreateInfo semaphoreInfo = {};
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
//[14]
VkFenceCreateInfo fenceInfo = {};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
//[14]if we had rendered an initial frame that finished
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
//[14]
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
//[14]
if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS ||
vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS ||
//[14]
vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) {
throw std::runtime_error("failed to create semaphores for a frame!");
}
}
}
//[14]--------------------------------------------------------------------------------------------------------
//[15]--------------------------------------------------------------------------------------------------------
//[15]异步执行的
//[15]acquire an image from the swap chain
void drawFrame() {
//[15]wait for the frame to be finished
//[15]vkWaitForFences函数接受一个fences数组,并等待其中任何一个或所有fences在返回之前发出信号。
//[15]这里传递的VK_TRUE表示要等待所有的fences,但是对于单个fences来说,这显然无关紧要。
//[15]就像vkAcquireNextImageKHR一样,这个函数也需要超时。与信号量不同,我们需要通过vkresetfines调用将fence重置为unsignaled状态。
//vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
//or
uint32_t imageIndex;
//[15]第三个参数指定可获得图像的超时时间(以纳秒为单位)。
//[15]接下来的两个参数指定了同步对象,这些对象将在引擎使用完图像时发出信号。 可以指定semaphore、fence或都指定
//[15]用于输出可用swap chain中图像的索引。
//vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex);
//or
//[15]
//vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
//or
//[18]
VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
//[18]VK_ERROR_OUT_OF_DATE_KHR: Swap chain 与 surface 不兼容,无法再用于渲染。 通常发生在窗口调整大小之后
//[18]VK_SUBOPTIMAL_KHR Swap: chain仍可用于成功呈现到surface,但surface属性不再完全匹配
if (result == VK_ERROR_OUT_OF_DATE_KHR) {
//[18]如果SwapChain在尝试获取图像时已过时,则无法再显示给它。因此,我们应该立即重新创建SwapChain,并在下一个drawFrame调用中重试。
recreateSwapChain();
return;
}
//[18]如果SwapChain是次优的,可以调用recreateSwapChain,但还是选择继续,因为我们已经获得了一个图像。VK_SUCCESS和VK_SUBOPTIMAL_KHR都被认为是“成功” 。
else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
throw std::runtime_error("failed to acquire swap chain image!");
}
//[15]Check if a previous frame is using this image (i.e. there is its fence to wait on)
if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) {
vkWaitForFences(device, 1, &imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX);
}
//[15] Mark the image as now being in use by this frame
imagesInFlight[imageIndex] = inFlightFences[currentFrame];
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
//VkSemaphore waitSemaphores[] = { imageAvailableSemaphore };
//or
//[15]
VkSemaphore waitSemaphores[] = { imageAvailableSemaphores[currentFrame] };
VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
//[15]前三个参数指定在执行开始之前等待哪些Semaphore,以及在管线的哪个阶段等待。
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = waitSemaphores;
submitInfo.pWaitDstStageMask = waitStages;
//[15]指定实际提交执行的commandBuffer。
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffers[imageIndex];
//[15]指定在commandBuffer完成执行后发送信号的Semaphore。
//VkSemaphore signalSemaphores[] = { renderFinishedSemaphore};
//or
//[15]
VkSemaphore signalSemaphores[] = { renderFinishedSemaphores[currentFrame] };
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signalSemaphores;
//[15]
vkResetFences(device, 1, &inFlightFences[currentFrame]);
//[15]最后一个参数引用了可选的 fenc, 当 command buffer 执行完成时,它将发出信号。
//[15]我们使用信号量进行同步,所以我们传递VK_NULL_HANDLE。
//if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE) != VK_SUCCESS) {
// throw std::runtime_error("failed to submit draw command buffer!");
//}
//or
//[15]
if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) {
throw std::runtime_error("failed to submit draw command buffer!");
}
VkPresentInfoKHR presentInfo = {};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
//[15]在呈现(Present)之前等待哪些信号量
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = signalSemaphores;
VkSwapchainKHR swapChains[] = { swapChain };
presentInfo.swapchainCount = 1;
//[15]为每个swapChain指定呈现的图像和图像索引
presentInfo.pSwapchains = swapChains;
presentInfo.pImageIndices = &imageIndex;
//[15]check for every individual swap chain if presentation was successful. (array of VkResult)
presentInfo.pResults = nullptr; // Optional
//[15]submits the request to present an image to the swap chain.
//vkQueuePresentKHR(presentQueue, &presentInfo);
//or
//[18]vkQueuePresentKHR 函数返回具有相同vkAcquireNextImageKHR返回值含义相同的值。
//[18]在这种情况下,如果SwapChain不是最优的,我们也会重新创建它,因为我们想要最好的结果。
result = vkQueuePresentKHR(presentQueue, &presentInfo);
//添加 framebufferResized 确保semaphores处于一致状态,否则可能永远不会正确等待发出信号的semaphores。
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) {
framebufferResized = false;
recreateSwapChain();
}
else if (result != VK_SUCCESS) {
throw std::runtime_error("failed to present swap chain image!");
}
//[15]
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
//[15]
vkQueueWaitIdle(presentQueue);
}
//[15]--------------------------------------------------------------------------------------------------------
//[17]recreate 之前先 cleanup
void cleanupSwapChain() {
for (size_t i = 0; i < swapChainFramebuffers.size(); i++) {
vkDestroyFramebuffer(device, swapChainFramebuffers[i], nullptr);
}
//[17]可以重新创建Command池,但相当浪费的。相反,使用vkfreecandbuffers函数清理现有CommandBuffer。就可以重用现有的池来分配新的CommandBuffer。
vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
vkDestroyPipeline(device, graphicsPipeline, nullptr);
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
vkDestroyRenderPass(device, renderPass, nullptr);
for (size_t i = 0; i < swapChainImageViews.size(); i++) {
vkDestroyImageView(device, swapChainImageViews[i], nullptr);
}
vkDestroySwapchainKHR(device, swapChain, nullptr);
}
//[16] recreate SwapChain, pipeline must rebuilt
void recreateSwapChain() {
//[20]处理最小化
int width = 0, height = 0;
glfwGetFramebufferSize(window, &width, &height);
while (width == 0 || height == 0) {
glfwGetFramebufferSize(window, &width, &height);
glfwWaitEvents();
}
//[16]等待资源完成使用。
vkDeviceWaitIdle(device);
cleanupSwapChain();
createSwapChain();
createImageViews();
createRenderPass();
createGraphicsPipeline();
createFramebuffers();
createCommandBuffers();
}
//[19]
static void framebufferResizeCallback(GLFWwindow* window, int width, int height) {
//[19-2]当前对象指针赋值
auto app = reinterpret_cast<Application*>(glfwGetWindowUserPointer(window));
app->framebufferResized = true;
}
};
//16开始
int main() {
Application app;
try {
app.run();
}
catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_FAILURE;
}