{.experimental: "codeReordering".}
{.deadCodeElim: on.}
#[
THINGS TO FIX
: why uboVS as a ref object fucks up vertice uploading
: #TODO: decouple rec.vk_device from rec and "properly" implement it
]#
import vulkan as nvk
, ../utils/lets
, bitops
, vkTypes
, ../memory/[utils]
#from depth_stencil import Depth_Stencil
proc `<`*(x,y: VkDeviceSize): bool = return x.int < y.int
proc `>`*(x,y: VkDeviceSize): bool = return x.int > y.int
proc `==`*(x,y: VkDeviceSize): bool = return x.int == y.int
proc getQueueFamilyIndex*( qfp: seq[VkQueueFamilyProperties], qFlags: VkQueueFlagBits): uint32 =
if bitand(qFlags.int, VK_QUEUE_GRAPHICS_BIT.int) == 1:
for i, x in qfp:
if bitand(qfp[i].queueFlags.int, qFlags.int) == 1: return i.uint32
elif bitand(qFlags.int, VK_QUEUE_TRANSFER_BIT.int) == 1:
for i, x in qfp:
if bitand(qfp[i].queueFlags.int, qFlags.int) == 1: return i.uint32
elif bitand(qFlags.int, VK_QUEUE_COMPUTE_BIT.int) == 1:
for i, x in qfp:
if bitand(qfp[i].queueFlags.int, qFlags.int) == 1: return i.uint32
else:
for i, x in qfp:
if bitand(qfp[i].queueFlags.int, qFlags.int) == 1: return i.uint32
proc chooseSwapSurfaceFormat*(availableFormats: seq[VkSurfaceFormatKHR]): VkSurfaceFormatKHR =
for availableFormat in availableFormats:
if availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM and
availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR:
return availableFormat
availableFormats[0]
proc chooseSwapPresentMode*(availablePresentModes: seq[VkPresentModeKHR]): VkPresentModeKHR =
for availablePresentMode in availablePresentModes:
if availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR:
return availablePresentMode
VK_PRESENT_MODE_FIFO_KHR
proc chooseSwapExtent*(capabilities: VkSurfaceCapabilitiesKHR): VkExtent2D =
if capabilities.currentExtent.width != uint32.high:
return capabilities.currentExtent
else:
# result = VkExtent2D(width: WIDTH, height: HEIGHT)
result.width = max( capabilities.minImageExtent.width, min(capabilities.maxImageExtent.width, result.width))
result.height = max(capabilities.minImageExtent.height, min(capabilities.maxImageExtent.height, result.height) )
proc createShaderModule*(device: VkDevice, code: string): VkShaderModule =
var createInfo = VkShaderModuleCreateInfo(
sType: VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
codeSize: code.len.uint32,
pCode: cast[ptr uint32](code[0].addr)
)
if vkCreateShaderModule(device, createInfo.addr, nil, result.addr) != VK_SUCCESS:
quit("failed to create shader module")
proc createCommandPool*( vk_device: VkDevice
, graphics_queue_index: uint32
): VkCommandPool =
var
poolInfo = VkCommandPoolCreateInfo( sType: VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO
, queueFamilyIndex: graphics_queue_index # rec.qfi.graphics
, flags: VkCommandPoolCreateFlags VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT# optional
)
if vkCreateCommandPool( vk_device
, poolInfo.addr
, nil
, addr result
) != VK_SUCCESS:
quit("failed to create command pool")
proc getCommandBuffers*( device: VkDevice
, command_pool: VkCommandPool
, start: bool
): VkCommandBuffer =
var
allocInfo = VkCommandBufferAllocateInfo( sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO
, commandPool: command_pool
, level: VK_COMMAND_BUFFER_LEVEL_PRIMARY
, commandBufferCount: 1
)
discard vkAllocateCommandBuffers(device, allocInfo.addr, result.addr)
if start:
var cmdBufInfo: VkCommandBufferBeginInfo
cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO
discard vkBeginCommandBuffer(result, addr cmdBufInfo)
result
proc createPipeLineCache*( vk_device: VKDevice
): VkPipelineCache =
var pipelineCacheCreateInfo: VkPipelineCacheCreateInfo
pipelineCacheCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO
discard vkCreatePipelineCache( vk_device
, addr pipelineCacheCreateInfo
, nil
, addr result
)
proc flushCommandBuffer*( device: VkDevice
, q: VkQueue
, command_pool: VkCommandPool
, commandBuffer: var VkCommandBuffer
) =
discard vkEndCommandBuffer commandBuffer
var
submitInfo: VkSubmitInfo
fenceCreateInfo: VkFenceCreateInfo
fence: VkFence
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO
submitInfo.commandBufferCount = 1
submitInfo.pCommandBuffers = addr commandBuffer #????????
# Create fence to ensure that the command buffer has finished executing
fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO
fenceCreateInfo.flags = VkFenceCreateFlags 0
discard vkCreateFence(device, addr fenceCreateInfo, nil, addr fence)
# Submit to the queue
discard vkQueueSubmit(q, 1, addr submitInfo, fence)
# # Wait for the fence to signal that command buffer has finished executing
discard vkWaitForFences(device, 1.uint32, addr fence, VkBool32 VK_TRUE, high uint64) # in nanoseconds
vkDestroyFence(device, fence, nil)
vkFreeCommandBuffers(device, command_pool, 1, addr commandBuffer)
proc createOneCommandBuffer*( device: VkDevice
, pool: VkCommandPool
, level: VkCommandBufferLevel = VK_COMMAND_BUFFER_LEVEL_PRIMARY
, begin: bool = false
): VkCommandBuffer =
var allocInfo = VkCommandBufferAllocateInfo( sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO
, commandPool: pool
, level: level
, commandBufferCount: 1
)
discard vkAllocateCommandBuffers(device, addr allocInfo, addr result)
if begin:
var cmdBufferBeginInfo: VkCommandBufferBeginInfo
cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO
discard vkBeginCommandBuffer(result, addr cmdBufferBeginInfo)
result
proc setImageLayout*( cmdbuffer: VkCommandBuffer
, image: VkImage
, oldImageLayout: VkImageLayout
, newImageLayout: VkImageLayout
, subresourceRange: VkImageSubresourceRange
, srcStageMask: VkPipelineStageFlags
, dstStageMask: VkPipelineStageFlags
) =
var
# Initialize an image memory barrier with no image transfer ownership
imageMemoryBarrier = VkImageMemoryBarrier( sType: VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER
, srcQueueFamilyIndex: VK_QUEUE_FAMILY_IGNORED
, dstQueueFamilyIndex: VK_QUEUE_FAMILY_IGNORED
, oldLayout: oldImageLayout
, newLayout: newImageLayout
, image: image
, subresourceRange: subresourceRange
)
# Source layouts (old)
# Source access mask controls actions that have to be finished on the old layout
# before it will be transitioned to the new layout
case oldImageLayout:
of VK_IMAGE_LAYOUT_UNDEFINED:
# Image layout is undefined (or does not matter)
# Only valid as initial layout
# No flags required, listed only for completeness
imageMemoryBarrier.srcAccessMask = VkAccessFlags 0
of VK_IMAGE_LAYOUT_PREINITIALIZED:
# Image is preinitialized
# Only valid as initial layout for linear images, preserves memory contents
# Make sure host writes have been finished
imageMemoryBarrier.srcAccessMask = VkAccessFlags VK_ACCESS_HOST_WRITE_BIT
of VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
# Image is a color attachment
# Make sure any writes to the color buffer have been finished
imageMemoryBarrier.srcAccessMask = VkAccessFlags VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
of VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
# Image is a depth/stencil attachment
# Make sure any writes to the depth/stencil buffer have been finished
imageMemoryBarrier.srcAccessMask = VkAccessFlags VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT
of VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
# Image is a transfer source
# Make sure any reads from the image have been finished
imageMemoryBarrier.srcAccessMask = VkAccessFlags VK_ACCESS_TRANSFER_READ_BIT
of VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
# Image is a transfer destination
# Make sure any writes to the image have been finished
imageMemoryBarrier.srcAccessMask = VkAccessFlags VK_ACCESS_TRANSFER_WRITE_BIT
of VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
# Image is read by a shader
# Make sure any shader reads from the image have been finished
imageMemoryBarrier.srcAccessMask = VkAccessFlags VK_ACCESS_SHADER_READ_BIT
else: discard # Other source layouts aren't handled (yet)
# Target layouts (new)
# Destination access mask controls the dependency for the new image layout
case newImageLayout:
of VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
# Image will be used as a transfer destination
# Make sure any writes to the image have been finished
imageMemoryBarrier.dstAccessMask = VkAccessFlags VK_ACCESS_TRANSFER_WRITE_BIT
of VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
# Image will be used as a transfer source
# Make sure any reads from the image have been finished
imageMemoryBarrier.dstAccessMask = VkAccessFlags VK_ACCESS_TRANSFER_READ_BIT
of VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
# Image will be used as a color attachment
# Make sure any writes to the color buffer have been finished
imageMemoryBarrier.dstAccessMask = VkAccessFlags VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
of VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
# Image layout will be used as a depth/stencil attachment
# Make sure any writes to depth/stencil buffer have been finished
imageMemoryBarrier.dstAccessMask = VkAccessFlags bitor(imageMemoryBarrier.dstAccessMask.ord, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT.ord)
of VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
# Image will be read in a shader (sampler, input attachment)
# Make sure any writes to the image have been finished
if (imageMemoryBarrier.srcAccessMask.int == 0).bool:
imageMemoryBarrier.srcAccessMask = VkAccessFlags bitor(VK_ACCESS_HOST_WRITE_BIT.ord, VK_ACCESS_TRANSFER_WRITE_BIT.ord)
imageMemoryBarrier.dstAccessMask = VkAccessFlags VK_ACCESS_SHADER_READ_BIT
else: discard #Other source layouts aren't handled (yet)
# Put barrier inside setup command buffer
vkCmdPipelineBarrier( cmdbuffer
, srcStageMask
, dstStageMask
, VkDependencyFlags 0
, 0
, nil
, 0
, nil
, 1
, addr imageMemoryBarrier)
proc beginSingleTimeCommands*( vk_device: VkDevice
, command_pool: VkCommandPool
): VkCommandBuffer =
var
commandBuffer: VkCommandBuffer
beginInfo = VkCommandBufferBeginInfo( sType:VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO
, flags: VkCommandBufferUsageFlags VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
)
allocInfo = VkCommandBufferAllocateInfo( sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO
, level: VK_COMMAND_BUFFER_LEVEL_PRIMARY
, commandPool: command_pool
, commandBufferCount: 1
)
discard vkAllocateCommandBuffers( vk_device
, addr allocInfo
, addr commandBuffer
)
discard vkBeginCommandBuffer(commandBuffer, addr beginInfo)
proc endSingleTimeCommands*( vk_device: VkDevice
, cmdBuffer: VkCommandBuffer
, graphics_queue: VkQueue
, command_pool: VkCommandPool
, fence: VkFence = VkFence 0
, cmdBufferCount: uint32 = 1
) =
var submitInfo = VkSubmitInfo( sType: VK_STRUCTURE_TYPE_SUBMIT_INFO
, commandBufferCount: cmdBufferCount
, pCommandBuffers: addr cmdBuffer
)
discard vkEndCommandBuffer(cmdBuffer
)
discard vkQueueSubmit( graphics_queue
, uint32 1
, addr submitInfo
, fence
)
discard vkQueueWaitIdle graphics_queue
vkFreeCommandBuffers( vk_device
, command_pool
, 1
, addr cmdBuffer
)
proc createImageView*( device: VkDevice
, image: VkImage
, format: VkFormat
): VkImageVIew =
var viewInfo = VkImageViewCreateInfo( sType: VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO
, image: image
, viewType: VK_IMAGE_VIEW_TYPE_2D
, format: format
, subresourceRange: VkImageSubresourceRange( aspectMask: VkImageAspectFlags VK_IMAGE_ASPECT_COLOR_BIT
, baseMipLevel: 0
, levelCount: 1
, baseArrayLayer: 0
, layerCount: 1
)
)
discard vkCreateImageView(device, addr viewInfo, nil, addr result)
result
# TODO: Samplers can be shared. Decide on parameters and reuse samplers
# when the same parameters are passed.
proc createSampler*(device: VkDevice): VkSampler =
var samplerInfo = VkSamplerCreateInfo( sType: VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO
, magFilter: VK_FILTER_LINEAR
, minFilter: VK_FILTER_LINEAR
, addressModeU: VK_SAMPLER_ADDRESS_MODE_REPEAT
, addressModeV: VK_SAMPLER_ADDRESS_MODE_REPEAT
, addressModeW: VK_SAMPLER_ADDRESS_MODE_REPEAT
, anisotropyEnable: vktrue
, maxAnisotropy: 16
, borderColor: VK_BORDER_COLOR_INT_OPAQUE_BLACK
, unnormalizedCoordinates: vkfalse
, compareEnable: vkfalse
, compareOp: VK_COMPARE_OP_ALWAYS
, mipmapMode: VK_SAMPLER_MIPMAP_MODE_LINEAR
)
discard vkCreateSampler(device, addr samplerInfo, nil, addr result)
result
proc newVkFenceCreateInfo*(sType: VkStructureType = VkStructureTypeFenceCreateInfo, pNext: pointer = nil, flags: VkFenceCreateFlags = 0.VkFenceCreateFlags): VkFenceCreateInfo =
result.sType = sType
result.pNext = pNext
result.flags = flags
proc createSyncPrims*( vk_device: VkDevice
, fences: var seq[VkFence]
, draw_command_buffers: var seq[VkCommandBuffer]
) =
var createInfo = newVkFenceCreateInfo()
#echo rec.draw_command_buffers.len
fences.setLen draw_command_buffers.len
for i,fence in fences:
discard vkCreateFence( vk_device
, addr createInfo
, nil
, addr fences[i]
)
proc aVkDescriptorImageInfo*( sampler: VkSampler
, imageView: VkImageView
, imageLayout: VkImageLayout
): VkDescriptorImageInfo =
result = VkDescriptorImageInfo()
result.sampler = sampler
result.imageView = imageView
result.imageLayout = imageLayout
proc aVkWriteDescriptorSet*( descSet: VkDescriptorSet
, descType: VkDescriptorType
, binding: uint32
, bufferInfo: VkDescriptorBufferInfo
, descriptorCount: uint32 = 1
): VkWriteDescriptorSet =
result = VkWriteDescriptorSet()
result.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET
result.dstSet = descSet
result.descriptorType = descType
result.dstBinding = binding
result.pBufferInfo = cast[ptr ptr VkDescriptorBufferInfo] (addr bufferInfo) # LMAO?????
result.descriptorCount = descriptorCount
proc aVkDescriptorSetLayoutBinding*( descType: VkDescriptorType
, stageFlags: VkShaderStageFlags
, binding: uint32
, descCount: uint32 = 1): VkDescriptorSetLayoutBinding =
result = VkDescriptorSetLayoutBinding()
result.descriptorType = descType
result.stageFlags = stageFlags
result.binding = binding
result.descriptorCount = descCount
proc aVkBufferCreateInfo*( usage: VkBufferUsageFlags
, size: VkDeviceSize = vk0
): VkBufferCreateInfo =
result = VkBufferCreateInfo(sType: VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO
, usage: usage
, size: size
)
proc Vkds*[T](t: T): VKDeviceSize {.inline.}= VKDeviceSize t
#[
static i32 find_memory_index(vulkan_context *context
, u32 type_filter
, u32 property_flags)
{
for (u32 i = 0; i < memory_properties.memoryTypeCount; ++i) {
// Check each memory type to see if its bit is set to 1.
if ( type_filter & (1 << i) && (memory_properties.memoryTypes[i].propertyFlags & property_flags) ==
property_flags
) {
return i;
}
}
KWARN("Unable to find suitable memory type!");
return -1;
}
]#
proc `==`*(x, y: VkMemoryPropertyFlags): bool = x.int == y.int
proc `and`*(x, y: VkMemoryPropertyFlags): int = x.int and y.int
proc find_memory_with_property*( memory_properties: VkPhysicalDeviceMemoryProperties
, the_type_bits_filter: var uint32
, required_memory_properties: VkMemoryPropertyFlags
): uint32 =
for memory_index in 0 ..< memoryProperties.memoryTypeCount:
var
is_required_memory_property = bitand( the_type_bits_filter.int
, (1 shl memory_index)
) >= 1
the_memory_indexs_property_flag = memory_properties.memoryTypes[memory_index].propertyFlags.int
#echo "RMP: ", is_required_memory_property, " <> (", bitand( the_type_bits_filter.int, (1 shl memory_index)), ") ", the_memory_indexs_property_flag
if is_required_memory_property and
bitand( the_memory_indexs_property_flag
, required_memory_properties.int
) == required_memory_properties.int:
return memory_index
quit("find_memory_with_property: no matching memory type could be found:")
proc create_image*( vk_device: VkDevice
, gpu_memory_properties: VkPhysicalDeviceMemoryProperties
, width: uint32
, height: uint32
, depth: uint32
, format: VkFormat
, tiling: VkImageTiling
, usage: VkImageUsageFlags
, properties: VkMemoryPropertyFlags
): BoundImage =
var
allocInfo = VkMemoryAllocateInfo(sType: VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO)
image: VKImage
memReqs: VkMemoryRequirements
imageMem: VkDeviceMemory
imageci = VkimageCreateInfo( sType: VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO
, imageType: VK_IMAGE_TYPE_2D
, extent: VkExtent3D( width: width
, height: height
, depth: 1
)
, mipLevels: 1
, arrayLayers: 1
, format: format
, tiling: tiling
, initialLayout: VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
, usage: usage
, samples: VK_SAMPLE_COUNT_1_BIT
, sharingMode: VK_SHARING_MODE_EXCLUSIVE
#[ , imageCreateMaxMipLevels: 1
, imageCreateMaxArrayLayers: 1
, imageCreateMaxExtent: VkExtent3D( width: width
, height: height
, depth: depth
)
, imageCreateSampleCounts: VK_SAMPLE_COUNT_1_BIT ]#
)
discard vkCreateImage( vk_device
, addr imageci
, nil
, addr image
)
vkGetImageMemoryRequirements( vk_device
, image
, addr memReqs
)
allocInfo.allocationSize = memReqs.size
allocInfo.memoryTypeIndex = find_memory_with_property( gpu_memory_properties
, memReqs.memoryTypeBits
, properties
)
discard vkBindImageMemory( vk_device
, image
, imageMem
, vk0
)
#TODO: This should be per pipeline correct?
proc create_descriptor_pool*( vk_device: VkDevice
): VkDescriptorPool =
var
sizes = [ VkDescriptorPoolSize( typee: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER
, descriptorCount: 1
)
, VkDescriptorPoolSize( typee: VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
, descriptorCount: 1
)
]
# Create the global descriptor pool
# All descriptors used in this example are allocated from this pool
descriptorPoolInfo = VkDescriptorPoolCreateInfo( sType : VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO
, pNext : nil
, poolSizeCount : 2
, pPoolSizes : addr sizes[0]
, maxSets: 4 # Set the max. number of descriptor sets that can be requested from this pool
)
discard vkCreateDescriptorPool( vk_device
, addr descriptorPoolInfo
, nil
, addr result
)
result