# HG changeset patch # User sam # Date 1711908382 -25200 # Node ID e1f92e3bec461bd60bea8fdb033cb821cd3bca80 # Parent b3f782703aa5c44282b46cc88c9813058a49cd87# Parent e0e7cdea5e4c97882301d0219e40a0d4fa5a3fbe merge diff -r b3f782703aa5 -r e1f92e3bec46 examples/E01_hello_triangle.nim diff -r b3f782703aa5 -r e1f92e3bec46 semicongine/engine.nim --- 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) diff -r b3f782703aa5 -r e1f92e3bec46 semicongine/renderer.nim --- 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 diff -r b3f782703aa5 -r e1f92e3bec46 semicongine/vulkan/buffer.nim --- 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)) diff -r b3f782703aa5 -r e1f92e3bec46 semicongine/vulkan/commandbuffer.nim --- 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 diff -r b3f782703aa5 -r e1f92e3bec46 semicongine/vulkan/image.nim --- 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 diff -r b3f782703aa5 -r e1f92e3bec46 semicongine/vulkan/swapchain.nim --- 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, diff -r b3f782703aa5 -r e1f92e3bec46 semicongine/vulkan/syncing.nim --- 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