首页 > 其他分享 >Vulkan 教程第一部分:基础概念与初步设置

Vulkan 教程第一部分:基础概念与初步设置

时间:2024-07-10 23:55:48浏览次数:20  
标签:std 教程 VK nullptr createInfo 设置 device Vulkan

目录

1. 什么是Vulkan?

2. 环境准备

3. 创建Vulkan应用程序的基本步骤

4. 代码实现

5. 详细的步骤和概念解释

1.初始化窗口:

2.初始化Vulkan:

3.创建Vulkan实例:

4.选择物理设备

5. 创建逻辑设备

6. 创建交换链

7. 创建图像视图

8. 创建渲染通道

9. 创建帧缓冲

10. 创建命令池和命令缓冲

1. 什么是Vulkan?

Vulkan是一个现代的高性能图形API(Application Programming Interface),由Khronos Group开发。它提供了直接与GPU硬件通信的低级接口,允许开发者最大化地利用硬件性能。与OpenGL相比,Vulkan提供了更高的性能和更细粒度的控制。

2. 环境准备

在开始编写Vulkan程序之前,需要准备以下环境:

  • 安装Vulkan SDK
  • 设置开发环境(如Visual Studio, CLion等)
  • 安装必要的驱动程序

安装Vulkan SDK:

可以从LunarG官网下载适用于不同操作系统的Vulkan SDK:LunarG Vulkan SDK

3. 创建Vulkan应用程序的基本步骤
  1. 初始化Vulkan库
  2. 创建Vulkan实例
  3. 选择物理设备(GPU)
  4. 创建逻辑设备
  5. 创建交换链
  6. 创建图像视图
  7. 创建渲染通道
  8. 创建帧缓冲
  9. 创建命令池和命令缓冲
  10. 绘制和呈现
4. 代码实现

以下是一个简单的Vulkan应用程序的代码示例:

CMakeLists.txt:

cmake_minimum_required(VERSION 3.10)
project(VulkanTutorial)

set(CMAKE_CXX_STANDARD 17)
find_package(Vulkan REQUIRED)

add_executable(VulkanTutorial main.cpp)
target_include_directories(VulkanTutorial PRIVATE ${Vulkan_INCLUDE_DIRS})
target_link_libraries(VulkanTutorial PRIVATE ${Vulkan_LIBRARIES})

main.cpp:

#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <iostream>
#include <stdexcept>
#include <cstdlib>
#include <vector>
#include <optional>

const int WIDTH = 800;
const int HEIGHT = 600;

class HelloTriangleApplication {
public:
    void run() {
        initWindow();
        initVulkan();
        mainLoop();
        cleanup();
    }

private:
    GLFWwindow* window;
    VkInstance instance;
    VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
    VkDevice device;

    void initWindow() {
        glfwInit();
        glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
        window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);
    }

    void initVulkan() {
        createInstance();
        pickPhysicalDevice();
        createLogicalDevice();
    }

    // 创建Vulkan实例
    void createInstance() {
        VkApplicationInfo appInfo{};
        appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
        appInfo.pApplicationName = "Hello Triangle";
        appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
        appInfo.pEngineName = "No Engine";
        appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
        appInfo.apiVersion = VK_API_VERSION_1_0;

        VkInstanceCreateInfo createInfo{};
        createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
        createInfo.pApplicationInfo = &appInfo;

        uint32_t glfwExtensionCount = 0;
        const char** glfwExtensions;
        glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);

        createInfo.enabledExtensionCount = glfwExtensionCount;
        createInfo.ppEnabledExtensionNames = glfwExtensions;
        createInfo.enabledLayerCount = 0;

        if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
            throw std::runtime_error("failed to create instance!");
        }
    }

    // 选择物理设备(GPU)
    void pickPhysicalDevice() {
        uint32_t deviceCount = 0;
        vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);

        if (deviceCount == 0) {
            throw std::runtime_error("failed to find GPUs with Vulkan support!");
        }

        std::vector<VkPhysicalDevice> devices(deviceCount);
        vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());

        for (const auto& device : devices) {
            if (isDeviceSuitable(device)) {
                physicalDevice = device;
                break;
            }
        }

        if (physicalDevice == VK_NULL_HANDLE) {
            throw std::runtime_error("failed to find a suitable GPU!");
        }
    }

    // 检查设备是否适合
    bool isDeviceSuitable(VkPhysicalDevice device) {
        VkPhysicalDeviceProperties deviceProperties;
        vkGetPhysicalDeviceProperties(device, &deviceProperties);

        VkPhysicalDeviceFeatures deviceFeatures;
        vkGetPhysicalDeviceFeatures(device, &deviceFeatures);

        return deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU && deviceFeatures.geometryShader;
    }

    // 创建逻辑设备
    void createLogicalDevice() {
        VkDeviceQueueCreateInfo queueCreateInfo{};
        queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
        queueCreateInfo.queueFamilyIndex = 0;
        queueCreateInfo.queueCount = 1;

        float queuePriority = 1.0f;
        queueCreateInfo.pQueuePriorities = &queuePriority;

        VkPhysicalDeviceFeatures deviceFeatures{};

        VkDeviceCreateInfo createInfo{};
        createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
        createInfo.pQueueCreateInfos = &queueCreateInfo;
        createInfo.queueCreateInfoCount = 1;
        createInfo.pEnabledFeatures = &deviceFeatures;
        createInfo.enabledExtensionCount = 0;
        createInfo.enabledLayerCount = 0;

        if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
            throw std::runtime_error("failed to create logical device!");
        }
    }

    void mainLoop() {
        while (!glfwWindowShouldClose(window)) {
            glfwPollEvents();
        }
    }

    void cleanup() {
        vkDestroyDevice(device, nullptr);
        vkDestroyInstance(instance, nullptr);
        glfwDestroyWindow(window);
        glfwTerminate();
    }
};

int main() {
    HelloTriangleApplication app;

    try {
        app.run();
    } catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}
5. 详细的步骤和概念解释
1.初始化窗口:
void initWindow() {
    glfwInit();
    glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
    window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);
}
  • 使用GLFW库初始化窗口系统,并创建一个800x600的窗口。
  • GLFW_CLIENT_API设为GLFW_NO_API,表示我们不使用OpenGL上下文,而是使用Vulkan。

2.初始化Vulkan:
  • 包括创建Vulkan实例、选择物理设备和创建逻辑设备。
3.创建Vulkan实例:
void createInstance() {
    VkApplicationInfo appInfo{};
    appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
    appInfo.pApplicationName = "Hello Triangle";
    appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
    appInfo.pEngineName = "No Engine";
    appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
    appInfo.apiVersion = VK_API_VERSION_1_0;

    VkInstanceCreateInfo createInfo{};
    createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
    createInfo.pApplicationInfo = &appInfo;

    uint32_t glfwExtensionCount = 0;
    const char** glfwExtensions;
    glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);

    createInfo.enabledExtensionCount = glfwExtensionCount;
    createInfo.ppEnabledExtensionNames = glfwExtensions;
    createInfo.enabledLayerCount = 0;

    if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
        throw std::runtime_error("failed to create instance!");
    }
}
  • VkApplicationInfo结构体包含应用程序的信息,这些信息可以帮助驱动进行优化。
  • VkInstanceCreateInfo结构体用于创建实例的信息。
  • 使用glfwGetRequiredInstanceExtensions获取GLFW所需的扩展列表。
  • 调用vkCreateInstance函数创建Vulkan实例。
4.选择物理设备
void pickPhysicalDevice() {
    uint32_t deviceCount = 0;
    vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);

    if (deviceCount == 0) {
        throw std::runtime_error("failed to find GPUs with Vulkan support!");
    }

    std::vector<VkPhysicalDevice> devices(deviceCount);
    vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());

    for (const auto& device : devices) {
        if (isDeviceSuitable(device)) {
            physicalDevice = device;
            break;
        }
    }

    if (physicalDevice == VK_NULL_HANDLE) {
        throw std::runtime_error("failed to find a suitable GPU!");
    }
}
  • vkEnumeratePhysicalDevices函数列举所有支持Vulkan的物理设备。
  • 遍历所有物理设备,检查是否适合我们的需求。
bool isDeviceSuitable(VkPhysicalDevice device) {
    VkPhysicalDeviceProperties deviceProperties;
    vkGetPhysicalDeviceProperties(device, &deviceProperties);

    VkPhysicalDeviceFeatures deviceFeatures;
    vkGetPhysicalDeviceFeatures(device, &deviceFeatures);

    return deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU && deviceFeatures.geometryShader;
}
  • vkGetPhysicalDeviceProperties获取物理设备的属性,例如设备类型和名称。
  • vkGetPhysicalDeviceFeatures获取物理设备的功能,例如是否支持几何着色器。
  • 在此示例中,我们选择离散GPU并且支持几何着色器的设备。
5. 创建逻辑设备
void createLogicalDevice() {
    VkDeviceQueueCreateInfo queueCreateInfo{};
    queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
    queueCreateInfo.queueFamilyIndex = 0;  // 这里只是一个示例,实际上需要找到合适的队列族索引
    queueCreateInfo.queueCount = 1;

    float queuePriority = 1.0f;
    queueCreateInfo.pQueuePriorities = &queuePriority;

    VkPhysicalDeviceFeatures deviceFeatures{};

    VkDeviceCreateInfo createInfo{};
    createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
    createInfo.pQueueCreateInfos = &queueCreateInfo;
    createInfo.queueCreateInfoCount = 1;
    createInfo.pEnabledFeatures = &deviceFeatures;
    createInfo.enabledExtensionCount = 0;
    createInfo.enabledLayerCount = 0;

    if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
        throw std::runtime_error("failed to create logical device!");
    }
}
  • VkDeviceQueueCreateInfo结构体用于描述逻辑设备队列。
  • queueFamilyIndex表示队列族索引。在实际应用中,需要通过查询找到支持所需操作的队列族索引。
  • queuePriority设置队列的优先级,范围为0.0到1.0。
  • VkDeviceCreateInfo结构体包含创建逻辑设备的信息。
6. 创建交换链

交换链(Swap Chain)是一个队列,包含一系列的图像,用于在屏幕上呈现。

VkSwapchainKHR swapChain;
std::vector<VkImage> swapChainImages;
VkFormat swapChainImageFormat;
VkExtent2D swapChainExtent;

void createSwapChain() {
    VkSwapchainCreateInfoKHR createInfo{};
    createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
    createInfo.surface = surface;

    VkSurfaceCapabilitiesKHR capabilities;
    vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &capabilities);

    createInfo.minImageCount = capabilities.minImageCount + 1;
    createInfo.imageFormat = VK_FORMAT_B8G8R8A8_SRGB;
    createInfo.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
    createInfo.imageExtent = capabilities.currentExtent;
    createInfo.imageArrayLayers = 1;
    createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;

    uint32_t queueFamilyIndices[] = {queueFamilyIndices.graphicsFamily.value(), queueFamilyIndices.presentFamily.value()};

    if (queueFamilyIndices[0] != queueFamilyIndices[1]) {
        createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
        createInfo.queueFamilyIndexCount = 2;
        createInfo.pQueueFamilyIndices = queueFamilyIndices;
    } else {
        createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
        createInfo.queueFamilyIndexCount = 0;
        createInfo.pQueueFamilyIndices = nullptr;
    }

    createInfo.preTransform = capabilities.currentTransform;
    createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
    createInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR;
    createInfo.clipped = VK_TRUE;
    createInfo.oldSwapchain = VK_NULL_HANDLE;

    if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) {
        throw std::runtime_error("failed to create swap chain!");
    }

    vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);
    swapChainImages.resize(imageCount);
    vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());

    swapChainImageFormat = createInfo.imageFormat;
    swapChainExtent = createInfo.imageExtent;
}

  • VkSwapchainCreateInfoKHR结构体包含创建交换链所需的信息。
  • vkGetPhysicalDeviceSurfaceCapabilitiesKHR获取表面的能力。
  • createInfo.imageFormatcreateInfo.imageColorSpace指定交换链图像的格式和颜色空间。
  • createInfo.imageExtent指定图像的分辨率。
  • createInfo.imageUsage指定图像的用途,此处为颜色附件。
  • createInfo.imageSharingMode指定图像共享模式。
  • vkCreateSwapchainKHR创建交换链。
7. 创建图像视图

图像视图(Image View)用于访问交换链中的图像。

std::vector<VkImageView> swapChainImageViews;

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];
        createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
        createInfo.format = swapChainImageFormat;
        createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
        createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
        createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
        createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
        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!");
        }
    }
}
  • VkImageViewCreateInfo结构体包含创建图像视图所需的信息。
  • createInfo.viewType指定图像视图的类型,此处为2D图像视图。
  • createInfo.format指定图像视图的格式。
  • createInfo.components指定颜色通道的映射。
  • createInfo.subresourceRange指定图像子资源范围,包括mip层和数组层。
  • vkCreateImageView创建图像视图。
8. 创建渲染通道

渲染通道(Render Pass)定义渲染过程中使用的附件(Attachments)。

VkRenderPass renderPass;

void createRenderPass() {
    VkAttachmentDescription colorAttachment{};
    colorAttachment.format = swapChainImageFormat;
    colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
    colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
    colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
    colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
    colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
    colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;

    VkAttachmentReference colorAttachmentRef{};
    colorAttachmentRef.attachment = 0;
    colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

    VkSubpassDescription subpass{};
    subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
    subpass.colorAttachmentCount = 1;
    subpass.pColorAttachments = &colorAttachmentRef;

    VkRenderPassCreateInfo renderPassInfo{};
    renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
    renderPassInfo.attachmentCount = 1;
    renderPassInfo.pAttachments = &colorAttachment;
    renderPassInfo.subpassCount = 1;
    renderPassInfo.pSubpasses = &subpass;

    VkSubpassDependency dependency{};
    dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
    dependency.dstSubpass = 0;
    dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
    dependency.srcAccessMask = 0;
    dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
    dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;

    renderPassInfo.dependencyCount = 1;
    renderPassInfo.pDependencies = &dependency;

    if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {
        throw std::runtime_error("failed to create render pass!");
    }
}
  • VkAttachmentDescription描述附件的格式和操作。
  • VkAttachmentReference定义子通道使用的附件。
  • VkSubpassDescription定义子通道及其附件。
  • VkRenderPassCreateInfo包含创建渲染通道的信息。
  • VkSubpassDependency定义子通道依赖关系,以确保正确的执行顺序。
  • vkCreateRenderPass创建渲染通道。
9. 创建帧缓冲

帧缓冲(Framebuffer)用于存储渲染过程中生成的图像。

std::vector<VkFramebuffer> swapChainFramebuffers;

void createFramebuffers() {
    swapChainFramebuffers.resize(swapChainImageViews.size());

    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!");
        }
    }
}
  • VkFramebufferCreateInfo结构体包含创建帧缓冲的信息。
  • 每个帧缓冲与交换链图像视图关联。
  • vkCreateFramebuffer创建帧缓冲。
10. 创建命令池和命令缓冲

命令池(Command Pool)用于管理命令缓冲(Command Buffer)的内存分配。命令缓冲存储要执行的绘制命令。

VkCommandPool commandPool;

void createCommandPool() {
    VkCommandPoolCreateInfo poolInfo{};
    poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
    poolInfo.queueFamilyIndex = 0;  // 这里应使用实际的图形队列族索引

    if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {
        throw std::runtime_error("failed to create command pool!");
    }
}
  • VkCommandPoolCreateInfo结构体包含创建命令池的信息。
  • queueFamilyIndex应设置为图形队列族的索引。
  • vkCreateCommandPool创建命令池。
std::vector<VkCommandBuffer> commandBuffers;

void createCommandBuffers() {
    commandBuffers.resize(swapChainFramebuffers.size());

    VkCommandBufferAllocateInfo allocInfo{};
    allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
    allocInfo.commandPool = commandPool;
    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!");
    }

    for (size_t i = 0; i < commandBuffers.size(); i++) {
        VkCommandBufferBeginInfo beginInfo{};
        beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;

        if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) {
            throw std::runtime_error("failed to begin recording command buffer!");
        }

        VkRenderPassBeginInfo renderPassInfo{};
        renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
        renderPassInfo.renderPass = renderPass;
        renderPassInfo.framebuffer = swapChainFramebuffers[i];
        renderPassInfo.renderArea.offset = {0, 0};
        renderPassInfo.renderArea.extent = swapChainExtent;

        VkClearValue clearColor = {0.0f, 0.0f, 0.0f, 1.0f};
        renderPassInfo.clearValueCount = 1;
        renderPassInfo.pClearValues = &clearColor;

        vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
        vkCmdEndRenderPass(commandBuffers[i]);

        if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) {
            throw std::runtime_error("failed to record command buffer!");
        }
    }
}
  • VkCommandBufferAllocateInfo结构体包含分配命令缓冲的信息。
  • vkAllocateCommandBuffers分配命令缓冲。
  • VkCommandBufferBeginInfo结构体包含开始记录命令缓冲的信息。
  • vkBeginCommandBuffer开始记录命令缓冲。
  • VkRenderPassBeginInfo结构体包含开始渲染通道的信息。
  • vkCmdBeginRenderPassvkCmdEndRenderPass命令分别开始和结束渲染通道。
  • vkEndCommandBuffer结束命令缓冲记录。

11. 绘制和呈现

最后,我们需要编写绘制和呈现图像的代码。

void drawFrame() {
    vkWaitForFences(device, 1, &inFlightFence, VK_TRUE, UINT64_MAX);
    vkResetFences(device, 1, &inFlightFence);

    uint32_t imageIndex;
    vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex);

    VkSubmitInfo submitInfo{};
    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;

    VkSemaphore waitSemaphores[] = {imageAvailableSemaphore};
    VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
    submitInfo.waitSemaphoreCount = 1;
    submitInfo.pWaitSemaphores = waitSemaphores;
    submitInfo.pWaitDstStageMask = waitStages;

    submitInfo.commandBufferCount = 1;
    submitInfo.pCommandBuffers = &commandBuffers[imageIndex];

    VkSemaphore signalSemaphores[] = {renderFinishedSemaphore};
    submitInfo.signalSemaphoreCount = 1;
    submitInfo.pSignalSemaphores = signalSemaphores;

    if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFence) != VK_SUCCESS) {
        throw std::runtime_error("failed to submit draw command buffer!");
    }

    VkPresentInfoKHR presentInfo{};
    presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;

    presentInfo.waitSemaphoreCount = 1;
    presentInfo.pWaitSemaphores = signalSemaphores;

    VkSwapchainKHR swapChains[] = {swapChain};
    presentInfo.swapchainCount = 1;
    presentInfo.pSwapchains = swapChains;
    presentInfo.pImageIndices = &imageIndex;

    vkQueuePresentKHR(presentQueue, &presentInfo);
}
  • vkWaitForFences等待前一帧的渲染完成。
  • vkAcquireNextImageKHR获取交换链中的下一个图像。
  • VkSubmitInfo结构体包含提交命令缓冲的信息。
  • vkQueueSubmit提交命令缓冲到图形队列。
  • VkPresentInfoKHR结构体包含呈现图像的信息。
  • vkQueuePresentKHR将图像呈现到屏幕上。

12. 主循环和清理

void mainLoop() {
    while (!glfwWindowShouldClose(window)) {
        glfwPollEvents();
        drawFrame();
    }

    vkDeviceWaitIdle(device);
}

void cleanup() {
    for (auto framebuffer : swapChainFramebuffers) {
        vkDestroyFramebuffer(device, framebuffer, nullptr);
    }

    vkDestroyCommandPool(device, commandPool, nullptr);

    for (auto imageView : swapChainImageViews) {
        vkDestroyImageView(device, imageView, nullptr);
    }

    vkDestroySwapchainKHR(device, swapChain, nullptr);
    vkDestroyDevice(device, nullptr);

    vkDestroySurfaceKHR(instance, surface, nullptr);
    vkDestroyInstance(instance, nullptr);

    glfwDestroyWindow(window);
    glfwTerminate();
}
  • mainLoop包含主循环,处理窗口事件并绘制帧。
  • cleanup函数销毁所有Vulkan对象并释放资源。

以上是Vulkan基础教程的详细步骤和代码实现。每个步骤都描述了如何设置和初始化Vulkan。接下来在第二部分,我们将继续深入探讨Vulkan的使用,包括图形管线的设置、着色器的编写、以及资源的管理。

 

标签:std,教程,VK,nullptr,createInfo,设置,device,Vulkan
From: https://blog.csdn.net/qq_54098120/article/details/140327011

相关文章

  • [分享] 最新麻豆MDYS14源码 油条视频 苹果CMS系统 附搭建教程
    简介:最新麻豆MDYS14源码油条视CMS系统附搭建教程基本介绍:后台增加自定义参数,对应会员升级页面,以及积分充值视频,演员,专题,收藏,会员系统模块齐全,支持子分类,不再只显示单一的主分类直接指定一个分类下视频为免费专区完整的卡密支付体系,无人看管,无需挂码。三个播放界面,未注册会......
  • Ollama完整教程:本地LLM管理、WebUI对话、Python/Java客户端API应用
    老牛同学在前面有关大模型应用的文章中,多次使用了Ollama来管理和部署本地大模型(包括:Qwen2、Llama3、Phi3、Gemma2等),但对Ollama这个非常方便管理本地大模型的软件的介绍却很少。目前,清华和智谱AI联合发布开源的GLM4-9B大模型也能支持Ollama进行本地部署了(本地部署GLM-4-9B清华......
  • UE4中OpenGLES和Vulkan特性开启
    OpenGLES在Android手机上使用OpenGLES的RHI来启动游戏[2022.06.14-10.28.57:996][0]LogAndroid:|AndroidPlatformMisc.cpp:2332|VulkanRHIwillNOTbeused:[2022.06.14-10.28.57:996][0]LogAndroid:|AndroidPlatformMisc.cpp:2335|**Vulkansupportisnotavailabl......
  • Creo工程图打印设置(一)
    pdf线宽问题打印笔table.pntpen1thickness0.015cmpen2thickness0.1cmpen3thickness0.01cmpen4thickness0.012cmpen5thickness0.01cmpen6thickness0.01cmpen7thickness0.01cmpen8thickness0.015cmCREO工程图导出的比例问题怎么解决coh......
  • and-design-vue设置dropdownClassName无效的问题
    样式发现是有插入的,但是没有生效,使用:deep也一样没有效果问题来源改组件是根app同级,所以使用:deep无效解决办法使用:global,需要加“!important”......
  • PHP环境集成面板使用教程
    “让天下没有难配的服务器环境!-phpStudy”phpStudy是一个PHP开发环境集成包,可用在本地电脑或者服务器上,该程序包集成最新的PHP/MySql/Apache/Nginx/Redis/FTP/Composer,一次性安装,无须配置即可使用,非常方便、好用!phpstudy2019年新推出的V8版本全新界面,支持最新php、mysql版......
  • spring clound @FeignClient @RequestHeader 设置token cookie
     publicMap<String,String>populateHeaders(){Map<String,String>headers=newHashMap();Stringcookie=this.getCookie();if(StringUtils.isNoneBlank(newCharSequence[]{cookie})){headers.put(&quo......
  • python项目导入上级目录设置”的setting.json是不是哪里还有错误呀?
    大家好,我是Python进阶者。一、前言前几天在Python白银交流群【王者级混子】问了一个Python代码处理的问题,问题如下:大佬们,我想问问我抄网上“vscode运行python项目导入上级目录设置”的setting.json是不是哪里还有错误呀?还是没法导入上级目录二、实现过程这里后来很快他自己找......
  • 【Stable Diffusion】(基础篇三)—— 关键词和参数设置
    提示词和文生图参数设置本系列笔记主要参考B站nenly同学的视频教程,传送门:B站第一套系统的AI绘画课!零基础学会StableDiffusion,这绝对是你看过的最容易上手的AI绘画教程|SDWebUI保姆级攻略_哔哩哔哩_bilibili本文主要讲解如何正确高效地使用合适的提示词来帮助完成AI绘......
  • 在unity中被攻击时无敌的设置
    publicclassInvincible:MonoBehaviour {publicSpriteRendererrender; publicColornormalColor; publicColorflashColor; publicintduration; publicboolisInvincible;publicIEnumeratorSetInvincibility(){  isInvincible=true;  for......