在前面的部分中,我们做了所有准备工作,所以现在我们终于可以开始使用Vulkan API了。让我们总结一下我们现在拥有的东西:
Vulkan API乍一看似乎很复杂。但让自己沉浸其中逐步理解变得更容易。最重要的是 - 结构,结构和结构......结构用于定义您想要获得的行为。从Vulkan API开始的第一件事是Vulkan实例。因此,当它首次尝试将Kotlin Native与Vulkan API一起使用时,让我们详细了解实例创建。
正如我上面提到的,首先要做的是 - 我们需要创建一个Vulkan实例。但是我们应该对API说它应该如何创建。例如,我们应该说我们将使用哪个平台表面,每个平台都会有所不同。要说出来,我们必须检查驱动程序扩展支持。在这里,我们将获得可用的扩展:
private fun setupExtensions(scope: MemScope): MutableList<String> {
val availableInstanceExtensions: MutableList<String> = ArrayList()
with(scope) {
val extensionsCount = alloc<UIntVar>()
extensionsCount.value = 0u
var result: VkResult
// Enumerate _instance extsensions and check if they're available
do {
result = vkEnumerateInstanceExtensionProperties(null, extensionsCount.ptr, null)
if (!VK_CHECK(result)) throw RuntimeException
("Could not enumerate _instance extensions.")
if (extensionsCount.value == 0u) break
val buffer = allocArray<VkExtensionProperties>(extensionsCount.value.toInt())
result = vkEnumerateInstanceExtensionProperties(null, extensionsCount.ptr, buffer)
for (i in 0 until extensionsCount.value.toInt()) {
val ext = buffer[i].extensionName.toKString()
if (!availableInstanceExtensions.contains(ext))
availableInstanceExtensions.add(ext)
}
} while (result == VK_INCOMPLETE)
}
return availableInstanceExtensions
}
在这里,我们传递初始化函数中先前创建的内存范围。内存范围定义了在其中分配的内存的生命周期。然后,我们使用vkEnumerateInstanceExtensionProperties
API函数获取可用的扩展并将它们添加到列表中。
此外,我们需要获得可用的调试层:
/**
* Prepare debug layers
*/
private fun prepareLayers(scope: MemScope, instanceCreateInfo:
VkInstanceCreateInfo): MutableList<String> {
logInfo("Preparing Debug Layers")
val availableLayers = mutableListOf<String>()
with(scope) {
// Layers optimal order:
// <a href='https://vulkan.lunarg.com/doc/view/1.0.13.0/windows/layers.html'/>
val layers = arrayOf(
"VK_LAYER_GOOGLE_threading",
"VK_LAYER_LUNARG_parameter_validation",
"VK_LAYER_LUNARG_object_tracker",
"VK_LAYER_LUNARG_core_validation",
"VK_LAYER_GOOGLE_unique_objects",
"VK_LAYER_LUNARG_standard_validation"
)
val layersCount = alloc<UIntVar>()
var result: VkResult
run failure@{
do {
// Enumerate available layers
result = vkEnumerateInstanceLayerProperties(layersCount.ptr, null)
if (!VK_CHECK(result)) {
logError("Failed to enumerate debug layers")
availableLayers.clear()
return@failure // failed to get layers break the loop
} else {
val buffer = allocArray<VkLayerProperties>(layersCount.value.toInt())
result = vkEnumerateInstanceLayerProperties(layersCount.ptr, buffer)
if (!VK_CHECK(result)) {
logError("Filed to enumerate Debug Layers to buffer")
availableLayers.clear()
return@failure // failed to get layers break the loop
}
for (i in 0 until layersCount.value.toInt()) {
val layer = buffer[i].layerName.toKString()
logDebug("Found $layer layer")
if (!availableLayers.contains(layer) && layers.contains(layer)) {
availableLayers.add(layer)
logDebug("$layer added")
}
}
}
} while (result == VK_INCOMPLETE)
}
// Setting debug layers it they're available
if (availableLayers.size > 0) {
if (availableLayers.contains("VK_LAYER_LUNARG_standard_validation"))
availableLayers.removeAll {
it != "VK_LAYER_LUNARG_standard_validation"
}
else
// sort available layers in accordance with recommended order
availableLayers.sortBy {
layers.indexOf(it)
}
logInfo("Setting up Layers:")
availableLayers.forEach {
logInfo(it)
}
instanceCreateInfo.enabledLayerCount = availableLayers.size.toUInt()
instanceCreateInfo.ppEnabledLayerNames =
availableLayers.toCStringArray(scope)
}
}
return availableLayers
}
再次,使用vkEnumerateInstanceLayerProperties
,我们创建了可用的调试层列表。我应该稍微解释一下最后一部分。在layers
,我定义了标准调试层 - 它们与VK_LAYER_LUNARG_standard_validation
层相同。但有时,它会返回此列表或仅仅VK_LAYER_LUNARG_standard_validation
是为了现在,我们将仅使用标准图层来检查是否只留下VK_LAYER_LUNARG_standard_validation
列表。最后。准备好的图层我们设置为Instance Create Info
结构,转换为C string
s数组。
现在是时候创建实例了:
...
// Application info
val appInfo = alloc<VkApplicationInfo>().apply {
sType = VK_STRUCTURE_TYPE_APPLICATION_INFO
pNext = null
apiVersion = VK_MAKE_VERSION(1u, 0u, 0u)
applicationVersion = VK_MAKE_VERSION(1u, 0u, 0u)
engineVersion = VK_MAKE_VERSION(1u, 0u, 0u)
pApplicationName = "kvarc".cstr.ptr
pEngineName = "kvarc".cstr.ptr
apiVersion = VK_API_VERSION_1_0.toUInt()
}
var instanceExt: Array<String> =
arrayOf(VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_PLATFORM_SURFACE_EXTENSION_NAME)
val debugSupported = availableInstanceExtensions.contains("VK_EXT_debug_report")
if (debug && debugSupported) instanceExt += "VK_EXT_debug_report"
// Debug layers will be added a little later if needed
val instanceCreateInfo = alloc<VkInstanceCreateInfo>().apply {
sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO
pNext = null
pApplicationInfo = appInfo.ptr
enabledExtensionCount = instanceExt.size.toUInt()
ppEnabledExtensionNames = instanceExt.toCStringArray(memScope)
enabledLayerCount = 0u
ppEnabledLayerNames = null
}
logInfo("Debug: $debug, DebugSupported: $debugSupported")
val availableLayers =
if (debug && debugSupported) prepareLayers(this, instanceCreateInfo) else ArrayList()
logInfo("Creating _instance")
if (!VK_CHECK(vkCreateInstance(instanceCreateInfo.ptr, null, _instance.ptr)))
throw RuntimeException("Failed to create _instance")
...
在这里,我们定义了应用程序信息结构 - 传递给它的类型,所需的API版本,应用程序信息,转换为C字符串的应用程序名称等等。同样,我们定义了实例创建信息。最后,使用vkCreateInstance
call 创建了实例。
最后要做的事情 - 设置调试回调。这里最有趣的部分 - 设置回调本身:
...
pfnCallback = staticCFunction { flags, _, _, _, msgCode, pLayerPrefix, pMsg, _ ->
var prefix = "kvarc-"
when {
flags and VK_DEBUG_REPORT_ERROR_BIT_EXT > 0u -> prefix += "ERROR:"
flags and VK_DEBUG_REPORT_WARNING_BIT_EXT > 0u -> prefix += "WARNING:"
flags and VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT > 0u -> prefix += "PERFORMANCE:"
flags and VK_DEBUG_REPORT_INFORMATION_BIT_EXT > 0u -> prefix += "INFO:"
flags and VK_DEBUG_REPORT_DEBUG_BIT_EXT > 0u -> prefix += "DEBUG:"
}
val debugMessage =
"$prefix [${pLayerPrefix?.toKString() ?: ""}] Code $msgCode:${pMsg?.toKString() ?: ""}"
if (flags and VK_DEBUG_REPORT_ERROR_BIT_EXT > 0.toUInt()) {
logError(debugMessage)
} else {
logDebug(debugMessage)
}
// abort/not
VK_FALSE.toUInt()
}
...
它可以通过staticCFunction
分配完成。在它的主体中,我们只需编写回调参数和所需的代码。
所以,我们刚刚用Kotlin Native创建了第一个Vulkan实例。很简单,不是吗?现在是时候使用它了。让我们创建一个renderer
类:
@ExperimentalUnsignedTypes
internal class Renderer : DisposableContainer() {
private var _instance: Instance? = null
fun initialize() {
_instance = Instance()
_instance!!.initialize(true)
}
override fun dispose() {
_instance?.dispose()
super.dispose()
}
}
为什么不进去init{}
,但是initialize
?因为我们需要在最后处置一堆资源 - 实例,设备等......如果出现问题,我们将不会拥有该renderer
对象,也无法处置它。
现在,我们应该将渲染器调用添加到两个平台:
var renderer = Renderer()
try {
renderer.initialize()
} catch (exc: Exception) {
logError(exc.message ?: "Failed to create renderer")
}
renderer.dispose()
很久以后会添加一个渲染循环...这就是现在......
使用Kotlin Native的Vulkan API - 实例 转载https://www.codesocang.com/appboke/39911.html
热门源码