Introduction
Well, in previous parts we prepared almost all to draw something. Now I in brief will describe last needed classes - FrameBuffers
, Sync
and CommandBuffers
.
There will be the FrameBuffers
class first - the list of frame buffers to define attachements we will use, which ImageViews they will use and their dimensions as for spec. And the FrameBuffers class. It's created in the standard way with corresponding structure and Vulkan API function calls.
Also, there will be the Sync
class. It will contain a semaphore to check if the presentation is complete and a semaphore to check if rendering is complete. And a list of fences for command buffers. All's created in the standard way also.
And last one is CommandBuffers
class - a list of command buffers for each image view. Here we'll define clear values (color/depth stencil), viewport, scissors, etc. Created in the standard way.
Main Loop
And at last we can draw out first triangle with use of Kotlin Native. Earlier we added the helper function to create buffers for its vertices and indices:
triangle = VertexBuffer.Triangle(_physicalDevice!!, _logicalDevice!!)
The steps are the same as we would use if we coded in c++ :
- Update uniform buffers
- Acquire next image with
vkAcquireNextImageKHR
function and set presentation semaphore - Wait until the command buffer has finished execution with use of a fence
- Reset the fence
- Submit to the queue with the fence
- Present the current buffer to the swapchain and pass
So, it will look like this:
fun draw() {
_uniformBuffers!!.update()
if (VK_CHECK(
vkAcquireNextImageKHR(
_logicalDevice!!.device,
_swapchain!!.swapchain,
UINT64_MAX,
_sync!!.presentSemaphore,
null,
currentBuffer.ptr
)
)
) {
if (VK_CHECK(
vkWaitForFences(
_logicalDevice!!.device,
1u,
_sync!!.waitFences[currentBuffer.value.toInt()].ptr,
VK_TRUE.toUInt(),
UINT64_MAX
)
)
) {
if (VK_CHECK(
vkResetFences(
_logicalDevice!!.device,
1u,
_sync!!.waitFences[currentBuffer.value.toInt()].ptr
)
)
) {
memScoped {
val pipelineStageWaitFlags = alloc<VkPipelineStageFlagsVar>()
pipelineStageWaitFlags.value =
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
val submitInfo = alloc<VkSubmitInfo>().apply {
sType = VK_STRUCTURE_TYPE_SUBMIT_INFO
pWaitDstStageMask = pipelineStageWaitFlags.ptr
pWaitSemaphores = _sync!!.presentSemaphorePtr
waitSemaphoreCount = 1u
pSignalSemaphores = _sync!!.renderSemaphorePtr
signalSemaphoreCount = 1u
commandBufferCount = 1u
pCommandBuffers =
_commandBuffers!!
.drawCmdBuffers[currentBuffer.value.toInt()].ptr
}
if (VK_CHECK(
vkQueueSubmit(
_logicalDevice!!.deviceQueue,
1u,
submitInfo.ptr,
_sync!!.waitFences[currentBuffer.value.toInt()]
.value
)
)
) {
val swapchains = allocArray<VkSwapchainKHRVar>(1.toInt()) {
this.value = _swapchain!!.swapchain
}
val presentInfo = alloc<VkPresentInfoKHR>().apply {
sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR
pNext = null
swapchainCount = 1u
pSwapchains = swapchains
waitSemaphoreCount = 1u
pWaitSemaphores = _sync!!.renderSemaphorePtr
pImageIndices = currentBuffer.ptr
}
if (!VK_CHECK(vkQueuePresentKHR(_logicalDevice!!
.deviceQueue, presentInfo.ptr))) {
logError("Failed present queue")
}
} else {
logError("Failed submit queue")
}
}
} else {
logError("Failed fences reset")
}
} else {
logError("Failed fences wait")
}
} else {
logError("Failed aquire next image")
}
}
Results
And here is our triangle:
So we drew something with a stable fps (now it's workin only on Windows platform). Sure it's hard to say about a performance with such simple scene. But as POC we proved that it's possible to work with Vulkan API and Kotlin Native and it's quite easy. As a next step I'm planning to complete Linux platform, add ImGUI and try some complex scene. All changes will be available in the master branch.
History