changeset 1167:cdf7ec8d04d2

add: MSAA, yipieee
author sam <sam@basx.dev>
date Tue, 25 Jun 2024 20:24:38 +0700
parents 92691ddcb9fe
children 73eaec7e1690 d88f0286681b
files semicongine/engine.nim semicongine/renderer.nim semicongine/vulkan/image.nim semicongine/vulkan/pipeline.nim semicongine/vulkan/renderpass.nim semicongine/vulkan/swapchain.nim
diffstat 6 files changed, 122 insertions(+), 42 deletions(-) [+]
line wrap: on
line diff
--- a/semicongine/engine.nim	Tue Jun 25 17:42:10 2024 +0700
+++ b/semicongine/engine.nim	Tue Jun 25 20:24:38 2024 +0700
@@ -114,6 +114,7 @@
   backFaceCulling = true,
   vSync = false,
   inFlightFrames = 2,
+  samples = VK_SAMPLE_COUNT_1_BIT,
 ) =
 
   assert not engine.renderer.isSome
@@ -130,6 +131,7 @@
     backFaceCulling = backFaceCulling,
     vSync = vSync,
     inFlightFrames = inFlightFrames,
+    samples = samples,
   ))
 
 proc InitRenderer*(engine: var Engine, clearColor = NewVec4f(0, 0, 0, 0), vSync = false) =
--- a/semicongine/renderer.nim	Tue Jun 25 17:42:10 2024 +0700
+++ b/semicongine/renderer.nim	Tue Jun 25 20:24:38 2024 +0700
@@ -62,16 +62,23 @@
   backFaceCulling = true,
   vSync = false,
   inFlightFrames = 2,
+  samples = VK_SAMPLE_COUNT_1_BIT,
 ): Renderer =
   assert device.vk.Valid
 
   result.device = device
-  result.renderPass = device.CreateRenderPass(shaders, clearColor = clearColor, backFaceCulling = backFaceCulling)
+  result.renderPass = device.CreateRenderPass(
+    shaders,
+    clearColor = clearColor,
+    backFaceCulling = backFaceCulling,
+    samples = samples
+  )
   let swapchain = device.CreateSwapchain(
     result.renderPass.vk,
     device.physicalDevice.GetSurfaceFormats().FilterSurfaceFormat(),
     vSync = vSync,
     inFlightFrames = inFlightFrames,
+    samples = samples,
   )
   if not swapchain.isSome:
     raise newException(Exception, "Unable to create swapchain")
--- a/semicongine/vulkan/image.nim	Tue Jun 25 17:42:10 2024 +0700
+++ b/semicongine/vulkan/image.nim	Tue Jun 25 20:24:38 2024 +0700
@@ -150,8 +150,38 @@
       addr region
     )
 
+proc CreateImage*(device: Device, width, height, depth: uint32, format: VkFormat, samples: VkSampleCountFlagBits, usage: openArray[VkImageUsageFlagBits]): VulkanImage =
+  assert device.vk.Valid
+  assert width > 0
+  assert height > 0
+
+  result.device = device
+  result.usage = @usage
+
+
+  result.width = width
+  result.height = height
+  result.depth = depth
+  result.format = format
+
+  var imageInfo = VkImageCreateInfo(
+    sType: VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+    imageType: VK_IMAGE_TYPE_2D,
+    extent: VkExtent3D(width: uint32(width), height: uint32(height), depth: 1),
+    mipLevels: 1,
+    arrayLayers: 1,
+    format: result.format,
+    tiling: VK_IMAGE_TILING_OPTIMAL,
+    initialLayout: VK_IMAGE_LAYOUT_UNDEFINED,
+    usage: toBits result.usage,
+    sharingMode: VK_SHARING_MODE_EXCLUSIVE,
+    samples: samples,
+  )
+  checkVkResult device.vk.vkCreateImage(addr imageInfo, nil, addr result.vk)
+  result.allocateMemory(requireMappable = false, preferVRAM = true, preferAutoFlush = false)
+
 # currently only usable for texture access from shader
-proc createImage[T](device: Device, queue: Queue, width, height: uint32, depth: PixelDepth, image: Image[T]): VulkanImage =
+proc createTextureImage[T](device: Device, queue: Queue, width, height: uint32, depth: PixelDepth, image: Image[T]): VulkanImage =
   assert device.vk.Valid
   assert width > 0
   assert height > 0
@@ -319,9 +349,9 @@
 proc UploadTexture*(device: Device, queue: Queue, texture: Texture): VulkanTexture =
   assert device.vk.Valid
   if texture.isGrayscale:
-    result.image = createImage(device = device, queue = queue, width = texture.grayImage.width, height = texture.grayImage.height, depth = 1, image = texture.grayImage)
+    result.image = createTextureImage(device = device, queue = queue, width = texture.grayImage.width, height = texture.grayImage.height, depth = 1, image = texture.grayImage)
   else:
-    result.image = createImage(device = device, queue = queue, width = texture.colorImage.width, height = texture.colorImage.height, depth = 4, image = texture.colorImage)
+    result.image = createTextureImage(device = device, queue = queue, width = texture.colorImage.width, height = texture.colorImage.height, depth = 4, image = texture.colorImage)
   result.imageView = result.image.CreateImageView()
   result.sampler = result.image.device.CreateSampler(texture.sampler)
 
--- a/semicongine/vulkan/pipeline.nim	Tue Jun 25 17:42:10 2024 +0700
+++ b/semicongine/vulkan/pipeline.nim	Tue Jun 25 20:24:38 2024 +0700
@@ -55,7 +55,7 @@
             descriptor.imageviews.add emptyTexture.imageView
             descriptor.samplers.add emptyTexture.sampler
 
-proc CreatePipeline*(device: Device, renderPass: VkRenderPass, shaderConfiguration: ShaderConfiguration, inFlightFrames: int, subpass = 0'u32, backFaceCulling = true): ShaderPipeline =
+proc CreatePipeline*(device: Device, renderPass: VkRenderPass, shaderConfiguration: ShaderConfiguration, inFlightFrames: int, subpass = 0'u32, backFaceCulling = true, samples = VK_SAMPLE_COUNT_1_BIT): ShaderPipeline =
   assert renderPass.Valid
   assert device.vk.Valid
 
@@ -128,7 +128,7 @@
     multisampling = VkPipelineMultisampleStateCreateInfo(
       sType: VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
       sampleShadingEnable: VK_FALSE,
-      rasterizationSamples: VK_SAMPLE_COUNT_1_BIT,
+      rasterizationSamples: samples,
       minSampleShading: 1.0,
       pSampleMask: nil,
       alphaToCoverageEnable: VK_FALSE,
--- a/semicongine/vulkan/renderpass.nim	Tue Jun 25 17:42:10 2024 +0700
+++ b/semicongine/vulkan/renderpass.nim	Tue Jun 25 20:24:38 2024 +0700
@@ -19,6 +19,7 @@
   clearColor = Vec4f([0.8'f32, 0.8'f32, 0.8'f32, 1'f32]),
   backFaceCulling = true,
   inFlightFrames = 2,
+  samples = VK_SAMPLE_COUNT_1_BIT
 ): RenderPass =
   assert device.vk.Valid
 
@@ -26,54 +27,69 @@
   for (materialtype, shaderconfig) in shaders:
     shaderconfig.AssertCanRender(materialtype)
 
-  var
-    attachments = @[VkAttachmentDescription(
+  var attachments = @[
+      VkAttachmentDescription(
         format: device.physicalDevice.GetSurfaceFormats().FilterSurfaceFormat().format,
-        samples: VK_SAMPLE_COUNT_1_BIT,
+        samples: samples,
         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,
-    )]
+        finalLayout: if samples == VK_SAMPLE_COUNT_1_BIT: VK_IMAGE_LAYOUT_PRESENT_SRC_KHR else: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+    ),
+    ]
+
+  if samples != VK_SAMPLE_COUNT_1_BIT:
+    attachments.add VkAttachmentDescription(
+      format: device.physicalDevice.GetSurfaceFormats().FilterSurfaceFormat().format,
+      samples: VK_SAMPLE_COUNT_1_BIT,
+      loadOp: VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+      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,
+    )
+
+  var
     # dependencies seems to be optional, TODO: benchmark difference
     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],
+      srcStageMask: toBits [VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT],
+      srcAccessMask: toBits [VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT],
+      dstStageMask: toBits [VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT],
+      dstAccessMask: toBits [VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT],
     )]
-    outputs = @[
-      VkAttachmentReference(
-        attachment: 0,
-        layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
-      )
-    ]
+    colorAttachment = VkAttachmentReference(
+      attachment: 0,
+      layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+    )
+    resolveAttachment = VkAttachmentReference(
+      attachment: 1,
+      layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+    )
 
-  var subpassesList = [
-    VkSubpassDescription(
-      flags: VkSubpassDescriptionFlags(0),
-      pipelineBindPoint: VK_PIPELINE_BIND_POINT_GRAPHICS,
-      inputAttachmentCount: 0,
-      pInputAttachments: nil,
-      colorAttachmentCount: uint32(outputs.len),
-      pColorAttachments: outputs.ToCPointer,
-      pResolveAttachments: nil,
-      pDepthStencilAttachment: nil,
-      preserveAttachmentCount: 0,
-      pPreserveAttachments: nil,
-    )
-  ]
+  var subpass = VkSubpassDescription(
+    flags: VkSubpassDescriptionFlags(0),
+    pipelineBindPoint: VK_PIPELINE_BIND_POINT_GRAPHICS,
+    inputAttachmentCount: 0,
+    pInputAttachments: nil,
+    colorAttachmentCount: 1,
+    pColorAttachments: addr(colorAttachment),
+    pResolveAttachments: if samples == VK_SAMPLE_COUNT_1_BIT: nil else: addr(resolveAttachment),
+    pDepthStencilAttachment: nil,
+    preserveAttachmentCount: 0,
+    pPreserveAttachments: nil,
+  )
 
   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,
+      subpassCount: 1,
+      pSubpasses: addr(subpass),
       dependencyCount: uint32(dependencies.len),
       pDependencies: dependencies.ToCPointer,
     )
@@ -84,7 +100,7 @@
   for (_, shaderconfig) in shaders:
     assert shaderconfig.outputs.len == 1
   for (materialtype, shaderconfig) in shaders:
-    result.shaderPipelines.add (materialtype, device.CreatePipeline(result.vk, shaderconfig, inFlightFrames, 0, backFaceCulling = backFaceCulling))
+    result.shaderPipelines.add (materialtype, device.CreatePipeline(result.vk, shaderconfig, inFlightFrames, 0, backFaceCulling = backFaceCulling, samples = samples))
 
 proc BeginRenderCommands*(commandBuffer: VkCommandBuffer, renderpass: RenderPass, framebuffer: Framebuffer, oneTimeSubmit: bool) =
   assert commandBuffer.Valid
--- a/semicongine/vulkan/swapchain.nim	Tue Jun 25 17:42:10 2024 +0700
+++ b/semicongine/vulkan/swapchain.nim	Tue Jun 25 20:24:38 2024 +0700
@@ -17,6 +17,9 @@
     nFramebuffers*: uint32
     currentInFlight*: int
     currentFramebufferIndex: uint32
+    samples: VkSampleCountFlagBits
+    colorImage: VulkanImage
+    colorImageView: ImageView
     framebufferViews*: seq[ImageView]
     framebuffers*: seq[Framebuffer]
     queueFinishedFence*: seq[Fence]
@@ -35,9 +38,10 @@
   renderPass: VkRenderPass,
   surfaceFormat: VkSurfaceFormatKHR,
   inFlightFrames: int,
+  samples: VkSampleCountFlagBits,
   desiredFramebufferCount = 3'u32,
   oldSwapchain = VkSwapchainKHR(0),
-  vSync = false
+  vSync = false,
 ): Option[Swapchain] =
   assert device.vk.Valid
   assert device.physicalDevice.vk.Valid
@@ -79,9 +83,21 @@
       dimension: TVec2[uint32]([capabilities.currentExtent.width, capabilities.currentExtent.height]),
       inFlightFrames: inFlightFrames,
       renderPass: renderPass,
-      vSync: vSync
+      vSync: vSync,
+      samples: samples,
     )
 
+  if samples != VK_SAMPLE_COUNT_1_BIT:
+    swapchain.colorImage = device.CreateImage(
+      width = capabilities.currentExtent.width,
+      height = capabilities.currentExtent.height,
+      depth = 4,
+      samples = samples,
+      format = surfaceFormat.format,
+      usage = [VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT],
+    )
+    swapchain.colorImageView = swapchain.colorImage.CreateImageView()
+
   if device.vk.vkCreateSwapchainKHR(addr createInfo, nil, addr swapchain.vk) == VK_SUCCESS:
     checkVkResult device.vk.vkGetSwapchainImagesKHR(swapchain.vk, addr swapchain.nFramebuffers, nil)
     var framebuffers = newSeq[VkImage](swapchain.nFramebuffers)
@@ -89,7 +105,10 @@
     for framebuffer in framebuffers:
       let framebufferView = VulkanImage(vk: framebuffer, format: surfaceFormat.format, device: device).CreateImageView()
       swapchain.framebufferViews.add framebufferView
-      swapchain.framebuffers.add device.CreateFramebuffer(renderPass, [framebufferView], swapchain.dimension)
+      if samples == VK_SAMPLE_COUNT_1_BIT:
+        swapchain.framebuffers.add device.CreateFramebuffer(renderPass, [framebufferView], swapchain.dimension)
+      else:
+        swapchain.framebuffers.add device.CreateFramebuffer(renderPass, [swapchain.colorImageView, framebufferView], swapchain.dimension)
     for i in 0 ..< swapchain.inFlightFrames:
       swapchain.queueFinishedFence.add device.CreateFence()
       swapchain.imageAvailableSemaphore.add device.CreateSemaphore()
@@ -97,6 +116,7 @@
     debug &"Created swapchain with: {swapchain.nFramebuffers} framebuffers, {inFlightFrames} in-flight frames, {swapchain.dimension.x}x{swapchain.dimension.y}"
     assert device.FirstPresentationQueue().isSome, "No present queue found"
     swapchain.presentQueue = device.FirstPresentationQueue().get
+
     result = some(swapchain)
   else:
     result = none(Swapchain)
@@ -173,6 +193,10 @@
   for framebuffer in swapchain.framebuffers.mitems:
     assert framebuffer.vk.Valid
     framebuffer.Destroy()
+  if swapchain.colorImage.vk.Valid:
+    swapchain.colorImage.Destroy()
+  if swapchain.colorImageView.vk.Valid:
+    swapchain.colorImageView.Destroy()
   for i in 0 ..< swapchain.inFlightFrames:
     assert swapchain.queueFinishedFence[i].vk.Valid
     assert swapchain.imageAvailableSemaphore[i].vk.Valid
@@ -194,5 +218,6 @@
     desiredFramebufferCount = swapchain.nFramebuffers,
     inFlightFrames = swapchain.inFlightFrames,
     oldSwapchain = swapchain.vk,
-    vSync = swapchain.vSync
+    vSync = swapchain.vSync,
+    samples = swapchain.samples,
   )