3.2 缓冲区与图像资源 本节摘要:Vulkan 只有两类数据资产:缓冲是一维字节数组,图像是带格式与维度解释的存储。本节讲清两者的参数体系——用法位决定能被谁使用、图像的五维属性决定它是什么、视图决定它被看成什么——并完整走一遍离屏渲染目标的创建流程。 缓冲与图像的正式定义可以这样给:缓冲是未经解释的一维字节序列,图像是自带格式语义的多维存储。前者靠你在着色器里自行解释字节含义,后者由硬件按格式直接理解。两者都遵循 3.1 节的三步绑定,但图像多出五维属性与视图规则——这些差异决定了各自的适用战场,也是本节的主线。 一、缓冲:用法位即准入印章 VkBufferCreateInfo 只有四个核心字段:size、usage、sharingMode、flags。
本节摘要:Vulkan 只有两类数据资产:缓冲是一维字节数组,图像是带格式与维度解释的存储。本节讲清两者的参数体系——用法位决定能被谁使用、图像的五维属性决定它是什么、视图决定它被看成什么——并完整走一遍离屏渲染目标的创建流程。
缓冲与图像的正式定义可以这样给:缓冲是未经解释的一维字节序列,图像是自带格式语义的多维存储。前者靠你在着色器里自行解释字节含义,后者由硬件按格式直接理解。两者都遵循 3.1 节的三步绑定,但图像多出五维属性与视图规则——这些差异决定了各自的适用战场,也是本节的主线。
VkBufferCreateInfo 只有四个核心字段:size、usage、sharingMode、flags。其中 usage 是重点——它不是注释,而是准入印章:只有在使用位里声明过的能力,对应命令才会受理这个缓冲。漏写 TRANSFER_DST_BIT 的话,拷贝命令会当场退件;事后补用法的唯一办法是销毁重建(usage 不可变)。
| 用法位 | 授权的操作 | 典型资源 |
|---|---|---|
| TRANSFER_SRC / DST | 拷贝的源与目的 | 暂存缓冲、上传目标 |
| VERTEX / INDEX | 顶点装配读取 | 网格数据 |
| UNIFORM_TEXEL_BUFFER | 以格式化纹理视角读 | 常量数组 |
| STORAGE_TEXEL_BUFFER | 格式化读写 | 可写数据数组 |
| STORAGE_BUFFER | 字节级读写(SSBO) | 粒子、变换缓冲 |
| UNIFORM_BUFFER | 常量块(UBO) | 每对象参数 |
| SHADER_DEVICE_ADDRESS | 取设备地址供指针引用 | 光追场景数据 |
组合规则按需取交集:一张「先经暂存上传、再作顶点流、还要被计算着色器更新」的缓冲,就把三个对应位都写上。用法越多,驱动可选的放置方案越少,所以别无脑全开——每个位都要有真实的消费者。
图像的信息密度比缓冲高一个量级。VkImageCreateInfo 的字段可以归成五个维度:维度形态(imageType:一维、二维、三维)与 extent;格式(VkFormat 决定每个纹素的解释,颜色、深度、压缩格式各有清单);mip 层数(mipLevels,主图加各级缩小版);数组层(arrayLayers,立方体贴图占六层、阴影级联各占一层);样本数(samples,多重采样的采样点数)。
两个属性值得单独展开。tiling:OPTIMAL(默认)由驱动决定物理排布,采样与渲染最快,但 CPU 无法直接解释其内存内容;LINEAR 排布可预测、可映射直写,但性能差且支持面收窄——它基本只用于「CPU 直写的小型上传纹理」这类边缘场景。usage 位:与缓冲同理,且交互更严格——既要采样又要渲染写入的图像必须同时声明 SAMPLED 与 COLOR_ATTACHMENT 两位。

VkImageView 是资源的「取景框」:从完整图像里框出一段(mip 区间、层区间、面别),并声明解释方式。交换链图像、帧缓冲附件、着色器采样,消费的都是视图而不是裸图像。一图多视图是常态——上图的四个视图来自同一张 VkImage。
VkImageViewCreateInfo vi = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO}; vi.image = offscreenColor; // 3.1 节三步绑定后的资产 vi.viewType = VK_IMAGE_VIEW_TYPE_2D; vi.format = offscreenFormat; // 通常与图像一致,兼容时也可不同 vi.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; vi.subresourceRange.baseMipLevel = 0; vi.subresourceRange.levelCount = 5; // 只暴露前五级 mip vi.subresourceRange.baseArrayLayer = 0; vi.subresourceRange.layerCount = 1; VkImageView view; vkCreateImageView(device, &vi, NULL, &view);
aspectMask 是最容易错的一项:颜色图像用 COLOR 位,深度模板图像按需取 DEPTH、STENCIL 或两者;把深度图像的视图建成 COLOR 面是初学者高频错误,验证层的报错信息会直接点名。缓冲的对应物是 VkBufferView(把字节序列按格式解释成纹素数组),用得少,概念同源。
背景:后处理管线需要一张与屏幕等大、RGBA16F 格式、可采样可写入的离屏图像。初版实现采样正常但写入时验证层报「imageUsage 不含 COLOR_ATTACHMENT」。
操作:按五维体系重新过账。维度形态与 extent 对齐交换链尺寸;格式选 RGBA16F(查 vkGetPhysicalDeviceFormatProperties 确认支持 COLOR_ATTACHMENT 与 SAMPLED 两个特性位——格式不是万能的,每张格式在不同用法上要单独查询);用法位同时声明 COLOR_ATTACHMENT、SAMPLED、TRANSFER_DST(清屏与拷入用);mipLevels 取一(渲染目标无需 mipmap);样本数一;tiling 默认最优。然后按 3.1 的三步绑定显存,再为「作为附件用」与「作为采样用」分别建两个视图。
结果:后处理链正常工作。两个视图的分工让「写它」与「读它」各自拿到正确的取景框,渲染通道(第 4 章)的附件声明也直接引用了对应视图。
解读:这个案例浓缩了本节全部要点——格式要按用途查、用法位要按消费者开、视图按角色分裂。报错发生在写入阶段而不是创建阶段,说明用法位的核对发生在命令受理时,验证层的价值就是把这种「延迟爆炸」提前到当场。
变式:需要 mipmap 的常规纹理则相反:mipLevels 计满整条链,渲染只碰第零层视图,mip 生成用 vkCmdBlitImage 逐层搬迁(3.3 节实操)。同一套五维账本,不同资产算出不同配置。
最后给一张决策便签。要存原始字节(网格、索引、通用数据),用缓冲加对应用法位。要硬件过滤采样,用图像加 SAMPLED。要被管线渲染写入,用图像加附件位并配视图进帧缓冲。要 CPU 直写直读,优先考虑缓冲加主机可见内存,图像的线性排布是退路而不是捷径。把这段口诀记牢,多数资源设计题三十秒内可以定案。