Variadic reified Generics


#1

Hello,
I am currently trying to build an engine wrapping vulkan. I am making queues typesafe (dynamically created with the right object using a constructor map, and reflection in order to actually use the typing).
A queue is simply an execution queue, the twist is each queue has different capabilities, thus the need for strong typing.

i am creating a builder in order to generate the queues automatically given a set of capability describing interfaces, and i need to manually overload what could be variadic generics.

    inline fun <reified A : IQueueFamily, reified B : IQueueFamily, reified C : IQueueFamily, reified D : IQueueFamily> withDevice(device: PhysicalDevice, a: QueuePriority<A>, b: QueuePriority<B>, c: QueuePriority<C>, d: QueuePriority<D>, layers: List<String> = listOf(), extensions: List<String> = listOf(), features: VkPhysicalDeviceFeatures = VkPhysicalDeviceFeatures.create(), crossinline fn: (PresentableLogicalDevice, IQueue<A>, IQueue<B>, IQueue<C>, IQueue<D>) -> Unit) {
        async {
            val ap = FamilyInfoPackage(device.familyOf<A>(window).sortedBy { it.type.featureSet.size }.first(), a)
            val bp = FamilyInfoPackage(device.familyOf<B>(window).sortedBy { it.type.featureSet.size }.first(), b)
            val cp = FamilyInfoPackage(device.familyOf<C>(window).sortedBy { it.type.featureSet.size }.first(), c)
            val dp = FamilyInfoPackage(device.familyOf<D>(window).sortedBy { it.type.featureSet.size }.first(), d)
            val families = listOf(ap, bp, cp, dp)
            val logicalDevice = device.createPresentableLogicalDevice(layers, (extensions + KHRSwapchain.VK_KHR_SWAPCHAIN_EXTENSION_NAME).distinct(), features, mapFamilies(families), window)
            fn(
                    logicalDevice,
                    logicalDevice.queues[ap.family]!![ap.index] as Queue<A>,
                    logicalDevice.queues[bp.family]!![bp.index] as Queue<B>,
                    logicalDevice.queues[cp.family]!![cp.index] as Queue<C>,
                    logicalDevice.queues[dp.family]!![dp.index] as Queue<D>
            )
        }
    }

reapeat this for a, ab, abc, abcd, abcde and every other ammount of different queues you wish to use.

what could be done is something like this:

    inline fun <vararg reified T : IQueueFamily> withDevice(device: PhysicalDevice, vararg t: QueuePriority<T>, layers: List<String> = listOf(), extensions: List<String> = listOf(), features: VkPhysicalDeviceFeatures = VkPhysicalDeviceFeatures.create(), crossinline fn: (PresentableLogicalDevice, vararg IQueue<T>) -> Unit) {
        async {
            val families = t.map {FamilyInfoPackage(device.familyOf<it::T>(window).sortedBy { it.type.featureSet.size }.first(), it)}
            val logicalDevice = device.createPresentableLogicalDevice(layers, (extensions + KHRSwapchain.VK_KHR_SWAPCHAIN_EXTENSION_NAME).distinct(), features, mapFamilies(families), window)
            fn(logicalDevice, *families.toTypedArray())
        }
    }

the Something<T>::T could be only accessible in a local context if T is reified.

Even if it only works with inline functions or the standard for(U in T){} it’s a great improvement.

this allows now to do things like:

withDevice(instance.physicalDevices.first(), QueuePriority(0.5f)) { device, queue: IQueue<MGCRTF> ->

}

MGCRTF stands for at minima (M) Graphics, Compute, pResent, Transfer Family.
I manually also had to implement manually all possible combination between the 6 family types, as interfaces, then as object that could be bundled with the queue for later identification and reflection.

A common use, rather than the previous simple would be:

withDevice(instance.physicalDevices.first(), QueuePriority(0.5f), QueuePriority(1f), QueuePriority(1f)) { device, renderQueue: IQueue<MGRF>, transferQueue: IQueue<MTF>, computeQueue: IQueue<MCF> ->

}

Some graphics cards have dedicated compute and transfer queues that allow to leverage better performance.