| 0 | 1 import std/enumerate | 
|  | 2 | 
|  | 3 import ./vulkan | 
|  | 4 import ./vulkan_helpers | 
|  | 5 import ./xlib_helpers | 
|  | 6 | 
|  | 7 import ./glslang/glslang | 
|  | 8 | 
| 1 | 9 var vertexShaderCode: string = """#version 450 | 
|  | 10 layout(location = 0) out vec3 fragColor; | 
|  | 11 vec3 colors[3] = vec3[]( | 
|  | 12     vec3(1.0, 0.0, 0.0), | 
|  | 13     vec3(0.0, 1.0, 0.0), | 
|  | 14     vec3(0.0, 0.0, 1.0) | 
|  | 15 ); | 
|  | 16 vec2 positions[3] = vec2[]( | 
|  | 17   vec2(0.0, -0.5), | 
|  | 18   vec2(0.5, 0.5), | 
|  | 19   vec2(-0.5, 0.5) | 
|  | 20 ); | 
|  | 21 void main() { | 
|  | 22   gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); | 
|  | 23   fragColor = colors[gl_VertexIndex]; | 
|  | 24 }""" | 
|  | 25 | 
|  | 26 var fragmentShaderCode: string = """#version 450 | 
|  | 27 layout(location = 0) out vec4 outColor; | 
|  | 28 layout(location = 0) in vec3 fragColor; | 
|  | 29 void main() { | 
|  | 30   outColor = vec4(fragColor, 1.0); | 
|  | 31 }""" | 
| 0 | 32 | 
|  | 33 import | 
|  | 34   x11/xlib, | 
|  | 35   x11/x | 
|  | 36 | 
|  | 37 const VULKAN_VERSION = VK_MAKE_API_VERSION(0'u32, 1'u32, 2'u32, 0'u32) | 
|  | 38 | 
|  | 39 type | 
| 1 | 40   GraphicsPipeline = object | 
|  | 41     shaderStages*: seq[VkPipelineShaderStageCreateInfo] | 
| 0 | 42   QueueFamily = object | 
|  | 43     properties*: VkQueueFamilyProperties | 
|  | 44     hasSurfaceSupport*: bool | 
|  | 45   PhyscialDevice = object | 
|  | 46     device*: VkPhysicalDevice | 
|  | 47     extensions*: seq[string] | 
|  | 48     properties*: VkPhysicalDeviceProperties | 
|  | 49     features*: VkPhysicalDeviceFeatures | 
|  | 50     queueFamilies*: seq[QueueFamily] | 
|  | 51     surfaceCapabilities*: VkSurfaceCapabilitiesKHR | 
|  | 52     surfaceFormats: seq[VkSurfaceFormatKHR] | 
|  | 53     presentModes: seq[VkPresentModeKHR] | 
|  | 54   Vulkan* = object | 
|  | 55     instance*: VkInstance | 
|  | 56     deviceList*: seq[PhyscialDevice] | 
|  | 57     activePhysicalDevice*: PhyscialDevice | 
|  | 58     activeQueueFamily*: uint32 | 
|  | 59     device*: VkDevice | 
|  | 60     presentationQueue*: VkQueue | 
|  | 61     surface*: VkSurfaceKHR | 
|  | 62     selectedSurfaceFormat: VkSurfaceFormatKHR | 
|  | 63     selectedPresentationMode: VkPresentModeKHR | 
|  | 64     selectedExtent: VkExtent2D | 
|  | 65     swapChain: VkSwapchainKHR | 
|  | 66     swapImages: seq[VkImage] | 
|  | 67     swapImageViews: seq[VkImageView] | 
|  | 68   Engine* = object | 
|  | 69     display*: PDisplay | 
|  | 70     window*: x.Window | 
|  | 71     vulkan*: Vulkan | 
| 1 | 72     pipeline*: GraphicsPipeline | 
| 0 | 73 | 
|  | 74 | 
|  | 75 proc getAllPhysicalDevices(instance: VkInstance, surface: VkSurfaceKHR): seq[PhyscialDevice] = | 
|  | 76   for vulkanPhysicalDevice in getVulkanPhysicalDevices(instance): | 
|  | 77     var device = PhyscialDevice(device: vulkanPhysicalDevice, extensions: getDeviceExtensions(vulkanPhysicalDevice)) | 
|  | 78     vkGetPhysicalDeviceProperties(vulkanPhysicalDevice, addr(device.properties)) | 
|  | 79     vkGetPhysicalDeviceFeatures(vulkanPhysicalDevice, addr(device.features)) | 
|  | 80     checkVkResult vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vulkanPhysicalDevice, surface, addr(device.surfaceCapabilities)) | 
|  | 81     device.surfaceFormats = getDeviceSurfaceFormats(vulkanPhysicalDevice, surface) | 
|  | 82     device.presentModes = getDeviceSurfacePresentModes(vulkanPhysicalDevice, surface) | 
|  | 83 | 
|  | 84     for i, queueFamilyProperty in enumerate(getQueueFamilies(vulkanPhysicalDevice)): | 
| 1 | 85       var hasSurfaceSupport: VkBool32 = VK_FALSE | 
| 0 | 86       checkVkResult vkGetPhysicalDeviceSurfaceSupportKHR(vulkanPhysicalDevice, uint32(i), surface, addr(hasSurfaceSupport)) | 
|  | 87       device.queueFamilies.add(QueueFamily(properties: queueFamilyProperty, hasSurfaceSupport: bool(hasSurfaceSupport))) | 
|  | 88 | 
|  | 89     result.add(device) | 
|  | 90 | 
|  | 91 proc filterForDevice(devices: seq[PhyscialDevice]): seq[(PhyscialDevice, uint32)] = | 
|  | 92   for device in devices: | 
|  | 93     if "VK_KHR_swapchain" in device.extensions: | 
|  | 94       for i, queueFamily in enumerate(device.queueFamilies): | 
|  | 95         let hasGraphics = bool(uint32(queueFamily.properties.queueFlags) and ord(VK_QUEUE_GRAPHICS_BIT)) | 
|  | 96         if ( | 
|  | 97           queueFamily.hasSurfaceSupport and | 
|  | 98           hasGraphics and | 
|  | 99           device.surfaceFormats.len > 0 and | 
|  | 100           device.presentModes.len > 0 | 
|  | 101         ): | 
|  | 102           result.add((device, uint32(i))) | 
|  | 103 | 
|  | 104 proc filterForSurfaceFormat(formats: seq[VkSurfaceFormatKHR]): seq[VkSurfaceFormatKHR] = | 
|  | 105   for format in formats: | 
|  | 106     if format.format == VK_FORMAT_B8G8R8A8_SRGB and format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: | 
|  | 107       result.add(format) | 
|  | 108 | 
|  | 109 proc getSwapExtent(display: PDisplay, window: Window, capabilities: VkSurfaceCapabilitiesKHR): VkExtent2D = | 
|  | 110   if capabilities.currentExtent.width != high(uint32): | 
|  | 111     return capabilities.currentExtent | 
|  | 112   else: | 
|  | 113     let (width, height) = xlibFramebufferSize(display, window) | 
|  | 114     return VkExtent2D( | 
|  | 115       width: min(max(uint32(width), capabilities.minImageExtent.width), capabilities.maxImageExtent.width), | 
|  | 116       height: min(max(uint32(height), capabilities.minImageExtent.height), capabilities.maxImageExtent.height), | 
|  | 117     ) | 
|  | 118 | 
|  | 119 proc igniteEngine*(): Engine = | 
|  | 120   vkLoad1_0() | 
|  | 121   vkLoad1_1() | 
|  | 122   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) | 
|  | 129 | 
|  | 130   # create vulkan-X11 surface | 
|  | 131   var surfaceCreateInfo = VkXlibSurfaceCreateInfoKHR( | 
|  | 132     sType: VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, | 
|  | 133     dpy: result.display, | 
|  | 134     window: result.window, | 
|  | 135   ) | 
|  | 136   checkVkResult vkCreateXlibSurfaceKHR(result.vulkan.instance, addr(surfaceCreateInfo), nil, addr(result.vulkan.surface)) | 
|  | 137 | 
|  | 138   # determine device and queue to use and instantiate | 
|  | 139   result.vulkan.deviceList = result.vulkan.instance.getAllPhysicalDevices(result.vulkan.surface) | 
|  | 140   let usableDevices = result.vulkan.deviceList.filterForDevice() | 
|  | 141   if len(usableDevices) == 0: | 
|  | 142     raise newException(Exception, "No suitable graphics device found") | 
|  | 143   (result.vulkan.activePhysicalDevice, result.vulkan.activeQueueFamily) = usableDevices[0] | 
|  | 144 | 
|  | 145   (result.vulkan.device, result.vulkan.presentationQueue) = getVulcanDevice( | 
|  | 146     result.vulkan.activePhysicalDevice.device, | 
|  | 147     result.vulkan.activePhysicalDevice.features, | 
|  | 148     result.vulkan.activeQueueFamily | 
|  | 149   ) | 
|  | 150 | 
|  | 151   # determine surface format for swapchain | 
|  | 152   let usableSurfaceFormats = filterForSurfaceFormat(result.vulkan.activePhysicalDevice.surfaceFormats) | 
|  | 153   if len(usableSurfaceFormats) == 0: | 
|  | 154     raise newException(Exception, "No suitable surface formats found") | 
|  | 155   result.vulkan.selectedSurfaceFormat = usableSurfaceFormats[0] | 
|  | 156   result.vulkan.selectedPresentationMode = getPresentMode(result.vulkan.activePhysicalDevice.presentModes) | 
|  | 157   result.vulkan.selectedExtent = getSwapExtent(result.display, result.window, result.vulkan.activePhysicalDevice.surfaceCapabilities) | 
|  | 158 | 
|  | 159   # setup swapchain | 
|  | 160   var swapchainCreateInfo = VkSwapchainCreateInfoKHR( | 
|  | 161     sType: VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, | 
|  | 162     surface: result.vulkan.surface, | 
|  | 163     minImageCount: max(result.vulkan.activePhysicalDevice.surfaceCapabilities.minImageCount + 1, result.vulkan.activePhysicalDevice.surfaceCapabilities.maxImageCount), | 
|  | 164     imageFormat: result.vulkan.selectedSurfaceFormat.format, | 
|  | 165     imageColorSpace: result.vulkan.selectedSurfaceFormat.colorSpace, | 
|  | 166     imageExtent: result.vulkan.selectedExtent, | 
|  | 167     imageArrayLayers: 1, | 
|  | 168     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?) | 
|  | 170     imageSharingMode: VK_SHARING_MODE_EXCLUSIVE, | 
|  | 171     preTransform: result.vulkan.activePhysicalDevice.surfaceCapabilities.currentTransform, | 
|  | 172     compositeAlpha: VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, | 
|  | 173     presentMode: result.vulkan.selectedPresentationMode, | 
| 1 | 174     clipped: VK_TRUE, | 
| 0 | 175     oldSwapchain: VkSwapchainKHR(0), | 
|  | 176   ) | 
|  | 177   checkVkResult vkCreateSwapchainKHR(result.vulkan.device, addr(swapchainCreateInfo), nil, addr(result.vulkan.swapChain)) | 
|  | 178   result.vulkan.swapImages = getSwapChainImages(result.vulkan.device, result.vulkan.swapChain) | 
|  | 179 | 
|  | 180   # setup swapchian image views | 
|  | 181   result.vulkan.swapImageViews = newSeq[VkImageView](result.vulkan.swapImages.len) | 
|  | 182   for i, image in enumerate(result.vulkan.swapImages): | 
|  | 183     var imageViewCreateInfo = VkImageViewCreateInfo( | 
|  | 184       sType: VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, | 
|  | 185       image: image, | 
|  | 186       viewType: VK_IMAGE_VIEW_TYPE_2D, | 
|  | 187       format: result.vulkan.selectedSurfaceFormat.format, | 
|  | 188       components: VkComponentMapping( | 
|  | 189         r: VK_COMPONENT_SWIZZLE_IDENTITY, | 
|  | 190         g: VK_COMPONENT_SWIZZLE_IDENTITY, | 
|  | 191         b: VK_COMPONENT_SWIZZLE_IDENTITY, | 
|  | 192         a: VK_COMPONENT_SWIZZLE_IDENTITY, | 
|  | 193       ), | 
|  | 194       subresourceRange: VkImageSubresourceRange( | 
|  | 195         aspectMask: VkImageAspectFlags(VK_IMAGE_ASPECT_COLOR_BIT), | 
|  | 196         baseMipLevel: 0, | 
|  | 197         levelCount: 1, | 
|  | 198         baseArrayLayer: 0, | 
|  | 199         layerCount: 1, | 
|  | 200       ), | 
|  | 201     ) | 
|  | 202     checkVkResult vkCreateImageView(result.vulkan.device, addr(imageViewCreateInfo), nil, addr(result.vulkan.swapImageViews[i])) | 
| 1 | 203 | 
|  | 204   # init shader system | 
|  | 205   checkGlslangResult glslang_initialize_process() | 
|  | 206 | 
|  | 207   # load shaders | 
|  | 208   result.pipeline.shaderStages.add(createShaderStage(result.vulkan.device, VK_SHADER_STAGE_VERTEX_BIT, vertexShaderCode)) | 
|  | 209   result.pipeline.shaderStages.add(createShaderStage(result.vulkan.device, VK_SHADER_STAGE_FRAGMENT_BIT, fragmentShaderCode)) | 
|  | 210 | 
|  | 211   # create graphis pipeline | 
|  | 212   var dynamicStates = [VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR] | 
|  | 213   var dynamicState = VkPipelineDynamicStateCreateInfo( | 
|  | 214     sType: VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, | 
|  | 215     dynamicStateCount: uint32(dynamicStates.len), | 
|  | 216     pDynamicStates: addr(dynamicStates[0]), | 
|  | 217   ) | 
|  | 218   var vertexInputInfo = VkPipelineVertexInputStateCreateInfo( | 
|  | 219     sType: VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, | 
|  | 220     vertexBindingDescriptionCount: 0, | 
|  | 221     pVertexBindingDescriptions: nil, | 
|  | 222     vertexAttributeDescriptionCount: 0, | 
|  | 223     pVertexAttributeDescriptions: nil, | 
|  | 224   ) | 
|  | 225   var inputAssembly = VkPipelineInputAssemblyStateCreateInfo( | 
|  | 226     sType: VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, | 
|  | 227     topology: VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, | 
|  | 228     primitiveRestartEnable: VK_FALSE, | 
|  | 229   ) | 
|  | 230 | 
|  | 231   # setup viewport | 
|  | 232   var viewport = VkViewport( | 
|  | 233     x: 0.0, | 
|  | 234     y: 0.0, | 
|  | 235     width: (float) result.vulkan.selectedExtent.width, | 
|  | 236     height: (float) result.vulkan.selectedExtent.height, | 
|  | 237     minDepth: 0.0, | 
|  | 238     maxDepth: 1.0, | 
|  | 239   ) | 
|  | 240   var scissor = VkRect2D( | 
|  | 241     offset: VkOffset2D(x: 0, y: 0), | 
|  | 242     extent: result.vulkan.selectedExtent | 
|  | 243   ) | 
| 0 | 244 | 
|  | 245 | 
|  | 246 proc fullThrottle*(engine: Engine) = | 
|  | 247   var event: XEvent | 
|  | 248   while true: | 
|  | 249     discard XNextEvent(engine.display, addr(event)) | 
|  | 250     case event.theType | 
|  | 251     of Expose: | 
|  | 252       discard | 
|  | 253     of ClientMessage: | 
|  | 254       if cast[Atom](event.xclient.data.l[0]) == deleteMessage: | 
|  | 255         break | 
|  | 256     of KeyPress: | 
|  | 257       let key = XLookupKeysym(cast[PXKeyEvent](addr(event)), 0) | 
|  | 258       if key != 0: | 
|  | 259         echo "Key ", key, " pressed" | 
|  | 260     of ButtonPressMask: | 
|  | 261       echo "Mouse button ", event.xbutton.button, " pressed at ", | 
|  | 262           event.xbutton.x, ",", event.xbutton.y | 
|  | 263     else: | 
|  | 264       discard | 
|  | 265 | 
|  | 266 proc trash*(engine: Engine) = | 
| 1 | 267   for shaderStage in engine.pipeline.shaderStages: | 
|  | 268     vkDestroyShaderModule(engine.vulkan.device, shaderStage.module, nil); | 
|  | 269   glslang_finalize_process() | 
| 0 | 270   vkDestroySwapchainKHR(engine.vulkan.device, engine.vulkan.swapChain, nil); | 
|  | 271   vkDestroySurfaceKHR(engine.vulkan.instance, engine.vulkan.surface, nil); | 
|  | 272   vkDestroyDevice(engine.vulkan.device, nil) | 
|  | 273   vkDestroyInstance(engine.vulkan.instance, nil) | 
|  | 274   checkXlibResult engine.display.XDestroyWindow(engine.window) | 
|  | 275   discard engine.display.XCloseDisplay() # always returns 0 |