Mercurial > games > semicongine
comparison src/engine.nim @ 2:213fdf8d31dd
did: hello world triangle, a bit of code organization
author | Sam <sam@basx.dev> |
---|---|
date | Mon, 19 Dec 2022 10:41:20 +0700 |
parents | bb2a7d3a7003 |
children | af9183acb173 |
comparison
equal
deleted
inserted
replaced
1:bb2a7d3a7003 | 2:213fdf8d31dd |
---|---|
1 import std/strformat | |
1 import std/enumerate | 2 import std/enumerate |
3 import std/logging | |
4 | |
2 | 5 |
3 import ./vulkan | 6 import ./vulkan |
4 import ./vulkan_helpers | 7 import ./vulkan_helpers |
5 import ./xlib_helpers | 8 import ./xlib_helpers |
6 | 9 |
7 import ./glslang/glslang | 10 import ./glslang/glslang |
11 | |
12 const MAX_FRAMES_IN_FLIGHT = 2 | |
13 | |
14 var logger = newConsoleLogger() | |
15 addHandler(logger) | |
16 | |
8 | 17 |
9 var vertexShaderCode: string = """#version 450 | 18 var vertexShaderCode: string = """#version 450 |
10 layout(location = 0) out vec3 fragColor; | 19 layout(location = 0) out vec3 fragColor; |
11 vec3 colors[3] = vec3[]( | 20 vec3 colors[3] = vec3[]( |
12 vec3(1.0, 0.0, 0.0), | 21 vec3(1.0, 0.0, 0.0), |
37 const VULKAN_VERSION = VK_MAKE_API_VERSION(0'u32, 1'u32, 2'u32, 0'u32) | 46 const VULKAN_VERSION = VK_MAKE_API_VERSION(0'u32, 1'u32, 2'u32, 0'u32) |
38 | 47 |
39 type | 48 type |
40 GraphicsPipeline = object | 49 GraphicsPipeline = object |
41 shaderStages*: seq[VkPipelineShaderStageCreateInfo] | 50 shaderStages*: seq[VkPipelineShaderStageCreateInfo] |
51 layout*: VkPipelineLayout | |
52 renderPass*: VkRenderPass | |
53 pipeline*: VkPipeline | |
42 QueueFamily = object | 54 QueueFamily = object |
43 properties*: VkQueueFamilyProperties | 55 properties*: VkQueueFamilyProperties |
44 hasSurfaceSupport*: bool | 56 hasSurfaceSupport*: bool |
45 PhyscialDevice = object | 57 PhysicalDevice = object |
46 device*: VkPhysicalDevice | 58 device*: VkPhysicalDevice |
47 extensions*: seq[string] | 59 extensions*: seq[string] |
48 properties*: VkPhysicalDeviceProperties | 60 properties*: VkPhysicalDeviceProperties |
49 features*: VkPhysicalDeviceFeatures | 61 features*: VkPhysicalDeviceFeatures |
50 queueFamilies*: seq[QueueFamily] | 62 queueFamilies*: seq[QueueFamily] |
51 surfaceCapabilities*: VkSurfaceCapabilitiesKHR | 63 surfaceCapabilities*: VkSurfaceCapabilitiesKHR |
52 surfaceFormats: seq[VkSurfaceFormatKHR] | 64 surfaceFormats: seq[VkSurfaceFormatKHR] |
53 presentModes: seq[VkPresentModeKHR] | 65 presentModes: seq[VkPresentModeKHR] |
54 Vulkan* = object | 66 Vulkan* = object |
67 debugMessenger: VkDebugUtilsMessengerEXT | |
55 instance*: VkInstance | 68 instance*: VkInstance |
56 deviceList*: seq[PhyscialDevice] | 69 deviceList*: seq[PhysicalDevice] |
57 activePhysicalDevice*: PhyscialDevice | 70 activePhysicalDevice*: PhysicalDevice |
58 activeQueueFamily*: uint32 | 71 graphicsQueueFamily*: uint32 |
72 graphicsQueue*: VkQueue | |
73 presentationQueueFamily*: uint32 | |
74 presentationQueue*: VkQueue | |
59 device*: VkDevice | 75 device*: VkDevice |
60 presentationQueue*: VkQueue | |
61 surface*: VkSurfaceKHR | 76 surface*: VkSurfaceKHR |
62 selectedSurfaceFormat: VkSurfaceFormatKHR | 77 selectedSurfaceFormat: VkSurfaceFormatKHR |
63 selectedPresentationMode: VkPresentModeKHR | 78 selectedPresentationMode: VkPresentModeKHR |
64 selectedExtent: VkExtent2D | 79 frameDimension: VkExtent2D |
65 swapChain: VkSwapchainKHR | 80 swapChain: VkSwapchainKHR |
66 swapImages: seq[VkImage] | 81 swapImages: seq[VkImage] |
82 swapFramebuffers: seq[VkFramebuffer] | |
67 swapImageViews: seq[VkImageView] | 83 swapImageViews: seq[VkImageView] |
84 pipeline*: GraphicsPipeline | |
85 commandPool*: VkCommandPool | |
86 commandBuffers*: array[MAX_FRAMES_IN_FLIGHT, VkCommandBuffer] | |
87 viewport*: VkViewport | |
88 scissor*: VkRect2D | |
89 imageAvailableSemaphores*: array[MAX_FRAMES_IN_FLIGHT, VkSemaphore] | |
90 renderFinishedSemaphores*: array[MAX_FRAMES_IN_FLIGHT, VkSemaphore] | |
91 inFlightFences*: array[MAX_FRAMES_IN_FLIGHT, VkFence] | |
68 Engine* = object | 92 Engine* = object |
69 display*: PDisplay | 93 display*: PDisplay |
70 window*: x.Window | 94 window*: x.Window |
71 vulkan*: Vulkan | 95 vulkan*: Vulkan |
72 pipeline*: GraphicsPipeline | 96 |
73 | 97 |
74 | 98 proc getAllPhysicalDevices(instance: VkInstance, surface: VkSurfaceKHR): seq[PhysicalDevice] = |
75 proc getAllPhysicalDevices(instance: VkInstance, surface: VkSurfaceKHR): seq[PhyscialDevice] = | |
76 for vulkanPhysicalDevice in getVulkanPhysicalDevices(instance): | 99 for vulkanPhysicalDevice in getVulkanPhysicalDevices(instance): |
77 var device = PhyscialDevice(device: vulkanPhysicalDevice, extensions: getDeviceExtensions(vulkanPhysicalDevice)) | 100 var device = PhysicalDevice(device: vulkanPhysicalDevice, extensions: getDeviceExtensions(vulkanPhysicalDevice)) |
78 vkGetPhysicalDeviceProperties(vulkanPhysicalDevice, addr(device.properties)) | 101 vkGetPhysicalDeviceProperties(vulkanPhysicalDevice, addr(device.properties)) |
79 vkGetPhysicalDeviceFeatures(vulkanPhysicalDevice, addr(device.features)) | 102 vkGetPhysicalDeviceFeatures(vulkanPhysicalDevice, addr(device.features)) |
80 checkVkResult vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vulkanPhysicalDevice, surface, addr(device.surfaceCapabilities)) | 103 checkVkResult vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vulkanPhysicalDevice, surface, addr(device.surfaceCapabilities)) |
81 device.surfaceFormats = getDeviceSurfaceFormats(vulkanPhysicalDevice, surface) | 104 device.surfaceFormats = getDeviceSurfaceFormats(vulkanPhysicalDevice, surface) |
82 device.presentModes = getDeviceSurfacePresentModes(vulkanPhysicalDevice, surface) | 105 device.presentModes = getDeviceSurfacePresentModes(vulkanPhysicalDevice, surface) |
83 | 106 |
107 debug(&"Physical device nr {int(vulkanPhysicalDevice)} {cleanString(device.properties.deviceName)}") | |
84 for i, queueFamilyProperty in enumerate(getQueueFamilies(vulkanPhysicalDevice)): | 108 for i, queueFamilyProperty in enumerate(getQueueFamilies(vulkanPhysicalDevice)): |
85 var hasSurfaceSupport: VkBool32 = VK_FALSE | 109 var hasSurfaceSupport: VkBool32 = VK_FALSE |
86 checkVkResult vkGetPhysicalDeviceSurfaceSupportKHR(vulkanPhysicalDevice, uint32(i), surface, addr(hasSurfaceSupport)) | 110 checkVkResult vkGetPhysicalDeviceSurfaceSupportKHR(vulkanPhysicalDevice, uint32(i), surface, addr(hasSurfaceSupport)) |
87 device.queueFamilies.add(QueueFamily(properties: queueFamilyProperty, hasSurfaceSupport: bool(hasSurfaceSupport))) | 111 device.queueFamilies.add(QueueFamily(properties: queueFamilyProperty, hasSurfaceSupport: bool(hasSurfaceSupport))) |
112 debug(&" Queue family {i} {queueFamilyProperty}") | |
88 | 113 |
89 result.add(device) | 114 result.add(device) |
90 | 115 |
91 proc filterForDevice(devices: seq[PhyscialDevice]): seq[(PhyscialDevice, uint32)] = | 116 proc filterForDevice(devices: seq[PhysicalDevice]): seq[(PhysicalDevice, uint32, uint32)] = |
92 for device in devices: | 117 for device in devices: |
93 if "VK_KHR_swapchain" in device.extensions: | 118 if not (device.surfaceFormats.len > 0 and device.presentModes.len > 0 and "VK_KHR_swapchain" in device.extensions): |
94 for i, queueFamily in enumerate(device.queueFamilies): | 119 continue |
95 let hasGraphics = bool(uint32(queueFamily.properties.queueFlags) and ord(VK_QUEUE_GRAPHICS_BIT)) | 120 var graphicsQueueFamily = high(uint32) |
96 if ( | 121 var presentationQueueFamily = high(uint32) |
97 queueFamily.hasSurfaceSupport and | 122 for i, queueFamily in enumerate(device.queueFamilies): |
98 hasGraphics and | 123 if queueFamily.hasSurfaceSupport: |
99 device.surfaceFormats.len > 0 and | 124 presentationQueueFamily = uint32(i) |
100 device.presentModes.len > 0 | 125 if bool(uint32(queueFamily.properties.queueFlags) and ord(VK_QUEUE_GRAPHICS_BIT)): |
101 ): | 126 graphicsQueueFamily = uint32(i) |
102 result.add((device, uint32(i))) | 127 if graphicsQueueFamily != high(uint32) and presentationQueueFamily != high(uint32): |
103 | 128 result.add((device, graphicsQueueFamily, presentationQueueFamily)) |
104 proc filterForSurfaceFormat(formats: seq[VkSurfaceFormatKHR]): seq[VkSurfaceFormatKHR] = | 129 |
105 for format in formats: | 130 for (device, graphicsQueueFamily, presentationQueueFamily) in result: |
106 if format.format == VK_FORMAT_B8G8R8A8_SRGB and format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: | 131 debug(&"Viable device: {cleanString(device.properties.deviceName)} (graphics queue family {graphicsQueueFamily}, presentation queue family {presentationQueueFamily})") |
107 result.add(format) | 132 |
108 | 133 |
109 proc getSwapExtent(display: PDisplay, window: Window, capabilities: VkSurfaceCapabilitiesKHR): VkExtent2D = | 134 proc getFrameDimension(display: PDisplay, window: Window, capabilities: VkSurfaceCapabilitiesKHR): VkExtent2D = |
110 if capabilities.currentExtent.width != high(uint32): | 135 if capabilities.currentExtent.width != high(uint32): |
111 return capabilities.currentExtent | 136 return capabilities.currentExtent |
112 else: | 137 else: |
113 let (width, height) = xlibFramebufferSize(display, window) | 138 let (width, height) = xlibFramebufferSize(display, window) |
114 return VkExtent2D( | 139 return VkExtent2D( |
115 width: min(max(uint32(width), capabilities.minImageExtent.width), capabilities.maxImageExtent.width), | 140 width: min(max(uint32(width), capabilities.minImageExtent.width), capabilities.maxImageExtent.width), |
116 height: min(max(uint32(height), capabilities.minImageExtent.height), capabilities.maxImageExtent.height), | 141 height: min(max(uint32(height), capabilities.minImageExtent.height), capabilities.maxImageExtent.height), |
117 ) | 142 ) |
118 | 143 |
144 proc createVulkanSurface(instance: VkInstance, display: PDisplay, window: Window): VkSurfaceKHR = | |
145 var surfaceCreateInfo = VkXlibSurfaceCreateInfoKHR( | |
146 sType: VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, | |
147 dpy: display, | |
148 window: window, | |
149 ) | |
150 checkVkResult vkCreateXlibSurfaceKHR(instance, addr(surfaceCreateInfo), nil, addr(result)) | |
151 | |
152 proc setupVulkanDeviceAndQueues(instance: VkInstance, surface: VkSurfaceKHR): (PhysicalDevice, uint32, uint32, VkDevice, VkQueue, VkQueue) = | |
153 let usableDevices = instance.getAllPhysicalDevices(surface).filterForDevice() | |
154 if len(usableDevices) == 0: | |
155 raise newException(Exception, "No suitable graphics device found") | |
156 result[0] = usableDevices[0][0] | |
157 result[1] = usableDevices[0][1] | |
158 result[2] = usableDevices[0][2] | |
159 | |
160 debug(&"Chose device {cleanString(result[0].properties.deviceName)}") | |
161 | |
162 (result[3], result[4], result[5]) = getVulcanDevice( | |
163 result[0].device, | |
164 result[0].features, | |
165 result[1], | |
166 result[2], | |
167 ) | |
168 | |
119 proc igniteEngine*(): Engine = | 169 proc igniteEngine*(): Engine = |
170 | |
171 # init X11 window | |
172 (result.display, result.window) = xlibInit() | |
173 | |
174 # create vulkan instance | |
120 vkLoad1_0() | 175 vkLoad1_0() |
121 vkLoad1_1() | 176 vkLoad1_1() |
122 vkLoad1_2() | 177 vkLoad1_2() |
123 | |
124 # init X11 window | |
125 (result.display, result.window) = xlibInit() | |
126 | |
127 # create vulkan instance | |
128 result.vulkan.instance = createVulkanInstance(VULKAN_VERSION) | 178 result.vulkan.instance = createVulkanInstance(VULKAN_VERSION) |
129 | 179 when ENABLEVULKANVALIDATIONLAYERS: |
130 # create vulkan-X11 surface | 180 var createInfo = VkDebugUtilsMessengerCreateInfoEXT( |
131 var surfaceCreateInfo = VkXlibSurfaceCreateInfoKHR( | 181 sType: VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, |
132 sType: VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, | 182 messageSeverity: VkDebugUtilsMessageSeverityFlagsEXT( |
133 dpy: result.display, | 183 ord(VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) or |
134 window: result.window, | 184 ord(VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) or |
135 ) | 185 ord(VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) |
136 checkVkResult vkCreateXlibSurfaceKHR(result.vulkan.instance, addr(surfaceCreateInfo), nil, addr(result.vulkan.surface)) | 186 ), |
137 | 187 messageType: VkDebugUtilsMessageTypeFlagsEXT( |
138 # determine device and queue to use and instantiate | 188 ord(VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) or |
139 result.vulkan.deviceList = result.vulkan.instance.getAllPhysicalDevices(result.vulkan.surface) | 189 ord(VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) or |
140 let usableDevices = result.vulkan.deviceList.filterForDevice() | 190 ord(VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) |
141 if len(usableDevices) == 0: | 191 ), |
142 raise newException(Exception, "No suitable graphics device found") | 192 pfnUserCallback: debugCallback, |
143 (result.vulkan.activePhysicalDevice, result.vulkan.activeQueueFamily) = usableDevices[0] | 193 pUserData: nil, |
144 | 194 ) |
145 (result.vulkan.device, result.vulkan.presentationQueue) = getVulcanDevice( | 195 checkVkResult vkCreateDebugUtilsMessengerEXT(result.vulkan.instance, addr(createInfo), nil, addr(result.vulkan.debugMessenger)) |
146 result.vulkan.activePhysicalDevice.device, | 196 |
147 result.vulkan.activePhysicalDevice.features, | 197 result.vulkan.surface = result.vulkan.instance.createVulkanSurface(result.display, result.window) |
148 result.vulkan.activeQueueFamily | 198 |
149 ) | 199 ( |
200 result.vulkan.activePhysicalDevice, | |
201 result.vulkan.graphicsQueueFamily, | |
202 result.vulkan.presentationQueueFamily, | |
203 result.vulkan.device, | |
204 result.vulkan.graphicsQueue, | |
205 result.vulkan.presentationQueue | |
206 ) = result.vulkan.instance.setupVulkanDeviceAndQueues(result.vulkan.surface) | |
150 | 207 |
151 # determine surface format for swapchain | 208 # determine surface format for swapchain |
152 let usableSurfaceFormats = filterForSurfaceFormat(result.vulkan.activePhysicalDevice.surfaceFormats) | 209 let usableSurfaceFormats = filterForSurfaceFormat(result.vulkan.activePhysicalDevice.surfaceFormats) |
153 if len(usableSurfaceFormats) == 0: | 210 if len(usableSurfaceFormats) == 0: |
154 raise newException(Exception, "No suitable surface formats found") | 211 raise newException(Exception, "No suitable surface formats found") |
155 result.vulkan.selectedSurfaceFormat = usableSurfaceFormats[0] | 212 result.vulkan.selectedSurfaceFormat = usableSurfaceFormats[0] |
156 result.vulkan.selectedPresentationMode = getPresentMode(result.vulkan.activePhysicalDevice.presentModes) | 213 result.vulkan.selectedPresentationMode = getPresentMode(result.vulkan.activePhysicalDevice.presentModes) |
157 result.vulkan.selectedExtent = getSwapExtent(result.display, result.window, result.vulkan.activePhysicalDevice.surfaceCapabilities) | 214 result.vulkan.frameDimension = result.display.getFrameDimension(result.window, result.vulkan.activePhysicalDevice.surfaceCapabilities) |
158 | 215 |
159 # setup swapchain | 216 # setup swapchain |
160 var swapchainCreateInfo = VkSwapchainCreateInfoKHR( | 217 var swapchainCreateInfo = VkSwapchainCreateInfoKHR( |
161 sType: VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, | 218 sType: VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, |
162 surface: result.vulkan.surface, | 219 surface: result.vulkan.surface, |
163 minImageCount: max(result.vulkan.activePhysicalDevice.surfaceCapabilities.minImageCount + 1, result.vulkan.activePhysicalDevice.surfaceCapabilities.maxImageCount), | 220 minImageCount: max(result.vulkan.activePhysicalDevice.surfaceCapabilities.minImageCount + 1, result.vulkan.activePhysicalDevice.surfaceCapabilities.maxImageCount), |
164 imageFormat: result.vulkan.selectedSurfaceFormat.format, | 221 imageFormat: result.vulkan.selectedSurfaceFormat.format, |
165 imageColorSpace: result.vulkan.selectedSurfaceFormat.colorSpace, | 222 imageColorSpace: result.vulkan.selectedSurfaceFormat.colorSpace, |
166 imageExtent: result.vulkan.selectedExtent, | 223 imageExtent: result.vulkan.frameDimension, |
167 imageArrayLayers: 1, | 224 imageArrayLayers: 1, |
168 imageUsage: VkImageUsageFlags(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT), | 225 imageUsage: VkImageUsageFlags(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT), |
169 # VK_SHARING_MODE_CONCURRENT no supported (i.e cannot use different queue families for drawing to swap surface?) | 226 # VK_SHARING_MODE_CONCURRENT no supported (i.e cannot use different queue families for drawing to swap surface?) |
170 imageSharingMode: VK_SHARING_MODE_EXCLUSIVE, | 227 imageSharingMode: VK_SHARING_MODE_EXCLUSIVE, |
171 preTransform: result.vulkan.activePhysicalDevice.surfaceCapabilities.currentTransform, | 228 preTransform: result.vulkan.activePhysicalDevice.surfaceCapabilities.currentTransform, |
172 compositeAlpha: VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, | 229 compositeAlpha: VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, |
173 presentMode: result.vulkan.selectedPresentationMode, | 230 presentMode: result.vulkan.selectedPresentationMode, |
174 clipped: VK_TRUE, | 231 clipped: VK_TRUE, |
175 oldSwapchain: VkSwapchainKHR(0), | 232 oldSwapchain: VkSwapchainKHR(0), |
176 ) | 233 ) |
177 checkVkResult vkCreateSwapchainKHR(result.vulkan.device, addr(swapchainCreateInfo), nil, addr(result.vulkan.swapChain)) | 234 checkVkResult result.vulkan.device.vkCreateSwapchainKHR(addr(swapchainCreateInfo), nil, addr(result.vulkan.swapChain)) |
178 result.vulkan.swapImages = getSwapChainImages(result.vulkan.device, result.vulkan.swapChain) | 235 result.vulkan.swapImages = result.vulkan.device.getSwapChainImages(result.vulkan.swapChain) |
179 | 236 |
180 # setup swapchian image views | 237 # setup swapchian image views |
181 result.vulkan.swapImageViews = newSeq[VkImageView](result.vulkan.swapImages.len) | 238 result.vulkan.swapImageViews = newSeq[VkImageView](result.vulkan.swapImages.len) |
182 for i, image in enumerate(result.vulkan.swapImages): | 239 for i, image in enumerate(result.vulkan.swapImages): |
183 var imageViewCreateInfo = VkImageViewCreateInfo( | 240 var imageViewCreateInfo = VkImageViewCreateInfo( |
197 levelCount: 1, | 254 levelCount: 1, |
198 baseArrayLayer: 0, | 255 baseArrayLayer: 0, |
199 layerCount: 1, | 256 layerCount: 1, |
200 ), | 257 ), |
201 ) | 258 ) |
202 checkVkResult vkCreateImageView(result.vulkan.device, addr(imageViewCreateInfo), nil, addr(result.vulkan.swapImageViews[i])) | 259 checkVkResult result.vulkan.device.vkCreateImageView(addr(imageViewCreateInfo), nil, addr(result.vulkan.swapImageViews[i])) |
203 | 260 |
204 # init shader system | 261 # init shader system |
205 checkGlslangResult glslang_initialize_process() | 262 checkGlslangResult glslang_initialize_process() |
206 | 263 |
207 # load shaders | 264 # load shaders |
208 result.pipeline.shaderStages.add(createShaderStage(result.vulkan.device, VK_SHADER_STAGE_VERTEX_BIT, vertexShaderCode)) | 265 result.vulkan.pipeline.shaderStages.add(result.vulkan.device.createShaderStage(VK_SHADER_STAGE_VERTEX_BIT, vertexShaderCode)) |
209 result.pipeline.shaderStages.add(createShaderStage(result.vulkan.device, VK_SHADER_STAGE_FRAGMENT_BIT, fragmentShaderCode)) | 266 result.vulkan.pipeline.shaderStages.add(result.vulkan.device.createShaderStage(VK_SHADER_STAGE_FRAGMENT_BIT, fragmentShaderCode)) |
267 | |
268 # setup render passes | |
269 var | |
270 colorAttachment = VkAttachmentDescription( | |
271 format: result.vulkan.selectedSurfaceFormat.format, | |
272 samples: VK_SAMPLE_COUNT_1_BIT, | |
273 loadOp: VK_ATTACHMENT_LOAD_OP_CLEAR, | |
274 storeOp: VK_ATTACHMENT_STORE_OP_STORE, | |
275 stencilLoadOp: VK_ATTACHMENT_LOAD_OP_DONT_CARE, | |
276 stencilStoreOp: VK_ATTACHMENT_STORE_OP_DONT_CARE, | |
277 initialLayout: VK_IMAGE_LAYOUT_UNDEFINED, | |
278 finalLayout: VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, | |
279 ) | |
280 colorAttachmentRef = VkAttachmentReference( | |
281 attachment: 0, | |
282 layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, | |
283 ) | |
284 subpass = VkSubpassDescription( | |
285 pipelineBindPoint: VK_PIPELINE_BIND_POINT_GRAPHICS, | |
286 colorAttachmentCount: 1, | |
287 pColorAttachments: addr(colorAttachmentRef) | |
288 ) | |
289 dependency = VkSubpassDependency( | |
290 srcSubpass: VK_SUBPASS_EXTERNAL, | |
291 dstSubpass: 0, | |
292 srcStageMask: VkPipelineStageFlags(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT), | |
293 srcAccessMask: VkAccessFlags(0), | |
294 dstStageMask: VkPipelineStageFlags(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT), | |
295 dstAccessMask: VkAccessFlags(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT), | |
296 ) | |
297 renderPassCreateInfo = VkRenderPassCreateInfo( | |
298 sType: VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, | |
299 attachmentCount: 1, | |
300 pAttachments: addr(colorAttachment), | |
301 subpassCount: 1, | |
302 pSubpasses: addr(subpass), | |
303 dependencyCount: 1, | |
304 pDependencies: addr(dependency), | |
305 ) | |
306 checkVkResult result.vulkan.device.vkCreateRenderPass(addr(renderPassCreateInfo), nil, addr(result.vulkan.pipeline.renderPass)) | |
210 | 307 |
211 # create graphis pipeline | 308 # create graphis pipeline |
212 var dynamicStates = [VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR] | 309 |
213 var dynamicState = VkPipelineDynamicStateCreateInfo( | 310 var |
214 sType: VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, | 311 # define which parts can be dynamic (pipeline is fixed after setup) |
215 dynamicStateCount: uint32(dynamicStates.len), | 312 dynamicStates = [VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR] |
216 pDynamicStates: addr(dynamicStates[0]), | 313 dynamicState = VkPipelineDynamicStateCreateInfo( |
217 ) | 314 sType: VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, |
218 var vertexInputInfo = VkPipelineVertexInputStateCreateInfo( | 315 dynamicStateCount: uint32(dynamicStates.len), |
219 sType: VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, | 316 pDynamicStates: addr(dynamicStates[0]), |
220 vertexBindingDescriptionCount: 0, | 317 ) |
221 pVertexBindingDescriptions: nil, | 318 |
222 vertexAttributeDescriptionCount: 0, | 319 # define input data format |
223 pVertexAttributeDescriptions: nil, | 320 vertexInputInfo = VkPipelineVertexInputStateCreateInfo( |
224 ) | 321 sType: VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, |
225 var inputAssembly = VkPipelineInputAssemblyStateCreateInfo( | 322 vertexBindingDescriptionCount: 0, |
226 sType: VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, | 323 pVertexBindingDescriptions: nil, |
227 topology: VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, | 324 vertexAttributeDescriptionCount: 0, |
228 primitiveRestartEnable: VK_FALSE, | 325 pVertexAttributeDescriptions: nil, |
229 ) | 326 ) |
327 inputAssembly = VkPipelineInputAssemblyStateCreateInfo( | |
328 sType: VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, | |
329 topology: VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, | |
330 primitiveRestartEnable: VK_FALSE, | |
331 ) | |
230 | 332 |
231 # setup viewport | 333 # setup viewport |
232 var viewport = VkViewport( | 334 result.vulkan.viewport = VkViewport( |
233 x: 0.0, | 335 x: 0.0, |
234 y: 0.0, | 336 y: 0.0, |
235 width: (float) result.vulkan.selectedExtent.width, | 337 width: (float) result.vulkan.frameDimension.width, |
236 height: (float) result.vulkan.selectedExtent.height, | 338 height: (float) result.vulkan.frameDimension.height, |
237 minDepth: 0.0, | 339 minDepth: 0.0, |
238 maxDepth: 1.0, | 340 maxDepth: 1.0, |
239 ) | 341 ) |
240 var scissor = VkRect2D( | 342 result.vulkan.scissor = VkRect2D( |
241 offset: VkOffset2D(x: 0, y: 0), | 343 offset: VkOffset2D(x: 0, y: 0), |
242 extent: result.vulkan.selectedExtent | 344 extent: result.vulkan.frameDimension |
243 ) | 345 ) |
244 | 346 var viewportState = VkPipelineViewportStateCreateInfo( |
245 | 347 sType: VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, |
246 proc fullThrottle*(engine: Engine) = | 348 viewportCount: 1, |
247 var event: XEvent | 349 pViewports: addr(result.vulkan.viewport), |
248 while true: | 350 scissorCount: 1, |
249 discard XNextEvent(engine.display, addr(event)) | 351 pScissors: addr(result.vulkan.scissor), |
250 case event.theType | 352 ) |
251 of Expose: | 353 |
252 discard | 354 # rasterizerization config |
253 of ClientMessage: | 355 var |
254 if cast[Atom](event.xclient.data.l[0]) == deleteMessage: | 356 rasterizer = VkPipelineRasterizationStateCreateInfo( |
255 break | 357 sType: VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, |
256 of KeyPress: | 358 depthClampEnable: VK_FALSE, |
257 let key = XLookupKeysym(cast[PXKeyEvent](addr(event)), 0) | 359 rasterizerDiscardEnable: VK_FALSE, |
258 if key != 0: | 360 polygonMode: VK_POLYGON_MODE_FILL, |
259 echo "Key ", key, " pressed" | 361 lineWidth: 1.0, |
260 of ButtonPressMask: | 362 cullMode: VkCullModeFlags(VK_CULL_MODE_BACK_BIT), |
261 echo "Mouse button ", event.xbutton.button, " pressed at ", | 363 frontFace: VK_FRONT_FACE_CLOCKWISE, |
262 event.xbutton.x, ",", event.xbutton.y | 364 depthBiasEnable: VK_FALSE, |
263 else: | 365 depthBiasConstantFactor: 0.0, |
264 discard | 366 depthBiasClamp: 0.0, |
367 depthBiasSlopeFactor: 0.0, | |
368 ) | |
369 multisampling = VkPipelineMultisampleStateCreateInfo( | |
370 sType: VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, | |
371 sampleShadingEnable: VK_FALSE, | |
372 rasterizationSamples: VK_SAMPLE_COUNT_1_BIT, | |
373 minSampleShading: 1.0, | |
374 pSampleMask: nil, | |
375 alphaToCoverageEnable: VK_FALSE, | |
376 alphaToOneEnable: VK_FALSE, | |
377 ) | |
378 colorBlendAttachment = VkPipelineColorBlendAttachmentState( | |
379 colorWriteMask: VkColorComponentFlags( | |
380 ord(VK_COLOR_COMPONENT_R_BIT) or | |
381 ord(VK_COLOR_COMPONENT_G_BIT) or | |
382 ord(VK_COLOR_COMPONENT_B_BIT) or | |
383 ord(VK_COLOR_COMPONENT_A_BIT) | |
384 ), | |
385 blendEnable: VK_TRUE, | |
386 srcColorBlendFactor: VK_BLEND_FACTOR_SRC_ALPHA, | |
387 dstColorBlendFactor: VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, | |
388 colorBlendOp: VK_BLEND_OP_ADD, | |
389 srcAlphaBlendFactor: VK_BLEND_FACTOR_ONE, | |
390 dstAlphaBlendFactor: VK_BLEND_FACTOR_ZERO, | |
391 alphaBlendOp: VK_BLEND_OP_ADD, | |
392 ) | |
393 colorBlending = VkPipelineColorBlendStateCreateInfo( | |
394 sType: VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, | |
395 logicOpEnable: VK_TRUE, | |
396 logicOp: VK_LOGIC_OP_COPY, | |
397 attachmentCount: 1, | |
398 pAttachments: addr(colorBlendAttachment), | |
399 blendConstants: [0.0'f, 0.0'f, 0.0'f, 0.0'f], | |
400 ) | |
401 | |
402 # create pipeline | |
403 pipelineLayoutInfo = VkPipelineLayoutCreateInfo( | |
404 sType: VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, | |
405 setLayoutCount: 0, | |
406 pSetLayouts: nil, | |
407 pushConstantRangeCount: 0, | |
408 pPushConstantRanges: nil, | |
409 ) | |
410 checkVkResult result.vulkan.device.vkCreatePipelineLayout(addr(pipelineLayoutInfo), nil, addr(result.vulkan.pipeline.layout)) | |
411 | |
412 var pipelineInfo = VkGraphicsPipelineCreateInfo( | |
413 sType: VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, | |
414 stageCount: 2, | |
415 pStages: addr(result.vulkan.pipeline.shaderStages[0]), | |
416 pVertexInputState: addr(vertexInputInfo), | |
417 pInputAssemblyState: addr(inputAssembly), | |
418 pViewportState: addr(viewportState), | |
419 pRasterizationState: addr(rasterizer), | |
420 pMultisampleState: addr(multisampling), | |
421 pDepthStencilState: nil, | |
422 pColorBlendState: addr(colorBlending), | |
423 pDynamicState: addr(dynamicState), | |
424 layout: result.vulkan.pipeline.layout, | |
425 renderPass: result.vulkan.pipeline.renderPass, | |
426 subpass: 0, | |
427 basePipelineHandle: VkPipeline(0), | |
428 basePipelineIndex: -1, | |
429 ) | |
430 checkVkResult result.vulkan.device.vkCreateGraphicsPipelines( | |
431 VkPipelineCache(0), | |
432 1, | |
433 addr(pipelineInfo), | |
434 nil, | |
435 addr(result.vulkan.pipeline.pipeline) | |
436 ) | |
437 | |
438 # set up framebuffers | |
439 result.vulkan.swapFramebuffers = newSeq[VkFramebuffer](result.vulkan.swapImages.len) | |
440 | |
441 for i, imageview in enumerate(result.vulkan.swapImageViews): | |
442 var framebufferInfo = VkFramebufferCreateInfo( | |
443 sType: VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, | |
444 renderPass: result.vulkan.pipeline.renderPass, | |
445 attachmentCount: 1, | |
446 pAttachments: addr(result.vulkan.swapImageViews[i]), | |
447 width: result.vulkan.frameDimension.width, | |
448 height: result.vulkan.frameDimension.height, | |
449 layers: 1, | |
450 ) | |
451 checkVkResult result.vulkan.device.vkCreateFramebuffer(addr(framebufferInfo), nil, addr(result.vulkan.swapFramebuffers[i])) | |
452 | |
453 # set up command buffer | |
454 var poolInfo = VkCommandPoolCreateInfo( | |
455 sType: VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, | |
456 flags: VkCommandPoolCreateFlags(VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT), | |
457 queueFamilyIndex: result.vulkan.graphicsQueueFamily, | |
458 ) | |
459 checkVkResult result.vulkan.device.vkCreateCommandPool(addr(poolInfo), nil, addr(result.vulkan.commandPool)) | |
460 | |
461 var allocInfo = VkCommandBufferAllocateInfo( | |
462 sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, | |
463 commandPool: result.vulkan.commandPool, | |
464 level: VK_COMMAND_BUFFER_LEVEL_PRIMARY, | |
465 commandBufferCount: result.vulkan.commandBuffers.len.uint32, | |
466 ) | |
467 checkVkResult result.vulkan.device.vkAllocateCommandBuffers(addr(allocInfo), addr(result.vulkan.commandBuffers[0])) | |
468 | |
469 # create semaphores for syncing rendering | |
470 var semaphoreInfo = VkSemaphoreCreateInfo(sType: VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO) | |
471 var fenceInfo = VkFenceCreateInfo( | |
472 sType: VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, | |
473 flags: VkFenceCreateFlags(VK_FENCE_CREATE_SIGNALED_BIT) | |
474 ) | |
475 for i in 0 ..< MAX_FRAMES_IN_FLIGHT: | |
476 checkVkResult result.vulkan.device.vkCreateSemaphore(addr(semaphoreInfo), nil, addr(result.vulkan.imageAvailableSemaphores[i])) | |
477 checkVkResult result.vulkan.device.vkCreateSemaphore(addr(semaphoreInfo), nil, addr(result.vulkan.renderFinishedSemaphores[i])) | |
478 checkVkResult result.vulkan.device.vkCreateFence(addr(fenceInfo), nil, addr(result.vulkan.inFlightFences[i])) | |
479 | |
480 | |
481 proc recordCommandBuffer(vulkan: var Vulkan, commandBuffer: VkCommandBuffer, imageIndex: uint32) = | |
482 var beginInfo = VkCommandBufferBeginInfo( | |
483 sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, | |
484 pInheritanceInfo: nil, | |
485 ) | |
486 checkVkResult commandBuffer.vkBeginCommandBuffer(addr(beginInfo)) | |
487 | |
488 var | |
489 clearColor = VkClearValue(color: VkClearColorValue(float32: [0.2'f, 0.2'f, 0.2'f, 1.0'f])) | |
490 renderPassInfo = VkRenderPassBeginInfo( | |
491 sType: VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, | |
492 renderPass: vulkan.pipeline.renderPass, | |
493 framebuffer: vulkan.swapFramebuffers[imageIndex], | |
494 renderArea: VkRect2D( | |
495 offset: VkOffset2D(x: 0, y: 0), | |
496 extent: vulkan.frameDimension, | |
497 ), | |
498 clearValueCount: 1, | |
499 pClearValues: addr(clearColor), | |
500 ) | |
501 commandBuffer.vkCmdBeginRenderPass(addr(renderPassInfo), VK_SUBPASS_CONTENTS_INLINE) | |
502 commandBuffer.vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan.pipeline.pipeline) | |
503 | |
504 commandBuffer.vkCmdSetViewport(firstViewport=0, viewportCount=1, addr(vulkan.viewport)) | |
505 commandBuffer.vkCmdSetScissor(firstScissor=0, scissorCount=1, addr(vulkan.scissor)) | |
506 commandBuffer.vkCmdDraw(vertexCount=3, instanceCount=1, firstVertex=0, firstInstance=0) | |
507 commandBuffer.vkCmdEndRenderPass() | |
508 checkVkResult commandBuffer.vkEndCommandBuffer() | |
509 | |
510 proc drawFrame(vulkan: var Vulkan, currentFrame: int) = | |
511 checkVkResult vulkan.device.vkWaitForFences(1, addr(vulkan.inFlightFences[currentFrame]), VK_TRUE, high(uint64)) | |
512 checkVkResult vulkan.device.vkResetFences(1, addr(vulkan.inFlightFences[currentFrame])) | |
513 var bufferImageIndex: uint32 | |
514 checkVkResult vulkan.device.vkAcquireNextImageKHR( | |
515 vulkan.swapChain, | |
516 high(uint64), | |
517 vulkan.imageAvailableSemaphores[currentFrame], | |
518 VkFence(0), | |
519 addr(bufferImageIndex) | |
520 ) | |
521 | |
522 checkVkResult vkResetCommandBuffer(vulkan.commandBuffers[currentFrame], VkCommandBufferResetFlags(0)) | |
523 recordCommandBuffer(vulkan, vulkan.commandBuffers[currentFrame], bufferImageIndex) | |
524 var | |
525 waitSemaphores = [vulkan.imageAvailableSemaphores[currentFrame]] | |
526 waitStages = [VkPipelineStageFlags(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT)] | |
527 signalSemaphores = [vulkan.renderFinishedSemaphores[currentFrame]] | |
528 submitInfo = VkSubmitInfo( | |
529 sType: VK_STRUCTURE_TYPE_SUBMIT_INFO, | |
530 waitSemaphoreCount: 1, | |
531 pWaitSemaphores: addr(waitSemaphores[0]), | |
532 pWaitDstStageMask: addr(waitStages[0]), | |
533 commandBufferCount: 1, | |
534 pCommandBuffers: addr(vulkan.commandBuffers[currentFrame]), | |
535 signalSemaphoreCount: 1, | |
536 pSignalSemaphores: addr(signalSemaphores[0]), | |
537 ) | |
538 checkVkResult vkQueueSubmit(vulkan.graphicsQueue, 1, addr(submitInfo), vulkan.inFlightFences[currentFrame]) | |
539 | |
540 var presentInfo = VkPresentInfoKHR( | |
541 sType: VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, | |
542 waitSemaphoreCount: 1, | |
543 pWaitSemaphores: addr(signalSemaphores[0]), | |
544 swapchainCount: 1, | |
545 pSwapchains: addr(vulkan.swapChain), | |
546 pImageIndices: addr(bufferImageIndex), | |
547 pResults: nil, | |
548 ) | |
549 checkVkResult vkQueuePresentKHR(vulkan.presentationQueue, addr(presentInfo)) | |
550 | |
551 | |
552 proc fullThrottle*(engine: var Engine) = | |
553 var | |
554 event: XEvent | |
555 killed = false | |
556 currentFrame = 0 | |
557 | |
558 while not killed: | |
559 while engine.display.XPending() > 0 and not killed: | |
560 discard engine.display.XNextEvent(addr(event)) | |
561 case event.theType | |
562 of ClientMessage: | |
563 if cast[Atom](event.xclient.data.l[0]) == deleteMessage: | |
564 killed = true | |
565 of KeyPress: | |
566 let key = XLookupKeysym(cast[PXKeyEvent](addr(event)), 0) | |
567 if key == XK_Escape: | |
568 killed = true | |
569 else: | |
570 discard | |
571 drawFrame(engine.vulkan, currentFrame) | |
572 currentFrame = (currentFrame + 1) mod MAX_FRAMES_IN_FLIGHT; | |
573 checkVkResult engine.vulkan.device.vkDeviceWaitIdle() | |
574 | |
265 | 575 |
266 proc trash*(engine: Engine) = | 576 proc trash*(engine: Engine) = |
267 for shaderStage in engine.pipeline.shaderStages: | 577 for i in 0 ..< MAX_FRAMES_IN_FLIGHT: |
268 vkDestroyShaderModule(engine.vulkan.device, shaderStage.module, nil); | 578 engine.vulkan.device.vkDestroySemaphore(engine.vulkan.imageAvailableSemaphores[i], nil) |
579 engine.vulkan.device.vkDestroySemaphore(engine.vulkan.renderFinishedSemaphores[i], nil) | |
580 engine.vulkan.device.vkDestroyFence(engine.vulkan.inFlightFences[i], nil) | |
581 | |
582 engine.vulkan.device.vkDestroyCommandPool(engine.vulkan.commandPool, nil) | |
583 for framebuffer in engine.vulkan.swapFramebuffers: | |
584 engine.vulkan.device.vkDestroyFramebuffer(framebuffer, nil) | |
585 | |
586 engine.vulkan.device.vkDestroyPipeline(engine.vulkan.pipeline.pipeline, nil) | |
587 engine.vulkan.device.vkDestroyPipelineLayout(engine.vulkan.pipeline.layout, nil) | |
588 engine.vulkan.device.vkDestroyRenderPass(engine.vulkan.pipeline.renderPass, nil) | |
589 | |
590 for shaderStage in engine.vulkan.pipeline.shaderStages: | |
591 engine.vulkan.device.vkDestroyShaderModule(shaderStage.module, nil) | |
592 | |
269 glslang_finalize_process() | 593 glslang_finalize_process() |
270 vkDestroySwapchainKHR(engine.vulkan.device, engine.vulkan.swapChain, nil); | 594 engine.vulkan.device.vkDestroySwapchainKHR(engine.vulkan.swapChain, nil) |
271 vkDestroySurfaceKHR(engine.vulkan.instance, engine.vulkan.surface, nil); | 595 engine.vulkan.instance.vkDestroySurfaceKHR(engine.vulkan.surface, nil) |
272 vkDestroyDevice(engine.vulkan.device, nil) | 596 engine.vulkan.device.vkDestroyDevice(nil) |
273 vkDestroyInstance(engine.vulkan.instance, nil) | 597 engine.vulkan.instance.vkDestroyInstance(nil) |
274 checkXlibResult engine.display.XDestroyWindow(engine.window) | 598 checkXlibResult engine.display.XDestroyWindow(engine.window) |
275 discard engine.display.XCloseDisplay() # always returns 0 | 599 discard engine.display.XCloseDisplay() # always returns 0 |