diff --git a/src/main/kotlin/vulkan/basics/04 Dynamic Uniform Buffers.kt b/src/main/kotlin/vulkan/basics/04 Dynamic Uniform Buffers.kt index 5431f65..fc7212f 100644 --- a/src/main/kotlin/vulkan/basics/04 Dynamic Uniform Buffers.kt +++ b/src/main/kotlin/vulkan/basics/04 Dynamic Uniform Buffers.kt @@ -1,341 +1,341 @@ -/* -* Vulkan Example - Dynamic uniform buffers -* -* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de -* -* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -* -* Summary: -* Demonstrates the use of dynamic uniform buffers. -* -* Instead of using one uniform buffer per-object, this example allocates one big uniform buffer -* with respect to the alignment reported by the device via minUniformBufferOffsetAlignment that -* contains all matrices for the objects in the scene. -* -* The used descriptor type VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC then allows to set a dynamic -* offset used to pass data from the single uniform buffer to the connected shader binding point. -*/ - -package vulkan.basics - -import glm_.L -import glm_.glm -import glm_.i -import glm_.mat4x4.Mat4 -import glm_.pow -import glm_.vec3.Vec3 -import glm_.vec3.operators.times -import kool.adr -import kool.cap -import kool.free -import kool.stak -import org.lwjgl.system.MemoryUtil.NULL -import org.lwjgl.vulkan.VkPipelineVertexInputStateCreateInfo -import org.lwjgl.vulkan.VkVertexInputAttributeDescription -import org.lwjgl.vulkan.VkVertexInputBindingDescription -import vkk.* -import vulkan.VERTEX_BUFFER_BIND_ID -import vulkan.assetPath -import vulkan.base.Buffer -import vulkan.base.Camera -import vulkan.base.VulkanExampleBase -import vulkan.base.initializers -import java.nio.ByteBuffer - -private const val OBJECT_INSTANCES = 125 - - -fun main(args: Array) { - DynamicUniformBuffers().apply { - setupWindow() - initVulkan() - prepare() - renderLoop() - destroy() - } -} - -private class DynamicUniformBuffers : VulkanExampleBase() { - - /** Vertex layout for this example */ - object Vertex { - // float pos[3]; -// float color[3]; - val size = Vec3.size * 2 - val offsetPos = 0 - val offsetColor = Vec3.size - } - - object vertices { - lateinit var inputState: VkPipelineVertexInputStateCreateInfo - lateinit var bindingDescriptions: VkVertexInputBindingDescription - lateinit var attributeDescriptions: VkVertexInputAttributeDescription.Buffer - } - - val vertexBuffer = Buffer() - val indexBuffer = Buffer() - var indexCount = 0 - - object uniformBuffers { - val view = Buffer() - val dynamic = Buffer() - } - - object uboVS : Bufferizable() { - val projection = Mat4() - val view = Mat4() - } - - // Store random per-object rotations - val rotations = Array(OBJECT_INSTANCES) { Vec3() } - val rotationSpeeds = Array(OBJECT_INSTANCES) { Vec3() } - - /** One big uniform buffer that contains all matrices - * Note that we need to manually allocate the data to cope for GPU-specific uniform buffer offset alignments */ - object uboDataDynamic { - lateinit var model: ByteBuffer - var address = NULL - } - - var pipeline = VkPipeline(NULL) - var pipelineLayout = VkPipelineLayout(NULL) - var descriptorSet = VkDescriptorSet(NULL) - var descriptorSetLayout = VkDescriptorSetLayout(NULL) - - var animationTimer = 0f - - var dynamicAlignment = 0L - - init { - title = "Dynamic uniform buffers" - camera.type = Camera.CameraType.lookAt - camera.setPosition(Vec3(0f, 0f, -30f)) - camera.setRotation(Vec3(.0f)) - camera.setPerspective(60f, size.aspect, 0.1f, 256f) -// settings.overlay = true TODO - } - - override fun destroy() { - - uboDataDynamic.model.free() - - /* Clean up used Vulkan resources - Note : Inherited destructor cleans up resources stored in base class */ - device.apply { - destroyPipeline(pipeline) - - destroyPipelineLayout(pipelineLayout) - destroyDescriptorSetLayout(descriptorSetLayout) - } - - vertexBuffer.destroy() - indexBuffer.destroy() - - uniformBuffers.view.destroy() - uniformBuffers.dynamic.destroy() - - super.destroy() - } - - override fun buildCommandBuffers() { - - val cmdBufInfo = vk.CommandBufferBeginInfo() - - val clearValues = vk.ClearValue(2).also { - it[0].color(defaultClearColor) - it[1].depthStencil(1f, 0) - } - val renderPassBeginInfo = vk.RenderPassBeginInfo { - renderPass = this@DynamicUniformBuffers.renderPass - renderArea.apply { - offset(0) - extent(size) - } - this.clearValues = clearValues - } - - for (i in drawCmdBuffers.indices) { - - renderPassBeginInfo.framebuffer(frameBuffers[i].L) - - drawCmdBuffers[i].apply { - - begin(cmdBufInfo) - - beginRenderPass(renderPassBeginInfo, VkSubpassContents.INLINE) - - setViewport(size) - setScissor(size) - - bindPipeline(VkPipelineBindPoint.GRAPHICS, pipeline) - - bindVertexBuffers(VERTEX_BUFFER_BIND_ID, vertexBuffer.buffer) - bindIndexBuffer(indexBuffer.buffer, VkDeviceSize(0), VkIndexType.UINT32) - - // Render multiple objects using different model matrices by dynamically offsetting into one uniform buffer - repeat(OBJECT_INSTANCES) { - // One dynamic offset per dynamic descriptor to offset into the ubo containing all model matrices - val dynamicOffset = it * dynamicAlignment - // Bind the descriptor set for rendering a mesh using the dynamic offset - bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, pipelineLayout, descriptorSet, dynamicOffset.i) - - drawIndexed(indexCount, 1, 0, 0, 0) - } - - drawUI() - - endRenderPass() - - end() - } - } - } - - fun draw() { - - super.prepareFrame() - - // Command buffer to be submitted to the queue - submitInfo.commandBuffer = drawCmdBuffers[currentBuffer] - - // Submit to queue - queue submit submitInfo - - super.submitFrame() - } - - fun generateCube() = stak { - // Setup vertices indices for a colored cube - val vertices = it.floats( - -1f, -1f, +1f, 1f, 0f, 0f, - +1f, -1f, +1f, 0f, 1f, 0f, - +1f, +1f, +1f, 0f, 0f, 1f, - -1f, +1f, +1f, 0f, 0f, 0f, - -1f, -1f, -1f, 1f, 0f, 0f, - +1f, -1f, -1f, 0f, 1f, 0f, - +1f, +1f, -1f, 0f, 0f, 1f, - -1f, +1f, -1f, 0f, 0f, 0f) - - val indices = it.ints(0, 1, 2, 2, 3, 0, 1, 5, 6, 6, 2, 1, 7, 6, 5, 5, 4, 7, 4, 0, 3, 3, 7, 4, 4, 5, 1, 1, 0, 4, 3, 2, 6, 6, 7, 3) - - indexCount = indices.cap - - // Create buffers - // For the sake of simplicity we won't stage the vertex data to the gpu memory - val flags = VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT - // Vertex buffer - vulkanDevice.createBuffer(VkBufferUsage.VERTEX_BUFFER_BIT.i, flags, vertexBuffer, vertices) - // Index buffer - vulkanDevice.createBuffer(VkBufferUsage.INDEX_BUFFER_BIT.i, flags, indexBuffer, indices) - } - - fun setupVertexDescriptions() { - // Binding description - vertices.bindingDescriptions = vk.VertexInputBindingDescription(VERTEX_BUFFER_BIND_ID, Vertex.size, VkVertexInputRate.VERTEX) - - // Attribute descriptions - vertices.attributeDescriptions = vk.VertexInputAttributeDescription( - // Location 0 : Position - VERTEX_BUFFER_BIND_ID, 0, VkFormat.R32G32B32_SFLOAT, Vertex.offsetPos, - // Location 1 : Color - VERTEX_BUFFER_BIND_ID, 1, VkFormat.R32G32B32_SFLOAT, Vertex.offsetColor) - - vertices.inputState = vk.PipelineVertexInputStateCreateInfo { - vertexBindingDescription = vertices.bindingDescriptions - vertexAttributeDescriptions = vertices.attributeDescriptions - } - } - - fun setupDescriptorPool() { - // Example uses one ubo and one image sampler - val poolSizes = vk.DescriptorPoolSize( - VkDescriptorType.UNIFORM_BUFFER, 1, - VkDescriptorType.UNIFORM_BUFFER_DYNAMIC, 1, - VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1) - - descriptorPool = device createDescriptorPool initializers.descriptorPoolCreateInfo(poolSizes, 2) - } - - fun setupDescriptorSetLayout() { - - val setLayoutBindings = vk.DescriptorSetLayoutBinding( - VkDescriptorType.UNIFORM_BUFFER, VkShaderStage.VERTEX_BIT.i, 0, - VkDescriptorType.UNIFORM_BUFFER_DYNAMIC, VkShaderStage.VERTEX_BIT.i, 1, - VkDescriptorType.COMBINED_IMAGE_SAMPLER, VkShaderStage.FRAGMENT_BIT.i, 2) - - val descriptorLayout = vk.DescriptorSetLayoutCreateInfo { bindings = setLayoutBindings } - - descriptorSetLayout = device createDescriptorSetLayout descriptorLayout - - val pipelineLayoutCreateInfo = vk.PipelineLayoutCreateInfo { setLayout = descriptorSetLayout } - - pipelineLayout = device createPipelineLayout pipelineLayoutCreateInfo - } - - fun setupDescriptorSet() { - - val allocInfo = vk.DescriptorSetAllocateInfo(descriptorPool, descriptorSetLayout) - - descriptorSet = device allocateDescriptorSets allocInfo - - val writeDescriptorSets = vk.WriteDescriptorSet( - // Binding 0 : Projection/View matrix uniform buffer - descriptorSet, VkDescriptorType.UNIFORM_BUFFER, 0, uniformBuffers.view.descriptor, - // Binding 1 : Instance matrix as dynamic uniform buffer - descriptorSet, VkDescriptorType.UNIFORM_BUFFER_DYNAMIC, 1, uniformBuffers.dynamic.descriptor) - - device updateDescriptorSets writeDescriptorSets - } - - fun preparePipelines() { - - val inputAssemblyState = vk.PipelineInputAssemblyStateCreateInfo(VkPrimitiveTopology.TRIANGLE_LIST, 0, false) - - val rasterizationState = vk.PipelineRasterizationStateCreateInfo(VkPolygonMode.FILL, VkCullMode.NONE.i, VkFrontFace.COUNTER_CLOCKWISE) - - val blendAttachmentState = vk.PipelineColorBlendAttachmentState(0xf, false) - - val colorBlendState = vk.PipelineColorBlendStateCreateInfo(blendAttachmentState) - - val depthStencilState = vk.PipelineDepthStencilStateCreateInfo(true, true, VkCompareOp.LESS_OR_EQUAL) - - val viewportState = vk.PipelineViewportStateCreateInfo(1, 1) - - val multisampleState = vk.PipelineMultisampleStateCreateInfo(rasterizationSamples = VkSampleCount.`1_BIT`) - - val dynamicStateEnables = listOf(VkDynamicState.VIEWPORT, VkDynamicState.SCISSOR) - - val dynamicState = vk.PipelineDynamicStateCreateInfo(dynamicStateEnables) - - // Load shaders - val shaderStages = vk.PipelineShaderStageCreateInfo(2).also { - it[0].loadShader("$assetPath/shaders/dynamicuniformbuffer/base.vert.spv", VkShaderStage.VERTEX_BIT) - it[1].loadShader("$assetPath/shaders/dynamicuniformbuffer/base.frag.spv", VkShaderStage.FRAGMENT_BIT) - } - - val pipelineCreateInfo = vk.GraphicsPipelineCreateInfo(pipelineLayout, renderPass).also { - it.vertexInputState = vertices.inputState - it.inputAssemblyState = inputAssemblyState - it.rasterizationState = rasterizationState - it.colorBlendState = colorBlendState - it.multisampleState = multisampleState - it.viewportState = viewportState - it.depthStencilState = depthStencilState - it.dynamicState = dynamicState - it.stages = shaderStages - } - - pipeline = device.createGraphicsPipelines(pipelineCache, pipelineCreateInfo) - } - - /** Prepare and initialize uniform buffer containing shader uniforms */ - fun prepareUniformBuffers() { - /* Allocate data for the dynamic uniform buffer object - We allocate this manually as the alignment of the offset differs between GPUs */ - - // Calculate required alignment based on minimum device offset alignment - val minUboAlignment = vulkanDevice.properties.limits.minUniformBufferOffsetAlignment - dynamicAlignment = Mat4.size.L +///* +//* Vulkan Example - Dynamic uniform buffers +//* +//* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de +//* +//* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +//* +//* Summary: +//* Demonstrates the use of dynamic uniform buffers. +//* +//* Instead of using one uniform buffer per-object, this example allocates one big uniform buffer +//* with respect to the alignment reported by the device via minUniformBufferOffsetAlignment that +//* contains all matrices for the objects in the scene. +//* +//* The used descriptor type VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC then allows to set a dynamic +//* offset used to pass data from the single uniform buffer to the connected shader binding point. +//*/ +// +//package vulkan.basics +// +//import glm_.L +//import glm_.glm +//import glm_.i +//import glm_.mat4x4.Mat4 +//import glm_.pow +//import glm_.vec3.Vec3 +//import glm_.vec3.operators.times +//import kool.adr +//import kool.cap +//import kool.free +//import kool.stak +//import org.lwjgl.system.MemoryUtil.NULL +//import org.lwjgl.vulkan.VkPipelineVertexInputStateCreateInfo +//import org.lwjgl.vulkan.VkVertexInputAttributeDescription +//import org.lwjgl.vulkan.VkVertexInputBindingDescription +//import vkk.* +//import vulkan.VERTEX_BUFFER_BIND_ID +//import vulkan.assetPath +//import vulkan.base.Buffer +//import vulkan.base.Camera +//import vulkan.base.VulkanExampleBase +//import vulkan.base.initializers +//import java.nio.ByteBuffer +// +//private const val OBJECT_INSTANCES = 125 +// +// +//fun main(args: Array) { +// DynamicUniformBuffers().apply { +// setupWindow() +// initVulkan() +// prepare() +// renderLoop() +// destroy() +// } +//} +// +//private class DynamicUniformBuffers : VulkanExampleBase() { +// +// /** Vertex layout for this example */ +// object Vertex { +// // float pos[3]; +//// float color[3]; +// val size = Vec3.size * 2 +// val offsetPos = 0 +// val offsetColor = Vec3.size +// } +// +// object vertices { +// lateinit var inputState: VkPipelineVertexInputStateCreateInfo +// lateinit var bindingDescriptions: VkVertexInputBindingDescription +// lateinit var attributeDescriptions: VkVertexInputAttributeDescription.Buffer +// } +// +// val vertexBuffer = Buffer() +// val indexBuffer = Buffer() +// var indexCount = 0 +// +// object uniformBuffers { +// val view = Buffer() +// val dynamic = Buffer() +// } +// +// object uboVS : Bufferizable() { +// val projection = Mat4() +// val view = Mat4() +// } +// +// // Store random per-object rotations +// val rotations = Array(OBJECT_INSTANCES) { Vec3() } +// val rotationSpeeds = Array(OBJECT_INSTANCES) { Vec3() } +// +// /** One big uniform buffer that contains all matrices +// * Note that we need to manually allocate the data to cope for GPU-specific uniform buffer offset alignments */ +// object uboDataDynamic { +// lateinit var model: ByteBuffer +// var address = NULL +// } +// +// var pipeline = VkPipeline(NULL) +// var pipelineLayout = VkPipelineLayout(NULL) +// var descriptorSet = VkDescriptorSet(NULL) +// var descriptorSetLayout = VkDescriptorSetLayout(NULL) +// +// var animationTimer = 0f +// +// var dynamicAlignment = 0L +// +// init { +// title = "Dynamic uniform buffers" +// camera.type = Camera.CameraType.lookAt +// camera.setPosition(Vec3(0f, 0f, -30f)) +// camera.setRotation(Vec3(.0f)) +// camera.setPerspective(60f, size.aspect, 0.1f, 256f) +//// settings.overlay = true TODO +// } +// +// override fun destroy() { +// +// uboDataDynamic.model.free() +// +// /* Clean up used Vulkan resources +// Note : Inherited destructor cleans up resources stored in base class */ +// device.apply { +// destroyPipeline(pipeline) +// +// destroyPipelineLayout(pipelineLayout) +// destroyDescriptorSetLayout(descriptorSetLayout) +// } +// +// vertexBuffer.destroy() +// indexBuffer.destroy() +// +// uniformBuffers.view.destroy() +// uniformBuffers.dynamic.destroy() +// +// super.destroy() +// } +// +// override fun buildCommandBuffers() { +// +// val cmdBufInfo = vk.CommandBufferBeginInfo() +// +// val clearValues = vk.ClearValue(2).also { +// it[0].color(defaultClearColor) +// it[1].depthStencil(1f, 0) +// } +// val renderPassBeginInfo = vk.RenderPassBeginInfo { +// renderPass = this@DynamicUniformBuffers.renderPass +// renderArea.apply { +// offset(0) +// extent(size) +// } +// this.clearValues = clearValues +// } +// +// for (i in drawCmdBuffers.indices) { +// +// renderPassBeginInfo.framebuffer(frameBuffers[i].L) +// +// drawCmdBuffers[i].apply { +// +// begin(cmdBufInfo) +// +// beginRenderPass(renderPassBeginInfo, VkSubpassContents.INLINE) +// +// setViewport(size) +// setScissor(size) +// +// bindPipeline(VkPipelineBindPoint.GRAPHICS, pipeline) +// +// bindVertexBuffers(VERTEX_BUFFER_BIND_ID, vertexBuffer.buffer) +// bindIndexBuffer(indexBuffer.buffer, VkDeviceSize(0), VkIndexType.UINT32) +// +// // Render multiple objects using different model matrices by dynamically offsetting into one uniform buffer +// repeat(OBJECT_INSTANCES) { +// // One dynamic offset per dynamic descriptor to offset into the ubo containing all model matrices +// val dynamicOffset = it * dynamicAlignment +// // Bind the descriptor set for rendering a mesh using the dynamic offset +// bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, pipelineLayout, descriptorSet, dynamicOffset.i) +// +// drawIndexed(indexCount, 1, 0, 0, 0) +// } +// +// drawUI() +// +// endRenderPass() +// +// end() +// } +// } +// } +// +// fun draw() { +// +// super.prepareFrame() +// +// // Command buffer to be submitted to the queue +// submitInfo.commandBuffer = drawCmdBuffers[currentBuffer] +// +// // Submit to queue +// queue submit submitInfo +// +// super.submitFrame() +// } +// +// fun generateCube() = stak { +// // Setup vertices indices for a colored cube +// val vertices = it.floats( +// -1f, -1f, +1f, 1f, 0f, 0f, +// +1f, -1f, +1f, 0f, 1f, 0f, +// +1f, +1f, +1f, 0f, 0f, 1f, +// -1f, +1f, +1f, 0f, 0f, 0f, +// -1f, -1f, -1f, 1f, 0f, 0f, +// +1f, -1f, -1f, 0f, 1f, 0f, +// +1f, +1f, -1f, 0f, 0f, 1f, +// -1f, +1f, -1f, 0f, 0f, 0f) +// +// val indices = it.ints(0, 1, 2, 2, 3, 0, 1, 5, 6, 6, 2, 1, 7, 6, 5, 5, 4, 7, 4, 0, 3, 3, 7, 4, 4, 5, 1, 1, 0, 4, 3, 2, 6, 6, 7, 3) +// +// indexCount = indices.cap +// +// // Create buffers +// // For the sake of simplicity we won't stage the vertex data to the gpu memory +// val flags = VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT +// // Vertex buffer +// vulkanDevice.createBuffer(VkBufferUsage.VERTEX_BUFFER_BIT.i, flags, vertexBuffer, vertices) +// // Index buffer +// vulkanDevice.createBuffer(VkBufferUsage.INDEX_BUFFER_BIT.i, flags, indexBuffer, indices) +// } +// +// fun setupVertexDescriptions() { +// // Binding description +// vertices.bindingDescriptions = vk.VertexInputBindingDescription(VERTEX_BUFFER_BIND_ID, Vertex.size, VkVertexInputRate.VERTEX) +// +// // Attribute descriptions +// vertices.attributeDescriptions = vk.VertexInputAttributeDescription( +// // Location 0 : Position +// VERTEX_BUFFER_BIND_ID, 0, VkFormat.R32G32B32_SFLOAT, Vertex.offsetPos, +// // Location 1 : Color +// VERTEX_BUFFER_BIND_ID, 1, VkFormat.R32G32B32_SFLOAT, Vertex.offsetColor) +// +// vertices.inputState = vk.PipelineVertexInputStateCreateInfo { +// vertexBindingDescription = vertices.bindingDescriptions +// vertexAttributeDescriptions = vertices.attributeDescriptions +// } +// } +// +// fun setupDescriptorPool() { +// // Example uses one ubo and one image sampler +// val poolSizes = vk.DescriptorPoolSize( +// VkDescriptorType.UNIFORM_BUFFER, 1, +// VkDescriptorType.UNIFORM_BUFFER_DYNAMIC, 1, +// VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1) +// +// descriptorPool = device createDescriptorPool initializers.descriptorPoolCreateInfo(poolSizes, 2) +// } +// +// fun setupDescriptorSetLayout() { +// +// val setLayoutBindings = vk.DescriptorSetLayoutBinding( +// VkDescriptorType.UNIFORM_BUFFER, VkShaderStage.VERTEX_BIT.i, 0, +// VkDescriptorType.UNIFORM_BUFFER_DYNAMIC, VkShaderStage.VERTEX_BIT.i, 1, +// VkDescriptorType.COMBINED_IMAGE_SAMPLER, VkShaderStage.FRAGMENT_BIT.i, 2) +// +// val descriptorLayout = vk.DescriptorSetLayoutCreateInfo { bindings = setLayoutBindings } +// +// descriptorSetLayout = device createDescriptorSetLayout descriptorLayout +// +// val pipelineLayoutCreateInfo = vk.PipelineLayoutCreateInfo { setLayout = descriptorSetLayout } +// +// pipelineLayout = device createPipelineLayout pipelineLayoutCreateInfo +// } +// +// fun setupDescriptorSet() { +// +// val allocInfo = vk.DescriptorSetAllocateInfo(descriptorPool, descriptorSetLayout) +// +// descriptorSet = device allocateDescriptorSets allocInfo +// +// val writeDescriptorSets = vk.WriteDescriptorSet( +// // Binding 0 : Projection/View matrix uniform buffer +// descriptorSet, VkDescriptorType.UNIFORM_BUFFER, 0, uniformBuffers.view.descriptor, +// // Binding 1 : Instance matrix as dynamic uniform buffer +// descriptorSet, VkDescriptorType.UNIFORM_BUFFER_DYNAMIC, 1, uniformBuffers.dynamic.descriptor) +// +// device updateDescriptorSets writeDescriptorSets +// } +// +// fun preparePipelines() { +// +// val inputAssemblyState = vk.PipelineInputAssemblyStateCreateInfo(VkPrimitiveTopology.TRIANGLE_LIST, 0, false) +// +// val rasterizationState = vk.PipelineRasterizationStateCreateInfo(VkPolygonMode.FILL, VkCullMode.NONE.i, VkFrontFace.COUNTER_CLOCKWISE) +// +// val blendAttachmentState = vk.PipelineColorBlendAttachmentState(0xf, false) +// +// val colorBlendState = vk.PipelineColorBlendStateCreateInfo(blendAttachmentState) +// +// val depthStencilState = vk.PipelineDepthStencilStateCreateInfo(true, true, VkCompareOp.LESS_OR_EQUAL) +// +// val viewportState = vk.PipelineViewportStateCreateInfo(1, 1) +// +// val multisampleState = vk.PipelineMultisampleStateCreateInfo(rasterizationSamples = VkSampleCount.`1_BIT`) +// +// val dynamicStateEnables = listOf(VkDynamicState.VIEWPORT, VkDynamicState.SCISSOR) +// +// val dynamicState = vk.PipelineDynamicStateCreateInfo(dynamicStateEnables) +// +// // Load shaders +// val shaderStages = vk.PipelineShaderStageCreateInfo(2).also { +// it[0].loadShader("$assetPath/shaders/dynamicuniformbuffer/base.vert.spv", VkShaderStage.VERTEX_BIT) +// it[1].loadShader("$assetPath/shaders/dynamicuniformbuffer/base.frag.spv", VkShaderStage.FRAGMENT_BIT) +// } +// +// val pipelineCreateInfo = vk.GraphicsPipelineCreateInfo(pipelineLayout, renderPass).also { +// it.vertexInputState = vertices.inputState +// it.inputAssemblyState = inputAssemblyState +// it.rasterizationState = rasterizationState +// it.colorBlendState = colorBlendState +// it.multisampleState = multisampleState +// it.viewportState = viewportState +// it.depthStencilState = depthStencilState +// it.dynamicState = dynamicState +// it.stages = shaderStages +// } +// +// pipeline = device.createGraphicsPipelines(pipelineCache, pipelineCreateInfo) +// } +// +// /** Prepare and initialize uniform buffer containing shader uniforms */ +// fun prepareUniformBuffers() { +// /* Allocate data for the dynamic uniform buffer object +// We allocate this manually as the alignment of the offset differs between GPUs */ +// +// // Calculate required alignment based on minimum device offset alignment +// val minUboAlignment = vulkanDevice.properties.limits.minUniformBufferOffsetAlignment +// dynamicAlignment = Mat4.size.L // if (minUboAlignment > 0) // dynamicAlignment = (dynamicAlignment + minUboAlignment - 1) and (minUboAlignment - 1).inv() // @@ -343,109 +343,109 @@ private class DynamicUniformBuffers : VulkanExampleBase() { // // uboDataDynamic.model = bufferBig(bufferSize).apply { uboDataDynamic.address = adr } // -//// println("minUniformBufferOffsetAlignment = $minUboAlignment") -//// println("dynamicAlignment = $dynamicAlignment") -//// -//// // Vertex shader uniform buffer block -//// -//// // Static shared uniform buffer object with projection and view matrix -//// vulkanDevice.createBuffer( -//// VkBufferUsage.UNIFORM_BUFFER_BIT.i, -//// VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, -//// uniformBuffers.view, -//// VkDeviceSize(uboVS.size.L)) -//// -//// // Uniform buffer object with per-object matrices -//// vulkanDevice.createBuffer( -//// VkBufferUsage.UNIFORM_BUFFER_BIT.i, -//// VkMemoryProperty.HOST_VISIBLE_BIT.i, -//// uniformBuffers.dynamic, -//// bufferSize) -//// -//// // Map persistent -//// uniformBuffers.view.map() -//// uniformBuffers.dynamic.map() -//// -//// // Prepare per-object matrices with offsets and random rotations -//// repeat(OBJECT_INSTANCES) { -//// rotations[it] = Vec3 { glm.gaussRand(-1f, 1f) } * 2f * glm.PIf -//// rotationSpeeds[it] = Vec3 { glm.gaussRand(-1f, 1f) } -//// } -//// -//// updateUniformBuffers() -//// updateDynamicUniformBuffer(true) - } - - fun updateUniformBuffers() { - // Fixed ubo with projection and view matrices - uboVS.projection put camera.matrices.perspective - uboVS.view put camera.matrices.view - - uboVS to uniformBuffers.view.mapped - } - - fun updateDynamicUniformBuffer(force: Boolean = false) { - // Update at max. 60 fps - animationTimer += frameTimer - if (animationTimer <= 1f / 60f && !force) return - - // Dynamic ubo with per-object model matrices indexed by offsets in the command buffer - val dim = OBJECT_INSTANCES pow (1f / 3f) - val offset = Vec3(5f) - - for (x in 0 until dim) - for (y in 0 until dim) - for (z in 0 until dim) { - val index = x * dim * dim + y * dim + z - - // Update rotations - rotations[index] plusAssign animationTimer * rotationSpeeds[index] - - // Update matrices - val pos = -((dim * offset) / 2f) + offset / 2f - pos.x += x * offset.x - pos.y += y * offset.y - pos.z += z * offset.z - val modelMat = glm.translate(Mat4(1f), pos) - .rotateAssign(rotations[index].x, 1f, 1f, 0f) - .rotateAssign(rotations[index].y, 0f, 1f, 0f) - .rotateAssign(rotations[index].z, 0f, 0f, 1f) - // Aligned offset - modelMat.to(uboDataDynamic.model, index * dynamicAlignment.i) - } - - animationTimer = 0f - - memCopy(uboDataDynamic.address, uniformBuffers.dynamic.mapped, uniformBuffers.dynamic.size) - // Flush to make changes visible to the host - val memoryRange = vk.MappedMemoryRange { - memory = uniformBuffers.dynamic.memory - size = uniformBuffers.dynamic.size - } - device flushMappedMemoryRanges memoryRange - } - - override fun prepare() { - super.prepare() - generateCube() - setupVertexDescriptions() - prepareUniformBuffers() - setupDescriptorSetLayout() - preparePipelines() - setupDescriptorPool() - setupDescriptorSet() - buildCommandBuffers() - prepared = true - window.show() - } - - override fun render() { - if (!prepared) - return - draw() - if (!paused) - updateDynamicUniformBuffer() - } - - override fun viewChanged() = updateUniformBuffers() -} \ No newline at end of file +// println("minUniformBufferOffsetAlignment = $minUboAlignment") +// println("dynamicAlignment = $dynamicAlignment") +// +// // Vertex shader uniform buffer block +// +// // Static shared uniform buffer object with projection and view matrix +// vulkanDevice.createBuffer( +// VkBufferUsage.UNIFORM_BUFFER_BIT.i, +// VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, +// uniformBuffers.view, +// VkDeviceSize(uboVS.size.L)) +// +// // Uniform buffer object with per-object matrices +// vulkanDevice.createBuffer( +// VkBufferUsage.UNIFORM_BUFFER_BIT.i, +// VkMemoryProperty.HOST_VISIBLE_BIT.i, +// uniformBuffers.dynamic, +// bufferSize) +// +// // Map persistent +// uniformBuffers.view.map() +// uniformBuffers.dynamic.map() +// +// // Prepare per-object matrices with offsets and random rotations +// repeat(OBJECT_INSTANCES) { +// rotations[it] = Vec3 { glm.gaussRand(-1f, 1f) } * 2f * glm.PIf +// rotationSpeeds[it] = Vec3 { glm.gaussRand(-1f, 1f) } +// } +// +// updateUniformBuffers() +// updateDynamicUniformBuffer(true) +// } +// +// fun updateUniformBuffers() { +// // Fixed ubo with projection and view matrices +// uboVS.projection put camera.matrices.perspective +// uboVS.view put camera.matrices.view +// +// uboVS to uniformBuffers.view.mapped +// } +// +// fun updateDynamicUniformBuffer(force: Boolean = false) { +// // Update at max. 60 fps +// animationTimer += frameTimer +// if (animationTimer <= 1f / 60f && !force) return +// +// // Dynamic ubo with per-object model matrices indexed by offsets in the command buffer +// val dim = OBJECT_INSTANCES pow (1f / 3f) +// val offset = Vec3(5f) +// +// for (x in 0 until dim) +// for (y in 0 until dim) +// for (z in 0 until dim) { +// val index = x * dim * dim + y * dim + z +// +// // Update rotations +// rotations[index] plusAssign animationTimer * rotationSpeeds[index] +// +// // Update matrices +// val pos = -((dim * offset) / 2f) + offset / 2f +// pos.x += x * offset.x +// pos.y += y * offset.y +// pos.z += z * offset.z +// val modelMat = glm.translate(Mat4(1f), pos) +// .rotateAssign(rotations[index].x, 1f, 1f, 0f) +// .rotateAssign(rotations[index].y, 0f, 1f, 0f) +// .rotateAssign(rotations[index].z, 0f, 0f, 1f) +// // Aligned offset +// modelMat.to(uboDataDynamic.model, index * dynamicAlignment.i) +// } +// +// animationTimer = 0f +// +// memCopy(uboDataDynamic.address, uniformBuffers.dynamic.mapped, uniformBuffers.dynamic.size) +// // Flush to make changes visible to the host +// val memoryRange = vk.MappedMemoryRange { +// memory = uniformBuffers.dynamic.memory +// size = uniformBuffers.dynamic.size +// } +// device flushMappedMemoryRanges memoryRange +// } +// +// override fun prepare() { +// super.prepare() +// generateCube() +// setupVertexDescriptions() +// prepareUniformBuffers() +// setupDescriptorSetLayout() +// preparePipelines() +// setupDescriptorPool() +// setupDescriptorSet() +// buildCommandBuffers() +// prepared = true +// window.show() +// } +// +// override fun render() { +// if (!prepared) +// return +// draw() +// if (!paused) +// updateDynamicUniformBuffer() +// } +// +// override fun viewChanged() = updateUniformBuffers() +//} \ No newline at end of file diff --git a/src/main/kotlin/vulkan/basics/07 Texture.kt b/src/main/kotlin/vulkan/basics/07 Texture.kt index 1e8de95..00e8cda 100644 --- a/src/main/kotlin/vulkan/basics/07 Texture.kt +++ b/src/main/kotlin/vulkan/basics/07 Texture.kt @@ -1,817 +1,817 @@ -///* -//* Vulkan Example - Texture loading (and display) example (including mip maps) -//* -//* Copyright (C) 2016-2017 by Sascha Willems - www.saschawillems.de -//* -//* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -//*/ -// -//package vulkan.basics -// -//import gli_.gli -//import glm_.L -//import glm_.f -//import glm_.func.rad -//import glm_.glm -//import glm_.mat4x4.Mat4 -//import glm_.vec2.Vec2 -//import glm_.vec2.Vec2i -//import glm_.vec3.Vec3 -//import glm_.vec4.Vec4 -//import kool.adr -//import kool.cap -//import kool.stak -//import org.lwjgl.system.MemoryUtil.NULL -//import org.lwjgl.system.MemoryUtil.memCopy -//import org.lwjgl.vulkan.* -//import vkk.* -//import vulkan.USE_STAGING -//import vulkan.VERTEX_BUFFER_BIND_ID -//import vulkan.assetPath -//import vulkan.base.Buffer -//import vulkan.base.VulkanExampleBase -//import vulkan.base.initializers -//import vulkan.base.tools -// -// -//fun main(args: Array) { -// Texture().apply { -// setupWindow() -// initVulkan() -// prepare() -// renderLoop() -// destroy() -// } -//} -// -//private class Texture : VulkanExampleBase() { -// -// /** Vertex layout for this example */ -// object Vertex { -// // float pos[3]; -//// float uv[2]; -//// float normal[3]; -// val size = Vec3.size * 2 + Vec2.size -// val posOffset = 0 -// val uvOffset = Vec3.size -// val normalOffset = Vec3.size + Vec2.size -// } -// -// /** Contains all Vulkan objects that are required to store and use a texture -// * Note that this repository contains a texture class (VulkanTexture.hpp) that encapsulates texture loading functionality -// * in a class that is used in subsequent demos */ -// object texture { -// var sampler = VkSampler(NULL) -// var image= VkImage (NULL) -// var imageLayout = VkImageLayout.UNDEFINED -// var deviceMemory = VkDeviceMemory(NULL) -// var view = VkImageView(NULL) -// val size = Vec2i() -// var mipLevels = 0 -// } -// -// object vertices { -// val inputState = cVkPipelineVertexInputStateCreateInfo { } -// lateinit var bindingDescriptions: VkVertexInputBindingDescription.Buffer -// lateinit var attributeDescriptions: VkVertexInputAttributeDescription.Buffer -// } -// -// val vertexBuffer = Buffer() -// val indexBuffer = Buffer() -// var indexCount = 0 -// -// val uniformBufferVS = Buffer() -// -// object uboVS : Bufferizable() { -// -// val projection = Mat4() -// @Order(1) -// val model = Mat4() -// val viewPos = Vec4() -// @Order(3) -// var lodBias = 0f -// } -// -// object pipelines { -// var solid = VkPipeline (NULL) -// } -// -// var pipelineLayout = VkPipelineLayout (NULL) -// var descriptorSet= VkDescriptorSet (NULL) -// var descriptorSetLayout= VkDescriptorSetLayout (NULL) -// -// init { -// zoom = -2.5f -// rotation(0f, 15f, 0f) -// title = "Texture loading" -//// settings.overlay = true -// } -// -// override fun destroy() { -// // Clean up used Vulkan resources -// // Note : Inherited destructor cleans up resources stored in base class -// -// destroyTextureImage() -// -// device.apply { -// destroyPipeline(pipelines.solid) -// -// destroyPipelineLayout(pipelineLayout) -// destroyDescriptorSetLayout(descriptorSetLayout) -// } -// -// vertexBuffer.destroy() -// indexBuffer.destroy() -// uniformBufferVS.destroy() -// -// super.destroy() -// } -// -// /** Enable physical device features required for this example */ -// override fun getEnabledFeatures() { -// // Enable anisotropic filtering if supported -// if (deviceFeatures.samplerAnisotropy) -// enabledFeatures.samplerAnisotropy = true -// } -// -// /** Create an image memory barrier used to change the layout of an image and put it into an active command buffer */ -// fun VkCommandBuffer.setImageLayout(image: VkImage, aspectMask: VkImageAspect, oldImageLayout: VkImageLayout, -// newImageLayout: VkImageLayout, subresourceRange: VkImageSubresourceRange) { -// // Create an image barrier object -// val imageMemoryBarrier = initializers.cImageMemoryBarrier().apply { -// oldLayout = oldImageLayout -// newLayout = newImageLayout -// this.image = image -// this.subresourceRange = subresourceRange -// } -// -// // Only sets masks for layouts used in this example -// // For a more complete version that can be used with other layouts see vks::tools::setImageLayout -// -// // Source layouts (old) -// imageMemoryBarrier.srcAccessMask = -// when (oldImageLayout) { -// /* Only valid as initial layout, memory contents are not preserved -// Can be accessed directly, no source dependency required */ -// VkImageLayout.UNDEFINED -> 0 -// /* Only valid as initial layout for linear images, preserves memory contents -// Make sure host writes to the image have been finished */ -// VkImageLayout.PREINITIALIZED -> VkAccess.HOST_WRITE_BIT.i -// /* Old layout is transfer destination -// Make sure any writes to the image have been finished */ -// VkImageLayout.TRANSFER_DST_OPTIMAL -> VkAccess.TRANSFER_WRITE_BIT.i -// -// else -> imageMemoryBarrier.srcAccessMask -// } -// -// // Target layouts (new) -// imageMemoryBarrier.dstAccessMask = -// when (newImageLayout) { -// /* Transfer source (copy, blit) -// Make sure any reads from the image have been finished */ -// VkImageLayout.TRANSFER_SRC_OPTIMAL -> VkAccess.TRANSFER_READ_BIT.i -// /* Transfer destination (copy, blit) -// Make sure any writes to the image have been finished */ -// VkImageLayout.TRANSFER_DST_OPTIMAL -> VkAccess.TRANSFER_WRITE_BIT.i -// // Shader read (sampler, input attachment) -// VkImageLayout.SHADER_READ_ONLY_OPTIMAL -> VkAccess.SHADER_READ_BIT.i -// -// else -> imageMemoryBarrier.dstAccessMask -// } -// -// // Put barrier on top of pipeline -// val srcStageFlags: VkPipelineStageFlags = VkPipelineStage.TOP_OF_PIPE_BIT.i -// val destStageFlags: VkPipelineStageFlags = VkPipelineStage.TOP_OF_PIPE_BIT.i -// -// // Put barrier inside setup command buffer -// pipelineBarrier(srcStageFlags, destStageFlags, tools.VK_FLAGS_NONE, imageMemoryBarrier = imageMemoryBarrier) -// } -// -// fun loadTexture() { -// // We use the Khronos texture format (https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/) -// val filename = "$assetPath/textures/metalplate01_rgba.ktx" -// // Texture data contains 4 channels (RGBA) with unnormalized 8-bit values, this is the most commonly supported format -// val format = VkFormat.R8G8B8A8_UNORM -// -// /* Set to true to use linear tiled images -// This is just for learning purposes and not suggested, as linear tiled images are pretty restricted and -// often only support a small set of features (e.g. no mips, etc.) */ -// val forceLinearTiling = false -// -// val tex2D = gli_.Texture2d(gli.load(filename)) -// -// assert(tex2D.notEmpty()) -// -// texture.size(tex2D[0].extent()) -// texture.mipLevels = tex2D.levels() -// -// // Get device properites for the requested texture format -// val formatProperties = physicalDevice getFormatProperties format -// -// /* Only use linear tiling if requested (and supported by the device) -// Support for linear tiling is mostly limited, so prefer to use optimal tiling instead -// On most implementations linear tiling will only support a very limited amount of formats and features -// (mip maps, cubemaps, arrays, etc.) */ -// USE_STAGING = true -// -// // Only use linear tiling if forced -// if (forceLinearTiling) -// // Don't use linear if format is not supported for (linear) shader sampling -// USE_STAGING = formatProperties.linearTilingFeatures hasnt VkFormatFeature.SAMPLED_IMAGE_BIT -// -// val memAllocInfo = vk.MemoryAllocateInfo { } -// val memReqs = vk.MemoryRequirements { } -// -// if (USE_STAGING) { -// // Create a host-visible staging buffer that contains the raw image data -// -// val bufferCreateInfo = vk.BufferCreateInfo { -// size = VkDeviceSize(tex2D.size.L) -// // This buffer is used as a transfer source for the buffer copy -// usage = VkBufferUsage.TRANSFER_SRC_BIT.i -// sharingMode = VkSharingMode.EXCLUSIVE -// } -// -// val stagingBuffer: VkBuffer = device createBuffer bufferCreateInfo -// -// // Get memory requirements for the staging buffer (alignment, memory type bits) -// device.getBufferMemoryRequirements(stagingBuffer, memReqs) -// +/* +* Vulkan Example - Texture loading (and display) example (including mip maps) +* +* Copyright (C) 2016-2017 by Sascha Willems - www.saschawillems.de +* +* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +*/ + +package vulkan.basics + +import gli_.gli +import glm_.L +import glm_.f +import glm_.func.rad +import glm_.glm +import glm_.mat4x4.Mat4 +import glm_.vec2.Vec2 +import glm_.vec2.Vec2i +import glm_.vec3.Vec3 +import glm_.vec4.Vec4 +import kool.adr +import kool.cap +import kool.stak +import org.lwjgl.system.MemoryUtil.NULL +import org.lwjgl.system.MemoryUtil.memCopy +import org.lwjgl.vulkan.* +import vkk.* +import vulkan.USE_STAGING +import vulkan.VERTEX_BUFFER_BIND_ID +import vulkan.assetPath +import vulkan.base.Buffer +import vulkan.base.VulkanExampleBase +import vulkan.base.initializers +import vulkan.base.tools + + +fun main(args: Array) { + Texture().apply { + setupWindow() + initVulkan() + prepare() + renderLoop() + destroy() + } +} + +private class Texture : VulkanExampleBase() { + + /** Vertex layout for this example */ + object Vertex { + // float pos[3]; +// float uv[2]; +// float normal[3]; + val size = Vec3.size * 2 + Vec2.size + val posOffset = 0 + val uvOffset = Vec3.size + val normalOffset = Vec3.size + Vec2.size + } + + /** Contains all Vulkan objects that are required to store and use a texture + * Note that this repository contains a texture class (VulkanTexture.hpp) that encapsulates texture loading functionality + * in a class that is used in subsequent demos */ + object texture { + var sampler = VkSampler(NULL) + var image= VkImage (NULL) + var imageLayout = VkImageLayout.UNDEFINED + var deviceMemory = VkDeviceMemory(NULL) + var view = VkImageView(NULL) + val size = Vec2i() + var mipLevels = 0 + } + + object vertices { + val inputState = cVkPipelineVertexInputStateCreateInfo { } + lateinit var bindingDescriptions: VkVertexInputBindingDescription.Buffer + lateinit var attributeDescriptions: VkVertexInputAttributeDescription.Buffer + } + + val vertexBuffer = Buffer() + val indexBuffer = Buffer() + var indexCount = 0 + + val uniformBufferVS = Buffer() + + object uboVS : Bufferizable() { + + val projection = Mat4() + @Order(1) + val model = Mat4() + val viewPos = Vec4() + @Order(3) + var lodBias = 0f + } + + object pipelines { + var solid = VkPipeline (NULL) + } + + var pipelineLayout = VkPipelineLayout (NULL) + var descriptorSet= VkDescriptorSet (NULL) + var descriptorSetLayout= VkDescriptorSetLayout (NULL) + + init { + zoom = -2.5f + rotation(0f, 15f, 0f) + title = "Texture loading" +// settings.overlay = true + } + + override fun destroy() { + // Clean up used Vulkan resources + // Note : Inherited destructor cleans up resources stored in base class + + destroyTextureImage() + + device.apply { + destroyPipeline(pipelines.solid) + + destroyPipelineLayout(pipelineLayout) + destroyDescriptorSetLayout(descriptorSetLayout) + } + + vertexBuffer.destroy() + indexBuffer.destroy() + uniformBufferVS.destroy() + + super.destroy() + } + + /** Enable physical device features required for this example */ + override fun getEnabledFeatures() { + // Enable anisotropic filtering if supported + if (deviceFeatures.samplerAnisotropy) + enabledFeatures.samplerAnisotropy = true + } + + /** Create an image memory barrier used to change the layout of an image and put it into an active command buffer */ + fun VkCommandBuffer.setImageLayout(image: VkImage, aspectMask: VkImageAspect, oldImageLayout: VkImageLayout, + newImageLayout: VkImageLayout, subresourceRange: VkImageSubresourceRange) { + // Create an image barrier object + val imageMemoryBarrier = initializers.cImageMemoryBarrier().apply { + oldLayout = oldImageLayout + newLayout = newImageLayout + this.image = image + this.subresourceRange = subresourceRange + } + + // Only sets masks for layouts used in this example + // For a more complete version that can be used with other layouts see vks::tools::setImageLayout + + // Source layouts (old) + imageMemoryBarrier.srcAccessMask = + when (oldImageLayout) { + /* Only valid as initial layout, memory contents are not preserved + Can be accessed directly, no source dependency required */ + VkImageLayout.UNDEFINED -> 0 + /* Only valid as initial layout for linear images, preserves memory contents + Make sure host writes to the image have been finished */ + VkImageLayout.PREINITIALIZED -> VkAccess.HOST_WRITE_BIT.i + /* Old layout is transfer destination + Make sure any writes to the image have been finished */ + VkImageLayout.TRANSFER_DST_OPTIMAL -> VkAccess.TRANSFER_WRITE_BIT.i + + else -> imageMemoryBarrier.srcAccessMask + } + + // Target layouts (new) + imageMemoryBarrier.dstAccessMask = + when (newImageLayout) { + /* Transfer source (copy, blit) + Make sure any reads from the image have been finished */ + VkImageLayout.TRANSFER_SRC_OPTIMAL -> VkAccess.TRANSFER_READ_BIT.i + /* Transfer destination (copy, blit) + Make sure any writes to the image have been finished */ + VkImageLayout.TRANSFER_DST_OPTIMAL -> VkAccess.TRANSFER_WRITE_BIT.i + // Shader read (sampler, input attachment) + VkImageLayout.SHADER_READ_ONLY_OPTIMAL -> VkAccess.SHADER_READ_BIT.i + + else -> imageMemoryBarrier.dstAccessMask + } + + // Put barrier on top of pipeline + val srcStageFlags: VkPipelineStageFlags = VkPipelineStage.TOP_OF_PIPE_BIT.i + val destStageFlags: VkPipelineStageFlags = VkPipelineStage.TOP_OF_PIPE_BIT.i + + // Put barrier inside setup command buffer + pipelineBarrier(srcStageFlags, destStageFlags, tools.VK_FLAGS_NONE, imageMemoryBarrier = imageMemoryBarrier) + } + + fun loadTexture() { + // We use the Khronos texture format (https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/) + val filename = "$assetPath/textures/metalplate01_rgba.ktx" + // Texture data contains 4 channels (RGBA) with unnormalized 8-bit values, this is the most commonly supported format + val format = VkFormat.R8G8B8A8_UNORM + + /* Set to true to use linear tiled images + This is just for learning purposes and not suggested, as linear tiled images are pretty restricted and + often only support a small set of features (e.g. no mips, etc.) */ + val forceLinearTiling = false + + val tex2D = gli_.Texture2d(gli.load(filename)) + + assert(tex2D.notEmpty()) + + texture.size(tex2D[0].extent()) + texture.mipLevels = tex2D.levels() + + // Get device properites for the requested texture format + val formatProperties = physicalDevice getFormatProperties format + + /* Only use linear tiling if requested (and supported by the device) + Support for linear tiling is mostly limited, so prefer to use optimal tiling instead + On most implementations linear tiling will only support a very limited amount of formats and features + (mip maps, cubemaps, arrays, etc.) */ + USE_STAGING = true + + // Only use linear tiling if forced + if (forceLinearTiling) + // Don't use linear if format is not supported for (linear) shader sampling + USE_STAGING = formatProperties.linearTilingFeatures hasnt VkFormatFeature.SAMPLED_IMAGE_BIT + + val memAllocInfo = vk.MemoryAllocateInfo { } + val memReqs = vk.MemoryRequirements { } + + if (USE_STAGING) { + // Create a host-visible staging buffer that contains the raw image data + + val bufferCreateInfo = vk.BufferCreateInfo { + size = VkDeviceSize(tex2D.size.L) + // This buffer is used as a transfer source for the buffer copy + usage = VkBufferUsage.TRANSFER_SRC_BIT.i + sharingMode = VkSharingMode.EXCLUSIVE + } + + val stagingBuffer: VkBuffer = device createBuffer bufferCreateInfo + + // Get memory requirements for the staging buffer (alignment, memory type bits) + device.getBufferMemoryRequirements(stagingBuffer, memReqs) + + memAllocInfo.allocationSize = memReqs.size + // Get memory type index for a host visible buffer + memAllocInfo.memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT) + + val stagingMemory: VkDeviceMemory = device allocateMemory memAllocInfo + device.bindBufferMemory(stagingBuffer, stagingMemory) + + // Copy texture data into staging buffer + val data = device.mapMemory(stagingMemory, VkDeviceSize(0), memReqs.size) + memCopy(tex2D.data().adr, data, tex2D.size.L) + device unmapMemory stagingMemory + + // Setup buffer copy regions for each mip level + val bufferCopyRegions = VkBufferImageCopy.calloc(texture.mipLevels) + var offset = VkDeviceSize(0) + + bufferCopyRegions.forEachIndexed { i, it -> + it.imageSubresource.apply { + aspectMask = VkImageAspect.COLOR_BIT.i + mipLevel = i + baseArrayLayer = 0 + layerCount = 1 + } + val (w, h) = tex2D[i].extent() + it.imageExtent.width = w // TODO BUG + it.imageExtent.height = h + it.imageExtent.depth = 1 + it.bufferOffset = offset + + offset += tex2D[i].size + } + + // Create optimal tiled target image + val imageCreateInfo = vk.ImageCreateInfo { + imageType = VkImageType.`2D` + this.format = format + mipLevels = texture.mipLevels + arrayLayers = 1 + samples = VkSampleCount.`1_BIT` + tiling = VkImageTiling.OPTIMAL + sharingMode = VkSharingMode.EXCLUSIVE + // Set initial layout of the image to undefined + initialLayout = VkImageLayout.UNDEFINED + extent.set(texture.size.x, texture.size.y, 1) + usage = VkImageUsage.TRANSFER_DST_BIT or VkImageUsage.SAMPLED_BIT + } + + texture.image = device createImage imageCreateInfo + device.getImageMemoryRequirements(texture.image, memReqs) + + memAllocInfo.allocationSize = memReqs.size + memAllocInfo.memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryProperty.DEVICE_LOCAL_BIT) + + texture.deviceMemory = device allocateMemory memAllocInfo + device.bindImageMemory(texture.image, texture.deviceMemory) + + val copyCmd = super.createCommandBuffer(VkCommandBufferLevel.PRIMARY, true) + + // Image barrier for optimal image + + // The sub resource range describes the regions of the image we will be transition + val subresourceRange = VkImageSubresourceRange.calloc().apply { + // Image only contains color data + aspectMask = VkImageAspect.COLOR_BIT.i + // Start at first mip level + baseMipLevel = 0 + // We will transition on all mip levels + levelCount = texture.mipLevels + // The 2D texture only has one layer + layerCount = 1 + } + + // Optimal image will be used as destination for the copy, so we must transfer from our + // initial undefined image layout to the transfer destination layout + copyCmd.setImageLayout( + texture.image, + VkImageAspect.COLOR_BIT, + VkImageLayout.UNDEFINED, + VkImageLayout.TRANSFER_DST_OPTIMAL, + subresourceRange) + + // Copy mip levels from staging buffer + copyCmd.copyBufferToImage( + stagingBuffer, + texture.image, + VkImageLayout.TRANSFER_DST_OPTIMAL, + bufferCopyRegions) + + // Change texture image layout to shader read after all mip levels have been copied + texture.imageLayout = VkImageLayout.SHADER_READ_ONLY_OPTIMAL + copyCmd.setImageLayout( + texture.image, + VkImageAspect.COLOR_BIT, + VkImageLayout.TRANSFER_DST_OPTIMAL, + texture.imageLayout, + subresourceRange) + + super.flushCommandBuffer(copyCmd, queue, true) + + // Clean up staging resources + device freeMemory stagingMemory + device destroyBuffer stagingBuffer + } else { + TODO() +// VkImage mappableImage +// VkDeviceMemory mappableMemory +// +// // Load mip map level 0 to linear tiling image +// VkImageCreateInfo imageCreateInfo = vks ::initializers::imageCreateInfo() +// imageCreateInfo.imageType = VK_IMAGE_TYPE_2D +// imageCreateInfo.format = format +// imageCreateInfo.mipLevels = 1 +// imageCreateInfo.arrayLayers = 1 +// imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT +// imageCreateInfo.tiling = VK_IMAGE_TILING_LINEAR +// imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT +// imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE +// imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED +// imageCreateInfo.extent = { texture.width, texture.height, 1 } +// VK_CHECK_RESULT(vkCreateImage(device, & imageCreateInfo, nullptr, & mappableImage)) +// +// // Get memory requirements for this image +// // like size and alignment +// vkGetImageMemoryRequirements(device, mappableImage, & memReqs) +// // Set memory allocation size to required memory size // memAllocInfo.allocationSize = memReqs.size -// // Get memory type index for a host visible buffer -// memAllocInfo.memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT) // -// val stagingMemory: VkDeviceMemory = device allocateMemory memAllocInfo -// device.bindBufferMemory(stagingBuffer, stagingMemory) +// // Get memory type that can be mapped to host memory +// memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) // -// // Copy texture data into staging buffer -// val data = device.mapMemory(stagingMemory, VkDeviceSize(0), memReqs.size) -// memCopy(tex2D.data().adr, data, tex2D.size.L) -// device unmapMemory stagingMemory +// // Allocate host memory +// VK_CHECK_RESULT(vkAllocateMemory(device, & memAllocInfo, nullptr, & mappableMemory)) // -// // Setup buffer copy regions for each mip level -// val bufferCopyRegions = VkBufferImageCopy.calloc(texture.mipLevels) -// var offset = VkDeviceSize(0) +// // Bind allocated image for use +// VK_CHECK_RESULT(vkBindImageMemory(device, mappableImage, mappableMemory, 0)) // -// bufferCopyRegions.forEachIndexed { i, it -> -// it.imageSubresource.apply { -// aspectMask = VkImageAspect.COLOR_BIT.i -// mipLevel = i -// baseArrayLayer = 0 -// layerCount = 1 -// } -// val (w, h) = tex2D[i].extent() -// it.imageExtent.width = w // TODO BUG -// it.imageExtent.height = h -// it.imageExtent.depth = 1 -// it.bufferOffset = offset +// // Get sub resource layout +// // Mip map count, array layer, etc. +// VkImageSubresource subRes = {} +// subRes.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT // -// offset += tex2D[i].size -// } +// VkSubresourceLayout subResLayout +// void * data // -// // Create optimal tiled target image -// val imageCreateInfo = vk.ImageCreateInfo { -// imageType = VkImageType.`2D` -// this.format = format -// mipLevels = texture.mipLevels -// arrayLayers = 1 -// samples = VkSampleCount.`1_BIT` -// tiling = VkImageTiling.OPTIMAL -// sharingMode = VkSharingMode.EXCLUSIVE -// // Set initial layout of the image to undefined -// initialLayout = VkImageLayout.UNDEFINED -// extent.set(texture.size.x, texture.size.y, 1) -// usage = VkImageUsage.TRANSFER_DST_BIT or VkImageUsage.SAMPLED_BIT -// } +// // Get sub resources layout +// // Includes row pitch, size offsets, etc. +// vkGetImageSubresourceLayout(device, mappableImage, & subRes, &subResLayout) // -// texture.image = device createImage imageCreateInfo -// device.getImageMemoryRequirements(texture.image, memReqs) +// // Map image memory +// VK_CHECK_RESULT(vkMapMemory(device, mappableMemory, 0, memReqs.size, 0, & data)) // -// memAllocInfo.allocationSize = memReqs.size -// memAllocInfo.memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryProperty.DEVICE_LOCAL_BIT) +// // Copy image data into memory +// memcpy(data, tex2D[subRes.mipLevel].data(), tex2D[subRes.mipLevel].size()) // -// texture.deviceMemory = device allocateMemory memAllocInfo -// device.bindImageMemory(texture.image, texture.deviceMemory) +// vkUnmapMemory(device, mappableMemory) // -// val copyCmd = super.createCommandBuffer(VkCommandBufferLevel.PRIMARY, true) +// // Linear tiled images don't need to be staged +// // and can be directly used as textures +// texture.image = mappableImage +// texture.deviceMemory = mappableMemory +// texture.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL // -// // Image barrier for optimal image +// VkCommandBuffer copyCmd = VulkanExampleBase ::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true) // -// // The sub resource range describes the regions of the image we will be transition -// val subresourceRange = VkImageSubresourceRange.calloc().apply { -// // Image only contains color data -// aspectMask = VkImageAspect.COLOR_BIT.i -// // Start at first mip level -// baseMipLevel = 0 -// // We will transition on all mip levels -// levelCount = texture.mipLevels -// // The 2D texture only has one layer -// layerCount = 1 -// } +// // Setup image memory barrier transfer image to shader read layout // -// // Optimal image will be used as destination for the copy, so we must transfer from our -// // initial undefined image layout to the transfer destination layout -// copyCmd.setImageLayout( -// texture.image, -// VkImageAspect.COLOR_BIT, -// VkImageLayout.UNDEFINED, -// VkImageLayout.TRANSFER_DST_OPTIMAL, -// subresourceRange) -// -// // Copy mip levels from staging buffer -// copyCmd.copyBufferToImage( -// stagingBuffer, -// texture.image, -// VkImageLayout.TRANSFER_DST_OPTIMAL, -// bufferCopyRegions) -// -// // Change texture image layout to shader read after all mip levels have been copied -// texture.imageLayout = VkImageLayout.SHADER_READ_ONLY_OPTIMAL -// copyCmd.setImageLayout( +// // The sub resource range describes the regions of the image we will be transition +// VkImageSubresourceRange subresourceRange = {} +// // Image only contains color data +// subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT +// // Start at first mip level +// subresourceRange.baseMipLevel = 0 +// // Only one mip level, most implementations won't support more for linear tiled images +// subresourceRange.levelCount = 1 +// // The 2D texture only has one layer +// subresourceRange.layerCount = 1 +// +// setImageLayout( +// copyCmd, // texture.image, -// VkImageAspect.COLOR_BIT, -// VkImageLayout.TRANSFER_DST_OPTIMAL, +// VK_IMAGE_ASPECT_COLOR_BIT, +// VK_IMAGE_LAYOUT_PREINITIALIZED, // texture.imageLayout, // subresourceRange) // -// super.flushCommandBuffer(copyCmd, queue, true) -// -// // Clean up staging resources -// device freeMemory stagingMemory -// device destroyBuffer stagingBuffer -// } else { -// TODO() -//// VkImage mappableImage -//// VkDeviceMemory mappableMemory -//// -//// // Load mip map level 0 to linear tiling image -//// VkImageCreateInfo imageCreateInfo = vks ::initializers::imageCreateInfo() -//// imageCreateInfo.imageType = VK_IMAGE_TYPE_2D -//// imageCreateInfo.format = format -//// imageCreateInfo.mipLevels = 1 -//// imageCreateInfo.arrayLayers = 1 -//// imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT -//// imageCreateInfo.tiling = VK_IMAGE_TILING_LINEAR -//// imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT -//// imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE -//// imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED -//// imageCreateInfo.extent = { texture.width, texture.height, 1 } -//// VK_CHECK_RESULT(vkCreateImage(device, & imageCreateInfo, nullptr, & mappableImage)) -//// -//// // Get memory requirements for this image -//// // like size and alignment -//// vkGetImageMemoryRequirements(device, mappableImage, & memReqs) -//// // Set memory allocation size to required memory size -//// memAllocInfo.allocationSize = memReqs.size -//// -//// // Get memory type that can be mapped to host memory -//// memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) -//// -//// // Allocate host memory -//// VK_CHECK_RESULT(vkAllocateMemory(device, & memAllocInfo, nullptr, & mappableMemory)) -//// -//// // Bind allocated image for use -//// VK_CHECK_RESULT(vkBindImageMemory(device, mappableImage, mappableMemory, 0)) -//// -//// // Get sub resource layout -//// // Mip map count, array layer, etc. -//// VkImageSubresource subRes = {} -//// subRes.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT -//// -//// VkSubresourceLayout subResLayout -//// void * data -//// -//// // Get sub resources layout -//// // Includes row pitch, size offsets, etc. -//// vkGetImageSubresourceLayout(device, mappableImage, & subRes, &subResLayout) -//// -//// // Map image memory -//// VK_CHECK_RESULT(vkMapMemory(device, mappableMemory, 0, memReqs.size, 0, & data)) -//// -//// // Copy image data into memory -//// memcpy(data, tex2D[subRes.mipLevel].data(), tex2D[subRes.mipLevel].size()) -//// -//// vkUnmapMemory(device, mappableMemory) -//// -//// // Linear tiled images don't need to be staged -//// // and can be directly used as textures -//// texture.image = mappableImage -//// texture.deviceMemory = mappableMemory -//// texture.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL -//// -//// VkCommandBuffer copyCmd = VulkanExampleBase ::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true) -//// -//// // Setup image memory barrier transfer image to shader read layout -//// -//// // The sub resource range describes the regions of the image we will be transition -//// VkImageSubresourceRange subresourceRange = {} -//// // Image only contains color data -//// subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT -//// // Start at first mip level -//// subresourceRange.baseMipLevel = 0 -//// // Only one mip level, most implementations won't support more for linear tiled images -//// subresourceRange.levelCount = 1 -//// // The 2D texture only has one layer -//// subresourceRange.layerCount = 1 -//// -//// setImageLayout( -//// copyCmd, -//// texture.image, -//// VK_IMAGE_ASPECT_COLOR_BIT, -//// VK_IMAGE_LAYOUT_PREINITIALIZED, -//// texture.imageLayout, -//// subresourceRange) -//// -//// VulkanExampleBase::flushCommandBuffer(copyCmd, queue, true) -// } -// -// // Create a texture sampler -// // In Vulkan textures are accessed by samplers -// // This separates all the sampling information from the texture data. This means you could have multiple sampler objects for the same texture with different settings -// // Note: Similar to the samplers available with OpenGL 3.3 -// val sampler = vk.SamplerCreateInfo { -// magFilter = VkFilter.LINEAR -// minFilter = VkFilter.LINEAR -// mipmapMode = VkSamplerMipmapMode.LINEAR -// addressModeU = VkSamplerAddressMode.REPEAT -// addressModeV = VkSamplerAddressMode.REPEAT -// addressModeW = VkSamplerAddressMode.REPEAT -// mipLodBias = 0f -// compareOp = VkCompareOp.NEVER -// minLod = 0f -// // Set max level-of-detail to mip level count of the texture -// maxLod = if (USE_STAGING) texture.mipLevels.f else 0f -// // Enable anisotropic filtering -// // This feature is optional, so we must check if it's supported on the device -// if (vulkanDevice.features.samplerAnisotropy) { -// // Use max. level of anisotropy for this example -// maxAnisotropy = vulkanDevice.properties.limits.maxSamplerAnisotropy -// anisotropyEnable = true -// } else { -// // The device does not support anisotropic filtering -// maxAnisotropy = 1f -// anisotropyEnable = false -// } -// borderColor = VkBorderColor.FLOAT_OPAQUE_WHITE -// } -// texture.sampler = device createSampler sampler -// -// /* Create image view -// Textures are not directly accessed by the shaders and are abstracted by image views containing additional -// information and sub resource ranges */ -// val view = vk.ImageViewCreateInfo { -// viewType = VkImageViewType.`2D` -// this.format = format -// components(VkComponentSwizzle.R, VkComponentSwizzle.G, VkComponentSwizzle.B, VkComponentSwizzle.A) -// // The subresource range describes the set of mip levels (and array layers) that can be accessed through this image view -// // It's possible to create multiple image views for a single image referring to different (and/or overlapping) ranges of the image -// subresourceRange.apply { -// aspectMask = VkImageAspect.COLOR_BIT.i -// baseMipLevel = 0 -// baseArrayLayer = 0 -// layerCount = 1 -// // Linear tiling usually won't support mip maps -// // Only set mip map count if optimal tiling is used -// levelCount = if (USE_STAGING) texture.mipLevels else 1 -// } -// // The view will be based on the texture's image -// image = texture.image -// } -// texture.view = device createImageView view -// } -// -// /** Free all Vulkan resources used by a texture object */ -// fun destroyTextureImage() { -// device destroyImageView texture.view -// device destroyImage texture.image -// device destroySampler texture.sampler -// device freeMemory texture.deviceMemory -// } -// -// override fun buildCommandBuffers() { -// -// val cmdBufInfo = vk.CommandBufferBeginInfo { } -// -// val clearValues = vk.ClearValue(2).also { -// it[0].color(defaultClearColor) -// it[1].depthStencil(1f, 0) -// } -// val (w, h) = size -// -// val renderPassBeginInfo = vk.RenderPassBeginInfo { -// renderPass = this@Texture.renderPass -// renderArea.apply { -// offset.set(0, 0) -// extent.set(w, h) // TODO BUG -// } -// this.clearValues = clearValues -// } -// -// drawCmdBuffers.forEachIndexed { i, it -> -// // Set target frame buffer -// renderPassBeginInfo.framebuffer(frameBuffers[i].L) // TODO BUG -// -// it begin cmdBufInfo -// -// it.beginRenderPass(renderPassBeginInfo, VkSubpassContents.INLINE) -// -// it setViewport initializers.viewport(size, 0f, 1f) -// -// it setScissor vk.Rect2D(size) -// -// it.bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, pipelineLayout, descriptorSet) -// it.bindPipeline(VkPipelineBindPoint.GRAPHICS, pipelines.solid) -// -// it.bindVertexBuffers(VERTEX_BUFFER_BIND_ID, vertexBuffer.buffer) -// it.bindIndexBuffer(indexBuffer.buffer, VkDeviceSize(0), VkIndexType.UINT32) -// -// it.drawIndexed(indexCount, 1, 0, 0, 0) -// -// it.drawUI() -// -// it.endRenderPass() -// -// it.end() -// } -// } -// -// fun draw() { -// -// super.prepareFrame() -// -// // Command buffer to be sumitted to the queue -// submitInfo.commandBuffer = drawCmdBuffers[currentBuffer] -// -// // Submit to queue -// queue submit submitInfo -// -// super.submitFrame() -// } -// -// fun generateQuad() = stak { -// // Setup vertices for a single uv-mapped quad made from two triangles -// val vertices = it.floats( -// +1f, +1f, 0f, 1f, 1f, 0f, 0f, 1f, -// -1f, +1f, 0f, 0f, 1f, 0f, 0f, 1f, -// -1f, -1f, 0f, 0f, 0f, 0f, 0f, 1f, -// +1f, -1f, 0f, 1f, 0f, 0f, 0f, 1f) -// -// // Setup indices -// val indices = it.ints(0, 1, 2, 2, 3, 0) -// indexCount = indices.cap -// -// // Create buffers -// // For the sake of simplicity we won't stage the vertex data to the gpu memory -// // Vertex buffer -// vulkanDevice.createBuffer( -// VkBufferUsage.VERTEX_BUFFER_BIT.i, -// VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, -// vertexBuffer, -// vertices) -// // Index buffer -// vulkanDevice.createBuffer( -// VkBufferUsage.INDEX_BUFFER_BIT.i, -// VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, -// indexBuffer, -// indices) -// } -// -// fun setupVertexDescriptions() { -// // Binding description -// vertices.bindingDescriptions = VkVertexInputBindingDescription.calloc(1) -// vertices.bindingDescriptions[0].apply { -// binding = VERTEX_BUFFER_BIND_ID -// stride = Vertex.size -// inputRate = VkVertexInputRate.VERTEX -// } -// -// // Attribute descriptions -// // Describes memory layout and shader positions -// vertices.attributeDescriptions = VkVertexInputAttributeDescription.calloc(3) -// // Location 0 : Position -// vertices.attributeDescriptions[0].apply { -// binding = VERTEX_BUFFER_BIND_ID -// location = 0 -// format = VkFormat.R32G32B32_SFLOAT -// offset = Vertex.posOffset -// } -// // Location 1 : Texture coordinates -// vertices.attributeDescriptions[1].apply { -// binding = VERTEX_BUFFER_BIND_ID -// location = 1 -// format = VkFormat.R32G32_SFLOAT -// offset = Vertex.uvOffset -// } -// // Location 1 : Vertex normal -// vertices.attributeDescriptions[2].apply { -// binding = VERTEX_BUFFER_BIND_ID -// location = 2 -// format = VkFormat.R32G32B32_SFLOAT -// offset = Vertex.normalOffset -// } -// -// vertices.inputState.apply { -// vertexBindingDescriptions = vertices.bindingDescriptions -// vertexAttributeDescriptions = vertices.attributeDescriptions -// } -// } -// -// fun setupDescriptorPool() { -// // Example uses one ubo and one image sampler -// val poolSizes = VkDescriptorPoolSize.calloc(2).also { -// it[0](VkDescriptorType.UNIFORM_BUFFER, 1) -// it[1](VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1) -// } -// -// val descriptorPoolInfo = vk.DescriptorPoolCreateInfo { -// this.poolSizes = poolSizes -// maxSets = 2 -// } -// -// descriptorPool = device createDescriptorPool descriptorPoolInfo -// } -// -// fun setupDescriptorSetLayout() { -// -// val setLayoutBindings = VkDescriptorSetLayoutBinding.calloc(2).also { -// // Binding 0 : Vertex shader uniform buffer -// it[0](0, VkDescriptorType.UNIFORM_BUFFER, 1, VkShaderStage.VERTEX_BIT.i) -// // Binding 1 : Fragment shader image sampler -// it[1](1, VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1, VkShaderStage.FRAGMENT_BIT.i) -// } -// -// val descriptorLayout = vk.DescriptorSetLayoutCreateInfo { -// bindings = setLayoutBindings -// } -// -// descriptorSetLayout = device createDescriptorSetLayout descriptorLayout -// -// val pipelineLayoutCreateInfo = vk.PipelineLayoutCreateInfo { -// setLayout = descriptorSetLayout -// } -// -// pipelineLayout = device createPipelineLayout pipelineLayoutCreateInfo -// } -// -// fun setupDescriptorSet() { -// -// val allocInfo = vk.DescriptorSetAllocateInfo { -// descriptorPool = this@Texture.descriptorPool -// setLayout = descriptorSetLayout -// descriptorSetCount = 1 -// } -// -// descriptorSet = device allocateDescriptorSets allocInfo -// -// // Setup a descriptor image info for the current texture to be used as a combined image sampler -// val textureDescriptor = vk.DescriptorImageInfo(1) { -// // The image's view (images are never directly accessed by the shader, but rather through views defining subresources) -// imageView = texture.view -// // The sampler (Telling the pipeline how to sample the texture, including repeat, border, etc.) -// sampler = texture.sampler -// // The current layout of the image (Note: Should always fit the actual use, e.g. shader read) -// imageLayout = texture.imageLayout -// } -// -// val writeDescriptorSets = VkWriteDescriptorSet.calloc(2) -// // Binding 0 : Vertex shader uniform buffer -// writeDescriptorSets[0].apply { -// type = VkStructureType.WRITE_DESCRIPTOR_SET -// dstSet = descriptorSet -// descriptorType = VkDescriptorType.UNIFORM_BUFFER -// dstBinding = 0 -// bufferInfo_ = uniformBufferVS.descriptor -// } -// // Binding 1 : Fragment shader texture sampler -// // Fragment shader: layout (binding = 1) uniform sampler2D samplerColor; -// writeDescriptorSets[1].apply { -// type = VkStructureType.WRITE_DESCRIPTOR_SET -// dstSet = descriptorSet -// // The descriptor set will use a combined image sampler (sampler and image could be split) -// descriptorType = VkDescriptorType.COMBINED_IMAGE_SAMPLER -// dstBinding = 1 // Shader binding point 1 -// imageInfo = textureDescriptor // Pointer to the descriptor image for our texture -// } -// -// device updateDescriptorSets writeDescriptorSets -// } -// -// fun preparePipelines() = stak { -// -// val inputAssemblyState = vk.PipelineInputAssemblyStateCreateInfo { -// topology = VkPrimitiveTopology.TRIANGLE_LIST -// } -// -// val rasterizationState = initializers.pipelineRasterizationStateCreateInfo( -// VkPolygonMode.FILL, -// VkCullMode.NONE.i, -// VkFrontFace.COUNTER_CLOCKWISE, -// 0) -// -// val blendAttachmentState = vk.PipelineColorBlendAttachmentState { colorWriteMask = 0xf } -// -// val colorBlendState = vk.PipelineColorBlendStateCreateInfo { attachment = blendAttachmentState } -// -// val depthStencilState = initializers.pipelineDepthStencilStateCreateInfo( -// true, -// true, -// VkCompareOp.LESS_OR_EQUAL) -// -// val viewportState = vk.PipelineViewportStateCreateInfo { -// viewportCount = 1 -// scissorCount = 1 -// } -// -// val multisampleState = vk.PipelineMultisampleStateCreateInfo { -// rasterizationSamples = VkSampleCount.`1_BIT` -// } -// -// val dynamicStateEnables = it.vkDynamicStateBufferOf(VkDynamicState.VIEWPORT, VkDynamicState.SCISSOR) -// val dynamicState = vk.PipelineDynamicStateCreateInfo { -// dynamicStates = dynamicStateEnables -// } -// -// // Load shaders -// val shaderStages = vk.PipelineShaderStageCreateInfo(2).also { -// it[0].loadShader("$assetPath/shaders/texture/texture.vert", VkShaderStage.VERTEX_BIT) -// it[1].loadShader("$assetPath/shaders/texture/texture.frag", VkShaderStage.FRAGMENT_BIT) -// } -// -// val pipelineCreateInfo = initializers.pipelineCreateInfo( -// pipelineLayout, -// renderPass).apply { -// vertexInputState = vertices.inputState -// this.inputAssemblyState = inputAssemblyState -// this.rasterizationState = rasterizationState -// this.colorBlendState = colorBlendState -// this.multisampleState = multisampleState -// this.viewportState = viewportState -// this.depthStencilState = depthStencilState -// this.dynamicState = dynamicState -// stages = shaderStages -// } -// -// pipelines.solid = device.createGraphicsPipelines(pipelineCache, pipelineCreateInfo) -// } -// -// // Prepare and initialize uniform buffer containing shader uniforms -// fun prepareUniformBuffers() { -// // Vertex shader uniform buffer block -// vulkanDevice.createBuffer( -// VkBufferUsage.UNIFORM_BUFFER_BIT.i, -// VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, -// uniformBufferVS, -// VkDeviceSize(uboVS.size.L)) -// +// VulkanExampleBase::flushCommandBuffer(copyCmd, queue, true) + } + + // Create a texture sampler + // In Vulkan textures are accessed by samplers + // This separates all the sampling information from the texture data. This means you could have multiple sampler objects for the same texture with different settings + // Note: Similar to the samplers available with OpenGL 3.3 + val sampler = vk.SamplerCreateInfo { + magFilter = VkFilter.LINEAR + minFilter = VkFilter.LINEAR + mipmapMode = VkSamplerMipmapMode.LINEAR + addressModeU = VkSamplerAddressMode.REPEAT + addressModeV = VkSamplerAddressMode.REPEAT + addressModeW = VkSamplerAddressMode.REPEAT + mipLodBias = 0f + compareOp = VkCompareOp.NEVER + minLod = 0f + // Set max level-of-detail to mip level count of the texture + maxLod = if (USE_STAGING) texture.mipLevels.f else 0f + // Enable anisotropic filtering + // This feature is optional, so we must check if it's supported on the device + if (vulkanDevice.features.samplerAnisotropy) { + // Use max. level of anisotropy for this example + maxAnisotropy = vulkanDevice.properties.limits.maxSamplerAnisotropy + anisotropyEnable = true + } else { + // The device does not support anisotropic filtering + maxAnisotropy = 1f + anisotropyEnable = false + } + borderColor = VkBorderColor.FLOAT_OPAQUE_WHITE + } + texture.sampler = device createSampler sampler + + /* Create image view + Textures are not directly accessed by the shaders and are abstracted by image views containing additional + information and sub resource ranges */ + val view = vk.ImageViewCreateInfo { + viewType = VkImageViewType.`2D` + this.format = format + components(VkComponentSwizzle.R, VkComponentSwizzle.G, VkComponentSwizzle.B, VkComponentSwizzle.A) + // The subresource range describes the set of mip levels (and array layers) that can be accessed through this image view + // It's possible to create multiple image views for a single image referring to different (and/or overlapping) ranges of the image + subresourceRange.apply { + aspectMask = VkImageAspect.COLOR_BIT.i + baseMipLevel = 0 + baseArrayLayer = 0 + layerCount = 1 + // Linear tiling usually won't support mip maps + // Only set mip map count if optimal tiling is used + levelCount = if (USE_STAGING) texture.mipLevels else 1 + } + // The view will be based on the texture's image + image = texture.image + } + texture.view = device createImageView view + } + + /** Free all Vulkan resources used by a texture object */ + fun destroyTextureImage() { + device destroyImageView texture.view + device destroyImage texture.image + device destroySampler texture.sampler + device freeMemory texture.deviceMemory + } + + override fun buildCommandBuffers() { + + val cmdBufInfo = vk.CommandBufferBeginInfo { } + + val clearValues = vk.ClearValue(2).also { + it[0].color(defaultClearColor) + it[1].depthStencil(1f, 0) + } + val (w, h) = size + + val renderPassBeginInfo = vk.RenderPassBeginInfo { + renderPass = this@Texture.renderPass + renderArea.apply { + offset.set(0, 0) + extent.set(w, h) // TODO BUG + } + this.clearValues = clearValues + } + + drawCmdBuffers.forEachIndexed { i, it -> + // Set target frame buffer + renderPassBeginInfo.framebuffer(frameBuffers[i].L) // TODO BUG + + it begin cmdBufInfo + + it.beginRenderPass(renderPassBeginInfo, VkSubpassContents.INLINE) + + it setViewport initializers.viewport(size, 0f, 1f) + + it setScissor vk.Rect2D(size) + + it.bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, pipelineLayout, descriptorSet) + it.bindPipeline(VkPipelineBindPoint.GRAPHICS, pipelines.solid) + + it.bindVertexBuffers(VERTEX_BUFFER_BIND_ID, vertexBuffer.buffer) + it.bindIndexBuffer(indexBuffer.buffer, VkDeviceSize(0), VkIndexType.UINT32) + + it.drawIndexed(indexCount, 1, 0, 0, 0) + + it.drawUI() + + it.endRenderPass() + + it.end() + } + } + + fun draw() { + + super.prepareFrame() + + // Command buffer to be sumitted to the queue + submitInfo.commandBuffer = drawCmdBuffers[currentBuffer] + + // Submit to queue + queue submit submitInfo + + super.submitFrame() + } + + fun generateQuad() = stak { + // Setup vertices for a single uv-mapped quad made from two triangles + val vertices = it.floats( + +1f, +1f, 0f, 1f, 1f, 0f, 0f, 1f, + -1f, +1f, 0f, 0f, 1f, 0f, 0f, 1f, + -1f, -1f, 0f, 0f, 0f, 0f, 0f, 1f, + +1f, -1f, 0f, 1f, 0f, 0f, 0f, 1f) + + // Setup indices + val indices = it.ints(0, 1, 2, 2, 3, 0) + indexCount = indices.cap + + // Create buffers + // For the sake of simplicity we won't stage the vertex data to the gpu memory + // Vertex buffer + vulkanDevice.createBuffer( + VkBufferUsage.VERTEX_BUFFER_BIT.i, + VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, + vertexBuffer, + vertices) + // Index buffer + vulkanDevice.createBuffer( + VkBufferUsage.INDEX_BUFFER_BIT.i, + VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, + indexBuffer, + indices) + } + + fun setupVertexDescriptions() { + // Binding description + vertices.bindingDescriptions = VkVertexInputBindingDescription.calloc(1) + vertices.bindingDescriptions[0].apply { + binding = VERTEX_BUFFER_BIND_ID + stride = Vertex.size + inputRate = VkVertexInputRate.VERTEX + } + + // Attribute descriptions + // Describes memory layout and shader positions + vertices.attributeDescriptions = VkVertexInputAttributeDescription.calloc(3) + // Location 0 : Position + vertices.attributeDescriptions[0].apply { + binding = VERTEX_BUFFER_BIND_ID + location = 0 + format = VkFormat.R32G32B32_SFLOAT + offset = Vertex.posOffset + } + // Location 1 : Texture coordinates + vertices.attributeDescriptions[1].apply { + binding = VERTEX_BUFFER_BIND_ID + location = 1 + format = VkFormat.R32G32_SFLOAT + offset = Vertex.uvOffset + } + // Location 1 : Vertex normal + vertices.attributeDescriptions[2].apply { + binding = VERTEX_BUFFER_BIND_ID + location = 2 + format = VkFormat.R32G32B32_SFLOAT + offset = Vertex.normalOffset + } + + vertices.inputState.apply { + vertexBindingDescriptions = vertices.bindingDescriptions + vertexAttributeDescriptions = vertices.attributeDescriptions + } + } + + fun setupDescriptorPool() { + // Example uses one ubo and one image sampler + val poolSizes = VkDescriptorPoolSize.calloc(2).also { + it[0](VkDescriptorType.UNIFORM_BUFFER, 1) + it[1](VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1) + } + + val descriptorPoolInfo = vk.DescriptorPoolCreateInfo { + this.poolSizes = poolSizes + maxSets = 2 + } + + descriptorPool = device createDescriptorPool descriptorPoolInfo + } + + fun setupDescriptorSetLayout() { + + val setLayoutBindings = VkDescriptorSetLayoutBinding.calloc(2).also { + // Binding 0 : Vertex shader uniform buffer + it[0](0, VkDescriptorType.UNIFORM_BUFFER, 1, VkShaderStage.VERTEX_BIT.i) + // Binding 1 : Fragment shader image sampler + it[1](1, VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1, VkShaderStage.FRAGMENT_BIT.i) + } + + val descriptorLayout = vk.DescriptorSetLayoutCreateInfo { + bindings = setLayoutBindings + } + + descriptorSetLayout = device createDescriptorSetLayout descriptorLayout + + val pipelineLayoutCreateInfo = vk.PipelineLayoutCreateInfo { + setLayout = descriptorSetLayout + } + + pipelineLayout = device createPipelineLayout pipelineLayoutCreateInfo + } + + fun setupDescriptorSet() { + + val allocInfo = vk.DescriptorSetAllocateInfo { + descriptorPool = this@Texture.descriptorPool + setLayout = descriptorSetLayout + descriptorSetCount = 1 + } + + descriptorSet = device allocateDescriptorSets allocInfo + + // Setup a descriptor image info for the current texture to be used as a combined image sampler + val textureDescriptor = vk.DescriptorImageInfo(1) { + // The image's view (images are never directly accessed by the shader, but rather through views defining subresources) + imageView = texture.view + // The sampler (Telling the pipeline how to sample the texture, including repeat, border, etc.) + sampler = texture.sampler + // The current layout of the image (Note: Should always fit the actual use, e.g. shader read) + imageLayout = texture.imageLayout + } + + val writeDescriptorSets = VkWriteDescriptorSet.calloc(2) + // Binding 0 : Vertex shader uniform buffer + writeDescriptorSets[0].apply { + type = VkStructureType.WRITE_DESCRIPTOR_SET + dstSet = descriptorSet + descriptorType = VkDescriptorType.UNIFORM_BUFFER + dstBinding = 0 + bufferInfo_ = uniformBufferVS.descriptor + } + // Binding 1 : Fragment shader texture sampler + // Fragment shader: layout (binding = 1) uniform sampler2D samplerColor; + writeDescriptorSets[1].apply { + type = VkStructureType.WRITE_DESCRIPTOR_SET + dstSet = descriptorSet + // The descriptor set will use a combined image sampler (sampler and image could be split) + descriptorType = VkDescriptorType.COMBINED_IMAGE_SAMPLER + dstBinding = 1 // Shader binding point 1 + imageInfo = textureDescriptor // Pointer to the descriptor image for our texture + } + + device updateDescriptorSets writeDescriptorSets + } + + fun preparePipelines() = stak { + + val inputAssemblyState = vk.PipelineInputAssemblyStateCreateInfo { + topology = VkPrimitiveTopology.TRIANGLE_LIST + } + + val rasterizationState = initializers.pipelineRasterizationStateCreateInfo( + VkPolygonMode.FILL, + VkCullMode.NONE.i, + VkFrontFace.COUNTER_CLOCKWISE, + 0) + + val blendAttachmentState = vk.PipelineColorBlendAttachmentState { colorWriteMask = 0xf } + + val colorBlendState = vk.PipelineColorBlendStateCreateInfo { attachment = blendAttachmentState } + + val depthStencilState = initializers.pipelineDepthStencilStateCreateInfo( + true, + true, + VkCompareOp.LESS_OR_EQUAL) + + val viewportState = vk.PipelineViewportStateCreateInfo { + viewportCount = 1 + scissorCount = 1 + } + + val multisampleState = vk.PipelineMultisampleStateCreateInfo { + rasterizationSamples = VkSampleCount.`1_BIT` + } + + val dynamicStateEnables = it.vkDynamicStateBufferOf(VkDynamicState.VIEWPORT, VkDynamicState.SCISSOR) + val dynamicState = vk.PipelineDynamicStateCreateInfo { + dynamicStates = dynamicStateEnables + } + + // Load shaders + val shaderStages = vk.PipelineShaderStageCreateInfo(2).also { + it[0].loadShader("$assetPath/shaders/texture/texture.vert", VkShaderStage.VERTEX_BIT) + it[1].loadShader("$assetPath/shaders/texture/texture.frag", VkShaderStage.FRAGMENT_BIT) + } + + val pipelineCreateInfo = initializers.pipelineCreateInfo( + pipelineLayout, + renderPass).apply { + vertexInputState = vertices.inputState + this.inputAssemblyState = inputAssemblyState + this.rasterizationState = rasterizationState + this.colorBlendState = colorBlendState + this.multisampleState = multisampleState + this.viewportState = viewportState + this.depthStencilState = depthStencilState + this.dynamicState = dynamicState + stages = shaderStages + } + + pipelines.solid = device.createGraphicsPipelines(pipelineCache, pipelineCreateInfo) + } + + // Prepare and initialize uniform buffer containing shader uniforms + fun prepareUniformBuffers() { + // Vertex shader uniform buffer block + vulkanDevice.createBuffer( + VkBufferUsage.UNIFORM_BUFFER_BIT.i, + VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, + uniformBufferVS, + VkDeviceSize(uboVS.size.L)) + + updateUniformBuffers() + } + + val viewMatrix = Mat4(1f).translateAssign(0f, 0f, zoom) + fun updateUniformBuffers() { + // Vertex shader + glm.perspective(uboVS.projection, 60f.rad, size.aspect, 0.001f, 256f) + + uboVS.model.apply { + put(viewMatrix * Mat4(1f).translateAssign(cameraPos)) + rotateAssign(rotation.x.rad, Vec3(1f, 0f, 0f)) + rotateAssign(rotation.y.rad, Vec3(0f, 1f, 0f)) + rotateAssign(rotation.z.rad, Vec3(0f, 0f, 1f)) + } + + uboVS.viewPos put Vec4(0f, 0f, -zoom, 0f) + + uniformBufferVS.mapping { dst -> uboVS to dst } + } + + override fun prepare() { + super.prepare() + loadTexture() + generateQuad() + setupVertexDescriptions() + prepareUniformBuffers() + setupDescriptorSetLayout() + preparePipelines() + setupDescriptorPool() + setupDescriptorSet() + buildCommandBuffers() + prepared = true + window.show() + } + + override fun render() { + if (!prepared) + return + draw() + } + + override fun viewChanged() = updateUniformBuffers() + +// virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) +// { +// if (overlay->header("Settings")) { +// if (overlay->sliderFloat("LOD bias", &uboVS.lodBias, 0.0f, (float)texture.mipLevels)) { // updateUniformBuffers() // } -// -// val viewMatrix = Mat4(1f).translateAssign(0f, 0f, zoom) -// fun updateUniformBuffers() { -// // Vertex shader -// glm.perspective(uboVS.projection, 60f.rad, size.aspect, 0.001f, 256f) -// -// uboVS.model.apply { -// put(viewMatrix * Mat4(1f).translateAssign(cameraPos)) -// rotateAssign(rotation.x.rad, Vec3(1f, 0f, 0f)) -// rotateAssign(rotation.y.rad, Vec3(0f, 1f, 0f)) -// rotateAssign(rotation.z.rad, Vec3(0f, 0f, 1f)) -// } -// -// uboVS.viewPos put Vec4(0f, 0f, -zoom, 0f) -// -// uniformBufferVS.mapping { dst -> uboVS to dst } -// } -// -// override fun prepare() { -// super.prepare() -// loadTexture() -// generateQuad() -// setupVertexDescriptions() -// prepareUniformBuffers() -// setupDescriptorSetLayout() -// preparePipelines() -// setupDescriptorPool() -// setupDescriptorSet() -// buildCommandBuffers() -// prepared = true -// window.show() // } -// -// override fun render() { -// if (!prepared) -// return -// draw() // } -// -// override fun viewChanged() = updateUniformBuffers() -// -//// virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) -//// { -//// if (overlay->header("Settings")) { -//// if (overlay->sliderFloat("LOD bias", &uboVS.lodBias, 0.0f, (float)texture.mipLevels)) { -//// updateUniformBuffers() -//// } -//// } -//// } -//} \ No newline at end of file +} \ No newline at end of file diff --git a/src/main/kotlin/vulkan/basics/08 Texture Cubemap.kt b/src/main/kotlin/vulkan/basics/08 Texture Cubemap.kt index 2936371..5b318fb 100644 --- a/src/main/kotlin/vulkan/basics/08 Texture Cubemap.kt +++ b/src/main/kotlin/vulkan/basics/08 Texture Cubemap.kt @@ -1,589 +1,589 @@ -///* -//* Vulkan Example - Cube map texture loading and displaying -//* -//* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de -//* -//* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -//*/ -// -//package vulkan.basics -// -//import gli_.gli -//import glm_.L -//import glm_.f -//import glm_.func.rad -//import glm_.glm -//import glm_.mat4x4.Mat4 -//import glm_.vec3.Vec3 -//import org.lwjgl.system.MemoryUtil.* -//import vkk.* -//import vulkan.assetPath -//import vulkan.base.* -// -//fun main(args: Array) { -// TextureCubemap().apply { -// setupWindow() -// initVulkan() -// prepare() -// renderLoop() -// destroy() -// } -//} -// -//private class TextureCubemap : VulkanExampleBase() { -// -// var displaySkybox = true -// -// val cubeMap = Texture() -// -// // Vertex layout for the models -// val vertexLayout = VertexLayout( -// VertexComponent.POSITION, -// VertexComponent.NORMAL, -// VertexComponent.UV) -// -// object models { -// val skybox = Model() -// val objects = ArrayList() -// var objectIndex = 0 -// } -// -// object uniformBuffers { -// val `object` = Buffer() -// val skybox = Buffer() -// } -// -// object uboVS : Bufferizable() { -// var projection = Mat4() -// @Order(1) -// var model = Mat4() -// @Order(2) -// var lodBias = 0f -// } -// -// object pipelines { -// var skybox = VkPipeline (NULL) -// var reflect = VkPipeline(NULL) -// } -// -// object descriptorSets { -// var `object` = VkDescriptorSet (NULL) -// var skybox = VkDescriptorSet (NULL) -// } -// -// var pipelineLayout = VkPipelineLayout(NULL) -// var descriptorSetLayout = VkDescriptorSetLayout(NULL) -// -// val objectNames = ArrayList() -// -// init { -// zoom = -4f -// rotationSpeed = 0.25f -// rotation(-7.25f, -120f, 0f) -// title = "Cube map textures" -//// settings.overlay = true -// } -// -// override fun destroy() { -// -// // Clean up used Vulkan resources -// // Note : Inherited destructor cleans up resources stored in base class -// -// device.apply { -// // Clean up texture resources -// destroyImageView(cubeMap.view) -// destroyImage(cubeMap.image) -// destroySampler(cubeMap.sampler) -// freeMemory(cubeMap.deviceMemory) -// -// destroyPipeline(pipelines.skybox) -// destroyPipeline(pipelines.reflect) -// -// destroyPipelineLayout(pipelineLayout) -// destroyDescriptorSetLayout(descriptorSetLayout) -// } -// for (model in models.objects) -// model.destroy() -// -// models.skybox.destroy() -// -// uniformBuffers.`object`.destroy() -// uniformBuffers.skybox.destroy() -// -// super.destroy() -// } -// -// // Enable physical device features required for this example -// override fun getEnabledFeatures() { -// if (deviceFeatures.samplerAnisotropy) -// enabledFeatures.samplerAnisotropy = true -// -// when { -// deviceFeatures.textureCompressionBC -> enabledFeatures.textureCompressionBC = true -// deviceFeatures.textureCompressionASTC_LDR -> enabledFeatures.textureCompressionASTC_LDR = true -// deviceFeatures.textureCompressionETC2 -> enabledFeatures.textureCompressionETC2 = true -// } -// } -// -// fun loadCubemap(filename: String, format: VkFormat, forceLinearTiling: Boolean) { -// -// val texCube = gli_.TextureCube(gli.load(filename)) -// -// assert(texCube.notEmpty()) -// -// cubeMap.size(texCube.extent()) -// cubeMap.mipLevels = texCube.levels() -// -// -// // Create a host-visible staging buffer that contains the raw image data -// -// val bufferCreateInfo = vk.BufferCreateInfo { -// size = VkDeviceSize(texCube.size.L) -// // This buffer is used as a transfer source for the buffer copy -// usage = VkBufferUsage.TRANSFER_SRC_BIT.i -// sharingMode = VkSharingMode.EXCLUSIVE -// } -// val stagingBuffer = device createBuffer bufferCreateInfo -// -// // Get memory requirements for the staging buffer (alignment, memory type bits) -// val memReqs = device getBufferMemoryRequirements stagingBuffer -// val memAllocInfo = vk.MemoryAllocateInfo { -// allocationSize = memReqs.size -// // Get memory type index for a host visible buffer -// memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT) -// } -// val stagingMemory = device allocateMemory memAllocInfo -// device.bindBufferMemory(stagingBuffer, stagingMemory) -// -// // Copy texture data into staging buffer -// device.mappingMemory(stagingMemory, VkDeviceSize(0), memReqs.size) { data -> -// memCopy(memAddress(texCube.data()), data, texCube.size.L) -// } -// -// // Create optimal tiled target image -// val imageCreateInfo = vk.ImageCreateInfo { -// imageType = VkImageType.`2D` -// this.format = format -// mipLevels = cubeMap.mipLevels -// samples = VkSampleCount.`1_BIT` -// tiling = VkImageTiling.OPTIMAL -// sharingMode = VkSharingMode.EXCLUSIVE -// initialLayout = VkImageLayout.UNDEFINED -// extent.set(cubeMap.size.x, cubeMap.size.y, 1) -// usage = VkImageUsage.TRANSFER_DST_BIT or VkImageUsage.SAMPLED_BIT -// // Cube faces count as array layers in Vulkan -// arrayLayers = 6 -// // This flag is required for cube map images -// flags = VkImageCreate.CUBE_COMPATIBLE_BIT.i -// } -// cubeMap.image = device createImage imageCreateInfo -// -// device.getImageMemoryRequirements(cubeMap.image, memReqs) -// -// memAllocInfo.allocationSize = memReqs.size -// memAllocInfo.memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryProperty.DEVICE_LOCAL_BIT) -// -// cubeMap.deviceMemory = device allocateMemory memAllocInfo -// device.bindImageMemory(cubeMap.image, cubeMap.deviceMemory) -// -// val copyCmd = createCommandBuffer(VkCommandBufferLevel.PRIMARY, true) -// -// // Setup buffer copy regions for each face including all of it's miplevels -// val bufferCopyRegions = vk.BufferImageCopy(6 * cubeMap.mipLevels) -// var offset = VkDeviceSize(0) -// -// for (face in 0..5) -// for (level in 0 until cubeMap.mipLevels) { -// -// bufferCopyRegions[face * cubeMap.mipLevels + level].apply { -// imageSubresource.apply { -// aspectMask = VkImageAspect.COLOR_BIT.i -// mipLevel = level -// baseArrayLayer = face -// layerCount = 1 -// } -// val extent = texCube[face][level].extent() -// imageExtent.set(extent.x, extent.y, 1) -// bufferOffset = offset -// } -// // Increase offset into staging buffer for next level / face -// offset += texCube[face][level].size -// } -// -// // Image barrier for optimal image (target) -// // Set initial layout for all array layers (faces) of the optimal (target) tiled texture -// val subresourceRange = vk.ImageSubresourceRange { -// aspectMask = VkImageAspect.COLOR_BIT.i -// baseMipLevel = 0 -// levelCount = cubeMap.mipLevels -// layerCount = 6 -// } -// tools.setImageLayout( -// copyCmd, -// cubeMap.image, -// VkImageLayout.UNDEFINED, -// VkImageLayout.TRANSFER_DST_OPTIMAL, -// subresourceRange) -// -// // Copy the cube map faces from the staging buffer to the optimal tiled image -// copyCmd.copyBufferToImage( -// stagingBuffer, -// cubeMap.image, -// VkImageLayout.TRANSFER_DST_OPTIMAL, -// bufferCopyRegions) -// -// // Change texture image layout to shader read after all faces have been copied -// cubeMap.imageLayout = VkImageLayout.SHADER_READ_ONLY_OPTIMAL -// tools.setImageLayout( -// copyCmd, -// cubeMap.image, -// VkImageLayout.TRANSFER_DST_OPTIMAL, -// cubeMap.imageLayout, -// subresourceRange) -// -// super.flushCommandBuffer(copyCmd, queue, true) -// -// // Create sampler -// val sampler = vk.SamplerCreateInfo { -// magFilter = VkFilter.LINEAR -// minFilter = VkFilter.LINEAR -// mipmapMode = VkSamplerMipmapMode.LINEAR -// addressModeU = VkSamplerAddressMode.CLAMP_TO_EDGE -// addressModeV = addressModeU -// addressModeW = addressModeU -// mipLodBias = 0f -// compareOp = VkCompareOp.NEVER -// minLod = 0f -// maxLod = cubeMap.mipLevels.f -// borderColor = VkBorderColor.FLOAT_OPAQUE_WHITE -// maxAnisotropy = 1f -// } -// if (vulkanDevice.features.samplerAnisotropy) { -// sampler.maxAnisotropy = vulkanDevice.properties.limits.maxSamplerAnisotropy -// sampler.anisotropyEnable = true -// } -// cubeMap.sampler = device createSampler sampler -// -// // Create image view -// val view = vk.ImageViewCreateInfo { -// // Cube map view type -// viewType = VkImageViewType.CUBE -// this.format = format -// components(VkComponentSwizzle.R, VkComponentSwizzle.G, VkComponentSwizzle.B, VkComponentSwizzle.A) -// this.subresourceRange.apply { -// set(VkImageAspect.COLOR_BIT.i, 0, 1, 0, 1) -// // 6 array layers (faces) -// layerCount = 6 -// // Set number of mip levels -// levelCount = cubeMap.mipLevels -// } -// image = cubeMap.image -// } -// cubeMap.view = device createImageView view -// -// // Clean up staging resources -// device freeMemory stagingMemory -// device destroyBuffer stagingBuffer -// } -// -// fun loadTextures() { -// // Vulkan core supports three different compressed texture formats -// // As the support differs between implemementations we need to check device features and select a proper format and file -// val (filename, format) = when { -// -// deviceFeatures.textureCompressionBC -> "cubemap_yokohama_bc3_unorm.ktx" to VkFormat.BC2_UNORM_BLOCK -// -// deviceFeatures.textureCompressionASTC_LDR -> "cubemap_yokohama_astc_8x8_unorm.ktx" to VkFormat.ASTC_8x8_UNORM_BLOCK -// -// deviceFeatures.textureCompressionETC2 -> "cubemap_yokohama_etc2_unorm.ktx" to VkFormat.ETC2_R8G8B8_UNORM_BLOCK -// -// else -> tools.exitFatal("Device does not support any compressed texture format!", ERROR_FEATURE_NOT_PRESENT) -// } -// -// loadCubemap("$assetPath/textures/$filename", format, false) -// } -// -// override fun buildCommandBuffers() { -// -// val cmdBufInfo = vk.CommandBufferBeginInfo() -// -// val clearValues = vk.ClearValue(2).also { -// it[0].color(defaultClearColor) -// it[1].depthStencil.set(1f, 0) -// } -// val renderPassBeginInfo = vk.RenderPassBeginInfo { -// renderPass = this@TextureCubemap.renderPass -// renderArea.offset.set(0, 0) -// renderArea.extent.set(size.x, size.y) -// this.clearValues = clearValues -// } -// for (i in drawCmdBuffers.indices) { -// -// // Set target frame buffer -// renderPassBeginInfo.framebuffer(frameBuffers[i].L) // TODO BUG -// -// drawCmdBuffers[i].apply { -// -// begin(cmdBufInfo) -// -// beginRenderPass(renderPassBeginInfo, VkSubpassContents.INLINE) -// -// setViewport(size) -// setScissor(size) -// -// // Skybox -// if (displaySkybox) { -// bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, pipelineLayout, descriptorSets.skybox) -// bindVertexBuffers(0, models.skybox.vertices.buffer) -// bindIndexBuffer(models.skybox.indices.buffer, VkDeviceSize(0), VkIndexType.UINT32) -// bindPipeline(VkPipelineBindPoint.GRAPHICS, pipelines.skybox) -// drawIndexed(models.skybox.indexCount, 1, 0, 0, 0) -// } -// -// // 3D object -// bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, pipelineLayout, descriptorSets.`object`) -// bindVertexBuffers(models.objects[models.objectIndex].vertices.buffer) -// bindIndexBuffer(models.objects[models.objectIndex].indices.buffer, VkDeviceSize(0), VkIndexType.UINT32) -// bindPipeline(VkPipelineBindPoint.GRAPHICS, pipelines.reflect) -// drawIndexed(models.objects[models.objectIndex].indexCount, 1, 0, 0, 0) -// -// drawUI() -// -// endRenderPass() -// -// end() -// } -// } -// } -// -// fun loadAssets() { -// // Skybox -// models.skybox.loadFromFile("$assetPath/models/cube.obj", vertexLayout, 0.05f, vulkanDevice, queue) -// // Objects -// val filenames = listOf("sphere.obj", "teapot.dae", "torusknot.obj") -// objectNames += listOf("Sphere", "Teapot", "Torusknot", "Venus") -// for (file in filenames) { -// val model = Model() -// val scale = 0.05f * if (file == "venus.fbx") 3f else 1f -// model.loadFromFile("$assetPath/models/$file", vertexLayout, scale, vulkanDevice, queue) -// models.objects += model -// } -// } -// -// fun setupDescriptorPool() { -// -// val poolSizes = vk.DescriptorPoolSize( -// VkDescriptorType.UNIFORM_BUFFER, 2, -// VkDescriptorType.COMBINED_IMAGE_SAMPLER, 2) -// -// val descriptorPoolInfo = vk.DescriptorPoolCreateInfo(poolSizes, 2) -// -// descriptorPool = device createDescriptorPool descriptorPoolInfo -// } -// -// fun setupDescriptorSetLayout() { -// -// val setLayoutBindings = vk.DescriptorSetLayoutBinding( -// // Binding 0 : Vertex shader uniform buffer -// VkDescriptorType.UNIFORM_BUFFER, VkShaderStage.VERTEX_BIT.i, 0, -// // Binding 1 : Fragment shader image sampler -// VkDescriptorType.COMBINED_IMAGE_SAMPLER, VkShaderStage.FRAGMENT_BIT.i, 1) -// -// val descriptorLayout = vk.DescriptorSetLayoutCreateInfo(setLayoutBindings) -// -// descriptorSetLayout = device createDescriptorSetLayout descriptorLayout -// -// val pipelineLayoutCreateInfo = vk.PipelineLayoutCreateInfo(descriptorSetLayout) -// -// pipelineLayout = device createPipelineLayout pipelineLayoutCreateInfo -// } -// -// fun setupDescriptorSets() { -// -// // Image descriptor for the cube map texture -// val textureDescriptor = vk.DescriptorImageInfo(cubeMap.sampler, cubeMap.view, cubeMap.imageLayout) -// -// val allocInfo = vk.DescriptorSetAllocateInfo(descriptorPool, descriptorSetLayout) -// -// // 3D object descriptor set -// descriptorSets.`object` = device allocateDescriptorSets allocInfo -// -// val writeDescriptorSets = vk.WriteDescriptorSet(2).also { -// // Binding 0 : Vertex shader uniform buffer -// it[0](descriptorSets.`object`, VkDescriptorType.UNIFORM_BUFFER, 0, uniformBuffers.`object`.descriptor) -// // Binding 1 : Fragment shader cubemap sampler -// it[1](descriptorSets.`object`, VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1, textureDescriptor) -// } -// device.updateDescriptorSets(writeDescriptorSets) -// -// // Sky box descriptor set -// descriptorSets.skybox = device allocateDescriptorSets allocInfo -// -// writeDescriptorSets.also { -// // Binding 0 : Vertex shader uniform buffer -// it[0](descriptorSets.skybox, VkDescriptorType.UNIFORM_BUFFER, 0, uniformBuffers.skybox.descriptor) -// // Binding 1 : Fragment shader cubemap sampler -// it[1](descriptorSets.skybox, VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1, textureDescriptor) -// } -// device updateDescriptorSets writeDescriptorSets -// } -// -// fun preparePipelines() { -// -// val inputAssemblyState = vk.PipelineInputAssemblyStateCreateInfo(VkPrimitiveTopology.TRIANGLE_LIST, 0, false) -// -// val rasterizationState = vk.PipelineRasterizationStateCreateInfo(VkPolygonMode.FILL, VkCullMode.BACK_BIT.i, VkFrontFace.COUNTER_CLOCKWISE) -// -// val blendAttachmentState = vk.PipelineColorBlendAttachmentState(0xf, false) -// -// val colorBlendState = vk.PipelineColorBlendStateCreateInfo(blendAttachmentState) -// -// val depthStencilState = vk.PipelineDepthStencilStateCreateInfo(false, false, VkCompareOp.LESS_OR_EQUAL) -// -// val viewportState = vk.PipelineViewportStateCreateInfo(1, 1) -// -// val multisampleState = vk.PipelineMultisampleStateCreateInfo(VkSampleCount.`1_BIT`) -// -// val dynamicStateEnables = listOf(VkDynamicState.VIEWPORT, VkDynamicState.SCISSOR) -// val dynamicState = vk.PipelineDynamicStateCreateInfo(dynamicStateEnables) -// -// // Vertex bindings and attributes -// val vertexInputBinding = vk.VertexInputBindingDescription(0, vertexLayout.stride, VkVertexInputRate.VERTEX) -// -// val vertexInputAttributes = vk.VertexInputAttributeDescription( -// 0, 0, VkFormat.R32G32B32_SFLOAT, 0, // Location 0: Position -// 0, 1, VkFormat.R32G32B32_SFLOAT, Vec3.size) // Location 1: Normal -// -// val vertexInputState = vk.PipelineVertexInputStateCreateInfo { -// vertexBindingDescription = vertexInputBinding -// vertexAttributeDescriptions = vertexInputAttributes -// } -// -// val shaderStages = vk.PipelineShaderStageCreateInfo(2) -// -// val pipelineCreateInfo = vk.GraphicsPipelineCreateInfo(pipelineLayout, renderPass, 0).also { -// it.inputAssemblyState = inputAssemblyState -// it.rasterizationState = rasterizationState -// it.colorBlendState = colorBlendState -// it.multisampleState = multisampleState -// it.viewportState = viewportState -// it.depthStencilState = depthStencilState -// it.dynamicState = dynamicState -// it.stages = shaderStages -// it.vertexInputState = vertexInputState -// } -// // Skybox pipeline (background cube) -// shaderStages[0].loadShader("$assetPath/shaders/texturecubemap/skybox.vert.spv", VkShaderStage.VERTEX_BIT) -// shaderStages[1].loadShader("$assetPath/shaders/texturecubemap/skybox.frag.spv", VkShaderStage.FRAGMENT_BIT) -// pipelines.skybox = device.createPipeline(pipelineCache, pipelineCreateInfo) -// -// // Cube map reflect pipeline -// shaderStages[0].loadShader("$assetPath/shaders/texturecubemap/reflect.vert.spv", VkShaderStage.VERTEX_BIT) -// shaderStages[1].loadShader("$assetPath/shaders/texturecubemap/reflect.frag.spv", VkShaderStage.FRAGMENT_BIT) -// // Enable depth test and write -// depthStencilState.depthWriteEnable = true -// depthStencilState.depthTestEnable = true -// // Flip cull mode -// rasterizationState.cullMode = VkCullMode.FRONT_BIT.i -// pipelines.reflect = device.createPipeline(pipelineCache, pipelineCreateInfo) -// } -// -// /** Prepare and initialize uniform buffer containing shader uniforms */ -// fun prepareUniformBuffers() { -// -// // Objact vertex shader uniform buffer -// vulkanDevice.createBuffer( -// VkBufferUsage.UNIFORM_BUFFER_BIT.i, -// VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, -// uniformBuffers.`object`, -// VkDeviceSize(uboVS.size.L)) -// -// // Skybox vertex shader uniform buffer -// vulkanDevice.createBuffer( -// VkBufferUsage.UNIFORM_BUFFER_BIT.i, -// VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, -// uniformBuffers.skybox, -// VkDeviceSize(uboVS.size.L)) -// -// // Map persistent -// uniformBuffers.`object`.map() -// uniformBuffers.skybox.map() -// +/* +* Vulkan Example - Cube map texture loading and displaying +* +* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de +* +* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +*/ + +package vulkan.basics + +import gli_.gli +import glm_.L +import glm_.f +import glm_.func.rad +import glm_.glm +import glm_.mat4x4.Mat4 +import glm_.vec3.Vec3 +import org.lwjgl.system.MemoryUtil.* +import vkk.* +import vulkan.assetPath +import vulkan.base.* + +fun main(args: Array) { + TextureCubemap().apply { + setupWindow() + initVulkan() + prepare() + renderLoop() + destroy() + } +} + +private class TextureCubemap : VulkanExampleBase() { + + var displaySkybox = true + + val cubeMap = Texture() + + // Vertex layout for the models + val vertexLayout = VertexLayout( + VertexComponent.POSITION, + VertexComponent.NORMAL, + VertexComponent.UV) + + object models { + val skybox = Model() + val objects = ArrayList() + var objectIndex = 0 + } + + object uniformBuffers { + val `object` = Buffer() + val skybox = Buffer() + } + + object uboVS : Bufferizable() { + var projection = Mat4() + @Order(1) + var model = Mat4() + @Order(2) + var lodBias = 0f + } + + object pipelines { + var skybox = VkPipeline (NULL) + var reflect = VkPipeline(NULL) + } + + object descriptorSets { + var `object` = VkDescriptorSet (NULL) + var skybox = VkDescriptorSet (NULL) + } + + var pipelineLayout = VkPipelineLayout(NULL) + var descriptorSetLayout = VkDescriptorSetLayout(NULL) + + val objectNames = ArrayList() + + init { + zoom = -4f + rotationSpeed = 0.25f + rotation(-7.25f, -120f, 0f) + title = "Cube map textures" +// settings.overlay = true + } + + override fun destroy() { + + // Clean up used Vulkan resources + // Note : Inherited destructor cleans up resources stored in base class + + device.apply { + // Clean up texture resources + destroyImageView(cubeMap.view) + destroyImage(cubeMap.image) + destroySampler(cubeMap.sampler) + freeMemory(cubeMap.deviceMemory) + + destroyPipeline(pipelines.skybox) + destroyPipeline(pipelines.reflect) + + destroyPipelineLayout(pipelineLayout) + destroyDescriptorSetLayout(descriptorSetLayout) + } + for (model in models.objects) + model.destroy() + + models.skybox.destroy() + + uniformBuffers.`object`.destroy() + uniformBuffers.skybox.destroy() + + super.destroy() + } + + // Enable physical device features required for this example + override fun getEnabledFeatures() { + if (deviceFeatures.samplerAnisotropy) + enabledFeatures.samplerAnisotropy = true + + when { + deviceFeatures.textureCompressionBC -> enabledFeatures.textureCompressionBC = true + deviceFeatures.textureCompressionASTC_LDR -> enabledFeatures.textureCompressionASTC_LDR = true + deviceFeatures.textureCompressionETC2 -> enabledFeatures.textureCompressionETC2 = true + } + } + + fun loadCubemap(filename: String, format: VkFormat, forceLinearTiling: Boolean) { + + val texCube = gli_.TextureCube(gli.load(filename)) + + assert(texCube.notEmpty()) + + cubeMap.size(texCube.extent()) + cubeMap.mipLevels = texCube.levels() + + + // Create a host-visible staging buffer that contains the raw image data + + val bufferCreateInfo = vk.BufferCreateInfo { + size = VkDeviceSize(texCube.size.L) + // This buffer is used as a transfer source for the buffer copy + usage = VkBufferUsage.TRANSFER_SRC_BIT.i + sharingMode = VkSharingMode.EXCLUSIVE + } + val stagingBuffer = device createBuffer bufferCreateInfo + + // Get memory requirements for the staging buffer (alignment, memory type bits) + val memReqs = device getBufferMemoryRequirements stagingBuffer + val memAllocInfo = vk.MemoryAllocateInfo { + allocationSize = memReqs.size + // Get memory type index for a host visible buffer + memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT) + } + val stagingMemory = device allocateMemory memAllocInfo + device.bindBufferMemory(stagingBuffer, stagingMemory) + + // Copy texture data into staging buffer + device.mappingMemory(stagingMemory, VkDeviceSize(0), memReqs.size) { data -> + memCopy(memAddress(texCube.data()), data, texCube.size.L) + } + + // Create optimal tiled target image + val imageCreateInfo = vk.ImageCreateInfo { + imageType = VkImageType.`2D` + this.format = format + mipLevels = cubeMap.mipLevels + samples = VkSampleCount.`1_BIT` + tiling = VkImageTiling.OPTIMAL + sharingMode = VkSharingMode.EXCLUSIVE + initialLayout = VkImageLayout.UNDEFINED + extent.set(cubeMap.size.x, cubeMap.size.y, 1) + usage = VkImageUsage.TRANSFER_DST_BIT or VkImageUsage.SAMPLED_BIT + // Cube faces count as array layers in Vulkan + arrayLayers = 6 + // This flag is required for cube map images + flags = VkImageCreate.CUBE_COMPATIBLE_BIT.i + } + cubeMap.image = device createImage imageCreateInfo + + device.getImageMemoryRequirements(cubeMap.image, memReqs) + + memAllocInfo.allocationSize = memReqs.size + memAllocInfo.memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryProperty.DEVICE_LOCAL_BIT) + + cubeMap.deviceMemory = device allocateMemory memAllocInfo + device.bindImageMemory(cubeMap.image, cubeMap.deviceMemory) + + val copyCmd = createCommandBuffer(VkCommandBufferLevel.PRIMARY, true) + + // Setup buffer copy regions for each face including all of it's miplevels + val bufferCopyRegions = vk.BufferImageCopy(6 * cubeMap.mipLevels) + var offset = VkDeviceSize(0) + + for (face in 0..5) + for (level in 0 until cubeMap.mipLevels) { + + bufferCopyRegions[face * cubeMap.mipLevels + level].apply { + imageSubresource.apply { + aspectMask = VkImageAspect.COLOR_BIT.i + mipLevel = level + baseArrayLayer = face + layerCount = 1 + } + val extent = texCube[face][level].extent() + imageExtent.set(extent.x, extent.y, 1) + bufferOffset = offset + } + // Increase offset into staging buffer for next level / face + offset += texCube[face][level].size + } + + // Image barrier for optimal image (target) + // Set initial layout for all array layers (faces) of the optimal (target) tiled texture + val subresourceRange = vk.ImageSubresourceRange { + aspectMask = VkImageAspect.COLOR_BIT.i + baseMipLevel = 0 + levelCount = cubeMap.mipLevels + layerCount = 6 + } + tools.setImageLayout( + copyCmd, + cubeMap.image, + VkImageLayout.UNDEFINED, + VkImageLayout.TRANSFER_DST_OPTIMAL, + subresourceRange) + + // Copy the cube map faces from the staging buffer to the optimal tiled image + copyCmd.copyBufferToImage( + stagingBuffer, + cubeMap.image, + VkImageLayout.TRANSFER_DST_OPTIMAL, + bufferCopyRegions) + + // Change texture image layout to shader read after all faces have been copied + cubeMap.imageLayout = VkImageLayout.SHADER_READ_ONLY_OPTIMAL + tools.setImageLayout( + copyCmd, + cubeMap.image, + VkImageLayout.TRANSFER_DST_OPTIMAL, + cubeMap.imageLayout, + subresourceRange) + + super.flushCommandBuffer(copyCmd, queue, true) + + // Create sampler + val sampler = vk.SamplerCreateInfo { + magFilter = VkFilter.LINEAR + minFilter = VkFilter.LINEAR + mipmapMode = VkSamplerMipmapMode.LINEAR + addressModeU = VkSamplerAddressMode.CLAMP_TO_EDGE + addressModeV = addressModeU + addressModeW = addressModeU + mipLodBias = 0f + compareOp = VkCompareOp.NEVER + minLod = 0f + maxLod = cubeMap.mipLevels.f + borderColor = VkBorderColor.FLOAT_OPAQUE_WHITE + maxAnisotropy = 1f + } + if (vulkanDevice.features.samplerAnisotropy) { + sampler.maxAnisotropy = vulkanDevice.properties.limits.maxSamplerAnisotropy + sampler.anisotropyEnable = true + } + cubeMap.sampler = device createSampler sampler + + // Create image view + val view = vk.ImageViewCreateInfo { + // Cube map view type + viewType = VkImageViewType.CUBE + this.format = format + components(VkComponentSwizzle.R, VkComponentSwizzle.G, VkComponentSwizzle.B, VkComponentSwizzle.A) + this.subresourceRange.apply { + set(VkImageAspect.COLOR_BIT.i, 0, 1, 0, 1) + // 6 array layers (faces) + layerCount = 6 + // Set number of mip levels + levelCount = cubeMap.mipLevels + } + image = cubeMap.image + } + cubeMap.view = device createImageView view + + // Clean up staging resources + device freeMemory stagingMemory + device destroyBuffer stagingBuffer + } + + fun loadTextures() { + // Vulkan core supports three different compressed texture formats + // As the support differs between implemementations we need to check device features and select a proper format and file + val (filename, format) = when { + + deviceFeatures.textureCompressionBC -> "cubemap_yokohama_bc3_unorm.ktx" to VkFormat.BC2_UNORM_BLOCK + + deviceFeatures.textureCompressionASTC_LDR -> "cubemap_yokohama_astc_8x8_unorm.ktx" to VkFormat.ASTC_8x8_UNORM_BLOCK + + deviceFeatures.textureCompressionETC2 -> "cubemap_yokohama_etc2_unorm.ktx" to VkFormat.ETC2_R8G8B8_UNORM_BLOCK + + else -> tools.exitFatal("Device does not support any compressed texture format!", ERROR_FEATURE_NOT_PRESENT) + } + + loadCubemap("$assetPath/textures/$filename", format, false) + } + + override fun buildCommandBuffers() { + + val cmdBufInfo = vk.CommandBufferBeginInfo() + + val clearValues = vk.ClearValue(2).also { + it[0].color(defaultClearColor) + it[1].depthStencil.set(1f, 0) + } + val renderPassBeginInfo = vk.RenderPassBeginInfo { + renderPass = this@TextureCubemap.renderPass + renderArea.offset.set(0, 0) + renderArea.extent.set(size.x, size.y) + this.clearValues = clearValues + } + for (i in drawCmdBuffers.indices) { + + // Set target frame buffer + renderPassBeginInfo.framebuffer(frameBuffers[i].L) // TODO BUG + + drawCmdBuffers[i].apply { + + begin(cmdBufInfo) + + beginRenderPass(renderPassBeginInfo, VkSubpassContents.INLINE) + + setViewport(size) + setScissor(size) + + // Skybox + if (displaySkybox) { + bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, pipelineLayout, descriptorSets.skybox) + bindVertexBuffers(0, models.skybox.vertices.buffer) + bindIndexBuffer(models.skybox.indices.buffer, VkDeviceSize(0), VkIndexType.UINT32) + bindPipeline(VkPipelineBindPoint.GRAPHICS, pipelines.skybox) + drawIndexed(models.skybox.indexCount, 1, 0, 0, 0) + } + + // 3D object + bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, pipelineLayout, descriptorSets.`object`) + bindVertexBuffers(models.objects[models.objectIndex].vertices.buffer) + bindIndexBuffer(models.objects[models.objectIndex].indices.buffer, VkDeviceSize(0), VkIndexType.UINT32) + bindPipeline(VkPipelineBindPoint.GRAPHICS, pipelines.reflect) + drawIndexed(models.objects[models.objectIndex].indexCount, 1, 0, 0, 0) + + drawUI() + + endRenderPass() + + end() + } + } + } + + fun loadAssets() { + // Skybox + models.skybox.loadFromFile("$assetPath/models/cube.obj", vertexLayout, 0.05f, vulkanDevice, queue) + // Objects + val filenames = listOf("sphere.obj", "teapot.dae", "torusknot.obj") + objectNames += listOf("Sphere", "Teapot", "Torusknot", "Venus") + for (file in filenames) { + val model = Model() + val scale = 0.05f * if (file == "venus.fbx") 3f else 1f + model.loadFromFile("$assetPath/models/$file", vertexLayout, scale, vulkanDevice, queue) + models.objects += model + } + } + + fun setupDescriptorPool() { + + val poolSizes = vk.DescriptorPoolSize( + VkDescriptorType.UNIFORM_BUFFER, 2, + VkDescriptorType.COMBINED_IMAGE_SAMPLER, 2) + + val descriptorPoolInfo = vk.DescriptorPoolCreateInfo(poolSizes, 2) + + descriptorPool = device createDescriptorPool descriptorPoolInfo + } + + fun setupDescriptorSetLayout() { + + val setLayoutBindings = vk.DescriptorSetLayoutBinding( + // Binding 0 : Vertex shader uniform buffer + VkDescriptorType.UNIFORM_BUFFER, VkShaderStage.VERTEX_BIT.i, 0, + // Binding 1 : Fragment shader image sampler + VkDescriptorType.COMBINED_IMAGE_SAMPLER, VkShaderStage.FRAGMENT_BIT.i, 1) + + val descriptorLayout = vk.DescriptorSetLayoutCreateInfo(setLayoutBindings) + + descriptorSetLayout = device createDescriptorSetLayout descriptorLayout + + val pipelineLayoutCreateInfo = vk.PipelineLayoutCreateInfo(descriptorSetLayout) + + pipelineLayout = device createPipelineLayout pipelineLayoutCreateInfo + } + + fun setupDescriptorSets() { + + // Image descriptor for the cube map texture + val textureDescriptor = vk.DescriptorImageInfo(cubeMap.sampler, cubeMap.view, cubeMap.imageLayout) + + val allocInfo = vk.DescriptorSetAllocateInfo(descriptorPool, descriptorSetLayout) + + // 3D object descriptor set + descriptorSets.`object` = device allocateDescriptorSets allocInfo + + val writeDescriptorSets = vk.WriteDescriptorSet(2).also { + // Binding 0 : Vertex shader uniform buffer + it[0](descriptorSets.`object`, VkDescriptorType.UNIFORM_BUFFER, 0, uniformBuffers.`object`.descriptor) + // Binding 1 : Fragment shader cubemap sampler + it[1](descriptorSets.`object`, VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1, textureDescriptor) + } + device.updateDescriptorSets(writeDescriptorSets) + + // Sky box descriptor set + descriptorSets.skybox = device allocateDescriptorSets allocInfo + + writeDescriptorSets.also { + // Binding 0 : Vertex shader uniform buffer + it[0](descriptorSets.skybox, VkDescriptorType.UNIFORM_BUFFER, 0, uniformBuffers.skybox.descriptor) + // Binding 1 : Fragment shader cubemap sampler + it[1](descriptorSets.skybox, VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1, textureDescriptor) + } + device updateDescriptorSets writeDescriptorSets + } + + fun preparePipelines() { + + val inputAssemblyState = vk.PipelineInputAssemblyStateCreateInfo(VkPrimitiveTopology.TRIANGLE_LIST, 0, false) + + val rasterizationState = vk.PipelineRasterizationStateCreateInfo(VkPolygonMode.FILL, VkCullMode.BACK_BIT.i, VkFrontFace.COUNTER_CLOCKWISE) + + val blendAttachmentState = vk.PipelineColorBlendAttachmentState(0xf, false) + + val colorBlendState = vk.PipelineColorBlendStateCreateInfo(blendAttachmentState) + + val depthStencilState = vk.PipelineDepthStencilStateCreateInfo(false, false, VkCompareOp.LESS_OR_EQUAL) + + val viewportState = vk.PipelineViewportStateCreateInfo(1, 1) + + val multisampleState = vk.PipelineMultisampleStateCreateInfo(VkSampleCount.`1_BIT`) + + val dynamicStateEnables = listOf(VkDynamicState.VIEWPORT, VkDynamicState.SCISSOR) + val dynamicState = vk.PipelineDynamicStateCreateInfo(dynamicStateEnables) + + // Vertex bindings and attributes + val vertexInputBinding = vk.VertexInputBindingDescription(0, vertexLayout.stride, VkVertexInputRate.VERTEX) + + val vertexInputAttributes = vk.VertexInputAttributeDescription( + 0, 0, VkFormat.R32G32B32_SFLOAT, 0, // Location 0: Position + 0, 1, VkFormat.R32G32B32_SFLOAT, Vec3.size) // Location 1: Normal + + val vertexInputState = vk.PipelineVertexInputStateCreateInfo { + vertexBindingDescription = vertexInputBinding + vertexAttributeDescriptions = vertexInputAttributes + } + + val shaderStages = vk.PipelineShaderStageCreateInfo(2) + + val pipelineCreateInfo = vk.GraphicsPipelineCreateInfo(pipelineLayout, renderPass, 0).also { + it.inputAssemblyState = inputAssemblyState + it.rasterizationState = rasterizationState + it.colorBlendState = colorBlendState + it.multisampleState = multisampleState + it.viewportState = viewportState + it.depthStencilState = depthStencilState + it.dynamicState = dynamicState + it.stages = shaderStages + it.vertexInputState = vertexInputState + } + // Skybox pipeline (background cube) + shaderStages[0].loadShader("$assetPath/shaders/texturecubemap/skybox.vert.spv", VkShaderStage.VERTEX_BIT) + shaderStages[1].loadShader("$assetPath/shaders/texturecubemap/skybox.frag.spv", VkShaderStage.FRAGMENT_BIT) + pipelines.skybox = device.createPipeline(pipelineCache, pipelineCreateInfo) + + // Cube map reflect pipeline + shaderStages[0].loadShader("$assetPath/shaders/texturecubemap/reflect.vert.spv", VkShaderStage.VERTEX_BIT) + shaderStages[1].loadShader("$assetPath/shaders/texturecubemap/reflect.frag.spv", VkShaderStage.FRAGMENT_BIT) + // Enable depth test and write + depthStencilState.depthWriteEnable = true + depthStencilState.depthTestEnable = true + // Flip cull mode + rasterizationState.cullMode = VkCullMode.FRONT_BIT.i + pipelines.reflect = device.createPipeline(pipelineCache, pipelineCreateInfo) + } + + /** Prepare and initialize uniform buffer containing shader uniforms */ + fun prepareUniformBuffers() { + + // Objact vertex shader uniform buffer + vulkanDevice.createBuffer( + VkBufferUsage.UNIFORM_BUFFER_BIT.i, + VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, + uniformBuffers.`object`, + VkDeviceSize(uboVS.size.L)) + + // Skybox vertex shader uniform buffer + vulkanDevice.createBuffer( + VkBufferUsage.UNIFORM_BUFFER_BIT.i, + VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, + uniformBuffers.skybox, + VkDeviceSize(uboVS.size.L)) + + // Map persistent + uniformBuffers.`object`.map() + uniformBuffers.skybox.map() + + updateUniformBuffers() + } + + fun updateUniformBuffers() { + + // 3D object + var viewMatrix = Mat4(1f) + uboVS.projection = glm.perspective(60.0f.rad, size.aspect, 0.001f, 256f) + viewMatrix = glm.translate(viewMatrix, 0f, 0f, zoom) + + uboVS.model put 1f + uboVS.model = viewMatrix * glm.translate(uboVS.model, cameraPos) + .rotateAssign(rotation.x.rad, 1f, 0f, 0f) + .rotateAssign(rotation.y.rad, 0f, 1f, 0f) + .rotateAssign(rotation.z.rad, 0f, 0f, 1f) + + uboVS to uniformBuffers.`object`.mapped + + // Skybox + viewMatrix put 1f + uboVS.projection = glm.perspective(60f.rad, size.aspect, 0.001f, 256f) + + uboVS.model put viewMatrix + uboVS.model + .rotateAssign(rotation.x.rad, 1f, 0f, 0f) + .rotateAssign(rotation.y.rad, 0f, 1f, 0f) + .rotateAssign(rotation.z.rad, 0f, 0f, 1f) + + uboVS to uniformBuffers.skybox.mapped + } + + fun draw() { + + super.prepareFrame() + + submitInfo.commandBuffer = drawCmdBuffers[currentBuffer] + queue submit submitInfo + + super.submitFrame() + } + + override fun prepare() { + super.prepare() + loadTextures() + loadAssets() + prepareUniformBuffers() + setupDescriptorSetLayout() + preparePipelines() + setupDescriptorPool() + setupDescriptorSets() + buildCommandBuffers() + prepared = true + window.show() + } + + override fun render() { + if (!prepared) + return + draw() + } + + override fun viewChanged() = updateUniformBuffers() + +// virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) +// { +// if (overlay->header("Settings")) { +// if (overlay->sliderFloat("LOD bias", &uboVS.lodBias, 0.0f, (float)cubeMap.mipLevels)) { // updateUniformBuffers() // } -// -// fun updateUniformBuffers() { -// -// // 3D object -// var viewMatrix = Mat4(1f) -// uboVS.projection = glm.perspective(60.0f.rad, size.aspect, 0.001f, 256f) -// viewMatrix = glm.translate(viewMatrix, 0f, 0f, zoom) -// -// uboVS.model put 1f -// uboVS.model = viewMatrix * glm.translate(uboVS.model, cameraPos) -// .rotateAssign(rotation.x.rad, 1f, 0f, 0f) -// .rotateAssign(rotation.y.rad, 0f, 1f, 0f) -// .rotateAssign(rotation.z.rad, 0f, 0f, 1f) -// -// uboVS to uniformBuffers.`object`.mapped -// -// // Skybox -// viewMatrix put 1f -// uboVS.projection = glm.perspective(60f.rad, size.aspect, 0.001f, 256f) -// -// uboVS.model put viewMatrix -// uboVS.model -// .rotateAssign(rotation.x.rad, 1f, 0f, 0f) -// .rotateAssign(rotation.y.rad, 0f, 1f, 0f) -// .rotateAssign(rotation.z.rad, 0f, 0f, 1f) -// -// uboVS to uniformBuffers.skybox.mapped -// } -// -// fun draw() { -// -// super.prepareFrame() -// -// submitInfo.commandBuffer = drawCmdBuffers[currentBuffer] -// queue submit submitInfo -// -// super.submitFrame() +// if (overlay->comboBox("Object type", &models.objectIndex, objectNames)) { +// buildCommandBuffers() // } -// -// override fun prepare() { -// super.prepare() -// loadTextures() -// loadAssets() -// prepareUniformBuffers() -// setupDescriptorSetLayout() -// preparePipelines() -// setupDescriptorPool() -// setupDescriptorSets() +// if (overlay->checkBox("Skybox", &displaySkybox)) { // buildCommandBuffers() -// prepared = true -// window.show() // } -// -// override fun render() { -// if (!prepared) -// return -// draw() // } -// -// override fun viewChanged() = updateUniformBuffers() -// -//// virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) -//// { -//// if (overlay->header("Settings")) { -//// if (overlay->sliderFloat("LOD bias", &uboVS.lodBias, 0.0f, (float)cubeMap.mipLevels)) { -//// updateUniformBuffers() -//// } -//// if (overlay->comboBox("Object type", &models.objectIndex, objectNames)) { -//// buildCommandBuffers() -//// } -//// if (overlay->checkBox("Skybox", &displaySkybox)) { -//// buildCommandBuffers() -//// } -//// } -//// } -//} \ No newline at end of file +// } +} \ No newline at end of file diff --git a/src/main/kotlin/vulkan/basics/09 Texture Array.kt b/src/main/kotlin/vulkan/basics/09 Texture Array.kt index 3e992e8..844ef98 100644 --- a/src/main/kotlin/vulkan/basics/09 Texture Array.kt +++ b/src/main/kotlin/vulkan/basics/09 Texture Array.kt @@ -1,581 +1,581 @@ -///* -//* Vulkan Example - Texture arrays and instanced rendering -//* -//* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de -//* -//* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -//*/ -// -//package vulkan.basics -// -//import gli_.Texture2dArray -//import gli_.gli -//import glm_.L -//import glm_.f -//import glm_.func.rad -//import glm_.glm -//import glm_.mat4x4.Mat4 -//import glm_.vec2.Vec2 -//import glm_.vec3.Vec3 -//import glm_.vec4.Vec4 -//import kool.bufferBig -//import kool.cap -//import kool.stak -//import org.lwjgl.system.MemoryUtil.* -//import org.lwjgl.vulkan.VkPipelineVertexInputStateCreateInfo -//import org.lwjgl.vulkan.VkVertexInputAttributeDescription -//import org.lwjgl.vulkan.VkVertexInputBindingDescription -//import vkk.* -//import vulkan.VERTEX_BUFFER_BIND_ID -//import vulkan.assetPath -//import vulkan.base.Buffer -//import vulkan.base.Texture -//import vulkan.base.VulkanExampleBase -//import vulkan.base.tools -//import java.nio.ByteBuffer -// -//fun main(args: Array) { -// TextureArray().apply { -// setupWindow() -// initVulkan() -// prepare() -// renderLoop() -// destroy() -// } -//} -// -//class TextureArray : VulkanExampleBase() { -// -// /** Vertex layout for this example */ -// object Vertex { -// // float pos[3]; -//// float uv[2]; -// val size = Vec3.size + Vec2.size -// } -// -// // Number of array layers in texture array -// // Also used as instance count -// var layerCount = 0 -// val textureArray = Texture() -// -// object vertices { -// lateinit var inputState: VkPipelineVertexInputStateCreateInfo -// lateinit var bindingDescription: VkVertexInputBindingDescription -// lateinit var attributeDescriptions: VkVertexInputAttributeDescription.Buffer -// } -// -// val vertexBuffer = Buffer() -// val indexBuffer = Buffer() -// var indexCount = 0 -// -// val uniformBufferVS = Buffer() -// -// class UboInstanceData { -// // Model matrix -// var model = Mat4() -// // Texture array index -// // Vec4 due to padding -// var arrayIndex = Vec4() -// -// fun to(bytes: ByteBuffer, offset: Int) { -// model.to(bytes, offset) -// bytes.putFloat(offset + Mat4.size, arrayIndex.x) -// } -// -// companion object { -// val size = Mat4.size + Vec4.size -// } -// } -// -// object uboVS { -// // Global matrices -// object matrices : Bufferizable() { -// var projection = Mat4() -// var view = Mat4() -// } -// // Seperate data for each instance -// val instance = ArrayList() -// -// fun prepare() { -// buffer = bufferBig(matrices.size + instance.size * UboInstanceData.size) -// address = memAddress(buffer) -// for (i in instance.indices) -// instance[i].to(buffer, matrices.size + UboInstanceData.size * i) -// } -// -// lateinit var buffer: ByteBuffer -// var address = NULL -// } -// -// -// var pipeline = VkPipeline(NULL) -// var pipelineLayout = VkPipelineLayout(NULL) -// var descriptorSet= VkDescriptorSet (NULL) -// var descriptorSetLayout= VkDescriptorSetLayout (NULL) -// -// init { -// zoom = -15f -// rotationSpeed = 0.25f -// rotation(-15f, 35f, 0f) -// title = "Texture arrays" -//// settings.overlay = true -// } -// -// override fun destroy() { -// -// // Clean up used Vulkan resources -// // Note : Inherited destructor cleans up resources stored in base class -// -// // Clean up texture resources -// device.apply { -// destroyImageView(textureArray.view) -// destroyImage(textureArray.image) -// destroySampler(textureArray.sampler) -// freeMemory(textureArray.deviceMemory) -// -// destroyPipeline(pipeline) -// -// destroyPipelineLayout(pipelineLayout) -// destroyDescriptorSetLayout(descriptorSetLayout) -// } -// vertexBuffer.destroy() -// indexBuffer.destroy() -// -// uniformBufferVS.destroy() -// -// super.destroy() -// } -// -// fun loadTextureArray(filename: String, format: VkFormat) { -// -// val tex2DArray = Texture2dArray(gli.load(filename)) -// -// assert(tex2DArray.notEmpty()) -// -// textureArray.size(tex2DArray.extent()) -// layerCount = tex2DArray.layers() -// -// // Create a host-visible staging buffer that contains the raw image data -// -// val bufferCreateInfo = vk.BufferCreateInfo { -// size = VkDeviceSize(tex2DArray.size.L) -// // This buffer is used as a transfer source for the buffer copy -// usage = VkBufferUsage.TRANSFER_SRC_BIT.i -// sharingMode = VkSharingMode.EXCLUSIVE -// } -// val stagingBuffer = device createBuffer bufferCreateInfo -// -// // Get memory requirements for the staging buffer (alignment, memory type bits) -// val memReqs = device getBufferMemoryRequirements stagingBuffer -// val memAllocInfo = vk.MemoryAllocateInfo { -// allocationSize = memReqs.size -// // Get memory type index for a host visible buffer -// memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT) -// } -// val stagingMemory = device allocateMemory memAllocInfo -// device.bindBufferMemory(stagingBuffer, stagingMemory) -// -// // Copy texture data into staging buffer -// device.mappingMemory(stagingMemory, VkDeviceSize(0), memReqs.size, 0) { data -> -// memCopy(memAddress(tex2DArray.data()), data, tex2DArray.size.L) -// } -// -// // Setup buffer copy regions for array layers -// val bufferCopyRegions = vk.BufferImageCopy(layerCount) -// var offset = VkDeviceSize(0) -// -// for (layer in 0 until layerCount) { -// -// bufferCopyRegions[layer].apply { -// imageSubresource.apply { -// aspectMask = VkImageAspect.COLOR_BIT.i -// mipLevel = 0 -// baseArrayLayer = layer -// layerCount = 1 -// } -// imageExtent.apply { -// val (w, h) = tex2DArray[layer][0].extent() // TODO BUG -// width = w -// height = h -// depth = 1 -// } -// bufferOffset = offset -// } -// -// // Increase offset into staging buffer for next level / face -// offset += tex2DArray[layer][0].size -// } -// -// // Create optimal tiled target image -// val imageCreateInfo = vk.ImageCreateInfo { -// imageType = VkImageType.`2D` -// this.format = format -// mipLevels = 1 -// samples = VkSampleCount.`1_BIT` -// tiling = VkImageTiling.OPTIMAL -// sharingMode = VkSharingMode.EXCLUSIVE -// initialLayout = VkImageLayout.UNDEFINED -// extent.set(textureArray.size.x, textureArray.size.y, 1) -// usage = VkImageUsage.TRANSFER_DST_BIT or VkImageUsage.SAMPLED_BIT -// arrayLayers = layerCount -// } -// textureArray.image = device createImage imageCreateInfo -// -// device.getImageMemoryRequirements(textureArray.image, memReqs) -// -// memAllocInfo.allocationSize = memReqs.size -// memAllocInfo.memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryProperty.DEVICE_LOCAL_BIT) -// -// textureArray.deviceMemory = device allocateMemory memAllocInfo -// device.bindImageMemory(textureArray.image, textureArray.deviceMemory) -// -// val copyCmd = super.createCommandBuffer(VkCommandBufferLevel.PRIMARY, true) -// -// // Image barrier for optimal image (target) -// // Set initial layout for all array layers (faces) of the optimal (target) tiled texture -// val subresourceRange = vk.ImageSubresourceRange { -// aspectMask = VkImageAspect.COLOR_BIT.i -// baseMipLevel = 0 -// levelCount = 1 -// layerCount = this@TextureArray.layerCount -// } -// tools.setImageLayout( -// copyCmd, -// textureArray.image, -// VkImageLayout.UNDEFINED, -// VkImageLayout.TRANSFER_DST_OPTIMAL, -// subresourceRange) -// -// // Copy the cube map faces from the staging buffer to the optimal tiled image -// copyCmd.copyBufferToImage( -// stagingBuffer, -// textureArray.image, -// VkImageLayout.TRANSFER_DST_OPTIMAL, -// bufferCopyRegions) -// -// // Change texture image layout to shader read after all faces have been copied -// textureArray.imageLayout = VkImageLayout.SHADER_READ_ONLY_OPTIMAL -// tools.setImageLayout( -// copyCmd, -// textureArray.image, -// VkImageLayout.TRANSFER_DST_OPTIMAL, -// textureArray.imageLayout, -// subresourceRange) -// -// super.flushCommandBuffer(copyCmd, queue, true) -// -// // Create sampler -// val sampler = vk.SamplerCreateInfo { -// magFilter = VkFilter.LINEAR -// minFilter = VkFilter.LINEAR -// mipmapMode = VkSamplerMipmapMode.LINEAR -// addressModeU = VkSamplerAddressMode.CLAMP_TO_EDGE -// addressModeV = addressModeU -// addressModeW = addressModeU -// mipLodBias = 0f -// maxAnisotropy = 8f -// compareOp = VkCompareOp.NEVER -// minLod = 0f -// maxLod = 0f -// borderColor = VkBorderColor.FLOAT_OPAQUE_WHITE -// } -// textureArray.sampler = device createSampler sampler -// -// // Create image view -// val view = vk.ImageViewCreateInfo { -// viewType = VkImageViewType.`2D_ARRAY` -// this.format = format -// components(VkComponentSwizzle.R, VkComponentSwizzle.G, VkComponentSwizzle.B, VkComponentSwizzle.A) -// this.subresourceRange.apply { -// set(VkImageAspect.COLOR_BIT.i, 0, 1, 0, 1) -// layerCount = this@TextureArray.layerCount // TODO move in ::set -// levelCount = 1 -// } -// image = textureArray.image -// } -// textureArray.view = device createImageView view -// -// // Clean up staging resources -// device freeMemory stagingMemory -// device destroyBuffer stagingBuffer -// } -// -// fun loadTextures() { -// // Vulkan core supports three different compressed texture formats -// // As the support differs between implemementations we need to check device features and select a proper format and file -// val (filename, format) = when { -// deviceFeatures.textureCompressionBC -> "texturearray_bc3_unorm.ktx" to VkFormat.BC3_UNORM_BLOCK -// deviceFeatures.textureCompressionASTC_LDR -> "texturearray_astc_8x8_unorm.ktx" to VkFormat.ASTC_8x8_UNORM_BLOCK -// deviceFeatures.textureCompressionETC2 -> "texturearray_etc2_unorm.ktx" to VkFormat.ETC2_R8G8B8_UNORM_BLOCK -// else -> tools.exitFatal("Device does not support any compressed texture format!", ERROR_FEATURE_NOT_PRESENT) -// } -// loadTextureArray("$assetPath/textures/$filename", format) -// } -// -// override fun buildCommandBuffers() { -// -// val cmdBufInfo = vk.CommandBufferBeginInfo() -// -// val clearValues = vk.ClearValue(2).also { -// it[0].color(defaultClearColor) -// it[1].depthStencil.set(1f, 0) -// } -// val renderPassBeginInfo = vk.RenderPassBeginInfo { -// renderPass = this@TextureArray.renderPass -// renderArea.apply { -// offset.set(0, 0) -// extent.set(size.x, size.y) -// } -// this.clearValues = clearValues -// } -// for (i in drawCmdBuffers.indices) { -// // Set target frame buffer -// renderPassBeginInfo.framebuffer(frameBuffers[i].L) // TODO BUG -// -// drawCmdBuffers[i].apply { -// -// begin(cmdBufInfo) -// -// beginRenderPass(renderPassBeginInfo, VkSubpassContents.INLINE) -// -// setViewport(size) -// setScissor(size) -// -// bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, pipelineLayout, descriptorSet) -// bindPipeline(VkPipelineBindPoint.GRAPHICS, pipeline) -// -// bindVertexBuffers(VERTEX_BUFFER_BIND_ID, vertexBuffer.buffer) -// bindIndexBuffer(indexBuffer.buffer, VkDeviceSize(0), VkIndexType.UINT32) -// -// drawIndexed(indexCount, layerCount, 0, 0, 0) -// -// drawUI() -// -// endRenderPass() -// -// end() -// } -// } -// } -// -// fun generateQuad() = stak { -// // Setup vertices for a single uv-mapped quad made from two triangles -// val vertices = it.floats( -// +2.5f, +2.5f, 0f, 1f, 1f, -// -2.5f, +2.5f, 0f, 0f, 1f, -// -2.5f, -2.5f, 0f, 0f, 0f, -// +2.5f, -2.5f, 0f, 1f, 0f) -// -// // Setup indices -// val indices = it.ints(0, 1, 2, 2, 3, 0) -// indexCount = indices.cap -// -// // Create buffers -// // For the sake of simplicity we won't stage the vertex data to the gpu memory -// // Vertex buffer -// vulkanDevice.createBuffer( -// VkBufferUsage.VERTEX_BUFFER_BIT.i, -// VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, -// vertexBuffer, -// vertices) -// // Index buffer -// vulkanDevice.createBuffer( -// VkBufferUsage.INDEX_BUFFER_BIT.i, -// VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, -// indexBuffer, -// indices) -// } -// -// fun setupVertexDescriptions() { -// // Binding description -// vertices.bindingDescription = vk.VertexInputBindingDescription(VERTEX_BUFFER_BIND_ID, Vertex.size, VkVertexInputRate.VERTEX) -// -// // Attribute descriptions -// // Describes memory layout and shader positions -// vertices.attributeDescriptions = vk.VertexInputAttributeDescription( -// // Location 0 : Position -// VERTEX_BUFFER_BIND_ID, 0, VkFormat.R32G32B32_SFLOAT, 0, -// // Location 1 : Texture coordinates -// VERTEX_BUFFER_BIND_ID, 1, VkFormat.R32G32_SFLOAT, Vec3.size) -// -// vertices.inputState = vk.PipelineVertexInputStateCreateInfo { -// vertexBindingDescription = vertices.bindingDescription -// vertexAttributeDescriptions = vertices.attributeDescriptions -// } -// } -// -// fun setupDescriptorPool() { -// -// val poolSizes = vk.DescriptorPoolSize( -// VkDescriptorType.UNIFORM_BUFFER, 1, -// VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1) -// -// val descriptorPoolInfo = vk.DescriptorPoolCreateInfo(poolSizes, 2) -// -// descriptorPool = device createDescriptorPool descriptorPoolInfo -// } -// -// fun setupDescriptorSetLayout() { -// -// val setLayoutBindings = vk.DescriptorSetLayoutBinding( -// // Binding 0 : Vertex shader uniform buffer -// VkDescriptorType.UNIFORM_BUFFER, VkShaderStage.VERTEX_BIT.i, 0, -// // Binding 1 : Fragment shader image sampler (texture array) -// VkDescriptorType.COMBINED_IMAGE_SAMPLER, VkShaderStage.FRAGMENT_BIT.i, 1) -// -// val descriptorLayout = vk.DescriptorSetLayoutCreateInfo(setLayoutBindings) -// -// descriptorSetLayout = device createDescriptorSetLayout descriptorLayout -// -// val pipelineLayoutCreateInfo = vk.PipelineLayoutCreateInfo(descriptorSetLayout) -// -// pipelineLayout = device createPipelineLayout pipelineLayoutCreateInfo -// } -// -// fun setupDescriptorSet() { -// -// val allocInfo = vk.DescriptorSetAllocateInfo(descriptorPool, descriptorSetLayout) -// -// descriptorSet = device allocateDescriptorSets allocInfo -// -// // Image descriptor for the texture array -// val textureDescriptor = vk.DescriptorImageInfo(textureArray.sampler, textureArray.view, textureArray.imageLayout) -// -// val writeDescriptorSets = vk.WriteDescriptorSet( -// // Binding 0 : Vertex shader uniform buffer -// descriptorSet, VkDescriptorType.UNIFORM_BUFFER, 0, uniformBufferVS.descriptor, -// // Binding 1 : Fragment shader cubemap sampler -// descriptorSet, VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1, textureDescriptor) -// -// device updateDescriptorSets writeDescriptorSets -// } -// -// fun preparePipelines() { -// -// val inputAssemblyState = vk.PipelineInputAssemblyStateCreateInfo(VkPrimitiveTopology.TRIANGLE_LIST, 0, false) -// -// val rasterizationState = vk.PipelineRasterizationStateCreateInfo(VkPolygonMode.FILL, VkCullMode.NONE.i, VkFrontFace.COUNTER_CLOCKWISE) -// -// val blendAttachmentState = vk.PipelineColorBlendAttachmentState(0xf, false) -// -// val colorBlendState = vk.PipelineColorBlendStateCreateInfo(blendAttachmentState) -// -// val depthStencilState = vk.PipelineDepthStencilStateCreateInfo(true, true, VkCompareOp.LESS_OR_EQUAL) -// -// val viewportState = vk.PipelineViewportStateCreateInfo(1, 1) -// -// val multisampleState = vk.PipelineMultisampleStateCreateInfo(VkSampleCount.`1_BIT`) -// -// val dynamicStateEnables = listOf(VkDynamicState.VIEWPORT, VkDynamicState.SCISSOR) -// val dynamicState = vk.PipelineDynamicStateCreateInfo(dynamicStateEnables) -// -// // Instacing pipeline -// // Load shaders -// val shaderStages = vk.PipelineShaderStageCreateInfo(2).also { -// it[0].loadShader("$assetPath/shaders/texturearray/instancing.vert.spv", VkShaderStage.VERTEX_BIT) -// it[1].loadShader("$assetPath/shaders/texturearray/instancing.frag.spv", VkShaderStage.FRAGMENT_BIT) -// } -// val pipelineCreateInfo = vk.GraphicsPipelineCreateInfo(pipelineLayout, renderPass).also { -// it.vertexInputState = vertices.inputState -// it.inputAssemblyState = inputAssemblyState -// it.rasterizationState = rasterizationState -// it.colorBlendState = colorBlendState -// it.multisampleState = multisampleState -// it.viewportState = viewportState -// it.depthStencilState = depthStencilState -// it.dynamicState = dynamicState -// it.stages = shaderStages -// } -// pipeline = device.createGraphicsPipelines(pipelineCache, pipelineCreateInfo) -// } -// -// fun prepareUniformBuffers() { -// -// for (i in 0 until layerCount) -// uboVS.instance += UboInstanceData() -// -// val uboSize = uboVS.matrices.size + layerCount * UboInstanceData.size -// -// // Vertex shader uniform buffer block -// vulkanDevice.createBuffer( -// VkBufferUsage.UNIFORM_BUFFER_BIT.i, -// VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, -// uniformBufferVS, -// VkDeviceSize(uboSize.L)) -// -// // Array indices and model matrices are fixed -// val offset = -1.5f -// val center = (layerCount * offset) / 2 -// for (i in 0 until layerCount) { -// // Instance model matrix -// uboVS.instance[i].model = glm.translate(Mat4(1f), 0f, i * offset - center, 0f) -// .rotateAssign(60f.rad, 1f, 0f, 0f) -// // Instance texture array index -// uboVS.instance[i].arrayIndex.x = i.f -// } -// -// // Update instanced part of the uniform buffer -// val dataOffset = VkDeviceSize(uboVS.matrices.size.L) -// val dataSize = VkDeviceSize(layerCount * UboInstanceData.size.L) -// uboVS.prepare() -// device.mappingMemory(uniformBufferVS.memory, dataOffset, dataSize, 0) { data -> -// memCopy(uboVS.address + dataOffset, data, dataSize) -// } -// -// // Map persistent -// uniformBufferVS.map() -// -// updateUniformBufferMatrices() -// } -// -// fun updateUniformBufferMatrices() { -// -// // Only updates the uniform buffer block part containing the global matrices -// -// // Projection -// uboVS.matrices.projection = glm.perspective(60f.rad, size.aspect, 0.001f, 256f) -// -// // View -// uboVS.matrices.view = glm.translate(Mat4(1f), 0f, -1f, zoom) -// .rotateAssign(rotation.x.rad, 1f, 0f, 0f) -// .rotateAssign(rotation.y.rad, 0f, 1f, 0f) -// .rotateAssign(rotation.z.rad, 0f, 0f, 1f) -// -// // Only update the matrices part of the uniform buffer -// uboVS.matrices to uniformBufferVS.mapped -// } -// -// fun draw() { -// -// super.prepareFrame() -// -// submitInfo.commandBuffer = drawCmdBuffers[currentBuffer] -// queue submit submitInfo -// -// super.submitFrame() -// } -// -// override fun prepare() { -// -// super.prepare() -// loadTextures() -// setupVertexDescriptions() -// generateQuad() -// prepareUniformBuffers() -// setupDescriptorSetLayout() -// preparePipelines() -// setupDescriptorPool() -// setupDescriptorSet() -// buildCommandBuffers() -// prepared = true -// window.show() -// } -// -// override fun render() { -// if (!prepared) -// return -// draw() -// } -// -// override fun viewChanged() = updateUniformBufferMatrices() -//} \ No newline at end of file +/* +* Vulkan Example - Texture arrays and instanced rendering +* +* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de +* +* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +*/ + +package vulkan.basics + +import gli_.Texture2dArray +import gli_.gli +import glm_.L +import glm_.f +import glm_.func.rad +import glm_.glm +import glm_.mat4x4.Mat4 +import glm_.vec2.Vec2 +import glm_.vec3.Vec3 +import glm_.vec4.Vec4 +import kool.bufferBig +import kool.cap +import kool.stak +import org.lwjgl.system.MemoryUtil.* +import org.lwjgl.vulkan.VkPipelineVertexInputStateCreateInfo +import org.lwjgl.vulkan.VkVertexInputAttributeDescription +import org.lwjgl.vulkan.VkVertexInputBindingDescription +import vkk.* +import vulkan.VERTEX_BUFFER_BIND_ID +import vulkan.assetPath +import vulkan.base.Buffer +import vulkan.base.Texture +import vulkan.base.VulkanExampleBase +import vulkan.base.tools +import java.nio.ByteBuffer + +fun main(args: Array) { + TextureArray().apply { + setupWindow() + initVulkan() + prepare() + renderLoop() + destroy() + } +} + +class TextureArray : VulkanExampleBase() { + + /** Vertex layout for this example */ + object Vertex { + // float pos[3]; +// float uv[2]; + val size = Vec3.size + Vec2.size + } + + // Number of array layers in texture array + // Also used as instance count + var layerCount = 0 + val textureArray = Texture() + + object vertices { + lateinit var inputState: VkPipelineVertexInputStateCreateInfo + lateinit var bindingDescription: VkVertexInputBindingDescription + lateinit var attributeDescriptions: VkVertexInputAttributeDescription.Buffer + } + + val vertexBuffer = Buffer() + val indexBuffer = Buffer() + var indexCount = 0 + + val uniformBufferVS = Buffer() + + class UboInstanceData { + // Model matrix + var model = Mat4() + // Texture array index + // Vec4 due to padding + var arrayIndex = Vec4() + + fun to(bytes: ByteBuffer, offset: Int) { + model.to(bytes, offset) + bytes.putFloat(offset + Mat4.size, arrayIndex.x) + } + + companion object { + val size = Mat4.size + Vec4.size + } + } + + object uboVS { + // Global matrices + object matrices : Bufferizable() { + var projection = Mat4() + var view = Mat4() + } + // Seperate data for each instance + val instance = ArrayList() + + fun prepare() { + buffer = bufferBig(matrices.size + instance.size * UboInstanceData.size) + address = memAddress(buffer) + for (i in instance.indices) + instance[i].to(buffer, matrices.size + UboInstanceData.size * i) + } + + lateinit var buffer: ByteBuffer + var address = NULL + } + + + var pipeline = VkPipeline(NULL) + var pipelineLayout = VkPipelineLayout(NULL) + var descriptorSet= VkDescriptorSet (NULL) + var descriptorSetLayout= VkDescriptorSetLayout (NULL) + + init { + zoom = -15f + rotationSpeed = 0.25f + rotation(-15f, 35f, 0f) + title = "Texture arrays" +// settings.overlay = true + } + + override fun destroy() { + + // Clean up used Vulkan resources + // Note : Inherited destructor cleans up resources stored in base class + + // Clean up texture resources + device.apply { + destroyImageView(textureArray.view) + destroyImage(textureArray.image) + destroySampler(textureArray.sampler) + freeMemory(textureArray.deviceMemory) + + destroyPipeline(pipeline) + + destroyPipelineLayout(pipelineLayout) + destroyDescriptorSetLayout(descriptorSetLayout) + } + vertexBuffer.destroy() + indexBuffer.destroy() + + uniformBufferVS.destroy() + + super.destroy() + } + + fun loadTextureArray(filename: String, format: VkFormat) { + + val tex2DArray = Texture2dArray(gli.load(filename)) + + assert(tex2DArray.notEmpty()) + + textureArray.size(tex2DArray.extent()) + layerCount = tex2DArray.layers() + + // Create a host-visible staging buffer that contains the raw image data + + val bufferCreateInfo = vk.BufferCreateInfo { + size = VkDeviceSize(tex2DArray.size.L) + // This buffer is used as a transfer source for the buffer copy + usage = VkBufferUsage.TRANSFER_SRC_BIT.i + sharingMode = VkSharingMode.EXCLUSIVE + } + val stagingBuffer = device createBuffer bufferCreateInfo + + // Get memory requirements for the staging buffer (alignment, memory type bits) + val memReqs = device getBufferMemoryRequirements stagingBuffer + val memAllocInfo = vk.MemoryAllocateInfo { + allocationSize = memReqs.size + // Get memory type index for a host visible buffer + memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT) + } + val stagingMemory = device allocateMemory memAllocInfo + device.bindBufferMemory(stagingBuffer, stagingMemory) + + // Copy texture data into staging buffer + device.mappingMemory(stagingMemory, VkDeviceSize(0), memReqs.size, 0) { data -> + memCopy(memAddress(tex2DArray.data()), data, tex2DArray.size.L) + } + + // Setup buffer copy regions for array layers + val bufferCopyRegions = vk.BufferImageCopy(layerCount) + var offset = VkDeviceSize(0) + + for (layer in 0 until layerCount) { + + bufferCopyRegions[layer].apply { + imageSubresource.apply { + aspectMask = VkImageAspect.COLOR_BIT.i + mipLevel = 0 + baseArrayLayer = layer + layerCount = 1 + } + imageExtent.apply { + val (w, h) = tex2DArray[layer][0].extent() // TODO BUG + width = w + height = h + depth = 1 + } + bufferOffset = offset + } + + // Increase offset into staging buffer for next level / face + offset += tex2DArray[layer][0].size + } + + // Create optimal tiled target image + val imageCreateInfo = vk.ImageCreateInfo { + imageType = VkImageType.`2D` + this.format = format + mipLevels = 1 + samples = VkSampleCount.`1_BIT` + tiling = VkImageTiling.OPTIMAL + sharingMode = VkSharingMode.EXCLUSIVE + initialLayout = VkImageLayout.UNDEFINED + extent.set(textureArray.size.x, textureArray.size.y, 1) + usage = VkImageUsage.TRANSFER_DST_BIT or VkImageUsage.SAMPLED_BIT + arrayLayers = layerCount + } + textureArray.image = device createImage imageCreateInfo + + device.getImageMemoryRequirements(textureArray.image, memReqs) + + memAllocInfo.allocationSize = memReqs.size + memAllocInfo.memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryProperty.DEVICE_LOCAL_BIT) + + textureArray.deviceMemory = device allocateMemory memAllocInfo + device.bindImageMemory(textureArray.image, textureArray.deviceMemory) + + val copyCmd = super.createCommandBuffer(VkCommandBufferLevel.PRIMARY, true) + + // Image barrier for optimal image (target) + // Set initial layout for all array layers (faces) of the optimal (target) tiled texture + val subresourceRange = vk.ImageSubresourceRange { + aspectMask = VkImageAspect.COLOR_BIT.i + baseMipLevel = 0 + levelCount = 1 + layerCount = this@TextureArray.layerCount + } + tools.setImageLayout( + copyCmd, + textureArray.image, + VkImageLayout.UNDEFINED, + VkImageLayout.TRANSFER_DST_OPTIMAL, + subresourceRange) + + // Copy the cube map faces from the staging buffer to the optimal tiled image + copyCmd.copyBufferToImage( + stagingBuffer, + textureArray.image, + VkImageLayout.TRANSFER_DST_OPTIMAL, + bufferCopyRegions) + + // Change texture image layout to shader read after all faces have been copied + textureArray.imageLayout = VkImageLayout.SHADER_READ_ONLY_OPTIMAL + tools.setImageLayout( + copyCmd, + textureArray.image, + VkImageLayout.TRANSFER_DST_OPTIMAL, + textureArray.imageLayout, + subresourceRange) + + super.flushCommandBuffer(copyCmd, queue, true) + + // Create sampler + val sampler = vk.SamplerCreateInfo { + magFilter = VkFilter.LINEAR + minFilter = VkFilter.LINEAR + mipmapMode = VkSamplerMipmapMode.LINEAR + addressModeU = VkSamplerAddressMode.CLAMP_TO_EDGE + addressModeV = addressModeU + addressModeW = addressModeU + mipLodBias = 0f + maxAnisotropy = 8f + compareOp = VkCompareOp.NEVER + minLod = 0f + maxLod = 0f + borderColor = VkBorderColor.FLOAT_OPAQUE_WHITE + } + textureArray.sampler = device createSampler sampler + + // Create image view + val view = vk.ImageViewCreateInfo { + viewType = VkImageViewType.`2D_ARRAY` + this.format = format + components(VkComponentSwizzle.R, VkComponentSwizzle.G, VkComponentSwizzle.B, VkComponentSwizzle.A) + this.subresourceRange.apply { + set(VkImageAspect.COLOR_BIT.i, 0, 1, 0, 1) + layerCount = this@TextureArray.layerCount // TODO move in ::set + levelCount = 1 + } + image = textureArray.image + } + textureArray.view = device createImageView view + + // Clean up staging resources + device freeMemory stagingMemory + device destroyBuffer stagingBuffer + } + + fun loadTextures() { + // Vulkan core supports three different compressed texture formats + // As the support differs between implemementations we need to check device features and select a proper format and file + val (filename, format) = when { + deviceFeatures.textureCompressionBC -> "texturearray_bc3_unorm.ktx" to VkFormat.BC3_UNORM_BLOCK + deviceFeatures.textureCompressionASTC_LDR -> "texturearray_astc_8x8_unorm.ktx" to VkFormat.ASTC_8x8_UNORM_BLOCK + deviceFeatures.textureCompressionETC2 -> "texturearray_etc2_unorm.ktx" to VkFormat.ETC2_R8G8B8_UNORM_BLOCK + else -> tools.exitFatal("Device does not support any compressed texture format!", ERROR_FEATURE_NOT_PRESENT) + } + loadTextureArray("$assetPath/textures/$filename", format) + } + + override fun buildCommandBuffers() { + + val cmdBufInfo = vk.CommandBufferBeginInfo() + + val clearValues = vk.ClearValue(2).also { + it[0].color(defaultClearColor) + it[1].depthStencil.set(1f, 0) + } + val renderPassBeginInfo = vk.RenderPassBeginInfo { + renderPass = this@TextureArray.renderPass + renderArea.apply { + offset.set(0, 0) + extent.set(size.x, size.y) + } + this.clearValues = clearValues + } + for (i in drawCmdBuffers.indices) { + // Set target frame buffer + renderPassBeginInfo.framebuffer(frameBuffers[i].L) // TODO BUG + + drawCmdBuffers[i].apply { + + begin(cmdBufInfo) + + beginRenderPass(renderPassBeginInfo, VkSubpassContents.INLINE) + + setViewport(size) + setScissor(size) + + bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, pipelineLayout, descriptorSet) + bindPipeline(VkPipelineBindPoint.GRAPHICS, pipeline) + + bindVertexBuffers(VERTEX_BUFFER_BIND_ID, vertexBuffer.buffer) + bindIndexBuffer(indexBuffer.buffer, VkDeviceSize(0), VkIndexType.UINT32) + + drawIndexed(indexCount, layerCount, 0, 0, 0) + + drawUI() + + endRenderPass() + + end() + } + } + } + + fun generateQuad() = stak { + // Setup vertices for a single uv-mapped quad made from two triangles + val vertices = it.floats( + +2.5f, +2.5f, 0f, 1f, 1f, + -2.5f, +2.5f, 0f, 0f, 1f, + -2.5f, -2.5f, 0f, 0f, 0f, + +2.5f, -2.5f, 0f, 1f, 0f) + + // Setup indices + val indices = it.ints(0, 1, 2, 2, 3, 0) + indexCount = indices.cap + + // Create buffers + // For the sake of simplicity we won't stage the vertex data to the gpu memory + // Vertex buffer + vulkanDevice.createBuffer( + VkBufferUsage.VERTEX_BUFFER_BIT.i, + VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, + vertexBuffer, + vertices) + // Index buffer + vulkanDevice.createBuffer( + VkBufferUsage.INDEX_BUFFER_BIT.i, + VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, + indexBuffer, + indices) + } + + fun setupVertexDescriptions() { + // Binding description + vertices.bindingDescription = vk.VertexInputBindingDescription(VERTEX_BUFFER_BIND_ID, Vertex.size, VkVertexInputRate.VERTEX) + + // Attribute descriptions + // Describes memory layout and shader positions + vertices.attributeDescriptions = vk.VertexInputAttributeDescription( + // Location 0 : Position + VERTEX_BUFFER_BIND_ID, 0, VkFormat.R32G32B32_SFLOAT, 0, + // Location 1 : Texture coordinates + VERTEX_BUFFER_BIND_ID, 1, VkFormat.R32G32_SFLOAT, Vec3.size) + + vertices.inputState = vk.PipelineVertexInputStateCreateInfo { + vertexBindingDescription = vertices.bindingDescription + vertexAttributeDescriptions = vertices.attributeDescriptions + } + } + + fun setupDescriptorPool() { + + val poolSizes = vk.DescriptorPoolSize( + VkDescriptorType.UNIFORM_BUFFER, 1, + VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1) + + val descriptorPoolInfo = vk.DescriptorPoolCreateInfo(poolSizes, 2) + + descriptorPool = device createDescriptorPool descriptorPoolInfo + } + + fun setupDescriptorSetLayout() { + + val setLayoutBindings = vk.DescriptorSetLayoutBinding( + // Binding 0 : Vertex shader uniform buffer + VkDescriptorType.UNIFORM_BUFFER, VkShaderStage.VERTEX_BIT.i, 0, + // Binding 1 : Fragment shader image sampler (texture array) + VkDescriptorType.COMBINED_IMAGE_SAMPLER, VkShaderStage.FRAGMENT_BIT.i, 1) + + val descriptorLayout = vk.DescriptorSetLayoutCreateInfo(setLayoutBindings) + + descriptorSetLayout = device createDescriptorSetLayout descriptorLayout + + val pipelineLayoutCreateInfo = vk.PipelineLayoutCreateInfo(descriptorSetLayout) + + pipelineLayout = device createPipelineLayout pipelineLayoutCreateInfo + } + + fun setupDescriptorSet() { + + val allocInfo = vk.DescriptorSetAllocateInfo(descriptorPool, descriptorSetLayout) + + descriptorSet = device allocateDescriptorSets allocInfo + + // Image descriptor for the texture array + val textureDescriptor = vk.DescriptorImageInfo(textureArray.sampler, textureArray.view, textureArray.imageLayout) + + val writeDescriptorSets = vk.WriteDescriptorSet( + // Binding 0 : Vertex shader uniform buffer + descriptorSet, VkDescriptorType.UNIFORM_BUFFER, 0, uniformBufferVS.descriptor, + // Binding 1 : Fragment shader cubemap sampler + descriptorSet, VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1, textureDescriptor) + + device updateDescriptorSets writeDescriptorSets + } + + fun preparePipelines() { + + val inputAssemblyState = vk.PipelineInputAssemblyStateCreateInfo(VkPrimitiveTopology.TRIANGLE_LIST, 0, false) + + val rasterizationState = vk.PipelineRasterizationStateCreateInfo(VkPolygonMode.FILL, VkCullMode.NONE.i, VkFrontFace.COUNTER_CLOCKWISE) + + val blendAttachmentState = vk.PipelineColorBlendAttachmentState(0xf, false) + + val colorBlendState = vk.PipelineColorBlendStateCreateInfo(blendAttachmentState) + + val depthStencilState = vk.PipelineDepthStencilStateCreateInfo(true, true, VkCompareOp.LESS_OR_EQUAL) + + val viewportState = vk.PipelineViewportStateCreateInfo(1, 1) + + val multisampleState = vk.PipelineMultisampleStateCreateInfo(VkSampleCount.`1_BIT`) + + val dynamicStateEnables = listOf(VkDynamicState.VIEWPORT, VkDynamicState.SCISSOR) + val dynamicState = vk.PipelineDynamicStateCreateInfo(dynamicStateEnables) + + // Instacing pipeline + // Load shaders + val shaderStages = vk.PipelineShaderStageCreateInfo(2).also { + it[0].loadShader("$assetPath/shaders/texturearray/instancing.vert.spv", VkShaderStage.VERTEX_BIT) + it[1].loadShader("$assetPath/shaders/texturearray/instancing.frag.spv", VkShaderStage.FRAGMENT_BIT) + } + val pipelineCreateInfo = vk.GraphicsPipelineCreateInfo(pipelineLayout, renderPass).also { + it.vertexInputState = vertices.inputState + it.inputAssemblyState = inputAssemblyState + it.rasterizationState = rasterizationState + it.colorBlendState = colorBlendState + it.multisampleState = multisampleState + it.viewportState = viewportState + it.depthStencilState = depthStencilState + it.dynamicState = dynamicState + it.stages = shaderStages + } + pipeline = device.createGraphicsPipelines(pipelineCache, pipelineCreateInfo) + } + + fun prepareUniformBuffers() { + + for (i in 0 until layerCount) + uboVS.instance += UboInstanceData() + + val uboSize = uboVS.matrices.size + layerCount * UboInstanceData.size + + // Vertex shader uniform buffer block + vulkanDevice.createBuffer( + VkBufferUsage.UNIFORM_BUFFER_BIT.i, + VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, + uniformBufferVS, + VkDeviceSize(uboSize.L)) + + // Array indices and model matrices are fixed + val offset = -1.5f + val center = (layerCount * offset) / 2 + for (i in 0 until layerCount) { + // Instance model matrix + uboVS.instance[i].model = glm.translate(Mat4(1f), 0f, i * offset - center, 0f) + .rotateAssign(60f.rad, 1f, 0f, 0f) + // Instance texture array index + uboVS.instance[i].arrayIndex.x = i.f + } + + // Update instanced part of the uniform buffer + val dataOffset = VkDeviceSize(uboVS.matrices.size.L) + val dataSize = VkDeviceSize(layerCount * UboInstanceData.size.L) + uboVS.prepare() + device.mappingMemory(uniformBufferVS.memory, dataOffset, dataSize, 0) { data -> + memCopy(uboVS.address + dataOffset, data, dataSize) + } + + // Map persistent + uniformBufferVS.map() + + updateUniformBufferMatrices() + } + + fun updateUniformBufferMatrices() { + + // Only updates the uniform buffer block part containing the global matrices + + // Projection + uboVS.matrices.projection = glm.perspective(60f.rad, size.aspect, 0.001f, 256f) + + // View + uboVS.matrices.view = glm.translate(Mat4(1f), 0f, -1f, zoom) + .rotateAssign(rotation.x.rad, 1f, 0f, 0f) + .rotateAssign(rotation.y.rad, 0f, 1f, 0f) + .rotateAssign(rotation.z.rad, 0f, 0f, 1f) + + // Only update the matrices part of the uniform buffer + uboVS.matrices to uniformBufferVS.mapped + } + + fun draw() { + + super.prepareFrame() + + submitInfo.commandBuffer = drawCmdBuffers[currentBuffer] + queue submit submitInfo + + super.submitFrame() + } + + override fun prepare() { + + super.prepare() + loadTextures() + setupVertexDescriptions() + generateQuad() + prepareUniformBuffers() + setupDescriptorSetLayout() + preparePipelines() + setupDescriptorPool() + setupDescriptorSet() + buildCommandBuffers() + prepared = true + window.show() + } + + override fun render() { + if (!prepared) + return + draw() + } + + override fun viewChanged() = updateUniformBufferMatrices() +} \ No newline at end of file diff --git a/src/main/kotlin/vulkan/basics/10 Texture 3d.kt b/src/main/kotlin/vulkan/basics/10 Texture 3d.kt index 7389b94..0f7b1c4 100644 --- a/src/main/kotlin/vulkan/basics/10 Texture 3d.kt +++ b/src/main/kotlin/vulkan/basics/10 Texture 3d.kt @@ -1,709 +1,709 @@ -///* -//* Vulkan Example - 3D texture loading (and generation using perlin noise) example -//* -//* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de -//* -//* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -//*/ -// -//package vulkan.basics -// -//import glm_.L -//import glm_.b -//import glm_.func.rad -//import glm_.glm -//import glm_.mat4x4.Mat4 -//import glm_.set -//import glm_.vec2.Vec2 -//import glm_.vec3.Vec3 -//import glm_.vec3.Vec3i -//import glm_.vec4.Vec4 -//import kool.bufferBig -//import kool.cap -//import kool.free -//import kool.stak -//import org.lwjgl.system.MemoryUtil.* -//import org.lwjgl.vulkan.VkDescriptorImageInfo -//import org.lwjgl.vulkan.VkPipelineVertexInputStateCreateInfo -//import org.lwjgl.vulkan.VkVertexInputAttributeDescription -//import org.lwjgl.vulkan.VkVertexInputBindingDescription -//import uno.kotlin.buffers.indices -//import vkk.* -//import vulkan.VERTEX_BUFFER_BIND_ID -//import vulkan.assetPath -//import vulkan.base.Buffer -//import vulkan.base.Model -//import vulkan.base.VulkanExampleBase -//import vulkan.base.tools -//import java.util.concurrent.ThreadLocalRandom -//import java.util.stream.IntStream -//import kotlin.system.measureTimeMillis -// -// -//fun main(args: Array) { -// Texture3d().apply { -// setupWindow() -// initVulkan() -// prepare() -// renderLoop() -// destroy() -// } -//} -// -//private class Texture3d : VulkanExampleBase() { -// -// /** Vertex layout for this example */ -// object Vertex : Bufferizable() { -// lateinit var pos: Vec3 -// lateinit var uv: Vec2 -// @Order(2) -// lateinit var normal: Vec3 -// } -// -// /** Fractal noise generator based on perlin noise above */ -// class FractalNoise { -// -// val octaves = 6 -// var frequency = 0f -// var amplitude = 0f -// var persistence = 0.5f -// -// fun noise(v: Vec3): Float { -// var sum = 0f -// frequency = 1f -// amplitude = 1f -// var max = 0f -// for (i in 0 until octaves) { -// sum += glm.perlin(v * frequency) * amplitude -// max += amplitude -// amplitude *= persistence -// frequency *= 2f -// } -// -// sum /= max -// return (sum + 1f) / 2f -// } -// } -// -// /** Contains all Vulkan objects that are required to store and use a 3D texture */ -// object texture { -// var sampler = VkSampler(NULL) -// var image = VkImage(NULL) -// var imageLayout = VkImageLayout.UNDEFINED -// var deviceMemory = VkDeviceMemory(NULL) -// var view = VkImageView(NULL) -// lateinit var descriptor: VkDescriptorImageInfo -// var format = VkFormat.UNDEFINED -// val extent = Vec3i() -// var mipLevels = 0 -// } -// -// var regenerateNoise = true -// -// object models { -// val cube = Model() -// } -// -// object vertices { -// lateinit var inputState: VkPipelineVertexInputStateCreateInfo -// lateinit var inputBinding: VkVertexInputBindingDescription -// lateinit var inputAttributes: VkVertexInputAttributeDescription.Buffer -// } -// -// val vertexBuffer = Buffer() -// val indexBuffer = Buffer() -// var indexCount = 0 -// -// val uniformBufferVS = Buffer() -// -// object uboVS : Bufferizable() { -// lateinit var projection: Mat4 -// @Order(1) -// lateinit var model: Mat4 -// lateinit var viewPos: Vec4 -// @Order(3) -// var depth = 0f -// } -// -// object pipelines { -// var solid = VkPipeline (NULL) -// } -// -// var pipelineLayout= VkPipelineLayout (NULL) -// var descriptorSet= VkDescriptorSet (NULL) -// var descriptorSetLayout= VkDescriptorSetLayout (NULL) -// -// init { -// zoom = -2.5f -// rotation(0f, 15f, 0f) -// title = "3D textures" -//// settings.overlay = true -//// srand((unsigned int) time (NULL)) -// } -// -// override fun destroy() { -// // Clean up used Vulkan resources -// // Note : Inherited destructor cleans up resources stored in base class -// -// destroyTextureImage() -// -// device.apply { -// destroyPipeline(pipelines.solid) -// -// destroyPipelineLayout(pipelineLayout) -// destroyDescriptorSetLayout(descriptorSetLayout) -// } -// vertexBuffer.destroy() -// indexBuffer.destroy() -// uniformBufferVS.destroy() -// -// super.destroy() -// } -// -// /** Prepare all Vulkan resources for the 3D texture (including descriptors) -// * Does not fill the texture with data */ -// fun prepareNoiseTexture(width: Int, height: Int, depth: Int) { -// // A 3D texture is described as width x height x depth -// texture.extent.put(width, height, depth) // TODO glm -// texture.mipLevels = 1 -// texture.format = VkFormat.R8_UNORM -// -// // Format support check -// // 3D texture support in Vulkan is mandatory (in contrast to OpenGL) so no need to check if it's supported -// val formatProperties = physicalDevice getFormatProperties texture.format -// // Check if format supports transfer -// if (formatProperties.optimalTilingFeatures hasnt VkFormatFeature.TRANSFER_DST_BIT) { -// System.err.println("Error: Device does not support flag TRANSFER_DST for selected texture format!") -// return -// } -// // Check if GPU supports requested 3D texture dimensions -// val maxImageDimension3D = vulkanDevice.properties.limits.maxImageDimension3D -// if (width > maxImageDimension3D || height > maxImageDimension3D || depth > maxImageDimension3D) { -// System.out.println("Error: Requested texture dimensions is greater than supported 3D texture dimension!") -// return -// } -// -// // Create optimal tiled target image -// val imageCreateInfo = vk.ImageCreateInfo { -// imageType = VkImageType.`3D` -// format = texture.format -// mipLevels = texture.mipLevels -// arrayLayers = 1 -// samples = VkSampleCount.`1_BIT` -// tiling = VkImageTiling.OPTIMAL -// sharingMode = VkSharingMode.EXCLUSIVE -// extent(texture.extent) -// // Set initial layout of the image to undefined -// initialLayout = VkImageLayout.UNDEFINED -// usage = VkImageUsage.TRANSFER_DST_BIT or VkImageUsage.SAMPLED_BIT -// } -// texture.image = device createImage imageCreateInfo -// -// // Device local memory to back up image -// val memReqs = device getImageMemoryRequirements texture.image -// val memAllocInfo = vk.MemoryAllocateInfo { -// allocationSize = memReqs.size -// memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryProperty.DEVICE_LOCAL_BIT) -// } -// texture.deviceMemory = device allocateMemory memAllocInfo -// device.bindImageMemory(texture.image, texture.deviceMemory, VkDeviceSize(0)) -// -// // Create sampler -// val sampler = vk.SamplerCreateInfo { -// magFilter = VkFilter.LINEAR -// minFilter = VkFilter.LINEAR -// mipmapMode = VkSamplerMipmapMode.LINEAR -// addressModeU = VkSamplerAddressMode.CLAMP_TO_EDGE -// addressModeV = VkSamplerAddressMode.CLAMP_TO_EDGE -// addressModeW = VkSamplerAddressMode.CLAMP_TO_EDGE -// mipLodBias = 0f -// compareOp = VkCompareOp.NEVER -// minLod = 0f -// maxLod = 0f -// maxAnisotropy = 1f -// anisotropyEnable = false -// borderColor = VkBorderColor.FLOAT_OPAQUE_WHITE -// } -// texture.sampler = device createSampler sampler -// -// // Create image view -// val view = vk.ImageViewCreateInfo { -// image = texture.image -// viewType = VkImageViewType.`3D` -// format = texture.format -// components(VkComponentSwizzle.R, VkComponentSwizzle.G, VkComponentSwizzle.B, VkComponentSwizzle.A) -// subresourceRange.apply { -// aspectMask = VkImageAspect.COLOR_BIT.i -// baseMipLevel = 0 -// baseArrayLayer = 0 -// layerCount = 1 -// levelCount = 1 -// } -// } -// texture.view = device createImageView view -// -// // Fill image descriptor image info to be used descriptor set setup -// texture.descriptor = vk.DescriptorImageInfo { -// imageLayout = VkImageLayout.SHADER_READ_ONLY_OPTIMAL -// imageView = texture.view -// this.sampler = texture.sampler -// } -// } -// -// /** Generate randomized noise and upload it to the 3D texture using staging */ -// fun updateNoiseTexture() { -// -// val ext = texture.extent -// val texMemSize = VkDeviceSize(ext.x * ext.y * ext.z.L) -// -// val data = bufferBig(texMemSize) -// val adr = memAddress(data) -// for (i in data.indices) -// memPutByte(adr + i, i.b) -// -// // Generate perlin based noise -// println("Generating ${ext.x} x ${ext.y} x ${ext.z} noise texture...") -// -//// auto tStart = std ::chrono::high_resolution_clock::now() -// -// /* Maximum value that can be returned by the rand function: -// define RAND_MAX 0x7fff -// 0111 1111 1111 1111 -// */ -// fun rand() = ThreadLocalRandom.current().nextInt() ushr 1 -// -// val time = measureTimeMillis { -// -// val FRACTAL = true -// val noiseScale = rand() % 10 + 4f -// -// val parallel = true -// -// if (!parallel) -// for (z in 0 until ext.z) { -// println(z) -// for (y in 0 until ext.y) -// for (x in 0 until ext.x) { -// println("x $x, y $y, z $z") -// val v = Vec3(x, y, z) / ext -// var n = when { -// FRACTAL -> FractalNoise().noise(v * noiseScale) -// else -> 20f * glm.perlin(v) -// } -// n -= glm.floor(n) -// -// data[x + y * ext.x + z * ext.x * ext.y] = glm.floor(n * 255).b -// } -// } -// else { -//// runBlocking { +/* +* Vulkan Example - 3D texture loading (and generation using perlin noise) example +* +* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de +* +* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +*/ + +package vulkan.basics + +import glm_.L +import glm_.b +import glm_.func.rad +import glm_.glm +import glm_.mat4x4.Mat4 +import glm_.set +import glm_.vec2.Vec2 +import glm_.vec3.Vec3 +import glm_.vec3.Vec3i +import glm_.vec4.Vec4 +import kool.bufferBig +import kool.cap +import kool.free +import kool.stak +import org.lwjgl.system.MemoryUtil.* +import org.lwjgl.vulkan.VkDescriptorImageInfo +import org.lwjgl.vulkan.VkPipelineVertexInputStateCreateInfo +import org.lwjgl.vulkan.VkVertexInputAttributeDescription +import org.lwjgl.vulkan.VkVertexInputBindingDescription +import uno.kotlin.buffers.indices +import vkk.* +import vulkan.VERTEX_BUFFER_BIND_ID +import vulkan.assetPath +import vulkan.base.Buffer +import vulkan.base.Model +import vulkan.base.VulkanExampleBase +import vulkan.base.tools +import java.util.concurrent.ThreadLocalRandom +import java.util.stream.IntStream +import kotlin.system.measureTimeMillis + + +fun main(args: Array) { + Texture3d().apply { + setupWindow() + initVulkan() + prepare() + renderLoop() + destroy() + } +} + +private class Texture3d : VulkanExampleBase() { + + /** Vertex layout for this example */ + object Vertex : Bufferizable() { + lateinit var pos: Vec3 + lateinit var uv: Vec2 + @Order(2) + lateinit var normal: Vec3 + } + + /** Fractal noise generator based on perlin noise above */ + class FractalNoise { + + val octaves = 6 + var frequency = 0f + var amplitude = 0f + var persistence = 0.5f + + fun noise(v: Vec3): Float { + var sum = 0f + frequency = 1f + amplitude = 1f + var max = 0f + for (i in 0 until octaves) { + sum += glm.perlin(v * frequency) * amplitude + max += amplitude + amplitude *= persistence + frequency *= 2f + } + + sum /= max + return (sum + 1f) / 2f + } + } + + /** Contains all Vulkan objects that are required to store and use a 3D texture */ + object texture { + var sampler = VkSampler(NULL) + var image = VkImage(NULL) + var imageLayout = VkImageLayout.UNDEFINED + var deviceMemory = VkDeviceMemory(NULL) + var view = VkImageView(NULL) + lateinit var descriptor: VkDescriptorImageInfo + var format = VkFormat.UNDEFINED + val extent = Vec3i() + var mipLevels = 0 + } + + var regenerateNoise = true + + object models { + val cube = Model() + } + + object vertices { + lateinit var inputState: VkPipelineVertexInputStateCreateInfo + lateinit var inputBinding: VkVertexInputBindingDescription + lateinit var inputAttributes: VkVertexInputAttributeDescription.Buffer + } + + val vertexBuffer = Buffer() + val indexBuffer = Buffer() + var indexCount = 0 + + val uniformBufferVS = Buffer() + + object uboVS : Bufferizable() { + lateinit var projection: Mat4 + @Order(1) + lateinit var model: Mat4 + lateinit var viewPos: Vec4 + @Order(3) + var depth = 0f + } + + object pipelines { + var solid = VkPipeline (NULL) + } + + var pipelineLayout= VkPipelineLayout (NULL) + var descriptorSet= VkDescriptorSet (NULL) + var descriptorSetLayout= VkDescriptorSetLayout (NULL) + + init { + zoom = -2.5f + rotation(0f, 15f, 0f) + title = "3D textures" +// settings.overlay = true +// srand((unsigned int) time (NULL)) + } + + override fun destroy() { + // Clean up used Vulkan resources + // Note : Inherited destructor cleans up resources stored in base class + + destroyTextureImage() + + device.apply { + destroyPipeline(pipelines.solid) + + destroyPipelineLayout(pipelineLayout) + destroyDescriptorSetLayout(descriptorSetLayout) + } + vertexBuffer.destroy() + indexBuffer.destroy() + uniformBufferVS.destroy() + + super.destroy() + } + + /** Prepare all Vulkan resources for the 3D texture (including descriptors) + * Does not fill the texture with data */ + fun prepareNoiseTexture(width: Int, height: Int, depth: Int) { + // A 3D texture is described as width x height x depth + texture.extent.put(width, height, depth) // TODO glm + texture.mipLevels = 1 + texture.format = VkFormat.R8_UNORM + + // Format support check + // 3D texture support in Vulkan is mandatory (in contrast to OpenGL) so no need to check if it's supported + val formatProperties = physicalDevice getFormatProperties texture.format + // Check if format supports transfer + if (formatProperties.optimalTilingFeatures hasnt VkFormatFeature.TRANSFER_DST_BIT) { + System.err.println("Error: Device does not support flag TRANSFER_DST for selected texture format!") + return + } + // Check if GPU supports requested 3D texture dimensions + val maxImageDimension3D = vulkanDevice.properties.limits.maxImageDimension3D + if (width > maxImageDimension3D || height > maxImageDimension3D || depth > maxImageDimension3D) { + System.out.println("Error: Requested texture dimensions is greater than supported 3D texture dimension!") + return + } + + // Create optimal tiled target image + val imageCreateInfo = vk.ImageCreateInfo { + imageType = VkImageType.`3D` + format = texture.format + mipLevels = texture.mipLevels + arrayLayers = 1 + samples = VkSampleCount.`1_BIT` + tiling = VkImageTiling.OPTIMAL + sharingMode = VkSharingMode.EXCLUSIVE + extent(texture.extent) + // Set initial layout of the image to undefined + initialLayout = VkImageLayout.UNDEFINED + usage = VkImageUsage.TRANSFER_DST_BIT or VkImageUsage.SAMPLED_BIT + } + texture.image = device createImage imageCreateInfo + + // Device local memory to back up image + val memReqs = device getImageMemoryRequirements texture.image + val memAllocInfo = vk.MemoryAllocateInfo { + allocationSize = memReqs.size + memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryProperty.DEVICE_LOCAL_BIT) + } + texture.deviceMemory = device allocateMemory memAllocInfo + device.bindImageMemory(texture.image, texture.deviceMemory, VkDeviceSize(0)) + + // Create sampler + val sampler = vk.SamplerCreateInfo { + magFilter = VkFilter.LINEAR + minFilter = VkFilter.LINEAR + mipmapMode = VkSamplerMipmapMode.LINEAR + addressModeU = VkSamplerAddressMode.CLAMP_TO_EDGE + addressModeV = VkSamplerAddressMode.CLAMP_TO_EDGE + addressModeW = VkSamplerAddressMode.CLAMP_TO_EDGE + mipLodBias = 0f + compareOp = VkCompareOp.NEVER + minLod = 0f + maxLod = 0f + maxAnisotropy = 1f + anisotropyEnable = false + borderColor = VkBorderColor.FLOAT_OPAQUE_WHITE + } + texture.sampler = device createSampler sampler + + // Create image view + val view = vk.ImageViewCreateInfo { + image = texture.image + viewType = VkImageViewType.`3D` + format = texture.format + components(VkComponentSwizzle.R, VkComponentSwizzle.G, VkComponentSwizzle.B, VkComponentSwizzle.A) + subresourceRange.apply { + aspectMask = VkImageAspect.COLOR_BIT.i + baseMipLevel = 0 + baseArrayLayer = 0 + layerCount = 1 + levelCount = 1 + } + } + texture.view = device createImageView view + + // Fill image descriptor image info to be used descriptor set setup + texture.descriptor = vk.DescriptorImageInfo { + imageLayout = VkImageLayout.SHADER_READ_ONLY_OPTIMAL + imageView = texture.view + this.sampler = texture.sampler + } + } + + /** Generate randomized noise and upload it to the 3D texture using staging */ + fun updateNoiseTexture() { + + val ext = texture.extent + val texMemSize = VkDeviceSize(ext.x * ext.y * ext.z.L) + + val data = bufferBig(texMemSize) + val adr = memAddress(data) + for (i in data.indices) + memPutByte(adr + i, i.b) + + // Generate perlin based noise + println("Generating ${ext.x} x ${ext.y} x ${ext.z} noise texture...") + +// auto tStart = std ::chrono::high_resolution_clock::now() + + /* Maximum value that can be returned by the rand function: + define RAND_MAX 0x7fff + 0111 1111 1111 1111 + */ + fun rand() = ThreadLocalRandom.current().nextInt() ushr 1 + + val time = measureTimeMillis { + + val FRACTAL = true + val noiseScale = rand() % 10 + 4f + + val parallel = true + + if (!parallel) + for (z in 0 until ext.z) { + println(z) + for (y in 0 until ext.y) + for (x in 0 until ext.x) { + println("x $x, y $y, z $z") + val v = Vec3(x, y, z) / ext + var n = when { + FRACTAL -> FractalNoise().noise(v * noiseScale) + else -> 20f * glm.perlin(v) + } + n -= glm.floor(n) + + data[x + y * ext.x + z * ext.x * ext.y] = glm.floor(n * 255).b + } + } + else { +// runBlocking { +// for (z in 0 until 1) { +// println(z) //// for (z in 0 until 1) { -//// println(z) -////// for (z in 0 until 1) { -//// for (y in 0 until 1) -//// for (x in 0 until 100) { -//// launch { -//// val v = Vec3(x, y, z) / texture.extent -//// var n = when { -//// FRACTAL -> FractalNoise().noise(v * noiseScale) -//// else -> 20f * glm.perlin(v) -//// } -//// n -= glm.floor(n) -//// -//// val offset = x + y * texture.extent.x + z * texture.extent.x * texture.extent.y -//// println("$adr, "+offset) -//// memPutByte(adr + offset, glm.floor(n * 255).b) -////// data[x + y * texture.extent.x + z * texture.extent.x * texture.extent.y] = glm.floor(n * 255).b -//// } -//// } -//// } -//// } -// IntStream -// .range(0, ext.x * ext.y * ext.z) -// .parallel() -// .forEach { -// val z = it / (ext.x * ext.y) -// val remainder = it - z * ext.x * ext.y -// val y = remainder / ext.x -// val x = remainder % ext.x -// val v = Vec3(x, y, z) / ext -// var n = when { -// FRACTAL -> FractalNoise().noise(v * noiseScale) -// else -> 20f * glm.perlin(v) +// for (y in 0 until 1) +// for (x in 0 until 100) { +// launch { +// val v = Vec3(x, y, z) / texture.extent +// var n = when { +// FRACTAL -> FractalNoise().noise(v * noiseScale) +// else -> 20f * glm.perlin(v) +// } +// n -= glm.floor(n) +// +// val offset = x + y * texture.extent.x + z * texture.extent.x * texture.extent.y +// println("$adr, "+offset) +// memPutByte(adr + offset, glm.floor(n * 255).b) +//// data[x + y * texture.extent.x + z * texture.extent.x * texture.extent.y] = glm.floor(n * 255).b +// } // } -// n -= glm.floor(n) -// -// data[x + y * ext.x + z * ext.x * ext.y] = glm.floor(n * 255).b -// } -//// -//// val channel = Channel() -//// -//// val processingPool = newThreadPool -//// -//// launch(processingPool) { for (request in channel) doProcessing(it) } -//// -//// -//// -//// for ... -//// -//// for ... -//// -//// channel.sendBlocking(ProcessingRequest(...)) -// } -// } -// println("Done in ${time}ms") -// -// // Create a host-visible staging buffer that contains the raw image data -// -// // Buffer object -// val bufferCreateInfo = vk.BufferCreateInfo { -// size = texMemSize -// usage = VkBufferUsage.TRANSFER_SRC_BIT.i -// sharingMode = VkSharingMode.EXCLUSIVE -// } -// val stagingBuffer: VkBuffer = device createBuffer bufferCreateInfo -// -// // Allocate host visible memory for data upload -// val memReqs = device getBufferMemoryRequirements stagingBuffer -// val memAllocInfo = vk.MemoryAllocateInfo { -// allocationSize = memReqs.size -// memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT) -// } -// val stagingMemory = device allocateMemory memAllocInfo -// device.bindBufferMemory(stagingBuffer, stagingMemory) -// -// // Copy texture data into staging buffer -// device.mappingMemory(stagingMemory, VkDeviceSize(0), memReqs.size) { mapped -> -// memCopy(memAddress(data), mapped, texMemSize) -// } -// -// val copyCmd = super.createCommandBuffer(VkCommandBufferLevel.PRIMARY, true) -// -// // Image barrier for optimal image -// -// // The sub resource range describes the regions of the image we will be transition -// val subresourceRange = vk.ImageSubresourceRange { -// aspectMask = VkImageAspect.COLOR_BIT.i -// baseMipLevel = 0 -// levelCount = 1 -// layerCount = 1 -// } -// // Optimal image will be used as destination for the copy, so we must transfer from our -// // initial undefined image layout to the transfer destination layout -// tools.setImageLayout( -// copyCmd, -// texture.image, -// VkImageLayout.UNDEFINED, -// VkImageLayout.TRANSFER_DST_OPTIMAL, -// subresourceRange) -// -// // Copy 3D noise data to texture -// -// // Setup buffer copy regions -// val bufferCopyRegion = vk.BufferImageCopy { -// imageSubresource.apply { -// aspectMask = VkImageAspect.COLOR_BIT.i -// mipLevel = 0 -// baseArrayLayer = 0 -// layerCount = 1 -// } -// imageExtent(texture.extent) -// } -// copyCmd.copyBufferToImage( -// stagingBuffer, -// texture.image, -// VkImageLayout.TRANSFER_DST_OPTIMAL, -// bufferCopyRegion) -// -// // Change texture image layout to shader read after all mip levels have been copied -// texture.imageLayout = VkImageLayout.SHADER_READ_ONLY_OPTIMAL -// tools.setImageLayout( -// copyCmd, -// texture.image, -// VkImageLayout.TRANSFER_DST_OPTIMAL, -// texture.imageLayout, -// subresourceRange) -// -// super.flushCommandBuffer(copyCmd, queue, true) -// -// // Clean up staging resources -// data.free() -// device freeMemory stagingMemory -// device destroyBuffer stagingBuffer -// regenerateNoise = false -// } -// -// /** Free all Vulkan resources used a texture object */ -// fun destroyTextureImage() { -// device.apply { -// if (texture.view.isValid()) -// destroyImageView(texture.view) -// if (texture.image.isValid()) -// destroyImage(texture.image) -// if (texture.sampler.isValid()) -// destroySampler(texture.sampler) -// if (texture.deviceMemory.isValid()) -// freeMemory(texture.deviceMemory) -// } -// } -// -// override fun buildCommandBuffers() { -// -// val cmdBufInfo = vk.CommandBufferBeginInfo() -// -// val clearValues = vk.ClearValue(2).also { -// it[0].color(defaultClearColor) -// it[1].depthStencil(1f, 0) -// } -// val renderPassBeginInfo = vk.RenderPassBeginInfo { -// renderPass = this@Texture3d.renderPass -// renderArea.apply { -// offset(0) -// extent(size) -// } -// this.clearValues = clearValues -// } -// for (i in drawCmdBuffers.indices) { -// // Set target frame buffer -// renderPassBeginInfo.framebuffer(frameBuffers[i].L) -// -// drawCmdBuffers[i].apply { -// -// begin(cmdBufInfo) -// -// beginRenderPass(renderPassBeginInfo, VkSubpassContents.INLINE) -// -// setViewport(size) -// -// setScissor(size) -// -// bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, pipelineLayout, descriptorSet) -// bindPipeline(VkPipelineBindPoint.GRAPHICS, pipelines.solid) -// -// bindVertexBuffers(VERTEX_BUFFER_BIND_ID, vertexBuffer.buffer) -// bindIndexBuffer(indexBuffer.buffer, VkDeviceSize(0), VkIndexType.UINT32) -// drawIndexed(indexCount, 1, 0, 0, 0) -// -// drawUI() -// -// endRenderPass() -// -// end() -// } -// } -// } -// -// fun draw() { -// -// super.prepareFrame() -// -// // Command buffer to be sumitted to the queue -// submitInfo.commandBuffer = drawCmdBuffers[currentBuffer] -// -// // Submit to queue -// queue submit submitInfo -// -// super.submitFrame() -// } -// -// fun generateQuad() = stak { -// // Setup vertices for a single uv-mapped quad made from two triangles -// val vertices = it.floats( -// +1f, +1f, 0f, 1f, 1f, 0f, 0f, 1f, -// -1f, +1f, 0f, 0f, 1f, 0f, 0f, 1f, -// -1f, -1f, 0f, 0f, 0f, 0f, 0f, 1f, -// +1f, -1f, 0f, 1f, 0f, 0f, 0f, 1f) -// -// // Setup indices -// val indices = it.ints(0, 1, 2, 2, 3, 0) -// indexCount = indices.cap -// -// // Create buffers -// // For the sake of simplicity we won't stage the vertex data to the gpu memory -// // Vertex buffer -// vulkanDevice.createBuffer( -// VkBufferUsage.VERTEX_BUFFER_BIT.i, -// VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, -// vertexBuffer, -// vertices) -// // Index buffer -// vulkanDevice.createBuffer( -// VkBufferUsage.INDEX_BUFFER_BIT.i, -// VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, -// indexBuffer, -// indices) -// } -// -// fun setupVertexDescriptions() { -// // Binding description -// vertices.inputBinding = vk.VertexInputBindingDescription(VERTEX_BUFFER_BIND_ID, Vertex.size, VkVertexInputRate.VERTEX) -// -// // Attribute descriptions -// // Describes memory layout and shader positions -// vertices.inputAttributes = vk.VertexInputAttributeDescription( -// // Location 0 : Position -// VERTEX_BUFFER_BIND_ID, 0, VkFormat.R32G32B32_SFLOAT, Vertex.offsetOf("pos"), -// // Location 1 : Texture coordinates -// VERTEX_BUFFER_BIND_ID, 1, VkFormat.R32G32_SFLOAT, Vertex.offsetOf("uv"), -// // Location 1 : Vertex normal -// VERTEX_BUFFER_BIND_ID, 2, VkFormat.R32G32B32_SFLOAT, Vertex.offsetOf("normal")) -// -// vertices.inputState = vk.PipelineVertexInputStateCreateInfo { -// vertexBindingDescription = vertices.inputBinding -// vertexAttributeDescriptions = vertices.inputAttributes -// } -// } -// -// fun setupDescriptorPool() { -// // Example uses one ubo and one image sampler -// val poolSizes = vk.DescriptorPoolSize( -// VkDescriptorType.UNIFORM_BUFFER, 1, -// VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1) -// -// val descriptorPoolInfo = vk.DescriptorPoolCreateInfo(poolSizes, 2) -// -// descriptorPool = device createDescriptorPool descriptorPoolInfo -// } -// -// fun setupDescriptorSetLayout() { -// -// val setLayoutBindings = vk.DescriptorSetLayoutBinding( -// // Binding 0 : Vertex shader uniform buffer -// VkDescriptorType.UNIFORM_BUFFER, VkShaderStage.VERTEX_BIT.i, 0, -// // Binding 1 : Fragment shader image sampler -// VkDescriptorType.COMBINED_IMAGE_SAMPLER, VkShaderStage.FRAGMENT_BIT.i, 1) -// -// val descriptorLayout = vk.DescriptorSetLayoutCreateInfo(setLayoutBindings) -// -// descriptorSetLayout = device createDescriptorSetLayout descriptorLayout -// -// val pipelineLayoutCreateInfo = vk.PipelineLayoutCreateInfo(descriptorSetLayout) -// -// pipelineLayout = device createPipelineLayout pipelineLayoutCreateInfo -// } -// -// fun setupDescriptorSet() { -// -// val allocInfo = vk.DescriptorSetAllocateInfo(descriptorPool, descriptorSetLayout) -// -// descriptorSet = device allocateDescriptorSets allocInfo -// -// val writeDescriptorSets = vk.WriteDescriptorSet( -// // Binding 0 : Vertex shader uniform buffer -// descriptorSet, VkDescriptorType.UNIFORM_BUFFER, 0, uniformBufferVS.descriptor, -// // Binding 1 : Fragment shader texture sampler -// descriptorSet, VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1, texture.descriptor) -// -// device updateDescriptorSets writeDescriptorSets -// } -// -// fun preparePipelines() { -// -// val inputAssemblyState = vk.PipelineInputAssemblyStateCreateInfo(VkPrimitiveTopology.TRIANGLE_LIST, 0, false) -// -// val rasterizationState = vk.PipelineRasterizationStateCreateInfo(VkPolygonMode.FILL, VkCullMode.NONE.i, VkFrontFace.CLOCKWISE) -// -// val blendAttachmentState = vk.PipelineColorBlendAttachmentState(0xf, false) -// -// val colorBlendState = vk.PipelineColorBlendStateCreateInfo(blendAttachmentState) -// -// val depthStencilState = vk.PipelineDepthStencilStateCreateInfo(true, true, VkCompareOp.LESS_OR_EQUAL) -// -// val viewportState = vk.PipelineViewportStateCreateInfo(1, 1) -// -// val multisampleState = vk.PipelineMultisampleStateCreateInfo(VkSampleCount.`1_BIT`) -// -// val dynamicStateEnables = listOf(VkDynamicState.VIEWPORT, VkDynamicState.SCISSOR) -// val dynamicState = vk.PipelineDynamicStateCreateInfo(dynamicStateEnables) -// -// // Load shaders -// val shaderStages = vk.PipelineShaderStageCreateInfo(2).also { -// it[0].loadShader("$assetPath/shaders/texture3d/texture3d.vert.spv", VkShaderStage.VERTEX_BIT) -// it[1].loadShader("$assetPath/shaders/texture3d/texture3d.frag.spv", VkShaderStage.FRAGMENT_BIT) -// } -// val pipelineCreateInfo = vk.GraphicsPipelineCreateInfo(pipelineLayout, renderPass).also { -// it.vertexInputState = vertices.inputState -// it.inputAssemblyState = inputAssemblyState -// it.rasterizationState = rasterizationState -// it.colorBlendState = colorBlendState -// it.multisampleState = multisampleState -// it.viewportState = viewportState -// it.depthStencilState = depthStencilState -// it.dynamicState = dynamicState -// it.stages = shaderStages -// } -// pipelines.solid = device.createGraphicsPipelines(pipelineCache, pipelineCreateInfo) -// } -// -// /** Prepare and initialize uniform buffer containing shader uniforms */ -// fun prepareUniformBuffers() { -// // Vertex shader uniform buffer block -// vulkanDevice.createBuffer( -// VkBufferUsage.UNIFORM_BUFFER_BIT.i, -// VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, -// uniformBufferVS, -// VkDeviceSize(uboVS.size.L)) -// -// updateUniformBuffers() -// } -// -// fun updateUniformBuffers(viewchanged: Boolean = true) { -// if (viewchanged) { -// uboVS.projection = glm.perspective(60f.rad, size.aspect, 0.001f, 256f) -// val viewMatrix = glm.translate(Mat4(1f), 0f, 0f, zoom) -// -// uboVS.model = viewMatrix * glm.translate(Mat4(1f), cameraPos) -// .rotateAssign(rotation.x.rad, 1f, 0f, 0f) -// .rotateAssign(rotation.y.rad, 0f, 1f, 0f) -// .rotateAssign(rotation.z.rad, 0f, 0f, 1f) -// -// uboVS.viewPos = Vec4(0f, 0f, -zoom, 0f) +// } +// } + IntStream + .range(0, ext.x * ext.y * ext.z) + .parallel() + .forEach { + val z = it / (ext.x * ext.y) + val remainder = it - z * ext.x * ext.y + val y = remainder / ext.x + val x = remainder % ext.x + val v = Vec3(x, y, z) / ext + var n = when { + FRACTAL -> FractalNoise().noise(v * noiseScale) + else -> 20f * glm.perlin(v) + } + n -= glm.floor(n) + + data[x + y * ext.x + z * ext.x * ext.y] = glm.floor(n * 255).b + } +// +// val channel = Channel() +// +// val processingPool = newThreadPool +// +// launch(processingPool) { for (request in channel) doProcessing(it) } +// +// +// +// for ... +// +// for ... +// +// channel.sendBlocking(ProcessingRequest(...)) + } + } + println("Done in ${time}ms") + + // Create a host-visible staging buffer that contains the raw image data + + // Buffer object + val bufferCreateInfo = vk.BufferCreateInfo { + size = texMemSize + usage = VkBufferUsage.TRANSFER_SRC_BIT.i + sharingMode = VkSharingMode.EXCLUSIVE + } + val stagingBuffer: VkBuffer = device createBuffer bufferCreateInfo + + // Allocate host visible memory for data upload + val memReqs = device getBufferMemoryRequirements stagingBuffer + val memAllocInfo = vk.MemoryAllocateInfo { + allocationSize = memReqs.size + memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT) + } + val stagingMemory = device allocateMemory memAllocInfo + device.bindBufferMemory(stagingBuffer, stagingMemory) + + // Copy texture data into staging buffer + device.mappingMemory(stagingMemory, VkDeviceSize(0), memReqs.size) { mapped -> + memCopy(memAddress(data), mapped, texMemSize) + } + + val copyCmd = super.createCommandBuffer(VkCommandBufferLevel.PRIMARY, true) + + // Image barrier for optimal image + + // The sub resource range describes the regions of the image we will be transition + val subresourceRange = vk.ImageSubresourceRange { + aspectMask = VkImageAspect.COLOR_BIT.i + baseMipLevel = 0 + levelCount = 1 + layerCount = 1 + } + // Optimal image will be used as destination for the copy, so we must transfer from our + // initial undefined image layout to the transfer destination layout + tools.setImageLayout( + copyCmd, + texture.image, + VkImageLayout.UNDEFINED, + VkImageLayout.TRANSFER_DST_OPTIMAL, + subresourceRange) + + // Copy 3D noise data to texture + + // Setup buffer copy regions + val bufferCopyRegion = vk.BufferImageCopy { + imageSubresource.apply { + aspectMask = VkImageAspect.COLOR_BIT.i + mipLevel = 0 + baseArrayLayer = 0 + layerCount = 1 + } + imageExtent(texture.extent) + } + copyCmd.copyBufferToImage( + stagingBuffer, + texture.image, + VkImageLayout.TRANSFER_DST_OPTIMAL, + bufferCopyRegion) + + // Change texture image layout to shader read after all mip levels have been copied + texture.imageLayout = VkImageLayout.SHADER_READ_ONLY_OPTIMAL + tools.setImageLayout( + copyCmd, + texture.image, + VkImageLayout.TRANSFER_DST_OPTIMAL, + texture.imageLayout, + subresourceRange) + + super.flushCommandBuffer(copyCmd, queue, true) + + // Clean up staging resources + data.free() + device freeMemory stagingMemory + device destroyBuffer stagingBuffer + regenerateNoise = false + } + + /** Free all Vulkan resources used a texture object */ + fun destroyTextureImage() { + device.apply { + if (texture.view.L != NULL) + destroyImageView(texture.view) + if (texture.image.L != NULL) + destroyImage(texture.image) + if (texture.sampler.L != NULL) + destroySampler(texture.sampler) + if (texture.deviceMemory.L != NULL) + freeMemory(texture.deviceMemory) + } + } + + override fun buildCommandBuffers() { + + val cmdBufInfo = vk.CommandBufferBeginInfo() + + val clearValues = vk.ClearValue(2).also { + it[0].color(defaultClearColor) + it[1].depthStencil(1f, 0) + } + val renderPassBeginInfo = vk.RenderPassBeginInfo { + renderPass = this@Texture3d.renderPass + renderArea.apply { + offset(0) + extent(size) + } + this.clearValues = clearValues + } + for (i in drawCmdBuffers.indices) { + // Set target frame buffer + renderPassBeginInfo.framebuffer(frameBuffers[i].L) + + drawCmdBuffers[i].apply { + + begin(cmdBufInfo) + + beginRenderPass(renderPassBeginInfo, VkSubpassContents.INLINE) + + setViewport(size) + + setScissor(size) + + bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, pipelineLayout, descriptorSet) + bindPipeline(VkPipelineBindPoint.GRAPHICS, pipelines.solid) + + bindVertexBuffers(VERTEX_BUFFER_BIND_ID, vertexBuffer.buffer) + bindIndexBuffer(indexBuffer.buffer, VkDeviceSize(0), VkIndexType.UINT32) + drawIndexed(indexCount, 1, 0, 0, 0) + + drawUI() + + endRenderPass() + + end() + } + } + } + + fun draw() { + + super.prepareFrame() + + // Command buffer to be sumitted to the queue + submitInfo.commandBuffer = drawCmdBuffers[currentBuffer] + + // Submit to queue + queue submit submitInfo + + super.submitFrame() + } + + fun generateQuad() = stak { + // Setup vertices for a single uv-mapped quad made from two triangles + val vertices = it.floats( + +1f, +1f, 0f, 1f, 1f, 0f, 0f, 1f, + -1f, +1f, 0f, 0f, 1f, 0f, 0f, 1f, + -1f, -1f, 0f, 0f, 0f, 0f, 0f, 1f, + +1f, -1f, 0f, 1f, 0f, 0f, 0f, 1f) + + // Setup indices + val indices = it.ints(0, 1, 2, 2, 3, 0) + indexCount = indices.cap + + // Create buffers + // For the sake of simplicity we won't stage the vertex data to the gpu memory + // Vertex buffer + vulkanDevice.createBuffer( + VkBufferUsage.VERTEX_BUFFER_BIT.i, + VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, + vertexBuffer, + vertices) + // Index buffer + vulkanDevice.createBuffer( + VkBufferUsage.INDEX_BUFFER_BIT.i, + VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, + indexBuffer, + indices) + } + + fun setupVertexDescriptions() { + // Binding description + vertices.inputBinding = vk.VertexInputBindingDescription(VERTEX_BUFFER_BIND_ID, Vertex.size, VkVertexInputRate.VERTEX) + + // Attribute descriptions + // Describes memory layout and shader positions + vertices.inputAttributes = vk.VertexInputAttributeDescription( + // Location 0 : Position + VERTEX_BUFFER_BIND_ID, 0, VkFormat.R32G32B32_SFLOAT, Vertex.offsetOf("pos"), + // Location 1 : Texture coordinates + VERTEX_BUFFER_BIND_ID, 1, VkFormat.R32G32_SFLOAT, Vertex.offsetOf("uv"), + // Location 1 : Vertex normal + VERTEX_BUFFER_BIND_ID, 2, VkFormat.R32G32B32_SFLOAT, Vertex.offsetOf("normal")) + + vertices.inputState = vk.PipelineVertexInputStateCreateInfo { + vertexBindingDescription = vertices.inputBinding + vertexAttributeDescriptions = vertices.inputAttributes + } + } + + fun setupDescriptorPool() { + // Example uses one ubo and one image sampler + val poolSizes = vk.DescriptorPoolSize( + VkDescriptorType.UNIFORM_BUFFER, 1, + VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1) + + val descriptorPoolInfo = vk.DescriptorPoolCreateInfo(poolSizes, 2) + + descriptorPool = device createDescriptorPool descriptorPoolInfo + } + + fun setupDescriptorSetLayout() { + + val setLayoutBindings = vk.DescriptorSetLayoutBinding( + // Binding 0 : Vertex shader uniform buffer + VkDescriptorType.UNIFORM_BUFFER, VkShaderStage.VERTEX_BIT.i, 0, + // Binding 1 : Fragment shader image sampler + VkDescriptorType.COMBINED_IMAGE_SAMPLER, VkShaderStage.FRAGMENT_BIT.i, 1) + + val descriptorLayout = vk.DescriptorSetLayoutCreateInfo(setLayoutBindings) + + descriptorSetLayout = device createDescriptorSetLayout descriptorLayout + + val pipelineLayoutCreateInfo = vk.PipelineLayoutCreateInfo(descriptorSetLayout) + + pipelineLayout = device createPipelineLayout pipelineLayoutCreateInfo + } + + fun setupDescriptorSet() { + + val allocInfo = vk.DescriptorSetAllocateInfo(descriptorPool, descriptorSetLayout) + + descriptorSet = device allocateDescriptorSets allocInfo + + val writeDescriptorSets = vk.WriteDescriptorSet( + // Binding 0 : Vertex shader uniform buffer + descriptorSet, VkDescriptorType.UNIFORM_BUFFER, 0, uniformBufferVS.descriptor, + // Binding 1 : Fragment shader texture sampler + descriptorSet, VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1, texture.descriptor) + + device updateDescriptorSets writeDescriptorSets + } + + fun preparePipelines() { + + val inputAssemblyState = vk.PipelineInputAssemblyStateCreateInfo(VkPrimitiveTopology.TRIANGLE_LIST, 0, false) + + val rasterizationState = vk.PipelineRasterizationStateCreateInfo(VkPolygonMode.FILL, VkCullMode.NONE.i, VkFrontFace.CLOCKWISE) + + val blendAttachmentState = vk.PipelineColorBlendAttachmentState(0xf, false) + + val colorBlendState = vk.PipelineColorBlendStateCreateInfo(blendAttachmentState) + + val depthStencilState = vk.PipelineDepthStencilStateCreateInfo(true, true, VkCompareOp.LESS_OR_EQUAL) + + val viewportState = vk.PipelineViewportStateCreateInfo(1, 1) + + val multisampleState = vk.PipelineMultisampleStateCreateInfo(VkSampleCount.`1_BIT`) + + val dynamicStateEnables = listOf(VkDynamicState.VIEWPORT, VkDynamicState.SCISSOR) + val dynamicState = vk.PipelineDynamicStateCreateInfo(dynamicStateEnables) + + // Load shaders + val shaderStages = vk.PipelineShaderStageCreateInfo(2).also { + it[0].loadShader("$assetPath/shaders/texture3d/texture3d.vert.spv", VkShaderStage.VERTEX_BIT) + it[1].loadShader("$assetPath/shaders/texture3d/texture3d.frag.spv", VkShaderStage.FRAGMENT_BIT) + } + val pipelineCreateInfo = vk.GraphicsPipelineCreateInfo(pipelineLayout, renderPass).also { + it.vertexInputState = vertices.inputState + it.inputAssemblyState = inputAssemblyState + it.rasterizationState = rasterizationState + it.colorBlendState = colorBlendState + it.multisampleState = multisampleState + it.viewportState = viewportState + it.depthStencilState = depthStencilState + it.dynamicState = dynamicState + it.stages = shaderStages + } + pipelines.solid = device.createGraphicsPipelines(pipelineCache, pipelineCreateInfo) + } + + /** Prepare and initialize uniform buffer containing shader uniforms */ + fun prepareUniformBuffers() { + // Vertex shader uniform buffer block + vulkanDevice.createBuffer( + VkBufferUsage.UNIFORM_BUFFER_BIT.i, + VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, + uniformBufferVS, + VkDeviceSize(uboVS.size.L)) + + updateUniformBuffers() + } + + fun updateUniformBuffers(viewchanged: Boolean = true) { + if (viewchanged) { + uboVS.projection = glm.perspective(60f.rad, size.aspect, 0.001f, 256f) + val viewMatrix = glm.translate(Mat4(1f), 0f, 0f, zoom) + + uboVS.model = viewMatrix * glm.translate(Mat4(1f), cameraPos) + .rotateAssign(rotation.x.rad, 1f, 0f, 0f) + .rotateAssign(rotation.y.rad, 0f, 1f, 0f) + .rotateAssign(rotation.z.rad, 0f, 0f, 1f) + + uboVS.viewPos = Vec4(0f, 0f, -zoom, 0f) + } else { + uboVS.depth += frameTimer * 0.15f + if (uboVS.depth > 1f) + uboVS.depth = uboVS.depth - 1f + } + + uniformBufferVS.mapping { mapped -> uboVS to mapped } + } + + override fun prepare() { + super.prepare() + generateQuad() + setupVertexDescriptions() + prepareUniformBuffers() + prepareNoiseTexture(256, 256, 256) + setupDescriptorSetLayout() + preparePipelines() + setupDescriptorPool() + setupDescriptorSet() + buildCommandBuffers() + prepared = true + window.show() + } + + override fun render() { + if (!prepared) + return + draw() + if (regenerateNoise) + updateNoiseTexture() + if (!paused) + updateUniformBuffers(false) + } + + override fun viewChanged() = updateUniformBuffers() + +// virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) +// { +// if (overlay->header("Settings")) { +// if (regenerateNoise) { overlay -> +// text("Generating new noise texture...") // } else { -// uboVS.depth += frameTimer * 0.15f -// if (uboVS.depth > 1f) -// uboVS.depth = uboVS.depth - 1f +// if (overlay->button("Generate new texture")) { +// regenerateNoise = true +// } // } -// -// uniformBufferVS.mapping { mapped -> uboVS to mapped } // } -// -// override fun prepare() { -// super.prepare() -// generateQuad() -// setupVertexDescriptions() -// prepareUniformBuffers() -// prepareNoiseTexture(256, 256, 256) -// setupDescriptorSetLayout() -// preparePipelines() -// setupDescriptorPool() -// setupDescriptorSet() -// buildCommandBuffers() -// prepared = true -// window.show() -// } -// -// override fun render() { -// if (!prepared) -// return -// draw() -// if (regenerateNoise) -// updateNoiseTexture() -// if (!paused) -// updateUniformBuffers(false) // } -// -// override fun viewChanged() = updateUniformBuffers() -// -//// virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) -//// { -//// if (overlay->header("Settings")) { -//// if (regenerateNoise) { overlay -> -//// text("Generating new noise texture...") -//// } else { -//// if (overlay->button("Generate new texture")) { -//// regenerateNoise = true -//// } -//// } -//// } -//// } -//} \ No newline at end of file +} \ No newline at end of file diff --git a/src/main/kotlin/vulkan/basics/11 Model Rendering.kt b/src/main/kotlin/vulkan/basics/11 Model Rendering.kt index c24b184..ff6c313 100644 --- a/src/main/kotlin/vulkan/basics/11 Model Rendering.kt +++ b/src/main/kotlin/vulkan/basics/11 Model Rendering.kt @@ -1,535 +1,535 @@ -///* -//* Vulkan Example - Model loading and rendering -//* -//* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de -//* -//* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -//*/ -// -//package vulkan.basics -// -//import assimp.AiPostProcessStepsFlags -//import assimp.Importer -//import assimp.or -//import glm_.L -//import glm_.func.rad -//import glm_.glm -//import glm_.mat4x4.Mat4 -//import glm_.size -//import glm_.vec2.Vec2 -//import glm_.vec3.Vec3 -//import glm_.vec4.Vec4 -//import kool.adr -//import org.lwjgl.system.MemoryUtil.NULL -//import org.lwjgl.vulkan.VkDevice -//import org.lwjgl.vulkan.VkPipelineVertexInputStateCreateInfo -//import org.lwjgl.vulkan.VkVertexInputAttributeDescription -//import org.lwjgl.vulkan.VkVertexInputBindingDescription -//import vkk.* -//import vulkan.USE_STAGING -//import vulkan.VERTEX_BUFFER_BIND_ID -//import vulkan.assetPath -//import vulkan.base.Buffer -//import vulkan.base.Texture2D -//import vulkan.base.VulkanExampleBase -//import vulkan.base.tools -//import assimp.AiPostProcessStep as Pp -// -// -//fun main(args: Array) { -// ModelRendering().apply { -// setupWindow() -// initVulkan() -// prepare() -// renderLoop() -// destroy() -// } -//} -// -// -//private class ModelRendering : VulkanExampleBase() { -// -// var wireframe = false -// -// object textures { -// val colorMap = Texture2D() -// } -// -// object vertices { -// lateinit var inputState: VkPipelineVertexInputStateCreateInfo -// lateinit var bindingDescriptions: VkVertexInputBindingDescription -// lateinit var attributeDescriptions: VkVertexInputAttributeDescription.Buffer -// } -// -// // Vertex layout used in this example -// // This must fit input locations of the vertex shader used to render the model -// class Vertex : Bufferizable() { -// lateinit var pos: Vec3 -// @Order(1) -// lateinit var normal: Vec3 -// lateinit var uv: Vec2 -// @Order(3) -// lateinit var color: Vec3 -// } -// -// // Contains all Vulkan resources required to represent vertex and index buffers for a model -// // This is for demonstration and learning purposes, the other examples use a model loader class for easy access -// object model { -// object vertices { -// var buffer = VkBuffer(NULL) -// var memory = VkDeviceMemory(NULL) -// } -// -// object indices { -// var count = 0 -// var buffer = VkBuffer(NULL) -// var memory = VkDeviceMemory(NULL) -// } -// -// // Destroys all Vulkan resources created for this model -// fun destroy(device: VkDevice) = device.apply { -// destroyBuffer(vertices.buffer) -// freeMemory(vertices.memory) -// destroyBuffer(indices.buffer) -// freeMemory(indices.memory) -// } -// } -// -// object uniformBuffers { -// val scene = Buffer() -// } -// -// object uboVS : Bufferizable() { -// lateinit var projection: Mat4 -// @Order(1) -// lateinit var model: Mat4 -// @Order(2) -// val lightPos = Vec4(25f, 5f, 5f, 1f) -// } -// -// object pipelines { -// var solid = VkPipeline(NULL) -// var wireframe = VkPipeline(NULL) -// } -// -// var pipelineLayout = VkPipelineLayout(NULL) -// var descriptorSet = VkDescriptorSet(NULL) -// var descriptorSetLayout = VkDescriptorSetLayout(NULL) -// -// init { -// zoom = -5.5f -// zoomSpeed = 2.5f -// rotationSpeed = 0.5f -// rotation(-0.5f, -112.75f, 0f) -// cameraPos(0.1f, 1.1f, 0f) -// title = "Model rendering" -// settings.overlay = false // TODO -// } -// -// override fun destroy() { -// // Clean up used Vulkan resources -// // Note : Inherited destructor cleans up resources stored in base class -// device.apply { -// destroyPipeline(pipelines.solid) -// if (pipelines.wireframe.isValid()) -// destroyPipeline(pipelines.wireframe) -// -// destroyPipelineLayout(pipelineLayout) -// destroyDescriptorSetLayout(descriptorSetLayout) -// } -// model.destroy(device) -// -// textures.colorMap.destroy() -// uniformBuffers.scene.destroy() -// -// super.destroy() -// } -// -// override fun getEnabledFeatures() { -// // Fill mode non solid is required for wireframe display -// if (deviceFeatures.fillModeNonSolid) -// enabledFeatures.fillModeNonSolid = true -// } -// -// override fun buildCommandBuffers() { -// -// val cmdBufInfo = vk.CommandBufferBeginInfo() -// -// val clearValues = vk.ClearValue(2).also { -// it[0].color(defaultClearColor) -// it[1].depthStencil(1f, 0) -// } -// val renderPassBeginInfo = vk.RenderPassBeginInfo { -// renderPass = this@ModelRendering.renderPass -// renderArea.apply { -// offset(0) -// extent(size) -// } -// this.clearValues = clearValues -// } -// for (i in drawCmdBuffers.indices) { -// // Set target frame buffer -// renderPassBeginInfo.framebuffer(frameBuffers[i].L) -// -// drawCmdBuffers[i].apply { -// -// begin(cmdBufInfo) -// -// beginRenderPass(renderPassBeginInfo, VkSubpassContents.INLINE) -// -// setViewport(size) -// -// setScissor(size) -// -// bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, pipelineLayout, descriptorSet) -// bindPipeline(VkPipelineBindPoint.GRAPHICS, if (wireframe) pipelines.wireframe else pipelines.solid) -// -// // Bind mesh vertex buffer -// bindVertexBuffers(VERTEX_BUFFER_BIND_ID, model.vertices.buffer) -// // Bind mesh index buffer -// bindIndexBuffer(model.indices.buffer, VkDeviceSize(0), VkIndexType.UINT32) -// // Render mesh vertex buffer using it's indices -// drawIndexed(model.indices.count, 1, 0, 0, 0) -// -// drawUI() -// -// endRenderPass() -// -// end() -// } -// } -// } -// -// /** Load a model from file using the ASSIMP model loader and generate all resources required to render the model */ -// fun loadModel(filename: String) { -// // Load the model from file using ASSIMP -// -// // Flags for loading the mesh -// val assimpFlags: AiPostProcessStepsFlags = Pp.FlipWindingOrder or Pp.Triangulate or Pp.PreTransformVertices -// -// val scene = Importer().readFile(filename, assimpFlags)!! -// -// // Generate vertex buffer from ASSIMP scene data -// val scale = 1f -// val vertices = ArrayList() -// -// // Iterate through all meshes in the file and extract the vertex components -// for (m in 0 until scene.numMeshes) -// for (v in 0 until scene.meshes[m].numVertices) { -// -// val vertex = Vertex().apply { -// // Use glm make_* functions to convert ASSIMP vectors to glm vectors -// pos = scene.meshes[m].vertices[v] * scale -// normal = scene.meshes[m].normals[v] -// // Texture coordinates and colors may have multiple channels, we only use the first [0] one -// uv = Vec2(scene.meshes[m].textureCoords[0][v]) -// // Mesh may not have vertex colors -// color = scene.meshes[m].colors.getOrNull(0)?.let { Vec3(it[v]) } ?: Vec3(1f) -// // Vulkan uses a right-handed NDC (contrary to OpenGL), so simply flip Y-Axis -// pos.y *= -1f -// } -// vertices += vertex -// } -// val vertexBuffer = bufferOf(vertices) -// val vertexBufferSize = VkDeviceSize(vertexBuffer.size.L) -// -// // Generate index buffer from ASSIMP scene data -// val indices = ArrayList() -// for (m in 0 until scene.numMeshes) { -// val indexBase = indices.size -// for (f in 0 until scene.meshes[m].numFaces) -// // We assume that all faces are triangulated -// for (i in 0..2) -// indices += scene.meshes[m].faces[f][i] + indexBase -// } -// val indexBuffer = intArrayOf(indices) -// val indexBufferSize = VkDeviceSize(indexBuffer.size.L) -// model.indices.count = indices.size -// -// // Static mesh should always be device local -// -// if (USE_STAGING) { -// -// val vertexStaging = object { -// var buffer = VkBuffer(NULL) -// var memory = VkDeviceMemory(NULL) -// } -// val indexStaging = object { -// var buffer = VkBuffer(NULL) -// var memory= VkDeviceMemory (NULL) -// } -// -// // Create staging buffers -// // Vertex data -// vulkanDevice.createBuffer( -// VkBufferUsage.TRANSFER_SRC_BIT.i, -// VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, -// vertexBufferSize, -// vertexStaging::buffer, -// vertexStaging::memory, -// vertexBuffer.adr) -// // Index data -// vulkanDevice.createBuffer( -// VkBufferUsage.TRANSFER_SRC_BIT.i, -// VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, -// indexBufferSize, -// indexStaging::buffer, -// indexStaging::memory, -// indexBuffer.adr) -// -// // Create device local buffers -// // Vertex buffer -// vulkanDevice.createBuffer( -// VkBufferUsage.VERTEX_BUFFER_BIT or VkBufferUsage.TRANSFER_DST_BIT, -// VkMemoryProperty.DEVICE_LOCAL_BIT.i, -// vertexBufferSize, -// model.vertices::buffer, -// model.vertices::memory) -// // Index buffer -// vulkanDevice.createBuffer( -// VkBufferUsage.INDEX_BUFFER_BIT or VkBufferUsage.TRANSFER_DST_BIT, -// VkMemoryProperty.DEVICE_LOCAL_BIT.i, -// indexBufferSize, -// model.indices::buffer, -// model.indices::memory) -// -// // Copy from staging buffers -// val copyCmd = super.createCommandBuffer(VkCommandBufferLevel.PRIMARY, true) -// -// val copyRegion = vk.BufferCopy { size = vertexBufferSize } -// copyCmd.copyBuffer( -// vertexStaging.buffer, -// model.vertices.buffer, -// copyRegion) -// -// copyRegion.size = indexBufferSize -// copyCmd.copyBuffer( -// indexStaging.buffer, -// model.indices.buffer, -// copyRegion) -// -// super.flushCommandBuffer(copyCmd, queue, true) -// -// device.apply { -// destroyBuffer(vertexStaging.buffer) -// freeMemory(vertexStaging.memory) -// destroyBuffer(indexStaging.buffer) -// freeMemory(indexStaging.memory) -// } -// } else { -// // Vertex buffer -// vulkanDevice.createBuffer( -// VkBufferUsage.VERTEX_BUFFER_BIT.i, -// VkMemoryProperty.HOST_VISIBLE_BIT.i, -// vertexBufferSize, -// model.vertices::buffer, -// model.vertices::memory, -// vertexBuffer.adr) -// // Index buffer -// vulkanDevice.createBuffer( -// VkBufferUsage.INDEX_BUFFER_BIT.i, -// VkMemoryProperty.HOST_VISIBLE_BIT.i, -// indexBufferSize, -// model.indices::buffer, -// model.indices::memory, -// indexBuffer.adr) -// } -// } -// -// fun loadAssets() { -// loadModel("$assetPath/models/voyager/voyager.dae") -// val (texture, format) = when { -// deviceFeatures.textureCompressionBC -> "voyager_bc3_unorm.ktx" to VkFormat.BC3_UNORM_BLOCK -// deviceFeatures.textureCompressionASTC_LDR -> "voyager_astc_8x8_unorm.ktx" to VkFormat.ASTC_8x8_UNORM_BLOCK -// deviceFeatures.textureCompressionETC2 -> "voyager_etc2_unorm.ktx" to VkFormat.ETC2_R8G8B8A8_UNORM_BLOCK -// else -> tools.exitFatal("Device does not support any compressed texture format!", ERROR_FEATURE_NOT_PRESENT) -// } -// textures.colorMap.loadFromFile("$assetPath/models/voyager/$texture", format, vulkanDevice, queue) -// } -// -// fun setupVertexDescriptions() { -// val vertex = Vertex() -// // Binding description -// vertices.bindingDescriptions = vk.VertexInputBindingDescription(VERTEX_BUFFER_BIND_ID, vertex.size, VkVertexInputRate.VERTEX) -// -// // Attribute descriptions -// // Describes memory layout and shader positions -// vertices.attributeDescriptions = vk.VertexInputAttributeDescription( -// // Location 0 : Position -// VERTEX_BUFFER_BIND_ID, 0, VkFormat.R32G32B32_SFLOAT, vertex.offsetOf("pos"), -// // Location 1 : Normal -// VERTEX_BUFFER_BIND_ID, 1, VkFormat.R32G32B32_SFLOAT, vertex.offsetOf("normal"), -// // Location 2 : Texture coordinates -// VERTEX_BUFFER_BIND_ID, 2, VkFormat.R32G32_SFLOAT, vertex.offsetOf("uv"), -// // Location 3 : Color -// VERTEX_BUFFER_BIND_ID, 3, VkFormat.R32G32B32_SFLOAT, vertex.offsetOf("color")) -// -// vertices.inputState = vk.PipelineVertexInputStateCreateInfo { -// vertexBindingDescription = vertices.bindingDescriptions -// vertexAttributeDescriptions = vertices.attributeDescriptions -// } -// } -// -// fun setupDescriptorPool() { -// // Example uses one ubo and one combined image sampler -// val poolSizes = vk.DescriptorPoolSize( -// VkDescriptorType.UNIFORM_BUFFER, 1, -// VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1) -// -// val descriptorPoolInfo = vk.DescriptorPoolCreateInfo(poolSizes, 1) -// -// descriptorPool = device createDescriptorPool descriptorPoolInfo -// } -// -// fun setupDescriptorSetLayout() { -// -// val setLayoutBindings = vk.DescriptorSetLayoutBinding( -// // Binding 0 : Vertex shader uniform buffer -// VkDescriptorType.UNIFORM_BUFFER, VkShaderStage.VERTEX_BIT.i, 0, -// // Binding 1 : Fragment shader combined sampler -// VkDescriptorType.COMBINED_IMAGE_SAMPLER, VkShaderStage.FRAGMENT_BIT.i, 1) -// -// val descriptorLayout = vk.DescriptorSetLayoutCreateInfo(setLayoutBindings) -// -// descriptorSetLayout = device createDescriptorSetLayout descriptorLayout -// -// val pipelineLayoutCreateInfo = vk.PipelineLayoutCreateInfo(descriptorSetLayout) -// -// pipelineLayout = device createPipelineLayout pipelineLayoutCreateInfo -// } -// -// fun setupDescriptorSet() { -// -// val allocInfo = vk.DescriptorSetAllocateInfo(descriptorPool, descriptorSetLayout) -// -// descriptorSet = device allocateDescriptorSets allocInfo -// -// val texDescriptor = vk.DescriptorImageInfo(textures.colorMap.sampler, textures.colorMap.view, VkImageLayout.GENERAL) -// -// val writeDescriptorSets = vk.WriteDescriptorSet( -// // Binding 0 : Vertex shader uniform buffer -// descriptorSet, VkDescriptorType.UNIFORM_BUFFER, 0, uniformBuffers.scene.descriptor, -// // Binding 1 : Color map -// descriptorSet, VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1, texDescriptor) -// -// device updateDescriptorSets writeDescriptorSets -// } -// -// fun preparePipelines() { -// -// val inputAssemblyState = vk.PipelineInputAssemblyStateCreateInfo(VkPrimitiveTopology.TRIANGLE_LIST, 0, false) -// -// val rasterizationState = vk.PipelineRasterizationStateCreateInfo(VkPolygonMode.FILL, VkCullMode.BACK_BIT.i, VkFrontFace.CLOCKWISE) -// -// val blendAttachmentState = vk.PipelineColorBlendAttachmentState(0xf, false) -// -// val colorBlendState = vk.PipelineColorBlendStateCreateInfo(blendAttachmentState) -// -// val depthStencilState = vk.PipelineDepthStencilStateCreateInfo(true, true, VkCompareOp.LESS_OR_EQUAL) -// -// val viewportState = vk.PipelineViewportStateCreateInfo(1, 1) -// -// val multisampleState = vk.PipelineMultisampleStateCreateInfo(VkSampleCount.`1_BIT`) -// -// val dynamicStateEnables = listOf(VkDynamicState.VIEWPORT, VkDynamicState.SCISSOR) -// val dynamicState = vk.PipelineDynamicStateCreateInfo(dynamicStateEnables) -// -// // Solid rendering pipeline -// // Load shaders -// val shaderStages = vk.PipelineShaderStageCreateInfo(2).also { -// it[0].loadShader("$assetPath/shaders/mesh/mesh.vert.spv", VkShaderStage.VERTEX_BIT) -// it[1].loadShader("$assetPath/shaders/mesh/mesh.frag.spv", VkShaderStage.FRAGMENT_BIT) -// } -// val pipelineCreateInfo = vk.GraphicsPipelineCreateInfo(pipelineLayout, renderPass).also { -// it.vertexInputState = vertices.inputState -// it.inputAssemblyState = inputAssemblyState -// it.rasterizationState = rasterizationState -// it.colorBlendState = colorBlendState -// it.multisampleState = multisampleState -// it.viewportState = viewportState -// it.depthStencilState = depthStencilState -// it.dynamicState = dynamicState -// it.stages = shaderStages -// } -// pipelines.solid = device.createGraphicsPipelines(pipelineCache, pipelineCreateInfo) -// -// // Wire frame rendering pipeline -// if (deviceFeatures.fillModeNonSolid) { -// rasterizationState.polygonMode = VkPolygonMode.LINE -// rasterizationState.lineWidth = 1f -// pipelines.wireframe = device.createGraphicsPipelines(pipelineCache, pipelineCreateInfo) -// } -// } -// -// /** Prepare and initialize uniform buffer containing shader uniforms */ -// fun prepareUniformBuffers() { -// // Vertex shader uniform buffer block -// vulkanDevice.createBuffer( -// VkBufferUsage.UNIFORM_BUFFER_BIT.i, -// VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, -// uniformBuffers.scene, -// VkDeviceSize(uboVS.size.L)) -// -// // Map persistent -// uniformBuffers.scene.map() -// -// updateUniformBuffers() -// } -// -// fun updateUniformBuffers() { -// -// uboVS.projection = glm.perspective(60f.rad, size.aspect, 0.1f, 256f) -// val viewMatrix = glm.translate(Mat4(1f), Vec3(0f, 0f, zoom)) -// -// uboVS.model = viewMatrix * glm.translate(Mat4(1f), cameraPos) -// .rotateAssign(rotation.x.rad, 1f, 0f, 0f) -// .rotateAssign(rotation.y.rad, 0f, 1f, 0f) -// .rotateAssign(rotation.z.rad, 0f, 0f, 1f) -// -// uboVS to uniformBuffers.scene.mapped -// } -// -// fun draw() { -// -// super.prepareFrame() -// -// // Command buffer to be sumitted to the queue -// submitInfo.commandBuffer = drawCmdBuffers[currentBuffer] -// -// // Submit to queue -// queue submit submitInfo -// -// super.submitFrame() -// } -// -// override fun prepare() { -// super.prepare() -// loadAssets() -// setupVertexDescriptions() -// prepareUniformBuffers() -// setupDescriptorSetLayout() -// preparePipelines() -// setupDescriptorPool() -// setupDescriptorSet() +/* +* Vulkan Example - Model loading and rendering +* +* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de +* +* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +*/ + +package vulkan.basics + +import assimp.AiPostProcessStepsFlags +import assimp.Importer +import assimp.or +import glm_.L +import glm_.func.rad +import glm_.glm +import glm_.mat4x4.Mat4 +import glm_.size +import glm_.vec2.Vec2 +import glm_.vec3.Vec3 +import glm_.vec4.Vec4 +import kool.adr +import org.lwjgl.system.MemoryUtil.NULL +import org.lwjgl.vulkan.VkDevice +import org.lwjgl.vulkan.VkPipelineVertexInputStateCreateInfo +import org.lwjgl.vulkan.VkVertexInputAttributeDescription +import org.lwjgl.vulkan.VkVertexInputBindingDescription +import vkk.* +import vulkan.USE_STAGING +import vulkan.VERTEX_BUFFER_BIND_ID +import vulkan.assetPath +import vulkan.base.Buffer +import vulkan.base.Texture2D +import vulkan.base.VulkanExampleBase +import vulkan.base.tools +import assimp.AiPostProcessStep as Pp + + +fun main(args: Array) { + ModelRendering().apply { + setupWindow() + initVulkan() + prepare() + renderLoop() + destroy() + } +} + + +private class ModelRendering : VulkanExampleBase() { + + var wireframe = false + + object textures { + val colorMap = Texture2D() + } + + object vertices { + lateinit var inputState: VkPipelineVertexInputStateCreateInfo + lateinit var bindingDescriptions: VkVertexInputBindingDescription + lateinit var attributeDescriptions: VkVertexInputAttributeDescription.Buffer + } + + // Vertex layout used in this example + // This must fit input locations of the vertex shader used to render the model + class Vertex : Bufferizable() { + lateinit var pos: Vec3 + @Order(1) + lateinit var normal: Vec3 + lateinit var uv: Vec2 + @Order(3) + lateinit var color: Vec3 + } + + // Contains all Vulkan resources required to represent vertex and index buffers for a model + // This is for demonstration and learning purposes, the other examples use a model loader class for easy access + object model { + object vertices { + var buffer = VkBuffer(NULL) + var memory = VkDeviceMemory(NULL) + } + + object indices { + var count = 0 + var buffer = VkBuffer(NULL) + var memory = VkDeviceMemory(NULL) + } + + // Destroys all Vulkan resources created for this model + fun destroy(device: VkDevice) = device.apply { + destroyBuffer(vertices.buffer) + freeMemory(vertices.memory) + destroyBuffer(indices.buffer) + freeMemory(indices.memory) + } + } + + object uniformBuffers { + val scene = Buffer() + } + + object uboVS : Bufferizable() { + lateinit var projection: Mat4 + @Order(1) + lateinit var model: Mat4 + @Order(2) + val lightPos = Vec4(25f, 5f, 5f, 1f) + } + + object pipelines { + var solid = VkPipeline(NULL) + var wireframe = VkPipeline(NULL) + } + + var pipelineLayout = VkPipelineLayout(NULL) + var descriptorSet = VkDescriptorSet(NULL) + var descriptorSetLayout = VkDescriptorSetLayout(NULL) + + init { + zoom = -5.5f + zoomSpeed = 2.5f + rotationSpeed = 0.5f + rotation(-0.5f, -112.75f, 0f) + cameraPos(0.1f, 1.1f, 0f) + title = "Model rendering" + settings.overlay = false // TODO + } + + override fun destroy() { + // Clean up used Vulkan resources + // Note : Inherited destructor cleans up resources stored in base class + device.apply { + destroyPipeline(pipelines.solid) + if (pipelines.wireframe.L != NULL) + destroyPipeline(pipelines.wireframe) + + destroyPipelineLayout(pipelineLayout) + destroyDescriptorSetLayout(descriptorSetLayout) + } + model.destroy(device) + + textures.colorMap.destroy() + uniformBuffers.scene.destroy() + + super.destroy() + } + + override fun getEnabledFeatures() { + // Fill mode non solid is required for wireframe display + if (deviceFeatures.fillModeNonSolid) + enabledFeatures.fillModeNonSolid = true + } + + override fun buildCommandBuffers() { + + val cmdBufInfo = vk.CommandBufferBeginInfo() + + val clearValues = vk.ClearValue(2).also { + it[0].color(defaultClearColor) + it[1].depthStencil(1f, 0) + } + val renderPassBeginInfo = vk.RenderPassBeginInfo { + renderPass = this@ModelRendering.renderPass + renderArea.apply { + offset(0) + extent(size) + } + this.clearValues = clearValues + } + for (i in drawCmdBuffers.indices) { + // Set target frame buffer + renderPassBeginInfo.framebuffer(frameBuffers[i].L) + + drawCmdBuffers[i].apply { + + begin(cmdBufInfo) + + beginRenderPass(renderPassBeginInfo, VkSubpassContents.INLINE) + + setViewport(size) + + setScissor(size) + + bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, pipelineLayout, descriptorSet) + bindPipeline(VkPipelineBindPoint.GRAPHICS, if (wireframe) pipelines.wireframe else pipelines.solid) + + // Bind mesh vertex buffer + bindVertexBuffers(VERTEX_BUFFER_BIND_ID, model.vertices.buffer) + // Bind mesh index buffer + bindIndexBuffer(model.indices.buffer, VkDeviceSize(0), VkIndexType.UINT32) + // Render mesh vertex buffer using it's indices + drawIndexed(model.indices.count, 1, 0, 0, 0) + + drawUI() + + endRenderPass() + + end() + } + } + } + + /** Load a model from file using the ASSIMP model loader and generate all resources required to render the model */ + fun loadModel(filename: String) { + // Load the model from file using ASSIMP + + // Flags for loading the mesh + val assimpFlags: AiPostProcessStepsFlags = Pp.FlipWindingOrder or Pp.Triangulate or Pp.PreTransformVertices + + val scene = Importer().readFile(filename, assimpFlags)!! + + // Generate vertex buffer from ASSIMP scene data + val scale = 1f + val vertices = ArrayList() + + // Iterate through all meshes in the file and extract the vertex components + for (m in 0 until scene.numMeshes) + for (v in 0 until scene.meshes[m].numVertices) { + + val vertex = Vertex().apply { + // Use glm make_* functions to convert ASSIMP vectors to glm vectors + pos = scene.meshes[m].vertices[v] * scale + normal = scene.meshes[m].normals[v] + // Texture coordinates and colors may have multiple channels, we only use the first [0] one + uv = Vec2(scene.meshes[m].textureCoords[0][v]) + // Mesh may not have vertex colors + color = scene.meshes[m].colors.getOrNull(0)?.let { Vec3(it[v]) } ?: Vec3(1f) + // Vulkan uses a right-handed NDC (contrary to OpenGL), so simply flip Y-Axis + pos.y *= -1f + } + vertices += vertex + } + val vertexBuffer = bufferOf(vertices) + val vertexBufferSize = VkDeviceSize(vertexBuffer.size.L) + + // Generate index buffer from ASSIMP scene data + val indices = ArrayList() + for (m in 0 until scene.numMeshes) { + val indexBase = indices.size + for (f in 0 until scene.meshes[m].numFaces) + // We assume that all faces are triangulated + for (i in 0..2) + indices += scene.meshes[m].faces[f][i] + indexBase + } + val indexBuffer = intArrayOf(indices) + val indexBufferSize = VkDeviceSize(indexBuffer.size.L) + model.indices.count = indices.size + + // Static mesh should always be device local + + if (USE_STAGING) { + + val vertexStaging = object { + var buffer = VkBuffer(NULL) + var memory = VkDeviceMemory(NULL) + } + val indexStaging = object { + var buffer = VkBuffer(NULL) + var memory= VkDeviceMemory (NULL) + } + + // Create staging buffers + // Vertex data + vulkanDevice.createBuffer( + VkBufferUsage.TRANSFER_SRC_BIT.i, + VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, + vertexBufferSize, + vertexStaging::buffer, + vertexStaging::memory, + vertexBuffer.adr) + // Index data + vulkanDevice.createBuffer( + VkBufferUsage.TRANSFER_SRC_BIT.i, + VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, + indexBufferSize, + indexStaging::buffer, + indexStaging::memory, + indexBuffer.adr) + + // Create device local buffers + // Vertex buffer + vulkanDevice.createBuffer( + VkBufferUsage.VERTEX_BUFFER_BIT or VkBufferUsage.TRANSFER_DST_BIT, + VkMemoryProperty.DEVICE_LOCAL_BIT.i, + vertexBufferSize, + model.vertices::buffer, + model.vertices::memory) + // Index buffer + vulkanDevice.createBuffer( + VkBufferUsage.INDEX_BUFFER_BIT or VkBufferUsage.TRANSFER_DST_BIT, + VkMemoryProperty.DEVICE_LOCAL_BIT.i, + indexBufferSize, + model.indices::buffer, + model.indices::memory) + + // Copy from staging buffers + val copyCmd = super.createCommandBuffer(VkCommandBufferLevel.PRIMARY, true) + + val copyRegion = vk.BufferCopy { size = vertexBufferSize } + copyCmd.copyBuffer( + vertexStaging.buffer, + model.vertices.buffer, + copyRegion) + + copyRegion.size = indexBufferSize + copyCmd.copyBuffer( + indexStaging.buffer, + model.indices.buffer, + copyRegion) + + super.flushCommandBuffer(copyCmd, queue, true) + + device.apply { + destroyBuffer(vertexStaging.buffer) + freeMemory(vertexStaging.memory) + destroyBuffer(indexStaging.buffer) + freeMemory(indexStaging.memory) + } + } else { + // Vertex buffer + vulkanDevice.createBuffer( + VkBufferUsage.VERTEX_BUFFER_BIT.i, + VkMemoryProperty.HOST_VISIBLE_BIT.i, + vertexBufferSize, + model.vertices::buffer, + model.vertices::memory, + vertexBuffer.adr) + // Index buffer + vulkanDevice.createBuffer( + VkBufferUsage.INDEX_BUFFER_BIT.i, + VkMemoryProperty.HOST_VISIBLE_BIT.i, + indexBufferSize, + model.indices::buffer, + model.indices::memory, + indexBuffer.adr) + } + } + + fun loadAssets() { + loadModel("$assetPath/models/voyager/voyager.dae") + val (texture, format) = when { + deviceFeatures.textureCompressionBC -> "voyager_bc3_unorm.ktx" to VkFormat.BC3_UNORM_BLOCK + deviceFeatures.textureCompressionASTC_LDR -> "voyager_astc_8x8_unorm.ktx" to VkFormat.ASTC_8x8_UNORM_BLOCK + deviceFeatures.textureCompressionETC2 -> "voyager_etc2_unorm.ktx" to VkFormat.ETC2_R8G8B8A8_UNORM_BLOCK + else -> tools.exitFatal("Device does not support any compressed texture format!", ERROR_FEATURE_NOT_PRESENT) + } + textures.colorMap.loadFromFile("$assetPath/models/voyager/$texture", format, vulkanDevice, queue) + } + + fun setupVertexDescriptions() { + val vertex = Vertex() + // Binding description + vertices.bindingDescriptions = vk.VertexInputBindingDescription(VERTEX_BUFFER_BIND_ID, vertex.size, VkVertexInputRate.VERTEX) + + // Attribute descriptions + // Describes memory layout and shader positions + vertices.attributeDescriptions = vk.VertexInputAttributeDescription( + // Location 0 : Position + VERTEX_BUFFER_BIND_ID, 0, VkFormat.R32G32B32_SFLOAT, vertex.offsetOf("pos"), + // Location 1 : Normal + VERTEX_BUFFER_BIND_ID, 1, VkFormat.R32G32B32_SFLOAT, vertex.offsetOf("normal"), + // Location 2 : Texture coordinates + VERTEX_BUFFER_BIND_ID, 2, VkFormat.R32G32_SFLOAT, vertex.offsetOf("uv"), + // Location 3 : Color + VERTEX_BUFFER_BIND_ID, 3, VkFormat.R32G32B32_SFLOAT, vertex.offsetOf("color")) + + vertices.inputState = vk.PipelineVertexInputStateCreateInfo { + vertexBindingDescription = vertices.bindingDescriptions + vertexAttributeDescriptions = vertices.attributeDescriptions + } + } + + fun setupDescriptorPool() { + // Example uses one ubo and one combined image sampler + val poolSizes = vk.DescriptorPoolSize( + VkDescriptorType.UNIFORM_BUFFER, 1, + VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1) + + val descriptorPoolInfo = vk.DescriptorPoolCreateInfo(poolSizes, 1) + + descriptorPool = device createDescriptorPool descriptorPoolInfo + } + + fun setupDescriptorSetLayout() { + + val setLayoutBindings = vk.DescriptorSetLayoutBinding( + // Binding 0 : Vertex shader uniform buffer + VkDescriptorType.UNIFORM_BUFFER, VkShaderStage.VERTEX_BIT.i, 0, + // Binding 1 : Fragment shader combined sampler + VkDescriptorType.COMBINED_IMAGE_SAMPLER, VkShaderStage.FRAGMENT_BIT.i, 1) + + val descriptorLayout = vk.DescriptorSetLayoutCreateInfo(setLayoutBindings) + + descriptorSetLayout = device createDescriptorSetLayout descriptorLayout + + val pipelineLayoutCreateInfo = vk.PipelineLayoutCreateInfo(descriptorSetLayout) + + pipelineLayout = device createPipelineLayout pipelineLayoutCreateInfo + } + + fun setupDescriptorSet() { + + val allocInfo = vk.DescriptorSetAllocateInfo(descriptorPool, descriptorSetLayout) + + descriptorSet = device allocateDescriptorSets allocInfo + + val texDescriptor = vk.DescriptorImageInfo(textures.colorMap.sampler, textures.colorMap.view, VkImageLayout.GENERAL) + + val writeDescriptorSets = vk.WriteDescriptorSet( + // Binding 0 : Vertex shader uniform buffer + descriptorSet, VkDescriptorType.UNIFORM_BUFFER, 0, uniformBuffers.scene.descriptor, + // Binding 1 : Color map + descriptorSet, VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1, texDescriptor) + + device updateDescriptorSets writeDescriptorSets + } + + fun preparePipelines() { + + val inputAssemblyState = vk.PipelineInputAssemblyStateCreateInfo(VkPrimitiveTopology.TRIANGLE_LIST, 0, false) + + val rasterizationState = vk.PipelineRasterizationStateCreateInfo(VkPolygonMode.FILL, VkCullMode.BACK_BIT.i, VkFrontFace.CLOCKWISE) + + val blendAttachmentState = vk.PipelineColorBlendAttachmentState(0xf, false) + + val colorBlendState = vk.PipelineColorBlendStateCreateInfo(blendAttachmentState) + + val depthStencilState = vk.PipelineDepthStencilStateCreateInfo(true, true, VkCompareOp.LESS_OR_EQUAL) + + val viewportState = vk.PipelineViewportStateCreateInfo(1, 1) + + val multisampleState = vk.PipelineMultisampleStateCreateInfo(VkSampleCount.`1_BIT`) + + val dynamicStateEnables = listOf(VkDynamicState.VIEWPORT, VkDynamicState.SCISSOR) + val dynamicState = vk.PipelineDynamicStateCreateInfo(dynamicStateEnables) + + // Solid rendering pipeline + // Load shaders + val shaderStages = vk.PipelineShaderStageCreateInfo(2).also { + it[0].loadShader("$assetPath/shaders/mesh/mesh.vert.spv", VkShaderStage.VERTEX_BIT) + it[1].loadShader("$assetPath/shaders/mesh/mesh.frag.spv", VkShaderStage.FRAGMENT_BIT) + } + val pipelineCreateInfo = vk.GraphicsPipelineCreateInfo(pipelineLayout, renderPass).also { + it.vertexInputState = vertices.inputState + it.inputAssemblyState = inputAssemblyState + it.rasterizationState = rasterizationState + it.colorBlendState = colorBlendState + it.multisampleState = multisampleState + it.viewportState = viewportState + it.depthStencilState = depthStencilState + it.dynamicState = dynamicState + it.stages = shaderStages + } + pipelines.solid = device.createGraphicsPipelines(pipelineCache, pipelineCreateInfo) + + // Wire frame rendering pipeline + if (deviceFeatures.fillModeNonSolid) { + rasterizationState.polygonMode = VkPolygonMode.LINE + rasterizationState.lineWidth = 1f + pipelines.wireframe = device.createGraphicsPipelines(pipelineCache, pipelineCreateInfo) + } + } + + /** Prepare and initialize uniform buffer containing shader uniforms */ + fun prepareUniformBuffers() { + // Vertex shader uniform buffer block + vulkanDevice.createBuffer( + VkBufferUsage.UNIFORM_BUFFER_BIT.i, + VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, + uniformBuffers.scene, + VkDeviceSize(uboVS.size.L)) + + // Map persistent + uniformBuffers.scene.map() + + updateUniformBuffers() + } + + fun updateUniformBuffers() { + + uboVS.projection = glm.perspective(60f.rad, size.aspect, 0.1f, 256f) + val viewMatrix = glm.translate(Mat4(1f), Vec3(0f, 0f, zoom)) + + uboVS.model = viewMatrix * glm.translate(Mat4(1f), cameraPos) + .rotateAssign(rotation.x.rad, 1f, 0f, 0f) + .rotateAssign(rotation.y.rad, 0f, 1f, 0f) + .rotateAssign(rotation.z.rad, 0f, 0f, 1f) + + uboVS to uniformBuffers.scene.mapped + } + + fun draw() { + + super.prepareFrame() + + // Command buffer to be sumitted to the queue + submitInfo.commandBuffer = drawCmdBuffers[currentBuffer] + + // Submit to queue + queue submit submitInfo + + super.submitFrame() + } + + override fun prepare() { + super.prepare() + loadAssets() + setupVertexDescriptions() + prepareUniformBuffers() + setupDescriptorSetLayout() + preparePipelines() + setupDescriptorPool() + setupDescriptorSet() + buildCommandBuffers() + prepared = true + window.show() + } + + override fun render() { + if (!prepared) + return + draw() + } + + override fun viewChanged() = updateUniformBuffers() + +// virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) +// { +// if (overlay->header("Settings")) { +// if (overlay->checkBox("Wireframe", &wireframe)) { // buildCommandBuffers() -// prepared = true -// window.show() // } -// -// override fun render() { -// if (!prepared) -// return -// draw() // } -// -// override fun viewChanged() = updateUniformBuffers() -// -//// virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) -//// { -//// if (overlay->header("Settings")) { -//// if (overlay->checkBox("Wireframe", &wireframe)) { -//// buildCommandBuffers() -//// } -//// } -//// } -//} \ No newline at end of file +// } +} \ No newline at end of file diff --git a/src/main/kotlin/vulkan/basics/12 Subpasses.kt b/src/main/kotlin/vulkan/basics/12 Subpasses.kt index eee0ec1..a5071f6 100644 --- a/src/main/kotlin/vulkan/basics/12 Subpasses.kt +++ b/src/main/kotlin/vulkan/basics/12 Subpasses.kt @@ -1,914 +1,913 @@ -///* -//* Vulkan Example - Using subpasses for G-Buffer compositing -//* -//* Copyright (C) 2016-2017 by Sascha Willems - www.saschawillems.de -//* -//* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -//* -//* Summary: -//* Implements a deferred rendering setup with a forward transparency pass using sub passes -//* -//* Sub passes allow reading from the previous framebuffer (in the same render pass) at -//* the same pixel position. -//* -//* This is a feature that was especially designed for tile-based-renderers -//* (mostly mobile GPUs) and is a new optomization feature in Vulkan for those GPU types. -//* -//*/ -// -//package vulkan.basics -// -//import glm_.BYTES -//import glm_.L -//import glm_.glm -//import glm_.mat4x4.Mat4 -//import glm_.vec3.Vec3 -//import glm_.vec4.Vec4 -//import kool.stak -//import org.lwjgl.system.MemoryUtil.NULL -//import org.lwjgl.vulkan.VK10.VK_SUBPASS_EXTERNAL -//import org.lwjgl.vulkan.VkPipelineVertexInputStateCreateInfo -//import org.lwjgl.vulkan.VkVertexInputAttributeDescription -//import org.lwjgl.vulkan.VkVertexInputBindingDescription -//import vkk.* -//import vulkan.NUM_LIGHTS -//import vulkan.VERTEX_BUFFER_BIND_ID -//import vulkan.assetPath -//import vulkan.base.* -//import java.lang.Math.abs -// -// -//fun main(args: Array) { -// Subpasses().apply { -// setupWindow() -// initVulkan() -// prepare() -// renderLoop() -// destroy() -// } -//} -// -//private class Subpasses : VulkanExampleBase() { -// -// object textures { -// val glass = Texture2D() -// } -// -// // Vertex layout for the models -// val vertexLayout = VertexLayout( -// VertexComponent.POSITION, -// VertexComponent.COLOR, -// VertexComponent.NORMAL, -// VertexComponent.UV) -// -// object models { -// val scene = Model() -// val transparent = Model() -// } -// -// object vertices { -// lateinit var inputState: VkPipelineVertexInputStateCreateInfo -// lateinit var bindingDescriptions: VkVertexInputBindingDescription -// lateinit var attributeDescriptions: VkVertexInputAttributeDescription.Buffer -// } -// -// object uboGBuffer : Bufferizable() { -// lateinit var projection: Mat4 -// @Order(1) -// lateinit var model: Mat4 -// lateinit var view: Mat4 -// } -// -// class Light { -// lateinit var position: Vec4 -// lateinit var color: Vec3 -// var radius = 0f -// } -// -// object uboLights : Bufferizable() { -// lateinit var viewPos: Vec4 -// val lights = Array(NUM_LIGHTS) { Light() } -// } -// -// object uniformBuffers { -// val GBuffer = Buffer() -// val lights = Buffer() -// } -// -// object pipelines { -// var offscreen = VkPipeline(NULL) -// var composition = VkPipeline(NULL) -// var transparent = VkPipeline(NULL) -// } -// -// object pipelineLayouts { -// var offscreen = VkPipelineLayout(NULL) -// var composition = VkPipelineLayout(NULL) -// var transparent = VkPipelineLayout(NULL) -// } -// -// object descriptorSets { -// var scene = VkDescriptorSet(NULL) -// var composition = VkDescriptorSet(NULL) -// var transparent = VkDescriptorSet(NULL) -// } -// -// object descriptorSetLayouts { -// var scene = VkDescriptorSetLayout(NULL) -// var composition = VkDescriptorSetLayout(NULL) -// var transparent = VkDescriptorSetLayout(NULL) -// } -// -// // G-Buffer framebuffer attachments -// class FrameBufferAttachment { -// var image = VkImage(NULL) -// var mem = VkDeviceMemory(0) -// var view = VkImageView(NULL) -// var format = VkFormat.UNDEFINED -// } -// -// object attachments { -// val position = FrameBufferAttachment() -// val normal = FrameBufferAttachment() -// val albedo = FrameBufferAttachment() -// } -// -// init { -// title = "Subpasses" -// camera.type = Camera.CameraType.firstPerson -// camera.movementSpeed = 5f -// camera.setPosition(Vec3(-3.2f, 1f, 5.9f)) -// camera.setRotation(Vec3(0.5f, 210.05f, 0f)) -// camera.setPerspective(60f, size.aspect, 0.1f, 256f) -// settings.overlay = false // TODO -// } -// -// override fun destroy() { -// -// device.apply { -// // Clean up used Vulkan resources -// // Note : Inherited destructor cleans up resources stored in base class -// -// destroyImageView(attachments.position.view) -// destroyImage(attachments.position.image) -// freeMemory(attachments.position.mem) -// -// destroyImageView(attachments.normal.view) -// destroyImage(attachments.normal.image) -// freeMemory(attachments.normal.mem) -// -// destroyImageView(attachments.albedo.view) -// destroyImage(attachments.albedo.image) -// freeMemory(attachments.albedo.mem) -// -// destroyPipeline(pipelines.offscreen) -// destroyPipeline(pipelines.composition) -// destroyPipeline(pipelines.transparent) -// -// destroyPipelineLayout(pipelineLayouts.offscreen) -// destroyPipelineLayout(pipelineLayouts.composition) -// destroyPipelineLayout(pipelineLayouts.transparent) -// -// destroyDescriptorSetLayout(descriptorSetLayouts.scene) -// destroyDescriptorSetLayout(descriptorSetLayouts.composition) -// destroyDescriptorSetLayout(descriptorSetLayouts.transparent) -// } -// textures.glass.destroy() -// models.scene.destroy() -// models.transparent.destroy() -// uniformBuffers.GBuffer.destroy() -// uniformBuffers.lights.destroy() -// -// super.destroy() -// } -// -// /** Enable physical device features required for this example */ -// override fun getEnabledFeatures() { -// // Enable anisotropic filtering if supported -// if (deviceFeatures.samplerAnisotropy) -// enabledFeatures.samplerAnisotropy = true -// // Enable texture compression -// when { -// deviceFeatures.textureCompressionBC -> enabledFeatures.textureCompressionBC = true -// deviceFeatures.textureCompressionASTC_LDR -> enabledFeatures.textureCompressionASTC_LDR = true -// deviceFeatures.textureCompressionETC2 -> enabledFeatures.textureCompressionETC2 = true -// } -// } -// -// /** Create a frame buffer attachment */ -// fun createAttachment(format: VkFormat, usage: VkImageUsageFlags, attachment: FrameBufferAttachment) { -// -// var aspectMask: VkImageAspectFlags = 0 -// var imageLayout = VkImageLayout.UNDEFINED -// -// attachment.format = format -// -// if (usage has VkImageUsage.COLOR_ATTACHMENT_BIT) { -// aspectMask = VkImageAspect.COLOR_BIT.i -// imageLayout = VkImageLayout.COLOR_ATTACHMENT_OPTIMAL -// } -// if (usage has VkImageUsage.DEPTH_STENCIL_ATTACHMENT_BIT) { -// aspectMask = VkImageAspect.DEPTH_BIT or VkImageAspect.STENCIL_BIT -// imageLayout = VkImageLayout.DEPTH_STENCIL_ATTACHMENT_OPTIMAL -// } -// -// assert(aspectMask > 0) -// -// val image = vk.ImageCreateInfo { -// imageType = VkImageType.`2D` -// this.format = format -// extent(size, 1) -// mipLevels = 1 -// arrayLayers = 1 -// samples = VkSampleCount.`1_BIT` -// tiling = VkImageTiling.OPTIMAL -// this.usage = usage or VkImageUsage.INPUT_ATTACHMENT_BIT // VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT flag is required for input attachments; -// initialLayout = VkImageLayout.UNDEFINED -// } -// -// attachment.image = device createImage image -// val memReqs = device getImageMemoryRequirements attachment.image -// val memAlloc = vk.MemoryAllocateInfo { -// allocationSize = memReqs.size -// memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryProperty.DEVICE_LOCAL_BIT) -// } -// attachment.mem = device allocateMemory memAlloc -// device.bindImageMemory(attachment.image, attachment.mem) -// -// val imageView = vk.ImageViewCreateInfo { -// viewType = VkImageViewType.`2D` -// this.format = format -// subresourceRange.apply { -// this.aspectMask = aspectMask -// baseMipLevel = 0 -// levelCount = 1 -// baseArrayLayer = 0 -// layerCount = 1 -// } -// this.image = attachment.image -// } -// attachment.view = device createImageView imageView -// } -// -// /** Create color attachments for the G-Buffer components */ -// fun createGBufferAttachments() { -// createAttachment(VkFormat.R16G16B16A16_SFLOAT, VkImageUsage.COLOR_ATTACHMENT_BIT.i, attachments.position) // (World space) Positions -// createAttachment(VkFormat.R16G16B16A16_SFLOAT, VkImageUsage.COLOR_ATTACHMENT_BIT.i, attachments.normal) // (World space) Normals -// createAttachment(VkFormat.R8G8B8A8_UNORM, VkImageUsage.COLOR_ATTACHMENT_BIT.i, attachments.albedo) // Albedo (color) -// } -// -// /** Override framebuffer setup from base class -// * Deferred components will be used as frame buffer attachments */ -// override fun setupFrameBuffer() = stak { -// -// val attachments = it.vkImageViewBufferBig(5) -// -// val frameBufferCreateInfo = vk.FramebufferCreateInfo { -// renderPass = this@Subpasses.renderPass -// this.attachments = attachments -// width = size.x -// height = size.y -// layers = 1 -// } -// // Create frame buffers for every swap chain image -// frameBuffers = initVkFramebufferArray(swapChain.imageCount) { i -> -// attachments[0] = swapChain.buffers[i].view // TODO put(vararg)? -// attachments[1] = Subpasses.attachments.position.view -// attachments[2] = Subpasses.attachments.normal.view -// attachments[3] = Subpasses.attachments.albedo.view -// attachments[4] = depthStencil.view -// device createFramebuffer frameBufferCreateInfo -// } -// } -// -// /** Override render pass setup from base class */ -// override fun setupRenderPass() { -// -// createGBufferAttachments() -// -// val attachments = vk.AttachmentDescription(5).also { -// // Color attachment -// it[0].apply { -// format = swapChain.colorFormat -// samples = VkSampleCount.`1_BIT` -// loadOp = VkAttachmentLoadOp.CLEAR -// storeOp = VkAttachmentStoreOp.STORE -// stencilLoadOp = VkAttachmentLoadOp.DONT_CARE -// stencilStoreOp = VkAttachmentStoreOp.DONT_CARE -// initialLayout = VkImageLayout.UNDEFINED -// finalLayout = VkImageLayout.PRESENT_SRC_KHR -// } -// // Deferred attachments -// // Position -// it[1].apply { -// format = Subpasses.attachments.position.format -// samples = VkSampleCount.`1_BIT` -// loadOp = VkAttachmentLoadOp.CLEAR -// storeOp = VkAttachmentStoreOp.DONT_CARE -// stencilLoadOp = VkAttachmentLoadOp.DONT_CARE -// stencilStoreOp = VkAttachmentStoreOp.DONT_CARE -// initialLayout = VkImageLayout.UNDEFINED -// finalLayout = VkImageLayout.COLOR_ATTACHMENT_OPTIMAL -// } -// // Normals -// it[2].apply { -// format = Subpasses.attachments.normal.format -// samples = VkSampleCount.`1_BIT` -// loadOp = VkAttachmentLoadOp.CLEAR -// storeOp = VkAttachmentStoreOp.DONT_CARE -// stencilLoadOp = VkAttachmentLoadOp.DONT_CARE -// stencilStoreOp = VkAttachmentStoreOp.DONT_CARE -// initialLayout = VkImageLayout.UNDEFINED -// finalLayout = VkImageLayout.COLOR_ATTACHMENT_OPTIMAL -// } -// // Albedo -// it[3].apply { -// format = Subpasses.attachments.albedo.format -// samples = VkSampleCount.`1_BIT` -// loadOp = VkAttachmentLoadOp.CLEAR -// storeOp = VkAttachmentStoreOp.DONT_CARE -// stencilLoadOp = VkAttachmentLoadOp.DONT_CARE -// stencilStoreOp = VkAttachmentStoreOp.DONT_CARE -// initialLayout = VkImageLayout.UNDEFINED -// finalLayout = VkImageLayout.COLOR_ATTACHMENT_OPTIMAL -// } -// // Depth attachment -// it[4].apply { -// format = depthFormat -// samples = VkSampleCount.`1_BIT` -// loadOp = VkAttachmentLoadOp.CLEAR -// storeOp = VkAttachmentStoreOp.DONT_CARE -// stencilLoadOp = VkAttachmentLoadOp.DONT_CARE -// stencilStoreOp = VkAttachmentStoreOp.DONT_CARE -// initialLayout = VkImageLayout.UNDEFINED -// finalLayout = VkImageLayout.DEPTH_STENCIL_ATTACHMENT_OPTIMAL -// } -// } -// // Three subpasses -// val subpassDescriptions = vk.SubpassDescription(3) -// -// // First subpass: Fill G-Buffer components -// // ---------------------------------------------------------------------------------------- -// -// val colorReferences = vk.AttachmentReference( -// 0, VkImageLayout.COLOR_ATTACHMENT_OPTIMAL, -// 1, VkImageLayout.COLOR_ATTACHMENT_OPTIMAL, -// 2, VkImageLayout.COLOR_ATTACHMENT_OPTIMAL, -// 3, VkImageLayout.COLOR_ATTACHMENT_OPTIMAL) -// val depthReference = vk.AttachmentReference(4, VkImageLayout.DEPTH_STENCIL_ATTACHMENT_OPTIMAL) -// -// subpassDescriptions[0].apply { -// pipelineBindPoint = VkPipelineBindPoint.GRAPHICS -// colorAttachmentCount = 4 -// colorAttachments = colorReferences -// depthStencilAttachment = depthReference -// } -// // Second subpass: Final composition (using G-Buffer components) -// // ---------------------------------------------------------------------------------------- -// -// val colorReference = vk.AttachmentReference(0, VkImageLayout.COLOR_ATTACHMENT_OPTIMAL) -// -// val inputReferences = vk.AttachmentReference( -// 1, VkImageLayout.SHADER_READ_ONLY_OPTIMAL, -// 2, VkImageLayout.SHADER_READ_ONLY_OPTIMAL, -// 3, VkImageLayout.SHADER_READ_ONLY_OPTIMAL) -// -// subpassDescriptions[1].apply { -// pipelineBindPoint = VkPipelineBindPoint.GRAPHICS -// colorAttachmentCount = 1 -// colorAttachment = colorReference -// depthStencilAttachment = depthReference -// // Use the color attachments filled in the first pass as input attachments -// inputAttachments = inputReferences -// } -// // Third subpass: Forward transparency -// // ---------------------------------------------------------------------------------------- -// colorReference(0, VkImageLayout.COLOR_ATTACHMENT_OPTIMAL) -// -// inputReferences[0](1, VkImageLayout.SHADER_READ_ONLY_OPTIMAL) -// -// subpassDescriptions[2].apply { -// pipelineBindPoint = VkPipelineBindPoint.GRAPHICS -// colorAttachmentCount = 1 -// colorAttachment = colorReference -// depthStencilAttachment = depthReference -// // Use the color/depth attachments filled in the first pass as input attachments -// inputAttachments = inputReferences -// } -// // Subpass dependencies for layout transitions -// val dependencies = vk.SubpassDependency(4).also { -// it[0].apply { -// srcSubpass = VK_SUBPASS_EXTERNAL -// dstSubpass = 0 -// srcStageMask = VkPipelineStage.BOTTOM_OF_PIPE_BIT.i -// dstStageMask = VkPipelineStage.COLOR_ATTACHMENT_OUTPUT_BIT.i -// srcAccessMask = VkAccess.MEMORY_READ_BIT.i -// dstAccessMask = VkAccess.COLOR_ATTACHMENT_READ_BIT or VkAccess.COLOR_ATTACHMENT_WRITE_BIT -// dependencyFlags = VkDependency.BY_REGION_BIT.i -// } -// // This dependency transitions the input attachment from color attachment to shader read -// it[1].apply { -// srcSubpass = 0 -// dstSubpass = 1 -// srcStageMask = VkPipelineStage.COLOR_ATTACHMENT_OUTPUT_BIT.i -// dstStageMask = VkPipelineStage.FRAGMENT_SHADER_BIT.i -// srcAccessMask = VkAccess.COLOR_ATTACHMENT_WRITE_BIT.i -// dstAccessMask = VkAccess.SHADER_READ_BIT.i -// dependencyFlags = VkDependency.BY_REGION_BIT.i -// } -// it[2].apply { -// srcSubpass = 1 -// dstSubpass = 2 -// srcStageMask = VkPipelineStage.COLOR_ATTACHMENT_OUTPUT_BIT.i -// dstStageMask = VkPipelineStage.FRAGMENT_SHADER_BIT.i -// srcAccessMask = VkAccess.COLOR_ATTACHMENT_WRITE_BIT.i -// dstAccessMask = VkAccess.SHADER_READ_BIT.i -// dependencyFlags = VkDependency.BY_REGION_BIT.i -// } -// it[3].apply { -// srcSubpass = 0 -// dstSubpass = VK_SUBPASS_EXTERNAL -// srcStageMask = VkPipelineStage.COLOR_ATTACHMENT_OUTPUT_BIT.i -// dstStageMask = VkPipelineStage.BOTTOM_OF_PIPE_BIT.i -// srcAccessMask = VkAccess.COLOR_ATTACHMENT_READ_BIT or VkAccess.COLOR_ATTACHMENT_WRITE_BIT -// dstAccessMask = VkAccess.MEMORY_READ_BIT.i -// dependencyFlags = VkDependency.BY_REGION_BIT.i -// } -// } -// val renderPassInfo = vk.RenderPassCreateInfo { -// this.attachments = attachments -// subpasses = subpassDescriptions -// this.dependencies = dependencies -// } -// renderPass = device createRenderPass renderPassInfo -// } -// -// override fun buildCommandBuffers() { -// -// val cmdBufInfo = vk.CommandBufferBeginInfo() -// -// val clearValues = vk.ClearValue(5).also { -// it[0].color(0f, 0f, 0f, 0f) -// it[1].color(0f, 0f, 0f, 0f) -// it[2].color(0f, 0f, 0f, 0f) -// it[3].color(0f, 0f, 0f, 0f) -// it[4].depthStencil(1f, 0) -// } -// val renderPassBeginInfo = vk.RenderPassBeginInfo { -// renderPass = this@Subpasses.renderPass -// renderArea.offset(0) -// renderArea.extent(size) -// this.clearValues = clearValues -// } -// for (i in drawCmdBuffers.indices) { -// // Set target frame buffer -// renderPassBeginInfo.framebuffer(frameBuffers[i].L) // TODO BUG -// -// drawCmdBuffers[i].apply { -// -// begin(cmdBufInfo) -// -// beginRenderPass(renderPassBeginInfo, VkSubpassContents.INLINE) -// -// setViewport(size) -// setScissor(size) -// -// // First sub pass -// // Renders the components of the scene to the G-Buffer atttachments -// debugMarker.withRegion(this, "Subpass 0: Deferred G-Buffer creation", Vec4(1f)) { -// -// bindPipeline(VkPipelineBindPoint.GRAPHICS, pipelines.offscreen) -// bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, pipelineLayouts.offscreen, descriptorSets.scene) -// bindVertexBuffers(VERTEX_BUFFER_BIND_ID, models.scene.vertices.buffer) -// bindIndexBuffer(models.scene.indices.buffer, VkDeviceSize(0), VkIndexType.UINT32) -// drawIndexed(models.scene.indexCount, 1, 0, 0, 0) -// } -// -// // Second sub pass -// // This subpass will use the G-Buffer components that have been filled in the first subpass as input attachment for the final compositing -// debugMarker.withRegion(this, "Subpass 1: Deferred composition", Vec4(1f)) { -// -// nextSubpass(VkSubpassContents.INLINE) -// -// bindPipeline(VkPipelineBindPoint.GRAPHICS, pipelines.composition) -// bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, pipelineLayouts.composition, descriptorSets.composition) -// draw(3, 1, 0, 0) -// } -// -// // Third subpass -// // Render transparent geometry using a forward pass that compares against depth generted during G-Buffer fill -// debugMarker.withRegion(this, "Subpass 2: Forward transparency", Vec4(1f)) { -// -// nextSubpass(VkSubpassContents.INLINE) -// -// bindPipeline(VkPipelineBindPoint.GRAPHICS, pipelines.transparent) -// bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, pipelineLayouts.transparent, descriptorSets.transparent) -// bindVertexBuffers(VERTEX_BUFFER_BIND_ID, models.transparent.vertices.buffer) -// bindIndexBuffer(models.transparent.indices.buffer, VkDeviceSize(0), VkIndexType.UINT32) -// drawIndexed(models.transparent.indexCount, 1, 0, 0, 0) -// } -// Create custom overlay render pass -// -// endRenderPass() -// -// end() -// } -// } -// } -// -// fun loadAssets() { -// -// models.scene.loadFromFile("$assetPath/models/samplebuilding.dae", vertexLayout, 1f, vulkanDevice, queue) -// models.transparent.loadFromFile("$assetPath/models/samplebuilding_glass.dae", vertexLayout, 1f, vulkanDevice, queue) -// // Textures -// val (text, format) = when { -// vulkanDevice.features.textureCompressionBC -> "bc3_unorm.ktx" to VkFormat.BC3_UNORM_BLOCK -// vulkanDevice.features.textureCompressionASTC_LDR -> "astc_8x8_unorm.ktx" to VkFormat.ASTC_8x8_UNORM_BLOCK -// vulkanDevice.features.textureCompressionETC2 -> "etc2_unorm.ktx" to VkFormat.ETC2_R8G8B8A8_UNORM_BLOCK -// else -> tools.exitFatal("Device does not support any compressed texture format!", ERROR_FEATURE_NOT_PRESENT) -// } -// textures.glass.loadFromFile("$assetPath/textures/colored_glass_$text", format, vulkanDevice, queue) -// } -// -// fun setupVertexDescriptions() { -// // Binding description -// vertices.bindingDescriptions = vk.VertexInputBindingDescription(VERTEX_BUFFER_BIND_ID, vertexLayout.stride, VkVertexInputRate.VERTEX) -// -// // Attribute descriptions -// vertices.attributeDescriptions = vk.VertexInputAttributeDescription( -// // Location 0: Position -// VERTEX_BUFFER_BIND_ID, 0, VkFormat.R32G32B32_SFLOAT, 0, -// // Location 1: Color -// VERTEX_BUFFER_BIND_ID, 1, VkFormat.R32G32B32_SFLOAT, Vec3.size, -// // Location 2: Normal -// VERTEX_BUFFER_BIND_ID, 2, VkFormat.R32G32B32_SFLOAT, Vec3.size * 2, -// // Location 3: UV -// VERTEX_BUFFER_BIND_ID, 3, VkFormat.R32G32_SFLOAT, Vec3.size * 3) -// -// vertices.inputState = vk.PipelineVertexInputStateCreateInfo { -// vertexBindingDescription = vertices.bindingDescriptions -// vertexAttributeDescriptions = vertices.attributeDescriptions -// } -// } -// -// fun setupDescriptorPool() { -// -// val poolSizes = vk.DescriptorPoolSize( -// VkDescriptorType.UNIFORM_BUFFER, 4, -// VkDescriptorType.COMBINED_IMAGE_SAMPLER, 4, -// VkDescriptorType.INPUT_ATTACHMENT, 4) -// -// val descriptorPoolInfo = vk.DescriptorPoolCreateInfo(poolSizes, 4) -// -// descriptorPool = device createDescriptorPool descriptorPoolInfo -// } -// -// fun setupDescriptorSetLayout() { -// // Deferred shading layout -// val setLayoutBindings = vk.DescriptorSetLayoutBinding( -// // Binding 0 : Vertex shader uniform buffer -// VkDescriptorType.UNIFORM_BUFFER, VkShaderStage.VERTEX_BIT.i, 0) -// -// val descriptorLayout = vk.DescriptorSetLayoutCreateInfo(setLayoutBindings) -// -// descriptorSetLayouts.scene = device createDescriptorSetLayout descriptorLayout -// -// val pipelineLayoutCreateInfo = vk.PipelineLayoutCreateInfo(descriptorSetLayouts.scene) -// -// // Offscreen (scene) rendering pipeline layout -// pipelineLayouts.offscreen = device createPipelineLayout pipelineLayoutCreateInfo -// } -// -// fun setupDescriptorSet() { -// -// val allocInfo = vk.DescriptorSetAllocateInfo(descriptorPool, descriptorSetLayouts.scene) -// -// descriptorSets.scene = device allocateDescriptorSets allocInfo -// -// val writeDescriptorSets = vk.WriteDescriptorSet( -// // Binding 0: Vertex shader uniform buffer -// descriptorSets.scene, VkDescriptorType.UNIFORM_BUFFER, 0, uniformBuffers.GBuffer.descriptor) -// -// device updateDescriptorSets writeDescriptorSets -// } -// -// fun preparePipelines() { -// -// val inputAssemblyState = vk.PipelineInputAssemblyStateCreateInfo(VkPrimitiveTopology.TRIANGLE_LIST, 0, false) -// -// val rasterizationState = vk.PipelineRasterizationStateCreateInfo(VkPolygonMode.FILL, VkCullMode.BACK_BIT.i, VkFrontFace.CLOCKWISE) -// -// val blendAttachmentState = vk.PipelineColorBlendAttachmentState(0xf, false) -// -// val colorBlendState = vk.PipelineColorBlendStateCreateInfo(blendAttachmentState) -// -// val depthStencilState = vk.PipelineDepthStencilStateCreateInfo(true, true, VkCompareOp.LESS_OR_EQUAL) -// -// val viewportState = vk.PipelineViewportStateCreateInfo(1, 1) -// -// val multisampleState = vk.PipelineMultisampleStateCreateInfo(VkSampleCount.`1_BIT`) -// -// val dynamicStateEnables = listOf(VkDynamicState.VIEWPORT, VkDynamicState.SCISSOR) -// val dynamicState = vk.PipelineDynamicStateCreateInfo(dynamicStateEnables) -// -// // Final fullscreen pass pipeline -// val shaderStages = vk.PipelineShaderStageCreateInfo(2) -// -// val pipelineCreateInfo = vk.GraphicsPipelineCreateInfo(pipelineLayouts.offscreen, renderPass).also { -// it.vertexInputState = vertices.inputState -// it.inputAssemblyState = inputAssemblyState -// it.rasterizationState = rasterizationState -// it.colorBlendState = colorBlendState -// it.multisampleState = multisampleState -// it.viewportState = viewportState -// it.depthStencilState = depthStencilState -// it.dynamicState = dynamicState -// it.stages = shaderStages -// it.subpass = 0 -// } -// val blendAttachmentStates = vk.PipelineColorBlendAttachmentState( -// 0xf, false, -// 0xf, false, -// 0xf, false, -// 0xf, false) -// -// colorBlendState.attachments = blendAttachmentStates -// -// // Offscreen scene rendering pipeline -// shaderStages[0].loadShader("$assetPath/shaders/subpasses/gbuffer.vert.spv", VkShaderStage.VERTEX_BIT) -// shaderStages[1].loadShader("$assetPath/shaders/subpasses/gbuffer.frag.spv", VkShaderStage.FRAGMENT_BIT) -// -// pipelines.offscreen = device.createGraphicsPipelines(pipelineCache, pipelineCreateInfo) -// } -// -// /** Create the Vulkan objects used in the composition pass (descriptor sets, pipelines, etc.) */ -// fun prepareCompositionPass() = stak { -// // Descriptor set layout -// var setLayoutBindings = vk.DescriptorSetLayoutBinding( -// // Binding 0: Position input attachment -// VkDescriptorType.INPUT_ATTACHMENT, VkShaderStage.FRAGMENT_BIT.i, 0, -// // Binding 1: Normal input attachment -// VkDescriptorType.INPUT_ATTACHMENT, VkShaderStage.FRAGMENT_BIT.i, 1, -// // Binding 2: Albedo input attachment -// VkDescriptorType.INPUT_ATTACHMENT, VkShaderStage.FRAGMENT_BIT.i, 2, -// // Binding 3: Light positions -// VkDescriptorType.UNIFORM_BUFFER, VkShaderStage.FRAGMENT_BIT.i, 3) -// -// var descriptorLayout = vk.DescriptorSetLayoutCreateInfo(setLayoutBindings) -// -// descriptorSetLayouts.composition = device createDescriptorSetLayout descriptorLayout -// -// // Pipeline layout -// var pipelineLayoutCreateInfo = vk.PipelineLayoutCreateInfo(descriptorSetLayouts.composition) -// -// pipelineLayouts.composition = device createPipelineLayout pipelineLayoutCreateInfo -// -// // Descriptor sets -// var allocInfo = vk.DescriptorSetAllocateInfo(descriptorPool, descriptorSetLayouts.composition) -// -// descriptorSets.composition = device allocateDescriptorSets allocInfo -// -// // Image descriptors for the offscreen color attachments -// val nullSampler = VkSampler(NULL) -// -// val texDescriptorPosition = vk.DescriptorImageInfo(nullSampler, attachments.position.view, VkImageLayout.SHADER_READ_ONLY_OPTIMAL) -// -// val texDescriptorNormal = vk.DescriptorImageInfo(nullSampler, attachments.normal.view, VkImageLayout.SHADER_READ_ONLY_OPTIMAL) -// -// val texDescriptorAlbedo = vk.DescriptorImageInfo(nullSampler, attachments.albedo.view, VkImageLayout.SHADER_READ_ONLY_OPTIMAL) -// -// var writeDescriptorSets = vk.WriteDescriptorSet( -// // Binding 0: Position texture target -// descriptorSets.composition, VkDescriptorType.INPUT_ATTACHMENT, 0, texDescriptorPosition, -// // Binding 1: Normals texture target -// descriptorSets.composition, VkDescriptorType.INPUT_ATTACHMENT, 1, texDescriptorNormal, -// // Binding 2: Albedo texture target -// descriptorSets.composition, VkDescriptorType.INPUT_ATTACHMENT, 2, texDescriptorAlbedo, -// // Binding 4: Fragment shader lights -// descriptorSets.composition, VkDescriptorType.UNIFORM_BUFFER, 3, uniformBuffers.lights.descriptor) -// -// device updateDescriptorSets writeDescriptorSets -// -// // Pipeline -// val inputAssemblyState = vk.PipelineInputAssemblyStateCreateInfo(VkPrimitiveTopology.TRIANGLE_LIST, 0, false) -// -// val rasterizationState = vk.PipelineRasterizationStateCreateInfo(VkPolygonMode.FILL, VkCullMode.NONE.i, VkFrontFace.CLOCKWISE) -// -// val blendAttachmentState = vk.PipelineColorBlendAttachmentState(0xf, false) -// -// val colorBlendState = vk.PipelineColorBlendStateCreateInfo(blendAttachmentState) -// -// val depthStencilState = vk.PipelineDepthStencilStateCreateInfo(true, true, VkCompareOp.LESS_OR_EQUAL) -// -// val viewportState = vk.PipelineViewportStateCreateInfo(1, 1) -// -// val multisampleState = vk.PipelineMultisampleStateCreateInfo(VkSampleCount.`1_BIT`) -// -// val dynamicStateEnables = listOf(VkDynamicState.VIEWPORT, VkDynamicState.SCISSOR) -// val dynamicState = vk.PipelineDynamicStateCreateInfo(dynamicStateEnables) -// -// val shaderStages = vk.PipelineShaderStageCreateInfo(2).also { -// it[0].loadShader("$assetPath/shaders/subpasses/composition.vert.spv", VkShaderStage.VERTEX_BIT) -// it[1].loadShader("$assetPath/shaders/subpasses/composition.frag.spv", VkShaderStage.FRAGMENT_BIT) -// } -// -// // Use specialization constants to pass number of lights to the shader -// val specializationEntry = vk.SpecializationMapEntry { -// constantId = 0 -// offset = 0 -// size = Int.BYTES.L -// } -// val specializationData = it.malloc(Int.BYTES).apply { putInt(0, NUM_LIGHTS) } -// -// val specializationInfo = vk.SpecializationInfo { -// mapEntry = specializationEntry -// data = specializationData -// } -// shaderStages[1].specializationInfo = specializationInfo -// -// val pipelineCreateInfo = vk.GraphicsPipelineCreateInfo(pipelineLayouts.composition, renderPass).also { -// -// val emptyInputState = vk.PipelineVertexInputStateCreateInfo() -// -// it.vertexInputState = emptyInputState -// it.inputAssemblyState = inputAssemblyState -// it.rasterizationState = rasterizationState -// it.colorBlendState = colorBlendState -// it.multisampleState = multisampleState -// it.viewportState = viewportState -// it.depthStencilState = depthStencilState -// it.dynamicState = dynamicState -// it.stages = shaderStages -// // Index of the subpass that this pipeline will be used in -// it.subpass = 1 -// } -// depthStencilState.depthWriteEnable = false -// -// pipelines.composition = device.createGraphicsPipelines(pipelineCache, pipelineCreateInfo) -// -// // Transparent (forward) pipeline -// -// // Descriptor set layout -// setLayoutBindings = vk.DescriptorSetLayoutBinding( -// VkDescriptorType.UNIFORM_BUFFER, VkShaderStage.VERTEX_BIT.i, 0, -// VkDescriptorType.INPUT_ATTACHMENT, VkShaderStage.FRAGMENT_BIT.i, 1, -// VkDescriptorType.COMBINED_IMAGE_SAMPLER, VkShaderStage.FRAGMENT_BIT.i, 2) -// -// descriptorLayout = vk.DescriptorSetLayoutCreateInfo(setLayoutBindings) -// descriptorSetLayouts.transparent = device createDescriptorSetLayout descriptorLayout -// -// // Pipeline layout -// pipelineLayoutCreateInfo = vk.PipelineLayoutCreateInfo(descriptorSetLayouts.transparent) -// pipelineLayouts.transparent = device createPipelineLayout pipelineLayoutCreateInfo -// -// // Descriptor sets -// allocInfo = vk.DescriptorSetAllocateInfo(descriptorPool, descriptorSetLayouts.transparent) -// descriptorSets.transparent = device allocateDescriptorSets allocInfo -// -// writeDescriptorSets = vk.WriteDescriptorSet( -// descriptorSets.transparent, VkDescriptorType.UNIFORM_BUFFER, 0, uniformBuffers.GBuffer.descriptor, -// descriptorSets.transparent, VkDescriptorType.INPUT_ATTACHMENT, 1, texDescriptorPosition, -// descriptorSets.transparent, VkDescriptorType.COMBINED_IMAGE_SAMPLER, 2, textures.glass.descriptor) -// device updateDescriptorSets writeDescriptorSets -// -// // Enable blending -// blendAttachmentState.apply { -// blendEnable = true -// srcColorBlendFactor = VkBlendFactor.SRC_ALPHA -// dstColorBlendFactor = VkBlendFactor.ONE_MINUS_SRC_ALPHA -// colorBlendOp = VkBlendOp.ADD -// srcAlphaBlendFactor = VkBlendFactor.ONE -// dstAlphaBlendFactor = VkBlendFactor.ZERO -// alphaBlendOp = VkBlendOp.ADD -// colorWriteMask = VkColorComponent.R_BIT or VkColorComponent.G_BIT or VkColorComponent.B_BIT or VkColorComponent.A_BIT -// } -// pipelineCreateInfo.apply { -// vertexInputState = vertices.inputState -// layout = pipelineLayouts.transparent -// subpass = 2 -// } -// shaderStages[0].loadShader("$assetPath/shaders/subpasses/transparent.vert.spv", VkShaderStage.VERTEX_BIT) -// shaderStages[1].loadShader("$assetPath/shaders/subpasses/transparent.frag.spv", VkShaderStage.FRAGMENT_BIT) -// -// pipelines.transparent = device.createGraphicsPipelines(pipelineCache, pipelineCreateInfo) -// } -// -// /** Prepare and initialize uniform buffer containing shader uniforms */ -// fun prepareUniformBuffers() { -// // Deferred vertex shader +/* +* Vulkan Example - Using subpasses for G-Buffer compositing +* +* Copyright (C) 2016-2017 by Sascha Willems - www.saschawillems.de +* +* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +* +* Summary: +* Implements a deferred rendering setup with a forward transparency pass using sub passes +* +* Sub passes allow reading from the previous framebuffer (in the same render pass) at +* the same pixel position. +* +* This is a feature that was especially designed for tile-based-renderers +* (mostly mobile GPUs) and is a new optomization feature in Vulkan for those GPU types. +* +*/ + +package vulkan.basics + +import glm_.BYTES +import glm_.L +import glm_.glm +import glm_.mat4x4.Mat4 +import glm_.vec3.Vec3 +import glm_.vec4.Vec4 +import kool.stak +import org.lwjgl.system.MemoryUtil.NULL +import org.lwjgl.vulkan.VK10.VK_SUBPASS_EXTERNAL +import org.lwjgl.vulkan.VkPipelineVertexInputStateCreateInfo +import org.lwjgl.vulkan.VkVertexInputAttributeDescription +import org.lwjgl.vulkan.VkVertexInputBindingDescription +import vkk.* +import vulkan.NUM_LIGHTS +import vulkan.VERTEX_BUFFER_BIND_ID +import vulkan.assetPath +import vulkan.base.* +import java.lang.Math.abs + + +fun main(args: Array) { + Subpasses().apply { + setupWindow() + initVulkan() + prepare() + renderLoop() + destroy() + } +} + +private class Subpasses : VulkanExampleBase() { + + object textures { + val glass = Texture2D() + } + + // Vertex layout for the models + val vertexLayout = VertexLayout( + VertexComponent.POSITION, + VertexComponent.COLOR, + VertexComponent.NORMAL, + VertexComponent.UV) + + object models { + val scene = Model() + val transparent = Model() + } + + object vertices { + lateinit var inputState: VkPipelineVertexInputStateCreateInfo + lateinit var bindingDescriptions: VkVertexInputBindingDescription + lateinit var attributeDescriptions: VkVertexInputAttributeDescription.Buffer + } + + object uboGBuffer : Bufferizable() { + lateinit var projection: Mat4 + @Order(1) + lateinit var model: Mat4 + lateinit var view: Mat4 + } + + class Light { + var position = Vec4() + var color = Vec3() + var radius = 0f + } + + object uboLights : Bufferizable() { + lateinit var viewPos: Vec4 + val lights = Array(NUM_LIGHTS) { Light() } + } + + object uniformBuffers { + val GBuffer = Buffer() + val lights = Buffer() + } + + object pipelines { + var offscreen = VkPipeline(NULL) + var composition = VkPipeline(NULL) + var transparent = VkPipeline(NULL) + } + + object pipelineLayouts { + var offscreen = VkPipelineLayout(NULL) + var composition = VkPipelineLayout(NULL) + var transparent = VkPipelineLayout(NULL) + } + + object descriptorSets { + var scene = VkDescriptorSet(NULL) + var composition = VkDescriptorSet(NULL) + var transparent = VkDescriptorSet(NULL) + } + + object descriptorSetLayouts { + var scene = VkDescriptorSetLayout(NULL) + var composition = VkDescriptorSetLayout(NULL) + var transparent = VkDescriptorSetLayout(NULL) + } + + // G-Buffer framebuffer attachments + class FrameBufferAttachment { + var image = VkImage(NULL) + var mem = VkDeviceMemory(0) + var view = VkImageView(NULL) + var format = VkFormat.UNDEFINED + } + + object attachments { + val position = FrameBufferAttachment() + val normal = FrameBufferAttachment() + val albedo = FrameBufferAttachment() + } + + init { + title = "Subpasses" + camera.type = Camera.CameraType.firstPerson + camera.movementSpeed = 5f + camera.setPosition(Vec3(-3.2f, 1f, 5.9f)) + camera.setRotation(Vec3(0.5f, 210.05f, 0f)) + camera.setPerspective(60f, size.aspect, 0.1f, 256f) + settings.overlay = false // TODO + } + + override fun destroy() { + + device.apply { + // Clean up used Vulkan resources + // Note : Inherited destructor cleans up resources stored in base class + + destroyImageView(attachments.position.view) + destroyImage(attachments.position.image) + freeMemory(attachments.position.mem) + + destroyImageView(attachments.normal.view) + destroyImage(attachments.normal.image) + freeMemory(attachments.normal.mem) + + destroyImageView(attachments.albedo.view) + destroyImage(attachments.albedo.image) + freeMemory(attachments.albedo.mem) + + destroyPipeline(pipelines.offscreen) + destroyPipeline(pipelines.composition) + destroyPipeline(pipelines.transparent) + + destroyPipelineLayout(pipelineLayouts.offscreen) + destroyPipelineLayout(pipelineLayouts.composition) + destroyPipelineLayout(pipelineLayouts.transparent) + + destroyDescriptorSetLayout(descriptorSetLayouts.scene) + destroyDescriptorSetLayout(descriptorSetLayouts.composition) + destroyDescriptorSetLayout(descriptorSetLayouts.transparent) + } + textures.glass.destroy() + models.scene.destroy() + models.transparent.destroy() + uniformBuffers.GBuffer.destroy() + uniformBuffers.lights.destroy() + + super.destroy() + } + + /** Enable physical device features required for this example */ + override fun getEnabledFeatures() { + // Enable anisotropic filtering if supported + if (deviceFeatures.samplerAnisotropy) + enabledFeatures.samplerAnisotropy = true + // Enable texture compression + when { + deviceFeatures.textureCompressionBC -> enabledFeatures.textureCompressionBC = true + deviceFeatures.textureCompressionASTC_LDR -> enabledFeatures.textureCompressionASTC_LDR = true + deviceFeatures.textureCompressionETC2 -> enabledFeatures.textureCompressionETC2 = true + } + } + + /** Create a frame buffer attachment */ + fun createAttachment(format: VkFormat, usage: VkImageUsageFlags, attachment: FrameBufferAttachment) { + + var aspectMask: VkImageAspectFlags = 0 + var imageLayout = VkImageLayout.UNDEFINED + + attachment.format = format + + if (usage has VkImageUsage.COLOR_ATTACHMENT_BIT) { + aspectMask = VkImageAspect.COLOR_BIT.i + imageLayout = VkImageLayout.COLOR_ATTACHMENT_OPTIMAL + } + if (usage has VkImageUsage.DEPTH_STENCIL_ATTACHMENT_BIT) { + aspectMask = VkImageAspect.DEPTH_BIT or VkImageAspect.STENCIL_BIT + imageLayout = VkImageLayout.DEPTH_STENCIL_ATTACHMENT_OPTIMAL + } + + assert(aspectMask > 0) + + val image = vk.ImageCreateInfo { + imageType = VkImageType.`2D` + this.format = format + extent(size, 1) + mipLevels = 1 + arrayLayers = 1 + samples = VkSampleCount.`1_BIT` + tiling = VkImageTiling.OPTIMAL + this.usage = usage or VkImageUsage.INPUT_ATTACHMENT_BIT // VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT flag is required for input attachments; + initialLayout = VkImageLayout.UNDEFINED + } + + attachment.image = device createImage image + val memReqs = device getImageMemoryRequirements attachment.image + val memAlloc = vk.MemoryAllocateInfo { + allocationSize = memReqs.size + memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryProperty.DEVICE_LOCAL_BIT) + } + attachment.mem = device allocateMemory memAlloc + device.bindImageMemory(attachment.image, attachment.mem) + + val imageView = vk.ImageViewCreateInfo { + viewType = VkImageViewType.`2D` + this.format = format + subresourceRange.apply { + this.aspectMask = aspectMask + baseMipLevel = 0 + levelCount = 1 + baseArrayLayer = 0 + layerCount = 1 + } + this.image = attachment.image + } + attachment.view = device createImageView imageView + } + + /** Create color attachments for the G-Buffer components */ + fun createGBufferAttachments() { + createAttachment(VkFormat.R16G16B16A16_SFLOAT, VkImageUsage.COLOR_ATTACHMENT_BIT.i, attachments.position) // (World space) Positions + createAttachment(VkFormat.R16G16B16A16_SFLOAT, VkImageUsage.COLOR_ATTACHMENT_BIT.i, attachments.normal) // (World space) Normals + createAttachment(VkFormat.R8G8B8A8_UNORM, VkImageUsage.COLOR_ATTACHMENT_BIT.i, attachments.albedo) // Albedo (color) + } + + /** Override framebuffer setup from base class + * Deferred components will be used as frame buffer attachments */ + override fun setupFrameBuffer() = stak { + + val attachments = it.vkImageViewBufferBig(5) + + val frameBufferCreateInfo = vk.FramebufferCreateInfo { + renderPass = this@Subpasses.renderPass + this.attachments = attachments + width = size.x + height = size.y + layers = 1 + } + // Create frame buffers for every swap chain image + frameBuffers = initVkFramebufferArray(swapChain.imageCount) { i -> + attachments[0] = swapChain.buffers[i].view // TODO put(vararg)? + attachments[1] = Subpasses.attachments.position.view + attachments[2] = Subpasses.attachments.normal.view + attachments[3] = Subpasses.attachments.albedo.view + attachments[4] = depthStencil.view + device createFramebuffer frameBufferCreateInfo + } + } + + /** Override render pass setup from base class */ + override fun setupRenderPass() { + + createGBufferAttachments() + + val attachments = vk.AttachmentDescription(5).also { + // Color attachment + it[0].apply { + format = swapChain.colorFormat + samples = VkSampleCount.`1_BIT` + loadOp = VkAttachmentLoadOp.CLEAR + storeOp = VkAttachmentStoreOp.STORE + stencilLoadOp = VkAttachmentLoadOp.DONT_CARE + stencilStoreOp = VkAttachmentStoreOp.DONT_CARE + initialLayout = VkImageLayout.UNDEFINED + finalLayout = VkImageLayout.PRESENT_SRC_KHR + } + // Deferred attachments + // Position + it[1].apply { + format = Subpasses.attachments.position.format + samples = VkSampleCount.`1_BIT` + loadOp = VkAttachmentLoadOp.CLEAR + storeOp = VkAttachmentStoreOp.DONT_CARE + stencilLoadOp = VkAttachmentLoadOp.DONT_CARE + stencilStoreOp = VkAttachmentStoreOp.DONT_CARE + initialLayout = VkImageLayout.UNDEFINED + finalLayout = VkImageLayout.COLOR_ATTACHMENT_OPTIMAL + } + // Normals + it[2].apply { + format = Subpasses.attachments.normal.format + samples = VkSampleCount.`1_BIT` + loadOp = VkAttachmentLoadOp.CLEAR + storeOp = VkAttachmentStoreOp.DONT_CARE + stencilLoadOp = VkAttachmentLoadOp.DONT_CARE + stencilStoreOp = VkAttachmentStoreOp.DONT_CARE + initialLayout = VkImageLayout.UNDEFINED + finalLayout = VkImageLayout.COLOR_ATTACHMENT_OPTIMAL + } + // Albedo + it[3].apply { + format = Subpasses.attachments.albedo.format + samples = VkSampleCount.`1_BIT` + loadOp = VkAttachmentLoadOp.CLEAR + storeOp = VkAttachmentStoreOp.DONT_CARE + stencilLoadOp = VkAttachmentLoadOp.DONT_CARE + stencilStoreOp = VkAttachmentStoreOp.DONT_CARE + initialLayout = VkImageLayout.UNDEFINED + finalLayout = VkImageLayout.COLOR_ATTACHMENT_OPTIMAL + } + // Depth attachment + it[4].apply { + format = depthFormat + samples = VkSampleCount.`1_BIT` + loadOp = VkAttachmentLoadOp.CLEAR + storeOp = VkAttachmentStoreOp.DONT_CARE + stencilLoadOp = VkAttachmentLoadOp.DONT_CARE + stencilStoreOp = VkAttachmentStoreOp.DONT_CARE + initialLayout = VkImageLayout.UNDEFINED + finalLayout = VkImageLayout.DEPTH_STENCIL_ATTACHMENT_OPTIMAL + } + } + // Three subpasses + val subpassDescriptions = vk.SubpassDescription(3) + + // First subpass: Fill G-Buffer components + // ---------------------------------------------------------------------------------------- + + val colorReferences = vk.AttachmentReference( + 0, VkImageLayout.COLOR_ATTACHMENT_OPTIMAL, + 1, VkImageLayout.COLOR_ATTACHMENT_OPTIMAL, + 2, VkImageLayout.COLOR_ATTACHMENT_OPTIMAL, + 3, VkImageLayout.COLOR_ATTACHMENT_OPTIMAL) + val depthReference = vk.AttachmentReference(4, VkImageLayout.DEPTH_STENCIL_ATTACHMENT_OPTIMAL) + + subpassDescriptions[0].apply { + pipelineBindPoint = VkPipelineBindPoint.GRAPHICS + colorAttachmentCount = 4 + colorAttachments = colorReferences + depthStencilAttachment = depthReference + } + // Second subpass: Final composition (using G-Buffer components) + // ---------------------------------------------------------------------------------------- + + val colorReference = vk.AttachmentReference(0, VkImageLayout.COLOR_ATTACHMENT_OPTIMAL) + + val inputReferences = vk.AttachmentReference( + 1, VkImageLayout.SHADER_READ_ONLY_OPTIMAL, + 2, VkImageLayout.SHADER_READ_ONLY_OPTIMAL, + 3, VkImageLayout.SHADER_READ_ONLY_OPTIMAL) + + subpassDescriptions[1].apply { + pipelineBindPoint = VkPipelineBindPoint.GRAPHICS + colorAttachmentCount = 1 + colorAttachment = colorReference + depthStencilAttachment = depthReference + // Use the color attachments filled in the first pass as input attachments + inputAttachments = inputReferences + } + // Third subpass: Forward transparency + // ---------------------------------------------------------------------------------------- + colorReference(0, VkImageLayout.COLOR_ATTACHMENT_OPTIMAL) + + inputReferences[0](1, VkImageLayout.SHADER_READ_ONLY_OPTIMAL) + + subpassDescriptions[2].apply { + pipelineBindPoint = VkPipelineBindPoint.GRAPHICS + colorAttachmentCount = 1 + colorAttachment = colorReference + depthStencilAttachment = depthReference + // Use the color/depth attachments filled in the first pass as input attachments + inputAttachments = inputReferences + } + // Subpass dependencies for layout transitions + val dependencies = vk.SubpassDependency(4).also { + it[0].apply { + srcSubpass = VK_SUBPASS_EXTERNAL + dstSubpass = 0 + srcStageMask = VkPipelineStage.BOTTOM_OF_PIPE_BIT.i + dstStageMask = VkPipelineStage.COLOR_ATTACHMENT_OUTPUT_BIT.i + srcAccessMask = VkAccess.MEMORY_READ_BIT.i + dstAccessMask = VkAccess.COLOR_ATTACHMENT_READ_BIT or VkAccess.COLOR_ATTACHMENT_WRITE_BIT + dependencyFlags = VkDependency.BY_REGION_BIT.i + } + // This dependency transitions the input attachment from color attachment to shader read + it[1].apply { + srcSubpass = 0 + dstSubpass = 1 + srcStageMask = VkPipelineStage.COLOR_ATTACHMENT_OUTPUT_BIT.i + dstStageMask = VkPipelineStage.FRAGMENT_SHADER_BIT.i + srcAccessMask = VkAccess.COLOR_ATTACHMENT_WRITE_BIT.i + dstAccessMask = VkAccess.SHADER_READ_BIT.i + dependencyFlags = VkDependency.BY_REGION_BIT.i + } + it[2].apply { + srcSubpass = 1 + dstSubpass = 2 + srcStageMask = VkPipelineStage.COLOR_ATTACHMENT_OUTPUT_BIT.i + dstStageMask = VkPipelineStage.FRAGMENT_SHADER_BIT.i + srcAccessMask = VkAccess.COLOR_ATTACHMENT_WRITE_BIT.i + dstAccessMask = VkAccess.SHADER_READ_BIT.i + dependencyFlags = VkDependency.BY_REGION_BIT.i + } + it[3].apply { + srcSubpass = 0 + dstSubpass = VK_SUBPASS_EXTERNAL + srcStageMask = VkPipelineStage.COLOR_ATTACHMENT_OUTPUT_BIT.i + dstStageMask = VkPipelineStage.BOTTOM_OF_PIPE_BIT.i + srcAccessMask = VkAccess.COLOR_ATTACHMENT_READ_BIT or VkAccess.COLOR_ATTACHMENT_WRITE_BIT + dstAccessMask = VkAccess.MEMORY_READ_BIT.i + dependencyFlags = VkDependency.BY_REGION_BIT.i + } + } + val renderPassInfo = vk.RenderPassCreateInfo { + this.attachments = attachments + subpasses = subpassDescriptions + this.dependencies = dependencies + } + renderPass = device createRenderPass renderPassInfo + } + + override fun buildCommandBuffers() { + + val cmdBufInfo = vk.CommandBufferBeginInfo() + + val clearValues = vk.ClearValue(5).also { + it[0].color(0f, 0f, 0f, 0f) + it[1].color(0f, 0f, 0f, 0f) + it[2].color(0f, 0f, 0f, 0f) + it[3].color(0f, 0f, 0f, 0f) + it[4].depthStencil(1f, 0) + } + val renderPassBeginInfo = vk.RenderPassBeginInfo { + renderPass = this@Subpasses.renderPass + renderArea.offset(0) + renderArea.extent(size) + this.clearValues = clearValues + } + for (i in drawCmdBuffers.indices) { + // Set target frame buffer + renderPassBeginInfo.framebuffer(frameBuffers[i].L) // TODO BUG + + drawCmdBuffers[i].apply { + + begin(cmdBufInfo) + + beginRenderPass(renderPassBeginInfo, VkSubpassContents.INLINE) + + setViewport(size) + setScissor(size) + + // First sub pass + // Renders the components of the scene to the G-Buffer atttachments + debugMarker.withRegion(this, "Subpass 0: Deferred G-Buffer creation", Vec4(1f)) { + + bindPipeline(VkPipelineBindPoint.GRAPHICS, pipelines.offscreen) + bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, pipelineLayouts.offscreen, descriptorSets.scene) + bindVertexBuffers(VERTEX_BUFFER_BIND_ID, models.scene.vertices.buffer) + bindIndexBuffer(models.scene.indices.buffer, VkDeviceSize(0), VkIndexType.UINT32) + drawIndexed(models.scene.indexCount, 1, 0, 0, 0) + } + + // Second sub pass + // This subpass will use the G-Buffer components that have been filled in the first subpass as input attachment for the final compositing + debugMarker.withRegion(this, "Subpass 1: Deferred composition", Vec4(1f)) { + + nextSubpass(VkSubpassContents.INLINE) + + bindPipeline(VkPipelineBindPoint.GRAPHICS, pipelines.composition) + bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, pipelineLayouts.composition, descriptorSets.composition) + draw(3, 1, 0, 0) + } + + // Third subpass + // Render transparent geometry using a forward pass that compares against depth generted during G-Buffer fill + debugMarker.withRegion(this, "Subpass 2: Forward transparency", Vec4(1f)) { + + nextSubpass(VkSubpassContents.INLINE) + + bindPipeline(VkPipelineBindPoint.GRAPHICS, pipelines.transparent) + bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, pipelineLayouts.transparent, descriptorSets.transparent) + bindVertexBuffers(VERTEX_BUFFER_BIND_ID, models.transparent.vertices.buffer) + bindIndexBuffer(models.transparent.indices.buffer, VkDeviceSize(0), VkIndexType.UINT32) + drawIndexed(models.transparent.indexCount, 1, 0, 0, 0) + } + + endRenderPass() + + end() + } + } + } + + fun loadAssets() { + + models.scene.loadFromFile("$assetPath/models/samplebuilding.dae", vertexLayout, 1f, vulkanDevice, queue) + models.transparent.loadFromFile("$assetPath/models/samplebuilding_glass.dae", vertexLayout, 1f, vulkanDevice, queue) + // Textures + val (text, format) = when { + vulkanDevice.features.textureCompressionBC -> "bc3_unorm.ktx" to VkFormat.BC3_UNORM_BLOCK + vulkanDevice.features.textureCompressionASTC_LDR -> "astc_8x8_unorm.ktx" to VkFormat.ASTC_8x8_UNORM_BLOCK + vulkanDevice.features.textureCompressionETC2 -> "etc2_unorm.ktx" to VkFormat.ETC2_R8G8B8A8_UNORM_BLOCK + else -> tools.exitFatal("Device does not support any compressed texture format!", ERROR_FEATURE_NOT_PRESENT) + } + textures.glass.loadFromFile("$assetPath/textures/colored_glass_$text", format, vulkanDevice, queue) + } + + fun setupVertexDescriptions() { + // Binding description + vertices.bindingDescriptions = vk.VertexInputBindingDescription(VERTEX_BUFFER_BIND_ID, vertexLayout.stride, VkVertexInputRate.VERTEX) + + // Attribute descriptions + vertices.attributeDescriptions = vk.VertexInputAttributeDescription( + // Location 0: Position + VERTEX_BUFFER_BIND_ID, 0, VkFormat.R32G32B32_SFLOAT, 0, + // Location 1: Color + VERTEX_BUFFER_BIND_ID, 1, VkFormat.R32G32B32_SFLOAT, Vec3.size, + // Location 2: Normal + VERTEX_BUFFER_BIND_ID, 2, VkFormat.R32G32B32_SFLOAT, Vec3.size * 2, + // Location 3: UV + VERTEX_BUFFER_BIND_ID, 3, VkFormat.R32G32_SFLOAT, Vec3.size * 3) + + vertices.inputState = vk.PipelineVertexInputStateCreateInfo { + vertexBindingDescription = vertices.bindingDescriptions + vertexAttributeDescriptions = vertices.attributeDescriptions + } + } + + fun setupDescriptorPool() { + + val poolSizes = vk.DescriptorPoolSize( + VkDescriptorType.UNIFORM_BUFFER, 4, + VkDescriptorType.COMBINED_IMAGE_SAMPLER, 4, + VkDescriptorType.INPUT_ATTACHMENT, 4) + + val descriptorPoolInfo = vk.DescriptorPoolCreateInfo(poolSizes, 4) + + descriptorPool = device createDescriptorPool descriptorPoolInfo + } + + fun setupDescriptorSetLayout() { + // Deferred shading layout + val setLayoutBindings = vk.DescriptorSetLayoutBinding( + // Binding 0 : Vertex shader uniform buffer + VkDescriptorType.UNIFORM_BUFFER, VkShaderStage.VERTEX_BIT.i, 0) + + val descriptorLayout = vk.DescriptorSetLayoutCreateInfo(setLayoutBindings) + + descriptorSetLayouts.scene = device createDescriptorSetLayout descriptorLayout + + val pipelineLayoutCreateInfo = vk.PipelineLayoutCreateInfo(descriptorSetLayouts.scene) + + // Offscreen (scene) rendering pipeline layout + pipelineLayouts.offscreen = device createPipelineLayout pipelineLayoutCreateInfo + } + + fun setupDescriptorSet() { + + val allocInfo = vk.DescriptorSetAllocateInfo(descriptorPool, descriptorSetLayouts.scene) + + descriptorSets.scene = device allocateDescriptorSets allocInfo + + val writeDescriptorSets = vk.WriteDescriptorSet( + // Binding 0: Vertex shader uniform buffer + descriptorSets.scene, VkDescriptorType.UNIFORM_BUFFER, 0, uniformBuffers.GBuffer.descriptor) + + device updateDescriptorSets writeDescriptorSets + } + + fun preparePipelines() { + + val inputAssemblyState = vk.PipelineInputAssemblyStateCreateInfo(VkPrimitiveTopology.TRIANGLE_LIST, 0, false) + + val rasterizationState = vk.PipelineRasterizationStateCreateInfo(VkPolygonMode.FILL, VkCullMode.BACK_BIT.i, VkFrontFace.CLOCKWISE) + + val blendAttachmentState = vk.PipelineColorBlendAttachmentState(0xf, false) + + val colorBlendState = vk.PipelineColorBlendStateCreateInfo(blendAttachmentState) + + val depthStencilState = vk.PipelineDepthStencilStateCreateInfo(true, true, VkCompareOp.LESS_OR_EQUAL) + + val viewportState = vk.PipelineViewportStateCreateInfo(1, 1) + + val multisampleState = vk.PipelineMultisampleStateCreateInfo(VkSampleCount.`1_BIT`) + + val dynamicStateEnables = listOf(VkDynamicState.VIEWPORT, VkDynamicState.SCISSOR) + val dynamicState = vk.PipelineDynamicStateCreateInfo(dynamicStateEnables) + + // Final fullscreen pass pipeline + val shaderStages = vk.PipelineShaderStageCreateInfo(2) + + val pipelineCreateInfo = vk.GraphicsPipelineCreateInfo(pipelineLayouts.offscreen, renderPass).also { + it.vertexInputState = vertices.inputState + it.inputAssemblyState = inputAssemblyState + it.rasterizationState = rasterizationState + it.colorBlendState = colorBlendState + it.multisampleState = multisampleState + it.viewportState = viewportState + it.depthStencilState = depthStencilState + it.dynamicState = dynamicState + it.stages = shaderStages + it.subpass = 0 + } + val blendAttachmentStates = vk.PipelineColorBlendAttachmentState( + 0xf, false, + 0xf, false, + 0xf, false, + 0xf, false) + + colorBlendState.attachments = blendAttachmentStates + + // Offscreen scene rendering pipeline + shaderStages[0].loadShader("$assetPath/shaders/subpasses/gbuffer.vert.spv", VkShaderStage.VERTEX_BIT) + shaderStages[1].loadShader("$assetPath/shaders/subpasses/gbuffer.frag.spv", VkShaderStage.FRAGMENT_BIT) + + pipelines.offscreen = device.createGraphicsPipelines(pipelineCache, pipelineCreateInfo) + } + + /** Create the Vulkan objects used in the composition pass (descriptor sets, pipelines, etc.) */ + fun prepareCompositionPass() = stak { + // Descriptor set layout + var setLayoutBindings = vk.DescriptorSetLayoutBinding( + // Binding 0: Position input attachment + VkDescriptorType.INPUT_ATTACHMENT, VkShaderStage.FRAGMENT_BIT.i, 0, + // Binding 1: Normal input attachment + VkDescriptorType.INPUT_ATTACHMENT, VkShaderStage.FRAGMENT_BIT.i, 1, + // Binding 2: Albedo input attachment + VkDescriptorType.INPUT_ATTACHMENT, VkShaderStage.FRAGMENT_BIT.i, 2, + // Binding 3: Light positions + VkDescriptorType.UNIFORM_BUFFER, VkShaderStage.FRAGMENT_BIT.i, 3) + + var descriptorLayout = vk.DescriptorSetLayoutCreateInfo(setLayoutBindings) + + descriptorSetLayouts.composition = device createDescriptorSetLayout descriptorLayout + + // Pipeline layout + var pipelineLayoutCreateInfo = vk.PipelineLayoutCreateInfo(descriptorSetLayouts.composition) + + pipelineLayouts.composition = device createPipelineLayout pipelineLayoutCreateInfo + + // Descriptor sets + var allocInfo = vk.DescriptorSetAllocateInfo(descriptorPool, descriptorSetLayouts.composition) + + descriptorSets.composition = device allocateDescriptorSets allocInfo + + // Image descriptors for the offscreen color attachments + val nullSampler = VkSampler(NULL) + + val texDescriptorPosition = vk.DescriptorImageInfo(nullSampler, attachments.position.view, VkImageLayout.SHADER_READ_ONLY_OPTIMAL) + + val texDescriptorNormal = vk.DescriptorImageInfo(nullSampler, attachments.normal.view, VkImageLayout.SHADER_READ_ONLY_OPTIMAL) + + val texDescriptorAlbedo = vk.DescriptorImageInfo(nullSampler, attachments.albedo.view, VkImageLayout.SHADER_READ_ONLY_OPTIMAL) + + var writeDescriptorSets = vk.WriteDescriptorSet( + // Binding 0: Position texture target + descriptorSets.composition, VkDescriptorType.INPUT_ATTACHMENT, 0, texDescriptorPosition, + // Binding 1: Normals texture target + descriptorSets.composition, VkDescriptorType.INPUT_ATTACHMENT, 1, texDescriptorNormal, + // Binding 2: Albedo texture target + descriptorSets.composition, VkDescriptorType.INPUT_ATTACHMENT, 2, texDescriptorAlbedo, + // Binding 4: Fragment shader lights + descriptorSets.composition, VkDescriptorType.UNIFORM_BUFFER, 3, uniformBuffers.lights.descriptor) + + device updateDescriptorSets writeDescriptorSets + + // Pipeline + val inputAssemblyState = vk.PipelineInputAssemblyStateCreateInfo(VkPrimitiveTopology.TRIANGLE_LIST, 0, false) + + val rasterizationState = vk.PipelineRasterizationStateCreateInfo(VkPolygonMode.FILL, VkCullMode.NONE.i, VkFrontFace.CLOCKWISE) + + val blendAttachmentState = vk.PipelineColorBlendAttachmentState(0xf, false) + + val colorBlendState = vk.PipelineColorBlendStateCreateInfo(blendAttachmentState) + + val depthStencilState = vk.PipelineDepthStencilStateCreateInfo(true, true, VkCompareOp.LESS_OR_EQUAL) + + val viewportState = vk.PipelineViewportStateCreateInfo(1, 1) + + val multisampleState = vk.PipelineMultisampleStateCreateInfo(VkSampleCount.`1_BIT`) + + val dynamicStateEnables = listOf(VkDynamicState.VIEWPORT, VkDynamicState.SCISSOR) + val dynamicState = vk.PipelineDynamicStateCreateInfo(dynamicStateEnables) + + val shaderStages = vk.PipelineShaderStageCreateInfo(2).also { + it[0].loadShader("$assetPath/shaders/subpasses/composition.vert.spv", VkShaderStage.VERTEX_BIT) + it[1].loadShader("$assetPath/shaders/subpasses/composition.frag.spv", VkShaderStage.FRAGMENT_BIT) + } + + // Use specialization constants to pass number of lights to the shader + val specializationEntry = vk.SpecializationMapEntry { + constantId = 0 + offset = 0 + size = Int.BYTES.L + } + val specializationData = it.malloc(Int.BYTES).apply { putInt(0, NUM_LIGHTS) } + + val specializationInfo = vk.SpecializationInfo { + mapEntry = specializationEntry + data = specializationData + } + shaderStages[1].specializationInfo = specializationInfo + + val pipelineCreateInfo = vk.GraphicsPipelineCreateInfo(pipelineLayouts.composition, renderPass).also { + + val emptyInputState = vk.PipelineVertexInputStateCreateInfo() + + it.vertexInputState = emptyInputState + it.inputAssemblyState = inputAssemblyState + it.rasterizationState = rasterizationState + it.colorBlendState = colorBlendState + it.multisampleState = multisampleState + it.viewportState = viewportState + it.depthStencilState = depthStencilState + it.dynamicState = dynamicState + it.stages = shaderStages + // Index of the subpass that this pipeline will be used in + it.subpass = 1 + } + depthStencilState.depthWriteEnable = false + + pipelines.composition = device.createGraphicsPipelines(pipelineCache, pipelineCreateInfo) + + // Transparent (forward) pipeline + + // Descriptor set layout + setLayoutBindings = vk.DescriptorSetLayoutBinding( + VkDescriptorType.UNIFORM_BUFFER, VkShaderStage.VERTEX_BIT.i, 0, + VkDescriptorType.INPUT_ATTACHMENT, VkShaderStage.FRAGMENT_BIT.i, 1, + VkDescriptorType.COMBINED_IMAGE_SAMPLER, VkShaderStage.FRAGMENT_BIT.i, 2) + + descriptorLayout = vk.DescriptorSetLayoutCreateInfo(setLayoutBindings) + descriptorSetLayouts.transparent = device createDescriptorSetLayout descriptorLayout + + // Pipeline layout + pipelineLayoutCreateInfo = vk.PipelineLayoutCreateInfo(descriptorSetLayouts.transparent) + pipelineLayouts.transparent = device createPipelineLayout pipelineLayoutCreateInfo + + // Descriptor sets + allocInfo = vk.DescriptorSetAllocateInfo(descriptorPool, descriptorSetLayouts.transparent) + descriptorSets.transparent = device allocateDescriptorSets allocInfo + + writeDescriptorSets = vk.WriteDescriptorSet( + descriptorSets.transparent, VkDescriptorType.UNIFORM_BUFFER, 0, uniformBuffers.GBuffer.descriptor, + descriptorSets.transparent, VkDescriptorType.INPUT_ATTACHMENT, 1, texDescriptorPosition, + descriptorSets.transparent, VkDescriptorType.COMBINED_IMAGE_SAMPLER, 2, textures.glass.descriptor) + device updateDescriptorSets writeDescriptorSets + + // Enable blending + blendAttachmentState.apply { + blendEnable = true + srcColorBlendFactor = VkBlendFactor.SRC_ALPHA + dstColorBlendFactor = VkBlendFactor.ONE_MINUS_SRC_ALPHA + colorBlendOp = VkBlendOp.ADD + srcAlphaBlendFactor = VkBlendFactor.ONE + dstAlphaBlendFactor = VkBlendFactor.ZERO + alphaBlendOp = VkBlendOp.ADD + colorWriteMask = VkColorComponent.R_BIT or VkColorComponent.G_BIT or VkColorComponent.B_BIT or VkColorComponent.A_BIT + } + pipelineCreateInfo.apply { + vertexInputState = vertices.inputState + layout = pipelineLayouts.transparent + subpass = 2 + } + shaderStages[0].loadShader("$assetPath/shaders/subpasses/transparent.vert.spv", VkShaderStage.VERTEX_BIT) + shaderStages[1].loadShader("$assetPath/shaders/subpasses/transparent.frag.spv", VkShaderStage.FRAGMENT_BIT) + + pipelines.transparent = device.createGraphicsPipelines(pipelineCache, pipelineCreateInfo) + } + + /** Prepare and initialize uniform buffer containing shader uniforms */ + fun prepareUniformBuffers() { + // Deferred vertex shader + vulkanDevice.createBuffer( + VkBufferUsage.UNIFORM_BUFFER_BIT.i, + VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, + uniformBuffers.GBuffer, + VkDeviceSize(uboGBuffer.size.L)) + + // Deferred fragment shader + TODO() // vulkanDevice.createBuffer( // VkBufferUsage.UNIFORM_BUFFER_BIT.i, // VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, -// uniformBuffers.GBuffer, -// VkDeviceSize(uboGBuffer.size.L)) -// -// // Deferred fragment shader -// TODO() -//// vulkanDevice.createBuffer( -//// VkBufferUsage.UNIFORM_BUFFER_BIT.i, -//// VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, -//// uniformBuffers.lights, -//// uboLights.size) -// -// // Update -// updateUniformBufferDeferredMatrices() -// updateUniformBufferDeferredLights() -// } -// -// fun updateUniformBufferDeferredMatrices() { -// -// uboGBuffer.projection = camera.matrices.perspective -// uboGBuffer.view = camera.matrices.view -// uboGBuffer.model put 1f -// -// uniformBuffers.GBuffer.mapping { mapped -> uboGBuffer to mapped } -// } -// -// fun initLights() { -// -// val colors = arrayOf( -// Vec3(1f, 1f, 1f), -// Vec3(1f, 0f, 0f), -// Vec3(0f, 1f, 0f), -// Vec3(0f, 0f, 1f), -// Vec3(1f, 1f, 0f)) -// -// fun rndDist() = glm.linearRand(-1f, 1f) -// fun rndCol() = glm.linearRand(0, colors.lastIndex) -// -// for (light in uboLights.lights) { -// light.position.put(rndDist() * 6f, 0.25f + abs(rndDist()) * 4f, rndDist() * 6f, 1f) -// light.color put colors[rndCol()] -// light.radius = 1f + abs(rndDist()) -// } -// } -// -// /** Update fragment shader light position uniform block */ -// fun updateUniformBufferDeferredLights() { -// // Current view position -// uboLights.viewPos = Vec4(camera.position, 0f) * Vec4(-1f, 1f, -1f, 1f) -// -// uniformBuffers.lights.mapping { mapped -> uboLights to mapped } -// } -// -// fun draw() { -// -// super.prepareFrame() -// -// // Command buffer to be sumitted to the queue -// submitInfo.commandBuffer = drawCmdBuffers[currentBuffer] -// -// // Submit to queue -// queue submit submitInfo -// -// super.submitFrame() -// } -// -// override fun prepare() { -// super.prepare() -// loadAssets() -// setupVertexDescriptions() +// uniformBuffers.lights, +// uboLights.size) + + // Update + updateUniformBufferDeferredMatrices() + updateUniformBufferDeferredLights() + } + + fun updateUniformBufferDeferredMatrices() { + + uboGBuffer.projection = camera.matrices.perspective + uboGBuffer.view = camera.matrices.view + uboGBuffer.model put 1f + + uniformBuffers.GBuffer.mapping { mapped -> uboGBuffer to mapped } + } + + fun initLights() { + + val colors = arrayOf( + Vec3(1f, 1f, 1f), + Vec3(1f, 0f, 0f), + Vec3(0f, 1f, 0f), + Vec3(0f, 0f, 1f), + Vec3(1f, 1f, 0f)) + + fun rndDist() = glm.linearRand(-1f, 1f) + fun rndCol() = glm.linearRand(0, colors.lastIndex) + + for (light in uboLights.lights) { + light.position.put(rndDist() * 6f, 0.25f + abs(rndDist()) * 4f, rndDist() * 6f, 1f) + light.color put colors[rndCol()] + light.radius = 1f + abs(rndDist()) + } + } + + /** Update fragment shader light position uniform block */ + fun updateUniformBufferDeferredLights() { + // Current view position + uboLights.viewPos = Vec4(camera.position, 0f) * Vec4(-1f, 1f, -1f, 1f) + + uniformBuffers.lights.mapping { mapped -> uboLights to mapped } + } + + fun draw() { + + super.prepareFrame() + + // Command buffer to be sumitted to the queue + submitInfo.commandBuffer = drawCmdBuffers[currentBuffer] + + // Submit to queue + queue submit submitInfo + + super.submitFrame() + } + + override fun prepare() { + super.prepare() + loadAssets() + setupVertexDescriptions() + initLights() + prepareUniformBuffers() + setupDescriptorSetLayout() + preparePipelines() + setupDescriptorPool() + setupDescriptorSet() + prepareCompositionPass() + buildCommandBuffers() + prepared = true + window.show() + } + + override fun render() { + if (!prepared) + return + draw() + } + + override fun viewChanged() { + updateUniformBufferDeferredMatrices() + updateUniformBufferDeferredLights() + } + + // UI overlay configuration needs to be adjusted for this example (renderpass setup, attachment count, etc.) +// virtual void OnSetupUIOverlay(vks::UIOverlayCreateInfo &createInfo) +// { +// createInfo.targetSubpass = 2 +// } +// +// virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) +// { +// if (overlay->header("Subpasses")) { overlay -> +// text("0: Deferred G-Buffer creation") +// overlay->text("1: Deferred composition") +// overlay->text("2: Forward transparency") +// } +// if (overlay->header("Settings")) { +// if (overlay->button("Randomize lights")) { // initLights() -// prepareUniformBuffers() -// setupDescriptorSetLayout() -// preparePipelines() -// setupDescriptorPool() -// setupDescriptorSet() -// prepareCompositionPass() -// buildCommandBuffers() -// prepared = true -// window.show() +// updateUniformBufferDeferredLights() // } -// -// override fun render() { -// if (!prepared) -// return -// draw() // } -// -// override fun viewChanged() { -// updateUniformBufferDeferredMatrices() -// updateUniformBufferDeferredLights() // } -// -// // UI overlay configuration needs to be adjusted for this example (renderpass setup, attachment count, etc.) -//// virtual void OnSetupUIOverlay(vks::UIOverlayCreateInfo &createInfo) -//// { -//// createInfo.targetSubpass = 2 -//// } -//// -//// virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) -//// { -//// if (overlay->header("Subpasses")) { overlay -> -//// text("0: Deferred G-Buffer creation") -//// overlay->text("1: Deferred composition") -//// overlay->text("2: Forward transparency") -//// } -//// if (overlay->header("Settings")) { -//// if (overlay->button("Randomize lights")) { -//// initLights() -//// updateUniformBufferDeferredLights() -//// } -//// } -//// } -//} \ No newline at end of file +} \ No newline at end of file diff --git a/src/main/kotlin/vulkan/basics/13 Offscreen Rendering.kt b/src/main/kotlin/vulkan/basics/13 Offscreen Rendering.kt index 229db20..a71704c 100644 --- a/src/main/kotlin/vulkan/basics/13 Offscreen Rendering.kt +++ b/src/main/kotlin/vulkan/basics/13 Offscreen Rendering.kt @@ -1,857 +1,857 @@ -/* -* Vulkan Example - Offscreen rendering using a separate framebuffer -* -* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de -* -* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -*/ - -package vulkan.basics - -import glm_.L -import glm_.func.rad -import glm_.glm -import glm_.mat4x4.Mat4 -import glm_.size -import glm_.vec2.Vec2 -import glm_.vec2.Vec2i -import glm_.vec3.Vec3 -import glm_.vec4.Vec4 -import kool.adr -import kool.stak -import org.lwjgl.system.MemoryUtil.NULL -import org.lwjgl.vulkan.VK10.VK_SUBPASS_EXTERNAL -import org.lwjgl.vulkan.VkCommandBuffer -import org.lwjgl.vulkan.VkDescriptorImageInfo -import vkk.* -import vulkan.VERTEX_BUFFER_BIND_ID -import vulkan.assetPath -import vulkan.base.* - - -fun main(args: Array) { - OffscreenRendering().apply { - setupWindow() - initVulkan() - prepare() - renderLoop() - destroy() - } -} - -// Offscreen frame buffer properties -private const val FB_DIM = 512 -private val FB_COLOR_FORMAT = VkFormat.R8G8B8A8_UNORM - -private class OffscreenRendering : VulkanExampleBase() { - - var debugDisplay = false - - object textures { - val colorMap = Texture2D() - } - - // Vertex layout for the models - val vertexLayout = VertexLayout( - VertexComponent.POSITION, - VertexComponent.UV, - VertexComponent.COLOR, - VertexComponent.NORMAL) - - object models { - val example = Model() - val quad = Model() - val plane = Model() - } - - object uniformBuffers { - val vsShared = Buffer() - val vsMirror = Buffer() - val vsOffScreen = Buffer() - val vsDebugQuad = Buffer() - } - - object uboShared : Bufferizable() { - lateinit var projection: Mat4 - @Order(1) - lateinit var model: Mat4 - @Order(2) - val lightPos = Vec4(0f, 0f, 0f, 1f) - } - - object pipelines { - var debug = VkPipeline(NULL) - var shaded = VkPipeline(NULL) - var shadedOffscreen = VkPipeline(NULL) - var mirror = VkPipeline(NULL) - } - - object pipelineLayouts { - var textured = VkPipelineLayout(NULL) - var shaded = VkPipelineLayout(NULL) - } - - object descriptorSets { - var offscreen = VkDescriptorSet(NULL) - var mirror = VkDescriptorSet(NULL) - var model = VkDescriptorSet(NULL) - var debugQuad = VkDescriptorSet(NULL) - } - - object descriptorSetLayouts { - var textured = VkDescriptorSetLayout(NULL) - var shaded = VkDescriptorSetLayout(NULL) - } - - // Framebuffer for offscreen rendering - class FrameBufferAttachment { - var image = VkImage(NULL) - var mem = VkDeviceMemory(NULL) - var view = VkImageView(NULL) - } - - object offscreenPass { - val size = Vec2i() - var frameBuffer = VkFramebuffer(NULL) - val color = FrameBufferAttachment() - val depth = FrameBufferAttachment() - var renderPass = VkRenderPass(NULL) - var sampler = VkSampler(NULL) - lateinit var descriptor: VkDescriptorImageInfo - var commandBuffer: VkCommandBuffer? = null - // Semaphore used to synchronize between offscreen and final scene render pass - var semaphore = VkSemaphore(NULL) - } - - val meshPos = Vec3(0f, -1.5f, 0f) - val meshRot = Vec3() - - init { - zoom = -6f - rotation(-2.5f, 0f, 0f) - cameraPos(0f, 1f, 0f) - timerSpeed *= 0.25f - title = "Offscreen rendering" -// settings.overlay = true - enabledFeatures.shaderClipDistance = true - } - - override fun destroy() { - - // Clean up used Vulkan resources - // Note : Inherited destructor cleans up resources stored in base class - - // Textures - textures.colorMap.destroy() - - device.apply { - // Frame buffer - - // Color attachment - destroyImageView(offscreenPass.color.view) - destroyImage(offscreenPass.color.image) - freeMemory(offscreenPass.color.mem) - - // Depth attachment - destroyImageView(offscreenPass.depth.view) - destroyImage(offscreenPass.depth.image) - freeMemory(offscreenPass.depth.mem) - - destroyRenderPass(offscreenPass.renderPass) - destroySampler(offscreenPass.sampler) - destroyFramebuffer(offscreenPass.frameBuffer) - - destroyPipeline(pipelines.debug) - destroyPipeline(pipelines.shaded) - destroyPipeline(pipelines.shadedOffscreen) - destroyPipeline(pipelines.mirror) - - destroyPipelineLayout(pipelineLayouts.textured) - destroyPipelineLayout(pipelineLayouts.shaded) - - destroyDescriptorSetLayout(descriptorSetLayouts.shaded) - destroyDescriptorSetLayout(descriptorSetLayouts.textured) - - // Models - models.apply { - example.destroy() - quad.destroy() - plane.destroy() - } - // Uniform buffers - uniformBuffers.apply { - vsShared.destroy() - vsMirror.destroy() - vsOffScreen.destroy() - vsDebugQuad.destroy() - } - freeCommandBuffer(cmdPool, offscreenPass.commandBuffer!!) - destroySemaphore(offscreenPass.semaphore) - } - super.destroy() - } - - /** Setup the offscreen framebuffer for rendering the mirrored scene - * The color attachment of this framebuffer will then be used to sample from in the fragment shader of the final pass */ - fun prepareOffscreen() = stak { - - offscreenPass.size put FB_DIM - - // Find a suitable depth format - val fbDepthFormat = tools getSupportedDepthFormat physicalDevice - assert(fbDepthFormat != VkFormat.UNDEFINED) - - // Color attachment - val image = vk.ImageCreateInfo { - imageType = VkImageType.`2D` - format = FB_COLOR_FORMAT - extent(offscreenPass.size, 1) - mipLevels = 1 - arrayLayers = 1 - samples = VkSampleCount.`1_BIT` - tiling = VkImageTiling.OPTIMAL - // We will sample directly from the color attachment - usage = VkImageUsage.COLOR_ATTACHMENT_BIT or VkImageUsage.SAMPLED_BIT - } - - offscreenPass.color.image = device createImage image - val memReqs = device getImageMemoryRequirements offscreenPass.color.image - val memAlloc = vk.MemoryAllocateInfo { - allocationSize = memReqs.size - memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryProperty.DEVICE_LOCAL_BIT) - } - offscreenPass.color.mem = device allocateMemory memAlloc - device.bindImageMemory(offscreenPass.color.image, offscreenPass.color.mem) - - val colorImageView = vk.ImageViewCreateInfo { - viewType = VkImageViewType.`2D` - format = FB_COLOR_FORMAT - subresourceRange.apply { - aspectMask = VkImageAspect.COLOR_BIT.i - baseMipLevel = 0 - levelCount = 1 - baseArrayLayer = 0 - layerCount = 1 - } - this.image = offscreenPass.color.image - } - offscreenPass.color.view = device createImageView colorImageView - - // Create sampler to sample from the attachment in the fragment shader - val samplerInfo = vk.SamplerCreateInfo { - magFilter = VkFilter.LINEAR - minFilter = VkFilter.LINEAR - mipmapMode = VkSamplerMipmapMode.LINEAR - addressModeU = VkSamplerAddressMode.CLAMP_TO_EDGE - addressModeV = addressModeU // TODO custom func? - addressModeW = addressModeU - mipLodBias = 0f - maxAnisotropy = 1f - minLod = 0f - maxLod = 1f - borderColor = VkBorderColor.FLOAT_OPAQUE_WHITE - } - offscreenPass.sampler = device createSampler samplerInfo - - // Depth stencil attachment - image.format = fbDepthFormat - image.usage = VkImageUsage.DEPTH_STENCIL_ATTACHMENT_BIT.i - - offscreenPass.depth.image = device createImage image - device.getImageMemoryRequirements(offscreenPass.depth.image, memReqs) - memAlloc.allocationSize = memReqs.size - memAlloc.memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryProperty.DEVICE_LOCAL_BIT) - offscreenPass.depth.mem = device allocateMemory memAlloc - device.bindImageMemory(offscreenPass.depth.image, offscreenPass.depth.mem) - - val depthStencilView = vk.ImageViewCreateInfo { - viewType = VkImageViewType.`2D` - format = fbDepthFormat - flags = 0 - subresourceRange.apply { - aspectMask = VkImageAspect.DEPTH_BIT or VkImageAspect.STENCIL_BIT - baseMipLevel = 0 - levelCount = 1 - baseArrayLayer = 0 - layerCount = 1 - } - this.image = offscreenPass.depth.image - } - offscreenPass.depth.view = device createImageView depthStencilView - - // Create a separate render pass for the offscreen rendering as it may differ from the one used for scene rendering - - val attchmentDescriptions = vk.AttachmentDescription(2).also { - // Color attachment - it[0].apply { - format = FB_COLOR_FORMAT - samples = VkSampleCount.`1_BIT` - loadOp = VkAttachmentLoadOp.CLEAR - storeOp = VkAttachmentStoreOp.STORE - stencilLoadOp = VkAttachmentLoadOp.DONT_CARE - stencilStoreOp = VkAttachmentStoreOp.DONT_CARE - initialLayout = VkImageLayout.UNDEFINED - finalLayout = VkImageLayout.SHADER_READ_ONLY_OPTIMAL - } - // Depth attachment - it[1].apply { - format = fbDepthFormat - samples = VkSampleCount.`1_BIT` - loadOp = VkAttachmentLoadOp.CLEAR - storeOp = VkAttachmentStoreOp.DONT_CARE - stencilLoadOp = VkAttachmentLoadOp.DONT_CARE - stencilStoreOp = VkAttachmentStoreOp.DONT_CARE - initialLayout = VkImageLayout.UNDEFINED - finalLayout = VkImageLayout.DEPTH_STENCIL_ATTACHMENT_OPTIMAL - } - } - val colorReference = vk.AttachmentReference(0, VkImageLayout.COLOR_ATTACHMENT_OPTIMAL) - val depthReference = vk.AttachmentReference(1, VkImageLayout.DEPTH_STENCIL_ATTACHMENT_OPTIMAL) - - val subpassDescription = vk.SubpassDescription { - pipelineBindPoint = VkPipelineBindPoint.GRAPHICS - colorAttachmentCount = 1 - colorAttachment = colorReference - depthStencilAttachment = depthReference - } - // Use subpass dependencies for layout transitions - val dependencies = vk.SubpassDependency(2).also { - it[0].apply { - srcSubpass = VK_SUBPASS_EXTERNAL - dstSubpass = 0 - srcStageMask = VkPipelineStage.BOTTOM_OF_PIPE_BIT.i - dstStageMask = VkPipelineStage.COLOR_ATTACHMENT_OUTPUT_BIT.i - srcAccessMask = VkAccess.MEMORY_READ_BIT.i - dstAccessMask = VkAccess.COLOR_ATTACHMENT_READ_BIT or VkAccess.COLOR_ATTACHMENT_WRITE_BIT - dependencyFlags = VkDependency.BY_REGION_BIT.i - } - it[1].apply { - srcSubpass = 0 - dstSubpass = VK_SUBPASS_EXTERNAL - srcStageMask = VkPipelineStage.COLOR_ATTACHMENT_OUTPUT_BIT.i - dstStageMask = VkPipelineStage.BOTTOM_OF_PIPE_BIT.i - srcAccessMask = VkAccess.COLOR_ATTACHMENT_READ_BIT or VkAccess.COLOR_ATTACHMENT_WRITE_BIT - dstAccessMask = VkAccess.MEMORY_READ_BIT.i - dependencyFlags = VkDependency.BY_REGION_BIT.i - } - } - // Create the actual renderpass - val renderPassInfo = vk.RenderPassCreateInfo { - attachments = attchmentDescriptions - subpass = subpassDescription - this.dependencies = dependencies - } - offscreenPass.renderPass = device createRenderPass renderPassInfo - - val attachments = it.vkImageViewBufferOf(offscreenPass.color.view, offscreenPass.depth.view) - - val fbufCreateInfo = vk.FramebufferCreateInfo { - renderPass = offscreenPass.renderPass - this.attachments = attachments - extent(offscreenPass.size, 1) - } - offscreenPass.frameBuffer = device createFramebuffer fbufCreateInfo - - // Fill a descriptor for later use in a descriptor set - offscreenPass.descriptor = vk.DescriptorImageInfo { - imageLayout = VkImageLayout.SHADER_READ_ONLY_OPTIMAL - imageView = offscreenPass.color.view - sampler = offscreenPass.sampler - } - } - - /** Sets up the command buffer that renders the scene to the offscreen frame buffer */ - fun buildOffscreenCommandBuffer() { - - if (offscreenPass.commandBuffer == null) - offscreenPass.commandBuffer = super.createCommandBuffer(VkCommandBufferLevel.PRIMARY, false) - - if (offscreenPass.semaphore.L == NULL) { - // Create a semaphore used to synchronize offscreen rendering and usage - val semaphoreCreateInfo = vk.SemaphoreCreateInfo() - offscreenPass.semaphore = device createSemaphore semaphoreCreateInfo - } - - val cmdBufInfo = vk.CommandBufferBeginInfo() - - val clearValues = vk.ClearValue(2).also { - it[0].color(0f) - it[1].depthStencil(1f, 0) - } - val renderPassBeginInfo = vk.RenderPassBeginInfo { - renderPass = offscreenPass.renderPass - framebuffer = offscreenPass.frameBuffer - renderArea.extent(offscreenPass.size) - this.clearValues = clearValues - } - - offscreenPass.commandBuffer!!.apply { - - begin(cmdBufInfo) - - beginRenderPass(renderPassBeginInfo, VkSubpassContents.INLINE) - - setViewport(offscreenPass.size) - - setScissor(offscreenPass.size) - - // Mirrored scene - bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, pipelineLayouts.shaded, descriptorSets.offscreen) - bindPipeline(VkPipelineBindPoint.GRAPHICS, pipelines.shadedOffscreen) - bindVertexBuffers(VERTEX_BUFFER_BIND_ID, models.example.vertices.buffer) - bindIndexBuffer(models.example.indices.buffer, VkDeviceSize(0), VkIndexType.UINT32) - drawIndexed(models.example.indexCount, 1, 0, 0, 0) - - drawUI() - - endRenderPass() - - end() - } - } - - override fun buildCommandBuffers() { - - val cmdBufInfo = vk.CommandBufferBeginInfo() - - val clearValues = vk.ClearValue(2).also { - it[0].color(defaultClearColor) - it[1].depthStencil(1f, 0) - } - val renderPassBeginInfo = vk.RenderPassBeginInfo { - renderPass = this@OffscreenRendering.renderPass - renderArea.offset(0) - renderArea.extent(size) - this.clearValues = clearValues - } - for (i in drawCmdBuffers.indices) { - // Set target frame buffer - renderPassBeginInfo.framebuffer(frameBuffers[i].L) - - drawCmdBuffers[i].apply { - - begin(cmdBufInfo) - - beginRenderPass(renderPassBeginInfo, VkSubpassContents.INLINE) - - setViewport(size) - - setScissor(size) - - if (debugDisplay) { - bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, pipelineLayouts.textured, descriptorSets.debugQuad) - bindPipeline(VkPipelineBindPoint.GRAPHICS, pipelines.debug) - bindVertexBuffers(VERTEX_BUFFER_BIND_ID, models.quad.vertices.buffer) - bindIndexBuffer(models.quad.indices.buffer, VkDeviceSize(0), VkIndexType.UINT32) - drawIndexed(models.quad.indexCount, 1, 0, 0, 0) - } - - // Scene - - // Reflection plane - bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, pipelineLayouts.textured, descriptorSets.mirror) - bindPipeline(VkPipelineBindPoint.GRAPHICS, pipelines.mirror) - - bindVertexBuffers(VERTEX_BUFFER_BIND_ID, models.plane.vertices.buffer) - bindIndexBuffer(models.plane.indices.buffer, VkDeviceSize(0), VkIndexType.UINT32) - drawIndexed(models.plane.indexCount, 1, 0, 0, 0) - - // Model - bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, pipelineLayouts.shaded, descriptorSets.model) - bindPipeline(VkPipelineBindPoint.GRAPHICS, pipelines.shaded) - - bindVertexBuffers(VERTEX_BUFFER_BIND_ID, models.example.vertices.buffer) - bindIndexBuffer(models.example.indices.buffer, VkDeviceSize(0), VkIndexType.UINT32) - drawIndexed(models.example.indexCount, 1, 0, 0, 0) - - endRenderPass() - - end() - } - } - } - - fun loadAssets() { - - models.plane.loadFromFile("$assetPath/models/plane.obj", vertexLayout, 0.5f, vulkanDevice, queue) - models.example.loadFromFile("$assetPath/models/chinesedragon.dae", vertexLayout, 0.3f, vulkanDevice, queue) - - // Textures - val (texFormat, format) = vulkanDevice.features.run { - when { - textureCompressionBC -> "bc3" to VkFormat.BC3_UNORM_BLOCK - textureCompressionASTC_LDR -> "astc_8x8" to VkFormat.ASTC_8x8_UNORM_BLOCK - textureCompressionETC2 -> "etc2" to VkFormat.ETC2_R8G8B8_UNORM_BLOCK - else -> tools.exitFatal("Device does not support any compressed texture format!", ERROR_FEATURE_NOT_PRESENT) - } - } - textures.colorMap.loadFromFile("$assetPath/textures/darkmetal_${texFormat}_unorm.ktx", format, vulkanDevice, queue) - } - - fun generateQuad() = stak { - // Setup vertices for a single uv-mapped quad -// struct Vertex { -// float pos [3] -// float uv [2] -// float col [3] -// float normal [3] +///* +//* Vulkan Example - Offscreen rendering using a separate framebuffer +//* +//* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de +//* +//* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +//*/ +// +//package vulkan.basics +// +//import glm_.L +//import glm_.func.rad +//import glm_.glm +//import glm_.mat4x4.Mat4 +//import glm_.size +//import glm_.vec2.Vec2 +//import glm_.vec2.Vec2i +//import glm_.vec3.Vec3 +//import glm_.vec4.Vec4 +//import kool.adr +//import kool.stak +//import org.lwjgl.system.MemoryUtil.NULL +//import org.lwjgl.vulkan.VK10.VK_SUBPASS_EXTERNAL +//import org.lwjgl.vulkan.VkCommandBuffer +//import org.lwjgl.vulkan.VkDescriptorImageInfo +//import vkk.* +//import vulkan.VERTEX_BUFFER_BIND_ID +//import vulkan.assetPath +//import vulkan.base.* +// +// +//fun main(args: Array) { +// OffscreenRendering().apply { +// setupWindow() +// initVulkan() +// prepare() +// renderLoop() +// destroy() +// } +//} +// +//// Offscreen frame buffer properties +//private const val FB_DIM = 512 +//private val FB_COLOR_FORMAT = VkFormat.R8G8B8A8_UNORM +// +//private class OffscreenRendering : VulkanExampleBase() { +// +// var debugDisplay = false +// +// object textures { +// val colorMap = Texture2D() +// } +// +// // Vertex layout for the models +// val vertexLayout = VertexLayout( +// VertexComponent.POSITION, +// VertexComponent.UV, +// VertexComponent.COLOR, +// VertexComponent.NORMAL) +// +// object models { +// val example = Model() +// val quad = Model() +// val plane = Model() +// } +// +// object uniformBuffers { +// val vsShared = Buffer() +// val vsMirror = Buffer() +// val vsOffScreen = Buffer() +// val vsDebugQuad = Buffer() +// } +// +// object uboShared : Bufferizable() { +// lateinit var projection: Mat4 +// @Order(1) +// lateinit var model: Mat4 +// @Order(2) +// val lightPos = Vec4(0f, 0f, 0f, 1f) +// } +// +// object pipelines { +// var debug = VkPipeline(NULL) +// var shaded = VkPipeline(NULL) +// var shadedOffscreen = VkPipeline(NULL) +// var mirror = VkPipeline(NULL) +// } +// +// object pipelineLayouts { +// var textured = VkPipelineLayout(NULL) +// var shaded = VkPipelineLayout(NULL) +// } +// +// object descriptorSets { +// var offscreen = VkDescriptorSet(NULL) +// var mirror = VkDescriptorSet(NULL) +// var model = VkDescriptorSet(NULL) +// var debugQuad = VkDescriptorSet(NULL) +// } +// +// object descriptorSetLayouts { +// var textured = VkDescriptorSetLayout(NULL) +// var shaded = VkDescriptorSetLayout(NULL) +// } +// +// // Framebuffer for offscreen rendering +// class FrameBufferAttachment { +// var image = VkImage(NULL) +// var mem = VkDeviceMemory(NULL) +// var view = VkImageView(NULL) +// } +// +// object offscreenPass { +// val size = Vec2i() +// var frameBuffer = VkFramebuffer(NULL) +// val color = FrameBufferAttachment() +// val depth = FrameBufferAttachment() +// var renderPass = VkRenderPass(NULL) +// var sampler = VkSampler(NULL) +// lateinit var descriptor: VkDescriptorImageInfo +// var commandBuffer: VkCommandBuffer? = null +// // Semaphore used to synchronize between offscreen and final scene render pass +// var semaphore = VkSemaphore(NULL) +// } +// +// val meshPos = Vec3(0f, -1.5f, 0f) +// val meshRot = Vec3() +// +// init { +// zoom = -6f +// rotation(-2.5f, 0f, 0f) +// cameraPos(0f, 1f, 0f) +// timerSpeed *= 0.25f +// title = "Offscreen rendering" +//// settings.overlay = true +// enabledFeatures.shaderClipDistance = true +// } +// +// override fun destroy() { +// +// // Clean up used Vulkan resources +// // Note : Inherited destructor cleans up resources stored in base class +// +// // Textures +// textures.colorMap.destroy() +// +// device.apply { +// // Frame buffer +// +// // Color attachment +// destroyImageView(offscreenPass.color.view) +// destroyImage(offscreenPass.color.image) +// freeMemory(offscreenPass.color.mem) +// +// // Depth attachment +// destroyImageView(offscreenPass.depth.view) +// destroyImage(offscreenPass.depth.image) +// freeMemory(offscreenPass.depth.mem) +// +// destroyRenderPass(offscreenPass.renderPass) +// destroySampler(offscreenPass.sampler) +// destroyFramebuffer(offscreenPass.frameBuffer) +// +// destroyPipeline(pipelines.debug) +// destroyPipeline(pipelines.shaded) +// destroyPipeline(pipelines.shadedOffscreen) +// destroyPipeline(pipelines.mirror) +// +// destroyPipelineLayout(pipelineLayouts.textured) +// destroyPipelineLayout(pipelineLayouts.shaded) +// +// destroyDescriptorSetLayout(descriptorSetLayouts.shaded) +// destroyDescriptorSetLayout(descriptorSetLayouts.textured) +// +// // Models +// models.apply { +// example.destroy() +// quad.destroy() +// plane.destroy() +// } +// // Uniform buffers +// uniformBuffers.apply { +// vsShared.destroy() +// vsMirror.destroy() +// vsOffScreen.destroy() +// vsDebugQuad.destroy() +// } +// freeCommandBuffer(cmdPool, offscreenPass.commandBuffer!!) +// destroySemaphore(offscreenPass.semaphore) // } - - val QUAD_COLOR_NORMAL = floatArrayOf( - 1f, 1f, 1f, - 0f, 0f, 1f) - val vertexBuffer = it.floats( - 1f, 1f, 0f, 1f, 1f, *QUAD_COLOR_NORMAL, - 0f, 1f, 0f, 0f, 1f, *QUAD_COLOR_NORMAL, - 0f, 0f, 0f, 0f, 0f, *QUAD_COLOR_NORMAL, - 1f, 0f, 0f, 1f, 0f, *QUAD_COLOR_NORMAL) - val vertexBufferSize = VkDeviceSize(vertexBuffer.size.L) - - vulkanDevice.createBuffer( - VkBufferUsage.VERTEX_BUFFER_BIT.i, - VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, - vertexBufferSize, - models.quad.vertices::buffer, - models.quad.vertices::memory, - vertexBuffer.adr) - - // Setup indices - val indexBuffer = it.ints(0, 1, 2, 2, 3, 0) - val indexBufferSize = VkDeviceSize(indexBuffer.size.L) - models.quad.indexCount = indexBuffer.size - - vulkanDevice.createBuffer( - VkBufferUsage.INDEX_BUFFER_BIT.i, - VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, - indexBufferSize, - models.quad.indices::buffer, - models.quad.indices::memory, - indexBuffer.adr) - - models.quad.device = device - } - - fun setupDescriptorPool() { - - val poolSizes = vk.DescriptorPoolSize( - VkDescriptorType.UNIFORM_BUFFER, 6, - VkDescriptorType.COMBINED_IMAGE_SAMPLER, 8) - - val descriptorPoolInfo = vk.DescriptorPoolCreateInfo(poolSizes, 5) - - descriptorPool = device createDescriptorPool descriptorPoolInfo - } - - fun setupDescriptorSetLayout() { - - val setLayoutBindings = vk.DescriptorSetLayoutBinding( - // Binding 0 : Vertex shader uniform buffer - VkDescriptorType.UNIFORM_BUFFER, VkShaderStage.VERTEX_BIT.i, 0, - // Binding 1 : Fragment shader image sampler - VkDescriptorType.COMBINED_IMAGE_SAMPLER, VkShaderStage.FRAGMENT_BIT.i, 1, - // Binding 2 : Fragment shader image sampler - VkDescriptorType.COMBINED_IMAGE_SAMPLER, VkShaderStage.FRAGMENT_BIT.i, 2) - - // Shaded layouts (only use first layout binding, that is [0]) - var descriptorLayoutInfo = vk.DescriptorSetLayoutCreateInfo(setLayoutBindings[0]) - descriptorSetLayouts.shaded = device createDescriptorSetLayout descriptorLayoutInfo - - var pipelineLayoutInfo = vk.PipelineLayoutCreateInfo(descriptorSetLayouts.shaded) - pipelineLayouts.shaded = device createPipelineLayout pipelineLayoutInfo - - // Textured layouts (use all layout bindings) - descriptorLayoutInfo = vk.DescriptorSetLayoutCreateInfo(setLayoutBindings) - descriptorSetLayouts.textured = device createDescriptorSetLayout descriptorLayoutInfo - - pipelineLayoutInfo = vk.PipelineLayoutCreateInfo(descriptorSetLayouts.textured) - pipelineLayouts.textured = device createPipelineLayout pipelineLayoutInfo - } - - fun setupDescriptorSet() { - // Mirror plane descriptor set - val allocInfo = vk.DescriptorSetAllocateInfo(descriptorPool, descriptorSetLayouts.textured) - - descriptorSets.mirror = device allocateDescriptorSets allocInfo - - val writeDescriptorSets = vk.WriteDescriptorSet( - // Binding 0 : Vertex shader uniform buffer - descriptorSets.mirror, VkDescriptorType.UNIFORM_BUFFER, 0, uniformBuffers.vsMirror.descriptor, - // Binding 1 : Fragment shader texture sampler - descriptorSets.mirror, VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1, offscreenPass.descriptor, - // Binding 2 : Fragment shader texture sampler - descriptorSets.mirror, VkDescriptorType.COMBINED_IMAGE_SAMPLER, 2, textures.colorMap.descriptor) - - device updateDescriptorSets writeDescriptorSets - - // Debug quad - descriptorSets.debugQuad = device allocateDescriptorSets allocInfo - - val debugQuadWriteDescriptorSets = vk.WriteDescriptorSet( - // Binding 0 : Vertex shader uniform buffer - descriptorSets.debugQuad, VkDescriptorType.UNIFORM_BUFFER, 0, uniformBuffers.vsDebugQuad.descriptor, - // Binding 1 : Fragment shader texture sampler - descriptorSets.debugQuad, VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1, offscreenPass.descriptor) - - device updateDescriptorSets debugQuadWriteDescriptorSets - - // Shaded descriptor sets - allocInfo.setLayout = descriptorSetLayouts.shaded - - // Model - // No texture - descriptorSets.model = device allocateDescriptorSets allocInfo - - val modelWriteDescriptorSets = vk.WriteDescriptorSet( - // Binding 0 : Vertex shader uniform buffer - descriptorSets.model, VkDescriptorType.UNIFORM_BUFFER, 0, uniformBuffers.vsShared.descriptor) - - device updateDescriptorSets modelWriteDescriptorSets - - // Offscreen - descriptorSets.offscreen = device allocateDescriptorSets allocInfo - - val offScreenWriteDescriptorSets = vk.WriteDescriptorSet( - // Binding 0 : Vertex shader uniform buffer - descriptorSets.offscreen, VkDescriptorType.UNIFORM_BUFFER, 0, uniformBuffers.vsOffScreen.descriptor) - - device updateDescriptorSets offScreenWriteDescriptorSets - } - - fun preparePipelines() { - - val inputAssemblyState = vk.PipelineInputAssemblyStateCreateInfo(VkPrimitiveTopology.TRIANGLE_LIST, 0, false) - - val rasterizationState = vk.PipelineRasterizationStateCreateInfo(VkPolygonMode.FILL, VkCullMode.FRONT_BIT.i, VkFrontFace.CLOCKWISE) - - val blendAttachmentState = vk.PipelineColorBlendAttachmentState(0xf, false) - - val colorBlendState = vk.PipelineColorBlendStateCreateInfo(blendAttachmentState) - - val depthStencilState = vk.PipelineDepthStencilStateCreateInfo(true, true, VkCompareOp.LESS_OR_EQUAL) - - val viewportState = vk.PipelineViewportStateCreateInfo(1, 1) - - val multisampleState = vk.PipelineMultisampleStateCreateInfo(VkSampleCount.`1_BIT`) - - val dynamicStateEnables = listOf(VkDynamicState.VIEWPORT, VkDynamicState.SCISSOR) - val dynamicState = vk.PipelineDynamicStateCreateInfo(dynamicStateEnables) - - // Solid rendering pipeline - // Load shaders - val shaderStages = vk.PipelineShaderStageCreateInfo(2).also { - it[0].loadShader("$assetPath/shaders/offscreen/quad.vert.spv", VkShaderStage.VERTEX_BIT) - it[1].loadShader("$assetPath/shaders/offscreen/quad.frag.spv", VkShaderStage.FRAGMENT_BIT) - } - - // Vertex bindings and attributes - val vertexInputBindings = vk.VertexInputBindingDescription(0, vertexLayout.stride, VkVertexInputRate.VERTEX) - - val vertexInputAttributes = vk.VertexInputAttributeDescription( - 0, 0, VkFormat.R32G32B32_SFLOAT, 0, // Location 0: Position - 0, 1, VkFormat.R32G32_SFLOAT, Vec3.size, // Location 1: UV - 0, 2, VkFormat.R32G32B32_SFLOAT, Vec3.size + Vec2.size, // Location 2: Color - 0, 3, VkFormat.R32G32B32_SFLOAT, Vec3.size * 2 + Vec2.size) // Location 3: Normal - - val vertexInputState = vk.PipelineVertexInputStateCreateInfo { - vertexBindingDescription = vertexInputBindings - vertexAttributeDescriptions = vertexInputAttributes - } - val pipelineCI = vk.GraphicsPipelineCreateInfo(pipelineLayouts.textured, renderPass) - .also { - it.vertexInputState = vertexInputState - it.inputAssemblyState = inputAssemblyState - it.rasterizationState = rasterizationState - it.colorBlendState = colorBlendState - it.multisampleState = multisampleState - it.viewportState = viewportState - it.depthStencilState = depthStencilState - it.dynamicState = dynamicState - it.stages = shaderStages - } - pipelines.debug = device.createGraphicsPipelines(pipelineCache, pipelineCI) - - // Mirror - shaderStages[0].loadShader("$assetPath/shaders/offscreen/mirror.vert.spv", VkShaderStage.VERTEX_BIT) - shaderStages[1].loadShader("$assetPath/shaders/offscreen/mirror.frag.spv", VkShaderStage.FRAGMENT_BIT) - rasterizationState.cullMode = VkCullMode.NONE.i - pipelines.mirror = device.createGraphicsPipelines(pipelineCache, pipelineCI) - - // Flip culling - rasterizationState.cullMode = VkCullMode.BACK_BIT.i - - // Phong shading pipelines - pipelineCI.layout = pipelineLayouts.shaded - // Scene - shaderStages[0].loadShader("$assetPath/shaders/offscreen/phong.vert.spv", VkShaderStage.VERTEX_BIT) - shaderStages[1].loadShader("$assetPath/shaders/offscreen/phong.frag.spv", VkShaderStage.FRAGMENT_BIT) - pipelines.shaded = device.createGraphicsPipelines(pipelineCache, pipelineCI) - // Offscreen - // Flip culling - rasterizationState.cullMode = VkCullMode.FRONT_BIT.i - pipelineCI.renderPass = offscreenPass.renderPass - pipelines.shadedOffscreen = device.createGraphicsPipelines(pipelineCache, pipelineCI) - } - - /** Prepare and initialize uniform buffer containing shader uniforms */ - fun prepareUniformBuffers() { - // Mesh vertex shader uniform buffer block - vulkanDevice.createBuffer( - VkBufferUsage.UNIFORM_BUFFER_BIT.i, - VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, - uniformBuffers.vsShared, - VkDeviceSize(uboShared.size.L)) - - // Mirror plane vertex shader uniform buffer block - vulkanDevice.createBuffer( - VkBufferUsage.UNIFORM_BUFFER_BIT.i, - VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, - uniformBuffers.vsMirror, - VkDeviceSize(uboShared.size.L)) - - // Offscreen vertex shader uniform buffer block - vulkanDevice.createBuffer( - VkBufferUsage.UNIFORM_BUFFER_BIT.i, - VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, - uniformBuffers.vsOffScreen, - VkDeviceSize(uboShared.size.L)) - - // Debug quad vertex shader uniform buffer block - vulkanDevice.createBuffer( - VkBufferUsage.UNIFORM_BUFFER_BIT.i, - VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, - uniformBuffers.vsDebugQuad, - VkDeviceSize(uboShared.size.L)) - - // Map persistent - uniformBuffers.apply { - vsShared.map() - vsMirror.map() - vsOffScreen.map() - vsDebugQuad.map() - } - updateUniformBuffers() - updateUniformBufferOffscreen() - } - - fun updateUniformBuffers() { - // Mesh - uboShared.projection = glm.perspective(60f.rad, size.aspect, 0.1f, 256f) - val viewMatrix = glm.translate(Mat4(1f), Vec3(0f, 0f, zoom)) - - uboShared.model = viewMatrix * glm.translate(Mat4(1f), cameraPos) - .rotateAssign(rotation.x.rad, 1f, 0f, 0f) - .rotateAssign((rotation.y + meshRot.y).rad, 0f, 1f, 0f) - .rotateAssign(rotation.z.rad, 0f, 0f, 1f) - - uboShared.model = glm.translate(uboShared.model, meshPos) - - uboShared to uniformBuffers.vsShared.mapped - - // Mirror - uboShared.model = viewMatrix * glm.translate(Mat4(1f), cameraPos) - .rotateAssign(rotation.x.rad, 1f, 0f, 0f) - .rotateAssign(rotation.y.rad, 0f, 1f, 0f) - .rotateAssign(rotation.z.rad, 0f, 0f, 1f) - - uboShared to uniformBuffers.vsMirror.mapped - - // Debug quad - uboShared.projection = glm.ortho(4f, 0f, 0f, 4f * size.aspect, -1f, 1f) - uboShared.model = glm.translate(Mat4(1f), 0f, 0f, 0f) - - uboShared to uniformBuffers.vsDebugQuad.mapped - } - - fun updateUniformBufferOffscreen() { - - uboShared.projection = glm.perspective(60f.rad, size.aspect, 0.1f, 256f) - val viewMatrix = glm.translate(Mat4(1f), 0f, 0f, zoom) - - uboShared.model = viewMatrix * glm.translate(Mat4(1f), cameraPos) - .rotateAssign(rotation.x.rad, 1f, 0f, 0f) - .rotateAssign((rotation.y + meshRot.y).rad, 0f, 1f, 0f) - .rotateAssign(rotation.z.rad, 0f, 0f, 1f) - - uboShared.model = glm.scale(uboShared.model, 1f, -1f, 1f) - uboShared.model = glm.translate(uboShared.model, meshPos) - - uboShared to uniformBuffers.vsOffScreen.mapped - } - - fun draw() { - - super.prepareFrame() - - /* The scene render command buffer has to wait for the offscreen rendering to be finished before we can use - the framebuffer color image for sampling during final rendering - To ensure this we use a dedicated offscreen synchronization semaphore that will be signaled when offscreen - rendering has been finished - This is necessary as an implementation may start both command buffers at the same time, there is no guarantee - that command buffers will be executed in the order they have been submitted by the application */ - - // Offscreen rendering - - // Wait for swap chain presentation to finish - submitInfo.waitSemaphore = semaphores.presentComplete - // Signal ready with offscreen semaphore - submitInfo.signalSemaphore = offscreenPass.semaphore - - // Submit work - submitInfo.commandBuffer = offscreenPass.commandBuffer - queue submit submitInfo - - // Scene rendering - - // Wait for offscreen semaphore - submitInfo.waitSemaphore = offscreenPass.semaphore - // Signal ready with render complete semaphpre - submitInfo.signalSemaphore = semaphores.renderComplete - - // Submit work - submitInfo.commandBuffer = drawCmdBuffers[currentBuffer] - queue submit submitInfo - - super.submitFrame() - } - - override fun prepare() { - super.prepare() - loadAssets() - generateQuad() - prepareOffscreen() - prepareUniformBuffers() - setupDescriptorSetLayout() - preparePipelines() - setupDescriptorPool() - setupDescriptorSet() - buildCommandBuffers() - buildOffscreenCommandBuffer() - prepared = true - window.show() - } - - override fun render() { - if (!prepared) - return - draw() - if (!paused) { - meshRot.y += frameTimer * 10f - updateUniformBuffers() - updateUniformBufferOffscreen() - } - } - - override fun viewChanged() { - updateUniformBuffers() - updateUniformBufferOffscreen() - } - -// virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) -// { -// if (overlay->header("Settings")) { -// if (overlay->checkBox("Display render target", &debugDisplay)) { +// super.destroy() +// } +// +// /** Setup the offscreen framebuffer for rendering the mirrored scene +// * The color attachment of this framebuffer will then be used to sample from in the fragment shader of the final pass */ +// fun prepareOffscreen() = stak { +// +// offscreenPass.size put FB_DIM +// +// // Find a suitable depth format +// val fbDepthFormat = tools getSupportedDepthFormat physicalDevice +// assert(fbDepthFormat != VkFormat.UNDEFINED) +// +// // Color attachment +// val image = vk.ImageCreateInfo { +// imageType = VkImageType.`2D` +// format = FB_COLOR_FORMAT +// extent(offscreenPass.size, 1) +// mipLevels = 1 +// arrayLayers = 1 +// samples = VkSampleCount.`1_BIT` +// tiling = VkImageTiling.OPTIMAL +// // We will sample directly from the color attachment +// usage = VkImageUsage.COLOR_ATTACHMENT_BIT or VkImageUsage.SAMPLED_BIT +// } +// +// offscreenPass.color.image = device createImage image +// val memReqs = device getImageMemoryRequirements offscreenPass.color.image +// val memAlloc = vk.MemoryAllocateInfo { +// allocationSize = memReqs.size +// memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryProperty.DEVICE_LOCAL_BIT) +// } +// offscreenPass.color.mem = device allocateMemory memAlloc +// device.bindImageMemory(offscreenPass.color.image, offscreenPass.color.mem) +// +// val colorImageView = vk.ImageViewCreateInfo { +// viewType = VkImageViewType.`2D` +// format = FB_COLOR_FORMAT +// subresourceRange.apply { +// aspectMask = VkImageAspect.COLOR_BIT.i +// baseMipLevel = 0 +// levelCount = 1 +// baseArrayLayer = 0 +// layerCount = 1 +// } +// this.image = offscreenPass.color.image +// } +// offscreenPass.color.view = device createImageView colorImageView +// +// // Create sampler to sample from the attachment in the fragment shader +// val samplerInfo = vk.SamplerCreateInfo { +// magFilter = VkFilter.LINEAR +// minFilter = VkFilter.LINEAR +// mipmapMode = VkSamplerMipmapMode.LINEAR +// addressModeU = VkSamplerAddressMode.CLAMP_TO_EDGE +// addressModeV = addressModeU // TODO custom func? +// addressModeW = addressModeU +// mipLodBias = 0f +// maxAnisotropy = 1f +// minLod = 0f +// maxLod = 1f +// borderColor = VkBorderColor.FLOAT_OPAQUE_WHITE +// } +// offscreenPass.sampler = device createSampler samplerInfo +// +// // Depth stencil attachment +// image.format = fbDepthFormat +// image.usage = VkImageUsage.DEPTH_STENCIL_ATTACHMENT_BIT.i +// +// offscreenPass.depth.image = device createImage image +// device.getImageMemoryRequirements(offscreenPass.depth.image, memReqs) +// memAlloc.allocationSize = memReqs.size +// memAlloc.memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryProperty.DEVICE_LOCAL_BIT) +// offscreenPass.depth.mem = device allocateMemory memAlloc +// device.bindImageMemory(offscreenPass.depth.image, offscreenPass.depth.mem) +// +// val depthStencilView = vk.ImageViewCreateInfo { +// viewType = VkImageViewType.`2D` +// format = fbDepthFormat +// flags = 0 +// subresourceRange.apply { +// aspectMask = VkImageAspect.DEPTH_BIT or VkImageAspect.STENCIL_BIT +// baseMipLevel = 0 +// levelCount = 1 +// baseArrayLayer = 0 +// layerCount = 1 +// } +// this.image = offscreenPass.depth.image +// } +// offscreenPass.depth.view = device createImageView depthStencilView +// +// // Create a separate render pass for the offscreen rendering as it may differ from the one used for scene rendering +// +// val attchmentDescriptions = vk.AttachmentDescription(2).also { +// // Color attachment +// it[0].apply { +// format = FB_COLOR_FORMAT +// samples = VkSampleCount.`1_BIT` +// loadOp = VkAttachmentLoadOp.CLEAR +// storeOp = VkAttachmentStoreOp.STORE +// stencilLoadOp = VkAttachmentLoadOp.DONT_CARE +// stencilStoreOp = VkAttachmentStoreOp.DONT_CARE +// initialLayout = VkImageLayout.UNDEFINED +// finalLayout = VkImageLayout.SHADER_READ_ONLY_OPTIMAL +// } +// // Depth attachment +// it[1].apply { +// format = fbDepthFormat +// samples = VkSampleCount.`1_BIT` +// loadOp = VkAttachmentLoadOp.CLEAR +// storeOp = VkAttachmentStoreOp.DONT_CARE +// stencilLoadOp = VkAttachmentLoadOp.DONT_CARE +// stencilStoreOp = VkAttachmentStoreOp.DONT_CARE +// initialLayout = VkImageLayout.UNDEFINED +// finalLayout = VkImageLayout.DEPTH_STENCIL_ATTACHMENT_OPTIMAL +// } +// } +// val colorReference = vk.AttachmentReference(0, VkImageLayout.COLOR_ATTACHMENT_OPTIMAL) +// val depthReference = vk.AttachmentReference(1, VkImageLayout.DEPTH_STENCIL_ATTACHMENT_OPTIMAL) +// +// val subpassDescription = vk.SubpassDescription { +// pipelineBindPoint = VkPipelineBindPoint.GRAPHICS +// colorAttachmentCount = 1 +// colorAttachment = colorReference +// depthStencilAttachment = depthReference +// } +// // Use subpass dependencies for layout transitions +// val dependencies = vk.SubpassDependency(2).also { +// it[0].apply { +// srcSubpass = VK_SUBPASS_EXTERNAL +// dstSubpass = 0 +// srcStageMask = VkPipelineStage.BOTTOM_OF_PIPE_BIT.i +// dstStageMask = VkPipelineStage.COLOR_ATTACHMENT_OUTPUT_BIT.i +// srcAccessMask = VkAccess.MEMORY_READ_BIT.i +// dstAccessMask = VkAccess.COLOR_ATTACHMENT_READ_BIT or VkAccess.COLOR_ATTACHMENT_WRITE_BIT +// dependencyFlags = VkDependency.BY_REGION_BIT.i +// } +// it[1].apply { +// srcSubpass = 0 +// dstSubpass = VK_SUBPASS_EXTERNAL +// srcStageMask = VkPipelineStage.COLOR_ATTACHMENT_OUTPUT_BIT.i +// dstStageMask = VkPipelineStage.BOTTOM_OF_PIPE_BIT.i +// srcAccessMask = VkAccess.COLOR_ATTACHMENT_READ_BIT or VkAccess.COLOR_ATTACHMENT_WRITE_BIT +// dstAccessMask = VkAccess.MEMORY_READ_BIT.i +// dependencyFlags = VkDependency.BY_REGION_BIT.i +// } +// } +// // Create the actual renderpass +// val renderPassInfo = vk.RenderPassCreateInfo { +// attachments = attchmentDescriptions +// subpass = subpassDescription +// this.dependencies = dependencies +// } +// offscreenPass.renderPass = device createRenderPass renderPassInfo +// +// val attachments = it.vkImageViewBufferOf(offscreenPass.color.view, offscreenPass.depth.view) +// +// val fbufCreateInfo = vk.FramebufferCreateInfo { +// renderPass = offscreenPass.renderPass +// this.attachments = attachments +// extent(offscreenPass.size, 1) +// } +// offscreenPass.frameBuffer = device createFramebuffer fbufCreateInfo +// +// // Fill a descriptor for later use in a descriptor set +// offscreenPass.descriptor = vk.DescriptorImageInfo { +// imageLayout = VkImageLayout.SHADER_READ_ONLY_OPTIMAL +// imageView = offscreenPass.color.view +// sampler = offscreenPass.sampler +// } +// } +// +// /** Sets up the command buffer that renders the scene to the offscreen frame buffer */ +// fun buildOffscreenCommandBuffer() { +// +// if (offscreenPass.commandBuffer == null) +// offscreenPass.commandBuffer = super.createCommandBuffer(VkCommandBufferLevel.PRIMARY, false) +// +// if (offscreenPass.semaphore.L == NULL) { +// // Create a semaphore used to synchronize offscreen rendering and usage +// val semaphoreCreateInfo = vk.SemaphoreCreateInfo() +// offscreenPass.semaphore = device createSemaphore semaphoreCreateInfo +// } +// +// val cmdBufInfo = vk.CommandBufferBeginInfo() +// +// val clearValues = vk.ClearValue(2).also { +// it[0].color(0f) +// it[1].depthStencil(1f, 0) +// } +// val renderPassBeginInfo = vk.RenderPassBeginInfo { +// renderPass = offscreenPass.renderPass +// framebuffer = offscreenPass.frameBuffer +// renderArea.extent(offscreenPass.size) +// this.clearValues = clearValues +// } +// +// offscreenPass.commandBuffer!!.apply { +// +// begin(cmdBufInfo) +// +// beginRenderPass(renderPassBeginInfo, VkSubpassContents.INLINE) +// +// setViewport(offscreenPass.size) +// +// setScissor(offscreenPass.size) +// +// // Mirrored scene +// bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, pipelineLayouts.shaded, descriptorSets.offscreen) +// bindPipeline(VkPipelineBindPoint.GRAPHICS, pipelines.shadedOffscreen) +// bindVertexBuffers(VERTEX_BUFFER_BIND_ID, models.example.vertices.buffer) +// bindIndexBuffer(models.example.indices.buffer, VkDeviceSize(0), VkIndexType.UINT32) +// drawIndexed(models.example.indexCount, 1, 0, 0, 0) +// +// drawUI() +// +// endRenderPass() +// +// end() +// } +// } +// +// override fun buildCommandBuffers() { +// +// val cmdBufInfo = vk.CommandBufferBeginInfo() +// +// val clearValues = vk.ClearValue(2).also { +// it[0].color(defaultClearColor) +// it[1].depthStencil(1f, 0) +// } +// val renderPassBeginInfo = vk.RenderPassBeginInfo { +// renderPass = this@OffscreenRendering.renderPass +// renderArea.offset(0) +// renderArea.extent(size) +// this.clearValues = clearValues +// } +// for (i in drawCmdBuffers.indices) { +// // Set target frame buffer +// renderPassBeginInfo.framebuffer(frameBuffers[i].L) +// +// drawCmdBuffers[i].apply { +// +// begin(cmdBufInfo) +// +// beginRenderPass(renderPassBeginInfo, VkSubpassContents.INLINE) +// +// setViewport(size) +// +// setScissor(size) +// +// if (debugDisplay) { +// bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, pipelineLayouts.textured, descriptorSets.debugQuad) +// bindPipeline(VkPipelineBindPoint.GRAPHICS, pipelines.debug) +// bindVertexBuffers(VERTEX_BUFFER_BIND_ID, models.quad.vertices.buffer) +// bindIndexBuffer(models.quad.indices.buffer, VkDeviceSize(0), VkIndexType.UINT32) +// drawIndexed(models.quad.indexCount, 1, 0, 0, 0) +// } +// +// // Scene +// +// // Reflection plane +// bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, pipelineLayouts.textured, descriptorSets.mirror) +// bindPipeline(VkPipelineBindPoint.GRAPHICS, pipelines.mirror) +// +// bindVertexBuffers(VERTEX_BUFFER_BIND_ID, models.plane.vertices.buffer) +// bindIndexBuffer(models.plane.indices.buffer, VkDeviceSize(0), VkIndexType.UINT32) +// drawIndexed(models.plane.indexCount, 1, 0, 0, 0) +// +// // Model +// bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, pipelineLayouts.shaded, descriptorSets.model) +// bindPipeline(VkPipelineBindPoint.GRAPHICS, pipelines.shaded) +// +// bindVertexBuffers(VERTEX_BUFFER_BIND_ID, models.example.vertices.buffer) +// bindIndexBuffer(models.example.indices.buffer, VkDeviceSize(0), VkIndexType.UINT32) +// drawIndexed(models.example.indexCount, 1, 0, 0, 0) +// +// endRenderPass() +// +// end() +// } +// } +// } +// +// fun loadAssets() { +// +// models.plane.loadFromFile("$assetPath/models/plane.obj", vertexLayout, 0.5f, vulkanDevice, queue) +// models.example.loadFromFile("$assetPath/models/chinesedragon.dae", vertexLayout, 0.3f, vulkanDevice, queue) +// +// // Textures +// val (texFormat, format) = vulkanDevice.features.run { +// when { +// textureCompressionBC -> "bc3" to VkFormat.BC3_UNORM_BLOCK +// textureCompressionASTC_LDR -> "astc_8x8" to VkFormat.ASTC_8x8_UNORM_BLOCK +// textureCompressionETC2 -> "etc2" to VkFormat.ETC2_R8G8B8_UNORM_BLOCK +// else -> tools.exitFatal("Device does not support any compressed texture format!", ERROR_FEATURE_NOT_PRESENT) +// } +// } +// textures.colorMap.loadFromFile("$assetPath/textures/darkmetal_${texFormat}_unorm.ktx", format, vulkanDevice, queue) +// } +// +// fun generateQuad() = stak { +// // Setup vertices for a single uv-mapped quad +//// struct Vertex { +//// float pos [3] +//// float uv [2] +//// float col [3] +//// float normal [3] +//// } +// +// val QUAD_COLOR_NORMAL = floatArrayOf( +// 1f, 1f, 1f, +// 0f, 0f, 1f) +// val vertexBuffer = it.floats( +// 1f, 1f, 0f, 1f, 1f, *QUAD_COLOR_NORMAL, +// 0f, 1f, 0f, 0f, 1f, *QUAD_COLOR_NORMAL, +// 0f, 0f, 0f, 0f, 0f, *QUAD_COLOR_NORMAL, +// 1f, 0f, 0f, 1f, 0f, *QUAD_COLOR_NORMAL) +// val vertexBufferSize = VkDeviceSize(vertexBuffer.size.L) +// +// vulkanDevice.createBuffer( +// VkBufferUsage.VERTEX_BUFFER_BIT.i, +// VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, +// vertexBufferSize, +// models.quad.vertices::buffer, +// models.quad.vertices::memory, +// vertexBuffer.adr) +// +// // Setup indices +// val indexBuffer = it.ints(0, 1, 2, 2, 3, 0) +// val indexBufferSize = VkDeviceSize(indexBuffer.size.L) +// models.quad.indexCount = indexBuffer.size +// +// vulkanDevice.createBuffer( +// VkBufferUsage.INDEX_BUFFER_BIT.i, +// VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, +// indexBufferSize, +// models.quad.indices::buffer, +// models.quad.indices::memory, +// indexBuffer.adr) +// +// models.quad.device = device +// } +// +// fun setupDescriptorPool() { +// +// val poolSizes = vk.DescriptorPoolSize( +// VkDescriptorType.UNIFORM_BUFFER, 6, +// VkDescriptorType.COMBINED_IMAGE_SAMPLER, 8) +// +// val descriptorPoolInfo = vk.DescriptorPoolCreateInfo(poolSizes, 5) +// +// descriptorPool = device createDescriptorPool descriptorPoolInfo +// } +// +// fun setupDescriptorSetLayout() { +// +// val setLayoutBindings = vk.DescriptorSetLayoutBinding( +// // Binding 0 : Vertex shader uniform buffer +// VkDescriptorType.UNIFORM_BUFFER, VkShaderStage.VERTEX_BIT.i, 0, +// // Binding 1 : Fragment shader image sampler +// VkDescriptorType.COMBINED_IMAGE_SAMPLER, VkShaderStage.FRAGMENT_BIT.i, 1, +// // Binding 2 : Fragment shader image sampler +// VkDescriptorType.COMBINED_IMAGE_SAMPLER, VkShaderStage.FRAGMENT_BIT.i, 2) +// +// // Shaded layouts (only use first layout binding, that is [0]) +// var descriptorLayoutInfo = vk.DescriptorSetLayoutCreateInfo(setLayoutBindings[0]) +// descriptorSetLayouts.shaded = device createDescriptorSetLayout descriptorLayoutInfo +// +// var pipelineLayoutInfo = vk.PipelineLayoutCreateInfo(descriptorSetLayouts.shaded) +// pipelineLayouts.shaded = device createPipelineLayout pipelineLayoutInfo +// +// // Textured layouts (use all layout bindings) +// descriptorLayoutInfo = vk.DescriptorSetLayoutCreateInfo(setLayoutBindings) +// descriptorSetLayouts.textured = device createDescriptorSetLayout descriptorLayoutInfo +// +// pipelineLayoutInfo = vk.PipelineLayoutCreateInfo(descriptorSetLayouts.textured) +// pipelineLayouts.textured = device createPipelineLayout pipelineLayoutInfo +// } +// +// fun setupDescriptorSet() { +// // Mirror plane descriptor set +// val allocInfo = vk.DescriptorSetAllocateInfo(descriptorPool, descriptorSetLayouts.textured) +// +// descriptorSets.mirror = device allocateDescriptorSets allocInfo +// +// val writeDescriptorSets = vk.WriteDescriptorSet( +// // Binding 0 : Vertex shader uniform buffer +// descriptorSets.mirror, VkDescriptorType.UNIFORM_BUFFER, 0, uniformBuffers.vsMirror.descriptor, +// // Binding 1 : Fragment shader texture sampler +// descriptorSets.mirror, VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1, offscreenPass.descriptor, +// // Binding 2 : Fragment shader texture sampler +// descriptorSets.mirror, VkDescriptorType.COMBINED_IMAGE_SAMPLER, 2, textures.colorMap.descriptor) +// +// device updateDescriptorSets writeDescriptorSets +// +// // Debug quad +// descriptorSets.debugQuad = device allocateDescriptorSets allocInfo +// +// val debugQuadWriteDescriptorSets = vk.WriteDescriptorSet( +// // Binding 0 : Vertex shader uniform buffer +// descriptorSets.debugQuad, VkDescriptorType.UNIFORM_BUFFER, 0, uniformBuffers.vsDebugQuad.descriptor, +// // Binding 1 : Fragment shader texture sampler +// descriptorSets.debugQuad, VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1, offscreenPass.descriptor) +// +// device updateDescriptorSets debugQuadWriteDescriptorSets +// +// // Shaded descriptor sets +// allocInfo.setLayout = descriptorSetLayouts.shaded +// +// // Model +// // No texture +// descriptorSets.model = device allocateDescriptorSets allocInfo +// +// val modelWriteDescriptorSets = vk.WriteDescriptorSet( +// // Binding 0 : Vertex shader uniform buffer +// descriptorSets.model, VkDescriptorType.UNIFORM_BUFFER, 0, uniformBuffers.vsShared.descriptor) +// +// device updateDescriptorSets modelWriteDescriptorSets +// +// // Offscreen +// descriptorSets.offscreen = device allocateDescriptorSets allocInfo +// +// val offScreenWriteDescriptorSets = vk.WriteDescriptorSet( +// // Binding 0 : Vertex shader uniform buffer +// descriptorSets.offscreen, VkDescriptorType.UNIFORM_BUFFER, 0, uniformBuffers.vsOffScreen.descriptor) +// +// device updateDescriptorSets offScreenWriteDescriptorSets +// } +// +// fun preparePipelines() { +// +// val inputAssemblyState = vk.PipelineInputAssemblyStateCreateInfo(VkPrimitiveTopology.TRIANGLE_LIST, 0, false) +// +// val rasterizationState = vk.PipelineRasterizationStateCreateInfo(VkPolygonMode.FILL, VkCullMode.FRONT_BIT.i, VkFrontFace.CLOCKWISE) +// +// val blendAttachmentState = vk.PipelineColorBlendAttachmentState(0xf, false) +// +// val colorBlendState = vk.PipelineColorBlendStateCreateInfo(blendAttachmentState) +// +// val depthStencilState = vk.PipelineDepthStencilStateCreateInfo(true, true, VkCompareOp.LESS_OR_EQUAL) +// +// val viewportState = vk.PipelineViewportStateCreateInfo(1, 1) +// +// val multisampleState = vk.PipelineMultisampleStateCreateInfo(VkSampleCount.`1_BIT`) +// +// val dynamicStateEnables = listOf(VkDynamicState.VIEWPORT, VkDynamicState.SCISSOR) +// val dynamicState = vk.PipelineDynamicStateCreateInfo(dynamicStateEnables) +// +// // Solid rendering pipeline +// // Load shaders +// val shaderStages = vk.PipelineShaderStageCreateInfo(2).also { +// it[0].loadShader("$assetPath/shaders/offscreen/quad.vert.spv", VkShaderStage.VERTEX_BIT) +// it[1].loadShader("$assetPath/shaders/offscreen/quad.frag.spv", VkShaderStage.FRAGMENT_BIT) +// } +// +// // Vertex bindings and attributes +// val vertexInputBindings = vk.VertexInputBindingDescription(0, vertexLayout.stride, VkVertexInputRate.VERTEX) +// +// val vertexInputAttributes = vk.VertexInputAttributeDescription( +// 0, 0, VkFormat.R32G32B32_SFLOAT, 0, // Location 0: Position +// 0, 1, VkFormat.R32G32_SFLOAT, Vec3.size, // Location 1: UV +// 0, 2, VkFormat.R32G32B32_SFLOAT, Vec3.size + Vec2.size, // Location 2: Color +// 0, 3, VkFormat.R32G32B32_SFLOAT, Vec3.size * 2 + Vec2.size) // Location 3: Normal +// +// val vertexInputState = vk.PipelineVertexInputStateCreateInfo { +// vertexBindingDescription = vertexInputBindings +// vertexAttributeDescriptions = vertexInputAttributes +// } +// val pipelineCI = vk.GraphicsPipelineCreateInfo(pipelineLayouts.textured, renderPass) +// .also { +// it.vertexInputState = vertexInputState +// it.inputAssemblyState = inputAssemblyState +// it.rasterizationState = rasterizationState +// it.colorBlendState = colorBlendState +// it.multisampleState = multisampleState +// it.viewportState = viewportState +// it.depthStencilState = depthStencilState +// it.dynamicState = dynamicState +// it.stages = shaderStages +// } +// pipelines.debug = device.createGraphicsPipelines(pipelineCache, pipelineCI) +// +// // Mirror +// shaderStages[0].loadShader("$assetPath/shaders/offscreen/mirror.vert.spv", VkShaderStage.VERTEX_BIT) +// shaderStages[1].loadShader("$assetPath/shaders/offscreen/mirror.frag.spv", VkShaderStage.FRAGMENT_BIT) +// rasterizationState.cullMode = VkCullMode.NONE.i +// pipelines.mirror = device.createGraphicsPipelines(pipelineCache, pipelineCI) +// +// // Flip culling +// rasterizationState.cullMode = VkCullMode.BACK_BIT.i +// +// // Phong shading pipelines +// pipelineCI.layout = pipelineLayouts.shaded +// // Scene +// shaderStages[0].loadShader("$assetPath/shaders/offscreen/phong.vert.spv", VkShaderStage.VERTEX_BIT) +// shaderStages[1].loadShader("$assetPath/shaders/offscreen/phong.frag.spv", VkShaderStage.FRAGMENT_BIT) +// pipelines.shaded = device.createGraphicsPipelines(pipelineCache, pipelineCI) +// // Offscreen +// // Flip culling +// rasterizationState.cullMode = VkCullMode.FRONT_BIT.i +// pipelineCI.renderPass = offscreenPass.renderPass +// pipelines.shadedOffscreen = device.createGraphicsPipelines(pipelineCache, pipelineCI) +// } +// +// /** Prepare and initialize uniform buffer containing shader uniforms */ +// fun prepareUniformBuffers() { +// // Mesh vertex shader uniform buffer block +// vulkanDevice.createBuffer( +// VkBufferUsage.UNIFORM_BUFFER_BIT.i, +// VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, +// uniformBuffers.vsShared, +// VkDeviceSize(uboShared.size.L)) +// +// // Mirror plane vertex shader uniform buffer block +// vulkanDevice.createBuffer( +// VkBufferUsage.UNIFORM_BUFFER_BIT.i, +// VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, +// uniformBuffers.vsMirror, +// VkDeviceSize(uboShared.size.L)) +// +// // Offscreen vertex shader uniform buffer block +// vulkanDevice.createBuffer( +// VkBufferUsage.UNIFORM_BUFFER_BIT.i, +// VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, +// uniformBuffers.vsOffScreen, +// VkDeviceSize(uboShared.size.L)) +// +// // Debug quad vertex shader uniform buffer block +// vulkanDevice.createBuffer( +// VkBufferUsage.UNIFORM_BUFFER_BIT.i, +// VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, +// uniformBuffers.vsDebugQuad, +// VkDeviceSize(uboShared.size.L)) +// +// // Map persistent +// uniformBuffers.apply { +// vsShared.map() +// vsMirror.map() +// vsOffScreen.map() +// vsDebugQuad.map() +// } +// updateUniformBuffers() +// updateUniformBufferOffscreen() +// } +// +// fun updateUniformBuffers() { +// // Mesh +// uboShared.projection = glm.perspective(60f.rad, size.aspect, 0.1f, 256f) +// val viewMatrix = glm.translate(Mat4(1f), Vec3(0f, 0f, zoom)) +// +// uboShared.model = viewMatrix * glm.translate(Mat4(1f), cameraPos) +// .rotateAssign(rotation.x.rad, 1f, 0f, 0f) +// .rotateAssign((rotation.y + meshRot.y).rad, 0f, 1f, 0f) +// .rotateAssign(rotation.z.rad, 0f, 0f, 1f) +// +// uboShared.model = glm.translate(uboShared.model, meshPos) +// +// uboShared to uniformBuffers.vsShared.mapped +// +// // Mirror +// uboShared.model = viewMatrix * glm.translate(Mat4(1f), cameraPos) +// .rotateAssign(rotation.x.rad, 1f, 0f, 0f) +// .rotateAssign(rotation.y.rad, 0f, 1f, 0f) +// .rotateAssign(rotation.z.rad, 0f, 0f, 1f) +// +// uboShared to uniformBuffers.vsMirror.mapped +// +// // Debug quad +// uboShared.projection = glm.ortho(4f, 0f, 0f, 4f * size.aspect, -1f, 1f) +// uboShared.model = glm.translate(Mat4(1f), 0f, 0f, 0f) +// +// uboShared to uniformBuffers.vsDebugQuad.mapped +// } +// +// fun updateUniformBufferOffscreen() { +// +// uboShared.projection = glm.perspective(60f.rad, size.aspect, 0.1f, 256f) +// val viewMatrix = glm.translate(Mat4(1f), 0f, 0f, zoom) +// +// uboShared.model = viewMatrix * glm.translate(Mat4(1f), cameraPos) +// .rotateAssign(rotation.x.rad, 1f, 0f, 0f) +// .rotateAssign((rotation.y + meshRot.y).rad, 0f, 1f, 0f) +// .rotateAssign(rotation.z.rad, 0f, 0f, 1f) +// +// uboShared.model = glm.scale(uboShared.model, 1f, -1f, 1f) +// uboShared.model = glm.translate(uboShared.model, meshPos) +// +// uboShared to uniformBuffers.vsOffScreen.mapped +// } +// +// fun draw() { +// +// super.prepareFrame() +// +// /* The scene render command buffer has to wait for the offscreen rendering to be finished before we can use +// the framebuffer color image for sampling during final rendering +// To ensure this we use a dedicated offscreen synchronization semaphore that will be signaled when offscreen +// rendering has been finished +// This is necessary as an implementation may start both command buffers at the same time, there is no guarantee +// that command buffers will be executed in the order they have been submitted by the application */ +// +// // Offscreen rendering +// +// // Wait for swap chain presentation to finish +// submitInfo.waitSemaphore = semaphores.presentComplete +// // Signal ready with offscreen semaphore +// submitInfo.signalSemaphore = offscreenPass.semaphore +// +// // Submit work +// submitInfo.commandBuffer = offscreenPass.commandBuffer +// queue submit submitInfo +// +// // Scene rendering +// +// // Wait for offscreen semaphore +// submitInfo.waitSemaphore = offscreenPass.semaphore +// // Signal ready with render complete semaphpre +// submitInfo.signalSemaphore = semaphores.renderComplete +// +// // Submit work +// submitInfo.commandBuffer = drawCmdBuffers[currentBuffer] +// queue submit submitInfo +// +// super.submitFrame() +// } +// +// override fun prepare() { +// super.prepare() +// loadAssets() +// generateQuad() +// prepareOffscreen() +// prepareUniformBuffers() +// setupDescriptorSetLayout() +// preparePipelines() +// setupDescriptorPool() +// setupDescriptorSet() // buildCommandBuffers() +// buildOffscreenCommandBuffer() +// prepared = true +// window.show() // } +// +// override fun render() { +// if (!prepared) +// return +// draw() +// if (!paused) { +// meshRot.y += frameTimer * 10f +// updateUniformBuffers() +// updateUniformBufferOffscreen() +// } // } +// +// override fun viewChanged() { +// updateUniformBuffers() +// updateUniformBufferOffscreen() // } -} \ No newline at end of file +// +//// virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) +//// { +//// if (overlay->header("Settings")) { +//// if (overlay->checkBox("Display render target", &debugDisplay)) { +//// buildCommandBuffers() +//// } +//// } +//// } +//} \ No newline at end of file diff --git a/src/main/kotlin/vulkan/computeShader/01 Image Processing.kt b/src/main/kotlin/vulkan/computeShader/01 Image Processing.kt index ac3f6fd..09fc221 100644 --- a/src/main/kotlin/vulkan/computeShader/01 Image Processing.kt +++ b/src/main/kotlin/vulkan/computeShader/01 Image Processing.kt @@ -1,634 +1,634 @@ -///* -//* Vulkan Example - Compute shader image processing -//* -//* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de -//* -//* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -//*/ -// -//package vulkan.computeShader -// -//import glm_.L -//import glm_.f -//import glm_.func.rad -//import glm_.glm -//import glm_.mat4x4.Mat4 -//import glm_.vec2.Vec2 -//import glm_.vec2.Vec2i -//import glm_.vec3.Vec3 -//import kool.bufferBig -//import kool.cap -//import kool.stak -//import org.lwjgl.system.MemoryUtil.* -//import org.lwjgl.vulkan.* -//import vkk.* -//import vulkan.UINT64_MAX -//import vulkan.VERTEX_BUFFER_BIND_ID -//import vulkan.assetPath -//import vulkan.base.* -//import vulkan.base.tools.VK_FLAGS_NONE -// -// -//fun main(args: Array) { -// ImageProcessing().apply { -// setupWindow() -// initVulkan() -// prepare() -// renderLoop() -// destroy() -// } -//} -// -//// Vertex layout for this example -//private val Vertex = object { -// // float pos[3]; -//// float uv[2]; -// val size = Vec3.size + Vec2.size -// val offPos = 0 -// val offUv = Vec3.size -//} -// -//private class ImageProcessing : VulkanExampleBase() { -// -// val textureColorMap = Texture2D() -// val textureComputeTarget = Texture2D() -// -// private val vertices = object { -// lateinit var inputState: VkPipelineVertexInputStateCreateInfo -// lateinit var bindingDescriptions: VkVertexInputBindingDescription -// lateinit var attributeDescriptions: VkVertexInputAttributeDescription.Buffer -// } -// -// /** Resources for the graphics part of the example */ -// private val graphics = object { -// var descriptorSetLayout = VkDescriptorSetLayout(NULL) // Image display shader binding layout -// var descriptorSetPreCompute = VkDescriptorSet(NULL) // Image display shader bindings before compute shader image manipulation -// var descriptorSetPostCompute = VkDescriptorSet(NULL) // Image display shader bindings after compute shader image manipulation -// var pipeline = VkPipeline(NULL) // Image display pipeline -// var pipelineLayout = VkPipelineLayout(NULL) // Layout of the graphics pipeline -// } -// -// /** Resources for the compute part of the example */ -// private val compute = object { -// lateinit var queue: VkQueue // Separate queue for compute commands (queue family may differ from the one used for graphics) -// var commandPool = VkCommandPool(NULL) // Use a separate command pool (queue family may differ from the one used for graphics) -// lateinit var commandBuffer: VkCommandBuffer // Command buffer storing the dispatch commands and barriers -// var fence = VkFence(NULL) // Synchronization fence to avoid rewriting compute CB if still in use -// var descriptorSetLayout = VkDescriptorSetLayout(NULL) // Compute shader binding layout -// var descriptorSet = VkDescriptorSet(NULL) // Compute shader bindings -// var pipelineLayout = VkPipelineLayout(NULL) // Layout of the compute pipeline -// val pipelines = ArrayList() // Compute pipelines for image filters -// var pipelineIndex = 0 // Current image filtering compute pipeline index -// var queueFamilyIndex = 0 // Family index of the graphics queue, used for barriers -// } -// -// val vertexBuffer = Buffer() -// var indexBuffer = Buffer() -// var indexCount = 0 -// -// var uniformBufferVS = Buffer() -// -// private val uboVS = object { -// var projection = Mat4() -// var model = Mat4() -// -// fun pack() { -// projection to buffer -// model.to(buffer, Mat4.size) -// } -// -// val size = Mat4.size * 2 -// val buffer = bufferBig(size) -// val address = memAddress(buffer) -// } -// -// var vertexBufferSize = 0 -// -// val shaderNames = ArrayList() -// -// init { -// zoom = -2.0f -// title = "Compute shader image load/store" -//// settings.overlay = true -// } -// -// override fun destroy() { -// device.apply { -// // Graphics -// destroyPipeline(graphics.pipeline) -// destroyPipelineLayout(graphics.pipelineLayout) -// destroyDescriptorSetLayout(graphics.descriptorSetLayout) -// -// // Compute -// for (pipeline in compute.pipelines) -// destroyPipeline(pipeline) -// destroyPipelineLayout(compute.pipelineLayout) -// destroyDescriptorSetLayout(compute.descriptorSetLayout) -// destroyFence(compute.fence) -// destroyCommandPool(compute.commandPool) -// } -// vertexBuffer.destroy() -// indexBuffer.destroy() -// uniformBufferVS.destroy() -// -// textureColorMap.destroy() -// textureComputeTarget.destroy() -// -// super.destroy() -// } -// -// /** Prepare a texture target that is used to store compute shader calculations */ -// fun prepareTextureTarget(tex: Texture, size: Vec2i, format: VkFormat) { -// -// // Get device properties for the requested texture format -// val formatProperties = physicalDevice getFormatProperties format -// // Check if requested image format supports image storage operations -// assert(formatProperties.optimalTilingFeatures has VkFormatFeature.STORAGE_IMAGE_BIT) -// -// // Prepare blit target texture -// tex.size(size) -// -// val imageCreateInfo = vk.ImageCreateInfo { -// imageType = VkImageType.`2D` -// this.format = format -// extent.set(size.x, size.y, 1) -// mipLevels = 1 -// arrayLayers = 1 -// samples = VkSampleCount.`1_BIT` -// tiling = VkImageTiling.OPTIMAL -// // Image will be sampled in the fragment shader and used as storage target in the compute shader -// usage = VkImageUsage.SAMPLED_BIT or VkImageUsage.STORAGE_BIT -// flags = 0 -// // Sharing mode exclusive means that ownership of the image does not need to be explicitly transferred between the compute and graphics queue -// sharingMode = VkSharingMode.EXCLUSIVE -// } -// -// tex.image = device createImage imageCreateInfo -// -// val memReqs = device getImageMemoryRequirements tex.image -// val memAllocInfo = vk.MemoryAllocateInfo { -// allocationSize = memReqs.size -// memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryProperty.DEVICE_LOCAL_BIT) -// } -// tex.deviceMemory = device allocateMemory memAllocInfo -// device.bindImageMemory(tex.image, tex.deviceMemory) -// -// val layoutCmd = super.createCommandBuffer(VkCommandBufferLevel.PRIMARY, true) -// -// tex.imageLayout = VkImageLayout.GENERAL -// tools.setImageLayout( -// layoutCmd, tex.image, -// VkImageAspect.COLOR_BIT.i, -// VkImageLayout.UNDEFINED, -// tex.imageLayout) -// -// super.flushCommandBuffer(layoutCmd, queue, true) -// -// // Create sampler -// val sampler = vk.SamplerCreateInfo { -// magFilter = VkFilter.LINEAR -// minFilter = VkFilter.LINEAR -// mipmapMode = VkSamplerMipmapMode.LINEAR -// addressModeU = VkSamplerAddressMode.CLAMP_TO_BORDER -// addressModeV = addressModeU -// addressModeW = addressModeU -// mipLodBias = 0f -// maxAnisotropy = 1f -// compareOp = VkCompareOp.NEVER -// minLod = 0f -// maxLod = tex.mipLevels.f -// borderColor = VkBorderColor.FLOAT_OPAQUE_WHITE -// } -// tex.sampler = device createSampler sampler -// -// // Create image view -// val view = vk.ImageViewCreateInfo { -// image = VkImage(NULL) -// viewType = VkImageViewType.`2D` -// this.format = format -// components(VkComponentSwizzle.R, VkComponentSwizzle.G, VkComponentSwizzle.B, VkComponentSwizzle.A) -// subresourceRange.set(VkImageAspect.COLOR_BIT.i, 0, 1, 0, 1) -// image = tex.image -// } -// tex.view = device createImageView view -// -// // Initialize a descriptor for later use -// tex.descriptor.imageLayout = tex.imageLayout -// tex.descriptor.imageView = tex.view -// tex.descriptor.sampler = tex.sampler -// tex.device = vulkanDevice -// } -// -// fun loadAssets() { -// textureColorMap.loadFromFile( -// "$assetPath/textures/vulkan_11_rgba.ktx", -// VkFormat.R8G8B8A8_UNORM, -// vulkanDevice, queue, -// VkImageUsage.SAMPLED_BIT or VkImageUsage.STORAGE_BIT, -// VkImageLayout.GENERAL) -// } -// -// override fun buildCommandBuffers() { -// // Destroy command buffers if already present -// if (!checkCommandBuffers()) { -// destroyCommandBuffers() -// createCommandBuffers() -// } -// -// val cmdBufInfo = vk.CommandBufferBeginInfo() -// -// val clearValues = vk.ClearValue(2).also { -// it[0].color(defaultClearColor) -// it[1].depthStencil.set(1f, 0) -// } -// val renderPassBeginInfo = vk.RenderPassBeginInfo { -// renderPass = this@ImageProcessing.renderPass -// renderArea.apply { -// offset.set(0, 0) -// extent.set(size.x, size.y) -// } -// this.clearValues = clearValues -// } -// for (i in drawCmdBuffers.indices) { -// // Set target frame buffer -// renderPassBeginInfo.framebuffer(frameBuffers[i].L) -// -// drawCmdBuffers[i].apply { -// -// begin(cmdBufInfo) -// -// // Image memory barrier to make sure that compute shader writes are finished before sampling from the texture -// val imageMemoryBarrier = vk.ImageMemoryBarrier { -// // We won't be changing the layout of the image -// oldLayout = VkImageLayout.GENERAL -// newLayout = VkImageLayout.GENERAL -// image = textureComputeTarget.image -// subresourceRange.set(VkImageAspect.COLOR_BIT.i, 0, 1, 0, 1) -// srcAccessMask = VkAccess.SHADER_WRITE_BIT.i -// dstAccessMask = VkAccess.SHADER_READ_BIT.i -// } -// pipelineBarrier( -// VkPipelineStage.COMPUTE_SHADER_BIT.i, -// VkPipelineStage.FRAGMENT_SHADER_BIT.i, -// VK_FLAGS_NONE, -// imageMemoryBarrier = imageMemoryBarrier) -// beginRenderPass(renderPassBeginInfo, VkSubpassContents.INLINE) -// -// val viewport = vk.Viewport(size.x * 0.5f, size.y.f) -// setViewport(viewport) -// -// setScissor(size) -// -// bindVertexBuffers(VERTEX_BUFFER_BIND_ID, vertexBuffer.buffer) -// bindIndexBuffer(indexBuffer.buffer, VkDeviceSize(0), VkIndexType.UINT32) -// -// // Left (pre compute) -// bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, graphics.pipelineLayout, graphics.descriptorSetPreCompute) -// bindPipeline(VkPipelineBindPoint.GRAPHICS, graphics.pipeline) -// -// drawIndexed(indexCount, 1, 0, 0, 0) -// -// // Right (post compute) -// bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, graphics.pipelineLayout, graphics.descriptorSetPostCompute) -// bindPipeline(VkPipelineBindPoint.GRAPHICS, graphics.pipeline) -// -// viewport.x = size.x / 2f -// setViewport(viewport) -// drawIndexed(indexCount, 1, 0, 0, 0) -// -// drawUI() -// -// endRenderPass() -// -// end() -// } -// } -// } -// -// fun buildComputeCommandBuffer() { -// // Flush the queue if we're rebuilding the command buffer after a pipeline change to ensure it's not currently in use -// compute.queue.waitIdle() -// -// val cmdBufInfo = vk.CommandBufferBeginInfo() -// -// compute.commandBuffer.apply { -// -// begin(cmdBufInfo) -// -// bindPipeline(VkPipelineBindPoint.COMPUTE, compute.pipelines[compute.pipelineIndex]) -// bindDescriptorSets(VkPipelineBindPoint.COMPUTE, compute.pipelineLayout, compute.descriptorSet) -// -// compute.commandBuffer.dispatch(textureComputeTarget.size / 16, 1) -// -// end() -// } -// } -// -// /** Setup vertices for a single uv-mapped quad */ -// fun generateQuad() = stak { -// // Setup vertices for a single uv-mapped quad made from two triangles -// val vertices = it.floats( -// +1f, +1f, 0f, 1f, 1f, -// -1f, +1f, 0f, 0f, 1f, -// -1f, -1f, 0f, 0f, 0f, -// +1f, -1f, 0f, 1f, 0f) -// -// // Setup indices -// val indices = it.ints(0, 1, 2, 2, 3, 0) -// indexCount = indices.cap -// -// // Create buffers -// // For the sake of simplicity we won't stage the vertex data to the gpu memory -// // Vertex buffer -// vulkanDevice.createBuffer( -// VkBufferUsage.VERTEX_BUFFER_BIT.i, -// VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, -// vertexBuffer, -// vertices) -// // Index buffer -// vulkanDevice.createBuffer( -// VkBufferUsage.INDEX_BUFFER_BIT.i, -// VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, -// indexBuffer, -// indices) -// } -// -// fun setupVertexDescriptions() { -// // Binding description -// vertices.bindingDescriptions = vk.VertexInputBindingDescription(VERTEX_BUFFER_BIND_ID, Vertex.size, VkVertexInputRate.VERTEX) -// -// // Attribute descriptions -// // Describes memory layout and shader positions -// vertices.attributeDescriptions = vk.VertexInputAttributeDescription( -// // Location 0: Position -// VERTEX_BUFFER_BIND_ID, 0, VkFormat.R32G32B32_SFLOAT, Vertex.offPos, -// // Location 1: Texture coordinates -// VERTEX_BUFFER_BIND_ID, 1, VkFormat.R32G32_SFLOAT, Vertex.offUv) -// -// // Assign to vertex buffer -// vertices.inputState = vk.PipelineVertexInputStateCreateInfo { -// vertexBindingDescription = vertices.bindingDescriptions -// vertexAttributeDescriptions = vertices.attributeDescriptions -// } -// } -// -// fun setupDescriptorPool() { -// -// val poolSizes = vk.DescriptorPoolSize( -// // Graphics pipelines uniform buffers -// VkDescriptorType.UNIFORM_BUFFER, 2, -// // Graphics pipelines image samplers for displaying compute output image -// VkDescriptorType.COMBINED_IMAGE_SAMPLER, 2, -// // Compute pipelines uses a storage image for image reads and writes -// VkDescriptorType.STORAGE_IMAGE, 2) -// val descriptorPoolInfo = vk.DescriptorPoolCreateInfo(poolSizes, 3) -// descriptorPool = device createDescriptorPool descriptorPoolInfo -// } -// -// fun setupDescriptorSetLayout() { -// -// val setLayoutBindings = vk.DescriptorSetLayoutBinding( -// // Binding 0: Vertex shader uniform buffer -// VkDescriptorType.UNIFORM_BUFFER, VkShaderStage.VERTEX_BIT.i, 0, -// // Binding 1: Fragment shader input image -// VkDescriptorType.COMBINED_IMAGE_SAMPLER, VkShaderStage.FRAGMENT_BIT.i, 1) -// -// val descriptorLayout = vk.DescriptorSetLayoutCreateInfo(setLayoutBindings) -// graphics.descriptorSetLayout = device createDescriptorSetLayout descriptorLayout -// -// val pipelineLayoutCreateInfo = vk.PipelineLayoutCreateInfo(graphics.descriptorSetLayout) -// graphics.pipelineLayout = device createPipelineLayout pipelineLayoutCreateInfo -// } -// -// fun setupDescriptorSet() { -// -// val allocInfo = vk.DescriptorSetAllocateInfo(descriptorPool, graphics.descriptorSetLayout) -// -// // Input image (before compute post processing) -// graphics.descriptorSetPreCompute = device allocateDescriptorSets allocInfo -// val baseImageWriteDescriptorSets = vk.WriteDescriptorSet( -// graphics.descriptorSetPreCompute, VkDescriptorType.UNIFORM_BUFFER, 0, uniformBufferVS.descriptor, -// graphics.descriptorSetPreCompute, VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1, textureColorMap.descriptor) -// -// device updateDescriptorSets baseImageWriteDescriptorSets -// -// // Final image (after compute shader processing) -// graphics.descriptorSetPostCompute = device allocateDescriptorSets allocInfo -// val writeDescriptorSets = vk.WriteDescriptorSet( -// graphics.descriptorSetPostCompute, VkDescriptorType.UNIFORM_BUFFER, 0, uniformBufferVS.descriptor, -// graphics.descriptorSetPostCompute, VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1, textureComputeTarget.descriptor) -// -// device updateDescriptorSets writeDescriptorSets -// -// } -// -// fun preparePipelines() { -// -// val inputAssemblyState = vk.PipelineInputAssemblyStateCreateInfo(VkPrimitiveTopology.TRIANGLE_LIST, 0, false) -// -// val rasterizationState = vk.PipelineRasterizationStateCreateInfo(VkPolygonMode.FILL, VkCullMode.NONE.i, VkFrontFace.CLOCKWISE) -// -// val blendAttachmentState = vk.PipelineColorBlendAttachmentState(0xf, false) -// -// val colorBlendState = vk.PipelineColorBlendStateCreateInfo(blendAttachmentState) -// -// val depthStencilState = vk.PipelineDepthStencilStateCreateInfo(true, true, VkCompareOp.LESS_OR_EQUAL) -// -// val viewportState = vk.PipelineViewportStateCreateInfo(1, 1) -// -// val multisampleState = vk.PipelineMultisampleStateCreateInfo(VkSampleCount.`1_BIT`) -// -// val dynamicStateEnables = listOf(VkDynamicState.VIEWPORT, VkDynamicState.SCISSOR) -// val dynamicState = vk.PipelineDynamicStateCreateInfo(dynamicStateEnables) -// -// // Rendering pipeline -// // Load shaders -// val shaderStages = vk.PipelineShaderStageCreateInfo(2).also { -// it[0].loadShader("$assetPath/shaders/computeshader/texture.vert.spv", VkShaderStage.VERTEX_BIT) -// it[1].loadShader("$assetPath/shaders/computeshader/texture.frag.spv", VkShaderStage.FRAGMENT_BIT) -// } -// val pipelineCreateInfo = vk.GraphicsPipelineCreateInfo(graphics.pipelineLayout, renderPass).also { -// it.vertexInputState = vertices.inputState -// it.inputAssemblyState = inputAssemblyState -// it.rasterizationState = rasterizationState -// it.colorBlendState = colorBlendState -// it.multisampleState = multisampleState -// it.viewportState = viewportState -// it.depthStencilState = depthStencilState -// it.dynamicState = dynamicState -// it.stages = shaderStages -// it.renderPass = renderPass -// } -// graphics.pipeline = device.createGraphicsPipelines(pipelineCache, pipelineCreateInfo) -// } -// -// /** Find and create a compute capable device queue */ -// fun getComputeQueue() { -// -// val queueFamilyProperties = physicalDevice.queueFamilyProperties -// -// // Some devices have dedicated compute queues, so we first try to find a queue that supports compute and not graphics -// var computeQueueFound = false -// for (i in queueFamilyProperties.indices) -// if (queueFamilyProperties[i].queueFlags has VkQueueFlag.COMPUTE_BIT && queueFamilyProperties[i].queueFlags hasnt VkQueueFlag.GRAPHICS_BIT) { -// compute.queueFamilyIndex = i -// computeQueueFound = true -// break -// } -// // If there is no dedicated compute queue, just find the first queue family that supports compute -// if (!computeQueueFound) -// for (i in queueFamilyProperties.indices) { -// if (queueFamilyProperties[i].queueFlags has VkQueueFlag.COMPUTE_BIT) { -// compute.queueFamilyIndex = i -// computeQueueFound = true -// break -// } -// } -// -// // Compute is mandatory in Vulkan, so there must be at least one queue family that supports compute -// assert(computeQueueFound) -// // Get a compute queue from the device -// compute.queue = device getQueue compute.queueFamilyIndex -// } -// -// fun prepareCompute() { -// -// getComputeQueue() -// -// // Create compute pipeline -// // Compute pipelines are created separate from graphics pipelines even if they use the same queue -// -// val setLayoutBindings = vk.DescriptorSetLayoutBinding( -// // Binding 0: Input image (read-only) -// VkDescriptorType.STORAGE_IMAGE, VkShaderStage.COMPUTE_BIT.i, 0, -// // Binding 1: Output image (write) -// VkDescriptorType.STORAGE_IMAGE, VkShaderStage.COMPUTE_BIT.i, 1) -// -// val descriptorLayout = vk.DescriptorSetLayoutCreateInfo(setLayoutBindings) -// compute.descriptorSetLayout = device createDescriptorSetLayout descriptorLayout -// -// val pipelineLayoutCreateInfo = vk.PipelineLayoutCreateInfo(compute.descriptorSetLayout) -// -// compute.pipelineLayout = device createPipelineLayout pipelineLayoutCreateInfo -// -// val allocInfo = vk.DescriptorSetAllocateInfo(descriptorPool, compute.descriptorSetLayout) -// -// compute.descriptorSet = device allocateDescriptorSets allocInfo -// val computeWriteDescriptorSets = vk.WriteDescriptorSet( -// compute.descriptorSet, VkDescriptorType.STORAGE_IMAGE, 0, textureColorMap.descriptor, -// compute.descriptorSet, VkDescriptorType.STORAGE_IMAGE, 1, textureComputeTarget.descriptor) -// -// device updateDescriptorSets computeWriteDescriptorSets -// -// // Create compute shader pipelines -// val computePipelineCreateInfo = vk.ComputePipelineCreateInfo(compute.pipelineLayout) -// -// // One pipeline for each effect -// shaderNames += listOf("emboss", "edgedetect", "sharpen") -// for (shaderName in shaderNames) { -// val fileName = "$assetPath/shaders/computeshader/$shaderName.comp.spv" -// computePipelineCreateInfo.stage.loadShader(fileName, VkShaderStage.COMPUTE_BIT) -// val pipeline = device.createComputePipelines(pipelineCache, computePipelineCreateInfo) -// compute.pipelines += pipeline -// } -// // Separate command pool as queue family for compute may be different than graphics -// val cmdPoolInfo = vk.CommandPoolCreateInfo { -// queueFamilyIndex = compute.queueFamilyIndex -// flags = VkCommandPoolCreate.RESET_COMMAND_BUFFER_BIT.i -// } -// compute.commandPool = device createCommandPool cmdPoolInfo -// -// // Create a command buffer for compute operations -// val cmdBufAllocateInfo = vk.CommandBufferAllocateInfo(compute.commandPool, VkCommandBufferLevel.PRIMARY, 1) -// -// compute.commandBuffer = device allocateCommandBuffer cmdBufAllocateInfo -// -// // Fence for compute CB sync -// val fenceCreateInfo = vk.FenceCreateInfo(VkFenceCreate.SIGNALED_BIT.i) -// compute.fence = device createFence fenceCreateInfo -// -// // Build a single command buffer containing the compute dispatch commands +/* +* Vulkan Example - Compute shader image processing +* +* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de +* +* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +*/ + +package vulkan.computeShader + +import glm_.L +import glm_.f +import glm_.func.rad +import glm_.glm +import glm_.mat4x4.Mat4 +import glm_.vec2.Vec2 +import glm_.vec2.Vec2i +import glm_.vec3.Vec3 +import kool.bufferBig +import kool.cap +import kool.stak +import org.lwjgl.system.MemoryUtil.* +import org.lwjgl.vulkan.* +import vkk.* +import vulkan.UINT64_MAX +import vulkan.VERTEX_BUFFER_BIND_ID +import vulkan.assetPath +import vulkan.base.* +import vulkan.base.tools.VK_FLAGS_NONE + + +fun main(args: Array) { + ImageProcessing().apply { + setupWindow() + initVulkan() + prepare() + renderLoop() + destroy() + } +} + +// Vertex layout for this example +private val Vertex = object { + // float pos[3]; +// float uv[2]; + val size = Vec3.size + Vec2.size + val offPos = 0 + val offUv = Vec3.size +} + +private class ImageProcessing : VulkanExampleBase() { + + val textureColorMap = Texture2D() + val textureComputeTarget = Texture2D() + + private val vertices = object { + lateinit var inputState: VkPipelineVertexInputStateCreateInfo + lateinit var bindingDescriptions: VkVertexInputBindingDescription + lateinit var attributeDescriptions: VkVertexInputAttributeDescription.Buffer + } + + /** Resources for the graphics part of the example */ + private val graphics = object { + var descriptorSetLayout = VkDescriptorSetLayout(NULL) // Image display shader binding layout + var descriptorSetPreCompute = VkDescriptorSet(NULL) // Image display shader bindings before compute shader image manipulation + var descriptorSetPostCompute = VkDescriptorSet(NULL) // Image display shader bindings after compute shader image manipulation + var pipeline = VkPipeline(NULL) // Image display pipeline + var pipelineLayout = VkPipelineLayout(NULL) // Layout of the graphics pipeline + } + + /** Resources for the compute part of the example */ + private val compute = object { + lateinit var queue: VkQueue // Separate queue for compute commands (queue family may differ from the one used for graphics) + var commandPool = VkCommandPool(NULL) // Use a separate command pool (queue family may differ from the one used for graphics) + lateinit var commandBuffer: VkCommandBuffer // Command buffer storing the dispatch commands and barriers + var fence = VkFence(NULL) // Synchronization fence to avoid rewriting compute CB if still in use + var descriptorSetLayout = VkDescriptorSetLayout(NULL) // Compute shader binding layout + var descriptorSet = VkDescriptorSet(NULL) // Compute shader bindings + var pipelineLayout = VkPipelineLayout(NULL) // Layout of the compute pipeline + val pipelines = ArrayList() // Compute pipelines for image filters + var pipelineIndex = 0 // Current image filtering compute pipeline index + var queueFamilyIndex = 0 // Family index of the graphics queue, used for barriers + } + + val vertexBuffer = Buffer() + var indexBuffer = Buffer() + var indexCount = 0 + + var uniformBufferVS = Buffer() + + private val uboVS = object { + var projection = Mat4() + var model = Mat4() + + fun pack() { + projection to buffer + model.to(buffer, Mat4.size) + } + + val size = Mat4.size * 2 + val buffer = bufferBig(size) + val address = memAddress(buffer) + } + + var vertexBufferSize = 0 + + val shaderNames = ArrayList() + + init { + zoom = -2.0f + title = "Compute shader image load/store" +// settings.overlay = true + } + + override fun destroy() { + device.apply { + // Graphics + destroyPipeline(graphics.pipeline) + destroyPipelineLayout(graphics.pipelineLayout) + destroyDescriptorSetLayout(graphics.descriptorSetLayout) + + // Compute + for (pipeline in compute.pipelines) + destroyPipeline(pipeline) + destroyPipelineLayout(compute.pipelineLayout) + destroyDescriptorSetLayout(compute.descriptorSetLayout) + destroyFence(compute.fence) + destroyCommandPool(compute.commandPool) + } + vertexBuffer.destroy() + indexBuffer.destroy() + uniformBufferVS.destroy() + + textureColorMap.destroy() + textureComputeTarget.destroy() + + super.destroy() + } + + /** Prepare a texture target that is used to store compute shader calculations */ + fun prepareTextureTarget(tex: Texture, size: Vec2i, format: VkFormat) { + + // Get device properties for the requested texture format + val formatProperties = physicalDevice getFormatProperties format + // Check if requested image format supports image storage operations + assert(formatProperties.optimalTilingFeatures has VkFormatFeature.STORAGE_IMAGE_BIT) + + // Prepare blit target texture + tex.size(size) + + val imageCreateInfo = vk.ImageCreateInfo { + imageType = VkImageType.`2D` + this.format = format + extent.set(size.x, size.y, 1) + mipLevels = 1 + arrayLayers = 1 + samples = VkSampleCount.`1_BIT` + tiling = VkImageTiling.OPTIMAL + // Image will be sampled in the fragment shader and used as storage target in the compute shader + usage = VkImageUsage.SAMPLED_BIT or VkImageUsage.STORAGE_BIT + flags = 0 + // Sharing mode exclusive means that ownership of the image does not need to be explicitly transferred between the compute and graphics queue + sharingMode = VkSharingMode.EXCLUSIVE + } + + tex.image = device createImage imageCreateInfo + + val memReqs = device getImageMemoryRequirements tex.image + val memAllocInfo = vk.MemoryAllocateInfo { + allocationSize = memReqs.size + memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryProperty.DEVICE_LOCAL_BIT) + } + tex.deviceMemory = device allocateMemory memAllocInfo + device.bindImageMemory(tex.image, tex.deviceMemory) + + val layoutCmd = super.createCommandBuffer(VkCommandBufferLevel.PRIMARY, true) + + tex.imageLayout = VkImageLayout.GENERAL + tools.setImageLayout( + layoutCmd, tex.image, + VkImageAspect.COLOR_BIT.i, + VkImageLayout.UNDEFINED, + tex.imageLayout) + + super.flushCommandBuffer(layoutCmd, queue, true) + + // Create sampler + val sampler = vk.SamplerCreateInfo { + magFilter = VkFilter.LINEAR + minFilter = VkFilter.LINEAR + mipmapMode = VkSamplerMipmapMode.LINEAR + addressModeU = VkSamplerAddressMode.CLAMP_TO_BORDER + addressModeV = addressModeU + addressModeW = addressModeU + mipLodBias = 0f + maxAnisotropy = 1f + compareOp = VkCompareOp.NEVER + minLod = 0f + maxLod = tex.mipLevels.f + borderColor = VkBorderColor.FLOAT_OPAQUE_WHITE + } + tex.sampler = device createSampler sampler + + // Create image view + val view = vk.ImageViewCreateInfo { + image = VkImage(NULL) + viewType = VkImageViewType.`2D` + this.format = format + components(VkComponentSwizzle.R, VkComponentSwizzle.G, VkComponentSwizzle.B, VkComponentSwizzle.A) + subresourceRange.set(VkImageAspect.COLOR_BIT.i, 0, 1, 0, 1) + image = tex.image + } + tex.view = device createImageView view + + // Initialize a descriptor for later use + tex.descriptor.imageLayout = tex.imageLayout + tex.descriptor.imageView = tex.view + tex.descriptor.sampler = tex.sampler + tex.device = vulkanDevice + } + + fun loadAssets() { + textureColorMap.loadFromFile( + "$assetPath/textures/vulkan_11_rgba.ktx", + VkFormat.R8G8B8A8_UNORM, + vulkanDevice, queue, + VkImageUsage.SAMPLED_BIT or VkImageUsage.STORAGE_BIT, + VkImageLayout.GENERAL) + } + + override fun buildCommandBuffers() { + // Destroy command buffers if already present + if (!checkCommandBuffers()) { + destroyCommandBuffers() + createCommandBuffers() + } + + val cmdBufInfo = vk.CommandBufferBeginInfo() + + val clearValues = vk.ClearValue(2).also { + it[0].color(defaultClearColor) + it[1].depthStencil.set(1f, 0) + } + val renderPassBeginInfo = vk.RenderPassBeginInfo { + renderPass = this@ImageProcessing.renderPass + renderArea.apply { + offset.set(0, 0) + extent.set(size.x, size.y) + } + this.clearValues = clearValues + } + for (i in drawCmdBuffers.indices) { + // Set target frame buffer + renderPassBeginInfo.framebuffer(frameBuffers[i].L) + + drawCmdBuffers[i].apply { + + begin(cmdBufInfo) + + // Image memory barrier to make sure that compute shader writes are finished before sampling from the texture + val imageMemoryBarrier = vk.ImageMemoryBarrier { + // We won't be changing the layout of the image + oldLayout = VkImageLayout.GENERAL + newLayout = VkImageLayout.GENERAL + image = textureComputeTarget.image + subresourceRange.set(VkImageAspect.COLOR_BIT.i, 0, 1, 0, 1) + srcAccessMask = VkAccess.SHADER_WRITE_BIT.i + dstAccessMask = VkAccess.SHADER_READ_BIT.i + } + pipelineBarrier( + VkPipelineStage.COMPUTE_SHADER_BIT.i, + VkPipelineStage.FRAGMENT_SHADER_BIT.i, + VK_FLAGS_NONE, + imageMemoryBarrier = imageMemoryBarrier) + beginRenderPass(renderPassBeginInfo, VkSubpassContents.INLINE) + + val viewport = vk.Viewport(size.x * 0.5f, size.y.f) + setViewport(viewport) + + setScissor(size) + + bindVertexBuffers(VERTEX_BUFFER_BIND_ID, vertexBuffer.buffer) + bindIndexBuffer(indexBuffer.buffer, VkDeviceSize(0), VkIndexType.UINT32) + + // Left (pre compute) + bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, graphics.pipelineLayout, graphics.descriptorSetPreCompute) + bindPipeline(VkPipelineBindPoint.GRAPHICS, graphics.pipeline) + + drawIndexed(indexCount, 1, 0, 0, 0) + + // Right (post compute) + bindDescriptorSets(VkPipelineBindPoint.GRAPHICS, graphics.pipelineLayout, graphics.descriptorSetPostCompute) + bindPipeline(VkPipelineBindPoint.GRAPHICS, graphics.pipeline) + + viewport.x = size.x / 2f + setViewport(viewport) + drawIndexed(indexCount, 1, 0, 0, 0) + + drawUI() + + endRenderPass() + + end() + } + } + } + + fun buildComputeCommandBuffer() { + // Flush the queue if we're rebuilding the command buffer after a pipeline change to ensure it's not currently in use + compute.queue.waitIdle() + + val cmdBufInfo = vk.CommandBufferBeginInfo() + + compute.commandBuffer.apply { + + begin(cmdBufInfo) + + bindPipeline(VkPipelineBindPoint.COMPUTE, compute.pipelines[compute.pipelineIndex]) + bindDescriptorSets(VkPipelineBindPoint.COMPUTE, compute.pipelineLayout, compute.descriptorSet) + + compute.commandBuffer.dispatch(textureComputeTarget.size / 16, 1) + + end() + } + } + + /** Setup vertices for a single uv-mapped quad */ + fun generateQuad() = stak { + // Setup vertices for a single uv-mapped quad made from two triangles + val vertices = it.floats( + +1f, +1f, 0f, 1f, 1f, + -1f, +1f, 0f, 0f, 1f, + -1f, -1f, 0f, 0f, 0f, + +1f, -1f, 0f, 1f, 0f) + + // Setup indices + val indices = it.ints(0, 1, 2, 2, 3, 0) + indexCount = indices.cap + + // Create buffers + // For the sake of simplicity we won't stage the vertex data to the gpu memory + // Vertex buffer + vulkanDevice.createBuffer( + VkBufferUsage.VERTEX_BUFFER_BIT.i, + VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, + vertexBuffer, + vertices) + // Index buffer + vulkanDevice.createBuffer( + VkBufferUsage.INDEX_BUFFER_BIT.i, + VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, + indexBuffer, + indices) + } + + fun setupVertexDescriptions() { + // Binding description + vertices.bindingDescriptions = vk.VertexInputBindingDescription(VERTEX_BUFFER_BIND_ID, Vertex.size, VkVertexInputRate.VERTEX) + + // Attribute descriptions + // Describes memory layout and shader positions + vertices.attributeDescriptions = vk.VertexInputAttributeDescription( + // Location 0: Position + VERTEX_BUFFER_BIND_ID, 0, VkFormat.R32G32B32_SFLOAT, Vertex.offPos, + // Location 1: Texture coordinates + VERTEX_BUFFER_BIND_ID, 1, VkFormat.R32G32_SFLOAT, Vertex.offUv) + + // Assign to vertex buffer + vertices.inputState = vk.PipelineVertexInputStateCreateInfo { + vertexBindingDescription = vertices.bindingDescriptions + vertexAttributeDescriptions = vertices.attributeDescriptions + } + } + + fun setupDescriptorPool() { + + val poolSizes = vk.DescriptorPoolSize( + // Graphics pipelines uniform buffers + VkDescriptorType.UNIFORM_BUFFER, 2, + // Graphics pipelines image samplers for displaying compute output image + VkDescriptorType.COMBINED_IMAGE_SAMPLER, 2, + // Compute pipelines uses a storage image for image reads and writes + VkDescriptorType.STORAGE_IMAGE, 2) + val descriptorPoolInfo = vk.DescriptorPoolCreateInfo(poolSizes, 3) + descriptorPool = device createDescriptorPool descriptorPoolInfo + } + + fun setupDescriptorSetLayout() { + + val setLayoutBindings = vk.DescriptorSetLayoutBinding( + // Binding 0: Vertex shader uniform buffer + VkDescriptorType.UNIFORM_BUFFER, VkShaderStage.VERTEX_BIT.i, 0, + // Binding 1: Fragment shader input image + VkDescriptorType.COMBINED_IMAGE_SAMPLER, VkShaderStage.FRAGMENT_BIT.i, 1) + + val descriptorLayout = vk.DescriptorSetLayoutCreateInfo(setLayoutBindings) + graphics.descriptorSetLayout = device createDescriptorSetLayout descriptorLayout + + val pipelineLayoutCreateInfo = vk.PipelineLayoutCreateInfo(graphics.descriptorSetLayout) + graphics.pipelineLayout = device createPipelineLayout pipelineLayoutCreateInfo + } + + fun setupDescriptorSet() { + + val allocInfo = vk.DescriptorSetAllocateInfo(descriptorPool, graphics.descriptorSetLayout) + + // Input image (before compute post processing) + graphics.descriptorSetPreCompute = device allocateDescriptorSets allocInfo + val baseImageWriteDescriptorSets = vk.WriteDescriptorSet( + graphics.descriptorSetPreCompute, VkDescriptorType.UNIFORM_BUFFER, 0, uniformBufferVS.descriptor, + graphics.descriptorSetPreCompute, VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1, textureColorMap.descriptor) + + device updateDescriptorSets baseImageWriteDescriptorSets + + // Final image (after compute shader processing) + graphics.descriptorSetPostCompute = device allocateDescriptorSets allocInfo + val writeDescriptorSets = vk.WriteDescriptorSet( + graphics.descriptorSetPostCompute, VkDescriptorType.UNIFORM_BUFFER, 0, uniformBufferVS.descriptor, + graphics.descriptorSetPostCompute, VkDescriptorType.COMBINED_IMAGE_SAMPLER, 1, textureComputeTarget.descriptor) + + device updateDescriptorSets writeDescriptorSets + + } + + fun preparePipelines() { + + val inputAssemblyState = vk.PipelineInputAssemblyStateCreateInfo(VkPrimitiveTopology.TRIANGLE_LIST, 0, false) + + val rasterizationState = vk.PipelineRasterizationStateCreateInfo(VkPolygonMode.FILL, VkCullMode.NONE.i, VkFrontFace.CLOCKWISE) + + val blendAttachmentState = vk.PipelineColorBlendAttachmentState(0xf, false) + + val colorBlendState = vk.PipelineColorBlendStateCreateInfo(blendAttachmentState) + + val depthStencilState = vk.PipelineDepthStencilStateCreateInfo(true, true, VkCompareOp.LESS_OR_EQUAL) + + val viewportState = vk.PipelineViewportStateCreateInfo(1, 1) + + val multisampleState = vk.PipelineMultisampleStateCreateInfo(VkSampleCount.`1_BIT`) + + val dynamicStateEnables = listOf(VkDynamicState.VIEWPORT, VkDynamicState.SCISSOR) + val dynamicState = vk.PipelineDynamicStateCreateInfo(dynamicStateEnables) + + // Rendering pipeline + // Load shaders + val shaderStages = vk.PipelineShaderStageCreateInfo(2).also { + it[0].loadShader("$assetPath/shaders/computeshader/texture.vert.spv", VkShaderStage.VERTEX_BIT) + it[1].loadShader("$assetPath/shaders/computeshader/texture.frag.spv", VkShaderStage.FRAGMENT_BIT) + } + val pipelineCreateInfo = vk.GraphicsPipelineCreateInfo(graphics.pipelineLayout, renderPass).also { + it.vertexInputState = vertices.inputState + it.inputAssemblyState = inputAssemblyState + it.rasterizationState = rasterizationState + it.colorBlendState = colorBlendState + it.multisampleState = multisampleState + it.viewportState = viewportState + it.depthStencilState = depthStencilState + it.dynamicState = dynamicState + it.stages = shaderStages + it.renderPass = renderPass + } + graphics.pipeline = device.createGraphicsPipelines(pipelineCache, pipelineCreateInfo) + } + + /** Find and create a compute capable device queue */ + fun getComputeQueue() { + + val queueFamilyProperties = physicalDevice.queueFamilyProperties + + // Some devices have dedicated compute queues, so we first try to find a queue that supports compute and not graphics + var computeQueueFound = false + for (i in queueFamilyProperties.indices) + if (queueFamilyProperties[i].queueFlags has VkQueueFlag.COMPUTE_BIT && queueFamilyProperties[i].queueFlags hasnt VkQueueFlag.GRAPHICS_BIT) { + compute.queueFamilyIndex = i + computeQueueFound = true + break + } + // If there is no dedicated compute queue, just find the first queue family that supports compute + if (!computeQueueFound) + for (i in queueFamilyProperties.indices) { + if (queueFamilyProperties[i].queueFlags has VkQueueFlag.COMPUTE_BIT) { + compute.queueFamilyIndex = i + computeQueueFound = true + break + } + } + + // Compute is mandatory in Vulkan, so there must be at least one queue family that supports compute + assert(computeQueueFound) + // Get a compute queue from the device + compute.queue = device getQueue compute.queueFamilyIndex + } + + fun prepareCompute() { + + getComputeQueue() + + // Create compute pipeline + // Compute pipelines are created separate from graphics pipelines even if they use the same queue + + val setLayoutBindings = vk.DescriptorSetLayoutBinding( + // Binding 0: Input image (read-only) + VkDescriptorType.STORAGE_IMAGE, VkShaderStage.COMPUTE_BIT.i, 0, + // Binding 1: Output image (write) + VkDescriptorType.STORAGE_IMAGE, VkShaderStage.COMPUTE_BIT.i, 1) + + val descriptorLayout = vk.DescriptorSetLayoutCreateInfo(setLayoutBindings) + compute.descriptorSetLayout = device createDescriptorSetLayout descriptorLayout + + val pipelineLayoutCreateInfo = vk.PipelineLayoutCreateInfo(compute.descriptorSetLayout) + + compute.pipelineLayout = device createPipelineLayout pipelineLayoutCreateInfo + + val allocInfo = vk.DescriptorSetAllocateInfo(descriptorPool, compute.descriptorSetLayout) + + compute.descriptorSet = device allocateDescriptorSets allocInfo + val computeWriteDescriptorSets = vk.WriteDescriptorSet( + compute.descriptorSet, VkDescriptorType.STORAGE_IMAGE, 0, textureColorMap.descriptor, + compute.descriptorSet, VkDescriptorType.STORAGE_IMAGE, 1, textureComputeTarget.descriptor) + + device updateDescriptorSets computeWriteDescriptorSets + + // Create compute shader pipelines + val computePipelineCreateInfo = vk.ComputePipelineCreateInfo(compute.pipelineLayout) + + // One pipeline for each effect + shaderNames += listOf("emboss", "edgedetect", "sharpen") + for (shaderName in shaderNames) { + val fileName = "$assetPath/shaders/computeshader/$shaderName.comp.spv" + computePipelineCreateInfo.stage.loadShader(fileName, VkShaderStage.COMPUTE_BIT) + val pipeline = device.createComputePipelines(pipelineCache, computePipelineCreateInfo) + compute.pipelines += pipeline + } + // Separate command pool as queue family for compute may be different than graphics + val cmdPoolInfo = vk.CommandPoolCreateInfo { + queueFamilyIndex = compute.queueFamilyIndex + flags = VkCommandPoolCreate.RESET_COMMAND_BUFFER_BIT.i + } + compute.commandPool = device createCommandPool cmdPoolInfo + + // Create a command buffer for compute operations + val cmdBufAllocateInfo = vk.CommandBufferAllocateInfo(compute.commandPool, VkCommandBufferLevel.PRIMARY, 1) + + compute.commandBuffer = device allocateCommandBuffer cmdBufAllocateInfo + + // Fence for compute CB sync + val fenceCreateInfo = vk.FenceCreateInfo(VkFenceCreate.SIGNALED_BIT.i) + compute.fence = device createFence fenceCreateInfo + + // Build a single command buffer containing the compute dispatch commands + buildComputeCommandBuffer() + } + + /** Prepare and initialize uniform buffer containing shader uniforms */ + fun prepareUniformBuffers() { + // Vertex shader uniform buffer block + vulkanDevice.createBuffer( + VkBufferUsage.UNIFORM_BUFFER_BIT.i, + VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, + uniformBufferVS, + VkDeviceSize(uboVS.size.L)) + + // Map persistent + uniformBufferVS.map() + + updateUniformBuffers() + } + + fun updateUniformBuffers() { + // Vertex shader uniform buffer block + uboVS.projection = glm.perspective(60f.rad, size.x * 0.5f / size.y, 0.1f, 256f) + val viewMatrix = glm.translate(Mat4(1f), 0f, 0f, zoom) + + uboVS.model = viewMatrix * glm.translate(Mat4(1f), cameraPos) + .rotateAssign(rotation.x.rad, 1f, 0f, 0f) + .rotateAssign(rotation.y.rad, 0f, 1f, 0f) + .rotateAssign(rotation.z.rad, 0f, 0f, 1f) + + uboVS.pack() + memCopy(uboVS.address, uniformBufferVS.mapped, uboVS.size.L) + } + + fun draw() { + + super.prepareFrame() + + submitInfo.commandBuffer = drawCmdBuffers[currentBuffer] + queue submit submitInfo + + super.submitFrame() + + // Submit compute commands + // Use a fence to ensure that compute command buffer has finished executin before using it again + device.waitForFence(compute.fence, true, UINT64_MAX) + device.resetFence(compute.fence) + + val computeSubmitInfo = vk.SubmitInfo { commandBuffer = compute.commandBuffer } + compute.queue.submit(computeSubmitInfo, compute.fence) + } + + override fun prepare() { + super.prepare() + loadAssets() + generateQuad() + setupVertexDescriptions() + prepareUniformBuffers() + prepareTextureTarget(textureComputeTarget, textureColorMap.size, VkFormat.R8G8B8A8_UNORM) + setupDescriptorSetLayout() + preparePipelines() + setupDescriptorPool() + setupDescriptorSet() + prepareCompute() + buildCommandBuffers() + prepared = true + window.show() + } + + override fun render() { + if (!prepared) + return + draw() + } + + override fun viewChanged() = updateUniformBuffers() + +// virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) +// { +// if (overlay->header("Settings")) { +// if (overlay->comboBox("Shader", &compute.pipelineIndex, shaderNames)) { // buildComputeCommandBuffer() // } -// -// /** Prepare and initialize uniform buffer containing shader uniforms */ -// fun prepareUniformBuffers() { -// // Vertex shader uniform buffer block -// vulkanDevice.createBuffer( -// VkBufferUsage.UNIFORM_BUFFER_BIT.i, -// VkMemoryProperty.HOST_VISIBLE_BIT or VkMemoryProperty.HOST_COHERENT_BIT, -// uniformBufferVS, -// VkDeviceSize(uboVS.size.L)) -// -// // Map persistent -// uniformBufferVS.map() -// -// updateUniformBuffers() -// } -// -// fun updateUniformBuffers() { -// // Vertex shader uniform buffer block -// uboVS.projection = glm.perspective(60f.rad, size.x * 0.5f / size.y, 0.1f, 256f) -// val viewMatrix = glm.translate(Mat4(1f), 0f, 0f, zoom) -// -// uboVS.model = viewMatrix * glm.translate(Mat4(1f), cameraPos) -// .rotateAssign(rotation.x.rad, 1f, 0f, 0f) -// .rotateAssign(rotation.y.rad, 0f, 1f, 0f) -// .rotateAssign(rotation.z.rad, 0f, 0f, 1f) -// -// uboVS.pack() -// memCopy(uboVS.address, uniformBufferVS.mapped, uboVS.size.L) -// } -// -// fun draw() { -// -// super.prepareFrame() -// -// submitInfo.commandBuffer = drawCmdBuffers[currentBuffer] -// queue submit submitInfo -// -// super.submitFrame() -// -// // Submit compute commands -// // Use a fence to ensure that compute command buffer has finished executin before using it again -// device.waitForFence(compute.fence, true, UINT64_MAX) -// device.resetFence(compute.fence) -// -// val computeSubmitInfo = vk.SubmitInfo { commandBuffer = compute.commandBuffer } -// compute.queue.submit(computeSubmitInfo, compute.fence) -// } -// -// override fun prepare() { -// super.prepare() -// loadAssets() -// generateQuad() -// setupVertexDescriptions() -// prepareUniformBuffers() -// prepareTextureTarget(textureComputeTarget, textureColorMap.size, VkFormat.R8G8B8A8_UNORM) -// setupDescriptorSetLayout() -// preparePipelines() -// setupDescriptorPool() -// setupDescriptorSet() -// prepareCompute() -// buildCommandBuffers() -// prepared = true -// window.show() // } -// -// override fun render() { -// if (!prepared) -// return -// draw() // } -// -// override fun viewChanged() = updateUniformBuffers() -// -//// virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) -//// { -//// if (overlay->header("Settings")) { -//// if (overlay->comboBox("Shader", &compute.pipelineIndex, shaderNames)) { -//// buildComputeCommandBuffer() -//// } -//// } -//// } -//} \ No newline at end of file +} \ No newline at end of file