Mercurial > games > semicongine
comparison src/zamikongine/vulkan_helpers.nim @ 19:b55d6ecde79d
did: introduce scene graph, meshs and generic vertex buffers
| author | Sam <sam@basx.dev> |
|---|---|
| date | Mon, 09 Jan 2023 11:04:19 +0700 |
| parents | src/vulkan_helpers.nim@b40466fa446a |
| children | b45a5d338cd0 |
comparison
equal
deleted
inserted
replaced
| 18:90e117952f74 | 19:b55d6ecde79d |
|---|---|
| 1 import std/tables | |
| 2 import std/strutils | |
| 3 import std/strformat | |
| 4 import std/logging | |
| 5 import std/macros | |
| 6 | |
| 7 import ./vulkan | |
| 8 import ./window | |
| 9 | |
| 10 | |
| 11 const ENABLEVULKANVALIDATIONLAYERS* = not defined(release) | |
| 12 | |
| 13 | |
| 14 template checkVkResult*(call: untyped) = | |
| 15 when defined(release): | |
| 16 discard call | |
| 17 else: | |
| 18 # yes, a bit cheap, but this is only for nice debug output | |
| 19 var callstr = astToStr(call).replace("\n", "") | |
| 20 while callstr.find(" ") >= 0: | |
| 21 callstr = callstr.replace(" ", " ") | |
| 22 debug "CALLING vulkan: ", callstr | |
| 23 let value = call | |
| 24 if value != VK_SUCCESS: | |
| 25 error "Vulkan error: ", astToStr(call), " returned ", $value | |
| 26 raise newException(Exception, "Vulkan error: " & astToStr(call) & " returned " & $value) | |
| 27 | |
| 28 func addrOrNil[T](obj: var openArray[T]): ptr T = | |
| 29 if obj.len > 0: addr(obj[0]) else: nil | |
| 30 | |
| 31 func VK_MAKE_API_VERSION*(variant: uint32, major: uint32, minor: uint32, patch: uint32): uint32 {.compileTime.} = | |
| 32 (variant shl 29) or (major shl 22) or (minor shl 12) or patch | |
| 33 | |
| 34 | |
| 35 func filterForSurfaceFormat*(formats: seq[VkSurfaceFormatKHR]): seq[VkSurfaceFormatKHR] = | |
| 36 for format in formats: | |
| 37 if format.format == VK_FORMAT_B8G8R8A8_SRGB and format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: | |
| 38 result.add(format) | |
| 39 | |
| 40 func getSuitableSurfaceFormat*(formats: seq[VkSurfaceFormatKHR]): VkSurfaceFormatKHR = | |
| 41 let usableSurfaceFormats = filterForSurfaceFormat(formats) | |
| 42 if len(usableSurfaceFormats) == 0: | |
| 43 raise newException(Exception, "No suitable surface formats found") | |
| 44 return usableSurfaceFormats[0] | |
| 45 | |
| 46 | |
| 47 func cleanString*(str: openArray[char]): string = | |
| 48 for i in 0 ..< len(str): | |
| 49 if str[i] == char(0): | |
| 50 result = join(str[0 ..< i]) | |
| 51 break | |
| 52 | |
| 53 proc getInstanceExtensions*(): seq[string] = | |
| 54 var extensionCount: uint32 | |
| 55 checkVkResult vkEnumerateInstanceExtensionProperties(nil, addr(extensionCount), nil) | |
| 56 var extensions = newSeq[VkExtensionProperties](extensionCount) | |
| 57 checkVkResult vkEnumerateInstanceExtensionProperties(nil, addr(extensionCount), addrOrNil(extensions)) | |
| 58 | |
| 59 for extension in extensions: | |
| 60 result.add(cleanString(extension.extensionName)) | |
| 61 | |
| 62 | |
| 63 proc getDeviceExtensions*(device: VkPhysicalDevice): seq[string] = | |
| 64 var extensionCount: uint32 | |
| 65 checkVkResult vkEnumerateDeviceExtensionProperties(device, nil, addr(extensionCount), nil) | |
| 66 var extensions = newSeq[VkExtensionProperties](extensionCount) | |
| 67 checkVkResult vkEnumerateDeviceExtensionProperties(device, nil, addr(extensionCount), addrOrNil(extensions)) | |
| 68 | |
| 69 for extension in extensions: | |
| 70 result.add(cleanString(extension.extensionName)) | |
| 71 | |
| 72 | |
| 73 proc getValidationLayers*(): seq[string] = | |
| 74 var n_layers: uint32 | |
| 75 checkVkResult vkEnumerateInstanceLayerProperties(addr(n_layers), nil) | |
| 76 var layers = newSeq[VkLayerProperties](n_layers) | |
| 77 checkVkResult vkEnumerateInstanceLayerProperties(addr(n_layers), addrOrNil(layers)) | |
| 78 | |
| 79 for layer in layers: | |
| 80 result.add(cleanString(layer.layerName)) | |
| 81 | |
| 82 | |
| 83 proc getVulkanPhysicalDevices*(instance: VkInstance): seq[VkPhysicalDevice] = | |
| 84 var n_devices: uint32 | |
| 85 checkVkResult vkEnumeratePhysicalDevices(instance, addr(n_devices), nil) | |
| 86 result = newSeq[VkPhysicalDevice](n_devices) | |
| 87 checkVkResult vkEnumeratePhysicalDevices(instance, addr(n_devices), addrOrNil(result)) | |
| 88 | |
| 89 | |
| 90 proc getQueueFamilies*(device: VkPhysicalDevice): seq[VkQueueFamilyProperties] = | |
| 91 var n_queuefamilies: uint32 | |
| 92 vkGetPhysicalDeviceQueueFamilyProperties(device, addr(n_queuefamilies), nil) | |
| 93 result = newSeq[VkQueueFamilyProperties](n_queuefamilies) | |
| 94 vkGetPhysicalDeviceQueueFamilyProperties(device, addr(n_queuefamilies), addrOrNil(result)) | |
| 95 | |
| 96 | |
| 97 proc getDeviceSurfaceFormats*(device: VkPhysicalDevice, surface: VkSurfaceKHR): seq[VkSurfaceFormatKHR] = | |
| 98 var n_formats: uint32 | |
| 99 checkVkResult vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, addr(n_formats), nil); | |
| 100 result = newSeq[VkSurfaceFormatKHR](n_formats) | |
| 101 checkVkResult vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, addr(n_formats), addrOrNil(result)) | |
| 102 | |
| 103 | |
| 104 proc getDeviceSurfacePresentModes*(device: VkPhysicalDevice, surface: VkSurfaceKHR): seq[VkPresentModeKHR] = | |
| 105 var n_modes: uint32 | |
| 106 checkVkResult vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, addr(n_modes), nil); | |
| 107 result = newSeq[VkPresentModeKHR](n_modes) | |
| 108 checkVkResult vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, addr(n_modes), addrOrNil(result)) | |
| 109 | |
| 110 | |
| 111 proc getSwapChainImages*(device: VkDevice, swapChain: VkSwapchainKHR): seq[VkImage] = | |
| 112 var n_images: uint32 | |
| 113 checkVkResult vkGetSwapchainImagesKHR(device, swapChain, addr(n_images), nil); | |
| 114 result = newSeq[VkImage](n_images) | |
| 115 checkVkResult vkGetSwapchainImagesKHR(device, swapChain, addr(n_images), addrOrNil(result)); | |
| 116 | |
| 117 | |
| 118 func getPresentMode*(modes: seq[VkPresentModeKHR]): VkPresentModeKHR = | |
| 119 let preferredModes = [ | |
| 120 VK_PRESENT_MODE_MAILBOX_KHR, # triple buffering | |
| 121 VK_PRESENT_MODE_FIFO_RELAXED_KHR, # double duffering | |
| 122 VK_PRESENT_MODE_FIFO_KHR, # double duffering | |
| 123 VK_PRESENT_MODE_IMMEDIATE_KHR, # single buffering | |
| 124 ] | |
| 125 for preferredMode in preferredModes: | |
| 126 for mode in modes: | |
| 127 if preferredMode == mode: | |
| 128 return mode | |
| 129 # should never be reached, but seems to be garuanteed by vulkan specs to always be available | |
| 130 return VK_PRESENT_MODE_FIFO_KHR | |
| 131 | |
| 132 | |
| 133 proc createVulkanInstance*(vulkanVersion: uint32): VkInstance = | |
| 134 | |
| 135 var requiredExtensions = @["VK_KHR_surface".cstring] | |
| 136 when defined(linux): | |
| 137 requiredExtensions.add("VK_KHR_xlib_surface".cstring) | |
| 138 when defined(windows): | |
| 139 requiredExtensions.add("VK_KHR_win32_surface".cstring) | |
| 140 when ENABLEVULKANVALIDATIONLAYERS: | |
| 141 requiredExtensions.add("VK_EXT_debug_utils".cstring) | |
| 142 | |
| 143 let availableExtensions = getInstanceExtensions() | |
| 144 for extension in requiredExtensions: | |
| 145 assert $extension in availableExtensions, $extension | |
| 146 | |
| 147 let availableLayers = getValidationLayers() | |
| 148 var usableLayers = newSeq[cstring]() | |
| 149 | |
| 150 when ENABLEVULKANVALIDATIONLAYERS: | |
| 151 const desiredLayers = ["VK_LAYER_KHRONOS_validation".cstring, "VK_LAYER_MESA_overlay".cstring] | |
| 152 for layer in desiredLayers: | |
| 153 if $layer in availableLayers: | |
| 154 usableLayers.add(layer) | |
| 155 | |
| 156 echo "Available validation layers: ", availableLayers | |
| 157 echo "Using validation layers: ", usableLayers | |
| 158 echo "Available extensions: ", availableExtensions | |
| 159 echo "Using extensions: ", requiredExtensions | |
| 160 | |
| 161 var appinfo = VkApplicationInfo( | |
| 162 sType: VK_STRUCTURE_TYPE_APPLICATION_INFO, | |
| 163 pApplicationName: "Hello Triangle", | |
| 164 pEngineName: "Custom engine", | |
| 165 apiVersion: vulkanVersion, | |
| 166 ) | |
| 167 var createinfo = VkInstanceCreateInfo( | |
| 168 sType: VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, | |
| 169 pApplicationInfo: addr(appinfo), | |
| 170 enabledLayerCount: usableLayers.len.uint32, | |
| 171 ppEnabledLayerNames: cast[ptr UncheckedArray[cstring]](addrOrNil(usableLayers)), | |
| 172 enabledExtensionCount: requiredExtensions.len.uint32, | |
| 173 ppEnabledExtensionNames: cast[ptr UncheckedArray[cstring]](addr(requiredExtensions[0])) | |
| 174 ) | |
| 175 checkVkResult vkCreateInstance(addr(createinfo), nil, addr(result)) | |
| 176 | |
| 177 loadVK_KHR_surface() | |
| 178 when defined(linux): | |
| 179 loadVK_KHR_xlib_surface() | |
| 180 when defined(windows): | |
| 181 loadVK_KHR_win32_surface() | |
| 182 loadVK_KHR_swapchain() | |
| 183 when ENABLEVULKANVALIDATIONLAYERS: | |
| 184 loadVK_EXT_debug_utils(result) | |
| 185 | |
| 186 | |
| 187 proc getVulcanDevice*( | |
| 188 physicalDevice: var VkPhysicalDevice, | |
| 189 features: var VkPhysicalDeviceFeatures, | |
| 190 graphicsQueueFamily: uint32, | |
| 191 presentationQueueFamily: uint32, | |
| 192 ): (VkDevice, VkQueue, VkQueue) = | |
| 193 # setup queue and device | |
| 194 # TODO: need check this, possibly wrong logic, see Vulkan tutorial | |
| 195 var priority = 1.0'f32 | |
| 196 var queueCreateInfo = [ | |
| 197 VkDeviceQueueCreateInfo( | |
| 198 sType: VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, | |
| 199 queueFamilyIndex: graphicsQueueFamily, | |
| 200 queueCount: 1, | |
| 201 pQueuePriorities: addr(priority), | |
| 202 ), | |
| 203 ] | |
| 204 | |
| 205 var requiredExtensions = ["VK_KHR_swapchain".cstring] | |
| 206 var deviceCreateInfo = VkDeviceCreateInfo( | |
| 207 sType: VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, | |
| 208 queueCreateInfoCount: uint32(queueCreateInfo.len), | |
| 209 pQueueCreateInfos: addrOrNil(queueCreateInfo), | |
| 210 pEnabledFeatures: addr(features), | |
| 211 enabledExtensionCount: requiredExtensions.len.uint32, | |
| 212 ppEnabledExtensionNames: cast[ptr UncheckedArray[cstring]](addr(requiredExtensions)) | |
| 213 ) | |
| 214 checkVkResult vkCreateDevice(physicalDevice, addr(deviceCreateInfo), nil, addr(result[0])) | |
| 215 vkGetDeviceQueue(result[0], graphicsQueueFamily, 0'u32, addr(result[1])); | |
| 216 vkGetDeviceQueue(result[0], presentationQueueFamily, 0'u32, addr(result[2])); | |
| 217 | |
| 218 proc debugCallback*( | |
| 219 messageSeverity: VkDebugUtilsMessageSeverityFlagBitsEXT, | |
| 220 messageTypes: VkDebugUtilsMessageTypeFlagsEXT, | |
| 221 pCallbackData: VkDebugUtilsMessengerCallbackDataEXT, | |
| 222 userData: pointer | |
| 223 ): VkBool32 {.cdecl.} = | |
| 224 echo &"{messageSeverity}: {VkDebugUtilsMessageTypeFlagBitsEXT(messageTypes)}: {pCallbackData.pMessage}" | |
| 225 return VK_FALSE | |
| 226 | |
| 227 proc getSurfaceCapabilities*(device: VkPhysicalDevice, surface: VkSurfaceKHR): VkSurfaceCapabilitiesKHR = | |
| 228 checkVkResult device.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(surface, addr(result)) | |
| 229 | |
| 230 when defined(linux): | |
| 231 proc createVulkanSurface*(instance: VkInstance, window: NativeWindow): VkSurfaceKHR = | |
| 232 var surfaceCreateInfo = VkXlibSurfaceCreateInfoKHR( | |
| 233 sType: VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, | |
| 234 dpy: window.display, | |
| 235 window: window.window, | |
| 236 ) | |
| 237 checkVkResult vkCreateXlibSurfaceKHR(instance, addr(surfaceCreateInfo), nil, addr(result)) | |
| 238 when defined(windows): | |
| 239 proc createVulkanSurface*(instance: VkInstance, window: NativeWindow): VkSurfaceKHR = | |
| 240 var surfaceCreateInfo = VkWin32SurfaceCreateInfoKHR( | |
| 241 sType: VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, | |
| 242 hinstance: window.hinstance, | |
| 243 hwnd: window.hwnd, | |
| 244 ) | |
| 245 checkVkResult vkCreateWin32SurfaceKHR(instance, addr(surfaceCreateInfo), nil, addr(result)) |
