Vulkan Tutorial in C - 014 - Dynamic Vertex Buffer Part 2
Capture Mouse Input
Right after the "Process Windows' messages" section of our code, let’s capture the mouse position and whether the mouse left button is pressed. This will allow us to demonstrate the dynamic nature of our new shiny vertex buffer:
POINT mousePos;
// Get cursor position relative to the screen
GetCursorPos(&mousePos);
// Convert to window-relative coordinates
ScreenToClient(vk.window, &mousePos);
f32 mx = mousePos.x - 0.5f * s;
f32 my = mousePos.y - 0.5f * s;
bool mouseLeftDown = GetAsyncKeyState(VK_LBUTTON) & 0x8000;
// Get cursor position relative to the screen
GetCursorPos(&mousePos);
// Convert to window-relative coordinates
ScreenToClient(vk.window, &mousePos);
f32 mx = mousePos.x - 0.5f * s;
f32 my = mousePos.y - 0.5f * s;
bool mouseLeftDown = GetAsyncKeyState(VK_LBUTTON) & 0x8000;
Update Dynamic Vertex Buffer Data
Next, right next to it, let’s write some quick and dirty vertex data generation code based on that mouse input. This is pretty ugly, but it’s the quickest way I could think of to demonstrate our dynamic data:
u32 dynamicVertexCount = 0;
if (mouseLeftDown)
{
dynamicVertexCount = 12;
f32 x = 100; // displacement in x
f32 dynamicVertices[] =
{
mx + 0, my + 0, 0, 0,
mx + s, my + 0, 1, 0,
mx + s, my + s, 1, 1,
mx + 0, my + 0, 0, 0,
mx + s, my + s, 1, 1,
mx + 0, my + s, 0, 1,
// Second quad
x + 0, 0, 0, 0,
x + s, 0, 1, 0,
x + s, s, 1, 1,
x + 0, 0, 0, 0,
x + s, s, 1, 1,
x + 0, s, 0, 1,
};
memcpy(dynamicVertBufferMemoryMap, dynamicVertices, sizeof(dynamicVertices));
}
else
{
dynamicVertexCount = 6;
f32 dynamicVertices[] =
{
mx + 0, my + 0, 0, 0,
mx + s, my + 0, 1, 0,
mx + s, my + s, 1, 1,
mx + 0, my + 0, 0, 0,
mx + s, my + s, 1, 1,
mx + 0, my + s, 0, 1
};
memcpy(dynamicVertBufferMemoryMap, dynamicVertices, sizeof(dynamicVertices));
}
This madman's code might look bizarre, but it's actually quite simple. It's copying two quads to our dynamic vertex buffer's memory, or just a single quad, depending on whether the mouse is pressed or not. In both cases, the first quad is positioned at the mouse cursor.if (mouseLeftDown)
{
dynamicVertexCount = 12;
f32 x = 100; // displacement in x
f32 dynamicVertices[] =
{
mx + 0, my + 0, 0, 0,
mx + s, my + 0, 1, 0,
mx + s, my + s, 1, 1,
mx + 0, my + 0, 0, 0,
mx + s, my + s, 1, 1,
mx + 0, my + s, 0, 1,
// Second quad
x + 0, 0, 0, 0,
x + s, 0, 1, 0,
x + s, s, 1, 1,
x + 0, 0, 0, 0,
x + s, s, 1, 1,
x + 0, s, 0, 1,
};
memcpy(dynamicVertBufferMemoryMap, dynamicVertices, sizeof(dynamicVertices));
}
else
{
dynamicVertexCount = 6;
f32 dynamicVertices[] =
{
mx + 0, my + 0, 0, 0,
mx + s, my + 0, 1, 0,
mx + s, my + s, 1, 1,
mx + 0, my + 0, 0, 0,
mx + s, my + s, 1, 1,
mx + 0, my + s, 0, 1
};
memcpy(dynamicVertBufferMemoryMap, dynamicVertices, sizeof(dynamicVertices));
}
This demonstrates that we can not only update the position of our quad every frame but also draw a different number of quads based on the game’s state.
Execute Graphics Pipeline function
Now, the last thing we need to do is execute both pipelines with our command buffer, where before we were executing just one. To handle this, let's create one final helper function. In it, we'll move the code we previously had, from vkCmdBindPipeline to vkCmdDraw. Place that right above WinMain:
void
vk_execute_graphics_pipeline(VkCommandBuffer *commandBuffer,
VkPipelineLayout *layout,
VkPipeline *pipeline,
VkDescriptorSet descSet,
VkBuffer *vertexBuffer,
u32 vertexCount)
{
// Bind the Graphics Pipeline
vkCmdBindPipeline(*commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
*pipeline);
// Bind the Descriptor Set
VkDescriptorSet descSets[] = { descSet };
vkCmdBindDescriptorSets(*commandBuffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
*layout, 0,
array_count(descSets),
descSets,
0, NULL);
// Bind the Vertex Buffer
VkDeviceSize offsets[] = { 0 };
VkBuffer vertexBuffers[] = { *vertexBuffer };
vkCmdBindVertexBuffers(*commandBuffer, 0,
array_count(vertexBuffers),
vertexBuffers,
offsets);
// Draw vertices
vkCmdDraw(*commandBuffer, vertexCount, 1, 0, 0);
}
vk_execute_graphics_pipeline(VkCommandBuffer *commandBuffer,
VkPipelineLayout *layout,
VkPipeline *pipeline,
VkDescriptorSet descSet,
VkBuffer *vertexBuffer,
u32 vertexCount)
{
// Bind the Graphics Pipeline
vkCmdBindPipeline(*commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
*pipeline);
// Bind the Descriptor Set
VkDescriptorSet descSets[] = { descSet };
vkCmdBindDescriptorSets(*commandBuffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
*layout, 0,
array_count(descSets),
descSets,
0, NULL);
// Bind the Vertex Buffer
VkDeviceSize offsets[] = { 0 };
VkBuffer vertexBuffers[] = { *vertexBuffer };
vkCmdBindVertexBuffers(*commandBuffer, 0,
array_count(vertexBuffers),
vertexBuffers,
offsets);
// Draw vertices
vkCmdDraw(*commandBuffer, vertexCount, 1, 0, 0);
}
Execute Graphics Pipelines
Finally, after vkCmdBeginRenderPass, we now have our two function calls: one for the static pipeline and one for the dynamic pipeline.
vk_execute_graphics_pipeline(&graphicsCommandBuffer,
&staticPipelineLayout,
&staticGraphicsPipeline,
descSet,
&vertexBuffer,
6); // vertexCount
vk_execute_graphics_pipeline(&graphicsCommandBuffer,
&dynamicPipelineLayout,
&dynamicGraphicsPipeline,
descSet,
&dynamicVertBuffer,
dynamicVertexCount);
And that’s it! This marks the end of yet another tutorial in our series. As usual you can find the full source code here.&staticPipelineLayout,
&staticGraphicsPipeline,
descSet,
&vertexBuffer,
6); // vertexCount
vk_execute_graphics_pipeline(&graphicsCommandBuffer,
&dynamicPipelineLayout,
&dynamicGraphicsPipeline,
descSet,
&dynamicVertBuffer,
dynamicVertexCount);
At this point, we can pretty much start writing our game if we want. However, there are still a few things we can improve.
Next, we'll be adding index buffers to the app to reduce the number of vertices in our vertex buffers. We’ll also finally tackle window resizing.
See you in the next one!
Comments
Post a Comment