目錄
- 程式資源建立流程
-
- window 相關(glfw)
- 建立Vulkan執行個體
- shader
- compile shader
- Code
程式資源建立流程
window 相關(glfw)
- initWindow()
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
GLFWwindow* window= glfwCreateWindow(Width, Height, "Vulkan", nullptr, nullptr);
- mainLoop()
while (!glfwWindowShouldClose(window))
{
glfwPollEvents();
}
- cleanup()
glfwDestroyWindow(window);
glfwTerminate();
建立Vulkan執行個體
- initVulkan()
.createInstance()
.createSurface()
.setupDebugMessenger()
.pickPhysicalDevice()
.createLogicalDevice()
.createSwapChain()
.createImageViews()
.createRenderPass()
.createGraphicsPipeline() //shader
.createFramebuffers()
.createCommandPool()
.createCommandBuffers()
.createSemaphores()
- mainLoop()
while (!glfwWindowShouldClose(window))
{
glfwPollEvents();
drawFrame();
}
- cleanup()
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);
}
vkDestroyCommandPool(device, commandPool, nullptr);
for (auto framebuffer : swapChainFramebuffers) {
vkDestroyFramebuffer(device, framebuffer, nullptr);
}
vkDestroyPipeline(device, graphicsPipeline, nullptr);
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
vkDestroyRenderPass(device, renderPass, nullptr);
for (auto imageView : swapChainImageViews) {
vkDestroyImageView(device, imageView, nullptr);
}
vkDestroySwapchainKHR(device, swapChain, nullptr);
vkDestroyDevice(device, nullptr);
if (enableValidationLayers) {
DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
}
vkDestroySurfaceKHR(instance, surface, nullptr);
vkDestroyInstance(instance, nullptr);
glfwDestroyWindow(window);
shader
shader.vert
#version 450
layout(location = 0) out vec3 fragColor;
vec2 positions[3] = vec2[](
vec2(0.0, -0.5),
vec2(0.5, 0.5),
vec2(-0.5, 0.5)
);
vec3 colors[3] = vec3[](
vec3(1.0, 0.0, 0.0),
vec3(0.0, 1.0, 0.0),
vec3(0.0, 0.0, 1.0)
);
void main() {
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
fragColor = colors[gl_VertexIndex];
}
shader.frag
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout(location = 0) in vec3 fragColor;
layout(location = 0) out vec4 outColor;
void main() {
outColor = vec4(fragColor, 1.0);
}
compile shader
compile.bat
C:\VulkanSDK\1.2.176.1\Bin32\glslc.exe shader.vert -o shaders/vert.spv
C:\VulkanSDK\1.2.176.1\Bin32\glslc.exe shader.frag -o shaders/frag.spv
pause
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();
}
private:
//[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;
//[1]
void initWindow() {
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);
}
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() {
//[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);
}
//[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 {
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);
//[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);
//[15]
vkQueueWaitIdle(presentQueue);
//[15]
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
}
//[15]--------------------------------------------------------------------------------------------------------
};
int main() {
Application app;
try {
app.run();
}
catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_FAILURE;
}