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