Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile / Android

Vulkan API with Kotlin Native - Draw

5.00/5 (1 vote)
26 Apr 2019GPL32 min read 6.6K  
Vulkan API with Kotlin Native. Drawing loop.

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:
 

Kotlin
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:

Kotlin
    fun draw() {

        _uniformBuffers!!.update()

        // wait for a success or error

        if (VK_CHECK(
                vkAcquireNextImageKHR(
                    _logicalDevice!!.device,
                    _swapchain!!.swapchain,
                    UINT64_MAX,
                    _sync!!.presentSemaphore,
                    null,
                    currentBuffer.ptr
                )
            )
        ) {

            // wait until the command buffer has finished execution 
            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 {

                        // Pipeline stage for the queue to wait

                        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
                        }

                        // Submit to the graphics queue 
                        if (VK_CHECK(
                                vkQueueSubmit(
                                    _logicalDevice!!.deviceQueue,
                                    1u,
                                    submitInfo.ptr,
                                    _sync!!.waitFences[currentBuffer.value.toInt()]
                                    .value
                                )
                            )
                        ) {

                            // Present the current buffer to the swap chain

                            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:

 

Image 1

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

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)