https://github.com/Dominaezzz/kgl
Thin multiplatform wrappers for graphics.
https://github.com/Dominaezzz/kgl
glfw3 graphics kgl kotlin kotlin-multiplatform kotlin-native opengl vulkan vulkan-api
Last synced: about 1 month ago
JSON representation
Thin multiplatform wrappers for graphics.
- Host: GitHub
- URL: https://github.com/Dominaezzz/kgl
- Owner: Dominaezzz
- License: apache-2.0
- Archived: true
- Created: 2019-01-07T22:29:28.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2023-02-25T12:46:35.000Z (about 2 years ago)
- Last Synced: 2024-11-03T02:32:32.881Z (6 months ago)
- Topics: glfw3, graphics, kgl, kotlin, kotlin-multiplatform, kotlin-native, opengl, vulkan, vulkan-api
- Language: Kotlin
- Homepage:
- Size: 2.4 MB
- Stars: 105
- Watchers: 6
- Forks: 13
- Open Issues: 13
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome-kotlin-multiplatform - kgl - This library provides a thin OOP wrapper with DSLs to make programming with vulkan easier. (Libraries / GUI)
README
[](https://www.apache.org/licenses/LICENSE-2.0)
[](https://github.com/Dominaezzz/kgl/actions)# Kotlin Graphics Libraries
Kotlin Multiplatform libraries for graphics.
- [GLFW](https://www.glfw.org)
- [Vulkan](https://www.khronos.org/vulkan)
- [OpenGL](https://www.opengl.org)
- [STB](https://github.com/nothings/stb)KGL uses LWJGL for the JVM target and the respective native libraries on the native targets.
It provides a thin OOP wrapper with DSLs to make programming with vulkan easier.You can find some kgl-vulkan samples [here](https://github.com/Dominaezzz/kgl-vulkan-samples) and kgl-opengl samples [here](https://github.com/Dominaezzz/kgl-opengl-samples).
## Usage
```kotlin
repositories {
maven("https://maven.pkg.github.com/Dominaezzz/kgl") {
credentials {
username = System.getenv("GITHUB_USER") // Your GitHub username.
password = System.getenv("GITHUB_TOKEN") // A GitHub token with `read:packages`.
}
}
}dependencies {
api("com.kgl:kgl-core:$kglVersion")
api("com.kgl:kgl-glfw:$kglVersion")
api("com.kgl:kgl-glfw-static:$kglVersion") // For GLFW static binaries
api("com.kgl:kgl-opengl:$kglVersion")
api("com.kgl:kgl-vulkan:$kglVersion")
api("com.kgl:kgl-glfw-vulkan:$kglVersion")
api("com.kgl:kgl-stb:$kglVersion")
}
```## Design
The main goal of this library is to hide the verbosity of working with vulkan.For example in C++, to create a vulkan instance one would have to write code like,
```C++
std::vector layers = TODO();
std::vector extensions = TODO();VkApplicationInfo applicationInfo = {};
applicationInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
applicationInfo.pNext = nullptr;
applicationInfo.pApplicationName = "Kgl App";
applicationInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
applicationInfo.pEngineName = "No Engine yet";
applicationInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
applicationInfo.apiVersion = VK_VERSION_1_1;VkInstanceCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.pNext = nullptr;
createInfo.flags = 0;
createInfo.pApplicationInfo = &applicationInfo;
createInfo.enabledLayerCount = layers.size();
createInfo.ppEnabledLayerNames = layers.data();
createInfo.enabledExtensionCount = extensions.size();
createInfo.ppEnabledExtensionNames = extensions.data();VkInstance instance;
if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
throw std::runtime_error("Failed to create instance!");
}
```
but in Kotlin (with the help of KGL),
```kotlin
val layers: List = TODO()
val extensions: List = TODO()val instance = Instance.create(layers, extensions) {
applicationInfo {
applicationName = "Kgl App"
applicationVersion = VkVersion(1u, 1u, 0u)
engineName = "No engine yet"
engineVersion = VkVersion(1u, 0u, 0u)
apiVersion = VkVersion(1u, 1u, 0u)
}
}
```To create a device in C++,
```C++
uint32_t deviceCount = 1;
VkPhysicalDevice physicalDevice;
vkEnumeratePhysicalDevices(instance, &deviceCount, &physicalDevice);
if (deviceCount < 1) throw std::runtime_error("Failed to find GPU with vulkan support.");std::vector queueCreateInfos(2);
float queuePriority = 1.0f;
VkDeviceQueueCreateInfo& queueCreateInfo1 = queueCreateInfos[0];
queueCreateInfo1.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo1.pNext = nullptr;
queueCreateInfo1.flags = VK_DEVICE_QUEUE_CREATE_PROTECTED;
queueCreateInfo1.queueFamilyIndex = 1;
queueCreateInfo1.queueCount = 1;
queueCreateInfo1.pQueuePriorities = &queuePriority;VkDeviceQueueCreateInfo& queueCreateInfo2 = queueCreateInfos[1];
queueCreateInfo2.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo2.pNext = nullptr;
queueCreateInfo2.flags = 0;
queueCreateInfo2.queueFamilyIndex = 2;
queueCreateInfo2.queueCount = 1;
queueCreateInfo2.pQueuePriorities = &queuePriority;VkPhysicalDeviceFeatures deviceFeatures = {};
deviceFeatures.samplerAnisotropy = VK_TRUE;
deviceFeatures.geometryShader = VK_TRUE;
deviceFeatures.depthClamp = VK_TRUE;std::vector layers = TODO();
std::vector extensions = TODO();VkDeviceCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
createInfo.pNext = nullptr;
createInfo.pQueueCreateInfos = queueCreateInfos.data();
createInfo.queueCreateInfoCount = queueCreateInfos.size();
createInfo.pEnabledFeatures = &deviceFeatures;
createInfo.enabledExtensionCount = extensions.size();
createInfo.ppEnabledExtensionNames = extensions.data();
createInfo.enabledLayerCount = layers.size();
createInfo.ppEnabledLayerNames = layers.data();VkDevice device;
if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
throw std::runtime_error("failed to create logical device!");
}
```
but in Kotlin,
```kotlin
val physicalDevice = instance.physicalDevices.first()val device = physicalDevice.createDevice(layers, extensions) {
queues {
queue(1u, 1.0f) {
flags = DeviceQueueCreate.PROTECTED
}queue(2u, 1.0f)
}enabledFeatures {
samplerAnisotropy = true
geometryShader = true
depthClamp = true
}
}
```### Handles
Every vulkan handle has a class of it's own. The name of the class being the name of the handle without the `Vk` prefix.
Like `Instance` for `VkInstance`, `CommandBuffer` for `VkCommandBuffer`, etc.
All handles keep a reference to their parent, to be able to destroy or free themselves later.
Some handles hold a few values from their creation. Like `Image` has `size`, `layers` and `format` properies.### Structs
Input structs on the other hand have a DSL builder class.
Output structs have a corresponding data class.
```C++
typedef struct VkLayerProperties {
char layerName[VK_MAX_EXTENSION_NAME_SIZE];
uint32_t specVersion;
uint32_t implementationVersion;
char description[VK_MAX_DESCRIPTION_SIZE];
} VkLayerProperties;
```
```kotlin
data class LayerProperties(
val layerName: String,
val specVersion: VkVersion,
val implementationVersion: UInt,
val description: String
)
```### Enums
Enums are represented with a kotlin enum.
If the enum is a part a bitmask then it extends `VkFlag` to allow for type-safe bit fiddling.
```C++
typedef VkFlags VkCullModeFlags;
typedef enum VkCullModeFlagBits {
VK_CULL_MODE_NONE = 0,
VK_CULL_MODE_FRONT_BIT = 0x00000001,
VK_CULL_MODE_BACK_BIT = 0x00000002,
VK_CULL_MODE_FRONT_AND_BACK = 0x00000003,
} VkCullModeFlagBits;VkCullModeFlags flags = VK_CULL_MODE_FRONT_BIT | VK_CULL_MODE_BACK_BIT;
```
```kotlin
enum class CullMode : VkFlag {
NONE,
FRONT,
BACK
}val flags: VkFlag = CullMode.FRONT or CullMode.BACK
```
Although this does mean that bitwise operations create new objects.
Once inline enums are implemented in Kotlin, we'll get the type-safety without the allocations.### Commands
Every command's function pointer has been explicitly loaded using `vkGetDeviceProcAddr` and `vkGetInstanceProcAddr` for optimal command calling performance.
This also means you don't need to have the [Vulkan SDK](https://vulkan.lunarg.com/sdk/home) installed to get started with kgl-vulkan.At the moment every core and extension (non-platform specific) command has been implemented as a member function/property of a handle class.
In most cases it is a member of the last of the first consecutive handles in the parameter list.
In other cases, it is moved to a handle class that makes the most sense.
```C
VkResult vkMapMemory(VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void** ppData);
```
```kotlin
fun DeviceMemory.map(offset: ULong, size: ULong, flags: VkFlag? = null): IoBuffer
```
or
```C++
VkResult vkEnumeratePhysicalDevices(VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices);
```
```kotlin
val Instance.physicalDevices: List
```### GLFW Example
```kotlin
val window = Window(1080, 720, "Sample!") {
clientApi = ClientApi.None
resizable = false
visible = true
}val (width, height) = window.size
val mode = Glfw.primaryMonitor!!.videoMode
window.position = ((mode.width - width) / 2) to ((mode.height - height) / 2)
```## Limitations
- OpenGL is only supported on native targets.
- Only core OpenGL is supported and is loaded on the first gl function call.
- Platform specific extensions have not been implemented yet. Mostly because of [this](https://youtrack.jetbrains.com/issue/KT-27801).
- Support for pNext has not been implemented yet.
- Some parts of the api that use an `IoBuffer` have not yet been implemented as it requires bespoke design.
- Documentation is partially generated.
- Until the bulk of library is under codegen, only version `1.1.92` will be supported.