diff semiconginev2/rendering/renderer.nim @ 1254:b0f4c8ccd49a

did: stuff to test gltf importer
author sam <sam@basx.dev>
date Sat, 27 Jul 2024 20:47:54 +0700
parents c4f98eb4bb05
children 2b5ca798f6d6
line wrap: on
line diff
--- a/semiconginev2/rendering/renderer.nim	Fri Jul 26 23:39:24 2024 +0700
+++ b/semiconginev2/rendering/renderer.nim	Sat Jul 27 20:47:54 2024 +0700
@@ -533,51 +533,97 @@
         for image in value.mitems:
           renderdata.createVulkanImage(image)
 
-proc HasGPUValueField[T](name: static string): bool {.compileTime.} =
-  for fieldname, value in default(T).fieldPairs():
-    when typeof(value) is GPUValue and fieldname == name: return true
-  return false
+type EMPTY = object # only used for static assertions
+
+proc assertAllDescriptorsBound(A, B, C, D, TShader: typedesc) =
+  var foundDescriptorSets = false
+  for attrName, attrValue in default(TShader).fieldPairs():
+    when hasCustomPragma(attrValue, DescriptorSets):
+      assert not foundDescriptorSets, "Only one shader attribute is allowed to have the pragma 'DescriptorSets'"
+      when not (A is EMPTY): assert typeof(attrValue[0]) is A
+      when not (B is EMPTY): assert typeof(attrValue[1]) is B
+      when not (C is EMPTY): assert typeof(attrValue[2]) is C
+      when not (D is EMPTY): assert typeof(attrValue[3]) is D
+
+var hasBoundDescriptorSets {.compileTime.} = false # okay, I am not sure if this is clean, unproblematic or sane. Just trying to get some comptime-validation
+var hasDescriptorSets {.compileTime} = false
 
-template WithGPUValueField(obj: object, name: static string, fieldvalue, body: untyped): untyped =
-  # HasGPUValueField MUST be used to check if this is supported
-  for fieldname, value in obj.fieldPairs():
-    when fieldname == name:
-      block:
-        let `fieldvalue` {.inject.} = value
-        body
-
-template WithBind*[A, B, C, D](commandBuffer: VkCommandBuffer, sets: (DescriptorSet[A], DescriptorSet[B], DescriptorSet[C], DescriptorSet[D]), pipeline: Pipeline, body: untyped): untyped =
+template WithBind*[A, B, C, D, TShader](commandBuffer: VkCommandBuffer, sets: (DescriptorSet[A], DescriptorSet[B], DescriptorSet[C], DescriptorSet[D]), pipeline: Pipeline[TShader], body: untyped): untyped =
+  static: assertAllDescriptorsBound(A, B, C, D, TShader)
+  block:
+    var descriptorSets: seq[VkDescriptorSet]
+    for dSet in sets.fields:
+      assert dSet.vk[currentFiF()].Valid, "DescriptorSet not initialized, maybe forgot to call InitDescriptorSet"
+      descriptorSets.add dSet.vk[currentFiF()]
+    svkCmdBindDescriptorSets(commandBuffer, descriptorSets, pipeline.layout)
+    static:
+      assert not hasBoundDescriptorSets, "Cannot call WithBind nested"
+      hasBoundDescriptorSets = true
+    body
+    static:
+      hasBoundDescriptorSets = false
+template WithBind*[A, B, C, TShader](commandBuffer: VkCommandBuffer, sets: (DescriptorSet[A], DescriptorSet[B], DescriptorSet[C]), pipeline: Pipeline[TShader], body: untyped): untyped =
+  static: assertAllDescriptorsBound(A, B, C, EMPTY, TShader)
   block:
     var descriptorSets: seq[VkDescriptorSet]
     for dSet in sets.fields:
       assert dSet.vk[currentFiF()].Valid, "DescriptorSet not initialized, maybe forgot to call InitDescriptorSet"
       descriptorSets.add dSet.vk[currentFiF()]
     svkCmdBindDescriptorSets(commandBuffer, descriptorSets, pipeline.layout)
+    static:
+      assert not hasBoundDescriptorSets, "Cannot call WithBind nested"
+      hasBoundDescriptorSets = true
     body
-template WithBind*[A, B, C](commandBuffer: VkCommandBuffer, sets: (DescriptorSet[A], DescriptorSet[B], DescriptorSet[C]), pipeline: Pipeline, body: untyped): untyped =
+    static:
+      hasBoundDescriptorSets = false
+template WithBind*[A, B, TShader](commandBuffer: VkCommandBuffer, sets: (DescriptorSet[A], DescriptorSet[B]), pipeline: Pipeline[TShader], body: untyped): untyped =
+  static: assertAllDescriptorsBound(A, B, EMPTY, EMPTY, TShader)
   block:
     var descriptorSets: seq[VkDescriptorSet]
     for dSet in sets.fields:
       assert dSet.vk[currentFiF()].Valid, "DescriptorSet not initialized, maybe forgot to call InitDescriptorSet"
       descriptorSets.add dSet.vk[currentFiF()]
     svkCmdBindDescriptorSets(commandBuffer, descriptorSets, pipeline.layout)
+    static:
+      assert not hasBoundDescriptorSets, "Cannot call WithBind nested"
+      hasBoundDescriptorSets = true
     body
-template WithBind*[A, B](commandBuffer: VkCommandBuffer, sets: (DescriptorSet[A], DescriptorSet[B]), pipeline: Pipeline, body: untyped): untyped =
+    static:
+      hasBoundDescriptorSets = false
+template WithBind*[A, TShader](commandBuffer: VkCommandBuffer, sets: (DescriptorSet[A], ), pipeline: Pipeline[TShader], body: untyped): untyped =
+  static: assertAllDescriptorsBound(A, EMPTY, EMPTY, EMPTY, TShader)
   block:
     var descriptorSets: seq[VkDescriptorSet]
     for dSet in sets.fields:
       assert dSet.vk[currentFiF()].Valid, "DescriptorSet not initialized, maybe forgot to call InitDescriptorSet"
       descriptorSets.add dSet.vk[currentFiF()]
     svkCmdBindDescriptorSets(commandBuffer, descriptorSets, pipeline.layout)
+    static:
+      assert not hasBoundDescriptorSets, "Cannot call WithBind nested"
+      hasBoundDescriptorSets = true
     body
-template WithBind*[A](commandBuffer: VkCommandBuffer, sets: (DescriptorSet[A], ), pipeline: Pipeline, body: untyped): untyped =
-  block:
-    var descriptorSets: seq[VkDescriptorSet]
-    for dSet in sets.fields:
-      assert dSet.vk[currentFiF()].Valid, "DescriptorSet not initialized, maybe forgot to call InitDescriptorSet"
-      descriptorSets.add dSet.vk[currentFiF()]
-    svkCmdBindDescriptorSets(commandBuffer, descriptorSets, pipeline.layout)
-    body
+    static:
+      hasBoundDescriptorSets = false
+
+proc assertCanRenderMesh(TShader, TMesh, TInstance: typedesc) =
+  for attrName, attrValue in default(TShader).fieldPairs:
+    if hasCustomPragma(attrValue, VertexAttribute):
+      var foundAttr = false
+      for meshAttrName, meshAttrValue in default(TMesh).fieldPairs:
+        if attrName == meshAttrName:
+          assert typeof(meshAttrValue) is GPUArray, "Mesh attribute '" & attrName & "' must be a GPUArray"
+          assert typeof(attrValue) is elementType(meshAttrValue.data), "Type of shader attribute and mesh attribute '" & attrName & "' is not the same"
+          foundAttr = true
+      assert foundAttr, "Attribute '" & attrName & "' is not provided in mesh type '" & name(TMesh) & "'"
+    if hasCustomPragma(attrValue, InstanceAttribute):
+      var foundAttr = false
+      for instAttrName, instAttrValue in default(TInstance).fieldPairs:
+        if attrName == instAttrName:
+          assert typeof(instAttrValue) is GPUArray, "Instance attribute '" & attrName & "' must be a GPUArray"
+          assert foundAttr == false, "Attribute '" & attrName & "' is defined in Mesh and Instance, can only be one"
+          assert typeof(attrValue) is elementType(instAttrValue.data), "Type of shader attribute and mesh attribute '" & attrName & "' is not the same"
+          foundAttr = true
+      assert foundAttr, "Attribute '" & attrName & "' is not provided in instance type '" & name(TInstance) & "'"
 
 proc Render*[TShader, TMesh, TInstance](
   commandBuffer: VkCommandBuffer,
@@ -586,6 +632,15 @@
   instances: TInstance,
 ) =
 
+  static: assertCanRenderMesh(TShader, TMesh, TInstance)
+  static:
+    hasDescriptorSets = false
+    for attrName, attrValue in default(TShader).fieldPairs():
+      if attrValue.hasCustomPragma(DescriptorSets):
+        hasDescriptorSets = true
+    if hasDescriptorSets:
+      assert hasBoundDescriptorSets, "Shader uses descriptor sets, but none are bound"
+
   var vertexBuffers: seq[VkBuffer]
   var vertexBuffersOffsets: seq[uint64]
   var elementCount = 0'u32
@@ -661,8 +716,6 @@
       firstInstance = 0
     )
 
-type EMPTY = object
-
 proc Render*[TShader, TMesh](
   commandBuffer: VkCommandBuffer,
   pipeline: Pipeline[TShader],