Code file for this section is 13-init_vertex_buffer.cpp
A vertex buffer is a CPU-visible and GPU-visible buffer that contains the vertex data that describes the geometry of the object(s) you wish to render. In general, the vertex data consists of position (x,y,z) data and the optional color, normal, or other information. Like other 3D APIs, the approach here is to fill the buffer with this vertex data and pass it to the GPU during a draw operation.
Creating a vertex buffer is nearly the same as creating a uniform buffer, and it begins with creating a buffer object, as you did in the uniform sample.
VkBufferCreateInfo buf_info = {};
buf_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
buf_info.pNext = NULL;
buf_info.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
buf_info.size = sizeof(g_vb_solid_face_colors_Data);
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.vertex_buffer.buf);
The only real difference between creating an uniform buffer object and a vertex buffer
object is the setting of the usage
field.
The cube data (g_vb_solid_face_colors_Data
) consists of 36 vertices that
define 12 triangles, 2 on each of the 6 cube faces.
Each triangle also has a face color associated with it.
You can inspect the cube_data.h
file to see the actual data.
Again, the steps are very similar to the steps taken to allocate the uniform buffer. First make a query to get the memory requirements, which include taking into account machine restrictions such as alignment. Look at the code in the sample to see that the process is very similar to the steps taken in the uniform sample.
Once allocated, the memory is mapped, initialized with the vertex data, and unmapped:
uint8_t *pData;
res = vkMapMemory(info.device, info.vertex_buffer.mem, 0,
mem_reqs.size, 0, (void **)&pData);
memcpy(pData, g_vb_solid_face_colors_Data,
sizeof(g_vb_solid_face_colors_Data));
vkUnmapMemory(info.device, info.vertex_buffer.mem);
As a final step, the memory is bound to the buffer object:
res = vkBindBufferMemory(info.device, info.vertex_buffer.buf,
info.vertex_buffer.mem, 0);
The sample lays out the vertex data in the following arrangement:
struct Vertex {
float posX, posY, posZ, posW; // Position data
float r, g, b, a; // Color
};
You need to create a vertex input binding to describe the data arrangement to the GPU.
The following vi_binding
and vi_attribs
members are set up here, but
are used later in a subsequent sample as part of creating the graphics pipeline.
But since you are looking at the vertex data format, it is good to examine this
code here:
info.vi_binding.binding = 0;
info.vi_binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
info.vi_binding.stride = sizeof(g_vb_solid_face_colors_Data[0]);
info.vi_attribs[0].binding = 0;
info.vi_attribs[0].location = 0;
info.vi_attribs[0].format = VK_FORMAT_R32G32B32A32_SFLOAT;
info.vi_attribs[0].offset = 0;
info.vi_attribs[1].binding = 0;
info.vi_attribs[1].location = 1;
info.vi_attribs[1].format = VK_FORMAT_R32G32B32A32_SFLOAT;
info.vi_attribs[1].offset = 16;
The stride
is the size of one vertex, or the amount needed to add to a pointer to
get to the next vertex.
The binding
and location
members refer to their respective values in the GLSL
shader source code.
You can review the shader source code in the shaders sample to see the correspondence.
Even though the first attribute is position data, a 4-byte float color format
is used to describe it in the format
member for attribute 0.
The format
for attribute 1 is more clearly a color format, since attribute 1 is a color.
The offset
members are straightforward indicators of where each attribute is found in the
vertex data.
You can skip over most of the code that sets up the render pass since you will see it in a later sample. But for now, find the code that binds the vertex buffer to the render pass:
vkCmdBeginRenderPass(info.cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindVertexBuffers(info.cmd, 0, /* Start Binding */
1, /* Binding Count */
&info.vertex_buffer.buf, /* pBuffers */
offsets); /* pOffsets */
vkCmdEndRenderPass(info.cmd);
Note that you can only connect the vertex buffer with a render pass only inside of
a render pass; that is, between a vkCmdBeginRenderPass
and a vkCmdEndRenderPass
while
recording a command buffer.
This essentially tells the GPU what vertex buffer to use when drawing.
Framebuffers | Index | Pipeline |