原文链接:https://vulkan.lunarg.com/doc/sdk/1.2.131.2/windows/tutorial/html/07-init_uniform_buffer.html
创建 Uniform Buffer
这一章节的代码文件是 07-init_uniform_buffer.cpp
你在最近的示例中已经创建过缓冲区,不妨现在再处理另一个。
uniform buffer 是一个面向 shader 以只读方式访问的缓冲区,以使 shader 能够读取静态参数的数据。
这又是一个例子,这一步是你在 Vulkan 程序里必须处理的,在其他图形API里就不必做。在 GLES 里,你只要简单的调一下API来设置发送给 shader 的 uniform 变量的内容。但是在这,你必须分配内存并且填写。
生成 Uniform Data
示例使用 uniform buffer 来传递 MVP(Model-View-Projection) 矩阵给 shader ,好让 shader 可以使用矩阵来转换顶点。
示例里像这样来生成数据:
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 Buffer 对象
创建这个 buffer 和在前一个示例里你如何创建 depth buffer 非常相似,只是改变一下用法:
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 Buffer 内存
像 depth buffer 一样,你需要为 uniform buffer 显式地分配内存:
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(host) 能够访问它。
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT 要求主机(host)对内存的写入对设备(device)可见(反之亦然),而不需要刷新内存的缓存。这对程序来说能简单一点,因为不必再调用 vkFlushMappedMemoryRanges 和 vkInvalidateMappedMemoryRanges 来确保数据对 GPU 可见。
映射和设置 Uniform Buffer 内存
在上一个示例中,在你分配 depth buffer 内存时,你不需要初始化它的内容。这是因为 GPU 负责读写它。但是对于 uniform buffer,你需要填充你想让 shader 读取的数据。当前情况下,数据是 MVP 矩阵。为了让 CPU 能访问内存,你需要映射:
res = vkMapMemory(info.device, info.uniform_data.mem, 0, mem_reqs.size, 0,
(void **)&pData);
拷贝 MVP 到 uniform buffer 里然后再取消映射就是相当简单的操作了:
memcpy(pData, &info.MVP, sizeof(info.MVP));
vkUnmapMemory(info.device, info.uniform_data.mem);
现在你就可以取消映射,因为我们在画一个静态的画面,不需要再更新MVP矩阵。但是如果你计划扩展这个示例来改变视野,保持着映射更高效。
最后,把你刚刚分配的内存和 buffer 对象关联起来:
res = vkBindBufferMemory(info.device, info.uniform_data.buf,
info.uniform_data.mem, 0);
好了,完成了。