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))