指令池

在创建指令缓冲对象之前,我们需要先创建指令池对象。指令池对象用于管理指令缓冲对象使用的内存,并负责指令缓冲对象的分配。我们添加了一个VkCommandPool成员变量到类中:

VkCommandPool commandPool;

添加一个叫做createCommandPool的函数,并在initVulkan函数中帧缓冲对象创建之后调用它:

void initVulkan() {
    createInstance();
    setupDebugCallback();
    createSurface();
    pickPhysicalDevice();
    createLogicalDevice();
    createSwapChain();
    createImageViews();
    createRenderPass();
    createGraphicsPipeline();
    createFramebuffers();
    createCommandPool();
}

        ...

void createCommandPool() {

}

指令池对象的创建只需要填写两个参数:

QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice);

VkCommandPoolCreateInfo poolInfo = {};
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily;
poolInfo.flags = 0; // Optional

指令缓冲对象在被提交给我们之前获取的队列后,被Vulkan执行。每个指令池对象分配的指令缓冲对象只能提交给一个特定类型的队列。在这里,我们使用的是绘制指令,它可以被提交给支持图形操作的队列。

有下面两种用于指令池对象创建的标记,可以提供有用的信息给Vulkan的驱动程序进行一定优化处理:

  • VK_COMMAND_POOL_CREATE_TRANSIENT_BIT:使用它分配的指令缓冲对象被频繁用来记录新的指令(使用这一标记可能会改变帧缓冲对象的内存分配策略)。

  • VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT:指令缓冲对象之间相互独立,不会被一起重置。不使用这一标记,指令缓冲对象会被放在一起重置。

对于我们的程序,我们只在程序初始化时记录指令到指令缓冲对象,然后在程序的主循环中执行指令,所以,我们不使用上面这两个标记。

if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {
    throw std::runtime_error("failed to create command pool!");
}

我们通过调用vkCreateCommandPool函数来完成指令池对象的创建。应用程序结束前我们需要清除创建的指令池对象:

void cleanup() {
vkDestroyCommandPool(device, commandPool, nullptr);

        ...
}