diff semiconginev2/rendering/swapchain.nim @ 1218:56781cc0fc7c compiletime-tests

did: renamge main package
author sam <sam@basx.dev>
date Wed, 17 Jul 2024 21:01:37 +0700
parents semicongine/rendering/swapchain.nim@e2901100a596
children 5dcb503ef0c0
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/semiconginev2/rendering/swapchain.nim	Wed Jul 17 21:01:37 2024 +0700
@@ -0,0 +1,235 @@
+const N_FRAMEBUFFERS = 3'u32
+
+proc InitSwapchain*(
+  renderPass: VkRenderPass,
+  vSync: bool = false,
+  samples = VK_SAMPLE_COUNT_1_BIT,
+  oldSwapchain: ref Swapchain = nil,
+): Option[Swapchain] =
+  assert vulkan.instance.Valid, "Vulkan not initialized"
+
+  var capabilities: VkSurfaceCapabilitiesKHR
+  checkVkResult vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vulkan.physicalDevice, vulkan.surface, addr(capabilities))
+  let
+    format = DefaultSurfaceFormat()
+    width = capabilities.currentExtent.width
+    height = capabilities.currentExtent.height
+
+  if width == 0 or height == 0:
+    return none(Swapchain)
+
+  # following "count" is established according to vulkan specs
+  var minFramebufferCount = N_FRAMEBUFFERS
+  minFramebufferCount = max(minFramebufferCount, capabilities.minImageCount)
+  if capabilities.maxImageCount != 0:
+    minFramebufferCount = min(minFramebufferCount, capabilities.maxImageCount)
+
+  # create swapchain
+  let hasTripleBuffering = VK_PRESENT_MODE_MAILBOX_KHR in svkGetPhysicalDeviceSurfacePresentModesKHR()
+  var swapchainCreateInfo = VkSwapchainCreateInfoKHR(
+    sType: VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
+    surface: vulkan.surface,
+    minImageCount: minFramebufferCount,
+    imageFormat: format,
+    imageColorSpace: VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, # only one supported without special extensions
+    imageExtent: capabilities.currentExtent,
+    imageArrayLayers: 1,
+    imageUsage: toBits [VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT],
+    imageSharingMode: VK_SHARING_MODE_EXCLUSIVE,
+    preTransform: capabilities.currentTransform,
+    compositeAlpha: VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,  # only used for blending with other windows, can be opaque
+    presentMode: if (vSync or not hasTripleBuffering): VK_PRESENT_MODE_FIFO_KHR else: VK_PRESENT_MODE_MAILBOX_KHR,
+    clipped: true,
+    oldSwapchain: if oldSwapchain != nil: oldSwapchain.vk else: VkSwapchainKHR(0),
+  )
+  var swapchain: Swapchain
+  if vkCreateSwapchainKHR(vulkan.device, addr(swapchainCreateInfo), nil, addr(swapchain.vk)) != VK_SUCCESS:
+    return none(Swapchain)
+
+  swapchain.width = width
+  swapchain.height = height
+  swapchain.renderPass = renderPass
+  swapchain.vSync = vSync
+  swapchain.samples = samples
+  swapchain.oldSwapchain = oldSwapchain
+  if swapchain.oldSwapchain != nil:
+    swapchain.oldSwapchainCounter = INFLIGHTFRAMES.int * 2
+
+  # create msaa image+view if desired
+  if samples != VK_SAMPLE_COUNT_1_BIT:
+    swapchain.msaaImage = svkCreate2DImage(
+      width = width,
+      height = height,
+      format = format,
+      usage = [VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT],
+      samples = samples,
+    )
+    let requirements = svkGetImageMemoryRequirements(swapchain.msaaImage)
+    swapchain.msaaMemory = svkAllocateMemory(
+      requirements.size,
+      BestMemory(mappable = false, filter = requirements.memoryTypes)
+    )
+    checkVkResult vkBindImageMemory(
+      vulkan.device,
+      swapchain.msaaImage,
+      swapchain.msaaMemory,
+      0,
+    )
+    swapchain.msaaImageView = svkCreate2DImageView(swapchain.msaaImage, format)
+
+  # create framebuffers
+  var actualNFramebuffers: uint32
+  checkVkResult vkGetSwapchainImagesKHR(vulkan.device, swapchain.vk, addr(actualNFramebuffers), nil)
+  var framebuffers = newSeq[VkImage](actualNFramebuffers)
+  checkVkResult vkGetSwapchainImagesKHR(vulkan.device, swapchain.vk, addr(actualNFramebuffers), framebuffers.ToCPointer)
+
+  for framebuffer in framebuffers:
+    swapchain.framebufferViews.add svkCreate2DImageView(framebuffer, format)
+    if samples == VK_SAMPLE_COUNT_1_BIT:
+      swapchain.framebuffers.add svkCreateFramebuffer(renderPass, width, height, [swapchain.framebufferViews[^1]])
+    else:
+      swapchain.framebuffers.add svkCreateFramebuffer(renderPass, width, height, [swapchain.msaaImageView, swapchain.framebufferViews[^1]])
+
+  # create sync primitives
+  for i in 0 ..< INFLIGHTFRAMES:
+    swapchain.queueFinishedFence[i] = svkCreateFence(signaled = true)
+    swapchain.imageAvailableSemaphore[i] = svkCreateSemaphore()
+    swapchain.renderFinishedSemaphore[i] = svkCreateSemaphore()
+
+  # command buffers
+  var commandPoolCreateInfo = VkCommandPoolCreateInfo(
+    sType: VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
+    flags: toBits [VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT],
+    queueFamilyIndex: vulkan.graphicsQueueFamily,
+  )
+  checkVkResult vkCreateCommandPool(vulkan.device, addr(commandPoolCreateInfo), nil, addr(swapchain.commandBufferPool))
+  var allocInfo = VkCommandBufferAllocateInfo(
+    sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
+    commandPool: swapchain.commandBufferPool,
+    level: VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+    commandBufferCount: INFLIGHTFRAMES,
+  )
+  checkVkResult vkAllocateCommandBuffers(vulkan.device, addr(allocInfo), swapchain.commandBuffers.ToCPointer)
+
+  return some(swapchain)
+
+proc DestroySwapchain*(swapchain: Swapchain) =
+
+  if swapchain.samples != VK_SAMPLE_COUNT_1_BIT:
+    vkDestroyImageView(vulkan.device, swapchain.msaaImageView, nil)
+    vkDestroyImage(vulkan.device, swapchain.msaaImage, nil)
+    vkFreeMemory(vulkan.device, swapchain.msaaMemory, nil)
+
+  for fence in swapchain.queueFinishedFence:
+    vkDestroyFence(vulkan.device, fence, nil)
+
+  for semaphore in swapchain.imageAvailableSemaphore:
+    vkDestroySemaphore(vulkan.device, semaphore, nil)
+
+  for semaphore in swapchain.renderFinishedSemaphore:
+    vkDestroySemaphore(vulkan.device, semaphore, nil)
+
+  for imageView in swapchain.framebufferViews:
+    vkDestroyImageView(vulkan.device, imageView, nil)
+
+  for framebuffer in swapchain.framebuffers:
+    vkDestroyFramebuffer(vulkan.device, framebuffer, nil)
+
+  vkDestroyCommandPool(vulkan.device, swapchain.commandBufferPool, nil)
+
+  vkDestroySwapchainKHR(vulkan.device, swapchain.vk, nil)
+
+proc TryAcquireNextImage(swapchain: var Swapchain): Option[VkFramebuffer] =
+  if not swapchain.queueFinishedFence[swapchain.currentFiF].Await(100_000_000):
+    return none(VkFramebuffer)
+
+  let nextImageResult = vkAcquireNextImageKHR(
+    vulkan.device,
+    swapchain.vk,
+    high(uint64),
+    swapchain.imageAvailableSemaphore[swapchain.currentFiF],
+    VkFence(0),
+    addr(swapchain.currentFramebufferIndex),
+  )
+
+  swapchain.queueFinishedFence[swapchain.currentFiF].svkResetFences()
+
+  if nextImageResult != VK_SUCCESS:
+    return none(VkFramebuffer)
+  return some(swapchain.framebuffers[swapchain.currentFramebufferIndex])
+
+proc Swap(swapchain: var Swapchain, commandBuffer: VkCommandBuffer): bool =
+  var
+    waitStage = VkPipelineStageFlags(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT)
+    submitInfo = VkSubmitInfo(
+      sType: VK_STRUCTURE_TYPE_SUBMIT_INFO,
+      waitSemaphoreCount: 1,
+      pWaitSemaphores: addr(swapchain.imageAvailableSemaphore[swapchain.currentFiF]),
+      pWaitDstStageMask: addr(waitStage),
+      commandBufferCount: 1,
+      pCommandBuffers: addr(commandBuffer),
+      signalSemaphoreCount: 1,
+      pSignalSemaphores: addr(swapchain.renderFinishedSemaphore[swapchain.currentFiF]),
+    )
+  checkVkResult vkQueueSubmit(
+    queue = vulkan.graphicsQueue,
+    submitCount = 1,
+    pSubmits = addr(submitInfo),
+    fence = swapchain.queueFinishedFence[swapchain.currentFiF]
+  )
+
+  var presentInfo = VkPresentInfoKHR(
+    sType: VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
+    waitSemaphoreCount: 1,
+    pWaitSemaphores: addr(swapchain.renderFinishedSemaphore[swapchain.currentFiF]),
+    swapchainCount: 1,
+    pSwapchains: addr(swapchain.vk),
+    pImageIndices: addr(swapchain.currentFramebufferIndex),
+    pResults: nil,
+  )
+  let presentResult = vkQueuePresentKHR(vulkan.graphicsQueue, addr(presentInfo))
+
+  if swapchain.oldSwapchain != nil:
+    dec swapchain.oldSwapchainCounter
+    if swapchain.oldSwapchainCounter <= 0:
+      DestroySwapchain(swapchain.oldSwapchain[])
+      swapchain.oldSwapchain = nil
+
+  if presentResult != VK_SUCCESS:
+    return false
+
+  swapchain.currentFiF = (uint32(swapchain.currentFiF) + 1) mod INFLIGHTFRAMES
+  return true
+
+proc Recreate(swapchain: Swapchain): Option[Swapchain] =
+  var oldSwapchain = new Swapchain
+  oldSwapchain[] = swapchain
+  InitSwapchain(
+    renderPass = swapchain.renderPass,
+    vSync = swapchain.vSync,
+    samples = swapchain.samples,
+    oldSwapchain = oldSwapchain,
+  )
+
+template WithNextFrame*(swapchain: var Swapchain, framebufferName, commandBufferName, body: untyped): untyped =
+  var maybeFramebuffer = TryAcquireNextImage(swapchain)
+  if maybeFramebuffer.isSome:
+    block:
+      let `framebufferName` {.inject.} = maybeFramebuffer.get
+      let `commandBufferName` {.inject.} = swapchain.commandBuffers[swapchain.currentFiF]
+      let beginInfo = VkCommandBufferBeginInfo(
+        sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
+        flags: VkCommandBufferUsageFlags(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT),
+      )
+      checkVkResult vkResetCommandBuffer(`commandBufferName`, VkCommandBufferResetFlags(0))
+      checkVkResult vkBeginCommandBuffer(`commandBufferName`, addr(beginInfo))
+
+      body
+
+      checkVkResult vkEndCommandBuffer(`commandBufferName`)
+      discard Swap(swapchain = swapchain, commandBuffer = `commandBufferName`)
+  else:
+    let maybeNewSwapchain = Recreate(swapchain)
+    if maybeNewSwapchain.isSome:
+      swapchain = maybeNewSwapchain.get
+