Skip to content

MeshMesh.kt

src/jvmMain/kotlin/afield/graphics/render/MeshMesh.kt · 214 lines

package afield.graphics.render

import afield.document.context.fromContext
import org.lwjgl.vulkan.EXTMeshShader.VK_SHADER_STAGE_MESH_BIT_EXT
import org.lwjgl.vulkan.EXTMeshShader.VK_SHADER_STAGE_TASK_BIT_EXT
import org.lwjgl.vulkan.EXTMeshShader.vkCmdDrawMeshTasksEXT
import org.lwjgl.vulkan.EXTMeshShader.vkCmdDrawMeshTasksIndirectEXT
import afield.graphics.vulkan.buffers.FrameBufferGroup
import afield.graphics.vulkan.Renderer
import afield.graphics.vulkan.buffers.MissingRefException
import afield.graphics.vulkan.buffers.Structure
import afield.graphics.vulkan.buffers.bindPushConstant
import afield.graphics.vulkan.buffers.getBoundPushConstant
import afield.graphics.vulkan.buffers.getPushConstant
import afield.graphics.vulkan.buffers.setPushConstant
import afield.graphics.vulkan.device.CommandBuffer
import afield.graphics.vulkan.util.MapOfSequences
import afield.app.diagnostics.logging.lprintln
import org.lwjgl.system.MemoryStack
import org.lwjgl.vulkan.VK10
import java.nio.ByteBuffer
import kotlin.reflect.KProperty

The MeshTask interface for issuing mesh-shader work, and MeshMesh, a task whose push constants and uniforms are bound to Kotlin properties by name.

meshTask wraps a lambda as a MeshTask.

interface MeshTask

fun compute(inside: MeshModule, cmd: CommandBuffer, pushConstantBuffer: ByteBuffer)

}

fun meshTask(c: (inside: MeshModule, cmd: CommandBuffer, pushConstantBuffer: ByteBuffer) -> Unit): MeshTask {

fun meshTask(c: (inside: MeshModule, cmd: CommandBuffer, pushConstantBuffer: ByteBuffer) -> Unit): MeshTask {
    return object : MeshTask {
        override fun compute(
            inside: MeshModule,
            cmd: CommandBuffer,
            pushConstantBuffer: ByteBuffer
        ) {
            c(inside, cmd, pushConstantBuffer)
        }
    }
}

class MeshMesh(var renderer: Renderer = fromContext()) : MeshTask

As names go, this one is pretty bad

class MeshMesh(var renderer: Renderer = fromContext()) : MeshTask {

var indirect: FrameBufferGroup? = null

GPU-written VkDrawMeshTasksIndirectCommandEXT; null keeps direct x/y/z dispatch.

var indirectOffset: Long = 0L

Byte offset of the three uint group counts, aligned to four bytes.

fun prepareForDraw(command: CommandBuffer)

Transfers must be prepared outside dynamic rendering, like a conventional mesh's buffers.

    fun prepareForDraw(command: CommandBuffer) {
        indirect?.let { group ->
            require(indirectOffset >= 0 && indirectOffset % 4L == 0L)
            group.getGPUAddress(command)
            require(indirectOffset + 12L <= group.getLastWritten().requestedSize)
        }
    }

class FromUniform

operator fun getValue(inside: MeshModulePointer, property: KProperty<*>): Structure

        operator fun getValue(inside: MeshModulePointer, property: KProperty<*>): Structure {
            return ModuleRebuild.access(inside, property.name, inside.current.liveUniformStructures[property.name]
                ?: throw IllegalArgumentException(" can't find '${property.name}' in this module, options are ${inside.current.liveUniformStructures.keys}"))
        }
    }

class BoundFromStructure<T>

operator fun setValue(inside: MeshMesh, property: KProperty<*>, any: () -> T) {

        operator fun setValue(inside: MeshMesh, property: KProperty<*>, any: () -> T) {
            if (inside.push.offsets.isEmpty()) inside.initializePushConstantsFromModule(inside.inside)
            inside.push.bindPushConstant(property.name, any) {
                throw IllegalArgumentException(" can't find ${property.name} in a mesh in this module ${inside.inside.current.parameters.name} as a push_constant")
            }
        }

operator fun getValue(inside: MeshMesh, property: KProperty<*>): () -> T {

        operator fun getValue(inside: MeshMesh, property: KProperty<*>): () -> T {
            if (inside.push.offsets.isEmpty()) inside.initializePushConstantsFromModule(inside.inside)
            return inside.push.getBoundPushConstant(property.name) {
                throw IllegalArgumentException(" can't find ${property.name} in a mesh in this module ${inside.inside.current.parameters.name} as a push_constant or a UBO structure")
            }
        }
    }

constructor(renderer: Renderer = fromContext(), pointer: MeshModulePointer = fromContext()) : this(renderer)

    constructor(renderer: Renderer = fromContext(), pointer: MeshModulePointer = fromContext()) : this(renderer) {
        this.inside = pointer
    }

lateinit var inside: MeshModulePointer

var update = MapOfSequences<String>()

var push = Structure()

var x = 1;

var y = 1;

var z = 1;

internal fun initializePushConstantsFromModule(inside: MeshModulePointer)

    internal fun initializePushConstantsFromModule(inside: MeshModulePointer) {
        for (pc in inside.current.fi.pushConstants) {
            push.setMembers(pc.members)
        }
        for (pc in inside.current.mi.pushConstants) {
            push.setMembers(pc.members)
        }
        for (pc in inside.current.ti.pushConstants) {
            push.setMembers(pc.members)
        }
    }

class FromStructure<T>

operator fun setValue(inside: MeshMesh, property: KProperty<*>, any: T)

        operator fun setValue(inside: MeshMesh, property: KProperty<*>, any: T) {
            if (inside.push.offsets.isEmpty()) inside.initializePushConstantsFromModule(inside.inside)
            inside.push.setPushConstant(property.name, any) {
                throw IllegalArgumentException(" can't find ${property.name} in a mesh in this module ${inside.inside.current.parameters.name} as a push_constant")
            }
        }

operator fun getValue(inside: MeshMesh, property: KProperty<*>): T

        operator fun getValue(inside: MeshMesh, property: KProperty<*>): T {
            if (inside.push.offsets.isEmpty()) inside.initializePushConstantsFromModule(inside.inside)
            return inside.push.getPushConstant(property.name) {
//            else if (property.name in inside.inside.current.liveUniformStructures) {
//                return inside.inside.current.liveUniformStructures[property.name] as T
//            }
                throw IllegalArgumentException(" can't find ${property.name} in a mesh in this module ${inside.inside.current.parameters.name} as a push_constant or a UBO structure")
            }
        }
    }

override fun compute(inside: MeshModule, cmd: CommandBuffer, pushConstantBuffer: ByteBuffer)

body — 80 lines, from line 134
    override fun compute(inside: MeshModule, cmd: CommandBuffer, pushConstantBuffer: ByteBuffer) {
        if (inside != this.inside.current) lprintln("warning,graphics", "MeshMesh built in a different module ${inside}!=${this.inside.current}")
        update.update()

        MemoryStack.stackPush().use { _ ->
            fun pushConstantsForCurrentDispatch(): Boolean {
                if (pushConstantBuffer.limit() <= 0) return true
                for (pc in inside.fi.pushConstants) {
                    push.setMembers(pc.members)
                }
                for (pc in inside.mi.pushConstants) {
                    push.setMembers(pc.members)
                }
                for (pc in inside.ti.pushConstants) {
                    push.setMembers(pc.members)
                }
                try {
                    pushConstantBuffer.clear()
                    push.writeToBuffer(pushConstantBuffer, cmd) {
                        throw MissingRefException(afield.graphics.vulkan.buffers.unassignedMemberMessage(it))
                    }
                } catch (e: Exception) {
                    inside.parameters.errorHandler(e.message ?: "push constant error", e)
                    lprintln("error,graphics", "push constant error, not dispatching mesh task : ${e.message}")
                    return false
                }
                pushConstantBuffer.clear()
                VK10.vkCmdPushConstants(
                    cmd.vkCommandBuffer,
                    inside.pipelineLayout,
                    VK_SHADER_STAGE_TASK_BIT_EXT or VK_SHADER_STAGE_MESH_BIT_EXT or VK10.VK_SHADER_STAGE_FRAGMENT_BIT,
                    0,
                    pushConstantBuffer
                )
                return true
            }

            fun dispatchForCurrentWriter(dispatchX: Int?, dispatchY: Int?, dispatchZ: Int?) {
                indirect?.let {
                    vkCmdDrawMeshTasksIndirectEXT(cmd.vkCommandBuffer, it.getLastWritten().buffer, indirectOffset, 1, 12)
                    return
                }
                val xx = dispatchX ?: x
                val yy = dispatchY ?: y
                val zz = dispatchZ ?: z
                if (xx * yy * zz > 0) {
                    vkCmdDrawMeshTasksEXT(cmd.vkCommandBuffer, xx, yy, zz)
                } else {
                    lprintln("warning,graphics", "MeshMesh has no work to do $xx $yy $zz")
                }
            }

            try {
                forEachFanOutDispatch(
                    renderer = renderer,
                    push = push,
                    onNoFanOut = {
                        if (pushConstantsForCurrentDispatch()) {
                            dispatchForCurrentWriter(null, null, null)
                        }
                    },
                    onWriter = { writer ->
                        if (pushConstantsForCurrentDispatch()) {
                            dispatchForCurrentWriter(
                                dispatchX = writer.publication.dispatchX,
                                dispatchY = writer.publication.dispatchY,
                                dispatchZ = writer.publication.dispatchZ
                            )
                        }
                    }
                )
            } catch (e: Exception) {
                inside.parameters.errorHandler(e.message ?: "fan-out dispatch error", e)
                lprintln("error,graphics", "fan-out dispatch error, not dispatching mesh task : ${e.message}")
            }
        }
    }


}