Mercurial > games > semicongine
comparison src/zamikongine/engine.nim @ 480:14e5151f68d1
did: introduce scene graph, meshs and generic vertex buffers
| author | Sam <sam@basx.dev> |
|---|---|
| date | Mon, 09 Jan 2023 11:04:19 +0700 |
| parents | |
| children | c472abfcee57 |
comparison
equal
deleted
inserted
replaced
| 479:16842d15319a | 480:14e5151f68d1 |
|---|---|
| 1 import std/sequtils | |
| 2 import std/typetraits | |
| 3 import std/strformat | |
| 4 import std/enumerate | |
| 5 import std/logging | |
| 6 | |
| 7 | |
| 8 import ./vulkan | |
| 9 import ./vulkan_helpers | |
| 10 import ./window | |
| 11 import ./events | |
| 12 import ./shader | |
| 13 import ./vertex | |
| 14 import ./buffer | |
| 15 import ./thing | |
| 16 import ./mesh | |
| 17 | |
| 18 import ./glslang/glslang | |
| 19 | |
| 20 const MAX_FRAMES_IN_FLIGHT = 2 | |
| 21 const DEBUG_LOG = not defined(release) | |
| 22 | |
| 23 var logger = newConsoleLogger() | |
| 24 addHandler(logger) | |
| 25 | |
| 26 | |
| 27 const VULKAN_VERSION = VK_MAKE_API_VERSION(0'u32, 1'u32, 2'u32, 0'u32) | |
| 28 | |
| 29 type | |
| 30 Device = object | |
| 31 device: VkDevice | |
| 32 physicalDevice: PhysicalDevice | |
| 33 graphicsQueueFamily: uint32 | |
| 34 presentationQueueFamily: uint32 | |
| 35 graphicsQueue: VkQueue | |
| 36 presentationQueue: VkQueue | |
| 37 Swapchain = object | |
| 38 swapchain: VkSwapchainKHR | |
| 39 images: seq[VkImage] | |
| 40 imageviews: seq[VkImageView] | |
| 41 RenderPipeline = object | |
| 42 shaders*: seq[ShaderProgram] | |
| 43 layout*: VkPipelineLayout | |
| 44 pipeline*: VkPipeline | |
| 45 QueueFamily = object | |
| 46 properties*: VkQueueFamilyProperties | |
| 47 hasSurfaceSupport*: bool | |
| 48 PhysicalDevice = object | |
| 49 device*: VkPhysicalDevice | |
| 50 extensions*: seq[string] | |
| 51 properties*: VkPhysicalDeviceProperties | |
| 52 features*: VkPhysicalDeviceFeatures | |
| 53 queueFamilies*: seq[QueueFamily] | |
| 54 formats: seq[VkSurfaceFormatKHR] | |
| 55 presentModes: seq[VkPresentModeKHR] | |
| 56 Vulkan* = object | |
| 57 debugMessenger: VkDebugUtilsMessengerEXT | |
| 58 instance*: VkInstance | |
| 59 deviceList*: seq[PhysicalDevice] | |
| 60 device*: Device | |
| 61 surface*: VkSurfaceKHR | |
| 62 surfaceFormat: VkSurfaceFormatKHR | |
| 63 frameDimension: VkExtent2D | |
| 64 swapchain: Swapchain | |
| 65 framebuffers: seq[VkFramebuffer] | |
| 66 renderPass*: VkRenderPass | |
| 67 pipeline*: RenderPipeline | |
| 68 commandPool*: VkCommandPool | |
| 69 commandBuffers*: array[MAX_FRAMES_IN_FLIGHT, VkCommandBuffer] | |
| 70 imageAvailableSemaphores*: array[MAX_FRAMES_IN_FLIGHT, VkSemaphore] | |
| 71 renderFinishedSemaphores*: array[MAX_FRAMES_IN_FLIGHT, VkSemaphore] | |
| 72 inFlightFences*: array[MAX_FRAMES_IN_FLIGHT, VkFence] | |
| 73 vertexBuffers: seq[(seq[Buffer], uint32)] | |
| 74 indexedVertexBuffers: seq[(seq[Buffer], Buffer, uint32, VkIndexType)] | |
| 75 Engine* = object | |
| 76 vulkan: Vulkan | |
| 77 window: NativeWindow | |
| 78 currentscenedata: ref Thing | |
| 79 | |
| 80 proc getAllPhysicalDevices(instance: VkInstance, surface: VkSurfaceKHR): seq[PhysicalDevice] = | |
| 81 for vulkanPhysicalDevice in getVulkanPhysicalDevices(instance): | |
| 82 var device = PhysicalDevice(device: vulkanPhysicalDevice, extensions: getDeviceExtensions(vulkanPhysicalDevice)) | |
| 83 vkGetPhysicalDeviceProperties(vulkanPhysicalDevice, addr(device.properties)) | |
| 84 vkGetPhysicalDeviceFeatures(vulkanPhysicalDevice, addr(device.features)) | |
| 85 device.formats = vulkanPhysicalDevice.getDeviceSurfaceFormats(surface) | |
| 86 device.presentModes = vulkanPhysicalDevice.getDeviceSurfacePresentModes(surface) | |
| 87 | |
| 88 debug(&"Physical device nr {int(vulkanPhysicalDevice)} {cleanString(device.properties.deviceName)}") | |
| 89 for i, queueFamilyProperty in enumerate(getQueueFamilies(vulkanPhysicalDevice)): | |
| 90 var hasSurfaceSupport: VkBool32 = VK_FALSE | |
| 91 checkVkResult vkGetPhysicalDeviceSurfaceSupportKHR(vulkanPhysicalDevice, uint32(i), surface, addr(hasSurfaceSupport)) | |
| 92 device.queueFamilies.add(QueueFamily(properties: queueFamilyProperty, hasSurfaceSupport: bool(hasSurfaceSupport))) | |
| 93 debug(&" Queue family {i} {queueFamilyProperty}") | |
| 94 | |
| 95 result.add(device) | |
| 96 | |
| 97 proc filterForDevice(devices: seq[PhysicalDevice]): seq[(PhysicalDevice, uint32, uint32)] = | |
| 98 for device in devices: | |
| 99 if not (device.formats.len > 0 and device.presentModes.len > 0 and "VK_KHR_swapchain" in device.extensions): | |
| 100 continue | |
| 101 var graphicsQueueFamily = high(uint32) | |
| 102 var presentationQueueFamily = high(uint32) | |
| 103 for i, queueFamily in enumerate(device.queueFamilies): | |
| 104 if queueFamily.hasSurfaceSupport: | |
| 105 presentationQueueFamily = uint32(i) | |
| 106 if bool(uint32(queueFamily.properties.queueFlags) and ord(VK_QUEUE_GRAPHICS_BIT)): | |
| 107 graphicsQueueFamily = uint32(i) | |
| 108 if graphicsQueueFamily != high(uint32) and presentationQueueFamily != high(uint32): | |
| 109 result.add((device, graphicsQueueFamily, presentationQueueFamily)) | |
| 110 | |
| 111 for (device, graphicsQueueFamily, presentationQueueFamily) in result: | |
| 112 debug(&"Viable device: {cleanString(device.properties.deviceName)} (graphics queue family {graphicsQueueFamily}, presentation queue family {presentationQueueFamily})") | |
| 113 | |
| 114 | |
| 115 proc getFrameDimension(window: NativeWindow, device: VkPhysicalDevice, surface: VkSurfaceKHR): VkExtent2D = | |
| 116 let capabilities = device.getSurfaceCapabilities(surface) | |
| 117 if capabilities.currentExtent.width != high(uint32): | |
| 118 return capabilities.currentExtent | |
| 119 else: | |
| 120 let (width, height) = window.size() | |
| 121 return VkExtent2D( | |
| 122 width: min(max(uint32(width), capabilities.minImageExtent.width), capabilities.maxImageExtent.width), | |
| 123 height: min(max(uint32(height), capabilities.minImageExtent.height), capabilities.maxImageExtent.height), | |
| 124 ) | |
| 125 | |
| 126 when DEBUG_LOG: | |
| 127 proc setupDebugLog(instance: VkInstance): VkDebugUtilsMessengerEXT = | |
| 128 var createInfo = VkDebugUtilsMessengerCreateInfoEXT( | |
| 129 sType: VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, | |
| 130 messageSeverity: VkDebugUtilsMessageSeverityFlagsEXT( | |
| 131 ord(VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) or | |
| 132 ord(VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) or | |
| 133 ord(VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) | |
| 134 ), | |
| 135 messageType: VkDebugUtilsMessageTypeFlagsEXT( | |
| 136 ord(VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) or | |
| 137 ord(VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) or | |
| 138 ord(VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) | |
| 139 ), | |
| 140 pfnUserCallback: debugCallback, | |
| 141 pUserData: nil, | |
| 142 ) | |
| 143 checkVkResult instance.vkCreateDebugUtilsMessengerEXT(addr(createInfo), nil, addr(result)) | |
| 144 | |
| 145 proc setupVulkanDeviceAndQueues(instance: VkInstance, surface: VkSurfaceKHR): Device = | |
| 146 let usableDevices = instance.getAllPhysicalDevices(surface).filterForDevice() | |
| 147 if len(usableDevices) == 0: | |
| 148 raise newException(Exception, "No suitable graphics device found") | |
| 149 result.physicalDevice = usableDevices[0][0] | |
| 150 result.graphicsQueueFamily = usableDevices[0][1] | |
| 151 result.presentationQueueFamily = usableDevices[0][2] | |
| 152 | |
| 153 debug(&"Chose device {cleanString(result.physicalDevice.properties.deviceName)}") | |
| 154 | |
| 155 (result.device, result.graphicsQueue, result.presentationQueue) = getVulcanDevice( | |
| 156 result.physicalDevice.device, | |
| 157 result.physicalDevice.features, | |
| 158 result.graphicsQueueFamily, | |
| 159 result.presentationQueueFamily, | |
| 160 ) | |
| 161 | |
| 162 proc setupSwapChain(device: VkDevice, physicalDevice: PhysicalDevice, surface: VkSurfaceKHR, dimension: VkExtent2D, surfaceFormat: VkSurfaceFormatKHR): Swapchain = | |
| 163 | |
| 164 let capabilities = physicalDevice.device.getSurfaceCapabilities(surface) | |
| 165 var selectedPresentationMode = getPresentMode(physicalDevice.presentModes) | |
| 166 # setup swapchain | |
| 167 var swapchainCreateInfo = VkSwapchainCreateInfoKHR( | |
| 168 sType: VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, | |
| 169 surface: surface, | |
| 170 minImageCount: max(capabilities.minImageCount + 1, capabilities.maxImageCount), | |
| 171 imageFormat: surfaceFormat.format, | |
| 172 imageColorSpace: surfaceFormat.colorSpace, | |
| 173 imageExtent: dimension, | |
| 174 imageArrayLayers: 1, | |
| 175 imageUsage: VkImageUsageFlags(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT), | |
| 176 # VK_SHARING_MODE_CONCURRENT no supported (i.e cannot use different queue families for drawing to swap surface?) | |
| 177 imageSharingMode: VK_SHARING_MODE_EXCLUSIVE, | |
| 178 preTransform: capabilities.currentTransform, | |
| 179 compositeAlpha: VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, | |
| 180 presentMode: selectedPresentationMode, | |
| 181 clipped: VK_TRUE, | |
| 182 oldSwapchain: VkSwapchainKHR(0), | |
| 183 ) | |
| 184 checkVkResult device.vkCreateSwapchainKHR(addr(swapchainCreateInfo), nil, addr(result.swapchain)) | |
| 185 result.images = device.getSwapChainImages(result.swapchain) | |
| 186 | |
| 187 # setup swapchian image views | |
| 188 | |
| 189 result.imageviews = newSeq[VkImageView](result.images.len) | |
| 190 for i, image in enumerate(result.images): | |
| 191 var imageViewCreateInfo = VkImageViewCreateInfo( | |
| 192 sType: VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, | |
| 193 image: image, | |
| 194 viewType: VK_IMAGE_VIEW_TYPE_2D, | |
| 195 format: surfaceFormat.format, | |
| 196 components: VkComponentMapping( | |
| 197 r: VK_COMPONENT_SWIZZLE_IDENTITY, | |
| 198 g: VK_COMPONENT_SWIZZLE_IDENTITY, | |
| 199 b: VK_COMPONENT_SWIZZLE_IDENTITY, | |
| 200 a: VK_COMPONENT_SWIZZLE_IDENTITY, | |
| 201 ), | |
| 202 subresourceRange: VkImageSubresourceRange( | |
| 203 aspectMask: VkImageAspectFlags(VK_IMAGE_ASPECT_COLOR_BIT), | |
| 204 baseMipLevel: 0, | |
| 205 levelCount: 1, | |
| 206 baseArrayLayer: 0, | |
| 207 layerCount: 1, | |
| 208 ), | |
| 209 ) | |
| 210 checkVkResult device.vkCreateImageView(addr(imageViewCreateInfo), nil, addr(result.imageviews[i])) | |
| 211 | |
| 212 proc setupRenderPass(device: VkDevice, format: VkFormat): VkRenderPass = | |
| 213 var | |
| 214 colorAttachment = VkAttachmentDescription( | |
| 215 format: format, | |
| 216 samples: VK_SAMPLE_COUNT_1_BIT, | |
| 217 loadOp: VK_ATTACHMENT_LOAD_OP_CLEAR, | |
| 218 storeOp: VK_ATTACHMENT_STORE_OP_STORE, | |
| 219 stencilLoadOp: VK_ATTACHMENT_LOAD_OP_DONT_CARE, | |
| 220 stencilStoreOp: VK_ATTACHMENT_STORE_OP_DONT_CARE, | |
| 221 initialLayout: VK_IMAGE_LAYOUT_UNDEFINED, | |
| 222 finalLayout: VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, | |
| 223 ) | |
| 224 colorAttachmentRef = VkAttachmentReference( | |
| 225 attachment: 0, | |
| 226 layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, | |
| 227 ) | |
| 228 subpass = VkSubpassDescription( | |
| 229 pipelineBindPoint: VK_PIPELINE_BIND_POINT_GRAPHICS, | |
| 230 colorAttachmentCount: 1, | |
| 231 pColorAttachments: addr(colorAttachmentRef) | |
| 232 ) | |
| 233 dependency = VkSubpassDependency( | |
| 234 srcSubpass: VK_SUBPASS_EXTERNAL, | |
| 235 dstSubpass: 0, | |
| 236 srcStageMask: VkPipelineStageFlags(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT), | |
| 237 srcAccessMask: VkAccessFlags(0), | |
| 238 dstStageMask: VkPipelineStageFlags(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT), | |
| 239 dstAccessMask: VkAccessFlags(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT), | |
| 240 ) | |
| 241 renderPassCreateInfo = VkRenderPassCreateInfo( | |
| 242 sType: VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, | |
| 243 attachmentCount: 1, | |
| 244 pAttachments: addr(colorAttachment), | |
| 245 subpassCount: 1, | |
| 246 pSubpasses: addr(subpass), | |
| 247 dependencyCount: 1, | |
| 248 pDependencies: addr(dependency), | |
| 249 ) | |
| 250 checkVkResult device.vkCreateRenderPass(addr(renderPassCreateInfo), nil, addr(result)) | |
| 251 | |
| 252 proc setupRenderPipeline[T](device: VkDevice, frameDimension: VkExtent2D, renderPass: VkRenderPass, vertexShader, fragmentShader: string): RenderPipeline = | |
| 253 # load shaders | |
| 254 result.shaders.add(device.initShaderProgram(VK_SHADER_STAGE_VERTEX_BIT, vertexShader)) | |
| 255 result.shaders.add(device.initShaderProgram(VK_SHADER_STAGE_FRAGMENT_BIT, fragmentShader)) | |
| 256 | |
| 257 var | |
| 258 # define which parts can be dynamic (pipeline is fixed after setup) | |
| 259 dynamicStates = [VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR] | |
| 260 dynamicState = VkPipelineDynamicStateCreateInfo( | |
| 261 sType: VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, | |
| 262 dynamicStateCount: uint32(dynamicStates.len), | |
| 263 pDynamicStates: addr(dynamicStates[0]), | |
| 264 ) | |
| 265 vertexbindings = generateInputVertexBinding[T]() | |
| 266 attributebindings = generateInputAttributeBinding[T]() | |
| 267 | |
| 268 # define input data format | |
| 269 vertexInputInfo = VkPipelineVertexInputStateCreateInfo( | |
| 270 sType: VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, | |
| 271 vertexBindingDescriptionCount: uint32(vertexbindings.len), | |
| 272 pVertexBindingDescriptions: addr(vertexbindings[0]), | |
| 273 vertexAttributeDescriptionCount: uint32(attributebindings.len), | |
| 274 pVertexAttributeDescriptions: addr(attributebindings[0]), | |
| 275 ) | |
| 276 inputAssembly = VkPipelineInputAssemblyStateCreateInfo( | |
| 277 sType: VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, | |
| 278 topology: VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, | |
| 279 primitiveRestartEnable: VK_FALSE, | |
| 280 ) | |
| 281 | |
| 282 # setup viewport | |
| 283 var viewportState = VkPipelineViewportStateCreateInfo( | |
| 284 sType: VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, | |
| 285 viewportCount: 1, | |
| 286 scissorCount: 1, | |
| 287 ) | |
| 288 | |
| 289 # rasterizerization config | |
| 290 var | |
| 291 rasterizer = VkPipelineRasterizationStateCreateInfo( | |
| 292 sType: VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, | |
| 293 depthClampEnable: VK_FALSE, | |
| 294 rasterizerDiscardEnable: VK_FALSE, | |
| 295 polygonMode: VK_POLYGON_MODE_FILL, | |
| 296 lineWidth: 1.0, | |
| 297 cullMode: VkCullModeFlags(VK_CULL_MODE_BACK_BIT), | |
| 298 frontFace: VK_FRONT_FACE_CLOCKWISE, | |
| 299 depthBiasEnable: VK_FALSE, | |
| 300 depthBiasConstantFactor: 0.0, | |
| 301 depthBiasClamp: 0.0, | |
| 302 depthBiasSlopeFactor: 0.0, | |
| 303 ) | |
| 304 multisampling = VkPipelineMultisampleStateCreateInfo( | |
| 305 sType: VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, | |
| 306 sampleShadingEnable: VK_FALSE, | |
| 307 rasterizationSamples: VK_SAMPLE_COUNT_1_BIT, | |
| 308 minSampleShading: 1.0, | |
| 309 pSampleMask: nil, | |
| 310 alphaToCoverageEnable: VK_FALSE, | |
| 311 alphaToOneEnable: VK_FALSE, | |
| 312 ) | |
| 313 colorBlendAttachment = VkPipelineColorBlendAttachmentState( | |
| 314 colorWriteMask: VkColorComponentFlags( | |
| 315 ord(VK_COLOR_COMPONENT_R_BIT) or | |
| 316 ord(VK_COLOR_COMPONENT_G_BIT) or | |
| 317 ord(VK_COLOR_COMPONENT_B_BIT) or | |
| 318 ord(VK_COLOR_COMPONENT_A_BIT) | |
| 319 ), | |
| 320 blendEnable: VK_TRUE, | |
| 321 srcColorBlendFactor: VK_BLEND_FACTOR_SRC_ALPHA, | |
| 322 dstColorBlendFactor: VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, | |
| 323 colorBlendOp: VK_BLEND_OP_ADD, | |
| 324 srcAlphaBlendFactor: VK_BLEND_FACTOR_ONE, | |
| 325 dstAlphaBlendFactor: VK_BLEND_FACTOR_ZERO, | |
| 326 alphaBlendOp: VK_BLEND_OP_ADD, | |
| 327 ) | |
| 328 colorBlending = VkPipelineColorBlendStateCreateInfo( | |
| 329 sType: VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, | |
| 330 logicOpEnable: VK_TRUE, | |
| 331 logicOp: VK_LOGIC_OP_COPY, | |
| 332 attachmentCount: 1, | |
| 333 pAttachments: addr(colorBlendAttachment), | |
| 334 blendConstants: [0.0'f, 0.0'f, 0.0'f, 0.0'f], | |
| 335 ) | |
| 336 | |
| 337 # create pipeline | |
| 338 pipelineLayoutInfo = VkPipelineLayoutCreateInfo( | |
| 339 sType: VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, | |
| 340 setLayoutCount: 0, | |
| 341 pSetLayouts: nil, | |
| 342 pushConstantRangeCount: 0, | |
| 343 pPushConstantRanges: nil, | |
| 344 ) | |
| 345 checkVkResult vkCreatePipelineLayout(device, addr(pipelineLayoutInfo), nil, addr(result.layout)) | |
| 346 | |
| 347 var stages: seq[VkPipelineShaderStageCreateInfo] | |
| 348 for shader in result.shaders: | |
| 349 stages.add(shader.shader) | |
| 350 var pipelineInfo = VkGraphicsPipelineCreateInfo( | |
| 351 sType: VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, | |
| 352 stageCount: uint32(stages.len), | |
| 353 pStages: addr(stages[0]), | |
| 354 pVertexInputState: addr(vertexInputInfo), | |
| 355 pInputAssemblyState: addr(inputAssembly), | |
| 356 pViewportState: addr(viewportState), | |
| 357 pRasterizationState: addr(rasterizer), | |
| 358 pMultisampleState: addr(multisampling), | |
| 359 pDepthStencilState: nil, | |
| 360 pColorBlendState: addr(colorBlending), | |
| 361 pDynamicState: addr(dynamicState), | |
| 362 layout: result.layout, | |
| 363 renderPass: renderPass, | |
| 364 subpass: 0, | |
| 365 basePipelineHandle: VkPipeline(0), | |
| 366 basePipelineIndex: -1, | |
| 367 ) | |
| 368 checkVkResult vkCreateGraphicsPipelines( | |
| 369 device, | |
| 370 VkPipelineCache(0), | |
| 371 1, | |
| 372 addr(pipelineInfo), | |
| 373 nil, | |
| 374 addr(result.pipeline) | |
| 375 ) | |
| 376 | |
| 377 proc setupFramebuffers(device: VkDevice, swapchain: var Swapchain, renderPass: VkRenderPass, dimension: VkExtent2D): seq[VkFramebuffer] = | |
| 378 result = newSeq[VkFramebuffer](swapchain.images.len) | |
| 379 for i, imageview in enumerate(swapchain.imageviews): | |
| 380 var framebufferInfo = VkFramebufferCreateInfo( | |
| 381 sType: VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, | |
| 382 renderPass: renderPass, | |
| 383 attachmentCount: 1, | |
| 384 pAttachments: addr(swapchain.imageviews[i]), | |
| 385 width: dimension.width, | |
| 386 height: dimension.height, | |
| 387 layers: 1, | |
| 388 ) | |
| 389 checkVkResult device.vkCreateFramebuffer(addr(framebufferInfo), nil, addr(result[i])) | |
| 390 | |
| 391 proc trash(device: VkDevice, swapchain: Swapchain, framebuffers: seq[VkFramebuffer]) = | |
| 392 for framebuffer in framebuffers: | |
| 393 device.vkDestroyFramebuffer(framebuffer, nil) | |
| 394 for imageview in swapchain.imageviews: | |
| 395 device.vkDestroyImageView(imageview, nil) | |
| 396 device.vkDestroySwapchainKHR(swapchain.swapchain, nil) | |
| 397 | |
| 398 proc recreateSwapchain(vulkan: Vulkan): (Swapchain, seq[VkFramebuffer]) = | |
| 399 debug(&"Recreate swapchain with dimension {vulkan.frameDimension}") | |
| 400 checkVkResult vulkan.device.device.vkDeviceWaitIdle() | |
| 401 | |
| 402 vulkan.device.device.trash(vulkan.swapchain, vulkan.framebuffers) | |
| 403 | |
| 404 result[0] = vulkan.device.device.setupSwapChain( | |
| 405 vulkan.device.physicalDevice, | |
| 406 vulkan.surface, | |
| 407 vulkan.frameDimension, | |
| 408 vulkan.surfaceFormat | |
| 409 ) | |
| 410 result[1] = vulkan.device.device.setupFramebuffers( | |
| 411 result[0], | |
| 412 vulkan.renderPass, | |
| 413 vulkan.frameDimension | |
| 414 ) | |
| 415 | |
| 416 | |
| 417 proc setupCommandBuffers(device: VkDevice, graphicsQueueFamily: uint32): (VkCommandPool, array[MAX_FRAMES_IN_FLIGHT, VkCommandBuffer]) = | |
| 418 # set up command buffer | |
| 419 var poolInfo = VkCommandPoolCreateInfo( | |
| 420 sType: VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, | |
| 421 flags: VkCommandPoolCreateFlags(VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT), | |
| 422 queueFamilyIndex: graphicsQueueFamily, | |
| 423 ) | |
| 424 checkVkResult device.vkCreateCommandPool(addr(poolInfo), nil, addr(result[0])) | |
| 425 | |
| 426 var allocInfo = VkCommandBufferAllocateInfo( | |
| 427 sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, | |
| 428 commandPool: result[0], | |
| 429 level: VK_COMMAND_BUFFER_LEVEL_PRIMARY, | |
| 430 commandBufferCount: result[1].len.uint32, | |
| 431 ) | |
| 432 checkVkResult device.vkAllocateCommandBuffers(addr(allocInfo), addr(result[1][0])) | |
| 433 | |
| 434 proc setupSyncPrimitives(device: VkDevice): ( | |
| 435 array[MAX_FRAMES_IN_FLIGHT, VkSemaphore], | |
| 436 array[MAX_FRAMES_IN_FLIGHT, VkSemaphore], | |
| 437 array[MAX_FRAMES_IN_FLIGHT, VkFence], | |
| 438 ) = | |
| 439 var semaphoreInfo = VkSemaphoreCreateInfo(sType: VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO) | |
| 440 var fenceInfo = VkFenceCreateInfo( | |
| 441 sType: VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, | |
| 442 flags: VkFenceCreateFlags(VK_FENCE_CREATE_SIGNALED_BIT) | |
| 443 ) | |
| 444 for i in 0 ..< MAX_FRAMES_IN_FLIGHT: | |
| 445 checkVkResult device.vkCreateSemaphore(addr(semaphoreInfo), nil, addr(result[0][i])) | |
| 446 checkVkResult device.vkCreateSemaphore(addr(semaphoreInfo), nil, addr(result[1][i])) | |
| 447 checkVkResult device.vkCreateFence(addr(fenceInfo), nil, addr(result[2][i])) | |
| 448 | |
| 449 proc igniteEngine*(): Engine = | |
| 450 | |
| 451 result.window = createWindow("Hello triangle") | |
| 452 | |
| 453 # setup vulkan functions | |
| 454 vkLoad1_0() | |
| 455 vkLoad1_1() | |
| 456 vkLoad1_2() | |
| 457 | |
| 458 checkGlslangResult glslang_initialize_process() | |
| 459 | |
| 460 # create vulkan instance | |
| 461 result.vulkan.instance = createVulkanInstance(VULKAN_VERSION) | |
| 462 when DEBUG_LOG: | |
| 463 result.vulkan.debugMessenger = result.vulkan.instance.setupDebugLog() | |
| 464 result.vulkan.surface = result.vulkan.instance.createVulkanSurface(result.window) | |
| 465 result.vulkan.device = result.vulkan.instance.setupVulkanDeviceAndQueues(result.vulkan.surface) | |
| 466 | |
| 467 # get basic frame information | |
| 468 result.vulkan.surfaceFormat = result.vulkan.device.physicalDevice.formats.getSuitableSurfaceFormat() | |
| 469 result.vulkan.frameDimension = result.window.getFrameDimension(result.vulkan.device.physicalDevice.device, result.vulkan.surface) | |
| 470 | |
| 471 # setup swapchain and render pipeline | |
| 472 result.vulkan.swapchain = result.vulkan.device.device.setupSwapChain( | |
| 473 result.vulkan.device.physicalDevice, | |
| 474 result.vulkan.surface, | |
| 475 result.vulkan.frameDimension, | |
| 476 result.vulkan.surfaceFormat | |
| 477 ) | |
| 478 result.vulkan.renderPass = result.vulkan.device.device.setupRenderPass(result.vulkan.surfaceFormat.format) | |
| 479 result.vulkan.framebuffers = result.vulkan.device.device.setupFramebuffers( | |
| 480 result.vulkan.swapchain, | |
| 481 result.vulkan.renderPass, | |
| 482 result.vulkan.frameDimension | |
| 483 ) | |
| 484 ( | |
| 485 result.vulkan.commandPool, | |
| 486 result.vulkan.commandBuffers, | |
| 487 ) = result.vulkan.device.device.setupCommandBuffers(result.vulkan.device.graphicsQueueFamily) | |
| 488 | |
| 489 ( | |
| 490 result.vulkan.imageAvailableSemaphores, | |
| 491 result.vulkan.renderFinishedSemaphores, | |
| 492 result.vulkan.inFlightFences, | |
| 493 ) = result.vulkan.device.device.setupSyncPrimitives() | |
| 494 | |
| 495 | |
| 496 proc setupPipeline*[T: object, U: uint16|uint32](engine: var Engine, scenedata: ref Thing, vertexShader, fragmentShader: string) = | |
| 497 engine.currentscenedata = scenedata | |
| 498 engine.vulkan.pipeline = setupRenderPipeline[T]( | |
| 499 engine.vulkan.device.device, | |
| 500 engine.vulkan.frameDimension, | |
| 501 engine.vulkan.renderPass, | |
| 502 vertexShader, | |
| 503 fragmentShader, | |
| 504 ) | |
| 505 for mesh in partsOfType[ref Mesh[T]](engine.currentscenedata): | |
| 506 engine.vulkan.vertexBuffers.add createVertexBuffers(mesh[], engine.vulkan.device.device, engine.vulkan.device.physicalDevice.device, engine.vulkan.commandPool, engine.vulkan.device.graphicsQueue) | |
| 507 for mesh in partsOfType[ref IndexedMesh[T, U]](engine.currentscenedata): | |
| 508 engine.vulkan.indexedVertexBuffers.add createIndexedVertexBuffers(mesh[], engine.vulkan.device.device, engine.vulkan.device.physicalDevice.device, engine.vulkan.commandPool, engine.vulkan.device.graphicsQueue) | |
| 509 | |
| 510 proc recordCommandBuffer(renderPass: VkRenderPass, pipeline: VkPipeline, commandBuffer: VkCommandBuffer, framebuffer: VkFramebuffer, frameDimension: VkExtent2D, engine: var Engine) = | |
| 511 var | |
| 512 beginInfo = VkCommandBufferBeginInfo( | |
| 513 sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, | |
| 514 pInheritanceInfo: nil, | |
| 515 ) | |
| 516 clearColor = VkClearValue(color: VkClearColorValue(float32: [0.2'f, 0.2'f, 0.2'f, 1.0'f])) | |
| 517 renderPassInfo = VkRenderPassBeginInfo( | |
| 518 sType: VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, | |
| 519 renderPass: renderPass, | |
| 520 framebuffer: framebuffer, | |
| 521 renderArea: VkRect2D( | |
| 522 offset: VkOffset2D(x: 0, y: 0), | |
| 523 extent: frameDimension, | |
| 524 ), | |
| 525 clearValueCount: 1, | |
| 526 pClearValues: addr(clearColor), | |
| 527 ) | |
| 528 viewport = VkViewport( | |
| 529 x: 0.0, | |
| 530 y: 0.0, | |
| 531 width: (float) frameDimension.width, | |
| 532 height: (float) frameDimension.height, | |
| 533 minDepth: 0.0, | |
| 534 maxDepth: 1.0, | |
| 535 ) | |
| 536 scissor = VkRect2D( | |
| 537 offset: VkOffset2D(x: 0, y: 0), | |
| 538 extent: frameDimension | |
| 539 ) | |
| 540 checkVkResult commandBuffer.vkBeginCommandBuffer(addr(beginInfo)) | |
| 541 commandBuffer.vkCmdBeginRenderPass(addr(renderPassInfo), VK_SUBPASS_CONTENTS_INLINE) | |
| 542 commandBuffer.vkCmdSetViewport(firstViewport=0, viewportCount=1, addr(viewport)) | |
| 543 commandBuffer.vkCmdSetScissor(firstScissor=0, scissorCount=1, addr(scissor)) | |
| 544 commandBuffer.vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline) | |
| 545 | |
| 546 for (vertexBufferSet, vertexCount) in engine.vulkan.vertexBuffers: | |
| 547 var | |
| 548 vertexBuffers: seq[VkBuffer] | |
| 549 offsets: seq[VkDeviceSize] | |
| 550 for buffer in vertexBufferSet: | |
| 551 vertexBuffers.add buffer.vkBuffer | |
| 552 offsets.add VkDeviceSize(0) | |
| 553 | |
| 554 commandBuffer.vkCmdBindVertexBuffers(firstBinding=0'u32, bindingCount=2'u32, pBuffers=addr(vertexBuffers[0]), pOffsets=addr(offsets[0])) | |
| 555 commandBuffer.vkCmdDraw(vertexCount=vertexCount, instanceCount=1'u32, firstVertex=0'u32, firstInstance=0'u32) | |
| 556 | |
| 557 for (vertexBufferSet, indexBuffer, indicesCount, indexType) in engine.vulkan.indexedVertexBuffers: | |
| 558 var | |
| 559 vertexBuffers: seq[VkBuffer] | |
| 560 offsets: seq[VkDeviceSize] | |
| 561 for buffer in vertexBufferSet: | |
| 562 vertexBuffers.add buffer.vkBuffer | |
| 563 offsets.add VkDeviceSize(0) | |
| 564 | |
| 565 commandBuffer.vkCmdBindVertexBuffers(firstBinding=0'u32, bindingCount=2'u32, pBuffers=addr(vertexBuffers[0]), pOffsets=addr(offsets[0])) | |
| 566 commandBuffer.vkCmdBindIndexBuffer(indexBuffer.vkBuffer, VkDeviceSize(0), indexType); | |
| 567 commandBuffer.vkCmdDrawIndexed(indicesCount, 1, 0, 0, 0); | |
| 568 commandBuffer.vkCmdEndRenderPass() | |
| 569 checkVkResult commandBuffer.vkEndCommandBuffer() | |
| 570 | |
| 571 proc drawFrame(window: NativeWindow, vulkan: var Vulkan, currentFrame: int, resized: bool, engine: var Engine) = | |
| 572 checkVkResult vulkan.device.device.vkWaitForFences(1, addr(vulkan.inFlightFences[currentFrame]), VK_TRUE, high(uint64)) | |
| 573 var bufferImageIndex: uint32 | |
| 574 let nextImageResult = vulkan.device.device.vkAcquireNextImageKHR( | |
| 575 vulkan.swapchain.swapchain, | |
| 576 high(uint64), | |
| 577 vulkan.imageAvailableSemaphores[currentFrame], | |
| 578 VkFence(0), | |
| 579 addr(bufferImageIndex) | |
| 580 ) | |
| 581 if nextImageResult == VK_ERROR_OUT_OF_DATE_KHR: | |
| 582 vulkan.frameDimension = window.getFrameDimension(vulkan.device.physicalDevice.device, vulkan.surface) | |
| 583 (vulkan.swapchain, vulkan.framebuffers) = vulkan.recreateSwapchain() | |
| 584 elif not (nextImageResult in [VK_SUCCESS, VK_SUBOPTIMAL_KHR]): | |
| 585 raise newException(Exception, "Vulkan error: vkAcquireNextImageKHR returned " & $nextImageResult) | |
| 586 checkVkResult vulkan.device.device.vkResetFences(1, addr(vulkan.inFlightFences[currentFrame])) | |
| 587 | |
| 588 checkVkResult vulkan.commandBuffers[currentFrame].vkResetCommandBuffer(VkCommandBufferResetFlags(0)) | |
| 589 vulkan.renderPass.recordCommandBuffer(vulkan.pipeline.pipeline, vulkan.commandBuffers[currentFrame], vulkan.framebuffers[bufferImageIndex], vulkan.frameDimension, engine) | |
| 590 var | |
| 591 waitSemaphores = [vulkan.imageAvailableSemaphores[currentFrame]] | |
| 592 waitStages = [VkPipelineStageFlags(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT)] | |
| 593 signalSemaphores = [vulkan.renderFinishedSemaphores[currentFrame]] | |
| 594 submitInfo = VkSubmitInfo( | |
| 595 sType: VK_STRUCTURE_TYPE_SUBMIT_INFO, | |
| 596 waitSemaphoreCount: 1, | |
| 597 pWaitSemaphores: addr(waitSemaphores[0]), | |
| 598 pWaitDstStageMask: addr(waitStages[0]), | |
| 599 commandBufferCount: 1, | |
| 600 pCommandBuffers: addr(vulkan.commandBuffers[currentFrame]), | |
| 601 signalSemaphoreCount: 1, | |
| 602 pSignalSemaphores: addr(signalSemaphores[0]), | |
| 603 ) | |
| 604 checkVkResult vkQueueSubmit(vulkan.device.graphicsQueue, 1, addr(submitInfo), vulkan.inFlightFences[currentFrame]) | |
| 605 | |
| 606 var presentInfo = VkPresentInfoKHR( | |
| 607 sType: VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, | |
| 608 waitSemaphoreCount: 1, | |
| 609 pWaitSemaphores: addr(signalSemaphores[0]), | |
| 610 swapchainCount: 1, | |
| 611 pSwapchains: addr(vulkan.swapchain.swapchain), | |
| 612 pImageIndices: addr(bufferImageIndex), | |
| 613 pResults: nil, | |
| 614 ) | |
| 615 let presentResult = vkQueuePresentKHR(vulkan.device.presentationQueue, addr(presentInfo)) | |
| 616 | |
| 617 if presentResult == VK_ERROR_OUT_OF_DATE_KHR or presentResult == VK_SUBOPTIMAL_KHR or resized: | |
| 618 vulkan.frameDimension = window.getFrameDimension(vulkan.device.physicalDevice.device, vulkan.surface) | |
| 619 (vulkan.swapchain, vulkan.framebuffers) = vulkan.recreateSwapchain() | |
| 620 | |
| 621 | |
| 622 proc fullThrottle*(engine: var Engine) = | |
| 623 var | |
| 624 killed = false | |
| 625 currentFrame = 0 | |
| 626 resized = false | |
| 627 | |
| 628 while not killed: | |
| 629 for event in engine.window.pendingEvents(): | |
| 630 case event.eventType: | |
| 631 of Quit: | |
| 632 killed = true | |
| 633 of ResizedWindow: | |
| 634 resized = true | |
| 635 of KeyDown: | |
| 636 echo event | |
| 637 if event.key == Escape: | |
| 638 killed = true | |
| 639 else: | |
| 640 discard | |
| 641 engine.window.drawFrame(engine.vulkan, currentFrame, resized, engine) | |
| 642 resized = false | |
| 643 currentFrame = (currentFrame + 1) mod MAX_FRAMES_IN_FLIGHT; | |
| 644 checkVkResult engine.vulkan.device.device.vkDeviceWaitIdle() | |
| 645 | |
| 646 proc trash*(engine: var Engine) = | |
| 647 for (bufferset, cnt) in engine.vulkan.vertexBuffers.mitems: | |
| 648 for buffer in bufferset.mitems: | |
| 649 buffer.trash() | |
| 650 for (bufferset, indexbuffer, cnt, t) in engine.vulkan.indexedVertexBuffers.mitems: | |
| 651 indexbuffer.trash() | |
| 652 for buffer in bufferset.mitems: | |
| 653 buffer.trash() | |
| 654 engine.vulkan.device.device.trash(engine.vulkan.swapchain, engine.vulkan.framebuffers) | |
| 655 checkVkResult engine.vulkan.device.device.vkDeviceWaitIdle() | |
| 656 | |
| 657 for i in 0 ..< MAX_FRAMES_IN_FLIGHT: | |
| 658 engine.vulkan.device.device.vkDestroySemaphore(engine.vulkan.imageAvailableSemaphores[i], nil) | |
| 659 engine.vulkan.device.device.vkDestroySemaphore(engine.vulkan.renderFinishedSemaphores[i], nil) | |
| 660 engine.vulkan.device.device.vkDestroyFence(engine.vulkan.inFlightFences[i], nil) | |
| 661 | |
| 662 engine.vulkan.device.device.vkDestroyCommandPool(engine.vulkan.commandPool, nil) | |
| 663 engine.vulkan.device.device.vkDestroyPipeline(engine.vulkan.pipeline.pipeline, nil) | |
| 664 engine.vulkan.device.device.vkDestroyPipelineLayout(engine.vulkan.pipeline.layout, nil) | |
| 665 engine.vulkan.device.device.vkDestroyRenderPass(engine.vulkan.renderPass, nil) | |
| 666 | |
| 667 for shader in engine.vulkan.pipeline.shaders: | |
| 668 engine.vulkan.device.device.vkDestroyShaderModule(shader.shader.module, nil) | |
| 669 | |
| 670 engine.vulkan.instance.vkDestroySurfaceKHR(engine.vulkan.surface, nil) | |
| 671 engine.vulkan.device.device.vkDestroyDevice(nil) | |
| 672 when DEBUG_LOG: | |
| 673 engine.vulkan.instance.vkDestroyDebugUtilsMessengerEXT(engine.vulkan.debugMessenger, nil) | |
| 674 glslang_finalize_process() | |
| 675 engine.window.trash() | |
| 676 engine.vulkan.instance.vkDestroyInstance(nil) |
