changeset 1064:e1f92e3bec46

merge
author sam <sam@basx.dev>
date Mon, 01 Apr 2024 01:06:22 +0700
parents b3f782703aa5 (current diff) e0e7cdea5e4c (diff)
children 86b9721dfc09
files examples/E01_hello_triangle.nim
diffstat 7 files changed, 51 insertions(+), 43 deletions(-) [+]
line wrap: on
line diff
--- a/semicongine/engine.nim	Mon Apr 01 01:05:20 2024 +0700
+++ b/semicongine/engine.nim	Mon Apr 01 01:06:22 2024 +0700
@@ -166,6 +166,8 @@
   assert engine.state == Running
   assert engine.renderer.isSome
   let t0 = getMonoTime()
+
+  engine.renderer.get.startNewFrame()
   scene.setShaderGlobal(ASPECT_RATIO_ATTRIBUTE, engine.getAspectRatio)
   engine.renderer.get.updateMeshData(scene)
   engine.renderer.get.updateUniformData(scene)
--- a/semicongine/renderer.nim	Mon Apr 01 01:05:20 2024 +0700
+++ b/semicongine/renderer.nim	Mon Apr 01 01:06:22 2024 +0700
@@ -44,6 +44,7 @@
     emptyTexture: VulkanTexture
     queue: Queue
     commandBufferPool: CommandBufferPool
+    nextFrameReady: bool = false
 
 proc currentFrameCommandBuffer(renderer: Renderer): VkCommandBuffer =
   renderer.commandBufferPool.buffers[renderer.swapchain.currentInFlight]
@@ -411,9 +412,10 @@
         offset += uniform.size
   scene.clearDirtyShaderGlobals()
 
-proc startNewFrame(renderer: var Renderer) =
-  # this is kinda important as we will wait for the queue finished fence from the swapchain
-  if not renderer.swapchain.nextFrame():
+proc startNewFrame*(renderer: var Renderer) =
+  # TODO: chance for an infinity-loop
+  while not renderer.swapchain.acquireNextFrame():
+    checkVkResult renderer.device.vk.vkDeviceWaitIdle()
     let res = renderer.swapchain.recreate()
     if not res.isSome:
       raise newException(Exception, "Unable to recreate swapchain")
@@ -421,12 +423,13 @@
     renderer.swapchain = res.get()
     checkVkResult renderer.device.vk.vkDeviceWaitIdle()
     oldSwapchain.destroy()
+  renderer.nextFrameReady = true
 
 proc render*(renderer: var Renderer, scene: Scene) =
   assert scene in renderer.scenedata
+  assert renderer.nextFrameReady, "startNewFrame() must be called before calling render()"
 
   # preparation
-  renderer.startNewFrame()
   renderer.currentFrameCommandBuffer.beginRenderCommands(renderer.renderPass, renderer.swapchain.currentFramebuffer(), oneTimeSubmit = true)
 
   # debug output
@@ -455,6 +458,7 @@
       renderer.swapchain = res.get()
       checkVkResult renderer.device.vk.vkDeviceWaitIdle()
       oldSwapchain.destroy()
+  renderer.nextFrameReady = false
 
 func valid*(renderer: Renderer): bool =
   renderer.device.vk.valid
--- a/semicongine/vulkan/buffer.nim	Mon Apr 01 01:05:20 2024 +0700
+++ b/semicongine/vulkan/buffer.nim	Mon Apr 01 01:06:22 2024 +0700
@@ -9,6 +9,7 @@
 import ./memory
 import ./physicaldevice
 import ./commandbuffer
+import ./syncing
 
 type
   Buffer* = object
@@ -99,7 +100,6 @@
 
 
 proc copy*(src, dst: Buffer, queue: Queue, dstOffset = 0) =
-  # TODO? This is super slow, because withSingleUseCommandBuffer uses vkQueueWaitIdle
   assert src.device.vk.valid
   assert dst.device.vk.valid
   assert src.device == dst.device
@@ -109,16 +109,14 @@
 
   var copyRegion = VkBufferCopy(size: VkDeviceSize(src.size), dstOffset: VkDeviceSize(dstOffset))
   withSingleUseCommandBuffer(src.device, queue, true, commandBuffer):
-    # TODO: This barrier is somehow "reversed"
-    # I think we need to check for the queue-finished fence before we
-    # do any buffer updates, otherwise will append buffer updates after the draw calls
     let barrier = VkMemoryBarrier(
       sType: VK_STRUCTURE_TYPE_MEMORY_BARRIER,
-      dstAccessMask: [VK_ACCESS_MEMORY_WRITE_BIT].toBits,
+      srcAccessMask: [VK_ACCESS_MEMORY_WRITE_BIT].toBits,
+      dstAccessMask: [VK_ACCESS_MEMORY_READ_BIT].toBits,
     )
     commandBuffer.pipelineBarrier(
-      [VK_PIPELINE_STAGE_VERTEX_INPUT_BIT],
-      [VK_PIPELINE_STAGE_TRANSFER_BIT],
+      srcStages = [VK_PIPELINE_STAGE_TRANSFER_BIT],
+      dstStages = [VK_PIPELINE_STAGE_VERTEX_INPUT_BIT],
       memoryBarriers = [barrier]
     )
     commandBuffer.vkCmdCopyBuffer(src.vk, dst.vk, 1, addr(copyRegion))
--- a/semicongine/vulkan/commandbuffer.nim	Mon Apr 01 01:05:20 2024 +0700
+++ b/semicongine/vulkan/commandbuffer.nim	Mon Apr 01 01:06:22 2024 +0700
@@ -1,8 +1,7 @@
-import std/strformat
-
 import ../core
 import ./device
 import ./physicaldevice
+import ./syncing
 
 type
   CommandBufferPool* = object
@@ -56,30 +55,32 @@
 
 template withSingleUseCommandBuffer*(device: Device, queue: Queue, needsTransfer: bool, commandBuffer, body: untyped): untyped =
   # TODO? This is super slow, because we call vkQueueWaitIdle
-  assert device.vk.valid
-  assert queue.vk.valid
+  block:
+    assert device.vk.valid
+    assert queue.vk.valid
 
-  var
-    commandBufferPool = createCommandBufferPool(device, queue.family, 1)
-    commandBuffer = commandBufferPool.buffers[0]
-    beginInfo = VkCommandBufferBeginInfo(
-      sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
-      flags: VkCommandBufferUsageFlags(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT),
-    )
-  checkVkResult commandBuffer.vkBeginCommandBuffer(addr beginInfo)
+    var
+      commandBufferPool = createCommandBufferPool(device, queue.family, 1)
+      commandBuffer = commandBufferPool.buffers[0]
+      beginInfo = VkCommandBufferBeginInfo(
+        sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
+        flags: VkCommandBufferUsageFlags(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT),
+      )
+    checkVkResult commandBuffer.vkBeginCommandBuffer(addr beginInfo)
 
-  block:
-    body
+    block:
+      body
 
-  checkVkResult commandBuffer.vkEndCommandBuffer()
-  var submitInfo = VkSubmitInfo(
-    sType: VK_STRUCTURE_TYPE_SUBMIT_INFO,
-    commandBufferCount: 1,
-    pCommandBuffers: addr commandBuffer,
-  )
-  checkVkResult queue.vk.vkQueueSubmit(1, addr submitInfo, VkFence(0))
-  checkVkResult queue.vk.vkQueueWaitIdle()
-  commandBufferPool.destroy()
+    checkVkResult commandBuffer.vkEndCommandBuffer()
+    var submitInfo = VkSubmitInfo(
+      sType: VK_STRUCTURE_TYPE_SUBMIT_INFO,
+      commandBufferCount: 1,
+      pCommandBuffers: addr commandBuffer,
+    )
+    checkVkResult queue.vk.vkQueueSubmit(1, addr submitInfo, VkFence(0))
+    checkVkResult queue.vk.vkQueueWaitIdle()
+    commandBufferPool.destroy()
+
 
 proc destroy*(commandpool: var CommandBufferPool) =
   assert commandpool.device.vk.valid
--- a/semicongine/vulkan/image.nim	Mon Apr 01 01:05:20 2024 +0700
+++ b/semicongine/vulkan/image.nim	Mon Apr 01 01:06:22 2024 +0700
@@ -8,6 +8,7 @@
 import ./buffer
 import ./memory
 import ./commandbuffer
+import ./syncing
 
 type
   PixelDepth = 1 .. 4
--- a/semicongine/vulkan/swapchain.nim	Mon Apr 01 01:05:20 2024 +0700
+++ b/semicongine/vulkan/swapchain.nim	Mon Apr 01 01:06:22 2024 +0700
@@ -106,12 +106,12 @@
   assert swapchain.vk.valid
   swapchain.framebuffers[swapchain.currentFramebufferIndex]
 
-proc nextFrame*(swapchain: var Swapchain): bool =
+proc acquireNextFrame*(swapchain: var Swapchain): bool =
   assert swapchain.device.vk.valid
   assert swapchain.vk.valid
 
   swapchain.currentInFlight = (swapchain.currentInFlight + 1) mod swapchain.inFlightFrames
-  swapchain.queueFinishedFence[swapchain.currentInFlight].wait()
+  swapchain.queueFinishedFence[swapchain.currentInFlight].await()
 
   let nextImageResult = swapchain.device.vk.vkAcquireNextImageKHR(
     swapchain.vk,
--- a/semicongine/vulkan/syncing.nim	Mon Apr 01 01:05:20 2024 +0700
+++ b/semicongine/vulkan/syncing.nim	Mon Apr 01 01:06:22 2024 +0700
@@ -8,6 +8,7 @@
   Fence* = object
     vk*: VkFence
     device: Device
+    awaitAction: proc() = nil
 
 proc createSemaphore*(device: Device): Semaphore =
   assert device.vk.valid
@@ -15,26 +16,27 @@
   result.device = device
   checkVkResult device.vk.vkCreateSemaphore(addr(semaphoreInfo), nil, addr(result.vk))
 
-proc createFence*(device: Device): Fence =
+proc createFence*(device: Device, awaitAction: proc() = nil): Fence =
   assert device.vk.valid
   var fenceInfo = VkFenceCreateInfo(
     sType: VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
     flags: toBits [VK_FENCE_CREATE_SIGNALED_BIT]
   )
   result.device = device
+  result.awaitAction = awaitAction
   checkVkResult device.vk.vkCreateFence(addr(fenceInfo), nil, addr(result.vk))
 
-proc wait*(fence: Fence) =
+proc await*(fence: var Fence) =
   assert fence.device.vk.valid
   assert fence.vk.valid
-  var varFence = fence.vk
-  checkVkResult vkWaitForFences(fence.device.vk, 1, addr(varFence), false, high(uint64))
+  checkVkResult vkWaitForFences(fence.device.vk, 1, addr fence.vk, false, high(uint64))
+  if fence.awaitAction != nil:
+    fence.awaitAction()
 
-proc reset*(fence: Fence) =
+proc reset*(fence: var Fence) =
   assert fence.device.vk.valid
   assert fence.vk.valid
-  var varFence = fence.vk
-  checkVkResult vkResetFences(fence.device.vk, 1, addr(varFence))
+  checkVkResult fence.device.vk.vkResetFences(1, addr fence.vk)
 
 proc destroy*(semaphore: var Semaphore) =
   assert semaphore.device.vk.valid