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;

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.

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);
}

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.

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

Popular Posts