当前位置:首页 > 安卓源码 > 技术博客 >

使用Kotlin Native的Vulkan API - 实例

时间:2019-04-29 15:55 来源:互联网 作者:源码搜藏 浏览: 收藏 挑错 推荐 打印

介绍 在前面的部分中,我们做了所有准备工作,所以现在我们终于可以开始使用Vulkan API了。 让我们总结一下我们现在拥有的东西: 该项目编译为Windows和Linux平台的本机代码。 与C库互操作。 我们可以移动,调整大小,切换到全屏模式等两个平台的本机窗口。

介绍

在前面的部分中,我们做了所有准备工作,所以现在我们终于可以开始使用Vulkan API了。让我们总结一下我们现在拥有的东西:

  • 该项目编译为Windows和Linux平台的本机代码。
  • 与C库互操作。
  • 我们可以移动,调整大小,切换到全屏模式等两个平台的本机窗口。
  • 我们可以检查我们是哪个平台,并定义要使用的相应Vulkan扩展。
  • 添加了所有需要的共享库的链接,包括Vulkan加载程序,调试层库等。
  • 添加了从源代码编译着色器的任务
  • Vulkan API声称是一个跨平台的图形API(不仅仅是,它也用于计算)和GPU直接控制。因此,我们还创建了项目的共同部分,我们将在其中使用它。

Vulkan API乍一看似乎很复杂。但让自己沉浸其中逐步理解变得更容易。最重要的是 - 结构,结构和结构......结构用于定义您想要获得的行为。从Vulkan API开始的第一件事是Vulkan实例。因此,当它首次尝试将Kotlin Native与Vulkan API一起使用时,让我们详细了解实例创建。

Vulkan实例

正如我上面提到的,首先要做的是 - 我们需要创建一个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
   }

在这里,我们传递初始化函数中先前创建的内存范围。内存范围定义了在其中分配的内存的生命周期。然后,我们使用vkEnumerateInstanceExtensionPropertiesAPI函数获取可用的扩展并将它们添加到列表中。

此外,我们需要获得可用的调试层:

 /**
 * 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 strings数组。

现在是时候创建实例了:

...

// 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字符串的应用程序名称等等。同样,我们定义了实例创建信息。最后,使用vkCreateInstancecall 创建了实例

最后要做的事情 - 设置调试回调。这里最有趣的部分 - 设置回调本身:

 ...
 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

技术博客阅读排行

最新文章