changeset 1204:e2901100a596 compiletime-tests

add: tests, some fixes, some helpers
author sam <sam@basx.dev>
date Mon, 15 Jul 2024 23:51:17 +0700
parents 6360c8d17ce0
children f7530247a21f
files semicongine/rendering.nim semicongine/rendering/renderer.nim semicongine/rendering/renderpasses.nim semicongine/rendering/shaders.nim semicongine/rendering/swapchain.nim semicongine/rendering/vulkan_wrappers.nim test1.nim tests/test_rendering tests/test_rendering.nim
diffstat 9 files changed, 298 insertions(+), 102 deletions(-) [+]
line wrap: on
line diff
--- a/semicongine/rendering.nim	Mon Jul 15 20:06:42 2024 +0700
+++ b/semicongine/rendering.nim	Mon Jul 15 23:51:17 2024 +0700
@@ -42,7 +42,7 @@
     # parameters to InitSwapchain, required for swapchain recreation
     renderPass: VkRenderPass
     vSync: bool
-    samples: VkSampleCountFlagBits
+    samples*: VkSampleCountFlagBits
     # populated through InitSwapchain proc
     vk: VkSwapchainKHR
     width: uint32
@@ -74,8 +74,11 @@
 
   # shader related types
   DescriptorSetType* = enum
-    GlobalSet
-    MaterialSet
+    First
+    Second
+    # only two supported for now, but more should be easy to add
+    # Third
+    # Fourth
   DescriptorSet*[T: object, sType: static DescriptorSetType] = object
     data*: T
     vk: array[INFLIGHTFRAMES.int, VkDescriptorSet]
@@ -196,7 +199,7 @@
     raise newException(Exception, errorMsg)
   return false
 
-proc InitVulkan(appName: string = "semicongine app"): VulkanGlobals =
+proc InitVulkan*(appName: string = "semicongine app") =
 
   include ./platform/vulkan_extensions # for REQUIRED_PLATFORM_EXTENSIONS
 
@@ -233,15 +236,15 @@
       enabledExtensionCount: requiredExtensions.len.uint32,
       ppEnabledExtensionNames: instanceExtensionsC
     )
-  checkVkResult vkCreateInstance(addr(createinfo), nil, addr(result.instance))
-  loadVulkan(result.instance)
+  checkVkResult vkCreateInstance(addr(createinfo), nil, addr(vulkan.instance))
+  loadVulkan(vulkan.instance)
 
   # load extensions
   #
   for extension in requiredExtensions:
-    loadExtension(result.instance, $extension)
-  result.window = CreateWindow(appName)
-  result.surface = CreateNativeSurface(result.instance, result.window)
+    loadExtension(vulkan.instance, $extension)
+  vulkan.window = CreateWindow(appName)
+  vulkan.surface = CreateNativeSurface(vulkan.instance, vulkan.window)
 
   # logical device creation
 
@@ -252,7 +255,7 @@
   # var deviceExtensions  = @["VK_KHR_swapchain", "VK_KHR_uniform_buffer_standard_layout"]
   var deviceExtensions = @["VK_KHR_swapchain"]
   for extension in deviceExtensions:
-    loadExtension(result.instance, extension)
+    loadExtension(vulkan.instance, extension)
 
   when not defined(release):
     var debugMessengerCreateInfo = VkDebugUtilsMessengerCreateInfoEXT(
@@ -263,21 +266,21 @@
       pUserData: nil,
     )
     checkVkResult vkCreateDebugUtilsMessengerEXT(
-      result.instance,
+      vulkan.instance,
       addr(debugMessengerCreateInfo),
       nil,
-      addr(result.debugMessenger)
+      addr(vulkan.debugMessenger)
     )
 
   # get physical device and graphics queue family
-  result.physicalDevice = GetBestPhysicalDevice(result.instance)
-  result.graphicsQueueFamily = GetQueueFamily(result.physicalDevice, VK_QUEUE_GRAPHICS_BIT)
+  vulkan.physicalDevice = GetBestPhysicalDevice(vulkan.instance)
+  vulkan.graphicsQueueFamily = GetQueueFamily(vulkan.physicalDevice, VK_QUEUE_GRAPHICS_BIT)
 
   let
     priority = cfloat(1)
     queueInfo = VkDeviceQueueCreateInfo(
       sType: VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
-      queueFamilyIndex: result.graphicsQueueFamily,
+      queueFamilyIndex: vulkan.graphicsQueueFamily,
       queueCount: 1,
       pQueuePriorities: addr(priority),
     )
@@ -294,17 +297,15 @@
     pEnabledFeatures: nil,
   )
   checkVkResult vkCreateDevice(
-    physicalDevice = result.physicalDevice,
+    physicalDevice = vulkan.physicalDevice,
     pCreateInfo = addr createDeviceInfo,
     pAllocator = nil,
-    pDevice = addr result.device
+    pDevice = addr vulkan.device
   )
-  result.graphicsQueue = svkGetDeviceQueue(result.device, result.graphicsQueueFamily, VK_QUEUE_GRAPHICS_BIT)
+  vulkan.graphicsQueue = svkGetDeviceQueue(vulkan.device, vulkan.graphicsQueueFamily, VK_QUEUE_GRAPHICS_BIT)
 
 proc DestroyVulkan*() =
   vkDestroyDevice(vulkan.device, nil)
   vkDestroySurfaceKHR(vulkan.instance, vulkan.surface, nil)
   vkDestroyDebugUtilsMessengerEXT(vulkan.instance, vulkan.debugMessenger, nil)
   vkDestroyInstance(vulkan.instance, nil)
-
-vulkan = InitVulkan()
--- a/semicongine/rendering/renderer.nim	Mon Jul 15 20:06:42 2024 +0700
+++ b/semicongine/rendering/renderer.nim	Mon Jul 15 23:51:17 2024 +0700
@@ -56,11 +56,6 @@
 template sType(descriptorSet: DescriptorSet): untyped =
   get(genericParams(typeof(descriptorSet)), 1)
 
-# template bufferType[T: SupportedGPUType, TBuffer: static BufferType](gpuArray: GPUArray[T, TBuffer]): untyped =
-  # TBuffer
-# template bufferType[T: SupportedGPUType, TBuffer: static BufferType](gpuValue: GPUValue[T, TBuffer]): untyped =
-  # TBuffer
-
 template bufferType(gpuData: GPUData): untyped =
   typeof(gpuData).TBuffer
 func NeedsMapping(bType: BufferType): bool =
@@ -252,7 +247,21 @@
   result.rawPointer = selectedBlock.rawPointer.pointerAddOffset(selectedBlock.offsetNextFree)
   renderData.memory[memoryType][selectedBlockI].offsetNextFree += memoryRequirements.size
 
-proc AssignBuffers*[T](renderdata: var RenderData, data: var T) =
+proc UpdateGPUBuffer(gpuData: GPUData) =
+  if gpuData.size == 0:
+    return
+  when NeedsMapping(gpuData):
+    copyMem(pointerAddOffset(gpuData.buffer.rawPointer, gpuData.offset), gpuData.rawPointer, gpuData.size)
+  else:
+    WithStagingBuffer((gpuData.buffer.vk, gpuData.offset), gpuData.size, stagingPtr):
+      copyMem(stagingPtr, gpuData.rawPointer, gpuData.size)
+
+proc UpdateAllGPUBuffers*[T](value: T) =
+  for name, fieldvalue in value.fieldPairs():
+    when typeof(fieldvalue) is GPUData:
+      UpdateGPUBuffer(fieldvalue)
+
+proc AssignBuffers*[T](renderdata: var RenderData, data: var T, uploadData = true) =
   for name, value in fieldPairs(data):
     when typeof(value) is GPUData:
 
@@ -280,23 +289,11 @@
       value.buffer = selectedBuffer
       value.offset = renderdata.buffers[value.bufferType][selectedBufferI].offsetNextFree
       renderdata.buffers[value.bufferType][selectedBufferI].offsetNextFree += value.size
-proc AssignBuffers*(renderdata: var RenderData, descriptorSet: var DescriptorSet) =
-  AssignBuffers(renderdata, descriptorSet.data)
+  if uploadData:
+    UpdateAllGPUBuffers(data)
 
-proc UpdateGPUBuffer(gpuData: GPUData) =
-  if gpuData.size == 0:
-    return
-  when NeedsMapping(gpuData):
-    copyMem(pointerAddOffset(gpuData.buffer.rawPointer, gpuData.offset), gpuData.rawPointer, gpuData.size)
-  else:
-    WithStagingBuffer((gpuData.buffer.vk, gpuData.offset), gpuData.size, stagingPtr):
-      copyMem(stagingPtr, gpuData.rawPointer, gpuData.size)
-
-proc UpdateAllGPUBuffers*[T](value: T) =
-  for name, fieldvalue in value.fieldPairs():
-    when typeof(fieldvalue) is GPUData:
-      UpdateGPUBuffer(fieldvalue)
-
+proc AssignBuffers*(renderdata: var RenderData, descriptorSet: var DescriptorSet, uploadData = true) =
+  AssignBuffers(renderdata, descriptorSet.data, uploadData = uploadData)
 
 proc InitRenderData*(descriptorPoolLimit = 1024'u32): RenderData =
   # allocate descriptor pools
@@ -485,7 +482,7 @@
         let `fieldvalue` {.inject.} = value
         body
 
-proc AssertCompatible(TShader, TMesh, TInstance, TGlobals, TMaterial: typedesc) =
+proc AssertCompatible(TShader, TMesh, TInstance, TFirstDescriptorSet, TSecondDescriptorSet: typedesc) =
   var descriptorSetCount = 0
 
   for shaderAttributeName, shaderAttribute in default(TShader).fieldPairs:
@@ -519,18 +516,18 @@
       descriptorSetCount.inc
 
 
-      when shaderAttribute.sType == GlobalSet:
-        assert shaderAttribute.sType == default(TGlobals).sType, "Shader has global descriptor set of type '" & $shaderAttribute.sType & "' but matching provided type is '" & $default(TGlobals).sType & "'"
-        assert typeof(shaderAttribute) is TGlobals, "Shader has global descriptor set type '" & typetraits.name(get(genericParams(typeof(shaderAttribute)), 0)) & "' but provided type is " & typetraits.name(TGlobals)
-      elif shaderAttribute.sType == MaterialSet:
-        assert shaderAttribute.sType == default(TMaterial).sType, "Shader has material descriptor set of type '" & $shaderAttribute.sType & "' but matching provided type is '" & $default(TMaterial).sType & "'"
-        assert typeof(shaderAttribute) is TMaterial, "Shader has materialdescriptor type '" & typetraits.name(get(genericParams(typeof(shaderAttribute)), 0)) & "' but provided type is " & typetraits.name(TMaterial)
+      when shaderAttribute.sType == First:
+        assert shaderAttribute.sType == default(TFirstDescriptorSet).sType, "Shader has first descriptor set of type '" & $shaderAttribute.sType & "' but matching provided type is '" & $default(TFirstDescriptorSet).sType & "'"
+        assert typeof(shaderAttribute) is TFirstDescriptorSet, "Shader has first descriptor set type '" & typetraits.name(get(genericParams(typeof(shaderAttribute)), 0)) & "' but provided type is " & typetraits.name(TFirstDescriptorSet)
+      elif shaderAttribute.sType == Second:
+        assert shaderAttribute.sType == default(TSecondDescriptorSet).sType, "Shader has second descriptor set of type '" & $shaderAttribute.sType & "' but matching provided type is '" & $default(TSecondDescriptorSet).sType & "'"
+        assert typeof(shaderAttribute) is TSecondDescriptorSet, "Shader has seconddescriptor type '" & typetraits.name(get(genericParams(typeof(shaderAttribute)), 0)) & "' but provided type is " & typetraits.name(TSecondDescriptorSet)
 
 
 
-template WithBind*[A, B](commandBuffer: VkCommandBuffer, globalDescriptorSet: DescriptorSet[A, GlobalSet], materialDescriptorSet: DescriptorSet[B, MaterialSet], pipeline: Pipeline, currentFiF: int, body: untyped): untyped =
+template WithBind*[A, B](commandBuffer: VkCommandBuffer, firstDescriptorSet: DescriptorSet[A, First], secondDescriptorSet: DescriptorSet[B, Second], pipeline: Pipeline, currentFiF: int, body: untyped): untyped =
   block:
-    let sets = [globalDescriptorSet.vk[currentFiF], materialDescriptorSet.vk[currentFiF]]
+    let sets = [firstDescriptorSet.vk[currentFiF], secondDescriptorSet.vk[currentFiF]]
     vkCmdBindDescriptorSets(
       commandBuffer = commandBuffer,
       pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
@@ -543,16 +540,31 @@
     )
     body
 
-proc Render*[TShader, TGlobals, TMaterial, TMesh, TInstance](
+template WithBind*[A](commandBuffer: VkCommandBuffer, firstDescriptorSet: DescriptorSet[A, First], pipeline: Pipeline, currentFiF: int, body: untyped): untyped =
+  block:
+    let sets = [firstDescriptorSet.vk[currentFiF]]
+    vkCmdBindDescriptorSets(
+      commandBuffer = commandBuffer,
+      pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
+      layout = pipeline.layout,
+      firstSet = 0,
+      descriptorSetCount = sets.len.uint32,
+      pDescriptorSets = sets.ToCPointer,
+      dynamicOffsetCount = 0,
+      pDynamicOffsets = nil
+    )
+    body
+
+proc Render*[TFirstDescriptorSet, TSecondDescriptorSet: DescriptorSet, TShader, TMesh, TInstance](
   commandBuffer: VkCommandBuffer,
   pipeline: Pipeline[TShader],
-  globalSet: TGlobals,
-  materialSet: TMaterial,
+  firstSet: TFirstDescriptorSet,
+  secondSet: TSecondDescriptorSet,
   mesh: TMesh,
   instances: TInstance,
 ) =
   when not defined(release):
-    static: AssertCompatible(TShader, TMesh, TInstance, TGlobals, TMaterial)
+    static: AssertCompatible(TShader, TMesh, TInstance, TFirstDescriptorSet, TSecondDescriptorSet)
 
   var vertexBuffers: seq[VkBuffer]
   var vertexBuffersOffsets: seq[uint64]
@@ -630,23 +642,48 @@
     )
 
 type EMPTY = object
+type EMPTY_FIRST_DESCRIPTORSET = DescriptorSet[EMPTY, First]
+type EMPTY_SECOND_DESCRIPTORSET = DescriptorSet[EMPTY, Second]
 
-proc Render*[TShader, TGlobals, TMaterial, TMesh](
+proc Render*[TFirstDescriptorSet, TSecondDescriptorSet: DescriptorSet, TShader, TMesh](
   commandBuffer: VkCommandBuffer,
   pipeline: Pipeline[TShader],
-  globalSet: TGlobals,
-  materialSet: TMaterial,
+  firstSet: TFirstDescriptorSet,
+  secondSet: TSecondDescriptorSet,
+  mesh: TMesh,
+) =
+  Render(commandBuffer, pipeline, firstSet, secondSet, mesh, EMPTY())
+proc Render*[TFirstDescriptorSet: DescriptorSet, TMesh, TShader](
+  commandBuffer: VkCommandBuffer,
+  pipeline: Pipeline[TShader],
+  firstSet: TFirstDescriptorSet,
   mesh: TMesh,
 ) =
-  Render(commandBuffer, pipeline, globalSet, materialSet, mesh, EMPTY())
-
-#[
-proc `@`*[T: SupportedGPUType, BT: static BufferType](
-  bufferType: BT,
-  data: openArray[T]
-): GPUArray[T, BT] =
-  GPUArray[T, BT](data: @data)
-]#
+  Render(commandBuffer, pipeline, firstSet, EMPTY_SECOND_DESCRIPTORSET(), mesh, EMPTY())
+proc Render*[TShader, TMesh](
+  commandBuffer: VkCommandBuffer,
+  pipeline: Pipeline[TShader],
+  mesh: TMesh,
+) =
+  Render(commandBuffer, pipeline, EMPTY_FIRST_DESCRIPTORSET(), EMPTY_SECOND_DESCRIPTORSET(), mesh, EMPTY())
+proc Render*[TFirstDescriptorSet: DescriptorSet, TMesh, TShader, TInstance](
+  commandBuffer: VkCommandBuffer,
+  pipeline: Pipeline[TShader],
+  firstSet: TFirstDescriptorSet,
+  mesh: TMesh,
+  instances: TInstance,
+) =
+  Render(commandBuffer, pipeline, firstSet, EMPTY_SECOND_DESCRIPTORSET(), mesh, instances)
+proc Render*[TShader, TMesh, TInstance](
+  commandBuffer: VkCommandBuffer,
+  pipeline: Pipeline[TShader],
+  mesh: TMesh,
+  instances: TInstance,
+) =
+  Render(commandBuffer, pipeline, EMPTY_FIRST_DESCRIPTORSET(), EMPTY_SECOND_DESCRIPTORSET(), mesh, instances)
 
 proc asGPUArray*[T](data: openArray[T], bufferType: static BufferType): auto =
   GPUArray[T, bufferType](data: @data)
+
+proc asGPUValue*[T](data: T, bufferType: static BufferType): auto =
+  GPUValue[T, bufferType](data: data)
--- a/semicongine/rendering/renderpasses.nim	Mon Jul 15 20:06:42 2024 +0700
+++ b/semicongine/rendering/renderpasses.nim	Mon Jul 15 23:51:17 2024 +0700
@@ -1,4 +1,6 @@
 proc CreatePresentationRenderPass*(samples = VK_SAMPLE_COUNT_1_BIT): VkRenderPass =
+  assert vulkan.instance.Valid, "Vulkan not initialized"
+
   let format = DefaultSurfaceFormat()
   var attachments = @[VkAttachmentDescription(
     format: format,
@@ -86,9 +88,9 @@
     )
     viewport = VkViewport(
       x: 0.0,
-      y: 0.0,
+      y: renderHeight.float32,
       width: renderWidth.float32,
-      height: renderHeight.float32,
+      height: -renderHeight.float32,
       minDepth: 0.0,
       maxDepth: 1.0,
     )
--- a/semicongine/rendering/shaders.nim	Mon Jul 15 20:06:42 2024 +0700
+++ b/semicongine/rendering/shaders.nim	Mon Jul 15 23:51:17 2024 +0700
@@ -296,13 +296,15 @@
   polygonMode: VkPolygonMode = VK_POLYGON_MODE_FILL,
   cullMode: VkCullModeFlagBits = VK_CULL_MODE_BACK_BIT,
   frontFace: VkFrontFace = VK_FRONT_FACE_CLOCKWISE,
-  descriptorPoolLimit = 1024
+  descriptorPoolLimit = 1024,
+  samples = VK_SAMPLE_COUNT_1_BIT,
 ): Pipeline[TShader] =
   # create pipeline
 
   const shader = default(TShader)
   (result.vertexShaderModule, result.fragmentShaderModule) = CompileShader(shader)
 
+  var n = 0'u32
   for theFieldname, value in fieldPairs(default(TShader)):
     when typeof(value) is DescriptorSet:
       var layoutbindings: seq[VkDescriptorSetLayoutBinding]
@@ -325,10 +327,11 @@
         nil,
         addr(result.descriptorSetLayouts[value.sType])
       )
+      inc n
   let pipelineLayoutInfo = VkPipelineLayoutCreateInfo(
     sType: VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
-    setLayoutCount: result.descriptorSetLayouts.len.uint32,
-    pSetLayouts: result.descriptorSetLayouts.ToCPointer,
+    setLayoutCount: n,
+    pSetLayouts: if n == 0: nil else: result.descriptorSetLayouts.ToCPointer,
     # pushConstantRangeCount: uint32(pushConstants.len),
       # pPushConstantRanges: pushConstants.ToCPointer,
   )
@@ -405,7 +408,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/rendering/swapchain.nim	Mon Jul 15 20:06:42 2024 +0700
+++ b/semicongine/rendering/swapchain.nim	Mon Jul 15 23:51:17 2024 +0700
@@ -6,7 +6,7 @@
   samples = VK_SAMPLE_COUNT_1_BIT,
   oldSwapchain: ref Swapchain = nil,
 ): Option[Swapchain] =
-  assert vulkan.instance.Valid
+  assert vulkan.instance.Valid, "Vulkan not initialized"
 
   var capabilities: VkSurfaceCapabilitiesKHR
   checkVkResult vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vulkan.physicalDevice, vulkan.surface, addr(capabilities))
@@ -62,6 +62,7 @@
       height = height,
       format = format,
       usage = [VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT],
+      samples = samples,
     )
     let requirements = svkGetImageMemoryRequirements(swapchain.msaaImage)
     swapchain.msaaMemory = svkAllocateMemory(
@@ -114,6 +115,11 @@
 
 proc DestroySwapchain*(swapchain: Swapchain) =
 
+  if swapchain.samples != VK_SAMPLE_COUNT_1_BIT:
+    vkDestroyImageView(vulkan.device, swapchain.msaaImageView, nil)
+    vkDestroyImage(vulkan.device, swapchain.msaaImage, nil)
+    vkFreeMemory(vulkan.device, swapchain.msaaMemory, nil)
+
   for fence in swapchain.queueFinishedFence:
     vkDestroyFence(vulkan.device, fence, nil)
 
--- a/semicongine/rendering/vulkan_wrappers.nim	Mon Jul 15 20:06:42 2024 +0700
+++ b/semicongine/rendering/vulkan_wrappers.nim	Mon Jul 15 23:51:17 2024 +0700
@@ -113,7 +113,7 @@
     addr(result),
   )
 
-proc svkCreate2DImage*(width, height: uint32, format: VkFormat, usage: openArray[VkImageUsageFlagBits]): VkImage =
+proc svkCreate2DImage*(width, height: uint32, format: VkFormat, usage: openArray[VkImageUsageFlagBits], samples = VK_SAMPLE_COUNT_1_BIT): VkImage =
   var imageProps: VkImageFormatProperties
   checkVkResult vkGetPhysicalDeviceImageFormatProperties(
     vulkan.physicalDevice,
@@ -136,7 +136,7 @@
     initialLayout: VK_IMAGE_LAYOUT_UNDEFINED,
     usage: usage.toBits,
     sharingMode: VK_SHARING_MODE_EXCLUSIVE,
-    samples: VK_SAMPLE_COUNT_1_BIT,
+    samples: samples,
   )
   checkVkResult vkCreateImage(vulkan.device, addr imageInfo, nil, addr(result))
 
--- a/test1.nim	Mon Jul 15 20:06:42 2024 +0700
+++ b/test1.nim	Mon Jul 15 23:51:17 2024 +0700
@@ -47,9 +47,11 @@
     color = vec4(1, 0, 0, 1);
 }"""
 
+InitVulkan()
+
 var myMesh1 = MeshA(
-  position: GPUArray[Vec3f, VertexBuffer](data: @[NewVec3f(-0.5, 0.5, ), NewVec3f(0, -0.5, ), NewVec3f(0.5, 0.5, )]),
-  indices: GPUArray[uint16, IndexBuffer](data: @[0'u16, 1'u16, 2'u16])
+  position: asGPUArray([NewVec3f(-0.5, -0.5), NewVec3f(0, 0.5), NewVec3f(0.5, -0.5)], VertexBuffer),
+  indices: asGPUArray([0'u16, 1'u16, 2'u16], IndexBuffer),
 )
 var uniforms1 = DescriptorSet[UniformsA, MaterialSet](
   data: UniformsA(
@@ -96,10 +98,6 @@
 
 # copy everything to GPU
 echo "Copying all data to GPU memory"
-UpdateAllGPUBuffers(myMesh1)
-UpdateAllGPUBuffers(instances1)
-UpdateAllGPUBuffers(uniforms1)
-UpdateAllGPUBuffers(myGlobals)
 renderdata.FlushAllMemory()
 
 # descriptors
Binary file tests/test_rendering has changed
--- a/tests/test_rendering.nim	Mon Jul 15 20:06:42 2024 +0700
+++ b/tests/test_rendering.nim	Mon Jul 15 23:51:17 2024 +0700
@@ -1,11 +1,13 @@
 import std/options
+import std/random
+
 import ../semicongine
 
 var
   mainRenderpass: VkRenderPass
   swapchain: Swapchain
 
-proc test_01_gl_triangle() =
+proc test_01_triangle(nFrames: int) =
   var renderdata = InitRenderData()
 
   type
@@ -23,41 +25,188 @@
     TriangleMesh = object
       position: GPUArray[Vec3f, VertexBuffer]
       color: GPUArray[Vec3f, VertexBuffer]
-    Empty = object
   var mesh = TriangleMesh(
-    position: asGPUArray([NewVec3f(-0.5, -0.5), NewVec3f(-0.5, 0.5), NewVec3f(0.5, -0.5)], VertexBuffer),
+    position: asGPUArray([NewVec3f(-0.5, -0.5), NewVec3f(0, 0.5), NewVec3f(0.5, -0.5)], VertexBuffer),
     color: asGPUArray([NewVec3f(0, 0, 1), NewVec3f(0, 1, 0), NewVec3f(1, 0, 0)], VertexBuffer),
   )
+  AssignBuffers(renderdata, mesh)
+  renderdata.FlushAllMemory()
 
   var
-    pipeline = CreatePipeline[TrianglShader](renderPass = mainRenderpass)
-    a, b: Empty
+    pipeline = CreatePipeline[TrianglShader](renderPass = mainRenderpass, samples = swapchain.samples)
 
-  while UpdateInputs():
+  var c = 0
+  while UpdateInputs() and c < nFrames:
     WithNextFrame(swapchain, framebuffer, commandbuffer):
       WithRenderPass(mainRenderpass, framebuffer, commandbuffer, swapchain.width, swapchain.height, NewVec4f(0, 0, 0, 0)):
         WithPipeline(commandbuffer, pipeline):
-          # WithBind(commandBuffer, a, b, pipeline, swapchain.currentFiF.int):
-            Render(
-              commandbuffer = commandbuffer,
-              pipeline = pipeline,
-              globalSet = a,
-              materialSet = b,
-              mesh = mesh,
-            )
+          Render(commandbuffer = commandbuffer, pipeline = pipeline, mesh = mesh)
+    inc c
 
   # cleanup
+  checkVkResult vkDeviceWaitIdle(vulkan.device)
   DestroyPipeline(pipeline)
   DestroyRenderData(renderdata)
 
 
-when isMainModule:
-  mainRenderpass = CreatePresentationRenderPass()
-  swapchain = InitSwapchain(renderpass = mainRenderpass).get()
+proc test_02_triangle_quad_instanced(nFrames: int) =
+  var renderdata = InitRenderData()
+
+  type
+    SomeShader = object
+      position {.VertexAttribute.}: Vec3f
+      color {.VertexAttribute.}: Vec3f
+      pos {.InstanceAttribute.}: Vec3f
+      scale {.InstanceAttribute.}: float32
+      fragmentColor {.Pass.}: Vec3f
+      outColor {.ShaderOutput.}: Vec4f
+      # code
+      vertexCode: string = """void main() {
+      fragmentColor = color;
+      gl_Position = vec4((position * scale) + pos, 1);}"""
+      fragmentCode: string = """void main() {
+      outColor = vec4(fragmentColor, 1);}"""
+    TriangleMesh = object
+      position: GPUArray[Vec3f, VertexBuffer]
+      color: GPUArray[Vec3f, VertexBuffer]
+    QuadMesh = object
+      position: GPUArray[Vec3f, VertexBuffer]
+      color: GPUArray[Vec3f, VertexBuffer]
+      indices: GPUArray[uint16, IndexBuffer]
+    Instances = object
+      pos: GPUArray[Vec3f, VertexBuffer]
+      scale: GPUArray[float32, VertexBuffer]
+  var tri = TriangleMesh(
+    position: asGPUArray([NewVec3f(-0.5, -0.5), NewVec3f(0, 0.5), NewVec3f(0.5, -0.5)], VertexBuffer),
+    color: asGPUArray([NewVec3f(0, 0, 1), NewVec3f(0, 1, 0), NewVec3f(1, 0, 0)], VertexBuffer),
+  )
+  var quad = QuadMesh(
+    position: asGPUArray([NewVec3f(-0.3, -0.3), NewVec3f(-0.3, 0.3), NewVec3f(0.3, 0.3), NewVec3f(0.3, -0.3)], VertexBuffer),
+    indices: asGPUArray([0'u16, 1'u16, 2'u16, 2'u16, 3'u16, 0'u16], IndexBuffer),
+    color: asGPUArray([NewVec3f(1, 1, 1), NewVec3f(1, 1, 1), NewVec3f(1, 1, 1), NewVec3f(1, 1, 1)], VertexBuffer),
+  )
+
+  var instancesA: Instances
+  for n in 1..100:
+    instancesA.pos.data.add NewVec3f(rand(-0.8'f32 .. 0.8'f32), rand(-0.8'f32 .. 0'f32))
+    instancesA.scale.data.add rand(0.3'f32 .. 0.4'f32)
+  var instancesB: Instances
+  for n in 1..100:
+    instancesB.pos.data.add NewVec3f(rand(-0.8'f32 .. 0.8'f32), rand(0'f32 .. 0.8'f32))
+    instancesB.scale.data.add rand(0.1'f32 .. 0.2'f32)
+
+  AssignBuffers(renderdata, tri)
+  AssignBuffers(renderdata, quad)
+  AssignBuffers(renderdata, instancesA)
+  AssignBuffers(renderdata, instancesB)
+  renderdata.FlushAllMemory()
+
+  var pipeline = CreatePipeline[SomeShader](renderPass = mainRenderpass, samples = swapchain.samples)
+
+  var c = 0
+  while UpdateInputs() and c < nFrames:
+    WithNextFrame(swapchain, framebuffer, commandbuffer):
+      WithRenderPass(mainRenderpass, 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)
+          Render(commandbuffer = commandbuffer, pipeline = pipeline, mesh = tri, instances = instancesA)
+          Render(commandbuffer = commandbuffer, pipeline = pipeline, mesh = tri, instances = instancesB)
+    inc c
+
+  # cleanup
+  checkVkResult vkDeviceWaitIdle(vulkan.device)
+  DestroyPipeline(pipeline)
+  DestroyRenderData(renderdata)
+
+proc test_03_global_descriptorset(nFrames: int) =
+  var renderdata = InitRenderData()
 
-  test_01_gl_triangle()
+  type
+    RenderSettings = object
+      gamma: float32
+    FirstDS = object
+      settings: GPUValue[RenderSettings, UniformBuffer]
+    QuadShader = object
+      position {.VertexAttribute.}: Vec3f
+      color {.VertexAttribute.}: Vec3f
+      fragmentColor {.Pass.}: Vec3f
+      outColor {.ShaderOutput.}: Vec4f
+      firstDS: DescriptorSet[FirstDS, First]
+      # code
+      vertexCode: string = """void main() {
+      fragmentColor = vec3(pow(color.r, settings.gamma), pow(color.g, settings.gamma), pow(color.b, settings.gamma));
+      gl_Position = vec4(position, 1);}"""
+      fragmentCode: string = """void main() {
+      outColor = vec4(fragmentColor, 1);}"""
+    QuadMesh = object
+      position: GPUArray[Vec3f, VertexBuffer]
+      color: GPUArray[Vec3f, VertexBuffer]
+      indices: GPUArray[uint16, IndexBuffer]
 
+  var quad = QuadMesh(
+    position: asGPUArray([NewVec3f(-0.3, -0.3), NewVec3f(-0.3, 0.3), NewVec3f(0.3, 0.3), NewVec3f(0.3, -0.3)], VertexBuffer),
+    indices: asGPUArray([0'u16, 1'u16, 2'u16, 2'u16, 3'u16, 0'u16], IndexBuffer),
+    color: asGPUArray([NewVec3f(1, 1, 1), NewVec3f(1, 1, 1), NewVec3f(1, 1, 1), NewVec3f(1, 1, 1)], VertexBuffer),
+  )
+  var firstDs = DescriptorSet[FirstDS, First](
+    data: FirstDS(
+      settings: asGPUValue(RenderSettings(
+          gamma: 1.0'f32
+    ), UniformBuffer)
+  )
+  )
+  AssignBuffers(renderdata, quad)
+  AssignBuffers(renderdata, firstDs)
+  renderdata.FlushAllMemory()
+
+  var pipeline = CreatePipeline[QuadShader](renderPass = mainRenderpass, samples = swapchain.samples)
+
+  var c = 0
+  while UpdateInputs() and c < nFrames:
+    WithNextFrame(swapchain, framebuffer, commandbuffer):
+      WithBind(commandbuffer, firstDs, pipeline, swapchain.currentFiF):
+        WithRenderPass(mainRenderpass, framebuffer, commandbuffer, swapchain.width, swapchain.height, NewVec4f(0, 0, 0, 0)):
+          WithPipeline(commandbuffer, pipeline):
+            Render(commandbuffer = commandbuffer, pipeline = pipeline, firstSet = firstDs, mesh = quad)
+    inc c
+
+  # cleanup
   checkVkResult vkDeviceWaitIdle(vulkan.device)
-  vkDestroyRenderPass(vulkan.device, mainRenderpass, nil)
-  DestroySwapchain(swapchain)
+  DestroyPipeline(pipeline)
+  DestroyRenderData(renderdata)
+
+when isMainModule:
+  var nFrames = 100
+  InitVulkan()
+
+  # test normal
+  block:
+    mainRenderpass = CreatePresentationRenderPass()
+    swapchain = InitSwapchain(renderpass = mainRenderpass).get()
+
+    # tests a simple triangle with minimalistic shader and vertex format
+    test_01_triangle(nFrames)
+
+    # tests instanced triangles and quads, mixing meshes and instances
+    test_02_triangle_quad_instanced(nFrames)
+
+    # tests
+    test_03_global_descriptorset(nFrames)
+
+    checkVkResult vkDeviceWaitIdle(vulkan.device)
+    vkDestroyRenderPass(vulkan.device, mainRenderpass, nil)
+    DestroySwapchain(swapchain)
+
+  # test MSAA
+  block:
+    mainRenderpass = CreatePresentationRenderPass(samples = VK_SAMPLE_COUNT_4_BIT)
+    swapchain = InitSwapchain(renderpass = mainRenderpass, samples = VK_SAMPLE_COUNT_4_BIT).get()
+
+    test_01_triangle(nFrames)
+
+    checkVkResult vkDeviceWaitIdle(vulkan.device)
+    vkDestroyRenderPass(vulkan.device, mainRenderpass, nil)
+    DestroySwapchain(swapchain)
+
   DestroyVulkan()