Skip to content

Commit ec7d5a0

Browse files
authored
Merge pull request #353 from asuessenbach/21_index_buffer
Align docu in 03_Index_buffer.adoc and sources in 21_index_buffer.cpp
2 parents 76cd80e + b6bf6fa commit ec7d5a0

2 files changed

Lines changed: 77 additions & 64 deletions

File tree

attachments/21_index_buffer.cpp

Lines changed: 52 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,13 @@ struct Vertex
4040

4141
static vk::VertexInputBindingDescription getBindingDescription()
4242
{
43-
return {0, sizeof(Vertex), vk::VertexInputRate::eVertex};
43+
return {.binding = 0, .stride = sizeof(Vertex), .inputRate = vk::VertexInputRate::eVertex};
4444
}
4545

4646
static std::array<vk::VertexInputAttributeDescription, 2> getAttributeDescriptions()
4747
{
48-
return {
49-
vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32Sfloat, offsetof(Vertex, pos)),
50-
vk::VertexInputAttributeDescription(1, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, color))};
48+
return {{{.location = 0, .binding = 0, .format = vk::Format::eR32G32Sfloat, .offset = offsetof(Vertex, pos)},
49+
{.location = 1, .binding = 0, .format = vk::Format::eR32G32B32Sfloat, .offset = offsetof(Vertex, color)}}};
5150
}
5251
};
5352

@@ -122,7 +121,7 @@ class HelloTriangleApplication
122121

123122
static void framebufferResizeCallback(GLFWwindow *window, int width, int height)
124123
{
125-
auto app = static_cast<HelloTriangleApplication *>(glfwGetWindowUserPointer(window));
124+
auto app = reinterpret_cast<HelloTriangleApplication *>(glfwGetWindowUserPointer(window));
126125
app->framebufferResized = true;
127126
}
128127

@@ -243,7 +242,7 @@ class HelloTriangleApplication
243242
vk::DebugUtilsMessageSeverityFlagsEXT severityFlags(vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning |
244243
vk::DebugUtilsMessageSeverityFlagBitsEXT::eError);
245244
vk::DebugUtilsMessageTypeFlagsEXT messageTypeFlags(
246-
vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation);
245+
vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation);
247246
vk::DebugUtilsMessengerCreateInfoEXT debugUtilsMessengerCreateInfoEXT{.messageSeverity = severityFlags,
248247
.messageType = messageTypeFlags,
249248
.pfnUserCallback = &debugCallback};
@@ -285,6 +284,7 @@ class HelloTriangleApplication
285284
vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT>();
286285
bool supportsRequiredFeatures = features.template get<vk::PhysicalDeviceVulkan11Features>().shaderDrawParameters &&
287286
features.template get<vk::PhysicalDeviceVulkan13Features>().dynamicRendering &&
287+
features.template get<vk::PhysicalDeviceVulkan13Features>().synchronization2 &&
288288
features.template get<vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT>().extendedDynamicState;
289289

290290
// Return true if the physicalDevice meets all the criteria
@@ -323,12 +323,16 @@ class HelloTriangleApplication
323323
}
324324

325325
// query for required features (Vulkan 1.1 and 1.3)
326-
vk::StructureChain<vk::PhysicalDeviceFeatures2, vk::PhysicalDeviceVulkan11Features, vk::PhysicalDeviceVulkan13Features, vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT> featureChain = {
327-
{}, // vk::PhysicalDeviceFeatures2
328-
{.shaderDrawParameters = true}, // vk::PhysicalDeviceVulkan11Features
329-
{.synchronization2 = true, .dynamicRendering = true}, // vk::PhysicalDeviceVulkan13Features
330-
{.extendedDynamicState = true} // vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT
331-
};
326+
vk::StructureChain<vk::PhysicalDeviceFeatures2,
327+
vk::PhysicalDeviceVulkan11Features,
328+
vk::PhysicalDeviceVulkan13Features,
329+
vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT>
330+
featureChain = {
331+
{}, // vk::PhysicalDeviceFeatures2
332+
{.shaderDrawParameters = true}, // vk::PhysicalDeviceVulkan11Features
333+
{.synchronization2 = true, .dynamicRendering = true}, // vk::PhysicalDeviceVulkan13Features
334+
{.extendedDynamicState = true} // vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT
335+
};
332336

333337
// create a Device
334338
float queuePriority = 0.5f;
@@ -396,7 +400,10 @@ class HelloTriangleApplication
396400

397401
auto bindingDescription = Vertex::getBindingDescription();
398402
auto attributeDescriptions = Vertex::getAttributeDescriptions();
399-
vk::PipelineVertexInputStateCreateInfo vertexInputInfo{.vertexBindingDescriptionCount = 1, .pVertexBindingDescriptions = &bindingDescription, .vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size()), .pVertexAttributeDescriptions = attributeDescriptions.data()};
403+
vk::PipelineVertexInputStateCreateInfo vertexInputInfo{.vertexBindingDescriptionCount = 1,
404+
.pVertexBindingDescriptions = &bindingDescription,
405+
.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size()),
406+
.pVertexAttributeDescriptions = attributeDescriptions.data()};
400407
vk::PipelineInputAssemblyStateCreateInfo inputAssembly{.topology = vk::PrimitiveTopology::eTriangleList};
401408
vk::PipelineViewportStateCreateInfo viewportState{.viewportCount = 1, .scissorCount = 1};
402409

@@ -447,18 +454,30 @@ class HelloTriangleApplication
447454
commandPool = vk::raii::CommandPool(device, poolInfo);
448455
}
449456

457+
std::pair<vk::raii::Buffer, vk::raii::DeviceMemory> createBuffer(vk::DeviceSize size, vk::BufferUsageFlags usage, vk::MemoryPropertyFlags properties)
458+
{
459+
vk::BufferCreateInfo bufferInfo{.size = size, .usage = usage, .sharingMode = vk::SharingMode::eExclusive};
460+
vk::raii::Buffer buffer = vk::raii::Buffer(device, bufferInfo);
461+
vk::MemoryRequirements memRequirements = buffer.getMemoryRequirements();
462+
vk::MemoryAllocateInfo allocInfo{.allocationSize = memRequirements.size, .memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties)};
463+
vk::raii::DeviceMemory bufferMemory = vk::raii::DeviceMemory(device, allocInfo);
464+
buffer.bindMemory(*bufferMemory, 0);
465+
return {std::move(buffer), std::move(bufferMemory)};
466+
}
467+
450468
void createVertexBuffer()
451469
{
452-
vk::DeviceSize bufferSize = sizeof(vertices[0]) * vertices.size();
453-
vk::raii::Buffer stagingBuffer({});
454-
vk::raii::DeviceMemory stagingBufferMemory({});
455-
createBuffer(bufferSize, vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent, stagingBuffer, stagingBufferMemory);
470+
vk::DeviceSize bufferSize = sizeof(vertices[0]) * vertices.size();
471+
472+
auto [stagingBuffer, stagingBufferMemory] =
473+
createBuffer(bufferSize, vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent);
456474

457475
void *dataStaging = stagingBufferMemory.mapMemory(0, bufferSize);
458476
memcpy(dataStaging, vertices.data(), bufferSize);
459477
stagingBufferMemory.unmapMemory();
460478

461-
createBuffer(bufferSize, vk::BufferUsageFlagBits::eTransferDst | vk::BufferUsageFlagBits::eVertexBuffer, vk::MemoryPropertyFlagBits::eDeviceLocal, vertexBuffer, vertexBufferMemory);
479+
std::tie(vertexBuffer, vertexBufferMemory) =
480+
createBuffer(bufferSize, vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eTransferDst, vk::MemoryPropertyFlagBits::eDeviceLocal);
462481

463482
copyBuffer(stagingBuffer, vertexBuffer, bufferSize);
464483
}
@@ -467,34 +486,24 @@ class HelloTriangleApplication
467486
{
468487
vk::DeviceSize bufferSize = sizeof(indices[0]) * indices.size();
469488

470-
vk::raii::Buffer stagingBuffer({});
471-
vk::raii::DeviceMemory stagingBufferMemory({});
472-
createBuffer(bufferSize, vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent, stagingBuffer, stagingBufferMemory);
489+
auto [stagingBuffer, stagingBufferMemory] =
490+
createBuffer(bufferSize, vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent);
473491

474492
void *data = stagingBufferMemory.mapMemory(0, bufferSize);
475493
memcpy(data, indices.data(), (size_t) bufferSize);
476494
stagingBufferMemory.unmapMemory();
477495

478-
createBuffer(bufferSize, vk::BufferUsageFlagBits::eTransferDst | vk::BufferUsageFlagBits::eIndexBuffer, vk::MemoryPropertyFlagBits::eDeviceLocal, indexBuffer, indexBufferMemory);
496+
std::tie(indexBuffer, indexBufferMemory) =
497+
createBuffer(bufferSize, vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eTransferDst, vk::MemoryPropertyFlagBits::eDeviceLocal);
479498

480499
copyBuffer(stagingBuffer, indexBuffer, bufferSize);
481500
}
482501

483-
void createBuffer(vk::DeviceSize size, vk::BufferUsageFlags usage, vk::MemoryPropertyFlags properties, vk::raii::Buffer &buffer, vk::raii::DeviceMemory &bufferMemory)
484-
{
485-
vk::BufferCreateInfo bufferInfo{.size = size, .usage = usage, .sharingMode = vk::SharingMode::eExclusive};
486-
buffer = vk::raii::Buffer(device, bufferInfo);
487-
vk::MemoryRequirements memRequirements = buffer.getMemoryRequirements();
488-
vk::MemoryAllocateInfo allocInfo{.allocationSize = memRequirements.size, .memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties)};
489-
bufferMemory = vk::raii::DeviceMemory(device, allocInfo);
490-
buffer.bindMemory(bufferMemory, 0);
491-
}
492-
493502
void copyBuffer(vk::raii::Buffer &srcBuffer, vk::raii::Buffer &dstBuffer, vk::DeviceSize size)
494503
{
495504
vk::CommandBufferAllocateInfo allocInfo{.commandPool = commandPool, .level = vk::CommandBufferLevel::ePrimary, .commandBufferCount = 1};
496505
vk::raii::CommandBuffer commandCopyBuffer = std::move(device.allocateCommandBuffers(allocInfo).front());
497-
commandCopyBuffer.begin(vk::CommandBufferBeginInfo{.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit});
506+
commandCopyBuffer.begin({.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit});
498507
commandCopyBuffer.copyBuffer(*srcBuffer, *dstBuffer, vk::BufferCopy(0, 0, size));
499508
commandCopyBuffer.end();
500509
queue.submit(vk::SubmitInfo{.commandBufferCount = 1, .pCommandBuffers = &*commandCopyBuffer}, nullptr);
@@ -527,7 +536,8 @@ class HelloTriangleApplication
527536
{
528537
auto &commandBuffer = commandBuffers[frameIndex];
529538
commandBuffer.begin({});
530-
// Before starting rendering, transition the swapchain image to COLOR_ATTACHMENT_OPTIMAL
539+
540+
// Before starting rendering, transition the swapchain image to vk::ImageLayout::eColorAttachmentOptimal
531541
transition_image_layout(
532542
imageIndex,
533543
vk::ImageLayout::eUndefined,
@@ -555,9 +565,10 @@ class HelloTriangleApplication
555565
commandBuffer.setScissor(0, vk::Rect2D(vk::Offset2D(0, 0), swapChainExtent));
556566
commandBuffer.bindVertexBuffers(0, *vertexBuffer, {0});
557567
commandBuffer.bindIndexBuffer(*indexBuffer, 0, vk::IndexTypeValue<decltype(indices)::value_type>::value);
558-
commandBuffer.drawIndexed(indices.size(), 1, 0, 0, 0);
568+
commandBuffer.drawIndexed(static_cast<uint32_t>(indices.size()), 1, 0, 0, 0);
559569
commandBuffer.endRendering();
560-
// After rendering, transition the swapchain image to PRESENT_SRC
570+
571+
// After rendering, transition the swapchain image to vk::ImageLayout::ePresentSrcKHR
561572
transition_image_layout(
562573
imageIndex,
563574
vk::ImageLayout::eColorAttachmentOptimal,
@@ -590,11 +601,11 @@ class HelloTriangleApplication
590601
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
591602
.image = swapChainImages[imageIndex],
592603
.subresourceRange = {
593-
.aspectMask = vk::ImageAspectFlagBits::eColor,
594-
.baseMipLevel = 0,
595-
.levelCount = 1,
596-
.baseArrayLayer = 0,
597-
.layerCount = 1}};
604+
.aspectMask = vk::ImageAspectFlagBits::eColor,
605+
.baseMipLevel = 0,
606+
.levelCount = 1,
607+
.baseArrayLayer = 0,
608+
.layerCount = 1}};
598609
vk::DependencyInfo dependency_info = {
599610
.dependencyFlags = {},
600611
.imageMemoryBarrierCount = 1,

en/04_Vertex_buffers/03_Index_buffer.adoc

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -48,48 +48,50 @@ const std::vector<uint16_t> indices = {
4848
It is possible to use either `uint16_t` or `uint32_t` for your index buffer depending on the number of entries in `vertices`.
4949
We can stick to `uint16_t` for now because we're using less than 65535 unique vertices.
5050

51-
Just like the vertex data, the indices need to be uploaded into a `VkBuffer` for the GPU to be able to access them.
51+
Just like the vertex data, the indices need to be uploaded into a `vk::raii::Buffer` for the GPU to be able to access them.
5252
Define two new class members to hold the resources for the index buffer:
5353

5454
[,c++]
5555
----
56-
vk::raii::Buffer vertexBuffer = nullptr;
56+
vk::raii::Buffer vertexBuffer = nullptr;
5757
vk::raii::DeviceMemory vertexBufferMemory = nullptr;
58-
vk::raii::Buffer indexBuffer = nullptr;
59-
vk::raii::DeviceMemory indexBufferMemory = nullptr;
58+
vk::raii::Buffer indexBuffer = nullptr;
59+
vk::raii::DeviceMemory indexBufferMemory = nullptr;
6060
----
6161

6262
The `createIndexBuffer` function that we'll add now is almost identical to `createVertexBuffer`:
6363

6464
[,c++]
6565
----
66-
void initVulkan() {
66+
void initVulkan()
67+
{
6768
...
6869
createVertexBuffer();
6970
createIndexBuffer();
7071
...
7172
}
7273
73-
void createIndexBuffer() {
74-
vk::DeviceSize bufferSize = sizeof(indices[0]) * indices.size();
74+
void createIndexBuffer()
75+
{
76+
vk::DeviceSize bufferSize = sizeof(indices[0]) * indices.size();
7577
76-
vk::raii::Buffer stagingBuffer({});
77-
vk::raii::DeviceMemory stagingBufferMemory({});
78-
createBuffer(bufferSize, vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent, stagingBuffer, stagingBufferMemory);
78+
auto [stagingBuffer, stagingBufferMemory] =
79+
createBuffer(bufferSize, vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent);
7980
80-
void* data = stagingBufferMemory.mapMemory(0, bufferSize);
81-
memcpy(data, indices.data(), (size_t) bufferSize);
82-
stagingBufferMemory.unmapMemory();
81+
void *data = stagingBufferMemory.mapMemory(0, bufferSize);
82+
memcpy(data, indices.data(), (size_t) bufferSize);
83+
stagingBufferMemory.unmapMemory();
8384
84-
createBuffer(bufferSize, vk::BufferUsageFlagBits::eTransferDst | vk::BufferUsageFlagBits::eIndexBuffer, vk::MemoryPropertyFlagBits::eDeviceLocal, indexBuffer, indexBufferMemory);
85+
std::tie(indexBuffer, indexBufferMemory) =
86+
createBuffer(bufferSize, vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eTransferDst, vk::MemoryPropertyFlagBits::eDeviceLocal);
8587
86-
copyBuffer(stagingBuffer, indexBuffer, bufferSize);
88+
copyBuffer(stagingBuffer, indexBuffer, bufferSize);
8789
}
8890
----
8991

9092
There are only two notable differences.
9193
The `bufferSize` is now equal to the number of indices times the size of the index type, either `uint16_t` or `uint32_t`.
92-
The usage of the `indexBuffer` should be `VK_BUFFER_USAGE_INDEX_BUFFER_BIT` instead of `VK_BUFFER_USAGE_VERTEX_BUFFER_BIT`, which makes sense.
94+
The usage of the `indexBuffer` should be `vk::BufferUsageFlagBits::eIndexBuffer` instead of `vk::BufferUsageFlagBits::eVertexBuffer`, which makes sense.
9395
Other than that, the process is exactly the same.
9496
We create a staging buffer to copy the contents of `indices` to and then copy it to the final device local index buffer.
9597

@@ -103,21 +105,21 @@ It's unfortunately not possible to use different indices for each vertex attribu
103105
[,c++]
104106
----
105107
commandBuffers[frameIndex].bindVertexBuffers(0, *vertexBuffer, {0});
106-
commandBuffers[frameIndex].bindIndexBuffer( *indexBuffer, 0, vk::IndexType::eUint16 );
108+
commandBuffers[frameIndex].bindIndexBuffer(*indexBuffer, 0, vk::IndexType::eUint16);
107109
----
108110

109-
An index buffer is bound with `vkCmdBindIndexBuffer` which has the index buffer, a byte offset into it, and the type of index data as parameters.
110-
As mentioned before, the possible types are `VK_INDEX_TYPE_UINT16` and `VK_INDEX_TYPE_UINT32`.
111+
An index buffer is bound with `vk::raii::CommandBuffer::bindIndexBuffer` which has the index buffer, a byte offset into it, and the type of index data as parameters.
112+
As mentioned before, the possible types are `vk::IndexType::eUint16` and `vk::IndexType::eUint32`.
111113

112114
Just binding an index buffer doesn't change anything yet, we also need to change the drawing command to tell Vulkan to use the index buffer.
113-
Remove the `vkCmdDraw` line and replace it with `vkCmdDrawIndexed`:
115+
Remove the `vk::raii::CommandBuffer::draw` line and replace it with `vk::raii::CommandBuffer::drawIndexed`:
114116

115117
[,c++]
116118
----
117-
commandBuffers[frameIndex].drawIndexed(indices.size(), 1, 0, 0, 0);
119+
commandBuffer.drawIndexed(static_cast<uint32_t>(indices.size()), 1, 0, 0, 0);
118120
----
119121

120-
A call to this function is very similar to `vkCmdDraw`.
122+
A call to this function is very similar to `vk::raii::CommandBuffer::draw`.
121123
The first two parameters specify the number of indices and the number of instances.
122124
We're not using instancing, so just specify `1` instance.
123125
The number of indices represents the number of vertices that will be passed to the vertex shader.
@@ -133,7 +135,7 @@ You now know how to save memory by reusing vertices with index buffers.
133135
This will become especially important in a future chapter where we're going to load complex 3D models.
134136

135137
The previous chapter already mentioned that you should allocate multiple resources like buffers from a single memory allocation, but in fact you should go a step further.
136-
https://developer.nvidia.com/vulkan-memory-management[Driver developers recommend] that you also store multiple buffers, like the vertex and index buffer, into a single `VkBuffer` and use offsets in commands like `vkCmdBindVertexBuffers`.
138+
https://developer.nvidia.com/vulkan-memory-management[Driver developers recommend] that you also store multiple buffers, like the vertex and index buffer, into a single `vk::raii::Buffer` and use offsets in commands like `vk::raii::CommandBuffer::bindVertexBuffers`.
137139
The advantage is that your data is more cache friendly in that case, because it's closer together.
138140
It is even possible to reuse the same chunk of memory for multiple resources if they are not used during the same render operations, provided that their data is refreshed, of course.
139141
This is known as _aliasing_ and some Vulkan functions have explicit flags to specify that you want to do this.

0 commit comments

Comments
 (0)