创建图像
译者注:示例代码点击此处
图像表示可以具有一维、二维或三维的数据,并且可以具有额外的mipmap级别和图层。图像数据的每个元素(纹理元素)也可以具有一个或多个样本。
图像可用于许多不同的目的。我们可以将它们用作复制操作的数据源。也可以通过描述符集将图像绑定到管线,并将它们用作纹理(类似于OpenGL)。可以渲染成图像,在这种情况下我们使用图像作为颜色或深度附件(渲染目标)。
我们在图像创建期间指定图像参数,例如大小、格式及其预期用途。
做好准备...
1.获取我们要在其上创建图像的逻辑设备句柄。将其储存在名为logical_device的VkDevice类型变量中。
2.选择图像类型(如果图像应具有一维、二维或三维)并使用适当的值初始化名为type的VkImageType类型变量。
3.选择图像格式,每个图像元素应包含的组件数和位数。将格式储存在名为format的VkFormat类型变量中。
4.选择图像的大小(尺寸)并使用它来初始化名为size的VkExtent3D类型变量。
5.选择应为图像定义的mipmap级别数量储存在名为num_mipmaps的uint32_t类型变量中。
6.选择应为图像定义的图层数量,并将其存储在名为num_layers的uint32_t类型变量中。如果图像将用作立方体贴图,则图层数量必须是6的倍数。
7.创建一个名为samples的VkSampleCountFlagBits类型的变量,并使用表示样本数量的值对其进行初始化。
8.选择预期的图像用法,在名为usage_scenarios的VkImageUsageFlags类型的变量中定义它们。
9.创建名为image_create_info的VkImageCreateInfo类型变量。为其成员赋值:
·sType为VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO
·pNext为nullptr
·flags的话,如果图像应该用作立方体贴图,请使用VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT值,否则使用0值。
·type为imageType
·format为format
·extent为size
·mipLevels为num_mipmaps
·arrayLayers为num_layers
·samples为samples
·tiling为VK_IMAGE_TILING_OPTIMAL
·usage为usage_scenarios
·sharingMode为VK_SHARING_MODE_EXCLUSIVE
·queueFamilyIndexCount为0
·pQueueFamilyIndices为nullptr
·initialLayout为VK_IMAGE_LAYOUT_UNDEFINED
10.创建一个名为image的VkImage类型变量。将创建图像句柄存储其中。
11.调用vkCreateImage( logical_device, &image_create_info, nullptr, &image ),提供逻辑设备句柄、指向image_create_info的指针、null值以及指向image变量的指针。
12.确保vkCreateImage调用返回的值等于VK_SUCCESS。
怎么做...
当我们想要创建图像时,需要准备多个参数:图像的类型、尺寸(大小),组建的数量以及每个组建的位数(格式)。我们还需要知道图像是否包含mipmap或者它是否包含多个图层(普通图像必须包含至少一个,而立方图图像必须至少包含六个)。还必须考虑预期的使用场景,这些场景也是在图像创建过程中定义的。我们不能以一种在创建的过程中未定义的方式使用图像。
提示:图像只能用于创建期间指定的用途(目的)。
以下是可以使用图像的目的列表:
·VK_IMAGE_USAGE_TRANSFER_SRC_BIT指定图像可用作复制操作的数据源
·VK_IMAGE_USAGE_TRANSFER_DST_BIT指定我们可以将数据赋值到图像
·VK_IMAGE_USAGE_SAMPLED_BIT表示我们可以从着色器内的图像中采集数据
·VK_IMAGE_USAGE_STORAGE_BIT指定图像可以用作着色器内的存储图像
·VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT指定我们可以渲染到图像中(将其用作帧缓冲区中的颜色渲染目标/附件)
·VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT表示图像可以用做深度和/或模板缓冲区(作为帧缓冲区中的深度渲染目标/附件)
·VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT表示绑定的图像的内存将被懒惰的分配(按需)
·VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT指定图像可用作着色器内的输入附件
不同的使用场景需要使用不同的图像布局。这些更改(转换)需使用图像内存屏障。但是在创建过程中,我们只能指定VK_IMAGE_LAYOUT_UNDEFINED (如果我们不关心初始化内容)或VK_IMAGE_LAYOUT_PREINITIALIZED(如果我们想通过映射主机可见内存来上传数据),并且在实际使用之前总是需要转换到另一个布局。
译者注:从上面的提示来理解用途是必须在创建期间指定的,但是这里有一句这些更改(转换)需使用图像内存屏障。看样子图像布局是可以在之后使用过程中改变的,而用途不是,这里很容易引起歧义,需要注意!
所有图像参数都通过VkImageCreateInfo类型的变量指定,如下所示:
VkImageCreateInfo image_create_info = {
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
nullptr,
cubemap ? VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT : 0u,
type,
format,
size,
num_mipmaps,
cubemap ? 6 * num_layers : num_layers,
samples,
VK_IMAGE_TILING_OPTIMAL,
usage_scenarios,
VK_SHARING_MODE_EXCLUSIVE,
0,
nullptr,
VK_IMAGE_LAYOUT_UNDEFINED,
};
当我们创建图像时,还需要指定平铺(tiling)。它定义了图像的内存结构。有两种可用的图像拼贴类型:线性和最佳。
当使用线性平铺(linear tiling)时,顾名思义图像的数据以线性方式在内存中,类似于缓冲区或c/c++数组。这准许我们映射图像的内存,并从应用程序中直接读取或初始化它,因此我们知道内存是如何组织的。不幸的是,它限制我们将图像用于许多目的;例如,我们不能将图像用作深度纹理或立方体贴图(某些驱动程序可能支持它,但规范并不要求它,一般来说我们不应该依赖它)。线性平铺会降低我们应用的性能。
提示:为获得最佳性能,简易使用最佳屏幕创建图像。
具有最佳屏幕(optimal tiling)的图像可用于所有目的;他们也有更好的表现。但这需要权衡,我们不知道图像的内存是如何组织的。在下图中,我们可以看到图像数据的示例及其内部结构:
每种类型的图像硬件都可以以对其最佳的不同方式存储图像数据。因此我们无法映射图像的内存并直接从应用程序初始化或读取它。在这种情况下,我们需要使用暂存资源(staging resources)。当准备好后,可以使用以下代码创建一个图像:
VkResult result = vkCreateImage( logical_device, &image_create_info, nullptr, &image );
if( VK_SUCCESS != result ) {
std::cout << "Could not create an image." << std::endl;
return false;
}
return true;