comparison src/zamikongine/engine.nim @ 19:b55d6ecde79d

did: introduce scene graph, meshs and generic vertex buffers
author Sam <sam@basx.dev>
date Mon, 09 Jan 2023 11:04:19 +0700
parents src/engine.nim@90e117952f74
children beb86492b178
comparison
equal deleted inserted replaced
18:90e117952f74 19:b55d6ecde79d
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)