Mercurial > games > semicongine
comparison semiconginev2/rendering/vulkan_wrappers.nim @ 1227:4d97cfc4888b
merge
| author | sam <sam@basx.dev> |
|---|---|
| date | Wed, 17 Jul 2024 23:45:43 +0700 |
| parents | 56781cc0fc7c |
| children | 5dcb503ef0c0 |
comparison
equal
deleted
inserted
replaced
| 1170:2addc5f6804f | 1227:4d97cfc4888b |
|---|---|
| 1 proc GetBestPhysicalDevice(instance: VkInstance): VkPhysicalDevice = | |
| 2 var nDevices: uint32 | |
| 3 checkVkResult vkEnumeratePhysicalDevices(instance, addr(nDevices), nil) | |
| 4 var devices = newSeq[VkPhysicalDevice](nDevices) | |
| 5 checkVkResult vkEnumeratePhysicalDevices(instance, addr(nDevices), devices.ToCPointer) | |
| 6 | |
| 7 var score = 0'u32 | |
| 8 for pDevice in devices: | |
| 9 var props: VkPhysicalDeviceProperties | |
| 10 # CANNOT use svkGetPhysicalDeviceProperties (not initialized yet) | |
| 11 vkGetPhysicalDeviceProperties(pDevice, addr(props)) | |
| 12 if props.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU and props.limits.maxImageDimension2D > score: | |
| 13 score = props.limits.maxImageDimension2D | |
| 14 result = pDevice | |
| 15 | |
| 16 if score == 0: | |
| 17 for pDevice in devices: | |
| 18 var props: VkPhysicalDeviceProperties | |
| 19 # CANNOT use svkGetPhysicalDeviceProperties (not initialized yet) | |
| 20 vkGetPhysicalDeviceProperties(pDevice, addr(props)) | |
| 21 if props.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU and props.limits.maxImageDimension2D > score: | |
| 22 score = props.limits.maxImageDimension2D | |
| 23 result = pDevice | |
| 24 | |
| 25 assert score > 0, "Unable to find integrated or discrete GPU" | |
| 26 | |
| 27 proc svkGetPhysicalDeviceSurfaceSupportKHR*(queueFamily: uint32): bool = | |
| 28 var presentation = VkBool32(false) | |
| 29 checkVkResult vkGetPhysicalDeviceSurfaceSupportKHR(vulkan.physicalDevice, queueFamily, vulkan.surface, addr(presentation)) | |
| 30 return bool(presentation) | |
| 31 | |
| 32 proc GetQueueFamily(pDevice: VkPhysicalDevice, qType: VkQueueFlagBits): uint32 = | |
| 33 var nQueuefamilies: uint32 | |
| 34 vkGetPhysicalDeviceQueueFamilyProperties(pDevice, addr nQueuefamilies, nil) | |
| 35 var queuFamilies = newSeq[VkQueueFamilyProperties](nQueuefamilies) | |
| 36 vkGetPhysicalDeviceQueueFamilyProperties(pDevice, addr nQueuefamilies, queuFamilies.ToCPointer) | |
| 37 for i in 0'u32 ..< nQueuefamilies: | |
| 38 if qType in toEnums(queuFamilies[i].queueFlags): | |
| 39 # for graphics queues we always also want prsentation, they seem never to be separated in practice | |
| 40 if svkGetPhysicalDeviceSurfaceSupportKHR(i) or qType != VK_QUEUE_GRAPHICS_BIT: | |
| 41 return i | |
| 42 assert false, &"Queue of type {qType} not found" | |
| 43 | |
| 44 proc svkGetDeviceQueue*(device: VkDevice, queueFamilyIndex: uint32, qType: VkQueueFlagBits): VkQueue = | |
| 45 vkGetDeviceQueue( | |
| 46 device, | |
| 47 queueFamilyIndex, | |
| 48 0, | |
| 49 addr(result), | |
| 50 ) | |
| 51 | |
| 52 proc DefaultSurfaceFormat(): VkFormat = | |
| 53 # EVERY windows driver and almost every linux driver should support this | |
| 54 VK_FORMAT_B8G8R8A8_SRGB | |
| 55 | |
| 56 func size(format: VkFormat): uint64 = | |
| 57 const formatSize = [ | |
| 58 VK_FORMAT_B8G8R8A8_SRGB.int: 4'u64, | |
| 59 ] | |
| 60 return formatSize[format.int] | |
| 61 | |
| 62 proc svkGetPhysicalDeviceSurfacePresentModesKHR*(): seq[VkPresentModeKHR] = | |
| 63 var n_modes: uint32 | |
| 64 checkVkResult vkGetPhysicalDeviceSurfacePresentModesKHR(vulkan.physicalDevice, vulkan.surface, addr(n_modes), nil) | |
| 65 result = newSeq[VkPresentModeKHR](n_modes) | |
| 66 checkVkResult vkGetPhysicalDeviceSurfacePresentModesKHR(vulkan.physicalDevice, vulkan.surface, addr(n_modes), result.ToCPointer) | |
| 67 | |
| 68 proc svkGetPhysicalDeviceSurfaceFormatsKHR(): seq[VkSurfaceFormatKHR] = | |
| 69 var n_formats: uint32 | |
| 70 checkVkResult vkGetPhysicalDeviceSurfaceFormatsKHR(vulkan.physicalDevice, vulkan.surface, addr(n_formats), nil) | |
| 71 result = newSeq[VkSurfaceFormatKHR](n_formats) | |
| 72 checkVkResult vkGetPhysicalDeviceSurfaceFormatsKHR(vulkan.physicalDevice, vulkan.surface, addr(n_formats), result.ToCPointer) | |
| 73 | |
| 74 proc hasValidationLayer*(): bool = | |
| 75 var n_layers: uint32 | |
| 76 checkVkResult vkEnumerateInstanceLayerProperties(addr(n_layers), nil) | |
| 77 if n_layers > 0: | |
| 78 var layers = newSeq[VkLayerProperties](n_layers) | |
| 79 checkVkResult vkEnumerateInstanceLayerProperties(addr(n_layers), layers.ToCPointer) | |
| 80 for layer in layers: | |
| 81 if layer.layerName.CleanString == "VK_LAYER_KHRONOS_validation": | |
| 82 return true | |
| 83 return false | |
| 84 | |
| 85 proc svkGetPhysicalDeviceProperties*(): VkPhysicalDeviceProperties = | |
| 86 vkGetPhysicalDeviceProperties(vulkan.physicalDevice, addr(result)) | |
| 87 | |
| 88 proc svkCreateBuffer*(size: uint64, usage: openArray[VkBufferUsageFlagBits]): VkBuffer = | |
| 89 var createInfo = VkBufferCreateInfo( | |
| 90 sType: VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, | |
| 91 flags: VkBufferCreateFlags(0), | |
| 92 size: size, | |
| 93 usage: usage.toBits, | |
| 94 sharingMode: VK_SHARING_MODE_EXCLUSIVE, | |
| 95 ) | |
| 96 checkVkResult vkCreateBuffer( | |
| 97 device = vulkan.device, | |
| 98 pCreateInfo = addr(createInfo), | |
| 99 pAllocator = nil, | |
| 100 pBuffer = addr(result), | |
| 101 ) | |
| 102 | |
| 103 proc svkAllocateMemory*(size: uint64, typeIndex: uint32): VkDeviceMemory = | |
| 104 var memoryAllocationInfo = VkMemoryAllocateInfo( | |
| 105 sType: VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, | |
| 106 allocationSize: size, | |
| 107 memoryTypeIndex: typeIndex, | |
| 108 ) | |
| 109 checkVkResult vkAllocateMemory( | |
| 110 vulkan.device, | |
| 111 addr(memoryAllocationInfo), | |
| 112 nil, | |
| 113 addr(result), | |
| 114 ) | |
| 115 | |
| 116 proc svkCreate2DImage*(width, height: uint32, format: VkFormat, usage: openArray[VkImageUsageFlagBits], samples = VK_SAMPLE_COUNT_1_BIT): VkImage = | |
| 117 var imageProps: VkImageFormatProperties | |
| 118 checkVkResult vkGetPhysicalDeviceImageFormatProperties( | |
| 119 vulkan.physicalDevice, | |
| 120 format, | |
| 121 VK_IMAGE_TYPE_2D, | |
| 122 VK_IMAGE_TILING_OPTIMAL, | |
| 123 usage.toBits, | |
| 124 VkImageCreateFlags(0), | |
| 125 addr(imageProps) | |
| 126 ) | |
| 127 | |
| 128 var imageInfo = VkImageCreateInfo( | |
| 129 sType: VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, | |
| 130 imageType: VK_IMAGE_TYPE_2D, | |
| 131 extent: VkExtent3D(width: width, height: height, depth: 1), | |
| 132 mipLevels: min(1'u32, imageProps.maxMipLevels), | |
| 133 arrayLayers: min(1'u32, imageProps.maxArrayLayers), | |
| 134 format: format, | |
| 135 tiling: VK_IMAGE_TILING_OPTIMAL, | |
| 136 initialLayout: VK_IMAGE_LAYOUT_UNDEFINED, | |
| 137 usage: usage.toBits, | |
| 138 sharingMode: VK_SHARING_MODE_EXCLUSIVE, | |
| 139 samples: samples, | |
| 140 ) | |
| 141 checkVkResult vkCreateImage(vulkan.device, addr imageInfo, nil, addr(result)) | |
| 142 | |
| 143 proc svkCreate2DImageView(image: VkImage, format: VkFormat): VkImageView = | |
| 144 var createInfo = VkImageViewCreateInfo( | |
| 145 sType: VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, | |
| 146 image: image, | |
| 147 viewType: VK_IMAGE_VIEW_TYPE_2D, | |
| 148 format: format, | |
| 149 components: VkComponentMapping( | |
| 150 r: VK_COMPONENT_SWIZZLE_IDENTITY, | |
| 151 g: VK_COMPONENT_SWIZZLE_IDENTITY, | |
| 152 b: VK_COMPONENT_SWIZZLE_IDENTITY, | |
| 153 a: VK_COMPONENT_SWIZZLE_IDENTITY, | |
| 154 ), | |
| 155 subresourceRange: VkImageSubresourceRange( | |
| 156 aspectMask: VkImageAspectFlags(VK_IMAGE_ASPECT_COLOR_BIT), | |
| 157 baseMipLevel: 0, | |
| 158 levelCount: 1, | |
| 159 baseArrayLayer: 0, | |
| 160 layerCount: 1, | |
| 161 ), | |
| 162 ) | |
| 163 checkVkResult vkCreateImageView(vulkan.device, addr(createInfo), nil, addr(result)) | |
| 164 | |
| 165 proc svkCreateFramebuffer*(renderpass: VkRenderPass, width, height: uint32, attachments: openArray[VkImageView]): VkFramebuffer = | |
| 166 var framebufferInfo = VkFramebufferCreateInfo( | |
| 167 sType: VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, | |
| 168 renderPass: renderpass, | |
| 169 attachmentCount: attachments.len.uint32, | |
| 170 pAttachments: attachments.ToCPointer, | |
| 171 width: width, | |
| 172 height: height, | |
| 173 layers: 1, | |
| 174 ) | |
| 175 checkVkResult vkCreateFramebuffer(vulkan.device, addr(framebufferInfo), nil, addr(result)) | |
| 176 | |
| 177 proc svkGetBufferMemoryRequirements*(buffer: VkBuffer): tuple[size: uint64, alignment: uint64, memoryTypes: seq[uint32]] = | |
| 178 var reqs: VkMemoryRequirements | |
| 179 vkGetBufferMemoryRequirements(vulkan.device, buffer, addr(reqs)) | |
| 180 result.size = reqs.size | |
| 181 result.alignment = reqs.alignment | |
| 182 for i in 0'u32 ..< VK_MAX_MEMORY_TYPES: | |
| 183 if ((1'u32 shl i) and reqs.memoryTypeBits) > 0: | |
| 184 result.memoryTypes.add i | |
| 185 | |
| 186 proc svkGetImageMemoryRequirements*(image: VkImage): tuple[size: uint64, alignment: uint64, memoryTypes: seq[uint32]] = | |
| 187 var reqs: VkMemoryRequirements | |
| 188 vkGetImageMemoryRequirements(vulkan.device, image, addr(reqs)) | |
| 189 result.size = reqs.size | |
| 190 result.alignment = reqs.alignment | |
| 191 for i in 0'u32 ..< VK_MAX_MEMORY_TYPES: | |
| 192 if ((1'u32 shl i) and reqs.memoryTypeBits) > 0: | |
| 193 result.memoryTypes.add i | |
| 194 | |
| 195 proc svkCreateFence*(signaled = false): VkFence = | |
| 196 var fenceInfo = VkFenceCreateInfo( | |
| 197 sType: VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, | |
| 198 flags: if signaled: toBits [VK_FENCE_CREATE_SIGNALED_BIT] else: VkFenceCreateFlags(0) | |
| 199 ) | |
| 200 checkVkResult vkCreateFence(vulkan.device, addr(fenceInfo), nil, addr(result)) | |
| 201 | |
| 202 proc svkCreateSemaphore*(): VkSemaphore = | |
| 203 var semaphoreInfo = VkSemaphoreCreateInfo(sType: VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO) | |
| 204 checkVkResult vkCreateSemaphore(vulkan.device, addr(semaphoreInfo), nil, addr(result)) | |
| 205 | |
| 206 proc Await*(fence: VkFence, timeout = high(uint64)): bool = | |
| 207 let waitResult = vkWaitForFences(vulkan.device, 1, addr(fence), false, timeout) | |
| 208 if waitResult == VK_TIMEOUT: | |
| 209 return false | |
| 210 checkVkResult waitResult | |
| 211 return true | |
| 212 | |
| 213 proc svkResetFences*(fence: VkFence) = | |
| 214 checkVkResult vkResetFences(vulkan.device, 1, addr(fence)) | |
| 215 | |
| 216 proc svkCmdBindDescriptorSets(commandBuffer: VkCommandBuffer, descriptorSets: openArray[VkDescriptorSet], layout: VkPipelineLayout) = | |
| 217 vkCmdBindDescriptorSets( | |
| 218 commandBuffer = commandBuffer, | |
| 219 pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, | |
| 220 layout = layout, | |
| 221 firstSet = 0, | |
| 222 descriptorSetCount = descriptorSets.len.uint32, | |
| 223 pDescriptorSets = descriptorSets.ToCPointer, | |
| 224 dynamicOffsetCount = 0, | |
| 225 pDynamicOffsets = nil | |
| 226 ) | |
| 227 | |
| 228 | |
| 229 proc svkCreateRenderPass( | |
| 230 attachments: openArray[VkAttachmentDescription], | |
| 231 colorAttachments: openArray[VkAttachmentReference], | |
| 232 resolveAttachments: openArray[VkAttachmentReference], | |
| 233 dependencies: openArray[VkSubpassDependency], | |
| 234 ): VkRenderPass = | |
| 235 assert colorAttachments.len == resolveAttachments.len or resolveAttachments.len == 0 | |
| 236 var subpass = VkSubpassDescription( | |
| 237 flags: VkSubpassDescriptionFlags(0), | |
| 238 pipelineBindPoint: VK_PIPELINE_BIND_POINT_GRAPHICS, | |
| 239 inputAttachmentCount: 0, | |
| 240 pInputAttachments: nil, | |
| 241 colorAttachmentCount: colorAttachments.len.uint32, | |
| 242 pColorAttachments: colorAttachments.ToCPointer, | |
| 243 pResolveAttachments: resolveAttachments.ToCPointer, | |
| 244 pDepthStencilAttachment: nil, | |
| 245 preserveAttachmentCount: 0, | |
| 246 pPreserveAttachments: nil, | |
| 247 ) | |
| 248 var createInfo = VkRenderPassCreateInfo( | |
| 249 sType: VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, | |
| 250 attachmentCount: uint32(attachments.len), | |
| 251 pAttachments: attachments.ToCPointer, | |
| 252 subpassCount: 1, | |
| 253 pSubpasses: addr(subpass), | |
| 254 dependencyCount: uint32(dependencies.len), | |
| 255 pDependencies: dependencies.ToCPointer, | |
| 256 ) | |
| 257 checkVkResult vkCreateRenderPass(vulkan.device, addr(createInfo), nil, addr(result)) | |
| 258 | |
| 259 proc BestMemory*(mappable: bool, filter: seq[uint32] = @[]): uint32 = | |
| 260 var physicalProperties: VkPhysicalDeviceMemoryProperties | |
| 261 vkGetPhysicalDeviceMemoryProperties(vulkan.physicalDevice, addr(physicalProperties)) | |
| 262 | |
| 263 var maxScore: float = -1 | |
| 264 var maxIndex: uint32 = 0 | |
| 265 for index in 0'u32 ..< physicalProperties.memoryTypeCount: | |
| 266 if filter.len == 0 or index in filter: | |
| 267 let flags = toEnums(physicalProperties.memoryTypes[index].propertyFlags) | |
| 268 if not mappable or VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT in flags: | |
| 269 var score: float = 0 | |
| 270 if VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT in flags: score += 1_000_000 | |
| 271 if VK_MEMORY_PROPERTY_HOST_CACHED_BIT in flags: score += 1_000 | |
| 272 score += float(physicalProperties.memoryHeaps[physicalProperties.memoryTypes[index].heapIndex].size) / 1_000_000_000 | |
| 273 if score > maxScore: | |
| 274 maxScore = score | |
| 275 maxIndex = index | |
| 276 assert maxScore > 0, &"Unable to find memory type (mappable: {mappable}, filter: {filter})" | |
| 277 return maxIndex | |
| 278 | |
| 279 template WithSingleUseCommandBuffer*(cmd, body: untyped): untyped = | |
| 280 block: | |
| 281 var | |
| 282 commandBufferPool: VkCommandPool | |
| 283 createInfo = VkCommandPoolCreateInfo( | |
| 284 sType: VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, | |
| 285 flags: VkCommandPoolCreateFlags(0), | |
| 286 queueFamilyIndex: vulkan.graphicsQueueFamily, | |
| 287 ) | |
| 288 checkVkResult vkCreateCommandPool(vulkan.device, addr createInfo, nil, addr(commandBufferPool)) | |
| 289 var | |
| 290 `cmd` {.inject.}: VkCommandBuffer | |
| 291 allocInfo = VkCommandBufferAllocateInfo( | |
| 292 sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, | |
| 293 commandPool: commandBufferPool, | |
| 294 level: VK_COMMAND_BUFFER_LEVEL_PRIMARY, | |
| 295 commandBufferCount: 1, | |
| 296 ) | |
| 297 checkVkResult vulkan.device.vkAllocateCommandBuffers(addr allocInfo, addr(`cmd`)) | |
| 298 var beginInfo = VkCommandBufferBeginInfo( | |
| 299 sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, | |
| 300 flags: VkCommandBufferUsageFlags(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT), | |
| 301 ) | |
| 302 checkVkResult `cmd`.vkBeginCommandBuffer(addr beginInfo) | |
| 303 | |
| 304 body | |
| 305 | |
| 306 checkVkResult `cmd`.vkEndCommandBuffer() | |
| 307 var submitInfo = VkSubmitInfo( | |
| 308 sType: VK_STRUCTURE_TYPE_SUBMIT_INFO, | |
| 309 commandBufferCount: 1, | |
| 310 pCommandBuffers: addr(`cmd`), | |
| 311 ) | |
| 312 | |
| 313 var fence = svkCreateFence() | |
| 314 checkVkResult vkQueueSubmit(vulkan.graphicsQueue, 1, addr(submitInfo), fence) | |
| 315 discard fence.Await() | |
| 316 vkDestroyFence(vulkan.device, fence, nil) | |
| 317 vkDestroyCommandPool(vulkan.device, commandBufferPool, nil) | |
| 318 | |
| 319 template WithStagingBuffer*[T: (VkBuffer, uint64)|(VkImage, uint32, uint32)]( | |
| 320 target: T, | |
| 321 bufferSize: uint64, | |
| 322 dataPointer, | |
| 323 body: untyped | |
| 324 ): untyped = | |
| 325 var `dataPointer` {.inject.}: pointer | |
| 326 let stagingBuffer = svkCreateBuffer(bufferSize, [VK_BUFFER_USAGE_TRANSFER_SRC_BIT]) | |
| 327 let memoryRequirements = svkGetBufferMemoryRequirements(stagingBuffer) | |
| 328 let memoryType = BestMemory(mappable = true, filter = memoryRequirements.memoryTypes) | |
| 329 let stagingMemory = svkAllocateMemory(memoryRequirements.size, memoryType) | |
| 330 checkVkResult vkMapMemory( | |
| 331 device = vulkan.device, | |
| 332 memory = stagingMemory, | |
| 333 offset = 0'u64, | |
| 334 size = VK_WHOLE_SIZE, | |
| 335 flags = VkMemoryMapFlags(0), | |
| 336 ppData = addr(`dataPointer`) | |
| 337 ) | |
| 338 checkVkResult vkBindBufferMemory(vulkan.device, stagingBuffer, stagingMemory, 0) | |
| 339 | |
| 340 block: | |
| 341 # usually: write data to dataPointer in body | |
| 342 body | |
| 343 | |
| 344 var stagingRange = VkMappedMemoryRange( | |
| 345 sType: VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, | |
| 346 memory: stagingMemory, | |
| 347 size: VK_WHOLE_SIZE, | |
| 348 ) | |
| 349 checkVkResult vkFlushMappedMemoryRanges(vulkan.device, 1, addr(stagingRange)) | |
| 350 | |
| 351 WithSingleUseCommandBuffer(commandBuffer): | |
| 352 when T is (VkBuffer, uint64): | |
| 353 let copyRegion = VkBufferCopy( | |
| 354 size: bufferSize, | |
| 355 dstOffset: target[1], | |
| 356 srcOffset: 0 | |
| 357 ) | |
| 358 vkCmdCopyBuffer( | |
| 359 commandBuffer = commandBuffer, | |
| 360 srcBuffer = stagingBuffer, | |
| 361 dstBuffer = target[0], | |
| 362 regionCount = 1, | |
| 363 pRegions = addr(copyRegion) | |
| 364 ) | |
| 365 elif T is (VkImage, uint32, uint32): | |
| 366 let region = VkBufferImageCopy( | |
| 367 bufferOffset: 0, | |
| 368 bufferRowLength: 0, | |
| 369 bufferImageHeight: 0, | |
| 370 imageSubresource: VkImageSubresourceLayers( | |
| 371 aspectMask: toBits [VK_IMAGE_ASPECT_COLOR_BIT], | |
| 372 mipLevel: 0, | |
| 373 baseArrayLayer: 0, | |
| 374 layerCount: 1, | |
| 375 ), | |
| 376 imageOffset: VkOffset3D(x: 0, y: 0, z: 0), | |
| 377 imageExtent: VkExtent3D(width: target[1], height: target[2], depth: 1) | |
| 378 ) | |
| 379 vkCmdCopyBufferToImage( | |
| 380 commandBuffer = commandBuffer, | |
| 381 srcBuffer = stagingBuffer, | |
| 382 dstImage = target[0], | |
| 383 dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, | |
| 384 regionCount = 1, | |
| 385 pRegions = addr(region) | |
| 386 ) | |
| 387 | |
| 388 vkDestroyBuffer(vulkan.device, stagingBuffer, nil) | |
| 389 vkFreeMemory(vulkan.device, stagingMemory, nil) | |
| 390 |
