├── .editorconfig ├── .gitignore ├── LICENSE ├── README.md ├── src └── vulkan.nim ├── tests ├── config.nims ├── shaders │ ├── frag.spv │ ├── shader.frag │ ├── shader.vert │ └── vert.spv ├── test.nim └── triangle.nim ├── tools ├── generator.nim └── utils.nim └── vulkan.nimble /.editorconfig: -------------------------------------------------------------------------------- 1 | # find your plugin here: http://editorconfig.org/ 2 | # always follow this rules 3 | 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### C ### 2 | # Prerequisites 3 | *.d 4 | 5 | # Object files 6 | *.o 7 | *.ko 8 | *.obj 9 | *.elf 10 | 11 | # Linker output 12 | *.ilk 13 | *.map 14 | *.exp 15 | 16 | # Precompiled Headers 17 | *.gch 18 | *.pch 19 | 20 | # Executables 21 | *.exe 22 | *.out 23 | *.app 24 | *.i*86 25 | *.x86_64 26 | *.hex 27 | 28 | # Debug files 29 | *.dSYM/ 30 | *.su 31 | *.idb 32 | *.pdb 33 | 34 | # Kernel Module Compile Results 35 | *.mod* 36 | *.cmd 37 | .tmp_versions/ 38 | modules.order 39 | Module.symvers 40 | Mkfile.old 41 | dkms.conf 42 | 43 | ### C++ ### 44 | # Prerequisites 45 | 46 | # Compiled Object files 47 | *.slo 48 | 49 | # Precompiled Headers 50 | 51 | # Compiled Dynamic libraries 52 | 53 | # Fortran module files 54 | *.mod 55 | *.smod 56 | 57 | # Compiled Static libraries 58 | *.lai 59 | 60 | # Executables 61 | 62 | ### Linux ### 63 | *~ 64 | 65 | # temporary files which can be created if a process still has a handle open of a deleted file 66 | .fuse_hidden* 67 | 68 | # KDE directory preferences 69 | .directory 70 | 71 | # Linux trash folder which might appear on any partition or disk 72 | .Trash-* 73 | 74 | # .nfs files are created when an open file is removed but is still being accessed 75 | .nfs* 76 | 77 | ### macOS ### 78 | *.DS_Store 79 | .AppleDouble 80 | .LSOverride 81 | 82 | # Icon must end with two \r 83 | Icon 84 | 85 | # Thumbnails 86 | ._* 87 | 88 | # Files that might appear in the root of a volume 89 | .DocumentRevisions-V100 90 | .fseventsd 91 | .Spotlight-V100 92 | .TemporaryItems 93 | .Trashes 94 | .VolumeIcon.icns 95 | .com.apple.timemachine.donotpresent 96 | 97 | # Directories potentially created on remote AFP share 98 | .AppleDB 99 | .AppleDesktop 100 | Network Trash Folder 101 | Temporary Items 102 | .apdisk 103 | 104 | ### Nim ### 105 | nimcache/ 106 | 107 | ### OSX ### 108 | 109 | # Icon must end with two \r 110 | 111 | # Thumbnails 112 | 113 | # Files that might appear in the root of a volume 114 | 115 | # Directories potentially created on remote AFP share 116 | 117 | ### VisualStudioCode ### 118 | .vscode/* 119 | !.vscode/settings.json 120 | !.vscode/tasks.json 121 | !.vscode/launch.json 122 | !.vscode/extensions.json 123 | .history 124 | 125 | ### Windows ### 126 | # Windows thumbnail cache files 127 | Thumbs.db 128 | ehthumbs.db 129 | ehthumbs_vista.db 130 | 131 | # Folder config file 132 | Desktop.ini 133 | 134 | # Recycle Bin used on file shares 135 | $RECYCLE.BIN/ 136 | 137 | # Binaries 138 | [Bb]in/ 139 | 140 | # Windows Installer files 141 | *.cab 142 | *.msi 143 | *.msm 144 | *.msp 145 | 146 | # Windows shortcuts 147 | *.lnk 148 | 149 | # Dynamic Libraries 150 | *.dll 151 | *.so 152 | *.dylib 153 | 154 | ### NimGL ### 155 | 156 | # Tests 157 | tests/general 158 | tests/tgeneral 159 | tests/tglfw 160 | tests/tmath 161 | tests/topengl 162 | tests/test 163 | tests/triangle 164 | tools/generator 165 | imgui.ini 166 | docs/ 167 | 168 | # End of https://www.gitignore.io/api/c,osx,nim,c++,linux,macos,windows,visualstudiocode 169 | 170 | vk.xml 171 | vk_layer_settings.txt 172 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Leonardo Mariscal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![docs](https://img.shields.io/badge/docs-passing-4caf50.svg?style=flat-square)](https://nimgl.dev/docs) 2 | 3 | # Vulkan Bindings for Nim [![Nimble](https://raw.githubusercontent.com/yglukhov/nimble-tag/master/nimble.png)](https://github.com/nim-lang/nimble) 4 | 5 | Separated module from [NimGL](https://nimgl.dev/). In order to mantain small 6 | sized modules to facilitate development and reduce number of required modules. 7 | 8 | Some developers do not require the entire bundle of APIs that 9 | [NimGL](https://nimgl.dev/) offer, and so, prefere to only download the required 10 | modules. 11 | 12 | ## Installation 13 | 14 | ### Nimble download 15 | 16 | You can install this package through the official package manager of Nim. 17 | 18 | ```bash 19 | $ nimble install https://github.com/nimgl/vulkan.git 20 | ``` 21 | 22 | In order to respect already existing libraries in the package registry, and 23 | because [NimGL](https://nimgl.dev/) already exists in there, this package is 24 | only able to be accesible by direct git url. 25 | 26 | ### Nimble direct install 27 | 28 | ```bash 29 | $ git clone --recursive -j8 https://github.com/nimgl/vulkan.git 30 | $ cd vulkan 31 | $ nimble install 32 | ``` 33 | 34 | ### NimGL module 35 | 36 | ```bash 37 | $ nimble install nimgl 38 | ``` 39 | 40 | You can find more information in the [main repo](https://github.com/nimgl/nimgl). 41 | 42 | ### Development 43 | 44 | It is currently being developed and tested on 45 | 46 | * Windows 10 47 | * MacOS Mojave 48 | * Linux Ubuntu 18.10 49 | 50 | ## Contribute 51 | 52 | I'm only one person and I use this library almost daily for school and personal 53 | projects. If you are missing some extension, procedures, bindings or anything 54 | related feel free to PR them or open an Issue with the specification and 55 | if you can some examples to have an idea on how to implement it. 56 | Thank you so much :tada: 57 | 58 | ### This being a separate module behaves slightly diferently. 59 | 60 | Please open all issues in the [main repository](https://github.com/nimgl/nimgl). 61 | The PRs and new feature development will occur in each binding's repo. 62 | 63 | ## Usage 64 | 65 | ```nim 66 | import vulkan 67 | 68 | # Create window (GLFW Recommended) 69 | 70 | if vkInit(): 71 | echo "Vulkan started successfully!" 72 | 73 | var extensionCount: uint32 74 | discard vkEnumerateInstanceExtensionProperties(nil, extensionCount.addr, nil) 75 | 76 | echo $extensionCount & " extensions supported" 77 | 78 | # You can load extensions 79 | loadVK_EXT_host_query_reset() 80 | if vkResetQueryPoolEXT == nil: 81 | echo "Failed to load VK_EXT_host_query_reset extension" 82 | ``` 83 | 84 | Check out the references and doc in order to understand Vulkan usage. 85 | 86 | ## License 87 | 88 | [MIT License](https://github.com/nimgl/nimgl/blob/master/LICENSE) 89 | 90 | NimGL is open source and is under the MIT License, we highly encourage every 91 | developer that uses it to make improvements and fork them here. 92 | 93 | ## Contributors 94 | 95 | Thank you to every contributor that has spent their time improving this library. 96 | 97 | [List of all contributors.](https://github.com/nimgl/nimgl/graphs/contributors) 98 | -------------------------------------------------------------------------------- /tests/config.nims: -------------------------------------------------------------------------------- 1 | switch("path", "$projectDir/../src") 2 | switch("d", "vulkan") 3 | -------------------------------------------------------------------------------- /tests/shaders/frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimgl/vulkan/091ff99e6542f613aadb83c4aad7948299088d93/tests/shaders/frag.spv -------------------------------------------------------------------------------- /tests/shaders/shader.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(location = 0) in vec3 fragColor; 5 | 6 | layout(location = 0) out vec4 outColor; 7 | 8 | void main() { 9 | outColor = vec4(fragColor, 1.0); 10 | } 11 | -------------------------------------------------------------------------------- /tests/shaders/shader.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(location = 0) out vec3 fragColor; 5 | 6 | vec2 positions[3] = vec2[]( 7 | vec2(0.0, -0.5), 8 | vec2(0.5, 0.5), 9 | vec2(-0.5, 0.5) 10 | ); 11 | 12 | vec3 colors[3] = vec3[]( 13 | vec3(1.0, 0.0, 0.0), 14 | vec3(0.0, 1.0, 0.0), 15 | vec3(0.0, 0.0, 1.0) 16 | ); 17 | 18 | void main() { 19 | gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); 20 | fragColor = colors[gl_VertexIndex]; 21 | } 22 | -------------------------------------------------------------------------------- /tests/shaders/vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimgl/vulkan/091ff99e6542f613aadb83c4aad7948299088d93/tests/shaders/vert.spv -------------------------------------------------------------------------------- /tests/test.nim: -------------------------------------------------------------------------------- 1 | import nimgl/glfw 2 | from nimgl/vulkan import nil 3 | from triangle import nil 4 | 5 | proc keyCallback(window: GLFWWindow, key: int32, scancode: int32, action: int32, mods: int32) {.cdecl.} = 6 | if action == GLFW_PRESS and key == GLFWKey.Escape: 7 | window.setWindowShouldClose(true) 8 | 9 | if isMainModule: 10 | doAssert glfwInit() 11 | 12 | glfwWindowHint(GLFWClientApi, GLFWNoApi) 13 | glfwWindowHint(GLFWResizable, GLFWFalse) 14 | 15 | var w = glfwCreateWindow(triangle.WIDTH, triangle.HEIGHT, "Vulkan Triangle") 16 | if w == nil: 17 | quit(-1) 18 | 19 | discard w.setKeyCallback(keyCallback) 20 | 21 | proc createSurface(instance: vulkan.VkInstance): vulkan.VkSurfaceKHR = 22 | if glfwCreateWindowSurface(instance, w, nil, result.addr) != vulkan.VKSuccess: 23 | quit("failed to create surface") 24 | 25 | var glfwExtensionCount: uint32 = 0 26 | var glfwExtensions: cstringArray 27 | glfwExtensions = glfwGetRequiredInstanceExtensions(glfwExtensionCount.addr) 28 | triangle.init(glfwExtensions, glfwExtensionCount, createSurface) 29 | 30 | while not w.windowShouldClose(): 31 | glfwPollEvents() 32 | triangle.tick() 33 | 34 | triangle.deinit() 35 | w.destroyWindow() 36 | glfwTerminate() 37 | -------------------------------------------------------------------------------- /tests/triangle.nim: -------------------------------------------------------------------------------- 1 | import nimgl/vulkan 2 | import sets 3 | import bitops 4 | 5 | type 6 | CreateSurfaceProc = proc (instance: VkInstance): VkSurfaceKHR 7 | QueueFamilyIndices = object 8 | graphicsFamily: uint32 9 | graphicsFamilyFound: bool 10 | presentFamily: uint32 11 | presentFamilyFound: bool 12 | SwapChain = object 13 | swapChain: VkSwapchainKHR 14 | swapChainImages: seq[VkImage] 15 | swapChainImageFormat: VkFormat 16 | swapChainExtent: VkExtent2D 17 | SwapChainSupportDetails = object 18 | capabilities: VkSurfaceCapabilitiesKHR 19 | formats: seq[VkSurfaceFormatKHR] 20 | presentModes: seq[VkPresentModeKHR] 21 | GraphicsPipeline = object 22 | pipelineLayout: VkPipelineLayout 23 | pipeline: VkPipeline 24 | Semaphores = object 25 | imageAvailable: VkSemaphore 26 | renderFinished: VkSemaphore 27 | 28 | const 29 | validationLayers = ["VK_LAYER_LUNARG_standard_validation"] 30 | deviceExtensions = ["VK_KHR_swapchain"] 31 | WIDTH* = 800 32 | HEIGHT* = 600 33 | VK_NULL_HANDLE = 0 34 | 35 | loadVK_KHR_surface() 36 | loadVK_KHR_swapchain() 37 | 38 | proc checkValidationLayers() = 39 | var layerCount: uint32 = 0 40 | discard vkEnumerateInstanceLayerProperties(layerCount.addr, nil) 41 | var layers = newSeq[VkLayerProperties](layerCount) 42 | discard vkEnumerateInstanceLayerProperties(layerCount.addr, layers[0].addr) 43 | 44 | for validate in validationLayers: 45 | var found = false 46 | for layer in layers: 47 | if cstring(layer.layerName.unsafeAddr) == validate: 48 | found = true 49 | break 50 | if not found: 51 | echo validate & " layer is not supported" 52 | 53 | proc isComplete(indices: QueueFamilyIndices): bool = 54 | indices.graphicsFamilyFound and indices.presentFamilyFound 55 | 56 | proc findQueueFamilies(pDevice: VkPhysicalDevice, surface: VkSurfaceKHR): QueueFamilyIndices = 57 | result.graphicsFamilyFound = false 58 | 59 | var queueFamilyCount: uint32 = 0 60 | vkGetPhysicalDeviceQueueFamilyProperties(pDevice, queueFamilyCount.addr, nil) 61 | var queueFamilies = newSeq[VkQueueFamilyProperties](queueFamilyCount) 62 | vkGetPhysicalDeviceQueueFamilyProperties(pDevice, queueFamilyCount.addr, queueFamilies[0].addr) 63 | 64 | var index: uint32 = 0 65 | for queueFamily in queueFamilies: 66 | if (queueFamily.queueFlags.uint32 and VkQueueGraphicsBit.uint32) > 0'u32: 67 | result.graphicsFamily = index 68 | result.graphicsFamilyFound = true 69 | var presentSupport: VkBool32 70 | discard vkGetPhysicalDeviceSurfaceSupportKHR(pDevice, index, surface, presentSupport.addr) 71 | if presentSupport.ord == 1: 72 | result.presentFamily = index 73 | result.presentFamilyFound = true 74 | if result.isComplete: 75 | break 76 | index.inc 77 | 78 | proc createLogicalDevice(physicalDevice: VkPhysicalDevice, surface: VkSurfaceKHR, graphicsQueue: var VkQueue, presentQueue: var VkQueue): VkDevice = 79 | let 80 | indices = findQueueFamilies(physicalDevice, surface) 81 | uniqueQueueFamilies = [indices.graphicsFamily, indices.presentFamily].toHashSet 82 | var 83 | queuePriority = 1f 84 | queueCreateInfos = newSeq[VkDeviceQueueCreateInfo]() 85 | 86 | for queueFamily in uniqueQueueFamilies: 87 | let deviceQueueCreateInfo = newVkDeviceQueueCreateInfo( 88 | sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, 89 | queueFamilyIndex = queueFamily, 90 | queueCount = 1, 91 | pQueuePriorities = queuePriority.addr 92 | ) 93 | queueCreateInfos.add(deviceQueueCreateInfo) 94 | 95 | var 96 | deviceFeatures = newSeq[VkPhysicalDeviceFeatures](1) 97 | deviceExts = allocCStringArray(deviceExtensions) 98 | deviceCreateInfo = newVkDeviceCreateInfo( 99 | pQueueCreateInfos = queueCreateInfos[0].addr, 100 | queueCreateInfoCount = queueCreateInfos.len.uint32, 101 | pEnabledFeatures = deviceFeatures[0].addr, 102 | enabledExtensionCount = deviceExtensions.len.uint32, 103 | enabledLayerCount = 0, 104 | ppEnabledLayerNames = nil, 105 | ppEnabledExtensionNames = deviceExts 106 | ) 107 | 108 | if vkCreateDevice(physicalDevice, deviceCreateInfo.addr, nil, result.addr) != VKSuccess: 109 | echo "failed to create logical device" 110 | 111 | deallocCStringArray(deviceExts) 112 | 113 | vkGetDeviceQueue(result, indices.graphicsFamily, 0, graphicsQueue.addr) 114 | vkGetDeviceQueue(result, indices.presentFamily, 0, presentQueue.addr) 115 | 116 | proc checkDeviceExtensionSupport(pDevice: VkPhysicalDevice): bool = 117 | var extCount: uint32 118 | discard vkEnumerateDeviceExtensionProperties(pDevice, nil, extCount.addr, nil) 119 | var availableExts = newSeq[VkExtensionProperties](extCount) 120 | discard vkEnumerateDeviceExtensionProperties(pDevice, nil, extCount.addr, availableExts[0].addr) 121 | 122 | var requiredExts = deviceExtensions.toHashSet 123 | for ext in availableExts.mitems: 124 | requiredExts.excl($ ext.extensionName.addr) 125 | requiredExts.len == 0 126 | 127 | proc querySwapChainSupport(pDevice: VkPhysicalDevice, surface: VkSurfaceKHR): SwapChainSupportDetails = 128 | discard vkGetPhysicalDeviceSurfaceCapabilitiesKHR(pDevice, surface, result.capabilities.addr) 129 | var formatCount: uint32 130 | discard vkGetPhysicalDeviceSurfaceFormatsKHR(pDevice, surface, formatCount.addr, nil) 131 | if formatCount != 0: 132 | result.formats.setLen(formatCount) 133 | discard vkGetPhysicalDeviceSurfaceFormatsKHR(pDevice, surface, formatCount.addr, result.formats[0].addr) 134 | var presentModeCount: uint32 135 | discard vkGetPhysicalDeviceSurfacePresentModesKHR(pDevice, surface, presentModeCount.addr, nil) 136 | if presentModeCount != 0: 137 | result.presentModes.setLen(presentModeCount) 138 | discard vkGetPhysicalDeviceSurfacePresentModesKHR(pDevice, surface, presentModeCount.addr, result.presentModes[0].addr) 139 | 140 | proc isDeviceSuitable(pDevice: VkPhysicalDevice, surface: VkSurfaceKHR): bool = 141 | var deviceProperties: VkPhysicalDeviceProperties 142 | vkGetPhysicalDeviceProperties(pDevice, deviceProperties.addr) 143 | 144 | #if deviceProperties.deviceType != VkPhysicalDeviceTypeDiscreteGPU: 145 | # return false 146 | 147 | let extsSupported = pDevice.checkDeviceExtensionSupport 148 | 149 | var swapChainAdequate = false 150 | if extsSupported: 151 | let swapChainSupport = querySwapChainSupport(pDevice, surface) 152 | swapChainAdequate = 153 | swapChainSupport.formats.len != 0 and 154 | swapChainSupport.presentModes.len != 0 155 | 156 | let indices: QueueFamilyIndices = findQueueFamilies(pDevice, surface) 157 | return indices.isComplete and extsSupported and swapChainAdequate 158 | 159 | proc createInstance(glfwExtensions: cstringArray, glfwExtensionCount: uint32): VkInstance = 160 | var appInfo = newVkApplicationInfo( 161 | pApplicationName = "NimGL Vulkan Example", 162 | applicationVersion = vkMakeVersion(1, 0, 0), 163 | pEngineName = "No Engine", 164 | engineVersion = vkMakeVersion(1, 0, 0), 165 | apiVersion = vkApiVersion1_1 166 | ) 167 | 168 | var instanceCreateInfo = newVkInstanceCreateInfo( 169 | pApplicationInfo = appInfo.addr, 170 | enabledExtensionCount = glfwExtensionCount, 171 | ppEnabledExtensionNames = glfwExtensions, 172 | enabledLayerCount = 0, 173 | ppEnabledLayerNames = nil, 174 | ) 175 | 176 | if vkCreateInstance(instanceCreateInfo.addr, nil, result.addr) != VKSuccess: 177 | quit("failed to create instance") 178 | 179 | var extensionCount: uint32 = 0 180 | discard vkEnumerateInstanceExtensionProperties(nil, extensionCount.addr, nil) 181 | var extensions = newSeq[VkExtensionProperties](extensionCount) 182 | discard vkEnumerateInstanceExtensionProperties(nil, extensionCount.addr, extensions[0].addr) 183 | 184 | # disabled for now 185 | #checkValidationLayers() 186 | 187 | proc pickPhysicalDevice(instance: VkInstance, surface: VkSurfaceKHR): VkPhysicalDevice = 188 | var deviceCount: uint32 = 0 189 | discard vkEnumeratePhysicalDevices(instance, deviceCount.addr, nil) 190 | var devices = newSeq[VkPhysicalDevice](deviceCount) 191 | discard vkEnumeratePhysicalDevices(instance, deviceCount.addr, devices[0].addr) 192 | 193 | for pDevice in devices: 194 | if isDeviceSuitable(pDevice, surface): 195 | return pDevice 196 | 197 | raise newException(Exception, "Suitable physical device not found") 198 | 199 | proc chooseSwapSurfaceFormat(availableFormats: seq[VkSurfaceFormatKHR]): VkSurfaceFormatKHR = 200 | for availableFormat in availableFormats: 201 | if availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB and 202 | availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: 203 | return availableFormat 204 | availableFormats[0] 205 | 206 | proc chooseSwapPresentMode(availablePresentModes: seq[VkPresentModeKHR]): VkPresentModeKHR = 207 | for availablePresentMode in availablePresentModes: 208 | if availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR: 209 | return availablePresentMode 210 | VK_PRESENT_MODE_FIFO_KHR 211 | 212 | proc chooseSwapExtent(capabilities: VkSurfaceCapabilitiesKHR): VkExtent2D = 213 | if capabilities.currentExtent.width != uint32.high: 214 | return capabilities.currentExtent 215 | else: 216 | result = VkExtent2D(width: WIDTH, height: HEIGHT) 217 | result.width = 218 | max( 219 | capabilities.minImageExtent.width, 220 | min(capabilities.maxImageExtent.width, result.width) 221 | ) 222 | result.height = 223 | max( 224 | capabilities.minImageExtent.height, 225 | min(capabilities.maxImageExtent.height, result.height) 226 | ) 227 | 228 | proc createSwapChain(device: VkDevice, physicalDevice: VkPhysicalDevice, surface: VkSurfaceKHR): SwapChain = 229 | let 230 | swapChainSupport = querySwapChainSupport(physicalDevice, surface) 231 | surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats) 232 | presentMode = chooseSwapPresentMode(swapChainSupport.presentModes) 233 | extent = chooseSwapExtent(swapChainSupport.capabilities) 234 | var imageCount = swapChainSupport.capabilities.minImageCount + 1 235 | if swapChainSupport.capabilities.maxImageCount > 0 and 236 | imageCount > swapChainSupport.capabilities.maxImageCount: 237 | imageCount = swapChainSupport.capabilities.maxImageCount 238 | var createInfo = VkSwapchainCreateInfoKHR( 239 | sType: cast[VkStructureType](1000001000), # VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR 240 | surface: surface, 241 | minImageCount: imageCount, 242 | imageFormat: surfaceFormat.format, 243 | imageColorSpace: surfaceFormat.colorSpace, 244 | imageExtent: extent, 245 | imageArrayLayers: 1, 246 | imageUsage: VkImageUsageFlags(0x00000010), # VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT 247 | ) 248 | let indices = findQueueFamilies(physicalDevice, surface) 249 | var queueFamilyIndices = [indices.graphicsFamily, indices.presentFamily] 250 | if indices.graphicsFamily != indices.presentFamily: 251 | createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT 252 | createInfo.queueFamilyIndexCount = queueFamilyIndices.len.uint32 253 | createInfo.pQueueFamilyIndices = queueFamilyIndices[0].addr 254 | else: 255 | createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE 256 | createInfo.queueFamilyIndexCount = 0 # optional 257 | createInfo.pQueueFamilyIndices = nil # optional 258 | createInfo.preTransform = swapChainSupport.capabilities.currentTransform 259 | createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR 260 | createInfo.presentMode = presentMode 261 | createInfo.clipped = VkBool32(VK_TRUE) 262 | createInfo.oldSwapChain = VkSwapchainKHR(0) 263 | if vkCreateSwapChainKHR(device, createInfo.addr, nil, result.swapChain.addr) != VK_SUCCESS: 264 | quit("failed to create swap chain") 265 | discard vkGetSwapchainImagesKHR(device, result.swapChain, imageCount.addr, nil) 266 | result.swapChainImages.setLen(imageCount) 267 | discard vkGetSwapchainImagesKHR(device, result.swapChain, imageCount.addr, result.swapChainImages[0].addr) 268 | result.swapChainImageFormat = surfaceFormat.format 269 | result.swapChainExtent = extent 270 | 271 | proc createImageViews(device: VkDevice, swapChainImages: seq[VkImage], swapChainImageFormat: VkFormat): seq[VkImageView] = 272 | result.setLen(swapChainImages.len) 273 | for i in 0 ..< swapChainImages.len: 274 | var createInfo = VkImageViewCreateInfo( 275 | sType: VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, 276 | image: swapChainImages[i], 277 | viewType: VK_IMAGE_VIEW_TYPE_2D, 278 | format: swapChainImageFormat, 279 | components: VkComponentMapping( 280 | r: VK_COMPONENT_SWIZZLE_IDENTITY, 281 | g: VK_COMPONENT_SWIZZLE_IDENTITY, 282 | b: VK_COMPONENT_SWIZZLE_IDENTITY, 283 | a: VK_COMPONENT_SWIZZLE_IDENTITY, 284 | ), 285 | subresourceRange: VkImageSubresourceRange( 286 | aspectMask: VkImageAspectFlags(VK_IMAGE_ASPECT_COLOR_BIT), 287 | baseMipLevel: 0, 288 | levelCount: 1, 289 | baseArrayLayer: 0, 290 | layerCount: 1, 291 | ), 292 | ) 293 | if vkCreateImageView(device, createInfo.addr, nil, result[i].addr) != VK_SUCCESS: 294 | quit("failed to create image view") 295 | 296 | proc createShaderModule(device: VkDevice, code: string): VkShaderModule = 297 | var createInfo = VkShaderModuleCreateInfo( 298 | sType: VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, 299 | codeSize: code.len.uint32, 300 | pCode: cast[ptr uint32](code[0].unsafeAddr) 301 | ) 302 | if vkCreateShaderModule(device, createInfo.addr, nil, result.addr) != VK_SUCCESS: 303 | quit("failed to create shader module") 304 | 305 | proc createGraphicsPipeline(device: VkDevice, swapChainExtent: VkExtent2D, renderPass: VkRenderPass): GraphicsPipeline = 306 | const 307 | vertShaderCode = staticRead("shaders/vert.spv") 308 | fragShaderCode = staticRead("shaders/frag.spv") 309 | var 310 | vertShaderModule = createShaderModule(device, vertShaderCode) 311 | fragShaderModule = createShaderModule(device, fragShaderCode) 312 | vertShaderStageInfo = VkPipelineShaderStageCreateInfo( 313 | sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, 314 | stage: VK_SHADER_STAGE_VERTEX_BIT, 315 | module: vertShaderModule, 316 | pName: "main", 317 | ) 318 | fragShaderStageInfo = VkPipelineShaderStageCreateInfo( 319 | sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, 320 | stage: VkShaderStageFlagBits(0x00000010), # VK_SHADER_STAGE_FRAGMENT_BIT 321 | module: fragShaderModule, 322 | pName: "main", 323 | ) 324 | shaderStages = [vertShaderStageInfo, fragShaderStageInfo] 325 | vertexInputInfo = VkPipelineVertexInputStateCreateInfo( 326 | sType: VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, 327 | vertexBindingDescriptionCount: 0, 328 | pVertexBindingDescriptions: nil, # optional 329 | vertexAttributeDescriptionCount: 0, 330 | pVertexAttributeDescriptions: nil, # optional 331 | ) 332 | inputAssembly = VkPipelineInputAssemblyStateCreateInfo( 333 | sType: VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, 334 | topology: VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 335 | primitiveRestartEnable: VkBool32(VK_FALSE), 336 | ) 337 | viewport = VkViewport( 338 | x: 0f, 339 | y: 0f, 340 | width: swapChainExtent.width.float, 341 | height: swapChainExtent.height.float, 342 | minDepth: 0f, 343 | maxDepth: 1f, 344 | ) 345 | scissor = VkRect2D( 346 | offset: VkOffset2D(x: 0, y: 0), 347 | extent: swapChainExtent, 348 | ) 349 | viewportState = VkPipelineViewportStateCreateInfo( 350 | sType: VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, 351 | viewportCount: 1, 352 | pViewports: viewport.addr, 353 | scissorCount: 1, 354 | pScissors: scissor.addr, 355 | ) 356 | rasterizer = VkPipelineRasterizationStateCreateInfo( 357 | sType: VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, 358 | depthClampEnable: VkBool32(VK_FALSE), 359 | rasterizerDiscardEnable: VkBool32(VK_FALSE), 360 | polygonMode: VK_POLYGON_MODE_FILL, 361 | lineWidth: 1f, 362 | cullMode: VkCullModeFlags(0x00000002), # VK_CULL_MODE_BACK_BIT 363 | frontFace: VK_FRONT_FACE_CLOCKWISE, 364 | depthBiasEnable: VkBool32(VK_FALSE), 365 | depthBiasConstantFactor: 0f, # optional 366 | depthBiasClamp: 0f, # optional 367 | depthBiasSlopeFactor: 0f, # optional 368 | ) 369 | multisampling = VkPipelineMultisampleStateCreateInfo( 370 | sType: VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, 371 | sampleShadingEnable: VkBool32(VK_FALSE), 372 | rasterizationSamples: VK_SAMPLE_COUNT_1_BIT, 373 | minSampleShading: 1f, # optional 374 | pSampleMask: nil, # optional 375 | alphaToCoverageEnable: VkBool32(VK_FALSE), # optional 376 | alphaToOneEnable: VkBool32(VK_FALSE), # optional 377 | ) 378 | colorBlendAttachment = VkPipelineColorBlendAttachmentState( 379 | colorWriteMask: VkColorComponentFlags( 380 | bitor( 381 | 0x00000001, # VK_COLOR_COMPONENT_R_BIT 382 | bitor( 383 | 0x00000002, # VK_COLOR_COMPONENT_G_BIT 384 | bitor( 385 | 0x00000004, # VK_COLOR_COMPONENT_B_BIT 386 | 0x00000008, # VK_COLOR_COMPONENT_A_BIT 387 | ))) 388 | ), 389 | blendEnable: VkBool32(VK_FALSE), 390 | srcColorBlendFactor: VK_BLEND_FACTOR_ONE, # optional 391 | dstColorBlendFactor: VK_BLEND_FACTOR_ZERO, # optional 392 | colorBlendOp: VK_BLEND_OP_ADD, # optional 393 | srcAlphaBlendFactor: VK_BLEND_FACTOR_ONE, # optional 394 | dstAlphaBlendFactor: VK_BLEND_FACTOR_ZERO, # optional 395 | alphaBlendOp: VK_BLEND_OP_ADD, # optional 396 | ) 397 | colorBlending = VkPipelineColorBlendStateCreateInfo( 398 | sType: VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, 399 | logicOpEnable: VkBool32(VK_FALSE), 400 | logicOp: VK_LOGIC_OP_COPY, # optional 401 | attachmentCount: 1, 402 | pAttachments: colorBlendAttachment.addr, 403 | blendConstants: [0f, 0f, 0f, 0f], # optional 404 | ) 405 | dynamicStates = [VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_LINE_WIDTH] 406 | dynamicState = VkPipelineDynamicStateCreateInfo( 407 | sType: VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, 408 | dynamicStateCount: dynamicStates.len.uint32, 409 | pDynamicStates: dynamicStates[0].addr, 410 | ) 411 | pipelineLayoutInfo = VkPipelineLayoutCreateInfo( 412 | sType: VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, 413 | setLayoutCount: 0, # optional 414 | pSetLayouts: nil, # optional 415 | pushConstantRangeCount: 0, # optional 416 | pPushConstantRanges: nil, # optional 417 | ) 418 | if vkCreatePipelineLayout(device, pipelineLayoutInfo.addr, nil, result.pipelineLayout.addr) != VK_SUCCESS: 419 | quit("failed to create pipeline layout") 420 | var 421 | pipelineInfo = VkGraphicsPipelineCreateInfo( 422 | sType: VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, 423 | stageCount: shaderStages.len.uint32, 424 | pStages: shaderStages[0].addr, 425 | pVertexInputState: vertexInputInfo.addr, 426 | pInputAssemblyState: inputAssembly.addr, 427 | pViewportState: viewportState.addr, 428 | pRasterizationState: rasterizer.addr, 429 | pMultisampleState: multisampling.addr, 430 | pDepthStencilState: nil, # optional 431 | pColorBlendState: colorBlending.addr, 432 | pDynamicState: nil, # optional 433 | layout: result.pipelineLayout, 434 | renderPass: renderPass, 435 | subpass: 0, 436 | basePipelineHandle: VkPipeline(VK_NULL_HANDLE), # optional 437 | basePipelineIndex: -1, # optional 438 | ) 439 | if vkCreateGraphicsPipelines(device, VkPipelineCache(VK_NULL_HANDLE), 1, pipelineInfo.addr, nil, result.pipeline.addr) != VK_SUCCESS: 440 | quit("fialed to create graphics pipeline") 441 | vkDestroyShaderModule(device, vertShaderModule, nil) 442 | vkDestroyShaderModule(device, fragShaderModule, nil) 443 | 444 | proc createRenderPass(device: VkDevice, swapChainImageFormat: VkFormat): VkRenderPass = 445 | var 446 | colorAttachment = VkAttachmentDescription( 447 | format: swapChainImageFormat, 448 | samples: VK_SAMPLE_COUNT_1_BIT, 449 | loadOp: VK_ATTACHMENT_LOAD_OP_CLEAR, 450 | storeOp: VK_ATTACHMENT_STORE_OP_STORE, 451 | stencilLoadOp: VK_ATTACHMENT_LOAD_OP_DONT_CARE, 452 | stencilStoreOp: VK_ATTACHMENT_STORE_OP_DONT_CARE, 453 | initialLayout: VK_IMAGE_LAYOUT_UNDEFINED, 454 | finalLayout: cast[VkImageLayout](1000001002), # VK_IMAGE_LAYOUT_PRESENT_SRC_KHR 455 | ) 456 | colorAttachmentRef = VkAttachmentReference( 457 | attachment: 0, 458 | layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 459 | ) 460 | subpass = VkSubpassDescription( 461 | pipelineBindPoint: VK_PIPELINE_BIND_POINT_GRAPHICS, 462 | colorAttachmentCount: 1, 463 | pColorAttachments: colorAttachmentRef.addr, 464 | ) 465 | dependency = VkSubpassDependency( 466 | srcSubpass: VK_SUBPASS_EXTERNAL, 467 | dstSubpass: 0, 468 | srcStageMask: VkPipelineStageFlags(0x00000400), #VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT 469 | srcAccessMask: VkAccessFlags(0), 470 | dstStageMask: VkPipelineStageFlags(0x00000400), #VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT 471 | dstAccessMask: VkAccessFlags(0x00000100), #VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT 472 | ) 473 | renderPassInfo = VkRenderPassCreateInfo( 474 | sType: VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, 475 | attachmentCount: 1, 476 | pAttachments: colorAttachment.addr, 477 | subpassCount: 1, 478 | pSubpasses: subpass.addr, 479 | dependencyCount: 1, 480 | pDependencies: dependency.addr, 481 | ) 482 | if vkCreateRenderPass(device, renderPassInfo.addr, nil, result.addr) != VK_SUCCESS: 483 | quit("failed to create render pass") 484 | 485 | proc createFramebuffers(device: VkDevice, swapChainExtent: VkExtent2D, swapChainImageViews: seq[VkImageView], renderPass: VkRenderPass): seq[VkFramebuffer] = 486 | result.setLen(swapChainImageViews.len) 487 | for i in 0 ..< swapChainImageViews.len: 488 | var 489 | attachments = [swapChainImageViews[i]] 490 | framebufferInfo = VkFramebufferCreateInfo( 491 | sType: VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, 492 | renderPass: renderPass, 493 | attachmentCount: attachments.len.uint32, 494 | pAttachments: attachments[0].addr, 495 | width: swapChainExtent.width, 496 | height: swapChainExtent.height, 497 | layers: 1, 498 | ) 499 | if vkCreateFramebuffer(device, framebufferInfo.addr, nil, result[i].addr) != VK_SUCCESS: 500 | quit("failed to create framebuffer") 501 | 502 | proc createCommandPool(device: VkDevice, physicalDevice: VkPhysicalDevice, surface: VkSurfaceKHR): VkCommandPool = 503 | var 504 | queueFamilyIndices = findQueueFamilies(physicalDevice, surface) 505 | poolInfo = VkCommandPoolCreateInfo( 506 | sType: VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, 507 | queueFamilyIndex: queueFamilyIndices.graphicsFamily, 508 | flags: VkCommandPoolCreateFlags(0), # optional 509 | ) 510 | if vkCreateCommandPool(device, poolInfo.addr, nil, result.addr) != VK_SUCCESS: 511 | quit("failed to create command pool") 512 | 513 | proc createCommandBuffers( 514 | device: VkDevice, 515 | swapChainExtent: VkExtent2D, 516 | renderPass: VkRenderPass, 517 | pipeline: VkPipeline, 518 | swapChainFrameBuffers: seq[VkFramebuffer], 519 | commandPool: VkCommandPool 520 | ): seq[VkCommandBuffer] = 521 | result.setLen(swapChainFramebuffers.len) 522 | var 523 | allocInfo = VkCommandBufferAllocateInfo( 524 | sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, 525 | commandPool: commandPool, 526 | level: VK_COMMAND_BUFFER_LEVEL_PRIMARY, 527 | commandBufferCount: result.len.uint32, 528 | ) 529 | if vkAllocateCommandBuffers(device, allocInfo.addr, result[0].addr) != VK_SUCCESS: 530 | quit("failed to allocate command buffers") 531 | for i in 0 ..< result.len: 532 | var 533 | beginInfo = VkCommandBufferBeginInfo( 534 | sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, 535 | flags: VkCommandBufferUsageFlags(0), # optional 536 | pInheritanceInfo: nil, 537 | ) 538 | if vkBeginCommandBuffer(result[i], beginInfo.addr) != VK_SUCCESS: 539 | quit("failed to begin recording command buffer") 540 | var 541 | clearColor = VkClearValue( 542 | color: VkClearColorValue(float32: [0f, 0f, 0f, 1f]), 543 | ) 544 | renderPassInfo = VkRenderPassBeginInfo( 545 | sType: VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, 546 | renderPass: renderPass, 547 | framebuffer: swapChainFramebuffers[i], 548 | renderArea: VkRect2d( 549 | offset: VkOffset2d(x: 0, y: 0), 550 | extent: swapChainExtent, 551 | ), 552 | clearValueCount: 1, 553 | pClearValues: clearColor.addr, 554 | ) 555 | vkCmdBeginRenderPass(result[i], renderPassInfo.addr, VK_SUBPASS_CONTENTS_INLINE) 556 | vkCmdBindPipeline(result[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline) 557 | vkCmdDraw(result[i], 3, 1, 0, 0) 558 | vkCmdEndRenderPass(result[i]) 559 | if vkEndCommandBuffer(result[i]) != VK_SUCCESS: 560 | quit("failed to record command buffer") 561 | 562 | proc createSemaphores(device: VkDevice): Semaphores = 563 | var semaphoreInfo = VkSemaphoreCreateInfo( 564 | sType: VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, 565 | ) 566 | if vkCreateSemaphore(device, semaphoreInfo.addr, nil, result.imageAvailable.addr) != VK_SUCCESS or 567 | vkCreateSemaphore(device, semaphoreInfo.addr, nil, result.renderFinished.addr) != VK_SUCCESS: 568 | quit("failed to create semaphores") 569 | 570 | var 571 | instance: VkInstance 572 | physicalDevice: VkPhysicalDevice 573 | device: VkDevice 574 | surface: VkSurfaceKHR 575 | graphicsQueue: VkQueue 576 | presentQueue: VkQueue 577 | swapChain: SwapChain 578 | swapChainImageViews: seq[VkImageView] 579 | renderPass: VkRenderPass 580 | graphicsPipeline: GraphicsPipeline 581 | swapChainFrameBuffers: seq[VkFramebuffer] 582 | commandPool: VkCommandPool 583 | commandBuffers: seq[VkCommandBuffer] 584 | semaphores: Semaphores 585 | 586 | proc init*(glfwExtensions: cstringArray, glfwExtensionCount: uint32, createSurface: CreateSurfaceProc) = 587 | vkPreload(); 588 | instance = createInstance(glfwExtensions, glfwExtensionCount) 589 | doAssert vkInit(instance) 590 | 591 | surface = createSurface(instance) 592 | physicalDevice = pickPhysicalDevice(instance, surface) 593 | device = createLogicalDevice(physicalDevice, surface, graphicsQueue, presentQueue) 594 | swapChain = createSwapChain(device, physicalDevice, surface) 595 | swapChainImageViews = createImageViews(device, swapChain.swapChainImages, swapChain.swapChainImageFormat) 596 | renderPass = createRenderPass(device, swapChain.swapChainImageFormat) 597 | graphicsPipeline = createGraphicsPipeline(device, swapChain.swapChainExtent, renderPass) 598 | swapChainFramebuffers = createFramebuffers(device, swapChain.swapChainExtent, swapChainImageViews, renderPass) 599 | commandPool = createCommandPool(device, physicalDevice, surface) 600 | commandBuffers = createCommandBuffers(device, swapChain.swapChainExtent, renderPass, graphicsPipeline.pipeline, swapChainFramebuffers, commandPool) 601 | semaphores = createSemaphores(device) 602 | 603 | proc tick*() = 604 | var imageIndex: uint32 605 | discard vkAcquireNextImageKHR(device, swapChain.swapChain, uint64.high, semaphores.imageAvailable, VkFence(VK_NULL_HANDLE), imageIndex.addr) 606 | var 607 | waitSemaphores = [semaphores.imageAvailable] 608 | waitStages = [ 609 | VkPipelineStageFlags(0x00000400), # VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT 610 | ] 611 | signalSemaphores = [semaphores.renderFinished] 612 | submitInfo = VkSubmitInfo( 613 | sType: VK_STRUCTURE_TYPE_SUBMIT_INFO, 614 | waitSemaphoreCount: waitSemaphores.len.uint32, 615 | pWaitSemaphores: waitSemaphores[0].addr, 616 | pWaitDstStageMask: waitStages[0].addr, 617 | commandBufferCount: 1, 618 | pCommandBuffers: commandBuffers[imageIndex].addr, 619 | signalSemaphoreCount: 1, 620 | pSignalSemaphores: signalSemaphores[0].addr, 621 | ) 622 | if vkQueueSubmit(graphicsQueue, 1, submitInfo.addr, VkFence(VK_NULL_HANDLE)) != VK_SUCCESS: 623 | quit("failed to submit draw command buffer") 624 | var 625 | swapChains = [swapChain.swapChain] 626 | presentInfo = VkPresentInfoKHR( 627 | sType: cast[VkStructureType](1000001001), # VK_STRUCTURE_TYPE_PRESENT_INFO_KHR 628 | waitSemaphoreCount: 1, 629 | pWaitSemaphores: signalSemaphores[0].addr, 630 | swapchainCount: 1, 631 | pSwapChains: swapChains[0].addr, 632 | pImageIndices: imageIndex.addr, 633 | pResults: nil, 634 | ) 635 | discard vkQueuePresentKHR(presentQueue, presentInfo.addr) 636 | 637 | proc deinit*() = 638 | discard vkDeviceWaitIdle(device) 639 | vkDestroySemaphore(device, semaphores.renderFinished, nil) 640 | vkDestroySemaphore(device, semaphores.imageAvailable, nil) 641 | vkDestroyCommandPool(device, commandPool, nil) 642 | for framebuffer in swapChainFramebuffers: 643 | vkDestroyFramebuffer(device, framebuffer, nil) 644 | vkDestroyPipeline(device, graphicsPipeline.pipeline, nil) 645 | vkDestroyPipelineLayout(device, graphicsPipeline.pipelineLayout, nil) 646 | vkDestroyRenderPass(device, renderPass, nil) 647 | for imageView in swapChainImageViews: 648 | vkDestroyImageView(device, imageView, nil) 649 | vkDestroySwapchainKHR(device, swapChain.swapChain, nil) 650 | vkDestroyDevice(device, nil) 651 | vkDestroySurfaceKHR(instance, surface, nil) 652 | vkDestroyInstance(instance, nil) 653 | -------------------------------------------------------------------------------- /tools/generator.nim: -------------------------------------------------------------------------------- 1 | # Written by Leonardo Mariscal , 2019 2 | 3 | import strutils, ./utils, httpClient, os, xmlparser, xmltree, streams, strformat, math, tables, algorithm, bitops 4 | 5 | type 6 | VkProc = object 7 | name: string 8 | rVal: string 9 | args: seq[VkArg] 10 | VkArg = object 11 | name: string 12 | argType: string 13 | VkStruct = object 14 | name: string 15 | members: seq[VkArg] 16 | 17 | var vkProcs: seq[VkProc] 18 | var vkStructs: seq[VkStruct] 19 | var vkStructureTypes: seq[string] 20 | 21 | proc translateType(s: string): string = 22 | result = s 23 | result = result.replace("int64_t", "int64") 24 | result = result.replace("int32_t", "int32") 25 | result = result.replace("int16_t", "int16") 26 | result = result.replace("int8_t", "int8") 27 | result = result.replace("size_t", "uint") # uint matches pointer size just like size_t 28 | result = result.replace("float", "float32") 29 | result = result.replace("double", "float64") 30 | result = result.replace("VK_DEFINE_HANDLE", "VkHandle") 31 | result = result.replace("VK_DEFINE_NON_DISPATCHABLE_HANDLE", "VkNonDispatchableHandle") 32 | result = result.replace("const ", "") 33 | result = result.replace(" const", "") 34 | result = result.replace("unsigned ", "u") 35 | result = result.replace("signed ", "") 36 | result = result.replace("struct ", "") 37 | 38 | if result.contains('*'): 39 | let levels = result.count('*') 40 | result = result.replace("*", "") 41 | for i in 0.., 2019 2 | 3 | const srcHeader* = """ 4 | # Written by Leonardo Mariscal , 2019 5 | 6 | ## Vulkan Bindings 7 | ## ==== 8 | ## WARNING: This is a generated file. Do not edit 9 | ## Any edits will be overwritten by the generator. 10 | 11 | var vkGetProc: proc(procName: cstring): pointer {.cdecl.} 12 | var currInst: pointer = nil 13 | 14 | when not defined(vkCustomLoader): 15 | import dynlib 16 | 17 | when defined(windows): 18 | const vkDLL = "vulkan-1.dll" 19 | elif defined(macosx): 20 | const vkDLL = "libMoltenVK.dylib" 21 | else: 22 | const vkDLL = "libvulkan.so.1" 23 | 24 | let vkHandleDLL = loadLib(vkDLL) 25 | if isNil(vkHandleDLL): 26 | quit("could not load: " & vkDLL) 27 | 28 | let vkGetProcAddress = cast[proc(inst: pointer, s: cstring): pointer {.stdcall.}](symAddr(vkHandleDLL, "vkGetInstanceProcAddr")) 29 | if vkGetProcAddress == nil: 30 | quit("failed to load `vkGetInstanceProcAddr` from " & vkDLL) 31 | 32 | vkGetProc = proc(procName: cstring): pointer {.cdecl.} = 33 | when defined(windows): 34 | result = vkGetProcAddress(currInst, procName) 35 | if result != nil: 36 | return 37 | result = symAddr(vkHandleDLL, procName) 38 | if result == nil: 39 | raiseInvalidLibrary(procName) 40 | 41 | proc setVKGetProc*(getProc: proc(procName: cstring): pointer {.cdecl.}) = 42 | vkGetProc = getProc 43 | 44 | type 45 | VkHandle* = int64 46 | VkNonDispatchableHandle* = int64 47 | ANativeWindow = ptr object 48 | CAMetalLayer = ptr object 49 | AHardwareBuffer = ptr object 50 | """ 51 | 52 | const vkInit* = """ 53 | var 54 | vkCreateInstance*: proc(pCreateInfo: ptr VkInstanceCreateInfo , pAllocator: ptr VkAllocationCallbacks , pInstance: ptr VkInstance ): VkResult {.stdcall.} 55 | vkEnumerateInstanceExtensionProperties*: proc(pLayerName: cstring , pPropertyCount: ptr uint32 , pProperties: ptr VkExtensionProperties ): VkResult {.stdcall.} 56 | vkEnumerateInstanceLayerProperties*: proc(pPropertyCount: ptr uint32 , pProperties: ptr VkLayerProperties ): VkResult {.stdcall.} 57 | vkEnumerateInstanceVersion*: proc(pApiVersion: ptr uint32 ): VkResult {.stdcall.} 58 | 59 | proc vkPreload*(load1_1: bool = true) = 60 | vkGetInstanceProcAddr = cast[proc(instance: VkInstance, pName: cstring ): PFN_vkVoidFunction {.stdcall.}](symAddr(vkHandleDLL, "vkGetInstanceProcAddr")) 61 | 62 | vkCreateInstance = cast[proc(pCreateInfo: ptr VkInstanceCreateInfo , pAllocator: ptr VkAllocationCallbacks , pInstance: ptr VkInstance ): VkResult {.stdcall.}](vkGetProc("vkCreateInstance")) 63 | vkEnumerateInstanceExtensionProperties = cast[proc(pLayerName: cstring , pPropertyCount: ptr uint32 , pProperties: ptr VkExtensionProperties ): VkResult {.stdcall.}](vkGetProc("vkEnumerateInstanceExtensionProperties")) 64 | vkEnumerateInstanceLayerProperties = cast[proc(pPropertyCount: ptr uint32 , pProperties: ptr VkLayerProperties ): VkResult {.stdcall.}](vkGetProc("vkEnumerateInstanceLayerProperties")) 65 | 66 | if load1_1: 67 | vkEnumerateInstanceVersion = cast[proc(pApiVersion: ptr uint32 ): VkResult {.stdcall.}](vkGetProc("vkEnumerateInstanceVersion")) 68 | 69 | proc vkInit*(instance: VkInstance, load1_0: bool = true, load1_1: bool = true): bool = 70 | currInst = cast[pointer](instance) 71 | if load1_0: 72 | vkLoad1_0() 73 | when not defined(macosx): 74 | if load1_1: 75 | vkLoad1_1() 76 | return true 77 | """ 78 | 79 | let keywords* = ["addr", "and", "as", "asm", "bind", "block", "break", "case", "cast", "concept", 80 | "const", "continue", "converter", "defer", "discard", "distinct", "div", "do", 81 | "elif", "else", "end", "enum", "except", "export", "finally", "for", "from", "func", 82 | "if", "import", "in", "include", "interface", "is", "isnot", "iterator", "let", 83 | "macro", "method", "mixin", "mod", "nil", "not", "notin", "object", "of", "or", 84 | "out", "proc", "ptr", "raise", "ref", "return", "shl", "shr", "static", "template", 85 | "try", "tuple", "type", "using", "var", "when", "while", "xor", "yield"] 86 | -------------------------------------------------------------------------------- /vulkan.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "1.2.1" 4 | author = "Leonardo Mariscal" 5 | description = "Vulkan bindings for Nim" 6 | license = "MIT" 7 | srcDir = "src" 8 | skipDirs = @["tests"] 9 | 10 | # Dependencies 11 | 12 | requires "nim >= 1.0.0" 13 | 14 | task gen, "Generate bindings from source": 15 | exec("nim c -d:ssl -r tools/generator.nim") 16 | 17 | task test, "Create basic triangle with Vulkan and GLFW": 18 | requires "nimgl@#1.0" # Please https://github.com/nim-lang/nimble/issues/482 19 | exec("nim c -r tests/test.nim") 20 | --------------------------------------------------------------------------------