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