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