changeset 559:baaa620887b4

add: more vulkan objects
author Sam <sam@basx.dev>
date Mon, 06 Mar 2023 23:50:21 +0700
parents affa6571a2c9
children 8de8a2102071
files src/semicongine/engine.nim src/semicongine/math.nim src/semicongine/math/vector.nim src/semicongine/vulkan.nim src/semicongine/vulkan/commandbuffer.nim src/semicongine/vulkan/device.nim src/semicongine/vulkan/framebuffer.nim src/semicongine/vulkan/renderpass.nim src/semicongine/vulkan/swapchain.nim src/semicongine/vulkan/syncing.nim tests/test_vulkan_wrapper.nim
diffstat 11 files changed, 277 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/src/semicongine/engine.nim	Thu Mar 02 23:16:45 2023 +0700
+++ b/src/semicongine/engine.nim	Mon Mar 06 23:50:21 2023 +0700
@@ -278,11 +278,9 @@
     dependency = VkSubpassDependency(
       srcSubpass: VK_SUBPASS_EXTERNAL,
       dstSubpass: 0,
-      srcStageMask: VkPipelineStageFlags(
-          VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT),
+      srcStageMask: VkPipelineStageFlags(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT),
       srcAccessMask: VkAccessFlags(0),
-      dstStageMask: VkPipelineStageFlags(
-          VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT),
+      dstStageMask: VkPipelineStageFlags(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT),
       dstAccessMask: VkAccessFlags(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT),
     )
     renderPassCreateInfo = VkRenderPassCreateInfo(
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/semicongine/math.nim	Mon Mar 06 23:50:21 2023 +0700
@@ -0,0 +1,8 @@
+import std/math
+export math
+
+import ./math/vector
+export vector
+
+import ./math/matrix
+export matrix
--- a/src/semicongine/math/vector.nim	Thu Mar 02 23:16:45 2023 +0700
+++ b/src/semicongine/math/vector.nim	Mon Mar 06 23:50:21 2023 +0700
@@ -5,8 +5,7 @@
 import std/typetraits
 import std/tables
 
-export math
-
+import ../vulkan/api
 
 type
   TVec2*[T: SomeNumber] = array[2, T]
@@ -271,3 +270,6 @@
 makeRandomInit(TVec2)
 makeRandomInit(TVec3)
 makeRandomInit(TVec4)
+
+converter Vec2VkExtent*(vec: TVec2[uint32]): VkExtent2D = VkExtent2D(width: vec[0], height: vec[1])
+converter Vec3VkExtent*(vec: TVec2[uint32]): VkExtent3D = VkExtent3D(width: vec[0], height: vec[1], depth: vec[2])
--- a/src/semicongine/vulkan.nim	Thu Mar 02 23:16:45 2023 +0700
+++ b/src/semicongine/vulkan.nim	Mon Mar 06 23:50:21 2023 +0700
@@ -13,6 +13,18 @@
 import ./vulkan/swapchain
 export swapchain
 
+import ./vulkan/renderpass
+export renderpass
+
+import ./vulkan/framebuffer
+export framebuffer
+
+import ./vulkan/commandbuffer
+export commandbuffer
+
+import ./vulkan/syncing
+export syncing
+
 import ./vulkan/buffer
 export buffer
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/semicongine/vulkan/commandbuffer.nim	Mon Mar 06 23:50:21 2023 +0700
@@ -0,0 +1,37 @@
+import ./api
+import ./device
+import ./physicaldevice
+import ./utils
+
+type
+  CommandPool = object
+    vk*: VkCommandPool
+    family*: QueueFamily
+    buffers: seq[VkCommandBuffer]
+    device: Device
+
+proc createCommandPool*(device: Device, family: QueueFamily, nBuffers: int): CommandPool =
+  assert device.vk.valid
+  var createInfo = VkCommandPoolCreateInfo(
+    sType: VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
+    flags: toBits [VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT],
+    queueFamilyIndex: family.index,
+  )
+  result.family = family
+  result.device = device
+  checkVkResult device.vk.vkCreateCommandPool(addr(createInfo), nil, addr(result.vk))
+
+  var allocInfo = VkCommandBufferAllocateInfo(
+    sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
+    commandPool: result.vk,
+    level: VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+    commandBufferCount: uint32(nBuffers),
+  )
+  result.buffers = newSeq[VkCommandBuffer](nBuffers)
+  checkVkResult device.vk.vkAllocateCommandBuffers(addr(allocInfo), result.buffers.toCPointer)
+
+proc destroy*(commandpool: var CommandPool) =
+  assert commandpool.device.vk.valid
+  assert commandpool.vk.valid
+  commandpool.device.vk.vkDestroyCommandPool(commandpool.vk, nil)
+  commandpool.vk.reset
--- a/src/semicongine/vulkan/device.nim	Thu Mar 02 23:16:45 2023 +0700
+++ b/src/semicongine/vulkan/device.nim	Mon Mar 06 23:50:21 2023 +0700
@@ -14,6 +14,7 @@
     queues*: Table[QueueFamily, Queue]
   Queue* = object
     vk*: VkQueue
+    family*: QueueFamily
     presentation: bool
     graphics: bool
 
@@ -68,7 +69,7 @@
   for family in deviceQueues.keys:
     var queue: VkQueue
     vkGetDeviceQueue(result.vk, family.index, 0, addr queue)
-    result.queues[family] = Queue(vk: queue, presentation: family.hasPresentation(physicalDevice.surface), graphics: family.hasGraphics())
+    result.queues[family] = Queue(vk: queue, family: family, presentation: family.hasPresentation(physicalDevice.surface), graphics: family.hasGraphics())
 
 func firstGraphicsQueue*(device: Device): Option[Queue] =
   assert device.vk.valid
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/semicongine/vulkan/framebuffer.nim	Mon Mar 06 23:50:21 2023 +0700
@@ -0,0 +1,36 @@
+import ./api
+import ./device
+import ./renderpass
+import ./utils
+import ./image
+import ../math
+
+type
+  Framebuffer* = object
+    vk*: VkFramebuffer
+    device*: Device
+
+proc createFramebuffer*(device: Device, renderpass: RenderPass, attachments: openArray[ImageView], dimension: TVec2[uint32]): Framebuffer =
+  assert device.vk.valid
+  assert renderpass.vk.valid
+  var theattachments: seq[VkImageView]
+  for a in attachments:
+    assert a.vk.valid
+    theattachments.add a.vk
+  var framebufferInfo = VkFramebufferCreateInfo(
+    sType: VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
+    renderPass: renderPass.vk,
+    attachmentCount: uint32(theattachments.len),
+    pAttachments: theattachments.toCPointer,
+    width: dimension[0],
+    height: dimension[1],
+    layers: 1,
+  )
+  result.device = device
+  checkVkResult device.vk.vkCreateFramebuffer(addr(framebufferInfo), nil, addr(result.vk))
+
+proc destroy*(framebuffer: var Framebuffer) =
+  assert framebuffer.device.vk.valid
+  assert framebuffer.vk.valid
+  framebuffer.device.vk.vkDestroyFramebuffer(framebuffer.vk, nil)
+  framebuffer.vk.reset
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/semicongine/vulkan/renderpass.nim	Mon Mar 06 23:50:21 2023 +0700
@@ -0,0 +1,113 @@
+import std/options
+
+import ./api
+import ./utils
+import ./device
+
+type
+  Subpass* = object
+    flags: VkSubpassDescriptionFlags
+    pipelineBindPoint: VkPipelineBindPoint
+    inputs: seq[VkAttachmentReference]
+    outputs: seq[VkAttachmentReference]
+    resolvers: seq[VkAttachmentReference]
+    depthStencil: Option[VkAttachmentReference]
+    preserves: seq[uint32]
+  RenderPass* = object
+    vk*: VkRenderPass
+    device: Device
+
+proc createRenderPass*(
+  device: Device,
+  attachments: var seq[VkAttachmentDescription],
+  subpasses: var seq[Subpass],
+  dependencies: var seq[VkSubpassDependency]
+): RenderPass =
+  assert device.vk.valid
+
+  var subpassesList = newSeq[VkSubpassDescription](subpasses.len)
+  for subpass in subpasses.mitems:
+    subpassesList.add VkSubpassDescription(
+      flags: subpass.flags,
+      pipelineBindPoint: subpass.pipelineBindPoint,
+      inputAttachmentCount: uint32(subpass.inputs.len),
+      pInputAttachments: subpass.inputs.toCPointer,
+      colorAttachmentCount: uint32(subpass.outputs.len),
+      pColorAttachments: subpass.outputs.toCPointer,
+      pResolveAttachments: subpass.resolvers.toCPointer,
+      pDepthStencilAttachment: if subpass.depthStencil.isSome: addr(subpass.depthStencil.get) else: nil,
+      preserveAttachmentCount: uint32(subpass.preserves.len),
+      pPreserveAttachments: subpass.preserves.toCPointer,
+    )
+
+  var createInfo = VkRenderPassCreateInfo(
+      sType: VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
+      attachmentCount: uint32(attachments.len),
+      pAttachments: attachments.toCPointer,
+      subpassCount: uint32(subpassesList.len),
+      pSubpasses: subpassesList.toCPointer,
+      dependencyCount: uint32(dependencies.len),
+      pDependencies: dependencies.toCPointer,
+    )
+  result.device = device
+  checkVkResult device.vk.vkCreateRenderPass(addr(createInfo), nil, addr(result.vk))
+
+proc createRenderAttachment(
+  format: VkFormat,
+  flags = VkAttachmentDescriptionFlags(0),
+  samples = VK_SAMPLE_COUNT_1_BIT,
+  loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
+  storeOp = VK_ATTACHMENT_STORE_OP_STORE,
+  stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+  stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
+  initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+  finalLayout = VK_IMAGE_LAYOUT_GENERAL,
+): auto =
+  VkAttachmentDescription(
+    format: format,
+    flags: flags,
+    samples: samples,
+    loadOp: loadOp,
+    storeOp: storeOp,
+    stencilLoadOp: stencilLoadOp,
+    stencilStoreOp: stencilStoreOp,
+    initialLayout: initialLayout,
+    finalLayout: finalLayout,
+  )
+
+proc simpleForwardRenderPass*(device: Device, format: VkFormat): RenderPass =
+  assert device.vk.valid
+  var
+    attachments = @[createRenderAttachment(
+      format = format,
+      samples = VK_SAMPLE_COUNT_1_BIT,
+      loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
+      storeOp = VK_ATTACHMENT_STORE_OP_STORE,
+      stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+      stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
+      initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+      finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
+    )]
+    subpasses = @[
+      Subpass(
+        pipelineBindPoint: VK_PIPELINE_BIND_POINT_GRAPHICS,
+        outputs: @[VkAttachmentReference(attachment: 0, layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)]
+      )
+    ]
+    dependencies = @[
+      VkSubpassDependency(
+        srcSubpass: VK_SUBPASS_EXTERNAL,
+        dstSubpass: 0,
+        srcStageMask: toBits [VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT],
+        srcAccessMask: VkAccessFlags(0),
+        dstStageMask: toBits [VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT],
+        dstAccessMask: toBits [VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT],
+      )
+    ]
+  device.createRenderPass(attachments = attachments, subpasses = subpasses, dependencies = dependencies)
+
+proc destroy*(renderpass: var RenderPass) =
+  assert renderpass.device.vk.valid
+  assert renderpass.vk.valid
+  renderpass.device.vk.vkDestroyRenderPass(renderpass.vk, nil)
+  renderpass.vk.reset
--- a/src/semicongine/vulkan/swapchain.nim	Thu Mar 02 23:16:45 2023 +0700
+++ b/src/semicongine/vulkan/swapchain.nim	Mon Mar 06 23:50:21 2023 +0700
@@ -3,17 +3,23 @@
 import ./device
 import ./physicaldevice
 import ./image
+import ../math
 
 type
   Swapchain = object
     vk*: VkSwapchainKHR
     device*: Device
-    images*: seq[Image]
     imageviews*: seq[ImageView]
-    format: VkFormat
+    format*: VkFormat
+    dimension*: TVec2[uint32]
 
 
-proc createSwapchain*(device: Device, surfaceFormat: VkSurfaceFormatKHR, nBuffers=3'u32, presentationMode: VkPresentModeKHR=VK_PRESENT_MODE_MAILBOX_KHR): (Swapchain, VkResult) =
+proc createSwapchain*(
+  device: Device,
+  surfaceFormat: VkSurfaceFormatKHR,
+  nBuffers=3'u32,
+  presentationMode: VkPresentModeKHR=VK_PRESENT_MODE_MAILBOX_KHR
+): (Swapchain, VkResult) =
   assert device.vk.valid
   assert device.physicalDevice.vk.valid
   var capabilities = device.physicalDevice.getSurfaceCapabilities()
@@ -44,7 +50,10 @@
     clipped: true,
   )
   var
-    swapchain = Swapchain(device: device, format: surfaceFormat.format)
+    swapchain = Swapchain(
+      device: device,
+      format: surfaceFormat.format,
+      dimension: TVec2[uint32]([capabilities.currentExtent.width, capabilities.currentExtent.height]))
     createResult = device.vk.vkCreateSwapchainKHR(addr(createInfo), nil, addr(swapchain.vk))
 
   if createResult == VK_SUCCESS:
@@ -54,7 +63,6 @@
     checkVkResult device.vk.vkGetSwapchainImagesKHR(swapChain.vk, addr(nImages), images.toCPointer)
     for vkimage in images:
       let image = Image(vk: vkimage, format: surfaceFormat.format, device: device)
-      swapChain.images.add image
       swapChain.imageviews.add image.createImageView()
 
   return (swapchain, createResult)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/semicongine/vulkan/syncing.nim	Mon Mar 06 23:50:21 2023 +0700
@@ -0,0 +1,37 @@
+import ./api
+import ./device
+
+type
+  Semaphore = object
+    vk*: VkSemaphore
+    device: Device
+  Fence = object
+    vk*: VkFence
+    device: Device
+
+proc createSemaphore(device: Device): Semaphore =
+  assert device.vk.valid
+  var semaphoreInfo = VkSemaphoreCreateInfo(sType: VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO)
+  result.device = device
+  checkVkResult device.vk.vkCreateSemaphore(addr(semaphoreInfo), nil, addr(result.vk))
+
+proc createFence(device: Device): 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
+  checkVkResult device.vk.vkCreateFence(addr(fenceInfo), nil, addr(result.vk))
+
+proc destroy(semaphore: var Semaphore) =
+  assert semaphore.device.vk.valid
+  assert semaphore.vk.valid
+  semaphore.device.vk.vkDestroySemaphore(semaphore.vk, nil)
+  semaphore.vk.reset
+
+proc destroy(fence: var Fence) =
+  assert fence.device.vk.valid
+  assert fence.vk.valid
+  fence.device.vk.vkDestroyFence(fence.vk, nil)
+  fence.vk.reset
--- a/tests/test_vulkan_wrapper.nim	Thu Mar 02 23:16:45 2023 +0700
+++ b/tests/test_vulkan_wrapper.nim	Mon Mar 06 23:50:21 2023 +0700
@@ -2,6 +2,7 @@
 
 import semicongine/vulkan
 import semicongine/platform/window
+import semicongine/math
 
 
 when isMainModule:
@@ -53,15 +54,26 @@
     selectedPhysicalDevice.filterForGraphicsPresentationQueues()
   )
 
-  echo "Created device ", device.physicalDevice.name
+  # setup render pipeline
   var (swapchain, res) = device.createSwapchain(device.physicalDevice.getSurfaceFormats().filterSurfaceFormat())
   if res != VK_SUCCESS:
     raise newException(Exception, "Unable to create swapchain")
+  var renderpass = device.simpleForwardRenderPass(swapchain.format)
+  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)
 
   echo "All successfull"
   echo "Start cleanup"
 
+  commandPool.destroy()
   # cleanup
+  for fb in framebuffers.mitems:
+    fb.destroy()
+  renderpass.destroy()
   swapchain.destroy()
   device.destroy()