changeset 1375:ca3299ea1bdf

did: make vector distinct from arrays, better code for compile-time handling of descriptors
author sam <sam@basx.dev>
date Sat, 07 Dec 2024 21:34:24 +0700
parents 92c089136a05
children aaf8fa2c7bb2 9ca552dad5fc
files semicongine/core/vector.nim semicongine/rendering.nim semicongine/rendering/renderer.nim semicongine/rendering/shaders.nim semicongine/text.nim tests/test_rendering.nim
diffstat 6 files changed, 107 insertions(+), 99 deletions(-) [+]
line wrap: on
line diff
--- a/semicongine/core/vector.nim	Fri Dec 06 22:48:25 2024 +0700
+++ b/semicongine/core/vector.nim	Sat Dec 07 21:34:24 2024 +0700
@@ -84,6 +84,15 @@
 func len*(v: TVec4): int =
   4
 
+func `$`*[T](v: TVec1[T]): string =
+  `$`(array[1, T](v))
+func `$`*[T](v: TVec2[T]): string =
+  `$`(array[2, T](v))
+func `$`*[T](v: TVec3[T]): string =
+  `$`(array[3, T](v))
+func `$`*[T](v: TVec4[T]): string =
+  `$`(array[4, T](v))
+
 func sum*[T](v: TVec1[T]): T =
   sum(array[1, T](v))
 func sum*[T](v: TVec2[T]): T =
--- a/semicongine/rendering.nim	Fri Dec 06 22:48:25 2024 +0700
+++ b/semicongine/rendering.nim	Sat Dec 07 21:34:24 2024 +0700
@@ -189,58 +189,58 @@
 proc `[]=`*[T, S](a: var GPUArray[T, S], i: SomeInteger, value: T) =
   a.data[i] = value
 
-template forDescriptorFields(
-    shader: typed, valuename, typename, countname, bindingNumber, body: untyped
-): untyped =
-  var `bindingNumber` {.inject.} = 0'u32
-  for theFieldname, `valuename` in fieldPairs(shader):
-    when typeof(`valuename`) is ImageObject:
-      block:
-        const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
-        const `countname` {.inject.} = 1'u32
-        body
-        `bindingNumber`.inc
-    elif typeof(`valuename`) is GPUValue and
-        typeof(`valuename`).TBuffer in [UniformBuffer, UniformBufferMapped]:
-      block:
-        const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER
-        const `countname` {.inject.} = 1'u32
-        body
-        `bindingNumber`.inc
-    elif typeof(`valuename`) is GPUValue and
-        typeof(`valuename`).TBuffer in [StorageBuffer, StorageBufferMapped]:
-      block:
-        const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER
-        const `countname` {.inject.} = 1'u32
-        body
-        `bindingNumber`.inc
-    elif typeof(`valuename`) is array:
-      when elementType(`valuename`) is ImageObject:
-        block:
-          const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
-          const `countname` {.inject.} = uint32(typeof(`valuename`).len)
-          body
-          `bindingNumber`.inc
-      elif elementType(`valuename`) is GPUValue and
-          elementType(`valuename`).TBuffer in [UniformBuffer, UniformBufferMapped]:
-        block:
-          const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER
-          const `countname` {.inject.} = len(`valuename`).uint32
-          body
-          `bindingNumber`.inc
-      elif elementType(`valuename`) is GPUValue and
-          elementType(`valuename`).TBuffer in [StorageBuffer, StorageBufferMapped]:
-        block:
-          const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER
-          const `countname` {.inject.} = len(`valuename`).uint32
-          body
-          `bindingNumber`.inc
+func getBufferType*[A, B](value: GPUValue[A, B]): BufferType {.compileTime.} =
+  B
+
+func getBufferType*[A, B](
+    value: openArray[GPUValue[A, B]]
+): BufferType {.compileTime.} =
+  B
+
+func getDescriptorType[T](): VkDescriptorType {.compileTIme.} =
+  when T is ImageObject:
+    VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
+  elif T is GPUValue:
+    when getBufferType(default(T)) in [UniformBuffer, UniformBufferMapped]:
+      VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER
+    elif getBufferType(default(T)) in [StorageBuffer, StorageBufferMapped]:
+      VK_DESCRIPTOR_TYPE_STORAGE_BUFFER
+    else:
+      {.error: "Unsupported descriptor type: " & typetraits.name(T).}
+  elif T is array:
+    when elementType(default(T)) is ImageObject:
+      VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
+    elif elementType(default(T)) is GPUValue:
+      when getBufferType(default(elementType(default(T)))) in
+          [UniformBuffer, UniformBufferMapped]:
+        VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER
+      elif getBufferType(default(elementType(default(T)))) in
+          [StorageBuffer, StorageBufferMapped]:
+        VK_DESCRIPTOR_TYPE_STORAGE_BUFFER
       else:
-        {.
-          error: "Unsupported descriptor type: " & typetraits.name(typeof(`valuename`))
-        .}
+        {.error: "Unsupported descriptor type: " & typetraits.name(T).}
     else:
-      {.error: "Unsupported descriptor type: " & typetraits.name(typeof(`valuename`)).}
+      {.error: "Unsupported descriptor type: " & typetraits.name(T).}
+  else:
+    {.error: "Unsupported descriptor type: " & typetraits.name(T).}
+
+func getDescriptorCount[T](): uint32 {.compileTIme.} =
+  when T is array:
+    len(T)
+  else:
+    1
+
+func getBindingNumber[T](field: static string): uint32 {.compileTime.} =
+  var c = 0'u32
+  var found = false
+  for name, value in fieldPairs(default(T)):
+    when name == field:
+      result = c
+      found = true
+    else:
+      inc c
+  assert found,
+    "Field '" & field & "' of descriptor '" & typetraits.name(T) & "' not found"
 
 proc currentFiF*(): int =
   assert vulkan.swapchain != nil, "Swapchain has not been initialized yet"
--- a/semicongine/rendering/renderer.nim	Fri Dec 06 22:48:25 2024 +0700
+++ b/semicongine/rendering/renderer.nim	Sat Dec 07 21:34:24 2024 +0700
@@ -124,16 +124,17 @@
   var imageWrites = newSeqOfCap[VkDescriptorImageInfo](1024)
   var bufferWrites = newSeqOfCap[VkDescriptorBufferInfo](1024)
 
-  forDescriptorFields(
-    descriptorSet.data, fieldValue, descriptorType, descriptorCount,
-    descriptorBindingNumber,
-  ):
+  for theFieldname, fieldvalue in fieldPairs(descriptorSet.data):
+    const descriptorType = getDescriptorType[typeof(fieldvalue)]()
+    const descriptorCount = getDescriptorCount[typeof(fieldvalue)]()
+    const descriptorBindingNumber =
+      getBindingNumber[typeof(descriptorSet.data)](theFieldname)
     for i in 0 ..< descriptorSet.vk.len:
-      when typeof(fieldValue) is GPUValue:
+      when typeof(fieldvalue) is GPUValue:
         bufferWrites.add VkDescriptorBufferInfo(
-          buffer: fieldValue.buffer.vk,
-          offset: fieldValue.offset,
-          range: fieldValue.size,
+          buffer: fieldvalue.buffer.vk,
+          offset: fieldvalue.offset,
+          range: fieldvalue.size,
         )
         descriptorSetWrites.add VkWriteDescriptorSet(
           sType: VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
@@ -145,10 +146,10 @@
           pImageInfo: nil,
           pBufferInfo: addr(bufferWrites[^1]),
         )
-      elif typeof(fieldValue) is ImageObject:
+      elif typeof(fieldvalue) is ImageObject:
         imageWrites.add VkDescriptorImageInfo(
-          sampler: fieldValue.sampler,
-          imageView: fieldValue.imageView,
+          sampler: fieldvalue.sampler,
+          imageView: fieldvalue.imageView,
           imageLayout: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
         )
         descriptorSetWrites.add VkWriteDescriptorSet(
@@ -161,9 +162,9 @@
           pImageInfo: addr(imageWrites[^1]),
           pBufferInfo: nil,
         )
-      elif typeof(fieldValue) is array:
-        when elementType(fieldValue) is ImageObject:
-          for image in fieldValue.litems:
+      elif typeof(fieldvalue) is array:
+        when elementType(fieldvalue) is ImageObject:
+          for image in fieldvalue.litems:
             imageWrites.add VkDescriptorImageInfo(
               sampler: image.sampler,
               imageView: image.imageView,
@@ -179,8 +180,8 @@
             pImageInfo: addr(imageWrites[^descriptorCount.int]),
             pBufferInfo: nil,
           )
-        elif elementType(fieldValue) is GPUValue:
-          for entry in fieldValue.litems:
+        elif elementType(fieldvalue) is GPUValue:
+          for entry in fieldvalue.litems:
             bufferWrites.add VkDescriptorBufferInfo(
               buffer: entry.buffer.vk, offset: entry.offset, range: entry.size
             )
@@ -196,11 +197,11 @@
           )
         else:
           {.
-            error: "Unsupported descriptor type: " & typetraits.name(typeof(fieldValue))
+            error: "Unsupported descriptor type: " & typetraits.name(typeof(fieldvalue))
           .}
       else:
         {.
-          error: "Unsupported descriptor type: " & typetraits.name(typeof(fieldValue))
+          error: "Unsupported descriptor type: " & typetraits.name(typeof(fieldvalue))
         .}
 
   vkUpdateDescriptorSets(
--- a/semicongine/rendering/shaders.nim	Fri Dec 06 22:48:25 2024 +0700
+++ b/semicongine/rendering/shaders.nim	Sat Dec 07 21:34:24 2024 +0700
@@ -207,6 +207,12 @@
   else:
     return 1
 
+func assertGPUType[T](t: T) =
+  assert T is SupportedGPUType, "'" & $(t) & "' is not a supported GPU type"
+
+func assertGPUType[T](t: openArray[T]) =
+  assert T is SupportedGPUType, "'" & $(t) & "' is not a supported GPU type"
+
 proc generateShaderSource[TShader](shader: TShader): (string, string) {.compileTime.} =
   const GLSL_VERSION = "450"
   var vsInput: seq[string]
@@ -268,14 +274,14 @@
           descriptorBinding.inc
         elif typeof(descriptorValue) is GPUValue:
           let bufferType =
-            if typeof(descriptorValue).TBuffer in [UniformBuffer, UniformBufferMapped]:
+            if getBufferType(descriptorValue) in [UniformBuffer, UniformBufferMapped]:
               "uniform"
             else:
               "readonly buffer"
           uniforms.add "layout(set=" & $setIndex & ", binding = " & $descriptorBinding &
             ") " & bufferType & " T" & descriptorName & " {"
           when typeof(descriptorValue.data) is object:
-            for blockFieldName, blockFieldValue in descriptorValue.data.fieldPairs():
+            for blockFieldName, blockFieldValue in fieldPairs(descriptorValue.data):
               when typeof(blockFieldValue) is array:
                 assert elementType(blockFieldValue) is SupportedGPUType,
                   bufferType & " block field '" & blockFieldName &
@@ -302,23 +308,17 @@
             descriptorBinding.inc
           elif elementType(descriptorValue) is GPUValue:
             let bufferType =
-              if typeof(descriptorValue).TBuffer in [UniformBuffer, UniformBufferMapped]:
+              if getBufferType(descriptorValue) in [UniformBuffer, UniformBufferMapped]:
                 "uniform"
               else:
                 "readonly buffer"
             uniforms.add "layout(set=" & $setIndex & ", binding = " & $descriptorBinding &
               ") " & bufferType & " T" & descriptorName & " {"
 
-            for blockFieldName, blockFieldValue in default(elementType(descriptorValue)).data
-            .fieldPairs():
-              when typeof(blockFieldValue) is array:
-                assert elementType(blockFieldValue) is SupportedGPUType,
-                  bufferType & " block field '" & blockFieldName &
-                    "' is not a SupportedGPUType"
-              else:
-                assert typeof(blockFieldValue) is SupportedGPUType,
-                  bufferType & " block field '" & blockFieldName &
-                    "' is not a SupportedGPUType"
+            for blockFieldName, blockFieldValue in fieldPairs(
+              default(elementType(descriptorValue)).data
+            ):
+              assertGPUType(blockFieldValue)
               uniforms.add "  " & glslType(blockFieldValue) & " " & blockFieldName & ";"
             uniforms.add "} " & descriptorName & "[" & $descriptorValue.len & "];"
             descriptorBinding.inc
@@ -331,7 +331,7 @@
       assert value is object, "push constants need to be objects"
       pushConstants.add "layout( push_constant ) uniform constants"
       pushConstants.add "{"
-      for constFieldName, constFieldValue in value.fieldPairs():
+      for constFieldName, constFieldValue in fieldPairs(value):
         assert typeof(constFieldValue) is SupportedGPUType,
           "push constant field '" & constFieldName & "' is not a SupportedGPUType"
         pushConstants.add "  " & glslType(constFieldValue) & " " & constFieldName & ";"
@@ -448,13 +448,11 @@
   for _, value in fieldPairs(default(TShader)):
     when hasCustomPragma(value, DescriptorSet):
       var layoutbindings: seq[VkDescriptorSetLayoutBinding]
-      forDescriptorFields(
-        value, fieldValue, descriptorType, descriptorCount, descriptorBindingNumber
-      ):
+      for theFieldname, fieldvalue in fieldPairs(value):
         layoutbindings.add VkDescriptorSetLayoutBinding(
-          binding: descriptorBindingNumber,
-          descriptorType: descriptorType,
-          descriptorCount: descriptorCount,
+          binding: getBindingNumber[typeof(value)](theFieldname),
+          descriptorType: getDescriptorType[typeof(fieldvalue)](),
+          descriptorCount: getDescriptorCount[typeof(fieldvalue)](),
           stageFlags: VkShaderStageFlags(VK_SHADER_STAGE_ALL_GRAPHICS),
           pImmutableSamplers: nil,
         )
--- a/semicongine/text.nim	Fri Dec 06 22:48:25 2024 +0700
+++ b/semicongine/text.nim	Sat Dec 07 21:34:24 2024 +0700
@@ -84,7 +84,7 @@
 
     fragmentUv {.Pass.}: Vec2f
     fragmentColor {.PassFlat.}: Vec4f
-    color {.ShaderOutput.}: Vec4f
+    outColor {.ShaderOutput.}: Vec4f
     glyphData {.DescriptorSet: 0.}: GlyphDescriptors[N]
     vertexCode* =
       """void main() {
@@ -105,7 +105,7 @@
     if(v == 0) {
       discard;
     }
-    color = vec4(fragmentColor.rgb, fragmentColor.a * v);
+    outColor = vec4(fragmentColor.rgb, fragmentColor.a * v);
 }"""
 
 proc `=copy`(dest: var FontObj, source: FontObj) {.error.}
--- a/tests/test_rendering.nim	Fri Dec 06 22:48:25 2024 +0700
+++ b/tests/test_rendering.nim	Sat Dec 07 21:34:24 2024 +0700
@@ -1032,24 +1032,24 @@
     setupSwapchain(renderpass = renderpass)
 
     # tests a simple triangle with minimalistic shader and vertex format
-    # test_01_triangle(time)
+    test_01_triangle(time)
 
     # tests instanced triangles and quads, mixing meshes and instances
-    # test_02_triangle_quad_instanced(time)
+    test_02_triangle_quad_instanced(time)
 
     # teste descriptor sets
-    # test_03_simple_descriptorset(time)
+    test_03_simple_descriptorset(time)
 
     # tests multiple descriptor sets and arrays
-    # test_04_multiple_descriptorsets(time)
+    test_04_multiple_descriptorsets(time)
 
     # rotating cube
-    # test_05_cube(time)
+    test_05_cube(time)
 
     # different draw modes (lines, points, and topologies)
-    # test_06_different_draw_modes(time)
+    test_06_different_draw_modes(time)
 
-    # test_07_png_texture(time)
+    test_07_png_texture(time)
 
     test_08_texture_array(time)
 
@@ -1058,7 +1058,7 @@
     clearSwapchain()
 
   # test multiple render passes
-  # for i, (depthBuffer, samples) in renderPasses:
-  # test_09_triangle_2pass(time, depthBuffer, samples)
+  for i, (depthBuffer, samples) in renderPasses:
+    test_09_triangle_2pass(time, depthBuffer, samples)
 
   destroyVulkan()