Mercurial > games > semicongine
changeset 567:05ac2455ff60
add: render main loop structure
author | Sam <sam@basx.dev> |
---|---|
date | Sun, 26 Mar 2023 01:56:19 +0700 |
parents | cc7ba46fe3c4 |
children | 1c0de073370b |
files | src/semicongine/engine.nim src/semicongine/math/vector.nim src/semicongine/vulkan/commandbuffer.nim src/semicongine/vulkan/framebuffer.nim src/semicongine/vulkan/pipeline.nim src/semicongine/vulkan/renderpass.nim src/semicongine/vulkan/swapchain.nim src/semicongine/vulkan/syncing.nim tests/test_vulkan_wrapper.nim |
diffstat | 9 files changed, 198 insertions(+), 66 deletions(-) [+] |
line wrap: on
line diff
--- a/src/semicongine/engine.nim Fri Mar 24 00:11:42 2023 +0700 +++ b/src/semicongine/engine.nim Sun Mar 26 01:56:19 2023 +0700 @@ -651,11 +651,8 @@ device.updateBufferData(buffer, data) -proc runPipeline[VertexType; Uniforms](commandBuffer: VkCommandBuffer, - pipeline: var RenderPipeline[VertexType, Uniforms], currentFrame: int) = - vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, - pipeline.pipeline) - +proc runPipeline[VertexType; Uniforms](commandBuffer: VkCommandBuffer, pipeline: var RenderPipeline[VertexType, Uniforms], currentFrame: int) = + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline) vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.layout, 0, 1, addr(pipeline.descriptors[currentFrame]), 0, nil) for (vertexBufferSet, indexed, indexBuffer, count, indexType) in pipeline.vertexBuffers: @@ -671,8 +668,7 @@ vkCmdBindIndexBuffer(commandBuffer, indexBuffer.vkBuffer, VkDeviceSize(0), indexType) vkCmdDrawIndexed(commandBuffer, count, 1, 0, 0, 0) else: - vkCmdDraw(commandBuffer, vertexCount = count, instanceCount = 1, - firstVertex = 0, firstInstance = 0) + vkCmdDraw(commandBuffer, vertexCount = count, instanceCount = 1, firstVertex = 0, firstInstance = 0) proc recordCommandBuffer(renderPass: VkRenderPass, pipeline: var RenderPipeline, commandBuffer: VkCommandBuffer, framebuffer: VkFramebuffer, @@ -682,8 +678,7 @@ sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, pInheritanceInfo: nil, ) - clearColor = VkClearValue(color: VkClearColorValue( - float32: pipeline.clearColor)) + clearColor = VkClearValue(color: VkClearColorValue(float32: pipeline.clearColor)) renderPassInfo = VkRenderPassBeginInfo( sType: VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, renderPass: renderPass,
--- a/src/semicongine/math/vector.nim Fri Mar 24 00:11:42 2023 +0700 +++ b/src/semicongine/math/vector.nim Sun Mar 26 01:56:19 2023 +0700 @@ -15,6 +15,9 @@ Vec2* = TVec2[float32] Vec3* = TVec3[float32] Vec4* = TVec4[float32] + Vec2I* = TVec2[uint32] + Vec3I* = TVec3[uint32] + Vec4I* = TVec4[uint32] converter toVec2*[T: SomeNumber](orig: TVec3[T]|TVec4[T]): TVec2[T] = TVec2[T]([orig[0], orig[1]])
--- a/src/semicongine/vulkan/commandbuffer.nim Fri Mar 24 00:11:42 2023 +0700 +++ b/src/semicongine/vulkan/commandbuffer.nim Sun Mar 26 01:56:19 2023 +0700 @@ -1,16 +1,20 @@ import ./api import ./device import ./physicaldevice +import ./renderpass +import ./framebuffer import ./utils +import ../math + type - CommandPool = object + CommandBufferPool* = object vk*: VkCommandPool family*: QueueFamily - buffers: seq[VkCommandBuffer] + buffers*: seq[VkCommandBuffer] device: Device -proc createCommandPool*(device: Device, family: QueueFamily, nBuffers: int): CommandPool = +proc createCommandBufferPool*(device: Device, family: QueueFamily, nBuffers: uint32): CommandBufferPool = assert device.vk.valid var createInfo = VkCommandPoolCreateInfo( sType: VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, @@ -25,12 +29,68 @@ sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, commandPool: result.vk, level: VK_COMMAND_BUFFER_LEVEL_PRIMARY, - commandBufferCount: uint32(nBuffers), + commandBufferCount: nBuffers, ) result.buffers = newSeq[VkCommandBuffer](nBuffers) checkVkResult device.vk.vkAllocateCommandBuffers(addr(allocInfo), result.buffers.toCPointer) -proc destroy*(commandpool: var CommandPool) = +proc beginRenderCommands*(commandBuffer: VkCommandBuffer, renderpass: RenderPass, framebuffer: Framebuffer) = + assert commandBuffer.valid + assert renderpass.vk.valid + assert framebuffer.vk.valid + let + w = framebuffer.dimension.x + h = framebuffer.dimension.y + + + var clearColors: seq[VkClearValue] + for subpass in renderpass.subpasses: + clearColors.add(VkClearValue(color: VkClearColorValue(float32: subpass.clearColor))) + var + beginInfo = VkCommandBufferBeginInfo( + sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + pInheritanceInfo: nil, + ) + renderPassInfo = VkRenderPassBeginInfo( + sType: VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + renderPass: renderPass.vk, + framebuffer: framebuffer.vk, + renderArea: VkRect2D( + offset: VkOffset2D(x: 0, y: 0), + extent: VkExtent2D(width: w, height: h), + ), + clearValueCount: uint32(clearColors.len), + pClearValues: clearColors.toCPointer(), + ) + viewport = VkViewport( + x: 0.0, + y: 0.0, + width: (float)w, + height: (float)h, + minDepth: 0.0, + maxDepth: 1.0, + ) + scissor = VkRect2D( + offset: VkOffset2D(x: 0, y: 0), + extent: VkExtent2D(width: w, height: h) + ) + checkVkResult commandBuffer.vkResetCommandBuffer(VkCommandBufferResetFlags(0)) + checkVkResult commandBuffer.vkBeginCommandBuffer(addr(beginInfo)) + commandBuffer.vkCmdBeginRenderPass(addr(renderPassInfo), VK_SUBPASS_CONTENTS_INLINE) + commandBuffer.vkCmdSetViewport(firstViewport=0, viewportCount=1, addr(viewport)) + commandBuffer.vkCmdSetScissor(firstScissor=0, scissorCount=1, addr(scissor)) + +proc endRenderCommands*(commandBuffer: VkCommandBuffer) = + commandBuffer.vkCmdEndRenderPass() + checkVkResult commandBuffer.vkEndCommandBuffer() + +template renderCommands*(commandBuffer: VkCommandBuffer, renderpass: RenderPass, framebuffer: Framebuffer, body: untyped) = + commandBuffer.beginRenderCommands(renderpass, framebuffer) + body + commandBuffer.endRenderCommands() + + +proc destroy*(commandpool: var CommandBufferPool) = assert commandpool.device.vk.valid assert commandpool.vk.valid commandpool.device.vk.vkDestroyCommandPool(commandpool.vk, nil)
--- a/src/semicongine/vulkan/framebuffer.nim Fri Mar 24 00:11:42 2023 +0700 +++ b/src/semicongine/vulkan/framebuffer.nim Sun Mar 26 01:56:19 2023 +0700 @@ -10,10 +10,15 @@ Framebuffer* = object device*: Device vk*: VkFramebuffer + dimension*: Vec2I -proc createFramebuffer*(device: Device, renderPass: RenderPass, attachments: openArray[ImageView], dimension: TVec2[uint32]): Framebuffer = +proc createFramebuffer*(device: Device, renderPass: RenderPass, attachments: openArray[ImageView], dimension: Vec2I): Framebuffer = assert device.vk.valid assert renderpass.vk.valid + + result.device = device + result.dimension = dimension + var theattachments: seq[VkImageView] for a in attachments: assert a.vk.valid @@ -27,7 +32,6 @@ height: dimension[1], layers: 1, ) - result.device = device checkVkResult device.vk.vkCreateFramebuffer(addr(framebufferInfo), nil, addr(result.vk)) proc destroy*(framebuffer: var Framebuffer) =
--- a/src/semicongine/vulkan/pipeline.nim Fri Mar 24 00:11:42 2023 +0700 +++ b/src/semicongine/vulkan/pipeline.nim Sun Mar 26 01:56:19 2023 +0700 @@ -38,14 +38,6 @@ ) result.descriptorSetLayout = renderPass.device.createDescriptorSetLayout(descriptors) - #[ - Descriptor - thetype: VkDescriptorType - count: uint32 - stages: seq[VkShaderStageFlagBits] - itemsize: uint32 - ]# - # TODO: Push constants # var pushConstant = VkPushConstantRange( # stageFlags: toBits shaderStage,
--- a/src/semicongine/vulkan/renderpass.nim Fri Mar 24 00:11:42 2023 +0700 +++ b/src/semicongine/vulkan/renderpass.nim Sun Mar 26 01:56:19 2023 +0700 @@ -3,9 +3,11 @@ import ./api import ./utils import ./device +import ../math type Subpass* = object + clearColor*: Vec4 flags: VkSubpassDescriptionFlags pipelineBindPoint: VkPipelineBindPoint inputs: seq[VkAttachmentReference] @@ -16,6 +18,7 @@ RenderPass* = object vk*: VkRenderPass device*: Device + subpasses*: seq[Subpass] proc createRenderPass*( device: Device, @@ -50,6 +53,7 @@ pDependencies: dependencies.toCPointer, ) result.device = device + result.subpasses = subpasses checkVkResult device.vk.vkCreateRenderPass(addr(createInfo), nil, addr(result.vk)) proc createRenderAttachment( @@ -75,7 +79,7 @@ finalLayout: finalLayout, ) -proc simpleForwardRenderPass*(device: Device, format: VkFormat): RenderPass = +proc simpleForwardRenderPass*(device: Device, format: VkFormat, clearColor=Vec4([0.5'f32, 0.5'f32, 0.5'f32, 1'f32])): RenderPass = assert device.vk.valid var attachments = @[createRenderAttachment( @@ -91,7 +95,8 @@ subpasses = @[ Subpass( pipelineBindPoint: VK_PIPELINE_BIND_POINT_GRAPHICS, - outputs: @[VkAttachmentReference(attachment: 0, layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)] + outputs: @[VkAttachmentReference(attachment: 0, layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)], + clearColor: clearColor ) ] dependencies = @[ @@ -104,7 +109,7 @@ dstAccessMask: toBits [VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT], ) ] - device.createRenderPass(attachments = attachments, subpasses = subpasses, dependencies = dependencies) + device.createRenderPass(attachments=attachments, subpasses=subpasses, dependencies=dependencies) proc destroy*(renderpass: var RenderPass) = assert renderpass.device.vk.valid
--- a/src/semicongine/vulkan/swapchain.nim Fri Mar 24 00:11:42 2023 +0700 +++ b/src/semicongine/vulkan/swapchain.nim Sun Mar 26 01:56:19 2023 +0700 @@ -1,3 +1,5 @@ +import std/options + import ./api import ./utils import ./device @@ -5,6 +7,8 @@ import ./image import ./renderpass import ./framebuffer +import ./commandbuffer +import ./pipeline import ./syncing import ../math @@ -15,19 +19,23 @@ vk*: VkSwapchainKHR format*: VkFormat dimension*: TVec2[uint32] + renderPass*: RenderPass nImages*: uint32 imageviews*: seq[ImageView] framebuffers*: seq[Framebuffer] nInFlight*: uint32 - inFlightFences: seq[Fence] - imageAvailableSemaphores*: seq[Semaphore] - renderFinishedSemaphores*: seq[Semaphore] + currentInFlight*: uint32 + queueFinishedFence: seq[Fence] + imageAvailableSemaphore*: seq[Semaphore] + renderFinishedSemaphore*: seq[Semaphore] + commandBufferPool: CommandBufferPool proc createSwapchain*( device: Device, renderPass: RenderPass, surfaceFormat: VkSurfaceFormatKHR, + queueFamily: QueueFamily, desiredNumberOfImages=3'u32, framesInFlight=2'u32, presentationMode: VkPresentModeKHR=VK_PRESENT_MODE_MAILBOX_KHR @@ -35,6 +43,7 @@ assert device.vk.valid assert device.physicalDevice.vk.valid assert renderPass.vk.valid + assert framesInFlight > 0 var capabilities = device.physicalDevice.getSurfaceCapabilities() @@ -68,7 +77,8 @@ device: device, format: surfaceFormat.format, nInFlight: framesInFlight, - dimension: TVec2[uint32]([capabilities.currentExtent.width, capabilities.currentExtent.height]) + dimension: TVec2[uint32]([capabilities.currentExtent.width, capabilities.currentExtent.height]), + renderPass: renderPass, ) createResult = device.vk.vkCreateSwapchainKHR(addr(createInfo), nil, addr(swapchain.vk)) @@ -84,33 +94,96 @@ swapChain.imageviews.add imageview swapChain.framebuffers.add swapchain.device.createFramebuffer(renderPass, [imageview], swapchain.dimension) for i in 0 ..< swapchain.nInFlight: - swapchain.inFlightFences.add device.createFence() - swapchain.imageAvailableSemaphores.add device.createSemaphore() - swapchain.renderFinishedSemaphores.add device.createSemaphore() + swapchain.queueFinishedFence.add device.createFence() + swapchain.imageAvailableSemaphore.add device.createSemaphore() + swapchain.renderFinishedSemaphore.add device.createSemaphore() + swapchain.commandBufferPool = device.createCommandBufferPool(queueFamily, swapchain.nInFlight) return (swapchain, createResult) -proc getImages*(device: VkDevice, swapChain: VkSwapchainKHR): seq[VkImage] = - var n_images: uint32 - checkVkResult vkGetSwapchainImagesKHR(device, swapChain, addr(n_images), nil) - result = newSeq[VkImage](n_images) - checkVkResult vkGetSwapchainImagesKHR(device, swapChain, addr(n_images), addr( - result[0])) +proc drawNextFrame*(swapchain: var Swapchain, pipeline: Pipeline): bool = + 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.nInFlight + swapchain.queueFinishedFence[swapchain.currentInFlight].wait() + + var currentFramebufferIndex: uint32 + + let nextImageResult = swapchain.device.vk.vkAcquireNextImageKHR( + swapchain.vk, + high(uint64), + swapchain.imageAvailableSemaphore[swapchain.currentInFlight].vk, + VkFence(0), + addr(currentFramebufferIndex) + ) + if not (nextImageResult in [VK_SUCCESS, VK_TIMEOUT, VK_NOT_READY, VK_SUBOPTIMAL_KHR]): + return false + + swapchain.queueFinishedFence[swapchain.currentInFlight].reset() + + renderCommands( + swapchain.commandBufferPool.buffers[swapchain.currentInFlight], + swapchain.renderpass, + swapchain.framebuffers[swapChain.currentInFlight] + ): + echo "TODO: Draw calls here" + + var + waitSemaphores = [swapchain.imageAvailableSemaphore[swapchain.currentInFlight].vk] + waitStages = [VkPipelineStageFlags(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT)] + submitInfo = VkSubmitInfo( + sType: VK_STRUCTURE_TYPE_SUBMIT_INFO, + waitSemaphoreCount: 1, + pWaitSemaphores: addr(waitSemaphores[0]), + pWaitDstStageMask: addr(waitStages[0]), + commandBufferCount: 1, + pCommandBuffers: addr(swapchain.commandBufferPool.buffers[swapchain.currentInFlight]), + signalSemaphoreCount: 1, + pSignalSemaphores: addr(swapchain.renderFinishedSemaphore[swapchain.currentInFlight].vk), + ) + checkVkResult vkQueueSubmit( + swapchain.device.firstGraphicsQueue().get.vk, + 1, + addr(submitInfo), + swapchain.queueFinishedFence[swapchain.currentInFlight].vk + ) + + var presentInfo = VkPresentInfoKHR( + sType: VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + waitSemaphoreCount: 1, + pWaitSemaphores: addr(swapchain.renderFinishedSemaphore[swapchain.currentInFlight].vk), + swapchainCount: 1, + pSwapchains: addr(swapchain.vk), + pImageIndices: addr(currentFramebufferIndex), + pResults: nil, + ) + let presentResult = vkQueuePresentKHR(swapchain.device.firstPresentationQueue().get().vk, addr(presentInfo)) + if not (presentResult in [VK_SUCCESS, VK_SUBOPTIMAL_KHR]): + return false + + return true + proc destroy*(swapchain: var Swapchain) = assert swapchain.vk.valid + assert swapchain.commandBufferPool.vk.valid + for imageview in swapchain.imageviews.mitems: assert imageview.vk.valid imageview.destroy() for framebuffer in swapchain.framebuffers.mitems: assert framebuffer.vk.valid framebuffer.destroy() + swapchain.commandBufferPool.destroy() for i in 0 ..< swapchain.nInFlight: - assert swapchain.inFlightFences[i].vk.valid - assert swapchain.imageAvailableSemaphores[i].vk.valid - assert swapchain.renderFinishedSemaphores[i].vk.valid - swapchain.inFlightFences[i].destroy() - swapchain.imageAvailableSemaphores[i].destroy() - swapchain.renderFinishedSemaphores[i].destroy() + assert swapchain.queueFinishedFence[i].vk.valid + assert swapchain.imageAvailableSemaphore[i].vk.valid + assert swapchain.renderFinishedSemaphore[i].vk.valid + swapchain.queueFinishedFence[i].destroy() + swapchain.imageAvailableSemaphore[i].destroy() + swapchain.renderFinishedSemaphore[i].destroy() swapchain.device.vk.vkDestroySwapchainKHR(swapchain.vk, nil) swapchain.vk.reset()
--- a/src/semicongine/vulkan/syncing.nim Fri Mar 24 00:11:42 2023 +0700 +++ b/src/semicongine/vulkan/syncing.nim Sun Mar 26 01:56:19 2023 +0700 @@ -24,6 +24,18 @@ result.device = device checkVkResult device.vk.vkCreateFence(addr(fenceInfo), nil, addr(result.vk)) +proc wait*(fence: 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)) + +proc reset*(fence: Fence) = + assert fence.device.vk.valid + assert fence.vk.valid + var varFence = fence.vk + checkVkResult vkResetFences(fence.device.vk, 1, addr(varFence)) + proc destroy*(semaphore: var Semaphore) = assert semaphore.device.vk.valid assert semaphore.vk.valid
--- a/tests/test_vulkan_wrapper.nim Fri Mar 24 00:11:42 2023 +0700 +++ b/tests/test_vulkan_wrapper.nim Sun Mar 26 01:56:19 2023 +0700 @@ -67,30 +67,22 @@ # setup render pipeline var surfaceFormat = device.physicalDevice.getSurfaceFormats().filterSurfaceFormat() var renderpass = device.simpleForwardRenderPass(surfaceFormat.format) - var (swapchain, res) = device.createSwapchain(renderpass, surfaceFormat) + var (swapchain, res) = device.createSwapchain(renderpass, surfaceFormat, device.firstGraphicsQueue().get().family) if res != VK_SUCCESS: raise newException(Exception, "Unable to create swapchain") - # var framebuffers: seq[Framebuffer] - # for imageview in swapchain.imageviews: - # framebuffers.add device.createFramebuffer(renderpass, [imageview], swapchain.dimension) # todo: could be create inside "device", but it would be nice to have nim v2 with support for circular dependencies first - var - commandPool = device.createCommandPool(family=device.firstGraphicsQueue().get().family, nBuffers=1) - imageAvailable = device.createSemaphore() - renderFinished = device.createSemaphore() - inflight = device.createFence() - const vertexBinary = shaderCode[Vertex, Uniforms, FragmentInput](stage=VK_SHADER_STAGE_VERTEX_BIT, version=450, entrypoint="main", "fragpos = pos;") const fragmentBinary = shaderCode[FragmentInput, void, Pixel](stage=VK_SHADER_STAGE_FRAGMENT_BIT, version=450, entrypoint="main", "color = vec4(1, 1, 1, 0);") - var vertexshader = createShader[Vertex, Uniforms, FragmentInput](device, VK_SHADER_STAGE_VERTEX_BIT, "main", vertexBinary) - var fragmentshader = createShader[FragmentInput, void, Pixel](device, VK_SHADER_STAGE_FRAGMENT_BIT, "main", fragmentBinary) - - var pipeline = renderpass.createPipeline(vertexshader, fragmentshader) - var descriptorPool = device.createDescriptorSetPool(@[(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1'u32)]) - var descriptorSet = descriptorPool.allocateDescriptorSet(pipeline.descriptorSetLayout, 1) + var + vertexshader = createShader[Vertex, Uniforms, FragmentInput](device, VK_SHADER_STAGE_VERTEX_BIT, "main", vertexBinary) + fragmentshader = createShader[FragmentInput, void, Pixel](device, VK_SHADER_STAGE_FRAGMENT_BIT, "main", fragmentBinary) + pipeline = renderpass.createPipeline(vertexshader, fragmentshader) + descriptorPool = device.createDescriptorSetPool(@[(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1'u32)]) + descriptorSet = descriptorPool.allocateDescriptorSet(pipeline.descriptorSetLayout, 1) echo "All successfull" + echo swapchain.drawNextFrame(pipeline) echo "Start cleanup" # cleanup @@ -99,10 +91,6 @@ vertexshader.destroy() fragmentshader.destroy() pipeline.destroy() - inflight.destroy() - imageAvailable.destroy() - renderFinished.destroy() - commandPool.destroy() renderpass.destroy() swapchain.destroy() device.destroy()