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

Vulkan API与Kotlin Native - Swapchain,Pipeline

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

介绍 在上一部分中,我们创建了一个绘制的表面。 现在,我们将准备绘制的内容。 但在此之前,我们应该做一些准备。 我们需要向量,矩阵,缓冲区等。 可以将 glm 库用于我们的目的但是因为我们想要检查Kotlin Native性能本身,我将自己实现类似于一个小数学库

介绍

在上一部分中,我们创建了一个绘制的表面。现在,我们将准备绘制的内容。但在此之前,我们应该做一些准备。我们需要向量,矩阵,缓冲区等。可以将glm库用于我们的目的但是因为我们想要检查Kotlin Native性能本身,我将自己实现类似于一个小数学库的东西。首先,我们将创建一个带有加,减,乘,点乘积等操作的基矢量类。此外,我们需要一个伴随对象将我们的基矢量转换为具有正确尺寸的矢量。为了便于使用VulkanAPI,我们将坐标定义为字节数组。

open class Vec(vararg coordinates: Float) {

    protected var buffer: FloatArray = FloatArray(coordinates.size).apply {
        coordinates.copyInto(this)
    }

    val len by lazy {
        sqrtf(this dot this)
    }

    val size by lazy {
        coordinates.size
    }

    fun normalize() = len.let {
        if (it != 0f)
            for (i in 0 until buffer.size)
                buffer[i] /= it
    }

    open fun normalized() = len.run {

        val copyed = buffer.copyOf()

        if (this != 0f)
            for (i in 0 until copyed.size)
                copyed[i] /= this

        when (buffer.size) {
            2 -> Vec.fromBuffer(*copyed) as Vec2
            3 -> Vec.fromBuffer(*copyed) as Vec3
            4 -> Vec.fromBuffer(*copyed) as Vec4
            else -> throw IllegalArgumentException("not supported vector size")
        }
    }

    infix fun dot(vec: Vec): Float = buffer.foldIndexed(0f) { index, sum, element ->
        sum + element * vec.buffer[index]
    }

    open operator fun plus(scalar: Float): Vec = Vec.fromBuffer(*buffer.map {
        it + scalar
    }.toFloatArray())
    
    ....
  
    companion object {

        fun fromBuffer(vararg buffer: Float): Vec {
            when (buffer.size) {
                2 -> return Vec2(buffer[0], buffer[1])
                3 -> return Vec3(buffer[0], buffer[1], buffer[2])
                4 -> return Vec4(buffer[0], buffer[1], buffer[2], buffer[3])
                else -> throw IllegalArgumentException("not supported vector size")
            }
        }
    }
    ...
}

作为一个例子,我将展示来自基类的4维向量的继承:

class Vec4(x: Float = 0f, y: Float = 0f, z: Float = 0f, w: Float = 0f) : Vec(x, y, z, w) {

    var x: Float
        get() = buffer[0]
        set(value) {
            buffer[0] = value
        }
    ....

    var w: Float
        get() = buffer[3]
        set(value) {
            buffer[3] = value
        }

    override operator fun inc(): Vec4 = super.inc() as Vec4
    ...
    override fun normalized(): Vec4 = super.normalized() as Vec4

    companion object {

        val Zero = Vec4()
    }
}

另外,我们需要矩阵。它们以相同的方式实现 - 首先,我们将创建一个基类,然后继承它:

基类:

open class Mat(vararg columns: Vec) {

    private var _size = columns.size

    protected var buffer = FloatArray(_size * _size).apply {
        columns.forEachIndexed { index, vec ->
            vec.toArray().forEachIndexed { idx, fl ->
                this[index * columns.size + idx] = fl
            }
        }
    }

    ... 

    open operator fun plus(v: Float): Mat {

        val arr = this.toBuffer()
        for (i in 0 until arr.size)
            arr[i] += v

        return when (size) {
            2 -> Mat2.from(*arr)
            3 -> Mat3.from(*arr)
            4 -> Mat4.from(*arr)
            else -> throw IllegalArgumentException("invalid dimensions")
        }
    }

    ...

    fun toBuffer(): FloatArray = buffer.copyOfRange(0, buffer.size)
}

而且Mat4

class Mat4(x: Vec4 = Vec4(x = 1f), y: Vec4 = Vec4(y = 1f), 
           z: Vec4 = Vec4(z = 1f), w: Vec4 = Vec4(w = 1f)) : Mat(x, y, z, w) {

    var X: Vec4
        get() = Vec4(buffer[0], buffer[1], buffer[2], buffer[3])
        set(value) {
            buffer[0] = value[0]
            buffer[1] = value[1]
            buffer[2] = value[2]
            buffer[3] = value[3]
        }
   ...

    var W: Vec4
        get() = Vec4(buffer[12], buffer[13], buffer[14], buffer[15])
        set(value) {
            buffer[12] = value[0]
            buffer[13] = value[1]
            buffer[14] = value[2]
            buffer[15] = value[3]
        }

    override operator fun inc(): Mat4 = super.inc() as Mat4
    ...
    override operator fun minus(m: Mat): Mat4 = super.minus(m) as Mat4

    companion object {

        fun from(vararg a: Float): Mat4 {
            assert(a.size == 16)
            return Mat4(Vec4(a[0], a[1], a[2], a[3]), Vec4(a[4], a[5], a[6], a[7]), 
                        Vec4(a[8], a[9], a[10], a[11]), Vec4(a[12], a[13], a[14], a[15]))
        }
        ...
    }
}

现在我们有了一些小数学库,但是有一些助手可以使用字节数组,因为我们将在使用VulkanAPI时使用它们让我们为它创建一个帮助类,允许我们使用迭代器forEach等。

@ExperimentalUnsignedTypes
internal class VulkanArray<T : CVariable>
private constructor(internal val _size: UInt) : DisposableContainer() {

    internal lateinit var _array: CArrayPointer<T>
        private set

    companion object {

        inline fun <reified K : CVariable> Make(size: UInt): VulkanArray<K> {
            val array = VulkanArray<K>(size)
            array._array = with(array.arena) { allocArray(size.toInt()) }
            return array
        }
    }
}

@ExperimentalUnsignedTypes
internal inline operator fun <reified T : CVariable> VulkanArray<T>.iterator(): Iterator<T> {

    return object : Iterator<T> {

        var cursor = 0
        override fun hasNext() = cursor < _size.toInt()
        override fun next(): T = _array.get(cursor++)
    }
}

@ExperimentalUnsignedTypes
internal inline fun <reified T : CVariable> VulkanArray<T>
    .forEach(callback: (it: T) -> Unit) {

    for (i in 0 until _size.toInt()) {
        callback(_array[i])
    }
}

...

现在我们准备继续。

SwapChain。RenderPass。

如您所知,一次只有一个图像呈现在表面上。但我们可以创建一个队列并渲染更多图像,同时将其中一个图像呈现给屏幕。因此,交换链是队列中这种可呈现图像的数组,它允许在屏幕上显示它们。要创建交换链,我们首先会得到支持的格式,检查我们是否可以使用VK_PRESENT_MODE_IMMEDIATE_KHRVK_PRESENT_MODE_MAILBOX_KHR检查我们是否可以使用复合alpha,创建交换链本身并创建带图像的缓冲区。
让我们得到支持的格式:

val formatsCount = alloc<UIntVar>()
var result: VkResult

var buffer: CArrayPointer<VkSurfaceFormatKHR>? = null

do {

    result = vkGetPhysicalDeviceSurfaceFormatsKHR(pDevice.device,
              surface, formatsCount.ptr, null)
    if (!VK_CHECK(result)) {
        throw RuntimeException("Could not get surface formats.")
    }

    if (formatsCount.value == 0u) break

    buffer?.let {
        nativeHeap.free(it)
        buffer = null
    }

    buffer = nativeHeap.allocArray(formatsCount.value.toInt())
    result =
        vkGetPhysicalDeviceSurfaceFormatsKHR(
            pDevice.device,
            surface,
            formatsCount.ptr,
            buffer!!.getPointer(memScope)
        )
    if (!VK_CHECK(result)) {
        throw RuntimeException("Could not get surface formats.")
    }


} while (result == VK_INCOMPLETE)

if (formatsCount.value == 1u) {
    displayFormat = buffer!![0].format
    _colorSpace = buffer!![0].colorSpace
} else {

    var chosenFormat: UInt? = null

    for (i in 0u until formatsCount.value) {
        if (buffer!![i.toInt()].format == VK_FORMAT_R8G8B8A8_UNORM) {
            chosenFormat = i
            break
        }
    }

    chosenFormat?.let {
        displayFormat = buffer!![it.toInt()].format
        _colorSpace = buffer!![it.toInt()].colorSpace
    } ?: kotlin.run {
        displayFormat = buffer!![0].format
        _colorSpace = buffer!![0].colorSpace
    }

}

nativeHeap.free(buffer!!)

接下来,我们将定义当前模式。早些时候,在PhysicalDevice课堂上,我们添加了surfacePresentModes属性。如果我们不使用vsync,我们只检查它是否包含VK_PRESENT_MODE_MAILBOX_KHR否则VK_PRESENT_MODE_IMMEDIATE_KHR使用它。默认情况下,它是VK_PRESENT_MODE_FIFO_KHR然后在表面能力中,我们应该检查最大图像计数以及它们是否包含任何复合alpha位。完成所有准备工作后,我们可以创建交换链本身和图像:

val swapchainCreateInfo: VkSwapchainCreateInfoKHR =
     alloc<VkSwapchainCreateInfoKHR>().apply {
     sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR
     ...
 }
...

if (!VK_CHECK(vkCreateSwapchainKHR(lDevice,
      swapchainCreateInfo.ptr, null, _swapchain.ptr)))
     throw RuntimeException("Failed to create swapchain")

 val imagesCount: UIntVar = alloc()

 if (!VK_CHECK(vkGetSwapchainImagesKHR(lDevice, _swapchain.value,
   imagesCount.ptr, null)))
     throw RuntimeException("Failed to initialize vulkan. No images")

 _imagesBuffer = arena.allocArray(imagesCount.value.toInt())

 if (!VK_CHECK(vkGetSwapchainImagesKHR(lDevice, _swapchain.value,
    imagesCount.ptr, _imagesBuffer)))
     throw RuntimeException("Failed to initialize vulkan. No images")

 for (i in 0u until imagesCount.value) {

     val imageViewCreateInfo: VkImageViewCreateInfo =
         alloc<VkImageViewCreateInfo>().apply {
         sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO
         ...
         image = _imagesBuffer[i.toInt()]!!
     }

     val imageView = with(arena) { alloc<VkImageViewVar>() }
     if (!VK_CHECK(vkCreateImageView(lDevice,
         imageViewCreateInfo.ptr, null, imageView.ptr))) {
         throw RuntimeException("Failed to create image views")
     }

     imageBuffers.add(Pair(_imagesBuffer[i.toInt()]!!, imageView))

 }
 ...

RenderPass实施是很容易做到。这是使用结构和结构的标准方式。所以我留给读者检查源代码。

统一缓冲区

统一缓冲区是在视频卡的存储器中分配的只读存储区,可由着色器程序使用。所以我们应该在主机内存中准备这些区域,然后将它们传递给显卡内存。对此,是的,再一些准备步骤。首先,让我们创建一个Vertex类:

@ExperimentalUnsignedTypes
class Vertex(position: Vec3, color: Vec3) {
    var buffer: FloatArray = FloatArray(6) { 0f }
        private set

    var position: Vec3 ...

    var color: Vec3 ...
    
    ...

    companion object {

        // Single vertex input binding at binding point 0

        fun bindingDescription(scope: MemScope) = 
            scope.alloc<VkVertexInputBindingDescription>().apply {
            binding = 0u
            stride = Vertex.SIZE.toUInt()
            inputRate = VK_VERTEX_INPUT_RATE_VERTEX
        }

        fun inputAtributes(scope: MemScope) = 
            scope.allocArray<VkVertexInputAttributeDescription>(2).apply {

            // Input attribute bindings describe shader attribute locations and memory layouts

            // These match the following shader layout (see triangle.vert):
            //	layout (location = 0) in vec3 inPos;
            //	layout (location = 1) in vec3 inColor;

            // Attribute location 0: Position
            this[0].binding = 0u
            this[0].location = 0u
            // Position attribute is three 32 bit signed (SFLOAT) floats (R32 G32 B32)
            this[0].format = VK_FORMAT_R32G32B32_SFLOAT
            this[0].offset = 0u

            // Attribute location 1: Color


        }

        val SIZE = 6 * sizeOf<FloatVar>()
        val BUFFER_SIZE = 6
    }
}

现在我们可以创建一个vertex缓冲区。我们将使用暂存缓冲区,让我们为它们创建类。StagingBuffer 本身只是几个属性,缓冲区和设备内存VkBufferVar VkDeviceMemoryVar 类型。并且StagingBuffers 类还包含顶点登台缓冲区和索引登台缓冲区的几个变量。顶点缓冲类的初始化将包括以下步骤:

  • 创建一个对主机可见的可映射缓冲区
  • 将数据复制到它
  • 在视频卡内存中创建一个与主机缓冲区大小相同的缓冲区
  • 使用命令缓冲区,将数据从主机复制到设备
  • 删除主机缓冲区
  • 在着色器中使用设备缓冲区

这是实施:

@ExperimentalUnsignedTypes
internal class VertexBuffer(
    private val pDevice: PhysicalDevice,
    private val lDevice: LogicalDevice,
    vertices: Array<Vertex>,
    indices: Array<UInt>
) : DisposableContainer() {

    ...
    init {

        var voffset = 0

        vertices.forEach { v ->
            v.buffer.copyInto(vertexBuffer, voffset)
            voffset += Vertex.BUFFER_SIZE
        }

        indices.forEachIndexed { index, uInt ->
            indexBuffer[index] = uInt
        }

        memScoped {

            val memoryAllocateInfo = alloc<VkMemoryAllocateInfo>().apply {
                sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO
            }

            val memReqs: VkMemoryRequirements = alloc()

            // Vertex buffer
            val vertexBufferInfo = alloc<VkBufferCreateInfo>().apply {
                sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO
                size = vertexBufferSize.toULong()
                usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT //copy source
            }

            val stagingBuffers = StagingBuffers()

            if (!VK_CHECK(
                    vkCreateBuffer(
                        lDevice.device,
                        vertexBufferInfo.ptr,
                        null,
                        stagingBuffers.vertices.buffer.ptr
                    )
                )
            )
            throw RuntimeException("Failed to create buffer")

            vkGetBufferMemoryRequirements(lDevice.device, 
                 stagingBuffers.vertices.buffer.value, memReqs.ptr)

            memoryAllocateInfo.allocationSize = memReqs.size

            // host visible memory and coherent
            memoryAllocateInfo.memoryTypeIndex = pDevice.getMemoryType(
                memReqs.memoryTypeBits,
                VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT or 
                VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
            )

            val mapped = alloc<COpaquePointerVar>()

            if (!VK_CHECK(
                    vkAllocateMemory(
                        lDevice.device,
                        memoryAllocateInfo.ptr,
                        null,
                        stagingBuffers.vertices.memory.ptr
                    )
                )
            )
                throw RuntimeException("Faild allocate memory")

            if (!VK_CHECK(
                    vkMapMemory(
                        lDevice.device,
                        stagingBuffers.vertices.memory.value,
                        0u,
                        memoryAllocateInfo.allocationSize,
                        0u,
                        mapped.ptr
                    )
                )
            )
                throw RuntimeException("Faild map memory")

            vertexBuffer.usePinned { buffer ->
                platform.posix.memcpy(mapped.value, buffer.addressOf(0), 
                    vertexBufferSize.toULong())
            }

            vkUnmapMemory(lDevice.device, stagingBuffers.vertices.memory.value)

            if (!VK_CHECK(
                    vkBindBufferMemory(
                        lDevice.device,
                        stagingBuffers.vertices.buffer.value,
                        stagingBuffers.vertices.memory.value,
                        0u
                    )
                )
            )
                throw RuntimeException("failed bind memory")

            // Create a _device local buffer to accept data
            vertexBufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT or 
               VK_BUFFER_USAGE_TRANSFER_DST_BIT

            if (!VK_CHECK(vkCreateBuffer(lDevice.device, vertexBufferInfo.ptr, 
                null, _vertexBuffer.ptr)))
                throw RuntimeException("Failed to create buffer")
            vkGetBufferMemoryRequirements(lDevice.device, _vertexBuffer.value, 
                  memReqs.ptr)

            memoryAllocateInfo.allocationSize = memReqs.size
            memoryAllocateInfo.memoryTypeIndex = pDevice.getMemoryType(
                memReqs.memoryTypeBits,
                VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
            )

            if (!VK_CHECK(vkAllocateMemory(lDevice.device, memoryAllocateInfo.ptr, 
                null, _vertexMemory.ptr)))
                throw RuntimeException("Faild allocate memory")

            if (!VK_CHECK(vkBindBufferMemory(lDevice.device, _vertexBuffer.value, 
               _vertexMemory.value, 0u)))
                throw RuntimeException("failed bind memory")

            ...
            /* the same for index buffer */
            ...
            
            // copy buffer
            val copyCmd = lDevice.createCommandBuffers(VK_COMMAND_BUFFER_LEVEL_PRIMARY, 
                 1u, true)
            
            val copyRegion = alloc<VkBufferCopy>()

            copyRegion.size = vertexBufferSize.toULong()
            vkCmdCopyBuffer(
                copyCmd._array[0],
                stagingBuffers.vertices.buffer.value,
                _vertexBuffer.value,
                1u,
                copyRegion.ptr
            )

            ...

            val cc = alloc<VkCommandBufferVar>()
            cc.value = copyCmd._array[0]
            lDevice.flushCommandBuffer(cc, false)

            vkDestroyBuffer(lDevice.device, stagingBuffers.vertices.buffer.value, null)
            vkFreeMemory(lDevice.device, stagingBuffers.vertices.memory.value, null)

            ...
        }
    }
}

注意:使用它是至关重要的,usePinned 否则你会得到垃圾。它暂时固定字节数组的本机内存地址。

接下来,我们需要UboVS 上课。它只包含字节数组中的模型,视图和投影矩阵,因此将其复制到缓冲区很简单。UniformBufferVars具有内存,缓冲区和描述符属性。

UniformBuffers 班级:

//
@ExperimentalUnsignedTypes
internal class UniformBuffers(
    private val pDevice: PhysicalDevice,
    private val lDevice: LogicalDevice,
    private val swapchain: SwapChain

) : DisposableContainer() {

    val uniformBufferVS: UniformBufferVars = UniformBufferVars()
    val uboVS: UboVS = UboVS(Mat4.ZERO, Mat4.ZERO, Mat4.ZERO)

    init {

        memScoped {

            val memReqs = alloc<VkMemoryRequirements>()

            uniformBufferVS.buffer = with(arena) { alloc() }
            uniformBufferVS.memory = with(arena) { alloc() }

            val bufferCreateInfo = alloc<VkBufferCreateInfo>().apply {
                sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO
                usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
                size = UboVS.SIZE.toULong()
            }

            if (!VK_CHECK(vkCreateBuffer(lDevice.device, bufferCreateInfo.ptr, 
                null, uniformBufferVS.buffer!!.ptr)))
                throw RuntimeException("failed to create buffer")

            val memoryAllocateInfo = alloc<VkMemoryAllocateInfo>().apply {
                sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO
                pNext = null
                allocationSize = 0u
                memoryTypeIndex = 0u
            }

            vkGetBufferMemoryRequirements(lDevice.device, 
                             uniformBufferVS.buffer!!.value, memReqs.ptr)
            memoryAllocateInfo.allocationSize = memReqs.size

            memoryAllocateInfo.memoryTypeIndex = pDevice.getMemoryType(
                memReqs.memoryTypeBits,
                VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT or 
                VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
            )

            if (!VK_CHECK(vkAllocateMemory(lDevice.device, 
                   memoryAllocateInfo.ptr, null, uniformBufferVS.memory!!.ptr)))
                throw RuntimeException("failed allocate memory")


            if (!VK_CHECK(
                    vkBindBufferMemory(
                        lDevice.device,
                        uniformBufferVS.buffer!!.value,
                        uniformBufferVS.memory!!.value,
                        0u
                    )
                )
            )
                throw  RuntimeException("faied bind memory")


            // Store information in the uniform's descriptor 
            // that is used by the descriptor set
            uniformBufferVS.descriptor = with(arena) { alloc() }
            uniformBufferVS.descriptor!!.offset = 0u
            uniformBufferVS.descriptor!!.buffer = uniformBufferVS.buffer!!.value
            uniformBufferVS.descriptor!!.range = UboVS.SIZE.toULong()
        }

        uboVS.modelMatrix = Mat4.identity

        update()
    }

    fun update() {

        uboVS.projectionMatrix = Mat4.perspective(
            radians(60f),
            swapchain.width.toInt().toFloat() / swapchain.height.toInt().toFloat(),
            0.1f, 256f
        )

        uboVS.viewMatrix = Mat4.translate(Mat4.identity, Vec3(0f, 0f, -5f))
        uboVS.modelMatrix = Mat4.rotate(uboVS.modelMatrix, radians(1f), Vec3(z = 1f))

        memScoped {

            val data = alloc<COpaquePointerVar>()

            if (!VK_CHECK(
                    vkMapMemory(
                        lDevice.device!!,
                        uniformBufferVS.memory!!.value,
                        0u,
                        UboVS.SIZE.toULong(),
                        0u,
                        data.ptr
                    )
                )
            )
                throw RuntimeException("failed bind memory")

            uboVS.buffer.usePinned { buffer ->
                platform.posix.memcpy(data.value, buffer.addressOf(0), 
                    UboVS.SIZE.toULong())
            }

            // Note: Since we requested a host coherent memory type for 
            // the uniform buffer, the write is instantly visible to the GPU
            vkUnmapMemory(lDevice.device, uniformBufferVS.memory!!.value)
        }
    }

 ...

}

好的,我们快要结束了。下一次,我们将添加帧缓冲区,命令缓冲区和绘图循环。

管道

在我们创建管道之前,我们需要管道缓存,描述符集布局,管道布局,描述符池和描述符集。它们也是以标准方式实现的renderpass,您可以在源代码中找到它们。我唯一要提的是 - 加载着色器。如果你还记得,我们编译它们并复制到assets文件夹。这里应该记住一件事 - usePinned将它们从文件系统加载到缓冲区时。

现在,我们将在渲染管道中定义所有固定状态:

@ExperimentalUnsignedTypes
internal class Pipeline(
    private val _device: LogicalDevice,
    private val _pipelineLayout: PipelineLayout,
    private val _renderPass: RenderPass,
    private val _pipelineCache: PipelineCache,
    private val _swapchain: SwapChain
) : DisposableContainer() {

    ...

    init {

        memScoped {

            // Shaders

            val shaderStages = allocArray<VkPipelineShaderStageCreateInfo>(2)

            val cwd = ByteArray(1024)
            cwd.usePinned {
                getcwd(it.addressOf(0), 1024)
            }

            var shaderFile = "${cwd.stringFromUtf8()}/assets/shaders/triangle.vert.spv"
            if(access(shaderFile, F_OK) == -1){
                shaderFile = "${cwd.stringFromUtf8()}
                  /build/bin/mingw/mainDebugExecutable/assets/shaders/triangle.vert.spv"
                if(access(shaderFile, F_OK) == -1){
                    shaderFile = "${cwd.stringFromUtf8()}/
                      build/bin/linux/mainDebugExecutable/assets/shaders/triangle.vert.spv"
                }
            }

            // Vertex shader
            shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO
            shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT
            shaderStages[0].module = _pipelineCache.loadShader(_device.device!!, shaderFile)
            shaderStages[0].pName = "main".cstr.ptr
            assert(shaderStages[0].module != null)

            shaderFile = "${cwd.stringFromUtf8()}/assets/shaders/triangle.frag.spv"
            if(access(shaderFile, F_OK) == -1){
                shaderFile = "${cwd.stringFromUtf8()}/build/
                    bin/mingw/mainDebugExecutable/assets/shaders/triangle.frag.spv"
                if(access(shaderFile, F_OK) == -1){
                    shaderFile = "${cwd.stringFromUtf8()}/build/bin/linux/
                                   mainDebugExecutable/assets/shaders/triangle.frag.spv"
                }
            }

            ...

            // Vertex input state 

            val pipelineVertexInputStateCreateInfo = 
                    alloc<VkPipelineVertexInputStateCreateInfo>().apply {
                sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO
                vertexBindingDescriptionCount = 1u
                pVertexBindingDescriptions = vertexInputBindingDescription.ptr
                vertexAttributeDescriptionCount = 2u
                pVertexAttributeDescriptions = vertexInputAttributs
            }

            // Input assembly state 

            val pipelineInputAssemblyStateCreateInfo = 
                      alloc<VkPipelineInputAssemblyStateCreateInfo>().apply {
                sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO
                topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST
                primitiveRestartEnable = 0u
            }

            // Viewport state 
            val viewport = alloc<VkViewport>().apply {
                x = 0.0f
                y = 0.0f
                width = _swapchain.width.toInt().toFloat()
                height = _swapchain.height.toInt().toFloat()
                minDepth = 0f
                maxDepth = 1f
            }

            val scissor = alloc<VkRect2D>().apply {
                offset.x = 0
                offset.y = 0
                extent.width = _swapchain.width
                extent.height = _swapchain.height
            }

            val pipelineViewportStateCreateInfo = 
                     alloc<VkPipelineViewportStateCreateInfo>().apply {
                sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO
                viewportCount = 1u
                scissorCount = 1u
                pViewports = viewport.ptr
                pScissors = scissor.ptr
            }

            // Rasterization state

            val pipelineRasterizationStateCreateInfo = 
                   alloc<VkPipelineRasterizationStateCreateInfo>().apply {
                sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO
                depthClampEnable = VK_FALSE.toUInt()
                rasterizerDiscardEnable = VK_FALSE.toUInt()
                polygonMode = VK_POLYGON_MODE_FILL
                cullMode = VK_CULL_MODE_BACK_BIT //VK_CULL_MODE_NONE
                lineWidth = 1.0f
                frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE
                depthBiasEnable = VK_FALSE.toUInt()
            }

            // Multi sampling state

            val pipelineMultisampleStateCreateInfo = 
                  alloc<VkPipelineMultisampleStateCreateInfo>().apply {
                sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO
                rasterizationSamples = VK_SAMPLE_COUNT_1_BIT
                pSampleMask = null
                sampleShadingEnable = 0u
            }

            // Color blend state 

            val blendAttachmentState = allocArray<VkPipelineColorBlendAttachmentState>(1)
            blendAttachmentState[0].apply {
                colorWriteMask = VK_COLOR_COMPONENT_R_BIT or 
                                 VK_COLOR_COMPONENT_G_BIT or VK_COLOR_COMPONENT_B_BIT or
                        VK_COLOR_COMPONENT_A_BIT //0xfu
                blendEnable = VK_FALSE.toUInt()
            }

            val pipelineColorBlendStateCreateInfo = 
                   alloc<VkPipelineColorBlendStateCreateInfo>().apply {
                sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO
                attachmentCount = 1u
                pAttachments = blendAttachmentState
                logicOpEnable = 0u
                logicOp = VK_LOGIC_OP_COPY
                blendConstants[0] = 0.0f
                blendConstants[1] = 0.0f
                blendConstants[2] = 0.0f
                blendConstants[3] = 0.0f
            }

            val pipelineCreateInfo = alloc<VkGraphicsPipelineCreateInfo>().apply {
                sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO
                // The layout used for this pipeline 
                // (can be shared among multiple pipelines using the same layout)
                layout = _pipelineLayout._pipelineLayout.value
                //Renderpass this pipeline is attached to
                renderPass = _renderPass.renderPass.value
            }

            // Enable dynamic states

            val dynamicStateEnables = allocArray<VkDynamicStateVar>(2)
            dynamicStateEnables[0] = VK_DYNAMIC_STATE_VIEWPORT
            dynamicStateEnables[1] = VK_DYNAMIC_STATE_SCISSOR

            val pipelineDynamicStateCreateInfo = 
                    alloc<VkPipelineDynamicStateCreateInfo>().apply {
                sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO
                pDynamicStates = dynamicStateEnables
                dynamicStateCount = 2u
            }

            // Depth and stencil state 

            val pipelineDepthStencilStateCreateInfo = 
                      alloc<VkPipelineDepthStencilStateCreateInfo>().apply {
                sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO
                depthTestEnable = VK_TRUE.toUInt()
                depthWriteEnable = VK_TRUE.toUInt()
                depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL
                depthBoundsTestEnable = VK_FALSE.toUInt()
                back.failOp = VK_STENCIL_OP_KEEP
                back.passOp = VK_STENCIL_OP_KEEP
                back.compareOp = VK_COMPARE_OP_ALWAYS
                stencilTestEnable = VK_FALSE.toUInt()
                front.failOp = VK_STENCIL_OP_KEEP
                front.passOp = VK_STENCIL_OP_KEEP
                front.compareOp = VK_COMPARE_OP_ALWAYS
            }

            pipelineCreateInfo.stageCount = 2u
            pipelineCreateInfo.pStages = shaderStages

            pipelineCreateInfo.pVertexInputState = pipelineVertexInputStateCreateInfo.ptr
            pipelineCreateInfo.pInputAssemblyState = pipelineInputAssemblyStateCreateInfo.ptr
            pipelineCreateInfo.pRasterizationState = pipelineRasterizationStateCreateInfo.ptr
            pipelineCreateInfo.pColorBlendState = pipelineColorBlendStateCreateInfo.ptr
            pipelineCreateInfo.pMultisampleState = pipelineMultisampleStateCreateInfo.ptr
            pipelineCreateInfo.pViewportState = pipelineViewportStateCreateInfo.ptr
            pipelineCreateInfo.pDepthStencilState = pipelineDepthStencilStateCreateInfo.ptr
            pipelineCreateInfo.renderPass = _renderPass.renderPass.value
            pipelineCreateInfo.pDynamicState = pipelineDynamicStateCreateInfo.ptr

            if (!VK_CHECK(
                    vkCreateGraphicsPipelines(
                        _device.device,
                        _pipelineCache.value,
                        1u,
                        pipelineCreateInfo.ptr,
                        null,
                        _pipeline.ptr
                    )
                )
            )
                throw RuntimeException("failed create pipeline")

            vkDestroyShaderModule(_device.device, shaderStages[0].module, null)
            vkDestroyShaderModule(_device.device, shaderStages[1].module, null)
        }
    }

...

}
Vulkan API与Kotlin Native - Swapchain,Pipeline 转载https://www.codesocang.com/appboke/39914.html

技术博客阅读排行

最新文章