t_vulkan.nim 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  1. import sets, strutils, sequtils, bitops, unittest
  2. when not (compiles do: import vulkan):
  3. {.error: "please run `nimble install https://github.com/DanielBelmes/vulkan`".}
  4. import vulkan, vmath
  5. import siwin
  6. type
  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. const
  28. deviceExtensions = ["VK_KHR_swapchain"]
  29. VK_NULL_HANDLE = 0
  30. WIDTH* = 800.uint32
  31. HEIGHT* = 600.uint32
  32. proc isComplete(indices: QueueFamilyIndices): bool =
  33. indices.graphicsFamilyFound and indices.presentFamilyFound
  34. proc findQueueFamilies(pDevice: VkPhysicalDevice, surface: VkSurfaceKHR): QueueFamilyIndices =
  35. result.graphicsFamilyFound = false
  36. var queueFamilyCount: uint32 = 0
  37. vkGetPhysicalDeviceQueueFamilyProperties(pDevice, queueFamilyCount.addr, nil)
  38. var queueFamilies = newSeq[VkQueueFamilyProperties](queueFamilyCount)
  39. vkGetPhysicalDeviceQueueFamilyProperties(pDevice, queueFamilyCount.addr, queueFamilies[0].addr)
  40. var index: uint32 = 0
  41. for queueFamily in queueFamilies:
  42. if (queueFamily.queueFlags.uint32 and VkQueueGraphicsBit.uint32) > 0'u32:
  43. result.graphicsFamily = index
  44. result.graphicsFamilyFound = true
  45. var presentSupport: VkBool32
  46. discard vkGetPhysicalDeviceSurfaceSupportKHR(pDevice, index, surface, presentSupport.addr)
  47. if presentSupport.ord == 1:
  48. result.presentFamily = index
  49. result.presentFamilyFound = true
  50. if result.isComplete:
  51. break
  52. index.inc
  53. proc createLogicalDevice(physicalDevice: VkPhysicalDevice, surface: VkSurfaceKHR, graphicsQueue: var VkQueue, presentQueue: var VkQueue): VkDevice =
  54. let
  55. indices = findQueueFamilies(physicalDevice, surface)
  56. uniqueQueueFamilies = [indices.graphicsFamily, indices.presentFamily].toHashSet
  57. var
  58. queuePriority = 1f
  59. queueCreateInfos = newSeq[VkDeviceQueueCreateInfo]()
  60. for queueFamily in uniqueQueueFamilies:
  61. let deviceQueueCreateInfo = newVkDeviceQueueCreateInfo(
  62. sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
  63. queueFamilyIndex = queueFamily,
  64. queueCount = 1,
  65. pQueuePriorities = queuePriority.addr
  66. )
  67. queueCreateInfos.add(deviceQueueCreateInfo)
  68. var
  69. deviceFeatures = newSeq[VkPhysicalDeviceFeatures](1)
  70. deviceExts = allocCStringArray(deviceExtensions)
  71. deviceCreateInfo = newVkDeviceCreateInfo(
  72. pQueueCreateInfos = queueCreateInfos[0].addr,
  73. queueCreateInfoCount = queueCreateInfos.len.uint32,
  74. pEnabledFeatures = deviceFeatures[0].addr,
  75. enabledExtensionCount = deviceExtensions.len.uint32,
  76. enabledLayerCount = 0,
  77. ppEnabledLayerNames = nil,
  78. ppEnabledExtensionNames = deviceExts
  79. )
  80. if vkCreateDevice(physicalDevice, deviceCreateInfo.addr, nil, result.addr) != VKSuccess:
  81. echo "failed to create logical device"
  82. deallocCStringArray(deviceExts)
  83. vkGetDeviceQueue(result, indices.graphicsFamily, 0, graphicsQueue.addr)
  84. vkGetDeviceQueue(result, indices.presentFamily, 0, presentQueue.addr)
  85. proc checkDeviceExtensionSupport(pDevice: VkPhysicalDevice): bool =
  86. var extCount: uint32
  87. discard vkEnumerateDeviceExtensionProperties(pDevice, nil, extCount.addr, nil)
  88. var availableExts = newSeq[VkExtensionProperties](extCount)
  89. discard vkEnumerateDeviceExtensionProperties(pDevice, nil, extCount.addr, availableExts[0].addr)
  90. var requiredExts = deviceExtensions.toHashSet
  91. for ext in availableExts.mitems:
  92. requiredExts.excl(ext.extensionName.join().strip(chars={'\0'}))
  93. requiredExts.len == 0
  94. proc querySwapChainSupport(pDevice: VkPhysicalDevice, surface: VkSurfaceKHR): SwapChainSupportDetails =
  95. discard vkGetPhysicalDeviceSurfaceCapabilitiesKHR(pDevice, surface, result.capabilities.addr)
  96. var formatCount: uint32
  97. discard vkGetPhysicalDeviceSurfaceFormatsKHR(pDevice, surface, formatCount.addr, nil)
  98. if formatCount != 0:
  99. result.formats.setLen(formatCount)
  100. discard vkGetPhysicalDeviceSurfaceFormatsKHR(pDevice, surface, formatCount.addr, result.formats[0].addr)
  101. var presentModeCount: uint32
  102. discard vkGetPhysicalDeviceSurfacePresentModesKHR(pDevice, surface, presentModeCount.addr, nil)
  103. if presentModeCount != 0:
  104. result.presentModes.setLen(presentModeCount)
  105. discard vkGetPhysicalDeviceSurfacePresentModesKHR(pDevice, surface, presentModeCount.addr, result.presentModes[0].addr)
  106. proc isDeviceSuitable(pDevice: VkPhysicalDevice, surface: VkSurfaceKHR): bool =
  107. var deviceProperties: VkPhysicalDeviceProperties
  108. vkGetPhysicalDeviceProperties(pDevice, deviceProperties.addr)
  109. let extsSupported = pDevice.checkDeviceExtensionSupport
  110. var swapChainAdequate = false
  111. if extsSupported:
  112. let swapChainSupport = querySwapChainSupport(pDevice, surface)
  113. swapChainAdequate =
  114. swapChainSupport.formats.len != 0 and
  115. swapChainSupport.presentModes.len != 0
  116. let indices: QueueFamilyIndices = findQueueFamilies(pDevice, surface)
  117. return indices.isComplete and extsSupported and swapChainAdequate
  118. proc createInstance(extensions: cstringArray, extensionCount: uint32): VkInstance =
  119. var appInfo = newVkApplicationInfo(
  120. pApplicationName = "siwin Vulkan example",
  121. applicationVersion = vkMakeVersion(1, 0, 0),
  122. pEngineName = "No Engine",
  123. engineVersion = vkMakeVersion(1, 0, 0),
  124. apiVersion = VkApiVersion1_1
  125. )
  126. var instanceCreateInfo = newVkInstanceCreateInfo(
  127. pApplicationInfo = appInfo.addr,
  128. enabledExtensionCount = extensionCount,
  129. ppEnabledExtensionNames = extensions,
  130. enabledLayerCount = 0,
  131. ppEnabledLayerNames = nil,
  132. )
  133. if vkCreateInstance(instanceCreateInfo.addr, nil, result.addr) != VKSuccess:
  134. quit("failed to create instance")
  135. var extensionCount: uint32 = 0
  136. discard vkEnumerateInstanceExtensionProperties(nil, extensionCount.addr, nil)
  137. var extensions = newSeq[VkExtensionProperties](extensionCount)
  138. discard vkEnumerateInstanceExtensionProperties(nil, extensionCount.addr, extensions[0].addr)
  139. proc pickPhysicalDevice(instance: VkInstance, surface: VkSurfaceKHR): VkPhysicalDevice =
  140. var deviceCount: uint32 = 0
  141. discard vkEnumeratePhysicalDevices(instance, deviceCount.addr, nil)
  142. var devices = newSeq[VkPhysicalDevice](deviceCount)
  143. discard vkEnumeratePhysicalDevices(instance, deviceCount.addr, devices[0].addr)
  144. for pDevice in devices:
  145. if isDeviceSuitable(pDevice, surface):
  146. return pDevice
  147. raise newException(Exception, "Suitable physical device not found")
  148. proc chooseSwapSurfaceFormat(availableFormats: seq[VkSurfaceFormatKHR]): VkSurfaceFormatKHR =
  149. for availableFormat in availableFormats:
  150. if availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB and
  151. availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR:
  152. return availableFormat
  153. availableFormats[0]
  154. proc chooseSwapPresentMode(availablePresentModes: seq[VkPresentModeKHR]): VkPresentModeKHR =
  155. for availablePresentMode in availablePresentModes:
  156. if availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR:
  157. return availablePresentMode
  158. VK_PRESENT_MODE_FIFO_KHR
  159. proc chooseSwapExtent(capabilities: VkSurfaceCapabilitiesKHR): VkExtent2D =
  160. if capabilities.currentExtent.width != uint32.high:
  161. return capabilities.currentExtent
  162. else:
  163. result = VkExtent2D(width: WIDTH, height: HEIGHT)
  164. result.width =
  165. max(
  166. capabilities.minImageExtent.width,
  167. min(capabilities.maxImageExtent.width, result.width)
  168. )
  169. result.height =
  170. max(
  171. capabilities.minImageExtent.height,
  172. min(capabilities.maxImageExtent.height, result.height)
  173. )
  174. proc createSwapChain(device: VkDevice, physicalDevice: VkPhysicalDevice, surface: VkSurfaceKHR): SwapChain =
  175. let
  176. swapChainSupport = querySwapChainSupport(physicalDevice, surface)
  177. surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats)
  178. presentMode = chooseSwapPresentMode(swapChainSupport.presentModes)
  179. extent = chooseSwapExtent(swapChainSupport.capabilities)
  180. var imageCount = swapChainSupport.capabilities.minImageCount + 1
  181. if swapChainSupport.capabilities.maxImageCount > 0 and
  182. imageCount > swapChainSupport.capabilities.maxImageCount:
  183. imageCount = swapChainSupport.capabilities.maxImageCount
  184. var createInfo = VkSwapchainCreateInfoKHR(
  185. sType: cast[VkStructureType](1000001000), # VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR
  186. surface: surface,
  187. minImageCount: imageCount,
  188. imageFormat: surfaceFormat.format,
  189. imageColorSpace: surfaceFormat.colorSpace,
  190. imageExtent: extent,
  191. imageArrayLayers: 1,
  192. imageUsage: VkImageUsageFlags(0x00000010), # VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
  193. )
  194. let indices = findQueueFamilies(physicalDevice, surface)
  195. var queueFamilyIndices = [indices.graphicsFamily, indices.presentFamily]
  196. if indices.graphicsFamily != indices.presentFamily:
  197. createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT
  198. createInfo.queueFamilyIndexCount = queueFamilyIndices.len.uint32
  199. createInfo.pQueueFamilyIndices = queueFamilyIndices[0].addr
  200. else:
  201. createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE
  202. createInfo.queueFamilyIndexCount = 0 # optional
  203. createInfo.pQueueFamilyIndices = nil # optional
  204. createInfo.preTransform = swapChainSupport.capabilities.currentTransform
  205. createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR
  206. createInfo.presentMode = presentMode
  207. createInfo.clipped = VkBool32(VK_TRUE)
  208. createInfo.oldSwapChain = VkSwapchainKHR(0)
  209. if vkCreateSwapChainKHR(device, createInfo.addr, nil, result.swapChain.addr) != VK_SUCCESS:
  210. quit("failed to create swap chain")
  211. discard vkGetSwapchainImagesKHR(device, result.swapChain, imageCount.addr, nil)
  212. result.swapChainImages.setLen(imageCount)
  213. discard vkGetSwapchainImagesKHR(device, result.swapChain, imageCount.addr, result.swapChainImages[0].addr)
  214. result.swapChainImageFormat = surfaceFormat.format
  215. result.swapChainExtent = extent
  216. proc createImageViews(device: VkDevice, swapChainImages: seq[VkImage], swapChainImageFormat: VkFormat): seq[VkImageView] =
  217. result.setLen(swapChainImages.len)
  218. for i in 0 ..< swapChainImages.len:
  219. var createInfo = VkImageViewCreateInfo(
  220. sType: VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
  221. image: swapChainImages[i],
  222. viewType: VK_IMAGE_VIEW_TYPE_2D,
  223. format: swapChainImageFormat,
  224. components: VkComponentMapping(
  225. r: VK_COMPONENT_SWIZZLE_IDENTITY,
  226. g: VK_COMPONENT_SWIZZLE_IDENTITY,
  227. b: VK_COMPONENT_SWIZZLE_IDENTITY,
  228. a: VK_COMPONENT_SWIZZLE_IDENTITY,
  229. ),
  230. subresourceRange: VkImageSubresourceRange(
  231. aspectMask: VkImageAspectFlags(VK_IMAGE_ASPECT_COLOR_BIT),
  232. baseMipLevel: 0,
  233. levelCount: 1,
  234. baseArrayLayer: 0,
  235. layerCount: 1,
  236. ),
  237. )
  238. if vkCreateImageView(device, createInfo.addr, nil, result[i].addr) != VK_SUCCESS:
  239. quit("failed to create image view")
  240. proc createShaderModule(device: VkDevice, code: string): VkShaderModule =
  241. var createInfo = VkShaderModuleCreateInfo(
  242. sType: VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
  243. codeSize: code.len.uint32,
  244. pCode: cast[ptr uint32](code[0].unsafeAddr)
  245. )
  246. if vkCreateShaderModule(device, createInfo.addr, nil, result.addr) != VK_SUCCESS:
  247. quit("failed to create shader module")
  248. proc createGraphicsPipeline(device: VkDevice, swapChainExtent: VkExtent2D, renderPass: VkRenderPass): GraphicsPipeline =
  249. const
  250. vertShaderCode = staticRead("shaders/vert.spv")
  251. fragShaderCode = staticRead("shaders/frag.spv")
  252. var
  253. vertShaderModule = createShaderModule(device, vertShaderCode)
  254. fragShaderModule = createShaderModule(device, fragShaderCode)
  255. vertShaderStageInfo = VkPipelineShaderStageCreateInfo(
  256. sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
  257. stage: VK_SHADER_STAGE_VERTEX_BIT,
  258. module: vertShaderModule,
  259. pName: "main",
  260. )
  261. fragShaderStageInfo = VkPipelineShaderStageCreateInfo(
  262. sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
  263. stage: cast[VkShaderStageFlagBits](0x00000010), # VK_SHADER_STAGE_FRAGMENT_BIT
  264. module: fragShaderModule,
  265. pName: "main",
  266. )
  267. shaderStages = [vertShaderStageInfo, fragShaderStageInfo]
  268. vertexInputInfo = VkPipelineVertexInputStateCreateInfo(
  269. sType: VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
  270. vertexBindingDescriptionCount: 0,
  271. pVertexBindingDescriptions: nil, # optional
  272. vertexAttributeDescriptionCount: 0,
  273. pVertexAttributeDescriptions: nil, # optional
  274. )
  275. inputAssembly = VkPipelineInputAssemblyStateCreateInfo(
  276. sType: VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
  277. topology: VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
  278. primitiveRestartEnable: VkBool32(VK_FALSE),
  279. )
  280. viewport = VkViewport(
  281. x: 0f,
  282. y: 0f,
  283. width: swapChainExtent.width.float,
  284. height: swapChainExtent.height.float,
  285. minDepth: 0f,
  286. maxDepth: 1f,
  287. )
  288. scissor = VkRect2D(
  289. offset: VkOffset2D(x: 0, y: 0),
  290. extent: swapChainExtent,
  291. )
  292. viewportState = VkPipelineViewportStateCreateInfo(
  293. sType: VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
  294. viewportCount: 1,
  295. pViewports: viewport.addr,
  296. scissorCount: 1,
  297. pScissors: scissor.addr,
  298. )
  299. rasterizer = VkPipelineRasterizationStateCreateInfo(
  300. sType: VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
  301. depthClampEnable: VkBool32(VK_FALSE),
  302. rasterizerDiscardEnable: VkBool32(VK_FALSE),
  303. polygonMode: VK_POLYGON_MODE_FILL,
  304. lineWidth: 1f,
  305. cullMode: VkCullModeFlags(0x00000002), # VK_CULL_MODE_BACK_BIT
  306. frontFace: VK_FRONT_FACE_CLOCKWISE,
  307. depthBiasEnable: VkBool32(VK_FALSE),
  308. depthBiasConstantFactor: 0f, # optional
  309. depthBiasClamp: 0f, # optional
  310. depthBiasSlopeFactor: 0f, # optional
  311. )
  312. multisampling = VkPipelineMultisampleStateCreateInfo(
  313. sType: VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
  314. sampleShadingEnable: VkBool32(VK_FALSE),
  315. rasterizationSamples: VK_SAMPLE_COUNT_1_BIT,
  316. minSampleShading: 1f, # optional
  317. pSampleMask: nil, # optional
  318. alphaToCoverageEnable: VkBool32(VK_FALSE), # optional
  319. alphaToOneEnable: VkBool32(VK_FALSE), # optional
  320. )
  321. colorBlendAttachment = VkPipelineColorBlendAttachmentState(
  322. colorWriteMask: VkColorComponentFlags(
  323. bitor(
  324. 0x00000001, # VK_COLOR_COMPONENT_R_BIT
  325. bitor(
  326. 0x00000002, # VK_COLOR_COMPONENT_G_BIT
  327. bitor(
  328. 0x00000004, # VK_COLOR_COMPONENT_B_BIT
  329. 0x00000008, # VK_COLOR_COMPONENT_A_BIT
  330. )))
  331. ),
  332. blendEnable: VkBool32(VK_FALSE),
  333. srcColorBlendFactor: VK_BLEND_FACTOR_ONE, # optional
  334. dstColorBlendFactor: VK_BLEND_FACTOR_ZERO, # optional
  335. colorBlendOp: VK_BLEND_OP_ADD, # optional
  336. srcAlphaBlendFactor: VK_BLEND_FACTOR_ONE, # optional
  337. dstAlphaBlendFactor: VK_BLEND_FACTOR_ZERO, # optional
  338. alphaBlendOp: VK_BLEND_OP_ADD, # optional
  339. )
  340. colorBlending = VkPipelineColorBlendStateCreateInfo(
  341. sType: VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
  342. logicOpEnable: VkBool32(VK_FALSE),
  343. logicOp: VK_LOGIC_OP_COPY, # optional
  344. attachmentCount: 1,
  345. pAttachments: colorBlendAttachment.addr,
  346. blendConstants: [0f, 0f, 0f, 0f], # optional
  347. )
  348. pipelineLayoutInfo = VkPipelineLayoutCreateInfo(
  349. sType: VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
  350. setLayoutCount: 0, # optional
  351. pSetLayouts: nil, # optional
  352. pushConstantRangeCount: 0, # optional
  353. pPushConstantRanges: nil, # optional
  354. )
  355. if vkCreatePipelineLayout(device, pipelineLayoutInfo.addr, nil, result.pipelineLayout.addr) != VK_SUCCESS:
  356. quit("failed to create pipeline layout")
  357. var
  358. pipelineInfo = VkGraphicsPipelineCreateInfo(
  359. sType: VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
  360. stageCount: shaderStages.len.uint32,
  361. pStages: shaderStages[0].addr,
  362. pVertexInputState: vertexInputInfo.addr,
  363. pInputAssemblyState: inputAssembly.addr,
  364. pViewportState: viewportState.addr,
  365. pRasterizationState: rasterizer.addr,
  366. pMultisampleState: multisampling.addr,
  367. pDepthStencilState: nil, # optional
  368. pColorBlendState: colorBlending.addr,
  369. pDynamicState: nil, # optional
  370. layout: result.pipelineLayout,
  371. renderPass: renderPass,
  372. subpass: 0,
  373. basePipelineHandle: VkPipeline(VK_NULL_HANDLE), # optional
  374. basePipelineIndex: -1, # optional
  375. )
  376. if vkCreateGraphicsPipelines(device, VkPipelineCache(VK_NULL_HANDLE), 1, pipelineInfo.addr, nil, result.pipeline.addr) != VK_SUCCESS:
  377. quit("fialed to create graphics pipeline")
  378. vkDestroyShaderModule(device, vertShaderModule, nil)
  379. vkDestroyShaderModule(device, fragShaderModule, nil)
  380. proc createRenderPass(device: VkDevice, swapChainImageFormat: VkFormat): VkRenderPass =
  381. var
  382. colorAttachment = VkAttachmentDescription(
  383. format: swapChainImageFormat,
  384. samples: VK_SAMPLE_COUNT_1_BIT,
  385. loadOp: VK_ATTACHMENT_LOAD_OP_CLEAR,
  386. storeOp: VK_ATTACHMENT_STORE_OP_STORE,
  387. stencilLoadOp: VK_ATTACHMENT_LOAD_OP_DONT_CARE,
  388. stencilStoreOp: VK_ATTACHMENT_STORE_OP_DONT_CARE,
  389. initialLayout: VK_IMAGE_LAYOUT_UNDEFINED,
  390. finalLayout: cast[VkImageLayout](1000001002), # VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
  391. )
  392. colorAttachmentRef = VkAttachmentReference(
  393. attachment: 0,
  394. layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
  395. )
  396. subpass = VkSubpassDescription(
  397. pipelineBindPoint: VK_PIPELINE_BIND_POINT_GRAPHICS,
  398. colorAttachmentCount: 1,
  399. pColorAttachments: colorAttachmentRef.addr,
  400. )
  401. dependency = VkSubpassDependency(
  402. srcSubpass: VK_SUBPASS_EXTERNAL,
  403. dstSubpass: 0,
  404. srcStageMask: VkPipelineStageFlags(0x00000400), #VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
  405. srcAccessMask: VkAccessFlags(0),
  406. dstStageMask: VkPipelineStageFlags(0x00000400), #VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
  407. dstAccessMask: VkAccessFlags(0x00000100), #VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
  408. )
  409. renderPassInfo = VkRenderPassCreateInfo(
  410. sType: VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
  411. attachmentCount: 1,
  412. pAttachments: colorAttachment.addr,
  413. subpassCount: 1,
  414. pSubpasses: subpass.addr,
  415. dependencyCount: 1,
  416. pDependencies: dependency.addr,
  417. )
  418. if vkCreateRenderPass(device, renderPassInfo.addr, nil, result.addr) != VK_SUCCESS:
  419. quit("failed to create render pass")
  420. proc createFramebuffers(device: VkDevice, swapChainExtent: VkExtent2D, swapChainImageViews: seq[VkImageView], renderPass: VkRenderPass): seq[VkFramebuffer] =
  421. result.setLen(swapChainImageViews.len)
  422. for i in 0 ..< swapChainImageViews.len:
  423. var
  424. attachments = [swapChainImageViews[i]]
  425. framebufferInfo = VkFramebufferCreateInfo(
  426. sType: VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
  427. renderPass: renderPass,
  428. attachmentCount: attachments.len.uint32,
  429. pAttachments: attachments[0].addr,
  430. width: swapChainExtent.width,
  431. height: swapChainExtent.height,
  432. layers: 1,
  433. )
  434. if vkCreateFramebuffer(device, framebufferInfo.addr, nil, result[i].addr) != VK_SUCCESS:
  435. quit("failed to create framebuffer")
  436. proc createCommandPool(device: VkDevice, physicalDevice: VkPhysicalDevice, surface: VkSurfaceKHR): VkCommandPool =
  437. var
  438. queueFamilyIndices = findQueueFamilies(physicalDevice, surface)
  439. poolInfo = VkCommandPoolCreateInfo(
  440. sType: VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
  441. queueFamilyIndex: queueFamilyIndices.graphicsFamily,
  442. flags: VkCommandPoolCreateFlags(0), # optional
  443. )
  444. if vkCreateCommandPool(device, poolInfo.addr, nil, result.addr) != VK_SUCCESS:
  445. quit("failed to create command pool")
  446. proc createCommandBuffers(
  447. device: VkDevice,
  448. swapChainExtent: VkExtent2D,
  449. renderPass: VkRenderPass,
  450. pipeline: VkPipeline,
  451. swapChainFrameBuffers: seq[VkFramebuffer],
  452. commandPool: VkCommandPool
  453. ): seq[VkCommandBuffer] =
  454. result.setLen(swapChainFramebuffers.len)
  455. var
  456. allocInfo = VkCommandBufferAllocateInfo(
  457. sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
  458. commandPool: commandPool,
  459. level: VK_COMMAND_BUFFER_LEVEL_PRIMARY,
  460. commandBufferCount: result.len.uint32,
  461. )
  462. if vkAllocateCommandBuffers(device, allocInfo.addr, result[0].addr) != VK_SUCCESS:
  463. quit("failed to allocate command buffers")
  464. for i in 0 ..< result.len:
  465. var
  466. beginInfo = VkCommandBufferBeginInfo(
  467. sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
  468. flags: VkCommandBufferUsageFlags(0), # optional
  469. pInheritanceInfo: nil,
  470. )
  471. if vkBeginCommandBuffer(result[i], beginInfo.addr) != VK_SUCCESS:
  472. quit("failed to begin recording command buffer")
  473. var
  474. clearColor = VkClearValue(
  475. color: VkClearColorValue(float32: [0f, 0f, 0f, 1f]),
  476. )
  477. renderPassInfo = VkRenderPassBeginInfo(
  478. sType: VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
  479. renderPass: renderPass,
  480. framebuffer: swapChainFramebuffers[i],
  481. renderArea: VkRect2d(
  482. offset: VkOffset2d(x: 0, y: 0),
  483. extent: swapChainExtent,
  484. ),
  485. clearValueCount: 1,
  486. pClearValues: clearColor.addr,
  487. )
  488. vkCmdBeginRenderPass(result[i], renderPassInfo.addr, VK_SUBPASS_CONTENTS_INLINE)
  489. vkCmdBindPipeline(result[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline)
  490. vkCmdDraw(result[i], 3, 1, 0, 0)
  491. vkCmdEndRenderPass(result[i])
  492. if vkEndCommandBuffer(result[i]) != VK_SUCCESS:
  493. quit("failed to record command buffer")
  494. proc createSemaphores(device: VkDevice): Semaphores =
  495. var semaphoreInfo = VkSemaphoreCreateInfo(
  496. sType: VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
  497. )
  498. if vkCreateSemaphore(device, semaphoreInfo.addr, nil, result.imageAvailable.addr) != VK_SUCCESS or
  499. vkCreateSemaphore(device, semaphoreInfo.addr, nil, result.renderFinished.addr) != VK_SUCCESS:
  500. quit("failed to create semaphores")
  501. var
  502. instance: VkInstance
  503. physicalDevice: VkPhysicalDevice
  504. device: VkDevice
  505. surface: VkSurfaceKHR
  506. graphicsQueue: VkQueue
  507. presentQueue: VkQueue
  508. swapChain: SwapChain
  509. swapChainImageViews: seq[VkImageView]
  510. renderPass: VkRenderPass
  511. graphicsPipeline: GraphicsPipeline
  512. swapChainFrameBuffers: seq[VkFramebuffer]
  513. commandPool: VkCommandPool
  514. commandBuffers: seq[VkCommandBuffer]
  515. semaphores: Semaphores
  516. proc init*() =
  517. physicalDevice = pickPhysicalDevice(instance, surface)
  518. device = createLogicalDevice(physicalDevice, surface, graphicsQueue, presentQueue)
  519. swapChain = createSwapChain(device, physicalDevice, surface)
  520. swapChainImageViews = createImageViews(device, swapChain.swapChainImages, swapChain.swapChainImageFormat)
  521. renderPass = createRenderPass(device, swapChain.swapChainImageFormat)
  522. graphicsPipeline = createGraphicsPipeline(device, swapChain.swapChainExtent, renderPass)
  523. swapChainFramebuffers = createFramebuffers(device, swapChain.swapChainExtent, swapChainImageViews, renderPass)
  524. commandPool = createCommandPool(device, physicalDevice, surface)
  525. commandBuffers = createCommandBuffers(device, swapChain.swapChainExtent, renderPass, graphicsPipeline.pipeline, swapChainFramebuffers, commandPool)
  526. semaphores = createSemaphores(device)
  527. proc tick*() =
  528. var imageIndex: uint32
  529. discard vkAcquireNextImageKHR(device, swapChain.swapChain, uint64.high, semaphores.imageAvailable, VkFence(VK_NULL_HANDLE), imageIndex.addr)
  530. var
  531. waitSemaphores = [semaphores.imageAvailable]
  532. waitStages = [
  533. VkPipelineStageFlags(0x00000400), # VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
  534. ]
  535. signalSemaphores = [semaphores.renderFinished]
  536. submitInfo = VkSubmitInfo(
  537. sType: VK_STRUCTURE_TYPE_SUBMIT_INFO,
  538. waitSemaphoreCount: waitSemaphores.len.uint32,
  539. pWaitSemaphores: waitSemaphores[0].addr,
  540. pWaitDstStageMask: waitStages[0].addr,
  541. commandBufferCount: 1,
  542. pCommandBuffers: commandBuffers[imageIndex].addr,
  543. signalSemaphoreCount: 1,
  544. pSignalSemaphores: signalSemaphores[0].addr,
  545. )
  546. if vkQueueSubmit(graphicsQueue, 1, submitInfo.addr, VkFence(VK_NULL_HANDLE)) != VK_SUCCESS:
  547. quit("failed to submit draw command buffer")
  548. var
  549. swapChains = [swapChain.swapChain]
  550. presentInfo = VkPresentInfoKHR(
  551. sType: cast[VkStructureType](1000001001), # VK_STRUCTURE_TYPE_PRESENT_INFO_KHR
  552. waitSemaphoreCount: 1,
  553. pWaitSemaphores: signalSemaphores[0].addr,
  554. swapchainCount: 1,
  555. pSwapChains: swapChains[0].addr,
  556. pImageIndices: imageIndex.addr,
  557. pResults: nil,
  558. )
  559. discard vkQueuePresentKHR(presentQueue, presentInfo.addr)
  560. proc deinit*() =
  561. discard vkDeviceWaitIdle(device)
  562. vkDestroySemaphore(device, semaphores.renderFinished, nil)
  563. vkDestroySemaphore(device, semaphores.imageAvailable, nil)
  564. vkDestroyCommandPool(device, commandPool, nil)
  565. for framebuffer in swapChainFramebuffers:
  566. vkDestroyFramebuffer(device, framebuffer, nil)
  567. vkDestroyPipeline(device, graphicsPipeline.pipeline, nil)
  568. vkDestroyPipelineLayout(device, graphicsPipeline.pipelineLayout, nil)
  569. vkDestroyRenderPass(device, renderPass, nil)
  570. for imageView in swapChainImageViews:
  571. vkDestroyImageView(device, imageView, nil)
  572. vkDestroySwapchainKHR(device, swapChain.swapChain, nil)
  573. vkDestroyDevice(device, nil)
  574. vkDestroyInstance(instance, nil)
  575. test "Vulkan":
  576. let exts = getRequiredVulkanExtensions()
  577. var cexts = exts.mapit(it[0].unsafeaddr)
  578. instance = createInstance(cast[cstringArray](cexts[0].addr), exts.len.uint32)
  579. let globals = newSiwinGlobals()
  580. let window = globals.newVulkanWindow(
  581. cast[pointer](instance), title="Vulkan test", size = ivec2(WIDTH.int32, HEIGHT.int32), resizable=false
  582. )
  583. surface = cast[VkSurfaceKHR](window.vulkanSurface)
  584. init()
  585. run window, WindowEventsHandler(
  586. onRender: proc(e: RenderEvent) =
  587. tick()
  588. ,
  589. onKey: proc(e: KeyEvent) =
  590. if e.pressed and e.key == Key.escape:
  591. close e.window
  592. )
  593. deinit()