changeset 1229:5dcb503ef0c0

did: refactor renderpass a bit, enable depth buffering and msaa on offscreen-rendering
author sam <sam@basx.dev>
date Thu, 18 Jul 2024 21:32:41 +0700
parents 4e465583ea32
children 51221494caeb
files semiconginev2/rendering.nim semiconginev2/rendering/renderer.nim semiconginev2/rendering/renderpasses.nim semiconginev2/rendering/shaders.nim semiconginev2/rendering/swapchain.nim semiconginev2/rendering/vulkan_wrappers.nim semiconginev2/resources.nim tests/test_rendering.nim
diffstat 8 files changed, 337 insertions(+), 147 deletions(-) [+]
line wrap: on
line diff
--- a/semiconginev2/rendering.nim	Thu Jul 18 16:33:24 2024 +0700
+++ b/semiconginev2/rendering.nim	Thu Jul 18 21:32:41 2024 +0700
@@ -12,6 +12,8 @@
 const MEMORY_BLOCK_ALLOCATION_SIZE = 100_000_000'u64 # ca. 100mb per block, seems reasonable
 const BUFFER_ALLOCATION_SIZE = 9_000_000'u64 # ca. 9mb per block, seems reasonable, can put 10 buffers into one memory block
 const MAX_DESCRIPTORSETS = 4
+const SURFACE_FORMAT* = VK_FORMAT_B8G8R8A8_SRGB
+const DEPTH_FORMAT* = VK_FORMAT_D32_SFLOAT
 
 # custom pragmas to classify shader attributes
 template VertexAttribute* {.pragma.}
@@ -42,29 +44,37 @@
     debugMessenger: VkDebugUtilsMessengerEXT
     # unclear as of yet
     anisotropy*: float32 = 0 # needs to be enable during device creation
-  Swapchain* = object
+  Renderpass* = ref object
+    vk*: VkRenderPass
+    samples*: VkSampleCountFlagBits
+    depthBuffer*: bool
+  Swapchain* = ref object
     # parameters to InitSwapchain, required for swapchain recreation
-    renderPass: VkRenderPass
+    renderPass*: RenderPass
     vSync: bool
-    samples*: VkSampleCountFlagBits
     # populated through InitSwapchain proc
     vk: VkSwapchainKHR
     width*: uint32
     height*: uint32
-    msaaImage: VkImage
-    msaaMemory: VkDeviceMemory
-    msaaImageView: VkImageView
     framebuffers: seq[VkFramebuffer]
     framebufferViews: seq[VkImageView]
     currentFramebufferIndex: uint32
     commandBufferPool: VkCommandPool
+    # depth buffer stuff, if enabled
+    depthImage: VkImage
+    depthImageView*: VkImageView
+    depthMemory: VkDeviceMemory
+    # MSAA stuff, if enabled
+    msaaImage: VkImage
+    msaaImageView*: VkImageView
+    msaaMemory: VkDeviceMemory
     # frame-in-flight handling
     currentFiF: range[0 .. (INFLIGHTFRAMES - 1).int]
     queueFinishedFence*: array[INFLIGHTFRAMES.int, VkFence]
     imageAvailableSemaphore*: array[INFLIGHTFRAMES.int, VkSemaphore]
     renderFinishedSemaphore*: array[INFLIGHTFRAMES.int, VkSemaphore]
     commandBuffers: array[INFLIGHTFRAMES.int, VkCommandBuffer]
-    oldSwapchain: ref Swapchain
+    oldSwapchain: Swapchain
     oldSwapchainCounter: int # swaps until old swapchain will be destroyed
 
 var vulkan*: VulkanGlobals
@@ -115,6 +125,7 @@
     imageview*: VkImageView
     sampler*: VkSampler
     isRenderTarget*: bool = false
+    samples*: VkSampleCountFlagBits = VK_SAMPLE_COUNT_1_BIT
   GPUArray*[T: SupportedGPUType, TBuffer: static BufferType] = object
     data*: seq[T]
     buffer*: Buffer
--- a/semiconginev2/rendering/renderer.nim	Thu Jul 18 16:33:24 2024 +0700
+++ b/semiconginev2/rendering/renderer.nim	Thu Jul 18 21:32:41 2024 +0700
@@ -444,7 +444,7 @@
     usage.add VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
   let format = GetVkFormat(elementType(image.data) is TVec1[uint8], usage = usage)
 
-  image.vk = svkCreate2DImage(image.width, image.height, format, usage)
+  image.vk = svkCreate2DImage(image.width, image.height, format, usage, image.samples)
   renderData.images.add image.vk
   image.sampler = createSampler(magFilter = image.interpolation, minFilter = image.interpolation)
   renderData.samplers.add image.sampler
@@ -485,12 +485,13 @@
 
   # data transfer and layout transition
   TransitionImageLayout(image.vk, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
-  WithStagingBuffer(
-    (image.vk, image.width, image.height),
-    memoryRequirements.size,
-    stagingPtr
-  ):
-    copyMem(stagingPtr, image.data.ToCPointer, image.size)
+  if image.data.len > 0:
+    WithStagingBuffer(
+      (image.vk, image.width, image.height),
+      memoryRequirements.size,
+      stagingPtr
+    ):
+      copyMem(stagingPtr, image.data.ToCPointer, image.size)
   TransitionImageLayout(image.vk, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
 
 
--- a/semiconginev2/rendering/renderpasses.nim	Thu Jul 18 16:33:24 2024 +0700
+++ b/semiconginev2/rendering/renderpasses.nim	Thu Jul 18 21:32:41 2024 +0700
@@ -1,9 +1,9 @@
-proc CreateDirectPresentationRenderPass*(samples = VK_SAMPLE_COUNT_1_BIT): VkRenderPass =
+proc CreateDirectPresentationRenderPass*(depthBuffer: bool, samples = VK_SAMPLE_COUNT_1_BIT): RenderPass =
   assert vulkan.instance.Valid, "Vulkan not initialized"
+  result = RenderPass(depthBuffer: depthBuffer, samples: samples)
 
-  let format = DefaultSurfaceFormat()
   var attachments = @[VkAttachmentDescription(
-    format: format,
+    format: SURFACE_FORMAT,
     samples: samples,
     loadOp: VK_ATTACHMENT_LOAD_OP_CLEAR,
     storeOp: VK_ATTACHMENT_STORE_OP_STORE,
@@ -12,9 +12,20 @@
     initialLayout: VK_IMAGE_LAYOUT_UNDEFINED,
     finalLayout: if samples == VK_SAMPLE_COUNT_1_BIT: VK_IMAGE_LAYOUT_PRESENT_SRC_KHR else: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
   )]
+  if depthBuffer:
+    attachments.add VkAttachmentDescription(
+      format: DEPTH_FORMAT,
+      samples: samples,
+      loadOp: VK_ATTACHMENT_LOAD_OP_CLEAR,
+      storeOp: VK_ATTACHMENT_STORE_OP_DONT_CARE,
+      stencilLoadOp: VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+      stencilStoreOp: VK_ATTACHMENT_STORE_OP_DONT_CARE,
+      initialLayout: VK_IMAGE_LAYOUT_UNDEFINED,
+      finalLayout: VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
+    )
   if samples != VK_SAMPLE_COUNT_1_BIT:
     attachments.add VkAttachmentDescription(
-      format: format,
+      format: SURFACE_FORMAT,
       samples: VK_SAMPLE_COUNT_1_BIT,
       loadOp: VK_ATTACHMENT_LOAD_OP_DONT_CARE,
       storeOp: VK_ATTACHMENT_STORE_OP_STORE,
@@ -30,49 +41,82 @@
         dstSubpass: 0,
         srcStageMask: toBits [VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT],
         dstStageMask: toBits [VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT],
-        srcAccessMask: toBits [VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT],
-        dstAccessMask: toBits [VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT],
+        srcAccessMask: toBits [VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT],
+        dstAccessMask: toBits [VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT],
       )
     ]
     colorAttachment = VkAttachmentReference(
       attachment: 0,
       layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
     )
+    depthAttachment = VkAttachmentReference(
+      attachment: 1,
+      layout: VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
+    )
     resolveAttachment = VkAttachmentReference(
-      attachment: 1,
+      attachment: (attachments.len - 1).uint32, # depending on whether depthBuffer is used or not
       layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
     )
 
-  if samples == VK_SAMPLE_COUNT_1_BIT:
-    return svkCreateRenderPass(attachments, [colorAttachment], [], dependencies)
-  else:
-    return svkCreateRenderPass(attachments, [colorAttachment], [resolveAttachment], dependencies)
+  result.vk = svkCreateRenderPass(
+    attachments = attachments,
+    colorAttachments = [colorAttachment],
+    depthAttachments = if depthBuffer: @[depthAttachment] else: @[],
+    resolveAttachments = if samples > VK_SAMPLE_COUNT_1_BIT: @[resolveAttachment] else: @[],
+    dependencies = dependencies,
+  )
 
-proc CreateIndirectPresentationRenderPass*(): (VkRenderPass, VkRenderPass) =
+proc CreateIndirectPresentationRenderPass*(depthBuffer: bool, samples = VK_SAMPLE_COUNT_1_BIT): (RenderPass, RenderPass) =
   assert vulkan.instance.Valid, "Vulkan not initialized"
 
+  result[0] = RenderPass(depthBuffer: depthBuffer, samples: samples)
+  result[1] = RenderPass(depthBuffer: false, samples: VK_SAMPLE_COUNT_1_BIT)
+
   # first renderpass, drawing
-  let format = DefaultSurfaceFormat()
   var
     attachments = @[VkAttachmentDescription(
-      format: format,
-      samples: VK_SAMPLE_COUNT_1_BIT,
+      format: SURFACE_FORMAT, # not strictly necessary
+      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_SHADER_READ_ONLY_OPTIMAL,
+      # finalLayout: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+      finalLayout: if samples == VK_SAMPLE_COUNT_1_BIT: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL else: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
     )]
+  if depthBuffer:
+    attachments.add VkAttachmentDescription(
+      format: DEPTH_FORMAT,
+      samples: samples,
+      loadOp: VK_ATTACHMENT_LOAD_OP_CLEAR,
+      storeOp: VK_ATTACHMENT_STORE_OP_DONT_CARE,
+      stencilLoadOp: VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+      stencilStoreOp: VK_ATTACHMENT_STORE_OP_DONT_CARE,
+      initialLayout: VK_IMAGE_LAYOUT_UNDEFINED,
+      finalLayout: VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
+    )
+  if samples != VK_SAMPLE_COUNT_1_BIT:
+    attachments.add VkAttachmentDescription(
+      format: SURFACE_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_SHADER_READ_ONLY_OPTIMAL,
+    )
 
+  var
     dependencies = @[
       VkSubpassDependency(
         srcSubpass: VK_SUBPASS_EXTERNAL,
         dstSubpass: 0,
-        srcStageMask: toBits [VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT],
-        dstStageMask: toBits [VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT],
-        srcAccessMask: VkAccessFlags(0),
-        dstAccessMask: toBits [VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT],
+        srcStageMask: toBits [VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT],
+        dstStageMask: toBits [VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT],
+        srcAccessMask: toBits [VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT],
+        dstAccessMask: toBits [VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT],
       ),
       VkSubpassDependency(
         srcSubpass: VK_SUBPASS_EXTERNAL,
@@ -95,11 +139,26 @@
       attachment: 0,
       layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
     )
+    depthAttachment = VkAttachmentReference(
+      attachment: 1,
+      layout: VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
+    )
+    resolveAttachment = VkAttachmentReference(
+      attachment: (attachments.len - 1).uint32, # depending on whether depthBuffer is used or not
+      layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+    )
+  result[0].vk = svkCreateRenderPass(
+    attachments = attachments,
+    colorAttachments = [colorAttachment],
+    depthAttachments = if depthBuffer: @[depthAttachment] else: @[],
+    resolveAttachments = if samples > VK_SAMPLE_COUNT_1_BIT: @[resolveAttachment] else: @[],
+    dependencies = dependencies
+  )
 
   # second renderpass, presentation
   var
     presentAttachments = @[VkAttachmentDescription(
-      format: format,
+      format: SURFACE_FORMAT,
       samples: VK_SAMPLE_COUNT_1_BIT,
       loadOp: VK_ATTACHMENT_LOAD_OP_CLEAR,
       storeOp: VK_ATTACHMENT_STORE_OP_STORE,
@@ -121,13 +180,16 @@
       layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
     )
 
-  result = (
-    svkCreateRenderPass(attachments, [colorAttachment], [], dependencies),
-    svkCreateRenderPass(presentAttachments, [presentColorAttachment], [], presentDependencies)
+  result[1].vk = svkCreateRenderPass(
+    attachments = presentAttachments,
+    colorAttachments = [presentColorAttachment],
+    depthAttachments = [],
+    resolveAttachments = [],
+    dependencies = presentDependencies
   )
 
 template WithRenderPass*(
-  theRenderpass: VkRenderPass,
+  theRenderpass: RenderPass,
   theFramebuffer: VkFramebuffer,
   commandbuffer: VkCommandBuffer,
   renderWidth: uint32,
@@ -136,16 +198,19 @@
   body: untyped
 ): untyped =
   var
-    clearColors = [VkClearValue(color: VkClearColorValue(float32: clearColor))]
+    clearColors = [
+      VkClearValue(color: VkClearColorValue(float32: clearColor)),
+      VkClearValue(depthStencil: VkClearDepthStencilValue(depth: 1'f32, stencil: 0))
+    ]
     renderPassInfo = VkRenderPassBeginInfo(
       sType: VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
-      renderPass: theRenderpass,
+      renderPass: theRenderpass.vk,
       framebuffer: theFramebuffer,
       renderArea: VkRect2D(
         offset: VkOffset2D(x: 0, y: 0),
         extent: VkExtent2D(width: renderWidth, height: renderHeight),
       ),
-      clearValueCount: uint32(clearColors.len),
+      clearValueCount: clearColors.len.uint32,
       pClearValues: clearColors.ToCPointer(),
     )
     viewport = VkViewport(
--- a/semiconginev2/rendering/shaders.nim	Thu Jul 18 16:33:24 2024 +0700
+++ b/semiconginev2/rendering/shaders.nim	Thu Jul 18 21:32:41 2024 +0700
@@ -344,13 +344,12 @@
         inc setNumber
 
 proc CreatePipeline*[TShader](
-  renderPass: VkRenderPass,
+  renderPass: RenderPass,
   topology: VkPrimitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
   polygonMode: VkPolygonMode = VK_POLYGON_MODE_FILL,
   cullMode: VkCullModeFlagBits = VK_CULL_MODE_BACK_BIT,
   frontFace: VkFrontFace = VK_FRONT_FACE_CLOCKWISE,
   descriptorPoolLimit = 1024,
-  samples = VK_SAMPLE_COUNT_1_BIT,
 ): Pipeline[TShader] =
   # create pipeline
 
@@ -439,12 +438,23 @@
     multisampling = VkPipelineMultisampleStateCreateInfo(
       sType: VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
       sampleShadingEnable: VK_FALSE,
-      rasterizationSamples: samples,
+      rasterizationSamples: renderPass.samples,
       minSampleShading: 1.0,
       pSampleMask: nil,
       alphaToCoverageEnable: VK_FALSE,
       alphaToOneEnable: VK_FALSE,
     )
+    # will only be enabled it the renderpass actually uses a depth buffer
+    depthStencil = VkPipelineDepthStencilStateCreateInfo(
+      sType: VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
+      depthTestEnable: true,
+      depthWriteEnable: true,
+      depthCompareOp: VK_COMPARE_OP_LESS,
+      depthBoundsTestEnable: false,
+      stencilTestEnable: false,
+      minDepthBounds: 0'f32,
+      maxDepthBounds: 0'f32,
+    )
     colorBlendAttachment = VkPipelineColorBlendAttachmentState(
       colorWriteMask: toBits [VK_COLOR_COMPONENT_R_BIT, VK_COLOR_COMPONENT_G_BIT, VK_COLOR_COMPONENT_B_BIT, VK_COLOR_COMPONENT_A_BIT],
       blendEnable: VK_TRUE,
@@ -476,11 +486,11 @@
     pViewportState: addr(viewportState),
     pRasterizationState: addr(rasterizer),
     pMultisampleState: addr(multisampling),
-    pDepthStencilState: nil,
+    pDepthStencilState: if renderPass.depthBuffer: addr(depthStencil) else: nil,
     pColorBlendState: addr(colorBlending),
     pDynamicState: addr(dynamicState),
     layout: result.layout,
-    renderPass: renderPass,
+    renderPass: renderPass.vk,
     subpass: 0,
     basePipelineHandle: VkPipeline(0),
     basePipelineIndex: -1,
--- a/semiconginev2/rendering/swapchain.nim	Thu Jul 18 16:33:24 2024 +0700
+++ b/semiconginev2/rendering/swapchain.nim	Thu Jul 18 21:32:41 2024 +0700
@@ -1,17 +1,15 @@
 const N_FRAMEBUFFERS = 3'u32
 
 proc InitSwapchain*(
-  renderPass: VkRenderPass,
+  renderPass: RenderPass,
   vSync: bool = false,
-  samples = VK_SAMPLE_COUNT_1_BIT,
-  oldSwapchain: ref Swapchain = nil,
+  oldSwapchain: Swapchain = nil,
 ): Option[Swapchain] =
   assert vulkan.instance.Valid, "Vulkan not initialized"
 
   var capabilities: VkSurfaceCapabilitiesKHR
   checkVkResult vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vulkan.physicalDevice, vulkan.surface, addr(capabilities))
   let
-    format = DefaultSurfaceFormat()
     width = capabilities.currentExtent.width
     height = capabilities.currentExtent.height
 
@@ -30,7 +28,7 @@
     sType: VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
     surface: vulkan.surface,
     minImageCount: minFramebufferCount,
-    imageFormat: format,
+    imageFormat: SURFACE_FORMAT,
     imageColorSpace: VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, # only one supported without special extensions
     imageExtent: capabilities.currentExtent,
     imageArrayLayers: 1,
@@ -42,27 +40,54 @@
     clipped: true,
     oldSwapchain: if oldSwapchain != nil: oldSwapchain.vk else: VkSwapchainKHR(0),
   )
-  var swapchain: Swapchain
+  var swapchain = Swapchain(
+    width: width,
+    height: height,
+    renderPass: renderPass,
+    vSync: vSync,
+    oldSwapchain: oldSwapchain,
+  )
+
   if vkCreateSwapchainKHR(vulkan.device, addr(swapchainCreateInfo), nil, addr(swapchain.vk)) != VK_SUCCESS:
     return none(Swapchain)
 
-  swapchain.width = width
-  swapchain.height = height
-  swapchain.renderPass = renderPass
-  swapchain.vSync = vSync
-  swapchain.samples = samples
-  swapchain.oldSwapchain = oldSwapchain
   if swapchain.oldSwapchain != nil:
     swapchain.oldSwapchainCounter = INFLIGHTFRAMES.int * 2
 
+  # create depth buffer image+view if desired
+  if renderPass.depthBuffer:
+    swapchain.depthImage = svkCreate2DImage(
+      width = width,
+      height = height,
+      format = DEPTH_FORMAT,
+      usage = [VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT],
+      samples = renderPass.samples,
+    )
+    let requirements = svkGetImageMemoryRequirements(swapchain.depthImage)
+    swapchain.depthMemory = svkAllocateMemory(
+      requirements.size,
+      BestMemory(mappable = false, filter = requirements.memoryTypes)
+    )
+    checkVkResult vkBindImageMemory(
+      vulkan.device,
+      swapchain.depthImage,
+      swapchain.depthMemory,
+      0,
+    )
+    swapchain.depthImageView = svkCreate2DImageView(
+      image = swapchain.depthImage,
+      format = DEPTH_FORMAT,
+      aspect = VK_IMAGE_ASPECT_DEPTH_BIT
+    )
+
   # create msaa image+view if desired
-  if samples != VK_SAMPLE_COUNT_1_BIT:
+  if renderPass.samples != VK_SAMPLE_COUNT_1_BIT:
     swapchain.msaaImage = svkCreate2DImage(
       width = width,
       height = height,
-      format = format,
+      format = SURFACE_FORMAT,
       usage = [VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT],
-      samples = samples,
+      samples = renderPass.samples,
     )
     let requirements = svkGetImageMemoryRequirements(swapchain.msaaImage)
     swapchain.msaaMemory = svkAllocateMemory(
@@ -75,7 +100,7 @@
       swapchain.msaaMemory,
       0,
     )
-    swapchain.msaaImageView = svkCreate2DImageView(swapchain.msaaImage, format)
+    swapchain.msaaImageView = svkCreate2DImageView(image = swapchain.msaaImage, format = SURFACE_FORMAT)
 
   # create framebuffers
   var actualNFramebuffers: uint32
@@ -84,11 +109,25 @@
   checkVkResult vkGetSwapchainImagesKHR(vulkan.device, swapchain.vk, addr(actualNFramebuffers), framebuffers.ToCPointer)
 
   for framebuffer in framebuffers:
-    swapchain.framebufferViews.add svkCreate2DImageView(framebuffer, format)
-    if samples == VK_SAMPLE_COUNT_1_BIT:
-      swapchain.framebuffers.add svkCreateFramebuffer(renderPass, width, height, [swapchain.framebufferViews[^1]])
+    swapchain.framebufferViews.add svkCreate2DImageView(framebuffer, SURFACE_FORMAT)
+    var attachments: seq[VkImageView]
+    if renderPass.samples == VK_SAMPLE_COUNT_1_BIT:
+      if renderPass.depthBuffer:
+        attachments = @[swapchain.framebufferViews[^1], swapchain.depthImageView]
+      else:
+        attachments = @[swapchain.framebufferViews[^1]]
     else:
-      swapchain.framebuffers.add svkCreateFramebuffer(renderPass, width, height, [swapchain.msaaImageView, swapchain.framebufferViews[^1]])
+      if renderPass.depthBuffer:
+        attachments = @[swapchain.msaaImageView, swapchain.depthImageView, swapchain.framebufferViews[^1]]
+      else:
+        attachments = @[swapchain.msaaImageView, swapchain.framebufferViews[^1]]
+
+    swapchain.framebuffers.add svkCreateFramebuffer(
+      renderpass = renderPass.vk,
+      width = width,
+      height = height,
+      attachments = attachments,
+    )
 
   # create sync primitives
   for i in 0 ..< INFLIGHTFRAMES:
@@ -115,11 +154,16 @@
 
 proc DestroySwapchain*(swapchain: Swapchain) =
 
-  if swapchain.samples != VK_SAMPLE_COUNT_1_BIT:
+  if swapchain.msaaImage.Valid:
     vkDestroyImageView(vulkan.device, swapchain.msaaImageView, nil)
     vkDestroyImage(vulkan.device, swapchain.msaaImage, nil)
     vkFreeMemory(vulkan.device, swapchain.msaaMemory, nil)
 
+  if swapchain.depthImage.Valid:
+    vkDestroyImageView(vulkan.device, swapchain.depthImageView, nil)
+    vkDestroyImage(vulkan.device, swapchain.depthImage, nil)
+    vkFreeMemory(vulkan.device, swapchain.depthMemory, nil)
+
   for fence in swapchain.queueFinishedFence:
     vkDestroyFence(vulkan.device, fence, nil)
 
@@ -139,7 +183,7 @@
 
   vkDestroySwapchainKHR(vulkan.device, swapchain.vk, nil)
 
-proc TryAcquireNextImage(swapchain: var Swapchain): Option[VkFramebuffer] =
+proc TryAcquireNextImage(swapchain: Swapchain): Option[VkFramebuffer] =
   if not swapchain.queueFinishedFence[swapchain.currentFiF].Await(100_000_000):
     return none(VkFramebuffer)
 
@@ -158,7 +202,7 @@
     return none(VkFramebuffer)
   return some(swapchain.framebuffers[swapchain.currentFramebufferIndex])
 
-proc Swap(swapchain: var Swapchain, commandBuffer: VkCommandBuffer): bool =
+proc Swap(swapchain: Swapchain, commandBuffer: VkCommandBuffer): bool =
   var
     waitStage = VkPipelineStageFlags(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT)
     submitInfo = VkSubmitInfo(
@@ -192,7 +236,7 @@
   if swapchain.oldSwapchain != nil:
     dec swapchain.oldSwapchainCounter
     if swapchain.oldSwapchainCounter <= 0:
-      DestroySwapchain(swapchain.oldSwapchain[])
+      DestroySwapchain(swapchain.oldSwapchain)
       swapchain.oldSwapchain = nil
 
   if presentResult != VK_SUCCESS:
@@ -202,21 +246,18 @@
   return true
 
 proc Recreate(swapchain: Swapchain): Option[Swapchain] =
-  var oldSwapchain = new Swapchain
-  oldSwapchain[] = swapchain
   InitSwapchain(
     renderPass = swapchain.renderPass,
     vSync = swapchain.vSync,
-    samples = swapchain.samples,
-    oldSwapchain = oldSwapchain,
+    oldSwapchain = swapchain,
   )
 
-template WithNextFrame*(swapchain: var Swapchain, framebufferName, commandBufferName, body: untyped): untyped =
-  var maybeFramebuffer = TryAcquireNextImage(swapchain)
+template WithNextFrame*(theSwapchain: var Swapchain, framebufferName, commandBufferName, body: untyped): untyped =
+  var maybeFramebuffer = TryAcquireNextImage(theSwapchain)
   if maybeFramebuffer.isSome:
     block:
       let `framebufferName` {.inject.} = maybeFramebuffer.get
-      let `commandBufferName` {.inject.} = swapchain.commandBuffers[swapchain.currentFiF]
+      let `commandBufferName` {.inject.} = theSwapchain.commandBuffers[theSwapchain.currentFiF]
       let beginInfo = VkCommandBufferBeginInfo(
         sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
         flags: VkCommandBufferUsageFlags(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT),
@@ -227,9 +268,9 @@
       body
 
       checkVkResult vkEndCommandBuffer(`commandBufferName`)
-      discard Swap(swapchain = swapchain, commandBuffer = `commandBufferName`)
+      discard Swap(swapchain = theSwapchain, commandBuffer = `commandBufferName`)
   else:
-    let maybeNewSwapchain = Recreate(swapchain)
+    let maybeNewSwapchain = Recreate(theSwapchain)
     if maybeNewSwapchain.isSome:
-      swapchain = maybeNewSwapchain.get
+      theSwapchain = maybeNewSwapchain.get
 
--- a/semiconginev2/rendering/vulkan_wrappers.nim	Thu Jul 18 16:33:24 2024 +0700
+++ b/semiconginev2/rendering/vulkan_wrappers.nim	Thu Jul 18 21:32:41 2024 +0700
@@ -49,10 +49,6 @@
     addr(result),
   )
 
-proc DefaultSurfaceFormat(): VkFormat =
-  # EVERY windows driver and almost every linux driver should support this
-  VK_FORMAT_B8G8R8A8_SRGB
-
 func size(format: VkFormat): uint64 =
   const formatSize = [
     VK_FORMAT_B8G8R8A8_SRGB.int: 4'u64,
@@ -140,7 +136,7 @@
   )
   checkVkResult vkCreateImage(vulkan.device, addr imageInfo, nil, addr(result))
 
-proc svkCreate2DImageView(image: VkImage, format: VkFormat): VkImageView =
+proc svkCreate2DImageView*(image: VkImage, format: VkFormat, aspect = VK_IMAGE_ASPECT_COLOR_BIT): VkImageView =
   var createInfo = VkImageViewCreateInfo(
     sType: VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
     image: image,
@@ -153,7 +149,7 @@
       a: VK_COMPONENT_SWIZZLE_IDENTITY,
     ),
     subresourceRange: VkImageSubresourceRange(
-      aspectMask: VkImageAspectFlags(VK_IMAGE_ASPECT_COLOR_BIT),
+      aspectMask: toBits [aspect],
       baseMipLevel: 0,
       levelCount: 1,
       baseArrayLayer: 0,
@@ -229,6 +225,7 @@
 proc svkCreateRenderPass(
   attachments: openArray[VkAttachmentDescription],
   colorAttachments: openArray[VkAttachmentReference],
+  depthAttachments: openArray[VkAttachmentReference],
   resolveAttachments: openArray[VkAttachmentReference],
   dependencies: openArray[VkSubpassDependency],
 ): VkRenderPass =
@@ -241,7 +238,7 @@
     colorAttachmentCount: colorAttachments.len.uint32,
     pColorAttachments: colorAttachments.ToCPointer,
     pResolveAttachments: resolveAttachments.ToCPointer,
-    pDepthStencilAttachment: nil,
+    pDepthStencilAttachment: depthAttachments.ToCPointer,
     preserveAttachmentCount: 0,
     pPreserveAttachments: nil,
   )
--- a/semiconginev2/resources.nim	Thu Jul 18 16:33:24 2024 +0700
+++ b/semiconginev2/resources.nim	Thu Jul 18 21:32:41 2024 +0700
@@ -96,8 +96,6 @@
 
 elif thebundletype == Exe:
 
-  import std/tables
-
   const BUILD_RESOURCEROOT* {.strdefine.}: string = ""
 
   proc loadResources(): Table[string, Table[string, string]] {.compileTime.} =
--- a/tests/test_rendering.nim	Thu Jul 18 16:33:24 2024 +0700
+++ b/tests/test_rendering.nim	Thu Jul 18 21:32:41 2024 +0700
@@ -3,11 +3,7 @@
 
 import ../semiconginev2
 
-var
-  mainRenderpass: VkRenderPass
-  swapchain: Swapchain
-
-proc test_01_triangle(nFrames: int, renderPass: VkRenderPass, samples = VK_SAMPLE_COUNT_1_BIT) =
+proc test_01_triangle(nFrames: int, swapchain: var Swapchain) =
   var renderdata = InitRenderData()
 
   type
@@ -33,12 +29,12 @@
   renderdata.FlushAllMemory()
 
   var
-    pipeline = CreatePipeline[TrianglShader](renderPass = renderPass, samples = samples)
+    pipeline = CreatePipeline[TrianglShader](renderPass = swapchain.renderPass)
 
   var c = 0
   while UpdateInputs() and c < nFrames:
     WithNextFrame(swapchain, framebuffer, commandbuffer):
-      WithRenderPass(renderPass, framebuffer, commandbuffer, swapchain.width, swapchain.height, NewVec4f(0, 0, 0, 0)):
+      WithRenderPass(swapchain.renderPass, framebuffer, commandbuffer, swapchain.width, swapchain.height, NewVec4f(0, 0, 0, 0)):
         WithPipeline(commandbuffer, pipeline):
           Render(commandbuffer = commandbuffer, pipeline = pipeline, mesh = mesh)
     inc c
@@ -49,7 +45,7 @@
   DestroyRenderData(renderdata)
 
 
-proc test_02_triangle_quad_instanced(nFrames: int, renderPass: VkRenderPass, samples = VK_SAMPLE_COUNT_1_BIT) =
+proc test_02_triangle_quad_instanced(nFrames: int, swapchain: var Swapchain) =
   var renderdata = InitRenderData()
 
   type
@@ -101,12 +97,12 @@
   AssignBuffers(renderdata, instancesB)
   renderdata.FlushAllMemory()
 
-  var pipeline = CreatePipeline[SomeShader](renderPass = renderPass, samples = samples)
+  var pipeline = CreatePipeline[SomeShader](renderPass = swapchain.renderPass)
 
   var c = 0
   while UpdateInputs() and c < nFrames:
     WithNextFrame(swapchain, framebuffer, commandbuffer):
-      WithRenderPass(renderPass, framebuffer, commandbuffer, swapchain.width, swapchain.height, NewVec4f(0, 0, 0, 0)):
+      WithRenderPass(swapchain.renderPass, framebuffer, commandbuffer, swapchain.width, swapchain.height, NewVec4f(0, 0, 0, 0)):
         WithPipeline(commandbuffer, pipeline):
           Render(commandbuffer = commandbuffer, pipeline = pipeline, mesh = quad, instances = instancesA)
           Render(commandbuffer = commandbuffer, pipeline = pipeline, mesh = quad, instances = instancesB)
@@ -119,7 +115,7 @@
   DestroyPipeline(pipeline)
   DestroyRenderData(renderdata)
 
-proc test_03_simple_descriptorset(nFrames: int, renderPass: VkRenderPass, samples = VK_SAMPLE_COUNT_1_BIT) =
+proc test_03_simple_descriptorset(nFrames: int, swapchain: var Swapchain) =
   var renderdata = InitRenderData()
 
   type
@@ -178,7 +174,7 @@
   UploadImages(renderdata, uniforms2)
   renderdata.FlushAllMemory()
 
-  var pipeline = CreatePipeline[QuadShader](renderPass = renderPass, samples = samples)
+  var pipeline = CreatePipeline[QuadShader](renderPass = swapchain.renderPass)
 
   InitDescriptorSet(renderdata, pipeline.descriptorSetLayouts[0], uniforms1)
   InitDescriptorSet(renderdata, pipeline.descriptorSetLayouts[0], uniforms2)
@@ -186,7 +182,7 @@
   var c = 0
   while UpdateInputs() and c < nFrames:
     WithNextFrame(swapchain, framebuffer, commandbuffer):
-      WithRenderPass(renderPass, framebuffer, commandbuffer, swapchain.width, swapchain.height, NewVec4f(0, 0, 0, 0)):
+      WithRenderPass(swapchain.renderPass, framebuffer, commandbuffer, swapchain.width, swapchain.height, NewVec4f(0, 0, 0, 0)):
         WithPipeline(commandbuffer, pipeline):
           WithBind(commandbuffer, (uniforms1, ), pipeline, swapchain.currentFiF):
             Render(commandbuffer = commandbuffer, pipeline = pipeline, mesh = quad)
@@ -199,7 +195,7 @@
   DestroyPipeline(pipeline)
   DestroyRenderData(renderdata)
 
-proc test_04_multiple_descriptorsets(nFrames: int, renderPass: VkRenderPass, samples = VK_SAMPLE_COUNT_1_BIT) =
+proc test_04_multiple_descriptorsets(nFrames: int, swapchain: var Swapchain) =
   var renderdata = InitRenderData()
 
   type
@@ -286,7 +282,7 @@
   UploadImages(renderdata, mainset)
   renderdata.FlushAllMemory()
 
-  var pipeline = CreatePipeline[QuadShader](renderPass = renderPass, samples = samples)
+  var pipeline = CreatePipeline[QuadShader](renderPass = swapchain.renderPass)
 
   InitDescriptorSet(renderdata, pipeline.descriptorSetLayouts[0], constset)
   InitDescriptorSet(renderdata, pipeline.descriptorSetLayouts[1], mainset)
@@ -297,7 +293,7 @@
   while UpdateInputs() and c < nFrames:
     TimeAndLog:
       WithNextFrame(swapchain, framebuffer, commandbuffer):
-        WithRenderPass(renderPass, framebuffer, commandbuffer, swapchain.width, swapchain.height, NewVec4f(0, 0, 0, 0)):
+        WithRenderPass(swapchain.renderPass, framebuffer, commandbuffer, swapchain.width, swapchain.height, NewVec4f(0, 0, 0, 0)):
           WithPipeline(commandbuffer, pipeline):
             WithBind(commandbuffer, (constset, mainset, otherset1), pipeline, swapchain.currentFiF):
               Render(commandbuffer = commandbuffer, pipeline = pipeline, mesh = quad)
@@ -315,9 +311,9 @@
   DestroyPipeline(pipeline)
   DestroyRenderData(renderdata)
 
-proc test_05_triangle_2pass(nFrames: int, samples = VK_SAMPLE_COUNT_1_BIT) =
+proc test_05_triangle_2pass(nFrames: int) =
   var
-    (offscreenRP, presentRP) = CreateIndirectPresentationRenderPass()
+    (offscreenRP, presentRP) = CreateIndirectPresentationRenderPass(depthBuffer = true, samples = VK_SAMPLE_COUNT_4_BIT)
     swapchain = InitSwapchain(renderpass = presentRP).get()
 
   var renderdata = InitRenderData()
@@ -376,25 +372,91 @@
       frameTexture: Image[TVec4[uint8]](width: swapchain.width, height: swapchain.height, isRenderTarget: true),
     )
   )
-  var uniforms2 = asDescriptorSet(
-    Uniforms(
-      frameTexture: Image[TVec4[uint8]](width: swapchain.width, height: swapchain.height, isRenderTarget: true),
-    )
-  )
   AssignBuffers(renderdata, mesh)
   AssignBuffers(renderdata, quad)
   UploadImages(renderdata, uniforms1)
-  UploadImages(renderdata, uniforms2)
   renderdata.FlushAllMemory()
 
   var
-    drawPipeline = CreatePipeline[TriangleShader](renderPass = offscreenRP, samples = samples)
-    presentPipeline = CreatePipeline[PresentShader](renderPass = presentRP, samples = samples)
+    drawPipeline = CreatePipeline[TriangleShader](renderPass = offscreenRP)
+    presentPipeline = CreatePipeline[PresentShader](renderPass = presentRP)
 
   InitDescriptorSet(renderdata, presentPipeline.descriptorSetLayouts[0], uniforms1)
-  InitDescriptorSet(renderdata, presentPipeline.descriptorSetLayouts[0], uniforms2)
+
+  # create depth buffer images (will not use the one in the swapchain
+  var
+    depthImage: VkImage
+    depthImageView: VkImageView
+    depthMemory: VkDeviceMemory
+  if offscreenRP.depthBuffer:
+    depthImage = svkCreate2DImage(
+      width = swapchain.width,
+      height = swapchain.height,
+      format = DEPTH_FORMAT,
+      usage = [VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT],
+      samples = offscreenRP.samples,
+    )
+    let requirements = svkGetImageMemoryRequirements(depthImage)
+    depthMemory = svkAllocateMemory(
+      requirements.size,
+      BestMemory(mappable = false, filter = requirements.memoryTypes)
+    )
+    checkVkResult vkBindImageMemory(
+      vulkan.device,
+      depthImage,
+      depthMemory,
+      0,
+    )
+    depthImageView = svkCreate2DImageView(
+      image = depthImage,
+      format = DEPTH_FORMAT,
+      aspect = VK_IMAGE_ASPECT_DEPTH_BIT
+    )
 
-  var offscreenFB = svkCreateFramebuffer(offscreenRP, swapchain.width, swapchain.height, [uniforms1.data.frameTexture.imageview])
+  # create msaa images (will not use the one in the swapchain
+  var
+    msaaImage: VkImage
+    msaaImageView: VkImageView
+    msaaMemory: VkDeviceMemory
+  if offscreenRP.samples != VK_SAMPLE_COUNT_1_BIT:
+    msaaImage = svkCreate2DImage(
+      width = swapchain.width,
+      height = swapchain.height,
+      format = SURFACE_FORMAT,
+      usage = [VK_IMAGE_USAGE_SAMPLED_BIT, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT],
+      samples = offscreenRP.samples,
+    )
+    let requirements = svkGetImageMemoryRequirements(msaaImage)
+    msaaMemory = svkAllocateMemory(
+      requirements.size,
+      BestMemory(mappable = false, filter = requirements.memoryTypes)
+    )
+    checkVkResult vkBindImageMemory(
+      vulkan.device,
+      msaaImage,
+      msaaMemory,
+      0,
+    )
+    msaaImageView = svkCreate2DImageView(image = msaaImage, format = SURFACE_FORMAT)
+
+  var attachments: seq[VkImageView]
+  if offscreenRP.samples == VK_SAMPLE_COUNT_1_BIT:
+    if offscreenRP.depthBuffer:
+      attachments = @[uniforms1.data.frameTexture.imageview, depthImageView]
+    else:
+      attachments = @[uniforms1.data.frameTexture.imageview]
+  else:
+    if offscreenRP.depthBuffer:
+      attachments = @[msaaImageView, depthImageView, uniforms1.data.frameTexture.imageview]
+    else:
+      attachments = @[msaaImageView, uniforms1.data.frameTexture.imageview]
+  echo attachments
+  var offscreenFB = svkCreateFramebuffer(
+    offscreenRP.vk,
+    swapchain.width,
+    swapchain.height,
+    attachments
+  )
 
   var c = 0
   while UpdateInputs() and c < nFrames:
@@ -417,50 +479,55 @@
   DestroyPipeline(presentPipeline)
   DestroyPipeline(drawPipeline)
   DestroyRenderData(renderdata)
-  vkDestroyRenderPass(vulkan.device, offscreenRP, nil)
-  vkDestroyRenderPass(vulkan.device, presentRP, nil)
+  if depthImage.Valid:
+    vkDestroyImageView(vulkan.device, depthImageView, nil)
+    vkDestroyImage(vulkan.device, depthImage, nil)
+    vkFreeMemory(vulkan.device, depthMemory, nil)
+  if msaaImage.Valid:
+    vkDestroyImageView(vulkan.device, msaaImageView, nil)
+    vkDestroyImage(vulkan.device, msaaImage, nil)
+    vkFreeMemory(vulkan.device, msaaMemory, nil)
+  vkDestroyRenderPass(vulkan.device, offscreenRP.vk, nil)
+  vkDestroyRenderPass(vulkan.device, presentRP.vk, nil)
   vkDestroyFramebuffer(vulkan.device, offscreenFB, nil)
   DestroySwapchain(swapchain)
 
 when isMainModule:
-  var nFrames = 2000
+  var nFrames = 1000
   InitVulkan()
 
+  var mainRenderpass: RenderPass
+  var renderPasses = [
+    (depthBuffer: false, samples: VK_SAMPLE_COUNT_1_BIT),
+    (depthBuffer: false, samples: VK_SAMPLE_COUNT_4_BIT),
+    (depthBuffer: true, samples: VK_SAMPLE_COUNT_1_BIT),
+    (depthBuffer: true, samples: VK_SAMPLE_COUNT_4_BIT),
+  ]
 
   # test normal
-  block:
-    mainRenderpass = CreateDirectPresentationRenderPass()
-    swapchain = InitSwapchain(renderpass = mainRenderpass).get()
+  if false:
+    for i, (depthBuffer, samples) in renderPasses:
+      var renderpass = CreateDirectPresentationRenderPass(depthBuffer = depthBuffer, samples = samples)
+      var swapchain = InitSwapchain(renderpass = renderpass).get()
 
-    # tests a simple triangle with minimalistic shader and vertex format
-    test_01_triangle(nFrames, renderPass = mainRenderpass)
-
-    # tests instanced triangles and quads, mixing meshes and instances
-    test_02_triangle_quad_instanced(nFrames, renderPass = mainRenderpass)
-
-    # teste descriptor sets
-    test_03_simple_descriptorset(nFrames, renderPass = mainRenderpass)
+      # tests a simple triangle with minimalistic shader and vertex format
+      test_01_triangle(nFrames, swapchain)
 
-    # tests multiple descriptor sets and arrays
-    test_04_multiple_descriptorsets(nFrames, renderPass = mainRenderpass)
+      # tests instanced triangles and quads, mixing meshes and instances
+      test_02_triangle_quad_instanced(nFrames, swapchain)
 
-    checkVkResult vkDeviceWaitIdle(vulkan.device)
-    vkDestroyRenderPass(vulkan.device, mainRenderpass, nil)
-    DestroySwapchain(swapchain)
+      # teste descriptor sets
+      test_03_simple_descriptorset(nFrames, swapchain)
 
-  # test MSAA
-  block:
-    mainRenderpass = CreateDirectPresentationRenderPass(samples = VK_SAMPLE_COUNT_4_BIT)
-    swapchain = InitSwapchain(renderpass = mainRenderpass, samples = VK_SAMPLE_COUNT_4_BIT).get()
+      # tests multiple descriptor sets and arrays
+      test_04_multiple_descriptorsets(nFrames, swapchain)
 
-    test_01_triangle(nFrames, renderPass = mainRenderpass, VK_SAMPLE_COUNT_4_BIT)
-
-    checkVkResult vkDeviceWaitIdle(vulkan.device)
-    vkDestroyRenderPass(vulkan.device, mainRenderpass, nil)
-    DestroySwapchain(swapchain)
+      checkVkResult vkDeviceWaitIdle(vulkan.device)
+      vkDestroyRenderPass(vulkan.device, renderpass.vk, nil)
+      DestroySwapchain(swapchain)
 
   # test multiple render passes
   block:
-    test_05_triangle_2pass(999999999)
+    test_05_triangle_2pass(nFrames)
 
   DestroyVulkan()