当前位置: 首页 > news >正文

Vulkan学习(七): Swap Chain Recreation

目录

  • Swap Chain Recreation
    • Recreating the swap chain
    • Suboptimal or out-of-date swap chain
    • Handling resizes explicitly
    • Handling minimization
  • Code

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 destroyedstd::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 enablecreateInfo.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]GLFWauto 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_validationfor (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 非最优的方式使用VulkanVkDebugUtilsMessageTypeFlagsEXT 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 surfaceif (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, constVkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, constVkAllocationCallbacks* 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]创建代理函数 销毁CreateDebugUtilsMessengerEXTvoid DestroyDebugUtilsMessengerEXT(VkInstance instance,VkDebugUtilsMessengerEXT debugMessenger, constVkAllocationCallbacks* 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]选择适合该程序的GPUfor (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 allif (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 supportbool 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;//orQueueFamilyIndices indices = findQueueFamilies(device);//return indices.graphicsFamily.has_value();//orreturn indices.isComplete() && extensionsSupported && swapChainAdequate;}SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) {SwapChainSupportDetails details;//[5]basic surface capabilities 基本性能vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities);//[5]the supported surface formatsuint32_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 modesuint32_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 familyQueueFamilyIndices 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; // trueuint32_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]寻找一个队列族,它能够链接windowVkBool32 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 featuresVkPhysicalDeviceFeatures 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 familiesstd::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]将队列信息加入驱动infocreateInfo.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]因为我们只从这个族中创建一个队列,所以我们只使用索引0vkGetDeviceQueue(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_EXCLUSIVEQueueFamilyIndices 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; // OptionalcreateInfo.pQueueFamilyIndices = nullptr; // Optional}//[7]指定对交换链中的图像应用某种变换createInfo.preTransform = swapChainSupport.capabilities.currentTransform;//[7]alpha channel should be used for blendingcreateInfo.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_KHRVkPresentModeKHR 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 3DcreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;createInfo.format = swapChainImageFormat;//[8]components字段允许旋转颜色通道。 createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; //defaultcreateInfo.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 datacolorAttachment.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]引用AttachmentVkAttachmentReference colorAttachmentRef = {};//[9]attachment参数通过attachment描述数组中的索引指定要引用的attachmentcolorAttachmentRef.attachment = 0;//[9]布局指定了我们希望attachment在使用此引用的subpass中具有哪种布局。colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;//[9]Vulkan may also support compute subpasses in the futureVkSubpassDescription 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]viewportVkViewport 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; // Optionalmultisampling.pSampleMask = nullptr; // Optionalmultisampling.alphaToCoverageEnable = VK_FALSE; // Optionalmultisampling.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_TRUEcolorBlendAttachment.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; // OptionalcolorBlending.attachmentCount = 1;colorBlending.pAttachments = &colorBlendAttachment;colorBlending.blendConstants[0] = 0.0f; // OptionalcolorBlending.blendConstants[1] = 0.0f; // OptionalcolorBlending.blendConstants[2] = 0.0f; // OptionalcolorBlending.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; // OptionalpipelineLayoutInfo.pSetLayouts = nullptr; // OptionalpipelineLayoutInfo.pushConstantRangeCount = 0; // OptionalpipelineLayoutInfo.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; // OptionalpipelineInfo.pColorBlendState = &colorBlending;pipelineInfo.pDynamicState = nullptr; // OptionalpipelineInfo.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; // OptionalpipelineInfo.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执行。//这意味着在管道创建完成后,就可以立即销毁ShaderModulevkDestroyShaderModule(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 framebuffersfor (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; //不使用flagif (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 bufferfor (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; // Optionalif (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) {throw std::runtime_error("failed to begin recording command buffer!");}//[13]Starting a render passVkRenderPassBeginInfo renderPassInfo = {};//[13]the render pass itself and the attachments to bindrenderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;renderPassInfo.renderPass = renderPass;renderPassInfo.framebuffer = swapChainFramebuffers[i];//[13]the size of the render arearenderPassInfo.renderArea.offset = { 0, 0 }; //It should match the size of the attachments for best performancerenderPassInfo.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 endedif (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 finishedfenceInfo.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 chainvoid 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 frameimagesInFlight[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 之前先 cleanupvoid 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 rebuiltvoid 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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.exyb.cn/news/show-2975.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

设计模式导读助记

各个设计模式的详细介绍都已经完成&#xff0c;但是不经常用总会忘&#xff0c;所以我想用 一句话 总结设计模式&#xff0c;思考模式的真正意图&#xff0c;再用 一点提示 来思考代码如何实现 写在前面 我整理的设计模式这一个系列&#xff0c;主要是结合了以下几本书 : 《设…...

RT-Thrad|STM32F103+ESP8266 S01+RT-Thread联网之环境搭建(1/3)

文章目录前言硬件准备百问网STM32F103ESP8266 01SESP8266 介绍ESP8266 01S技术规格参数软件准备下载安装 Keil μVision5Pack Installer安装 ST-Link 驱动获取RT-Thread源码下载安装 RT-Thread env 工具文章列表 RT-Thrad|STM32F103ESP8266 S01RT-Thread联网之环境搭建(1/3)RT…...

天眼查怎么删除信息_天眼查删除信息的方法介绍

天眼查信息怎么删除 天眼查风险信息怎么清除 天眼查问答信息怎么删除 天眼查法律诉讼信息可以删吗 天涯查上的信息删除怎么操作&#xff0c;天眼查成立于2014年&#xff0c;至今发展迅速&#xff0c;已经帮助了无数的企业和消费者&#xff0c;那么很多企业的天眼查信息有时候需…...

5.Random

用于生产一个随机数 步骤&#xff1a; 1.导包 import java.util.Random; 2.创建对象 Random random new Random();3.获取随机数 int number random.nextInt(10); //随机数的取值范围是[0,10),即大于等于&#xff0c;小于10 上面不能获取到10&#xff0c;若要获取到10&…...

Xxl-Job调度器原理解析

项目解析源码地址&#xff1a;https://gitee.com/lidishan/xxl-job-code-analysisxxl-job版本&#xff1a;2.3.0Xxl-Job分为执行器、调度器。而我们平时的客户端就属于一个执行器&#xff0c;执行器启动的时候会自动注册到调度器上&#xff0c;然后调度器进行远程调度。调度器初…...

51单片机利用锁存器控制数码管显示年月日时分秒

数码管模块中的两片74hc573&#xff0c;一片锁存段码&#xff0c;一片锁存位码&#xff0c;这样才能驱动8位数码管。74hc573是锁存器&#xff0c;用于数码管显示时通常是采用段选、片选共用同一组并口的驱动方式。 驱动数码管需要两个信号&#xff0c;一个是段选信号&#xff…...

webrtc之SVC实现(十)

一、概念 SVC&#xff08;可适性视频编码或可分级视频编码&#xff09;是传统H.264/MPEG-4 AVC编码的延伸&#xff0c;可提升更大的编码弹性&#xff0c;并具有时间可适性&#xff08;Temporal Scalability&#xff09;、空间可适性&#xff08;Spatial Scalability&#xff09…...

LeetCode 数值的整数次方

实现 pow(x, n) &#xff0c;即计算 x 的 n 次幂函数&#xff08;即&#xff0c;xn&#xff09;。不得使用库函数&#xff0c;同时不需要考虑大数问题。 示例 1&#xff1a; 输入&#xff1a;x 2.00000, n 10 输出&#xff1a;1024.00000 示例 2&#xff1a; 输入&#xf…...

python 继承和多态

在已有类的基础上创建新类&#xff0c;这其中的一种做法就是让一个类从另一个类那里将属性和方法直接继承下来&#xff0c;从而减少重复代码的编写。提供继承信息的我们称之为父类&#xff0c;也叫超类或基类&#xff1b;得到继承信息的我们称之为子类&#xff0c;也叫派生类或…...

JAVA 基础学习之 继承与方法覆写

1 继承引入​​​​​​​ 三个类都有重复的代码&#xff0c;可以把这共同的代码抽出去&#xff0c;抽出去放到另外一个类里面&#xff1b;下面的3个类和上面的类需要发生一点关系&#xff08;继承&#xff09;&#xff0c;上面的类叫做 父类&#xff08;超类&#xff0c;基类&…...

个性化个人主页html5模板

介绍&#xff1a; 个性化个人主页html5模板 网盘下载地址&#xff1a; http://kekewl.net/tARDuX5n02U0 图片&#xff1a;...

java IO教程《三》

缓冲区流讲解(Buffered) 什么是缓冲区&#xff1f; 缓冲流&#xff0c;也叫高效流&#xff0c;是对4个基本的File流的增强&#xff0c;所以也是4个流&#xff0c;按照数据类型分类&#xff1a; 字节缓冲流&#xff1a;BufferedInputStream&#xff0c;BufferedOutputStream字…...

elementUI-Tree 树形控件的使用

elementUI-Tree 树形控件的使用 实现效果&#xff1a; 控件的官方使用说明 控件要求返回的数据结构 {"success": true,"code": 20000,"message": "成功","data": {"items": [{"id": "1394579386…...

实训第一天以及第二天所学记录

实训第一天以及第二天所学记录 浏览器内核 IE&#xff1a;Trident Firefox&#xff1a;Gecko Chrome&#xff1a;Webkit / Blink Safari&#xff1a;Webkit Opera&#xff1a;Presto / Blink 在VScode中使用注释的快捷键 按住键盘的Ctrl/ 元素 &#xff08;标签 标记&…...

跨域请求

/* * Description: 配置文件 */ module.exports { publicPath: "./", devServer: { open: true, proxy: "http://106.15.179.105/api" //跨域路径 }, }; // proxy是代理的意思 // 代理跨域就是在欺骗浏览器 让浏览器认为你访问的还是 同…...

Mac os下通过Anaconda在远程服务器配置python虚拟环境

一、SSH管理软件 这里推荐一款本人正在使用的软件&#xff0c;Termius。Termius是一款非常好用而且漂亮的SSH客户端&#xff0c;能快速远程控制服务器&#xff0c;可以定制自己喜欢的主题.Termius不仅涵盖了Windows、Linux、OSX&#xff0c;还变态得支持Android和iOS&#xff…...

Labview需求(部分)

本人从事工控行业多年,有一些资源,目前labview的单子干不过来了,想找几个靠谱的labview工程师跟我合作,想做兼职,接单的labview工程师可以私聊我,另外我有一些项目还需要跟单片机工程师,plc工程师,fpga工程师合作,欢迎大家找我合作!以下是手里比较着急的项目,需要外包 需求1&am…...

2021java1年经验公司面试真题

1面我就省略了&#xff0c;主要就是看你的以前工作情况&#xff0c;以前工作内容能不能清晰表达&#xff0c;还有一些简单的java基础问题&#xff0c;大概20多分钟。二面就是40分钟基础&#xff0c;20分钟业务&#xff0c;10分钟个人情况。下面是二面问题 1.自我介绍 做一下自…...

Unity基础之C#核心篇笔记4:多态

Unity基础之C#核心篇笔记4&#xff1a;多态多态1.多态的概念2.解决的问题3.多态的实现4.总结抽象类和抽象方法1.抽象类2.抽象函数3.总结4.练习题接口1.接口的概念2. 接口的申明3.接口的使用4.接口可以继承接口5.显示实现接口6.总结7.练习题密封方法1.密封方法基本概念2.实例3.总…...

LeetCode每日一题 - 有多少小于当前数字的数字

题目&#xff1a; 给你一个数组 nums&#xff0c;对于其中每个元素 nums[i]&#xff0c;请你统计数组中比它小的所有数字的数目。 换而言之&#xff0c;对于每个 nums[i] 你必须计算出有效的 j 的数量&#xff0c;其中 j 满足 j ! i 且 nums[j] < nums[i] 。 以数组形式返回…...

策略评价与建立模拟

摘要 评价策略回测的指标建立模拟交易未来函数运行过慢过拟合策略失效收益与风险的取舍自测与自学在学习了如何编写策略后&#xff0c;我们将介绍下评价策略回测的指标&#xff0c;如何建立模拟交易&#xff0c;以及除回测之外还有哪些需要关注的方面。 策略回测指标 如下图&…...

权限提升部分思路

权限提升权限提升简称提权。现在的操作系统都是多用户操作系统&#xff0c;用户之间都有权限控制&#xff0c;比如通过web漏洞拿到的是web进程的权限&#xff0c;往往web服务都是一个权限很低的账号启动的&#xff0c;因此通过webshell进行一些操作会受到限制&#xff0c;这就需…...

分享让人折服的优秀代码基因 ReviewBoard

1 背景 近来参与一个较大团队的项目实施&#xff0c;项目的金额两千万&#xff0c;人数近百。但是&#xff0c;项目实施后&#xff0c;暴露出以下几个问题&#xff1a; &#xff08;1&#xff09;质量不佳&#xff0c;团队成员水平参差不齐&#xff0c;软件外部质量、内部质量…...

Linux_csa的复习笔记(内容精简)

突然发现&#xff0c;小编自己的Linux忘的差不多了&#xff0c;这怎么行&#xff0c;所以小编对以前考试的内容进行学习&#xff0c;在补充一些内容&#xff0c;这篇文章希望能如和小编一样忘记的人记起来&#xff0c;好了家常话聊到这里&#xff0c;下面开始上干货。 分区和文…...

Spring Cloud(Greenwich版)-03-编写高可用Eureka Server(集群)

前言 上一章Spring Cloud&#xff08;Greenwich版&#xff09;-02-服务注册与服务发现-Eureka入门&#xff0c;我们实现了将User和Goods微服务都注册到了Eureka上&#xff0c;那么在生产环境中为了达到高可用的目的&#xff0c;Eureka Service是集群部署... 原文地址&#xff…...

zookeeper--一致性协议 ZAB

一、简介 ZAB 协议全称&#xff1a;Zookeeper Atomic Broadcast&#xff08;Zookeeper 原子广播协议&#xff09;。 是一个为分布式应用提供高效且可靠的分布式协调服务。在解决分布式一致性方面&#xff0c;Zookeeper 并没有使用 Paxos &#xff0c;而是采用了 ZAB 协议。 Z…...

JDK8新特性里提供了3个时间类:LocalDate、LocalTime、LocalDateTime

参考资料&#xff1a;好好学Java https://mp.weixin.qq.com/s/Dd_7yUh3lq3TqE2cjsYXvw JDK8新特性里提供了3个时间类&#xff1a;LocalDate、LocalTime、LocalDateTime 在项目开发中&#xff0c;已经需要对Date类型进行格式&#xff0c;否则可读性很差&#xff0c;格式化Date…...

this、call、apply、bind

this的指向 作为普通函数&#xff08;也指对象的方法&#xff09;进行使用&#xff0c;谁去调用该函数&#xff0c;那么this便会指向谁。作为构造函数使用&#xff0c;指向该构造函数的实例。在箭头函数内部的使用&#xff0c;指向上一级函数的this&#xff1b;若上一级不是函…...

5.Random

用于生产一个随机数 步骤&#xff1a; 1.导包 import java.util.Random; 2.创建对象 Random random new Random();3.获取随机数 int number random.nextInt(10); //随机数的取值范围是[0,10),即大于等于&#xff0c;小于10 上面不能获取到10&#xff0c;若要获取到10&…...

hexo添加自动更新的站点统计页面

本站原来的站点统计图页面的统计数据需要博主我每个月手动统计上传非常的麻烦容易把人累死直到我看到了fox的统计页面又是我眼前一亮于是又去借鉴学习了一波&#xff0c;注本文大部分代码都出自fox大佬之手。另外如果你也想要利用GitHubactions定时自动更新请参考前文完成集成化…...

Android 自定义View实现打钩(签到)的动画

先看效果图&#xff1a; 这里&#xff0c;我没有添加打钩的图片&#xff0c;而是单纯的用canvas来实现动画效果 中间的钩&#xff0c;我用了路径Path来进行描绘并实现它的动画效果。首先&#xff0c;这个钩由两条线段&#xff0c;三个顶点组成的&#xff0c;其实将这三个顶点作…...

三维重建之PIFuHD

Fackbook AI 研究出从一张图片生成Mesh模型的算法PIFuHD ​ Paper: https://arxiv.org/pdf/2004.00452.pdf Code: https://github.com/facebookresearch/pifuhd 一&#xff0c;Demo数据预处理 这里面需要先编译pifuhd和lightweight-human-pose-estimation.pytorch&#xf…...

梯度与负梯度

梯度方向为方向导数最大的方向&#xff0c;梯度的模为最大的方向导数。 梯度可以理解为上升最快的方向。负梯度则可以理解为下降最快的方向。 梯度下降法原理相同。沿着负梯度方向&#xff08;代价&#xff09;则下降最快。...

pytorch梯度

tensor梯度的相关性 若一个节点requires_grad被设置为True&#xff0c;那么所有依赖它的节点的requires_grad都为True。 0 import torch 1 xtorch.ones(1) 2 wtorch.ones(1,requires_gradTrue) 3 yx*w 4 x.requires_grad,w.requires_grad,y.requires_grad 5 outp…...

pytorch梯度的计算过程

1、基础知识: 与numpy中的基本操作相似&#xff0c; pytorch 的作用是引入GPU加快运算&#xff0c; 增加图形界面&#xff0c; 适合大数据运算&#xff0c; 尤其是deep learninggradient 梯度类似于求导&#xff0c; 找到梯度下降的最佳路径。tensor 除了可以进行线性代数运算…...

梯度下降及具体计算方式

阅读目录 1. 批量梯度下降法BGD2. 随机梯度下降法SGD3. 小批量梯度下降法MBGD4. 总结在应用机器学习算法时&#xff0c;我们通常采用梯度下降法来对采用的算法进行训练。其实&#xff0c;常用的梯度下降法还具体包含有三种不同的形式&#xff0c;它们也各自有着不同的优缺点。…...

pytorch梯度累积

增大batchsize训练模型&#xff0c;一般都能带来一定的提升。在显卡内存不够的情况下&#xff0c;可以通过梯度累积的方式&#xff0c;来扩大batchsize。 因为pytorch中&#xff0c;反向传播之后&#xff0c;梯度是不清零的&#xff0c;因此要实现梯度累积&#xff0c;比较简单…...

次梯度

原文链接&#xff1a; 次梯度方法 首发于凸优化学习笔记写文章【凸优化笔记5】-次梯度方法&#xff08;Subgradient method&#xff09;Lauer南风&#xff0c;南风29 人赞同了该文章目录1.问题引入2.次梯度的定义3.次梯度优化条件&#xff08;Subgradient optimality condition…...

梯度剪裁

目录 1、梯度剪裁的原因 2、梯度裁剪的使用 2.1、固定阈值剪裁 2.2、根据参数的范数来衡量的 3、梯度裁剪的使用位置 梯度剪裁&#xff0c;一种避免梯度爆炸的方式。 1、梯度剪裁的原因 神经网络是通过梯度下降来学习的。而梯度爆炸问题一般会随着网络层数的增加而变得越…...

什么是梯度

机器学习/深度学习中&#xff0c;需要使用训练数据来最小化损失函数&#xff0c;从而确定参数的值。而最小化损失函数&#xff0c;即需要求得损失函数的极值。 求解函数极值时&#xff0c;需要用到导数。对于某个连续函数f(x)f(x)f(x)&#xff0c;令其一阶导数 f′(x)0f&#x…...

Databend 开源周报 #69

Databend 是一款强大的云数仓。专为弹性和高效设计&#xff0c;自由且开源。 即刻体验云服务&#xff1a;https://app.databend.com。 New Features multiple catalog 实现删除用户定义目录 (#8820) meta 新增用于删除 key 和使 key 过期的 cli 命令 (#8858) planner 支…...

髂嵴最高点在哪里_【髂嵴最高点怎么摸】_最高点怎么摸_如何摸-大众养生网

文章导读髂嵴是髂骨翼的内缘&#xff0c;其前端工程师的突起称髂前上棘&#xff0c;以后端突起称髂后上棘&#xff0c;左右髂嵴的最高点连线平第4腰椎间盘横突。或者第三腰椎间盘和第四腰椎间盘正中间。髂嵴的最高点既并并不是髂前上棘也不是髂肿块。仅仅髂肿块要比髂前上棘略高…...

matlab中调用interp函数时出现使用 griddedInterpolant 网格矢量未定义与给定值匹配的点网格时怎么处理。

matlab中调用interp1函数时出现使用 griddedInterpolant 网格矢量未定义与给定值匹配的点网格时怎么处理&#xff1f;求大神解答 在这里插入代码片 drv_pwr_dmd_simudrv_ctrl_pwr_dmd_simuprb.W{1}interp1(sch_cycle(:,1),drv_pwr_dmd_simu,0:prb.N/prb.Ts); prb.W{2} interp1…...

点云关键点——(1)几种关键点提取

点云配准中&#xff0c;有时候点云数量太大&#xff0c;需要进行关键点提取&#xff0c;下面介绍几种点云pcl中点云关键点提取算法。 一、iss关键点提取 iss关键点的具体原理可以查看相关论文&#xff0c;下面主要参数设置如下&#xff1a; //iss关键点提取PointCloud::Ptr cl…...

CAS单点登录(一)——初识SSO

前言&#xff1a;其实好早就想把CAS的这一套知识整合一下&#xff0c;在工作上也应用到了这块&#xff0c;只是最近才在工作上接触到CAS&#xff0c;所以刚好把这些知识总结一下。这块可能是一个比较大的模块知识点&#xff0c;所以会有多篇文章进行逐一展开&#xff0c;笔者会…...

华为 PPP点到点链路层协议 用在哪里?底层的工作原理是什么?

我是艺博东 &#xff0c;一个思科出身专注于华为的网工。 文章目录PPP点到点链路层协议PPP主要由三类协议族组成PPP 链路的状态机简单配置CHAP 挑战握手认证协议PAP 密码认证协议PPP点到点链路层协议 PPP的物理接口常用&#xff1a;Serial接口。 PPP的应用场景。路由器作为企业…...

Matlab-错误使用 griddedInterpolant 网格向量未定义与给定值匹配的点网格

IDE&#xff1a; Matlab 2018a BUG : 错误使用 griddedInterpolant 网格向量未定义与给定值匹配的点网格。 出错 interp3 (line 146) F griddedInterpolant(X, Y, Z, V, method,extrap); 出错 slice (line 134) vi interp3(x,y,z,v,sx,sy,s…...

基于深度学习方法的点云算法3——PointNet++(点云分类分割)

基于深度学习方法的点云算法3——PointNet&#xff08;点云分类分割&#xff09; 请点点赞&#xff0c;会持续更新&#xff01;&#xff01;&#xff01; 基于深度学习方法的点云算法1——PointNetLK&#xff08;点云配准&#xff09; 基于深度学习方法的点云算法2——PointNet…...

ASP.NET Core 3.1系列(15)——Entity Framework Core之DB First

1、前言 本文开始介绍一些关于Entity Framework Core的内容。在EFCore中&#xff0c;常用的为DB First模式和Code First模式&#xff0c;下面就来介绍一下如何在EFCore中使用DB First模式生成实体类和数据库上下文。 2、创建测试数据库 在SQL Server中新建一个数据库Dao&…...

校园订餐

实验环境&#xff1a;Tomcat 9.0、MySQL 8.0、struts-2.5.8、MyEclipse2017 CI 7、hibernate-release-5.2.16 利用strutshibernate写的一个小Demo可以看一下我运行的视频 百度云盘(7hjr) 有要的可以自行下载 下载地址 1.登录界面 2.注册页面 3.忘记密码 …...