Mercurial > games > semicongine
changeset 131:11666d28e04d
add: recreation of swapchain (at least on linux, windows will likely fail, needs testing
author | Sam <sam@basx.dev> |
---|---|
date | Wed, 19 Apr 2023 01:45:16 +0700 |
parents | ff345f9e4eb7 |
children | 25fe4972ef9c |
files | src/semicongine/engine.nim src/semicongine/renderer.nim src/semicongine/vulkan/swapchain.nim tests/test_vulkan_wrapper.nim |
diffstat | 4 files changed, 95 insertions(+), 55 deletions(-) [+] |
line wrap: on
line diff
--- a/src/semicongine/engine.nim Tue Apr 18 03:06:14 2023 +0700 +++ b/src/semicongine/engine.nim Wed Apr 19 01:45:16 2023 +0700 @@ -81,13 +81,13 @@ selectedPhysicalDevice.filterForGraphicsPresentationQueues() ) -proc setRenderer*(engine: var Engine, renderPasses: openArray[RenderPass]) = - engine.renderer = engine.device.initRenderer(renderPasses) +proc setRenderer*(engine: var Engine, renderPass: RenderPass) = + engine.renderer = engine.device.initRenderer(renderPass) proc addScene*(engine: var Engine, entity: Entity, vertexInput: seq[ShaderAttribute]) = engine.renderer.setupDrawableBuffers(entity, vertexInput) -proc renderScene*(engine: var Engine, entity: Entity): auto = +proc renderScene*(engine: var Engine, entity: Entity) = assert engine.running engine.renderer.render(entity)
--- a/src/semicongine/renderer.nim Tue Apr 18 03:06:14 2023 +0700 +++ b/src/semicongine/renderer.nim Wed Apr 19 01:45:16 2023 +0700 @@ -25,24 +25,23 @@ Renderer* = object device: Device surfaceFormat: VkSurfaceFormatKHR - renderPasses: seq[RenderPass] + renderPass: RenderPass swapchain: Swapchain scenedata: Table[Entity, SceneData] -proc initRenderer*(device: Device, renderPasses: openArray[RenderPass]): Renderer = +proc initRenderer*(device: Device, renderPass: RenderPass): Renderer = assert device.vk.valid - assert renderPasses.len > 0 - for renderPass in renderPasses: - assert renderPass.vk.valid + assert renderPass.vk.valid result.device = device - result.renderPasses = renderPasses.toSeq + result.renderPass = renderPass result.surfaceFormat = device.physicalDevice.getSurfaceFormats().filterSurfaceFormat() - let (swapchain, res) = device.createSwapchain(result.renderPasses[^1], result.surfaceFormat, device.firstGraphicsQueue().get().family, 2) - if res != VK_SUCCESS: + # use last renderpass as output for swapchain + let swapchain = device.createSwapchain(result.renderPass.vk, result.surfaceFormat, device.firstGraphicsQueue().get().family) + if not swapchain.isSome: raise newException(Exception, "Unable to create swapchain") - result.swapchain = swapchain + result.swapchain = swapchain.get() proc setupDrawableBuffers*(renderer: var Renderer, tree: Entity, inputs: seq[ShaderAttribute]) = assert not (tree in renderer.scenedata) @@ -128,14 +127,25 @@ renderer.scenedata[tree] = data -proc render*(renderer: var Renderer, entity: Entity): bool = - # TODO: check if nextFrame had any problems - var commandBuffer = renderer.swapchain.nextFrame() +proc render*(renderer: var Renderer, entity: Entity) = + var + commandBufferResult = renderer.swapchain.nextFrame() + commandBuffer: VkCommandBuffer - commandBuffer.beginRenderCommands(renderer.swapchain.renderPass, renderer.swapchain.currentFramebuffer()) + if not commandBufferResult.isSome: + let res = renderer.swapchain.recreate() + if res.isSome: + renderer.swapchain = res.get() + commandBufferResult = renderer.swapchain.nextFrame() + assert commandBufferResult.isSome + else: + raise newException(Exception, "Unable to recreate swapchain") + commandBuffer = commandBufferResult.get() - for i in 0 ..< renderer.swapchain.renderPass.subpasses.len: - let subpass = renderer.swapchain.renderPass.subpasses[i] + commandBuffer.beginRenderCommands(renderer.renderPass, renderer.swapchain.currentFramebuffer()) + + for i in 0 ..< renderer.renderPass.subpasses.len: + let subpass = renderer.renderPass.subpasses[i] for pipeline in subpass.pipelines: var mpipeline = pipeline commandBuffer.vkCmdBindPipeline(subpass.pipelineBindPoint, mpipeline.vk) @@ -144,18 +154,24 @@ debug "Scene buffers:" for (location, buffer) in renderer.scenedata[entity].vertexBuffers.pairs: - echo " ", location, ": ", buffer - echo " Index buffer: ", renderer.scenedata[entity].indexBuffer + debug " ", location, ": ", buffer + debug " Index buffer: ", renderer.scenedata[entity].indexBuffer for drawable in renderer.scenedata[entity].drawables: commandBuffer.draw(drawable, vertexBuffers=renderer.scenedata[entity].vertexBuffers, indexBuffer=renderer.scenedata[entity].indexBuffer) - if i < renderer.swapchain.renderPass.subpasses.len - 1: + if i < renderer.renderPass.subpasses.len - 1: commandBuffer.vkCmdNextSubpass(VK_SUBPASS_CONTENTS_INLINE) commandBuffer.endRenderCommands() - return renderer.swapchain.swap() + if not renderer.swapchain.swap(): + let res = renderer.swapchain.recreate() + if res.isSome: + renderer.swapchain = res.get() + else: + raise newException(Exception, "Unable to recreate swapchain") + func framesRendered*(renderer: Renderer): uint64 = renderer.swapchain.framesRendered @@ -166,6 +182,5 @@ buffer.destroy() if data.indexBuffer.vk.valid: data.indexBuffer.destroy() - for renderpass in renderer.renderPasses.mitems: - renderpass.destroy() + renderer.renderPass.destroy() renderer.swapchain.destroy()
--- a/src/semicongine/vulkan/swapchain.nim Tue Apr 18 03:06:14 2023 +0700 +++ b/src/semicongine/vulkan/swapchain.nim Wed Apr 19 01:45:16 2023 +0700 @@ -1,4 +1,5 @@ import std/options +import std/strformat import std/logging import ./api @@ -17,9 +18,7 @@ Swapchain* = object device*: Device vk*: VkSwapchainKHR - format*: VkFormat dimension*: TVec2[uint32] - renderPass*: RenderPass nImages*: uint32 imageviews*: seq[ImageView] framebuffers*: seq[Framebuffer] @@ -30,28 +29,36 @@ imageAvailableSemaphore*: seq[Semaphore] renderFinishedSemaphore*: seq[Semaphore] commandBufferPool: CommandBufferPool + # required for recreation: + renderPass: VkRenderPass + surfaceFormat: VkSurfaceFormatKHR + queueFamily: QueueFamily + imageCount: uint32 + presentMode: VkPresentModeKHR inFlightFrames: int proc createSwapchain*( device: Device, - renderPass: RenderPass, + renderPass: VkRenderPass, surfaceFormat: VkSurfaceFormatKHR, queueFamily: QueueFamily, desiredNumberOfImages=3'u32, - presentationMode: VkPresentModeKHR=VK_PRESENT_MODE_MAILBOX_KHR, + presentMode: VkPresentModeKHR=VK_PRESENT_MODE_MAILBOX_KHR, inFlightFrames=2 -): (Swapchain, VkResult) = +): Option[Swapchain] = assert device.vk.valid assert device.physicalDevice.vk.valid - assert renderPass.vk.valid + assert renderPass.valid assert inFlightFrames > 0 var capabilities = device.physicalDevice.getSurfaceCapabilities() + if capabilities.currentExtent.width == 0 or capabilities.currentExtent.height == 0: + return none(Swapchain) var imageCount = desiredNumberOfImages # following is according to vulkan specs - if presentationMode in [VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR, VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR]: + if presentMode in [VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR, VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR]: imageCount = 1 else: imageCount = max(imageCount, capabilities.minImageCount) @@ -71,20 +78,20 @@ 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: presentationMode, + presentMode: presentMode, clipped: true, ) var swapchain = Swapchain( device: device, - format: surfaceFormat.format, + surfaceFormat: surfaceFormat, dimension: TVec2[uint32]([capabilities.currentExtent.width, capabilities.currentExtent.height]), - renderPass: renderPass, - inFlightFrames: inFlightFrames + inFlightFrames: inFlightFrames, + queueFamily: queueFamily, + renderPass: renderPass ) - createResult = device.vk.vkCreateSwapchainKHR(addr(createInfo), nil, addr(swapchain.vk)) - if createResult == VK_SUCCESS: + if device.vk.vkCreateSwapchainKHR(addr(createInfo), nil, addr(swapchain.vk)) == VK_SUCCESS: var nImages: uint32 checkVkResult device.vk.vkGetSwapchainImagesKHR(swapChain.vk, addr(nImages), nil) swapchain.nImages = nImages @@ -94,23 +101,25 @@ let image = Image(vk: vkimage, format: surfaceFormat.format, device: device) let imageview = image.createImageView() swapChain.imageviews.add imageview - swapChain.framebuffers.add swapchain.device.createFramebuffer(renderPass.vk, [imageview], swapchain.dimension) + swapChain.framebuffers.add swapchain.device.createFramebuffer(renderPass, [imageview], swapchain.dimension) for i in 0 ..< swapchain.inFlightFrames: swapchain.queueFinishedFence.add device.createFence() swapchain.imageAvailableSemaphore.add device.createSemaphore() swapchain.renderFinishedSemaphore.add device.createSemaphore() swapchain.commandBufferPool = device.createCommandBufferPool(queueFamily, swapchain.inFlightFrames) - - return (swapchain, createResult) + debug &"Created swapchain with: {nImages} framebuffers, {inFlightFrames} in-flight frames, {swapchain.dimension.x}x{swapchain.dimension.y}" + result = some(swapchain) + else: + result = none(Swapchain) proc currentFramebuffer*(swapchain: Swapchain): Framebuffer = + assert swapchain.device.vk.valid + assert swapchain.vk.valid swapchain.framebuffers[swapchain.currentFramebufferIndex] -proc nextFrame*(swapchain: var Swapchain): VkCommandBuffer = +proc nextFrame*(swapchain: var Swapchain): Option[VkCommandBuffer] = assert swapchain.device.vk.valid assert swapchain.vk.valid - assert swapchain.device.firstGraphicsQueue().isSome - assert swapchain.device.firstPresentationQueue().isSome swapchain.currentInFlight = (swapchain.currentInFlight + 1) mod swapchain.inFlightFrames swapchain.queueFinishedFence[swapchain.currentInFlight].wait() @@ -122,16 +131,19 @@ VkFence(0), addr(swapchain.currentFramebufferIndex) ) - # TODO: resize swapchain on VK_SUBOPTIMAL_KHR - echo "HIIIIIIIIIIII next image result", nextImageResult - if not (nextImageResult in [VK_SUCCESS, VK_TIMEOUT, VK_NOT_READY, VK_SUBOPTIMAL_KHR]): - return - swapchain.queueFinishedFence[swapchain.currentInFlight].reset() - - return swapchain.commandBufferPool.buffers[swapchain.currentInFlight] + if nextImageResult == VK_SUCCESS: + swapchain.queueFinishedFence[swapchain.currentInFlight].reset() + result = some(swapchain.commandBufferPool.buffers[swapchain.currentInFlight]) + else: + result = none(VkCommandBuffer) proc swap*(swapchain: var Swapchain): bool = + assert swapchain.device.vk.valid + assert swapchain.vk.valid + assert swapchain.device.firstGraphicsQueue().isSome + assert swapchain.device.firstPresentationQueue().isSome + var commandBuffer = swapchain.commandBufferPool.buffers[swapchain.currentInFlight] waitSemaphores = [swapchain.imageAvailableSemaphore[swapchain.currentInFlight].vk] @@ -163,9 +175,7 @@ pResults: nil, ) let presentResult = vkQueuePresentKHR(swapchain.device.firstPresentationQueue().get().vk, addr(presentInfo)) - # TODO: resize swapchain on VK_SUBOPTIMAL_KHR - echo "HIIIIIIIIIIII Present Result:", presentResult - if not (presentResult in [VK_SUCCESS, VK_SUBOPTIMAL_KHR]): + if presentResult != VK_SUCCESS: return false inc swapchain.framesRendered @@ -193,3 +203,18 @@ swapchain.device.vk.vkDestroySwapchainKHR(swapchain.vk, nil) swapchain.vk.reset() + +proc recreate*(swapchain: var Swapchain): Option[Swapchain] = + assert swapchain.vk.valid + assert swapchain.device.vk.valid + checkVkResult swapchain.device.vk.vkDeviceWaitIdle() + result = createSwapchain( + device=swapchain.device, + renderPass=swapchain.renderPass, + surfaceFormat=swapchain.surfaceFormat, + queueFamily=swapchain.queueFamily, + desiredNumberOfImages=swapchain.imageCount, + presentMode=swapchain.presentMode, + inFlightFrames=swapchain.inFlightFrames, + ) + swapchain.destroy()
--- a/tests/test_vulkan_wrapper.nim Tue Apr 18 03:06:14 2023 +0700 +++ b/tests/test_vulkan_wrapper.nim Wed Apr 19 01:45:16 2023 +0700 @@ -135,7 +135,7 @@ var surfaceFormat = engine.gpuDevice.physicalDevice.getSurfaceFormats().filterSurfaceFormat() renderPass = engine.gpuDevice.simpleForwardRenderPass(surfaceFormat.format, vertexCode, fragmentCode, 2) - engine.setRenderer([renderPass]) + engine.setRenderer(renderPass) # INIT SCENES var scenes = [scene_simple(), scene_different_mesh_types(), scene_primitives()] @@ -154,7 +154,7 @@ if not engine.running or engine.keyIsDown(Escape): engine.destroy() return - discard engine.renderScene(scene) + engine.renderScene(scene) echo "Rendered ", engine.framesRendered, " frames" echo "Processed ", engine.eventsProcessed, " events"