Vulkan填坑学习Day20—暂存缓冲区

本文介绍Vulkan图形编程中使用暂存缓冲区提高性能的方法。通过创建两个顶点缓冲区,一个用于CPU访问,另一个用于图形卡设备local内存。利用缓冲区拷贝命令,将数据从暂存缓冲区高效传输到设备内存,实现图形数据的快速加载。

Vulkan 暂存缓冲区

顶点缓冲区现在已经可以正常工作,但相比于显卡内部读取数据,单纯从CPU访问内存数据的方式性能不是最佳的。最佳的方式是采用VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT标志位,通常来说用在专用的图形卡,CPU是无法访问的。在本章节我们创建两个顶点缓冲区。一个缓冲区提供给CPU-HOST内存访问使用,用于从顶点数组中提交数据,另一个顶点缓冲区用于设备local内存。我们将会使用缓冲区拷贝的命令将数据从暂存缓冲区拷贝到实际的图形卡内存中。
在这里插入图片描述

一、传输队列

缓冲区拷贝的命令需要队列簇支持传输操作,可以通过VK_QUEUE_TRANSFER_BIT标志位指定。好消息是任何支持VK_QUEUE_GRAPHICS_BIT 或者 VK_QUEUE_COMPUTE_BIT标志位功能的队列簇都默认支持VK_QUEUE_TRANSFER_BIT操作。这部分的实现不需要在queueFlags显示的列出。

如果需要明确化,甚至可以尝试为不同的队列簇指定具体的传输操作。这部分实现需要对代码做出如下修改:

  • 修改QueueFamilyIndices和findQueueFamilies,明确指定队列簇需要具备VK_QUEUE_TRANSFER标志位,而不是VK_QUEUE_GRAPHICS_BIT。
  • 修改createLogicalDevice函数,请求一个传输队列句柄。
  • 创建两个命令对象池分配命令缓冲区,用于向传输队列簇提交命令。
  • 修改资源的sharingMode为VK_SHARING_MODE_CONCURRENT,并指定为graphics和transfer队列簇。
  • 提交任何传输命令,诸如vkCmdCopyBuffer(本章节使用)到传输队列,而不是图形队列。

需要一些额外的工作,但是它我们更清楚的了解资源在不同队列簇如何共享的。

二、创建临时缓冲区

考虑到我们在本章节需要创建多个缓冲区,比较理想的是创建辅助函数来完成。新增函数createBuffer并将createVertexBuffer中的部分代码(不包括映射)移入该函数。

void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) {
   
   
    VkBufferCreateInfo bufferInfo = {
   
   };
    bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
    bufferInfo.size = size;
    bufferInfo.usage = usage;
    bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;

    if (vkCreateBuffer(device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) {
   
   
        throw std::runtime_error("failed to create buffer!");
    }

    VkMemoryRequirements memRequirements;
    vkGetBufferMemoryRequirements(device, buffer, &memRequirements);

    VkMemoryAllocateInfo allocInfo = {
   
   };
    allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
    allocInfo.allocationSize = memRequirements.size;
    all
Vulkan图形API中,创建渲染通道(Render Pass)和帧缓冲区(Frame Buffer)是构建图形管线的重要步骤。这两个步骤定义了渲染操作如何处理图像资源,以及如何将渲染结果绑定到具体的图像上。 ### 创建渲染通道 (Render Pass) 渲染通道描述了渲染操作中使用的图像类型、它们的用途以及如何处理它们的内容。创建渲染通道需要指定以下内容: 1. **附件描述(Attachment Descriptions)**:定义每个附件的格式、加载和存储操作、初始状态和最终布局。 2. **子通道(Subpasses)**:描述渲染操作的阶段,以及每个阶段使用的附件。 3. **依赖关系(Dependencies)**:定义子通道之间的同步和布局转换。 以下是一个简单的Vulkan渲染通道创建示例: ```cpp VkRenderPassCreateInfo renderPassInfo = {}; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; // 附件描述 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; // 最终布局用于呈现 renderPassInfo.attachmentCount = 1; renderPassInfo.pAttachments = &colorAttachment; // 子通道描述 VkSubpassDescription subpass = {}; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &colorAttachmentRef; // 附件引用 VkAttachmentReference colorAttachmentRef = {}; colorAttachmentRef.attachment = 0; colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &colorAttachmentRef; 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.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependency.srcAccessMask = 0; dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; renderPassInfo.dependencyCount = 1; renderPassInfo.pDependencies = &dependency; // 创建渲染通道 VkRenderPass renderPass; if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { throw std::runtime_error("failed to create render pass!"); } ``` ### 创建帧缓冲区 (Frame Buffer) 帧缓冲区将具体的图像资源绑定到渲染通道中定义的附件槽位。每个帧缓冲区通常与交换链中的一个图像相关联。 以下是一个简单的帧缓冲区创建示例: ```cpp std::vector<VkFramebuffer> swapChainFramebuffers; swapChainFramebuffers.resize(swapChainImageViews.size()); for (size_t i = 0; i < swapChainImageViews.size(); i++) { VkFramebufferCreateInfo framebufferInfo = {}; framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; framebufferInfo.renderPass = renderPass; framebufferInfo.attachmentCount = 1; framebufferInfo.pAttachments = &swapChainImageViews[i]; // 图像视图 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!"); } } ``` ### 渲染通道与帧缓冲区的关系 - **渲染通道**定义了渲染操作的整体结构,包括附件的格式、子通道的顺序以及子通道之间的依赖关系。 - **帧缓冲区**将具体的图像资源绑定到渲染通道中定义的附件槽位。每个帧缓冲区通常与交换链中的一个图像相关联,并在渲染操作中使用。 在记录命令时,使用`vkCmdBeginRenderPass`来启动渲染通道,并指定帧缓冲区作为参数。渲染通道的执行方式可以通过`VkSubpassContents`参数控制,例如`VK_SUBPASS_CONTENTS_INLINE`或`VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS` [^2]。 ### 示例:记录渲染通道命令 ```cpp VkRenderPassBeginInfo renderPassInfo = {}; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; renderPassInfo.renderPass = renderPass; renderPassInfo.framebuffer = swapChainFramebuffers[currentFrame]; 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(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); { // 在这里记录绘制命令 vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); vkCmdDraw(commandBuffer, 3, 1, 0, 0); } vkCmdEndRenderPass(commandBuffer); ``` ### 总结 - 渲染通道定义了渲染操作的结构和附件的行为。 - 帧缓冲区将具体的图像资源绑定到渲染通道中定义的附件槽位。 - 渲染通道和帧缓冲区Vulkan中渲染管线的关键组成部分,必须在渲染操作之前正确创建和配置。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值