天天看点

Vulkan编程指南翻译 第二章 第二节 资源

第二章 内存与资源 第二节 资源

Vulkan操纵数组。与之相比,其他东西重要性皆次之。数据被存储在resources中,resource存放在内存硬件中。Vulkan有两种基础的资源:buffers 与 images。Buffer是一块儿简单的、连续的数据,可以用来存储任何东西—数据结构,原生数组,甚至图像数据,你应当选择buffer。另一方面,Images,是结构化的,拥有类型信息,可以是多维的,自己也可组成数组,可以支持高级的读写操作。

两种类型的resource都是通过两个步骤完成构造的:一,创建resource,然后放在内存中。这么做的原因是允许应用程序自己来管理内存。内存管理比较复杂,由驱动来做就会比较困难,也许一个程序中工作正常,在其他的程序中就不工作。因此,应该是由应用程序来做内存管理,而不是驱动。例如,一个应用程序可以使用数量很少但数据量很大的资源,使用单独的内存分配器的管理策略,让它们常驻内存,然而,其他的程序可能需要不断的创建并销毁小数据量的资源。

即使images数据结构比较复杂,它被创建的过程和buffes类似。本节将先讲解buffer的创建,后讲解images。

Buffres

Buffers是Vulkan中最简单但使用非常广泛的资源类型。它通常被用来存储连续的结构化的或非结构化的数据,在内存中可以有格式或者原生的bytes。当我们将讨论到这些话题时,就会降到buffers对象的各种使用方式。创建buffer对象,需调用vkCreateBuffer(),原型如下:

VkResult vkCreateBuffer (

VkDevice  device,

const VkBufferCreateInfo*  pCreateInfo,

const VkAllocationCallbacks* pAllocator,

VkBuffer*  pBuffer); 

如同Vulkan中大多数函数,它有许多个参数,这些参数被打包进一个数据结构中,通过指针传到Vulkan。这里,pCreateInfo参数是指向一个instance的VkBufferCreateInfo成员的指针,它的定义如下:

typedef struct VkBufferCreateInfo {

VkStructureType  sType;

const void*  pNext;

VkBufferCreateFlags  flags;

VkDeviceSize  size;

VkBufferUsageFlags  usage;

VkSharingMode  sharingMode;

uint32_t  queueFamilyIndexCount;

const uint32_t* pQueueFamilyIndices;

} VkBufferCreateInfo;sType应当被设置为VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,pNext应被设置为nullptr—除非你想要使用拓展。flags告诉Vulkan有关Buffer的属性信息。在当前的Vulkan版本中,只能设置为sparse buffes相关的值,我们将在本章稍后部分讲解。暂时,flags被设置为0。

size设定了buffer的大小,以bytes为单位。usage告诉Vulkan你如何使用buffer,它只能被设置为VkBufferUsageFlagBits这个枚举类型的某一个值。在某些架构中,buffer的使用方式会影响到被创建的过程。本章中用到的设定值有下面几个:

l VK_BUFFER_USAGE_TRANSFER_SRC_BIT 、VK_BUFFER_USAGE_TRANSFER_DST_BIT 表示在转移commands过程中,可以被用来做源地址或者目的地。转移operatin会把数据从源地址copy到目标地址。第四章将会讲解。

l VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT、VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT表示buffer可被用来存储uniform或者storage texel。Texel buffer是存放像素的格式化的数组,可以被用作源地址或者目标地址,被运行在GPU上的shader读写。Texel Buffer将在第六章讲解。

l VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT 、 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT表示可以存储uniform或者storage buffer。和texel buffers相反,常规的uniform和storage buffers并没有格式,可用来存储任意的数据。我们在第六章“Shaders and Pipelines“中讲解。

l VK_BUFFER_USAGE_INDEX_BUFFER_BIT 、 VK_BUFFER_USAGE_VERTEX_BUFFER_BIT可以用来存放索引或者顶点数据,被绘制commands使用。在第八章”绘制“中,我们讲解绘制命令,包含索引化的绘制命令。

l VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT表示可以存储间接派发与绘制命令的参数,这些commands从buffers中直接获取参数,而不是从应用程序。在第六章和第八章会讲解。

VkBufferCreateInfo的sharingMode域,表示buffer将在GPU支持的多个command queues中如何被使用。因为Vulkan并行的执行多个operation,一些Vulkan实现需要知道buffer是被一个还是被多个command使用。设置sharingMode为VK_SHARING_MODE_EXCLUSIVE时,表明buffer只会被一个queue使用,设置为VK_SHARING_MODE_CONCURRENT时表示你计划在多个queue中同时使用这个buffer。使用VK_SHARING_MODE_CONCURRENT可能会导致在一些系统上低效率,所以,除非你的确需要才设置为这个值。

如果你真的设置为VK_SHARING_MODE_CONCURRENT,你需要告诉Vulkan哪些queue将使用这个buffer。这通过设置VkBufferCreateInfo,的pQueueFamilyIndices域来完成,这是一个指向queue families数组的指针。queueFamilyIndexCount是数组的长度,是将使用这个buffer的queue families的个数。当sharingMode被设置为VK_SHARING_MODE_EXCLUSIVE时,queueFamilyCount、pQueueFamilies都会被忽略。

Listing 2.3示例了如何创建一个1MB大小的buffer对象,可读可写,还有每次只被一个queue family使用。

Listing 2.3: Creating a Buffer Object

static const VkBufferCreateInfo bufferCreateInfo =

{

VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, nullptr,

0,

1024 * 1024,

VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,

VK_SHARING_MODE_EXCLUSIVE,

0, nullptr

};VkBuffer buffer = VK_NULL_HANDLE; 

vkCreateBuffer(device, &bufferCreateInfo, &buffer);

在Listing 2.3中的代码被运行后,一个新的VkBuffer的handle被创建,并且被赋值到buffer变量。这个buffer并不能被使用,因为,他首先需要被存放到内存,这个操作将在本章的”设备内存管理“小节中讲到。

格式 和 Support?

Buffer是相对简单的资源类型,存放的数据没有格式的概念,images和buffer views包含有他们内容的相关信息。部分信息描述了资源中数据的格式。一些格式对特定的管线中使用它们有特殊的要求或者限制。例如,一个格式可读但是不可写,这在压缩格式中很常见。

为了知道对各种格式的属性和supoort级别,你可以调用vkGetPhysicalDeviceFormatProperties(),原型如下:

void vkGetPhysicalDeviceFormatProperties (

VkPhysicalDevice  physicalDevice,

VkFormat  format,

VkFormatProperties*  pFormatProperties);

因为对物理设备而非逻辑设备特定格式的支持,物理设备的handle是被physicalDevice.指定的。如果应用程序需要完全支持某些格式,你可以在创建逻辑设备之前做检查,或者在应用程序启动时拒绝特定的物理设备。如果设备识别格式,它将把支持级别写到instance的VkFormatProperties类型的域pFormatProperties.。VkFormatProperties的定义如下:

typedef struct VkFormatProperties {

VkFormatFeatureFlags  linearTilingFeatures;

VkFormatFeatureFlags  optimalTilingFeatures;

VkFormatFeatureFlags  bufferFeatures;

} VkFormatProperties;VkFormatProperties的三个域都是位域,可选值由VkFormatFeatureFlagBits这个枚举中的某些值构成。一幅图像可以是单列或者是块状模式:线性的,就是在内存中连续的排列,先按行,再按照列排放;或者最优,图像数据以最优方案被排放,可以最高效率的利用显卡内存子系统。linearTilingFeatures域表示对一种格式线性铺排的支持级别,optimalTilingFeatures表示对图像优化铺排方式的支持级别,bufferFeatures表示对buffer使用的支持级别。

可选的bit值定义如下:

l VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT 这种格式被 shader采样的只读图像使用

l VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT 过滤模式,包含线性过滤,这种格式用于被采用的image

l VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT: 这种格式被shader可读写的images使用

l VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT: 这种格式被 支持shader的atomic操作的可读写images使用。

l VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT: 这种格式被shader只读的texel buffer使用

l VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT:此格式 被shader读写的texel buffer使用

l VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT:此格式被 支持shader原子操作的texel buffers使用

l VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT:此格式被 管线的顶点组装阶段用作源地址的顶点数据使用

l VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT:此格式被管线的颜色混合阶段用作颜色附件使用

l VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT:当blending被启用时,有此格式的images可被用作颜色附件

l VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT:此格式可被深度、挡板,或者深度-挡板 附件使用

l VK_FORMAT_FEATURE_BLIT_SRC_BIT: 此格式被用作image拷贝操作的源数据使用

l VK_FORMAT_FEATURE_BLIT_DST_BIT: 此格式被用作image拷贝操作的目的数据使用

许多格式是几种状态位的组合。实际上,许多格式被强制支持。在Vulkan技术规范文档中,有一个完整的“必须支持“格式列表。如果一种格式在此列表中,那么它不需要被强制去测试是否支持。然而,完了完整性,各种Vulkan实现被期待会准确的向Vulkan委员会汇报对各种格式,甚至是必须支持格式的兼容性。

vkGetPhysicalDeviceFormatProperties()函数会返回一个粗糙的结果集,告诉我们一个格式是否可被用在特定的场景。对images来说,一个特定格式和对images不同支持级别之间的相互影响会复杂多了。因此,当用于images时,为了更多的获取对格式的支持信息,你可以调用vkGetPhysicalDeviceImageFormatProperties(),原型如下:

VkResult vkGetPhysicalDeviceImageFormatProperties (

VkPhysicalDevice  physicalDevice,

VkFormat  format,

VkImageType  type,

VkImageTiling  tiling,

VkImageUsageFlags  usage,

VkImageCreateFlags  flags,

VkImageFormatProperties*  pImageFormatProperties);

与vkGetPhysicalDeviceFormatProperties(),类似,vkGetPhysicalDeviceImageFormatProperties()接受一个VkPhysicalDevice handle作为第一个参数,反馈物理设备而非逻辑设备对某个格式的支持结果。你查询的这个格式通过format参数传递。

你想询问的image的类型通过type指定。它应当是images 类型中的某一个:VK_IMAGE_TYPE_1D,VK_IMAGE_TYPE_2D,VK_IMAGE_TYPE_3D。不同的image类型也许有不同的限制条件和增强措施。Image的Tiling模式是通过tiling参数指定的,值为VK_IMAGE_TILING_LINEAR或者VK_IMAGE_TILING_OPTIMAL,表示线性或者最优铺排。

Image的类型是通过usage参数指定的。这个位域表明image就如何被使用。如何使用images将在本章稍后讲到。flags参数应当被设置为和创建image时同样。

如果这个格式被识别并且被Vulkan的实现所支持,那么支持级别将会写入到一个VkImageFormatProperties类型的数据结构pImageFormatProperties. VkImageFormatProperties的定义如下:

typedef struct VkImageFormatProperties {

VkExtent3D maxExtent;

uint32_t  maxMipLevels;

uint32_t  maxArrayLayers;

VkSampleCountFlags  sampleCounts;

VkDeviceSize  maxResourceSize;

} VkImageFormatProperties;VkImageFormatProperties的成员extent报告了某个格式的image在创建时最大尺寸的大小。例如,每个像素占位(bit)更少的格式比占位更多的格式,可以创建更大的image。Extent是VkExtent3D类型的数据,定义如下:

typedef struct VkExtent3D {

uint32_t  width;

uint32_t  height;

uint32_t  depth;

} VkExtent3D;

maxMipLevels域,报告了一个 指定格式的image可以支持的最大mipmap个数。多数情况下,maxMipLevels要么报告log2 (max (extent.x, extent.y, extent.z)),要么 1 表示不支持mipmap。

maxArrayLayers域报告了image支持的array layers最大数量。再次强调,如果arrays被支持,或不支持时为1,这几可能会是一个非常大的数字。

如果 image格式支持多重采样,那么支持的采样数通过sampleCounts获得,这是yige位字段,每一位都表示支持的采样数量。如果n被设置,那么支持2n 采样的images就能够被这种格式支持。如果格式完全支持,这个位字段至少有一个位是被设置过的。几乎不可能看到支持多重采样但不支持单采样的格式。

最后,maxResourceSize域,指定了这个格式的资源,以byte为单位最大的大小。此域不应当和maxExtent混淆了。maxExtent表示了支持的每一个维度的最大值。比如,某个Vulkan实现表示可以支持16,384 × 16,384 pixels × 2,048 layers的image,每个像素128 bits。那么,以每个维度的最大值来穿件图像,将会产生8TB的数据。该Vulkan实现几乎不会真的实现支持创建8TB的图像。然而,它可能支持创建n 8 × 8 × 2,048 array or a 16,384 × 16,284 nonarray 图像,两者都可以放到合适的内存块中。

Images

Images比buffers更加复杂,因为他们是多维的;有独特的布局和格式信息;即可被用作过滤、混合、深度或者stencil测试等操作的源地址 或者是目的地址。可以用vkCreateImage()函数创建images,原型如下:

VkResult vkCreateImage (

VkDevice  device,

const VkImageCreateInfo*  pCreateInfo,

const VkAllocationCallbacks* pAllocator,

VkImage* pImage);

被用来创建image的的设备,通过device参数被传入。Image相关的信息通过一个数据结构pCreateInfo被传入。它是一个VkImageCreateInfo类型 数据的指针,定义如下:

typedef struct VkImageCreateInfo {

VkStructureType  sType;

const void*  pNext;

VkImageCreateFlags  flags;

VkImageType  imageType;

VkFormat  format;

VkExtent3D  extent;

uint32_t  mipLevels;

uint32_t  arrayLayers;

VkSampleCountFlagBits  samples;

VkImageTiling  tiling;

VkImageUsageFlags  usage;

VkSharingMode  sharingMode;

uint32_t  queueFamilyIndexCount;

const uint32_t*  pQueueFamilyIndices;

VkImageLayout  initialLayout;

} VkImageCreateInfo;

你可以看到,这是一个比VkBufferCreateInfo显著复杂的数据结构。常见的域,sType、 pNext,在最前面,与其他的多数Vulkan内部数据结构类似。sType域应当被设置为VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO。flags域包含描述image的属性信息。可以从VkImageCreateFlagBits这个枚举中选择几个值来构造。前面三个-- VK_IMAGE_CREATE_SPARSE_BINDING_BIT, VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT, and VK_IMAGE_CREATE_SPARSE_ALIASED_BIT –是用来控制稀疏图像的,在本章稍后讲解。

如果被设置为VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT,那么你可以为image创建具有不同格式view。Image views(image视图)基本上是一种特殊的图像,它可以和父图像共享数据和布局,但是可以表现出不同的格式。这就允许了image的数据在同时被不同的方式解读。使用图像视图,就可以使用一份数据创建多个iamge。本章稍后讲解。如果被设置为VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT,那么你可以创建立方纹理视图。立方纹理视图于本章稍后讲解。

imageType 域指定了你想创建的图像的类型。图像类型基本上就是图像的维度,可选值有VK_IMAGE_TYPE_1D, VK_IMAGE_TYPE_2D, VK_IMAGE_TYPE_3D,分别表示1D,2D,3D。

图像也有格式,它描述了像素数据是如何在内存在存放的,并且是如何被Vulkan读取的。图像的格式是通过formats指定的,而且必须是由VkFormat这个枚举的值组成的。Vulkan支持很大数量的格式—在这里无法列出。我们在本书中使用某些作为例子并加以讲解。剩下的格式,请参考Vulkan文档。

图像的extent是指以像素为单位的大小。通过extent域指定,它是VkExtent3D类型的数据,有width, height, depth三个成员。他们应当被设置为目标图像的宽度,高度和深度。对于1D图像,高度是1,对于1D、2D图像,depth需被设置为1。相较于把下一维度当作数组的大小,Vulkan显式的指定 数组大小,通过arrayLayers.指定。

能够创建的图像的大小依赖于每个GPU设备。想要获取这个最大数值,可调用vkGetPhysicalDeviceFeatures()并且检查内嵌的VkPhysicalDeviceLimits结构的 maxImageDimension1D, maxImageDimension2D, 和 maxImageDimension3D。maxImageDimension1D包含一维图像的最大宽度,maxImageDimension2D、maxImageDimension3D都包含最后一维的最大值。同样的,maxImageArrayLayers包含了image可拥有的层数的最大值。

如果这是一张立方纹理,那么最后一位的最大值就被存储在maxImageDimensionCube。maxImageDimension1D, maxImageDimension2D, 和 maxImageDimensionCube 都被保证不小于4096像素,且maxImageDimensionCube和maxImageArrayLayers被保证不小于256。如果想要创建的图像比这些维数的最大值小,那么就无需检查硬件的特征。进一步来讲,Vulkan实现一般都会支持远高于最低标准的规格。所以,我们可以合理的假定可以创建更大的图像,而不是为了适配低端设备做容错处理。

mipLevels.指定了mipmap的层数。Mipmapping是为了提高降采样的质量而预先提供的一组被过滤过的图片。这些图片以金字塔的形式组成了mipmap的各个层级。

Vulkan编程指南翻译 第二章 第二节 资源

在一个mipmapped 贴图中,基础级别是最小号码的级别(通常是0),并且拥有贴图的原装尺寸。后继的级别依次有上一层的一半大小,直到某个维度的尺寸变为一个像素。从mipmaped的贴图进行采用将在第六章讲解。

同样的,samples指定了采样的次数。这个域与其他的不大相似。它必须是VkSampleCountFlagBits这个枚举中的某个值,是一个bit定义、用在位域的变量。然而,在现在的Vulkan中,只有2的幂采样数才能被使用,意味着它们只用1表示含义,所以,按位的枚举值就可以工作了。

余下的几个域描述了图像就会被如何使用。首先是tiling模式,通过tiling域指定。这是一个VkImageTiling枚举类型的变量,只有VK_IMAGE_TILING_LINEAR、 VK_IMAGE_TILING_OPTIMAL这两个选项。Linear tiling表示图像数据从左到右,从上到下被存放,如果你映射到底层内存或者通过CPU写入,他将形成线性的图像。同时,优化tiling是不透明的表示方式,由Vulkan在内存中存放,使GPU的内存子系统更高效的访问。这是你多数时候的选项,除非你打算用CPU来操作数据。对于绝大多数操作,优化tiling会比线性tiling表现的显著高效,而且线性tiling也不被一些操作和格式所支持,这取决于Vulkan的具体实现者。

usage 也是位域变量,描述了图像在哪儿被使用。这和VkBufferCreateInfo的usage类似。这里的usage由VkImageUsageFlags枚举值形成,如下:

l VK_IMAGE_USAGE_TRANSFER_SRC_BIT 、 VK_IMAGE_USAGE_TRANSFER_DST_BIT表示图像图像操作的源地址和目标地址。图像转移命令将在第四章讲解。

l VK_IMAGE_USAGE_SAMPLED_BIT 表示图像可以被shader采样

l VK_IMAGE_USAGE_STORAGE_BIT表示图像可以被用作通用存储,包括shader的写入

l VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT表示图像可以绑定一个颜色附件并且用绘制操作卷入。Framebuffers和它们的附件在第七章绘制管线中讲解。

l VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT表示图像可以绑定一个深度或者stencil附件,且用作深度或者stencil测试。深度或者stencil操作在第十章“像素处理“中讲解。

l VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT表示图像可以被用作临时附件,是一种特殊的图像,被用于存储绘制操作的中间结果。临时附件在第十三章”多遍渲染“中讲解。

l VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT表示图像可被用作渲染过程中的特殊输入。输入图像和常规菜呀或存储的图像不一样的点在于fragment shader可以读取他们的像素点。输入输入附件在第十三章。

sharingMode 与本章早一些提到的VkBufferCreateInfo结构的同名成员,在功能上是相同的。若被设置为VK_SHARING_MODE_EXCLUSIVE,这个图像将在某个时刻只能被一个队列使用。若设置为VK_SHARING_MODE_CONCURRENT,,那么图像可以同时被多个queue访问到。同样,当sharingMode 被设置为VK_SHARING_MODE_CONCURRENT. 时,queueFamilyIndexCount和pQueueFamilyIndices与上面小节的功能描述相同。

最后,图像有布局(layout),在某种程度上指定了在任意时刻它将会被如何使用。initialLayout域决定了图像以哪种布局被创建。VkImageLayout这个枚举类型定义了可用的布局方式,他们如下:

l VK_IMAGE_LAYOUT_UNDEFINED:

l VK_IMAGE_LAYOUT_GENERAL:

l VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:

l VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:

l VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:

l VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:

l VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:

l VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:

l VK_IMAGE_LAYOUT_PREINITIALIZED:

l VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:

图像可从一个布局转移到另外一个,我们将会在相关章节讲到不同的布局。然而,图像初始创建时,只能是VK_IMAGE_LAYOUT_UNDEFINED或者VK_IMAGE_LAYOUT_PREINITIALIZED布局。当你在内存中有数据并且迅速绑定到图像资源时,才使用VK_IMAGE_LAYOUT_PREINITIALIZED。当你在使用前,计划把资源转移到另外一个布局,应当使用VK_IMAGE_LAYOUT_UNDEFINED。任何时候,当图像被移出VK_IMAGE_LAYOUT_UNDEFINED layout时,几乎没有性能消耗。

改变图像布局的机制也被称为”管线壁垒“,或”壁垒“。一个壁垒,不仅仅是改变资源布局的途径,也可以用来同步Vulkan管线不同阶段,甚至在一个GPU设备上同时运行的队列,对该资源的访问。由此,管线壁垒相当的复杂,正确的使用它并不简单。管线壁垒在第四章深度讲解。

Listing 2.4 展示了一个简单的图像资源创建的例子

Listing 2.4: Creating an Image Object

VkImage image = VK_NULL_HANDLE;

VkResult result = VK_SUCCESS;

static const VkImageCreateInfo imageCreateInfo =

{

VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,  // sType

nullptr, // pNext

0,  // flags

VK_IMAGE_TYPE_2D, // imageType

VK_FORMAT_R8G8B8A8_UNORM,  // format

{ 1024, 1024, 1 },  // extent

10,  // mipLevels

1,  // arrayLayers

VK_SAMPLE_COUNT_1_BIT, // samples

VK_IMAGE_TILING_OPTIMAL, // tiling

VK_IMAGE_USAGE_SAMPLED_BIT,  // usage

VK_SHARING_MODE_EXCLUSIVE,  // sharingMode

0,  // queueFamilyIndexCount

nullptr,  // pQueueFamilyIndices

VK_IMAGE_LAYOUT_UNDEFINED  // initialLayout

};

result = vkCreateImage(device, &imageCreateInfo, nullptr, &image);

在Listing 2.4中创建的图像是一个1,024 × 1,024 2D图像,单采样,VK_FORMAT_R8G8B8A8_UNORM格式,最优tiling。代码以未定义布局创建了它,这表示我们可以把它转移到另外一个地方兵器用数据填充它。这个图像将会被作为贴图使用被shader访问,所以,我们设置usage为VK_IMAGE_USAGE_SAMPLED_BIT。在我们简单的应用程序中,我们只是用单一队列,所以,我们设置共享模式为exclusive.

线性图像

如之前章节讨论过的,各种资源都有两种tiling模式:VK_IMAGE_TILING_LINEAR and VK_IMAGE_TILING_OPTIMAL。VK_IMAGE_TILING_OPTIMAL是一种不透明,实现方式各异的布局,是为了提高设备内存子系统对图像的读写性能。然而,VK_IMAGE_TILING_LINEAR是一种不可见(transparent)的数据布局方式,是为了足够的直观,在图像内部,像素以从左到右,从上倒下的方式布局,几乎不可能把数据映射到资源并让CPU直接的读写。

如果想让CPU可以访问潜在的图像数据,除了图像的宽度,高度,深度,像素格式,还有几个其他的信息是必需的:图像的row pitch--; array pitch – 不同array layer之间的距离; depth pitch – 深度切片间的距离。当然,array pitch 和 depth pitch分别应对于array 或 3D 图像,row pitch只可用于2D和3D图像。

一个图像通常是由几个子资源组成的。一些格式有多于一个的aspect,是类似于深度图像的深度或者stencil 成分的一种成分。一个image下不同的子资源的布局可能是不同的,因此有不同的布局信息。这个信息可调用vkGetImageSubresourceLayout()来查询,原型如下:

void vkGetImageSubresourceLayout (

VkDevice device,

VkImage image,

const VkImageSubresource* pSubresource,

VkSubresourceLayout* pLayout);

被查询图像所在的设备通过device参数传递过去,查询的图像通过image参数传递。子资源的信息通过一个VkImageSubresource类型的指针pSubresource,传递回来,定义如下:

typedef struct VkImageSubresource {

VkImageAspectFlags  aspectMask;

uint32_t  mipLevel;

uint32_t  arrayLayer;

} VkImageSubresource;