Mercurial > games > semicongine
diff semiconginev2/rendering/vulkan_wrappers.nim @ 1218:56781cc0fc7c compiletime-tests
did: renamge main package
author | sam <sam@basx.dev> |
---|---|
date | Wed, 17 Jul 2024 21:01:37 +0700 |
parents | semicongine/rendering/vulkan_wrappers.nim@04e446a7eb2b |
children | 5dcb503ef0c0 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/semiconginev2/rendering/vulkan_wrappers.nim Wed Jul 17 21:01:37 2024 +0700 @@ -0,0 +1,390 @@ +proc GetBestPhysicalDevice(instance: VkInstance): VkPhysicalDevice = + var nDevices: uint32 + checkVkResult vkEnumeratePhysicalDevices(instance, addr(nDevices), nil) + var devices = newSeq[VkPhysicalDevice](nDevices) + checkVkResult vkEnumeratePhysicalDevices(instance, addr(nDevices), devices.ToCPointer) + + var score = 0'u32 + for pDevice in devices: + var props: VkPhysicalDeviceProperties + # CANNOT use svkGetPhysicalDeviceProperties (not initialized yet) + vkGetPhysicalDeviceProperties(pDevice, addr(props)) + if props.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU and props.limits.maxImageDimension2D > score: + score = props.limits.maxImageDimension2D + result = pDevice + + if score == 0: + for pDevice in devices: + var props: VkPhysicalDeviceProperties + # CANNOT use svkGetPhysicalDeviceProperties (not initialized yet) + vkGetPhysicalDeviceProperties(pDevice, addr(props)) + if props.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU and props.limits.maxImageDimension2D > score: + score = props.limits.maxImageDimension2D + result = pDevice + + assert score > 0, "Unable to find integrated or discrete GPU" + +proc svkGetPhysicalDeviceSurfaceSupportKHR*(queueFamily: uint32): bool = + var presentation = VkBool32(false) + checkVkResult vkGetPhysicalDeviceSurfaceSupportKHR(vulkan.physicalDevice, queueFamily, vulkan.surface, addr(presentation)) + return bool(presentation) + +proc GetQueueFamily(pDevice: VkPhysicalDevice, qType: VkQueueFlagBits): uint32 = + var nQueuefamilies: uint32 + vkGetPhysicalDeviceQueueFamilyProperties(pDevice, addr nQueuefamilies, nil) + var queuFamilies = newSeq[VkQueueFamilyProperties](nQueuefamilies) + vkGetPhysicalDeviceQueueFamilyProperties(pDevice, addr nQueuefamilies, queuFamilies.ToCPointer) + for i in 0'u32 ..< nQueuefamilies: + if qType in toEnums(queuFamilies[i].queueFlags): + # for graphics queues we always also want prsentation, they seem never to be separated in practice + if svkGetPhysicalDeviceSurfaceSupportKHR(i) or qType != VK_QUEUE_GRAPHICS_BIT: + return i + assert false, &"Queue of type {qType} not found" + +proc svkGetDeviceQueue*(device: VkDevice, queueFamilyIndex: uint32, qType: VkQueueFlagBits): VkQueue = + vkGetDeviceQueue( + device, + queueFamilyIndex, + 0, + addr(result), + ) + +proc DefaultSurfaceFormat(): VkFormat = + # EVERY windows driver and almost every linux driver should support this + VK_FORMAT_B8G8R8A8_SRGB + +func size(format: VkFormat): uint64 = + const formatSize = [ + VK_FORMAT_B8G8R8A8_SRGB.int: 4'u64, + ] + return formatSize[format.int] + +proc svkGetPhysicalDeviceSurfacePresentModesKHR*(): seq[VkPresentModeKHR] = + var n_modes: uint32 + checkVkResult vkGetPhysicalDeviceSurfacePresentModesKHR(vulkan.physicalDevice, vulkan.surface, addr(n_modes), nil) + result = newSeq[VkPresentModeKHR](n_modes) + checkVkResult vkGetPhysicalDeviceSurfacePresentModesKHR(vulkan.physicalDevice, vulkan.surface, addr(n_modes), result.ToCPointer) + +proc svkGetPhysicalDeviceSurfaceFormatsKHR(): seq[VkSurfaceFormatKHR] = + var n_formats: uint32 + checkVkResult vkGetPhysicalDeviceSurfaceFormatsKHR(vulkan.physicalDevice, vulkan.surface, addr(n_formats), nil) + result = newSeq[VkSurfaceFormatKHR](n_formats) + checkVkResult vkGetPhysicalDeviceSurfaceFormatsKHR(vulkan.physicalDevice, vulkan.surface, addr(n_formats), result.ToCPointer) + +proc hasValidationLayer*(): bool = + var n_layers: uint32 + checkVkResult vkEnumerateInstanceLayerProperties(addr(n_layers), nil) + if n_layers > 0: + var layers = newSeq[VkLayerProperties](n_layers) + checkVkResult vkEnumerateInstanceLayerProperties(addr(n_layers), layers.ToCPointer) + for layer in layers: + if layer.layerName.CleanString == "VK_LAYER_KHRONOS_validation": + return true + return false + +proc svkGetPhysicalDeviceProperties*(): VkPhysicalDeviceProperties = + vkGetPhysicalDeviceProperties(vulkan.physicalDevice, addr(result)) + +proc svkCreateBuffer*(size: uint64, usage: openArray[VkBufferUsageFlagBits]): VkBuffer = + var createInfo = VkBufferCreateInfo( + sType: VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + flags: VkBufferCreateFlags(0), + size: size, + usage: usage.toBits, + sharingMode: VK_SHARING_MODE_EXCLUSIVE, + ) + checkVkResult vkCreateBuffer( + device = vulkan.device, + pCreateInfo = addr(createInfo), + pAllocator = nil, + pBuffer = addr(result), + ) + +proc svkAllocateMemory*(size: uint64, typeIndex: uint32): VkDeviceMemory = + var memoryAllocationInfo = VkMemoryAllocateInfo( + sType: VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + allocationSize: size, + memoryTypeIndex: typeIndex, + ) + checkVkResult vkAllocateMemory( + vulkan.device, + addr(memoryAllocationInfo), + nil, + addr(result), + ) + +proc svkCreate2DImage*(width, height: uint32, format: VkFormat, usage: openArray[VkImageUsageFlagBits], samples = VK_SAMPLE_COUNT_1_BIT): VkImage = + var imageProps: VkImageFormatProperties + checkVkResult vkGetPhysicalDeviceImageFormatProperties( + vulkan.physicalDevice, + format, + VK_IMAGE_TYPE_2D, + VK_IMAGE_TILING_OPTIMAL, + usage.toBits, + VkImageCreateFlags(0), + addr(imageProps) + ) + + var imageInfo = VkImageCreateInfo( + sType: VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + imageType: VK_IMAGE_TYPE_2D, + extent: VkExtent3D(width: width, height: height, depth: 1), + mipLevels: min(1'u32, imageProps.maxMipLevels), + arrayLayers: min(1'u32, imageProps.maxArrayLayers), + format: format, + tiling: VK_IMAGE_TILING_OPTIMAL, + initialLayout: VK_IMAGE_LAYOUT_UNDEFINED, + usage: usage.toBits, + sharingMode: VK_SHARING_MODE_EXCLUSIVE, + samples: samples, + ) + checkVkResult vkCreateImage(vulkan.device, addr imageInfo, nil, addr(result)) + +proc svkCreate2DImageView(image: VkImage, format: VkFormat): VkImageView = + var createInfo = VkImageViewCreateInfo( + sType: VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + image: image, + viewType: VK_IMAGE_VIEW_TYPE_2D, + format: format, + components: VkComponentMapping( + r: VK_COMPONENT_SWIZZLE_IDENTITY, + g: VK_COMPONENT_SWIZZLE_IDENTITY, + b: VK_COMPONENT_SWIZZLE_IDENTITY, + a: VK_COMPONENT_SWIZZLE_IDENTITY, + ), + subresourceRange: VkImageSubresourceRange( + aspectMask: VkImageAspectFlags(VK_IMAGE_ASPECT_COLOR_BIT), + baseMipLevel: 0, + levelCount: 1, + baseArrayLayer: 0, + layerCount: 1, + ), + ) + checkVkResult vkCreateImageView(vulkan.device, addr(createInfo), nil, addr(result)) + +proc svkCreateFramebuffer*(renderpass: VkRenderPass, width, height: uint32, attachments: openArray[VkImageView]): VkFramebuffer = + var framebufferInfo = VkFramebufferCreateInfo( + sType: VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + renderPass: renderpass, + attachmentCount: attachments.len.uint32, + pAttachments: attachments.ToCPointer, + width: width, + height: height, + layers: 1, + ) + checkVkResult vkCreateFramebuffer(vulkan.device, addr(framebufferInfo), nil, addr(result)) + +proc svkGetBufferMemoryRequirements*(buffer: VkBuffer): tuple[size: uint64, alignment: uint64, memoryTypes: seq[uint32]] = + var reqs: VkMemoryRequirements + vkGetBufferMemoryRequirements(vulkan.device, buffer, addr(reqs)) + result.size = reqs.size + result.alignment = reqs.alignment + for i in 0'u32 ..< VK_MAX_MEMORY_TYPES: + if ((1'u32 shl i) and reqs.memoryTypeBits) > 0: + result.memoryTypes.add i + +proc svkGetImageMemoryRequirements*(image: VkImage): tuple[size: uint64, alignment: uint64, memoryTypes: seq[uint32]] = + var reqs: VkMemoryRequirements + vkGetImageMemoryRequirements(vulkan.device, image, addr(reqs)) + result.size = reqs.size + result.alignment = reqs.alignment + for i in 0'u32 ..< VK_MAX_MEMORY_TYPES: + if ((1'u32 shl i) and reqs.memoryTypeBits) > 0: + result.memoryTypes.add i + +proc svkCreateFence*(signaled = false): VkFence = + var fenceInfo = VkFenceCreateInfo( + sType: VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + flags: if signaled: toBits [VK_FENCE_CREATE_SIGNALED_BIT] else: VkFenceCreateFlags(0) + ) + checkVkResult vkCreateFence(vulkan.device, addr(fenceInfo), nil, addr(result)) + +proc svkCreateSemaphore*(): VkSemaphore = + var semaphoreInfo = VkSemaphoreCreateInfo(sType: VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO) + checkVkResult vkCreateSemaphore(vulkan.device, addr(semaphoreInfo), nil, addr(result)) + +proc Await*(fence: VkFence, timeout = high(uint64)): bool = + let waitResult = vkWaitForFences(vulkan.device, 1, addr(fence), false, timeout) + if waitResult == VK_TIMEOUT: + return false + checkVkResult waitResult + return true + +proc svkResetFences*(fence: VkFence) = + checkVkResult vkResetFences(vulkan.device, 1, addr(fence)) + +proc svkCmdBindDescriptorSets(commandBuffer: VkCommandBuffer, descriptorSets: openArray[VkDescriptorSet], layout: VkPipelineLayout) = + vkCmdBindDescriptorSets( + commandBuffer = commandBuffer, + pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, + layout = layout, + firstSet = 0, + descriptorSetCount = descriptorSets.len.uint32, + pDescriptorSets = descriptorSets.ToCPointer, + dynamicOffsetCount = 0, + pDynamicOffsets = nil + ) + + +proc svkCreateRenderPass( + attachments: openArray[VkAttachmentDescription], + colorAttachments: openArray[VkAttachmentReference], + resolveAttachments: openArray[VkAttachmentReference], + dependencies: openArray[VkSubpassDependency], +): VkRenderPass = + assert colorAttachments.len == resolveAttachments.len or resolveAttachments.len == 0 + var subpass = VkSubpassDescription( + flags: VkSubpassDescriptionFlags(0), + pipelineBindPoint: VK_PIPELINE_BIND_POINT_GRAPHICS, + inputAttachmentCount: 0, + pInputAttachments: nil, + colorAttachmentCount: colorAttachments.len.uint32, + pColorAttachments: colorAttachments.ToCPointer, + pResolveAttachments: resolveAttachments.ToCPointer, + pDepthStencilAttachment: nil, + preserveAttachmentCount: 0, + pPreserveAttachments: nil, + ) + var createInfo = VkRenderPassCreateInfo( + sType: VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + attachmentCount: uint32(attachments.len), + pAttachments: attachments.ToCPointer, + subpassCount: 1, + pSubpasses: addr(subpass), + dependencyCount: uint32(dependencies.len), + pDependencies: dependencies.ToCPointer, + ) + checkVkResult vkCreateRenderPass(vulkan.device, addr(createInfo), nil, addr(result)) + +proc BestMemory*(mappable: bool, filter: seq[uint32] = @[]): uint32 = + var physicalProperties: VkPhysicalDeviceMemoryProperties + vkGetPhysicalDeviceMemoryProperties(vulkan.physicalDevice, addr(physicalProperties)) + + var maxScore: float = -1 + var maxIndex: uint32 = 0 + for index in 0'u32 ..< physicalProperties.memoryTypeCount: + if filter.len == 0 or index in filter: + let flags = toEnums(physicalProperties.memoryTypes[index].propertyFlags) + if not mappable or VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT in flags: + var score: float = 0 + if VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT in flags: score += 1_000_000 + if VK_MEMORY_PROPERTY_HOST_CACHED_BIT in flags: score += 1_000 + score += float(physicalProperties.memoryHeaps[physicalProperties.memoryTypes[index].heapIndex].size) / 1_000_000_000 + if score > maxScore: + maxScore = score + maxIndex = index + assert maxScore > 0, &"Unable to find memory type (mappable: {mappable}, filter: {filter})" + return maxIndex + +template WithSingleUseCommandBuffer*(cmd, body: untyped): untyped = + block: + var + commandBufferPool: VkCommandPool + createInfo = VkCommandPoolCreateInfo( + sType: VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + flags: VkCommandPoolCreateFlags(0), + queueFamilyIndex: vulkan.graphicsQueueFamily, + ) + checkVkResult vkCreateCommandPool(vulkan.device, addr createInfo, nil, addr(commandBufferPool)) + var + `cmd` {.inject.}: VkCommandBuffer + allocInfo = VkCommandBufferAllocateInfo( + sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + commandPool: commandBufferPool, + level: VK_COMMAND_BUFFER_LEVEL_PRIMARY, + commandBufferCount: 1, + ) + checkVkResult vulkan.device.vkAllocateCommandBuffers(addr allocInfo, addr(`cmd`)) + var beginInfo = VkCommandBufferBeginInfo( + sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + flags: VkCommandBufferUsageFlags(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT), + ) + checkVkResult `cmd`.vkBeginCommandBuffer(addr beginInfo) + + body + + checkVkResult `cmd`.vkEndCommandBuffer() + var submitInfo = VkSubmitInfo( + sType: VK_STRUCTURE_TYPE_SUBMIT_INFO, + commandBufferCount: 1, + pCommandBuffers: addr(`cmd`), + ) + + var fence = svkCreateFence() + checkVkResult vkQueueSubmit(vulkan.graphicsQueue, 1, addr(submitInfo), fence) + discard fence.Await() + vkDestroyFence(vulkan.device, fence, nil) + vkDestroyCommandPool(vulkan.device, commandBufferPool, nil) + +template WithStagingBuffer*[T: (VkBuffer, uint64)|(VkImage, uint32, uint32)]( + target: T, + bufferSize: uint64, + dataPointer, + body: untyped +): untyped = + var `dataPointer` {.inject.}: pointer + let stagingBuffer = svkCreateBuffer(bufferSize, [VK_BUFFER_USAGE_TRANSFER_SRC_BIT]) + let memoryRequirements = svkGetBufferMemoryRequirements(stagingBuffer) + let memoryType = BestMemory(mappable = true, filter = memoryRequirements.memoryTypes) + let stagingMemory = svkAllocateMemory(memoryRequirements.size, memoryType) + checkVkResult vkMapMemory( + device = vulkan.device, + memory = stagingMemory, + offset = 0'u64, + size = VK_WHOLE_SIZE, + flags = VkMemoryMapFlags(0), + ppData = addr(`dataPointer`) + ) + checkVkResult vkBindBufferMemory(vulkan.device, stagingBuffer, stagingMemory, 0) + + block: + # usually: write data to dataPointer in body + body + + var stagingRange = VkMappedMemoryRange( + sType: VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, + memory: stagingMemory, + size: VK_WHOLE_SIZE, + ) + checkVkResult vkFlushMappedMemoryRanges(vulkan.device, 1, addr(stagingRange)) + + WithSingleUseCommandBuffer(commandBuffer): + when T is (VkBuffer, uint64): + let copyRegion = VkBufferCopy( + size: bufferSize, + dstOffset: target[1], + srcOffset: 0 + ) + vkCmdCopyBuffer( + commandBuffer = commandBuffer, + srcBuffer = stagingBuffer, + dstBuffer = target[0], + regionCount = 1, + pRegions = addr(copyRegion) + ) + elif T is (VkImage, uint32, uint32): + let region = VkBufferImageCopy( + bufferOffset: 0, + bufferRowLength: 0, + bufferImageHeight: 0, + imageSubresource: VkImageSubresourceLayers( + aspectMask: toBits [VK_IMAGE_ASPECT_COLOR_BIT], + mipLevel: 0, + baseArrayLayer: 0, + layerCount: 1, + ), + imageOffset: VkOffset3D(x: 0, y: 0, z: 0), + imageExtent: VkExtent3D(width: target[1], height: target[2], depth: 1) + ) + vkCmdCopyBufferToImage( + commandBuffer = commandBuffer, + srcBuffer = stagingBuffer, + dstImage = target[0], + dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + regionCount = 1, + pRegions = addr(region) + ) + + vkDestroyBuffer(vulkan.device, stagingBuffer, nil) + vkFreeMemory(vulkan.device, stagingMemory, nil) +