Anda di halaman 1dari 9

27/03/2019 Vulkan API with Kotlin Native - Project Setup - CodeProject

Vulkan API with Kotlin Native - Project Setup


Igor Kushnarev, 27 Mar 2019

Using Vulkan API with Kotlin Native example.

Introduction
Source Code

By this article I want to start a series telling how to work with Vulkan API using Kotlin Native. What is Kotlin Native? It supposed to
compile your code to binaries not requiring Java VM or any other VM. So, it supposed to be platform independent code (except
parts that really platform dependant). Kotlin Native can produce arm32, arm64, x86_64, wasm32, etc. We're interested in arm and
x86 binaries. Kotlin Native can work with c and c++ libraries via so-called cinterop. It's still in development stage so there are some
difficulties with it to work. First of all there are no IDE that has a good support for it. What I tried: IntelliJ Clion trial version, I could
create a project, but I could not get a working multiplatform one. Android Studio - works fine for Android but not for other
platforms. IntelliJ IDEA community edition - the only one that worked with some limitations. And no one of them could debug a
project. So it would be nice to have some help with Vulkan debug layers and RenderDoc. The compile process is not quite fast for
now. And some other issues... I' ll tell about their workarounds during this article. Anyway it seems to be a good choice to use
alongside with c++ to develop graphics, math, science and so on libraries when it becomes mature. At first we will work with
Windows and Linux platforms and at the end of the series we'll add Android support. As for Vulkan API I would recommend good
examples in c++ by Sascha Willems or for beginners the set from Kronos:

At first a couple of quotes:

If you are interested in Kotlin/Native because of performance, you really should choose something else. Kotlin/Native is
native not for performance. It is for situations, where you can’t afford a JVM (like an iOS app) and want to share logic
between platforms, or just write clean readable code. Kotlin/JVM is faster than K/N, and Rust is faster than both.

ark1JetBrains Team

If we compare development time and time to market performance I would say that Kotlin/Native shall be way faster :).
Jokes aside, it really depends on workloads, for truly heavy computational workloads like image processing and heavy
crypto I would suggest to use neither language, and rely on C. For tasks Kotlin/Native was intended for, performance shall
be acceptable or even brilliant, especially if measure not raw throughput, but perceived performance.

Nikolay_Igotti Kotlin team

As you will see in this project Kotlin is a good language. It's easy to learn. It's easy to use. And I very hope both Jetbrain team and
Kotlin team will change their minds concerning Kotlin Native performance and Kotlin Native will evolve this way.

So, what we can do with Kotlin/Native:      

Compile to arm and x86 platform. All variants are available from presets 
https://www.codeproject.com/Articles/1288159/Vulkan-API-with-Kotlin-Native-Project-Setup?display=Print 1/9
27/03/2019 Vulkan API with Kotlin Native - Project Setup - CodeProject

Produce executable   
Produce static or dynamic library   
Produce Apple framework   
Use external static/dynamic libraries or Apple framework   

 And what we can't:

Debug project    
Use macroses from c/c++ part  
I didn' t find a way to check if we're in debug mode or at runtime 
No intrinsic functions or inline assembly - only in C/C++ part.
No coroutines yet 

Let' s begin...   

Tools
IntelliJ IDEA  
Vulkan SDK  
RenderDoc
MINGW for Windows 
Android Studio  
Android NDK
For Linux - build essentials, gcc-multilib, g++-multilib, xcb, xkb, xkbcommon, xkbcommon-x11, xcb-xkb, Xxf86vm,  xkb
extensions, Mesa libraries

Setting up the project


In IntelliJ IDEA create a new " Native" project (New project -> Kotlin -> Kotlin Native and check use "Use Auto-Import" so it
won' t ask every time a gradle file being changed). Don' t use the " Multiplatform" one! With it you won 't be able to
use common native code. IDE will create a project with a target depending on the platform you' re in. Don' t worry we'll add all
needed platforms manually using project gradle file. If you're on Windows platform you will see sample package and SampleMingw
file containing main function, refactor them to names you wish, for our project I will use k(Kotlin)v(Vulkan)arc  abriaviature for
package and MainMingw for main Windows file. For other platforms follow the same steps as for Windows just replacing
"sample" and  " platform" names.

Let' s take a look at project gradle file. Change yellowed "args '' " in runProgram block to "args []" so it won' t irritate us
anymore and rename it to runReleaseLinuxProgram . Then copy/paste this block rename it to runDebugLinuxProgram and change
builldType to "DEBUG". We will work with this. 

Now if you will open "Gradle panel" at the left of IDE you will see two tasks, that we can run: 

runDebugLinuxProgram 
runReleaseLinuxProgram  

https://www.codeproject.com/Articles/1288159/Vulkan-API-with-Kotlin-Native-Project-Setup?display=Print 2/9
27/03/2019 Vulkan API with Kotlin Native - Project Setup - CodeProject

Right click it and run. Now you can hav a cup of coffe until cinerops are done. Luckily it's just onse, after that IDE will use alredy
compiled binaries. As a result you will see " Hello, Kotlin/Native!" as a result in " Run" tab at the bottom of IDE:  

To access Vulkan SDK include files and libraries add " VULKAN_SDK_ROOT" environment variable and for Windows
"MINGW_ROOT" environment variable also. Set their values to corresponding folders. Now we can use them in our gradle file:

// MINGW and Vulkan SDK paths

def ENV = System.getenv()


project.ext.set("MINGW", ENV['MINGW_ROOT'])
project.ext.set("VULKAN", ENV['VULKAN_SDK_ROOT'])

https://www.codeproject.com/Articles/1288159/Vulkan-API-with-Kotlin-Native-Project-Setup?display=Print 3/9
27/03/2019 Vulkan API with Kotlin Native - Project Setup - CodeProject

Now we can add interop with C libraries. For this create nativeInterop/cinterop folder in the project source folder and add following
files:

Lvulkan.def (for Linux) - here we' ll add all needed include file and define xkb_event as we can't add union in Kotlin
 

package = vulkan
headers = vulkan/vulkan.h X11/Xlib.h X11/Xatom.h X11/keysym.h X11/extensions/xf86vmode.h X11/XKBlib.h
X11/extensions/XKBproto.h stdio.h stdlib.h locale.h unistd.h termios.h xcb/xkb.h xkbcommon/xkbcommon-
x11.h xkbcommon/xkbcommon-compose.h

---

union xkb_event {

struct {
uint8_t response_type;
uint8_t xkbType;
uint16_t sequence;
xcb_timestamp_t time;
uint8_t deviceID;
} any;

xcb_xkb_new_keyboard_notify_event_t new_keyboard_notify;
xcb_xkb_map_notify_event_t map_notify;
xcb_xkb_state_notify_event_t state_notify;

};

vulkan.def (for Windows)   

package = vulkan
headers = vulkan/vulkan.h

And global.def (to pass data between threads, taken from Kotlin Native samples. )

package = kvn.global

---

typedef struct {
void* kotlinObject;
} SharedDataStruct;

SharedDataStruct sharedData;

Now to use them change the gradle file as following - we' ll add kotlin targets sections:

For Linux

configure([linux]) {

// Comment to generate Kotlin/Native library (KLIB) instead of executable file:


compilations.main.outputKinds('EXECUTABLE')
// fully qualified name of the application's entry point:
compilations.main.entryPoint = 'kvarc.main'

compilations.main.linkerOpts "-L${project.VULKAN}lib -L/usr/lib/x86_64-linux-gnu -lVkLayer_threading


-lVkLayer_core_validation -lVkLayer_object_tracker -lVkLayer_parameter_validation -
lVkLayer_unique_objects -lvulkan -lX11 -lxcb -lxkbcommon -lxkbcommon-x11 -lxcb-xkb -lXxf86vm -lxcb"

compilations.main.cinterops {
lvulkan {
includeDirs "/usr/include", "${project.VULKAN}/include"
compilerOpts "-DVK_USE_PLATFORM_XCB_KHR"
}
global
}
}
https://www.codeproject.com/Articles/1288159/Vulkan-API-with-Kotlin-Native-Project-Setup?display=Print 4/9
27/03/2019 Vulkan API with Kotlin Native - Project Setup - CodeProject

For Windows

configure([mingw]) {

// Comment to generate Kotlin/Native library (KLIB) instead of executable file:


compilations.main.outputKinds('EXECUTABLE')
// Fully qualified name of the application's entry point:
compilations.main.entryPoint = 'kvarc.main'

compilations.main.linkerOpts "-L${project.VULKAN}lib -lvulkan-1 -lVkLayer_threading -


lVkLayer_core_validation -lVkLayer_object_tracker -lVkLayer_parameter_validation -
lVkLayer_unique_objects"

compilations.main.cinterops {
vulkan {
compilerOpts "-DVK_USE_PLATFORM_WIN32_KHR"
includeDirs "${project.MINGW}\\include", "${project.VULKAN}\\include"
}
global
}
}

What's just happened. In "outputKinds" we said that wee need an executable file. We the program entry pount to "kvarc.main"
function - "kvarc" is the package name like in Java. In "linkerOpts" we set a path to libraries and libraries needed.

Now if you try to run program it will crash while loading libraries. On Windows just add a path to them to the PATH variable and on
Linux add to LD_LIBRARY_PATH.
After that let' s do some more modifications to the gradle build file. But first let's add shaders to the project. Add shaders folder
to the project and add triangle vertex and fragment shaders there (thanks to Sascha Willems):

Vertex shader:

#version 450

layout (location = 0) in vec3 inPos;


layout (location = 1) in vec3 inColor;

layout (binding = 0) uniform UBO


{
mat4 projectionMatrix;
mat4 modelMatrix;
mat4 viewMatrix;
} ubo;

layout (location = 0) out vec3 outColor;

out gl_PerVertex
{
vec4 gl_Position;
};

void main()
{
outColor = inColor;
gl_Position = ubo.projectionMatrix * ubo.viewMatrix * ubo.modelMatrix * vec4(inPos.xyz, 1.0);
}

Fragment shader:

#version 450

layout (location = 0) in vec3 inColor;

layout (location = 0) out vec4 outFragColor;

void main()
{
outFragColor = vec4(inColor, 1.0);
}

https://www.codeproject.com/Articles/1288159/Vulkan-API-with-Kotlin-Native-Project-Setup?display=Print 5/9
27/03/2019 Vulkan API with Kotlin Native - Project Setup - CodeProject

And now a big function to run platform dependent build:

// Task create function to compile project, compile shaders and run program
def createRunTask(def buildType, def platform ) {

return tasks.create("tmpRunProgram${buildType.capitalize()}${platform}") {

// get link executable task name


def depends = platform.substring(0,1).capitalize() + platform.substring(1)

dependsOn "link${buildType.capitalize()}Executable$depends"

doLast {

def folder = new File(


"${projectDir}/build/bin/$platform/main/$buildType/executable/assets/shaders" )

// check if a folder for assets exists and create if not


if( !folder.exists() ) {
folder.mkdirs()
}

def shaders = fileTree("${projectDir}/src/shaders").filter { it.isFile() }.files.name

shaders.each { shader ->

exec {

workingDir "."
commandLine "${project.VULKAN}/bin/glslangValidator", "-V",
"$projectDir/src/shaders/$shader", "-o", "$folder/${shader}.spv"

//store the output instead of printing to the console:


standardOutput = new ByteArrayOutputStream()

//extension method to obtain the output:


ext.output = {
return standardOutput.toString()
}

def programFile = kotlin.targets."${platform}".compilations.main.getBinary('EXECUTABLE',


buildType)
exec {
executable programFile
args []
environment 'LD_LIBRARY_PATH' : "${project.VULKAN}lib:/usr/lib:"
}

}
}
}

What is it? It creates a task with a name depending on selected platform and selected build type. Then it goes through all shader
files in source folder, compiles them and save to assets folder. After that it runs the app with "LD_LIBRARY_PATH" set with Vulkan
SDK libraries path.

And our tasks for different platforms and releas/debug builds:

// Linux debug build


task runLinuxDebugProgram {
dependsOn createRunTask("debug", "linux")
}

// Linux release build


task runLinuxReleaseProgram {
dependsOn createRunTask("release", "linux")
}

https://www.codeproject.com/Articles/1288159/Vulkan-API-with-Kotlin-Native-Project-Setup?display=Print 6/9
27/03/2019 Vulkan API with Kotlin Native - Project Setup - CodeProject
// Windows debug build
task runWindowsDebugProgram {
dependsOn createRunTask("debug", "mingw")
}

// Windows release build


task runWindowsReleaseProgram {
dependsOn createRunTask("release", "mingw")
}

And last step - add common code, so we won' t return to the gradle build file anymore - for now we'll just create common/kvarc
folder in the project source folder. Then modify the gradle build file again, in sourceSets:

// Windows build
mingwMain {
kotlin.srcDirs += file("src/common")
}

// Linux build
linuxMain {
kotlin.srcDirs += file("src/common")
}

Note: don' t follow their note to enable common source sets, you won' t be able to use native platform dependent libraries in
this case.
The problem with common code we just added  - every time you open the project or changing the gradle build file you will need to
change the project structure - remove the common code from there for not platform you' re working on and set "use project
properties" check for every module. Go to Preferences->Build, Execution, Deployment->Compiler->Kotlin Compiler and append
to Additional command line parameters the following key "-Xmulti-platform".

Now the common code part. We we'll need to know on which platform we're in during runtime. For this we will use enum class with
platform list:

package kvarc.utils

/**
* Supported platforms
*/
internal enum class PlatformEnum {
WINDOWS,
LINUX,
ANDROID //TODO
}

Now we'll add common platform class:

package kvarc

import kvarc.utils.PlatformEnum

internal expect class Platform {

companion object {
val type: PlatformEnum
}

Here "expect" keyword means that our class expects "actual" class to be implemented by each platform. And here they are,
changing:

MainLinux.kt:

package kvarc

import kvarc.utils.PlatformEnum

internal actual class Platform {


https://www.codeproject.com/Articles/1288159/Vulkan-API-with-Kotlin-Native-Project-Setup?display=Print 7/9
27/03/2019 Vulkan API with Kotlin Native - Project Setup - CodeProject

actual companion object {


actual val type = PlatformEnum.LINUX
}

MainMingw.kt

package kvarc

import kvarc.utils.PlatformEnum

internal actual class Platform {

actual companion object {


actual val type = PlatformEnum.WINDOWS
}

And the final step - common "main" function:

package kvarc

import kvarc.utils.PlatformEnum

fun main(args: Array<String>) {

when (Platform.type) {
PlatformEnum.WINDOWS -> println("Windows platform")
PlatformEnum.LINUX -> println("Linux platform")
PlatformEnum.ANDROID -> println("Android platform")
}

That's it. All out preparations are finished. Now, after we'll implement native windows for each platform we will work only with
common code

To be continued...

Resources
Kotlin Native Overview
IntelliJ IDEA for Windows
IntelliJ IDEA for Linux
Vulkan SDK - please use a version not later than 1.1.92.1. In later version something changed with debug layers, I didn't
check it yet.
MinGW
RenderDoc
Android Studio
Android NDK
Vulkan Documentation
Sascha Willems - Examples and demos for the new Vulkan API
Beginners Guide to Vulkan
Kotlin Native Repository
 

https://www.codeproject.com/Articles/1288159/Vulkan-API-with-Kotlin-Native-Project-Setup?display=Print 8/9
27/03/2019 Vulkan API with Kotlin Native - Project Setup - CodeProject

License
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author


Igor Kushnarev
Software Developer
Russian Federation

• More than 10 years experience in software development


• 3 years experience in direction of automation department.
• software engineering: experience in the whole life cycle of software development
• languages: Kotlin, Swift,Objective-C, C++, C#, Java, ASP.NET, HTML, XML, JavaScript, Visual FoxPro, MS SQL, T-SQL
• Gathering, specification and the analysis of requirements of the customer to the software.
• The analysis of a subject area.
• Estimations of labour input of development.
And so on...

Comments and Discussions


0 messages have been posted for this article Visit https://www.codeproject.com/Articles/1288159/Vulkan-API-with-Kotlin-
Native-Project-Setup to post and view comments on this article, or click here to get a print view with messages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile Article Copyright 2019 by Igor Kushnarev
Web01 | 2.8.190306.1 | Last Updated 27 Mar 2019 Everything else Copyright © CodeProject, 1999-2019

https://www.codeproject.com/Articles/1288159/Vulkan-API-with-Kotlin-Native-Project-Setup?display=Print 9/9

Anda mungkin juga menyukai