Vulkan【8】创建Uniform缓冲区

创建Uniform缓冲区

本节的代码是 07-init_uniform_buffer.cpp

Uniform缓冲区是一个访问只读着色器的缓冲区,以便着色器可以读取常量参数数据。

这是另一个你必须在Vulkan程序中执行的步骤。在GLES中,你只需要做一个API调用来设置发送给着色器的统一变量的内容。而在这里,你必须分配内存并将其填充。

设置 Uniform 数据

样例使用Uniform缓冲区将MVP(模型-视图-投影)矩阵传递给着色器,以便着色器可以使用它来转换每个顶点。

示例是这样设置的:

info.Projection = glm::perspective(glm::radians(45.0f), 1.0f, 0.1f, 100.0f);
info.View = glm::lookAt(
    glm::vec3(-5, 3, -10), // Camera is at (-5,3,-10), in World Space
    glm::vec3(0, 0, 0),    // and looks at the origin
    glm::vec3(0, -1, 0)    // Head is up (set to 0,-1,0 to look upside-down)
    );
info.Model = glm::mat4(1.0f);
// Vulkan clip space has inverted Y and half Z.
info.Clip = glm::mat4(1.0f,  0.0f, 0.0f, 0.0f,
                      0.0f, -1.0f, 0.0f, 0.0f,
                      0.0f,  0.0f, 0.5f, 0.0f,
                      0.0f,  0.0f, 0.5f, 1.0f);

info.MVP = info.Clip * info.Projection * info.View * info.Model;

注意,这里使用glm库来简化代码。info.MVP是一个4x4矩阵。

创建Uniform缓冲区对象

创建这个缓冲区与你在之前的示例中创建深度缓冲的方式非常相似,只是对用法进行了更改:

VkBufferCreateInfo buf_info = {};
buf_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
buf_info.pNext = NULL;
buf_info.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
buf_info.size = sizeof(info.MVP);
buf_info.queueFamilyIndexCount = 0;
buf_info.pQueueFamilyIndices = NULL;
buf_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
buf_info.flags = 0;
res = vkCreateBuffer(info.device, &buf_info, NULL, &info.uniform_data.buf);
assert(res == VK_SUCCESS);

分配Uniform 缓冲区内存

像深度缓冲一样,您需要显式地为Uniform缓冲区分配内存。

VkMemoryRequirements mem_reqs;
vkGetBufferMemoryRequirements(info.device, info.uniform_data.buf,
                              &mem_reqs);

VkMemoryAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
alloc_info.pNext = NULL;
alloc_info.memoryTypeIndex = 0;

alloc_info.allocationSize = mem_reqs.size;
pass = memory_type_from_properties(info, mem_reqs.memoryTypeBits,
                                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
                                       VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
                                   &alloc_info.memoryTypeIndex);

res = vkAllocateMemory(info.device, &alloc_info, NULL,
                       &(info.uniform_data.mem));

VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT表示内存应该被映射,以便CPU(主机)能够访问它。

VK_MEMORY_PROPERTY_HOST_COHERENT_BIT要求主机对内存的写入是可见的(反之亦然),而不需要刷新内存缓存。这只会让编程变得更简单,因为没有必要调用vkFlushMappedMemoryRangesvkInvalidateMappedMemoryRanges,以确保数据对GPU是可见的。

映射和设置Uniform 缓冲区内存

当您在先前的示例中分配深度缓冲区时,您不需要初始化深度缓冲内存的内容。这是因为GPU会负责阅读和编写它。但是在uniform 缓冲区,您需要用您希望着色器读取的数据填充它。在这里,数据是MVP矩阵。为了获得对内存的CPU访问,您需要映射它:

res = vkMapMemory(info.device, info.uniform_data.mem, 0, mem_reqs.size, 0,
                  (void **)&pData);

这是一个非常简单的事情,把MVP复制到统一的缓冲区中,然后把它解出来:

memcpy(pData, &info.MVP, sizeof(info.MVP));

vkUnmapMemory(info.device, info.uniform_data.mem);

您需要立即取消它的映射,因为像页表这样的内存映射机制的大小有限,特别是对于CPU和GPU都可见的内存。

最后,您将刚刚分配给缓冲对象的内存关联起来:

res = vkBindBufferMemory(info.device, info.uniform_data.buf,
                         info.uniform_data.mem, 0);

结束。

© Copyright 2016 LunarG, Inc