changeset 956:cca6f1a675db

fix: again, I think, I have now a correct syncing of vertex buffers updates and drawing of multiple frames in flight XD
author sam <sam@basx.dev>
date Mon, 01 Apr 2024 22:42:50 +0700
parents 53249d9bb7a3
children d27c8dbfef1c
files examples/E01_hello_triangle.nim semicongine/engine.nim semicongine/renderer.nim semicongine/vulkan/buffer.nim semicongine/vulkan/commandbuffer.nim semicongine/vulkan/image.nim semicongine/vulkan/swapchain.nim
diffstat 7 files changed, 45 insertions(+), 37 deletions(-) [+]
line wrap: on
line diff
--- a/examples/E01_hello_triangle.nim	Mon Apr 01 01:06:22 2024 +0700
+++ b/examples/E01_hello_triangle.nim	Mon Apr 01 22:42:50 2024 +0700
@@ -26,7 +26,7 @@
   )
   myengine = initEngine("Hello triangle", showFps = true)
 
-myengine.initRenderer({VERTEX_COLORED_MATERIAL: shaderConfiguration}, vSync = false, inFlightFrames = 2)
+myengine.initRenderer({VERTEX_COLORED_MATERIAL: shaderConfiguration}, inFlightFrames = 1)
 myengine.loadScene(scene)
 
 while myengine.updateInputs() == Running and not myengine.keyWasPressed(Escape):
--- a/semicongine/engine.nim	Mon Apr 01 01:06:22 2024 +0700
+++ b/semicongine/engine.nim	Mon Apr 01 22:42:50 2024 +0700
@@ -22,7 +22,7 @@
 import ./text
 import ./panel
 
-const COUNT_N_RENDERTIMES = 99
+const COUNT_N_RENDERTIMES = 199
 
 type
   EngineState* = enum
@@ -149,18 +149,24 @@
   ))
 
 proc initRenderer*(engine: var Engine, clearColor = newVec4f(0, 0, 0, 0), vSync = false) =
+  checkVkResult engine.device.vk.vkDeviceWaitIdle()
   engine.initRenderer(@[], clearColor, vSync = vSync)
+  checkVkResult engine.device.vk.vkDeviceWaitIdle()
 
 proc loadScene*(engine: var Engine, scene: var Scene) =
   assert engine.renderer.isSome
   assert not scene.loaded
+  checkVkResult engine.device.vk.vkDeviceWaitIdle()
   scene.addShaderGlobal(ASPECT_RATIO_ATTRIBUTE, engine.getAspectRatio)
   engine.renderer.get.setupDrawableBuffers(scene)
   engine.renderer.get.updateMeshData(scene, forceAll = true)
   engine.renderer.get.updateUniformData(scene, forceAll = true)
+  checkVkResult engine.device.vk.vkDeviceWaitIdle()
 
 proc unloadScene*(engine: var Engine, scene: Scene) =
+  checkVkResult engine.device.vk.vkDeviceWaitIdle()
   engine.renderer.get.destroy(scene)
+  checkVkResult engine.device.vk.vkDeviceWaitIdle()
 
 proc renderScene*(engine: var Engine, scene: var Scene) =
   assert engine.state == Running
--- a/semicongine/renderer.nim	Mon Apr 01 01:06:22 2024 +0700
+++ b/semicongine/renderer.nim	Mon Apr 01 22:42:50 2024 +0700
@@ -330,30 +330,41 @@
 
   renderer.scenedata[scene] = scenedata
 
-proc refreshMeshAttributeData(renderer: Renderer, scene: var Scene, drawable: Drawable, mesh: Mesh, attribute: string) =
-  debug &"Refreshing data on mesh mesh for {attribute}"
-  # ignore attributes that are not used in this shader
-  if not (attribute in renderer.scenedata[scene].attributeLocation):
-    return
-
-  let memoryPerformanceHint = renderer.scenedata[scene].attributeLocation[attribute]
-  renderer.scenedata[scene].vertexBuffers[memoryPerformanceHint].setData(
-    renderer.queue,
-    mesh[].getPointer(attribute),
-    mesh[].attributeSize(attribute),
-    renderer.scenedata[scene].vertexBufferOffsets[(mesh, attribute)]
-  )
-
 proc updateMeshData*(renderer: var Renderer, scene: var Scene, forceAll = false) =
   assert scene in renderer.scenedata
 
+  var addedBarrier = false;
   for (drawable, mesh) in renderer.scenedata[scene].drawables.mitems:
     if mesh[].attributes.contains(TRANSFORM_ATTRIB):
       mesh[].updateInstanceTransforms(TRANSFORM_ATTRIB)
     let attrs = (if forceAll: mesh[].attributes else: mesh[].dirtyAttributes)
     for attribute in attrs:
-      renderer.refreshMeshAttributeData(scene, drawable, mesh, attribute)
-      debug &"Update mesh attribute {attribute}"
+      # ignore attributes that are not used in this scene
+      if attribute in renderer.scenedata[scene].attributeLocation:
+        debug &"Update mesh attribute {attribute}"
+        let memoryPerformanceHint = renderer.scenedata[scene].attributeLocation[attribute]
+        # if we have to do a vkCmdCopyBuffer (not buffer.canMap), then we want to added a barrier to
+        # not infer with the current frame that is being renderer (relevant when we have multiple frames in flight)
+        # (remark: ...I think..., I am pretty new to this sync stuff)
+        if not renderer.scenedata[scene].vertexBuffers[memoryPerformanceHint].canMap and not addedBarrier:
+          withSingleUseCommandBuffer(renderer.device, renderer.queue, commandBuffer):
+            let barrier = VkMemoryBarrier(
+              sType: VK_STRUCTURE_TYPE_MEMORY_BARRIER,
+              srcAccessMask: [VK_ACCESS_MEMORY_READ_BIT].toBits,
+              dstAccessMask: [VK_ACCESS_MEMORY_WRITE_BIT].toBits,
+            )
+            commandBuffer.pipelineBarrier(
+              srcStages = [VK_PIPELINE_STAGE_VERTEX_INPUT_BIT],
+              dstStages = [VK_PIPELINE_STAGE_TRANSFER_BIT],
+              memoryBarriers = [barrier]
+            )
+            addedBarrier = true
+        renderer.scenedata[scene].vertexBuffers[memoryPerformanceHint].setData(
+          renderer.queue,
+          mesh[].getPointer(attribute),
+          mesh[].attributeSize(attribute),
+          renderer.scenedata[scene].vertexBufferOffsets[(mesh, attribute)]
+        )
     mesh[].clearDirtyAttributes()
 
 proc updateUniformData*(renderer: var Renderer, scene: var Scene, forceAll = false) =
@@ -413,7 +424,7 @@
   scene.clearDirtyShaderGlobals()
 
 proc startNewFrame*(renderer: var Renderer) =
-  # TODO: chance for an infinity-loop
+  # TODO: chance for an infinity-loop?
   while not renderer.swapchain.acquireNextFrame():
     checkVkResult renderer.device.vk.vkDeviceWaitIdle()
     let res = renderer.swapchain.recreate()
@@ -458,6 +469,7 @@
       renderer.swapchain = res.get()
       checkVkResult renderer.device.vk.vkDeviceWaitIdle()
       oldSwapchain.destroy()
+  renderer.swapchain.currentInFlight = (renderer.swapchain.currentInFlight + 1) mod renderer.swapchain.inFlightFrames
   renderer.nextFrameReady = false
 
 func valid*(renderer: Renderer): bool =
--- a/semicongine/vulkan/buffer.nim	Mon Apr 01 01:06:22 2024 +0700
+++ b/semicongine/vulkan/buffer.nim	Mon Apr 01 22:42:50 2024 +0700
@@ -9,7 +9,6 @@
 import ./memory
 import ./physicaldevice
 import ./commandbuffer
-import ./syncing
 
 type
   Buffer* = object
@@ -108,17 +107,7 @@
   assert VK_BUFFER_USAGE_TRANSFER_DST_BIT in dst.usage
 
   var copyRegion = VkBufferCopy(size: VkDeviceSize(src.size), dstOffset: VkDeviceSize(dstOffset))
-  withSingleUseCommandBuffer(src.device, queue, true, commandBuffer):
-    let barrier = VkMemoryBarrier(
-      sType: VK_STRUCTURE_TYPE_MEMORY_BARRIER,
-      srcAccessMask: [VK_ACCESS_MEMORY_WRITE_BIT].toBits,
-      dstAccessMask: [VK_ACCESS_MEMORY_READ_BIT].toBits,
-    )
-    commandBuffer.pipelineBarrier(
-      srcStages = [VK_PIPELINE_STAGE_TRANSFER_BIT],
-      dstStages = [VK_PIPELINE_STAGE_VERTEX_INPUT_BIT],
-      memoryBarriers = [barrier]
-    )
+  withSingleUseCommandBuffer(src.device, queue, commandBuffer):
     commandBuffer.vkCmdCopyBuffer(src.vk, dst.vk, 1, addr(copyRegion))
 
 proc destroy*(buffer: var Buffer) =
@@ -137,9 +126,12 @@
     )
   buffer.vk.reset
 
+template canMap*(buffer: Buffer): bool =
+  buffer.memory.canMap
+
 proc setData*(dst: Buffer, queue: Queue, src: pointer, size: int, bufferOffset = 0) =
   assert bufferOffset + size <= dst.size
-  if dst.memory.canMap:
+  if dst.canMap:
     copyMem(cast[pointer](cast[int](dst.memory.data) + bufferOffset), src, size)
     if dst.memory.needsFlushing:
       dst.memory.flush()
--- a/semicongine/vulkan/commandbuffer.nim	Mon Apr 01 01:06:22 2024 +0700
+++ b/semicongine/vulkan/commandbuffer.nim	Mon Apr 01 22:42:50 2024 +0700
@@ -53,7 +53,7 @@
   )
 
 
-template withSingleUseCommandBuffer*(device: Device, queue: Queue, needsTransfer: bool, commandBuffer, body: untyped): untyped =
+template withSingleUseCommandBuffer*(device: Device, queue: Queue, commandBuffer, body: untyped): untyped =
   # TODO? This is super slow, because we call vkQueueWaitIdle
   block:
     assert device.vk.valid
--- a/semicongine/vulkan/image.nim	Mon Apr 01 01:06:22 2024 +0700
+++ b/semicongine/vulkan/image.nim	Mon Apr 01 22:42:50 2024 +0700
@@ -8,7 +8,6 @@
 import ./buffer
 import ./memory
 import ./commandbuffer
-import ./syncing
 
 type
   PixelDepth = 1 .. 4
@@ -112,7 +111,7 @@
   else:
     raise newException(Exception, "Unsupported layout transition!")
 
-  withSingleUseCommandBuffer(image.device, queue, false, commandBuffer):
+  withSingleUseCommandBuffer(image.device, queue, commandBuffer):
     commandBuffer.pipelineBarrier([srcStage], [dstStage], imageBarriers = [barrier])
 
 proc copy*(src: Buffer, dst: VulkanImage, queue: Queue) =
@@ -135,7 +134,7 @@
     imageOffset: VkOffset3D(x: 0, y: 0, z: 0),
     imageExtent: VkExtent3D(width: uint32(dst.width), height: uint32(dst.height), depth: 1)
   )
-  withSingleUseCommandBuffer(src.device, queue, true, commandBuffer):
+  withSingleUseCommandBuffer(src.device, queue, commandBuffer):
     commandBuffer.vkCmdCopyBufferToImage(
       src.vk,
       dst.vk,
--- a/semicongine/vulkan/swapchain.nim	Mon Apr 01 01:06:22 2024 +0700
+++ b/semicongine/vulkan/swapchain.nim	Mon Apr 01 22:42:50 2024 +0700
@@ -110,7 +110,6 @@
   assert swapchain.device.vk.valid
   assert swapchain.vk.valid
 
-  swapchain.currentInFlight = (swapchain.currentInFlight + 1) mod swapchain.inFlightFrames
   swapchain.queueFinishedFence[swapchain.currentInFlight].await()
 
   let nextImageResult = swapchain.device.vk.vkAcquireNextImageKHR(