changeset 1177:4ef959278451 compiletime-tests

sync from bedroom to office
author sam <sam@basx.dev>
date Sun, 30 Jun 2024 06:40:33 +0700
parents 511c9f7cd1da
children ec4ff70299f2
files static_utils.nim
diffstat 1 files changed, 246 insertions(+), 95 deletions(-) [+]
line wrap: on
line diff
--- a/static_utils.nim	Sat Jun 29 21:04:04 2024 +0700
+++ b/static_utils.nim	Sun Jun 30 06:40:33 2024 +0700
@@ -19,8 +19,11 @@
 template Pass* {.pragma.}
 template PassFlat* {.pragma.}
 template ShaderOutput* {.pragma.}
+template VertexIndices*{.pragma.}
 
 const INFLIGHTFRAMES = 2'u32
+const MEMORY_ALIGNMENT = 65536'u64 # Align buffers inside memory along this alignment
+const BUFFERALIGNMENT = 64'u64 # align offsets inside buffers along this alignment
 
 type
   SupportedGPUType* = float32 | float64 | int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | TVec2[int32] | TVec2[int64] | TVec3[int32] | TVec3[int64] | TVec4[int32] | TVec4[int64] | TVec2[uint32] | TVec2[uint64] | TVec3[uint32] | TVec3[uint64] | TVec4[uint32] | TVec4[uint64] | TVec2[float32] | TVec2[float64] | TVec3[float32] | TVec3[float64] | TVec4[float32] | TVec4[float64] | TMat2[float32] | TMat2[float64] | TMat23[float32] | TMat23[float64] | TMat32[float32] | TMat32[float64] | TMat3[float32] | TMat3[float64] | TMat34[float32] | TMat34[float64] | TMat43[float32] | TMat43[float64] | TMat4[float32] | TMat4[float64]
@@ -201,15 +204,16 @@
   IndirectGPUMemory = object
     vk: VkDeviceMemory
     size: uint64
+    needsTransfer: bool # usually true
   DirectGPUMemory = object
     vk: VkDeviceMemory
     size: uint64
     data: pointer
+    needsFlush: bool # usually true
   GPUMemory = IndirectGPUMemory | DirectGPUMemory
 
   Buffer[TMemory: GPUMemory] = object
     vk: VkBuffer
-    memory*: TMemory
     offset: uint64
     size: uint64
 
@@ -240,13 +244,17 @@
     descriptorSetLayout: VkDescriptorSetLayout
   RenderData = object
     descriptorPool: VkDescriptorPool
-    indirectMemory: seq[IndirectGPUMemory]
-    nextFreeIndirectMemoryOffset: seq[uint64]
+    # tuple is memory and offset to next free allocation in that memory
+    indirectMemory: seq[tuple[memory: IndirectGPUMemory, nextFree: uint64]]
+    directMemory: seq[tuple[memory: DirectGPUMemory, nextFree: uint64]]
     indirectBuffers: seq[Buffer[IndirectGPUMemory]]
-    directMemory: seq[DirectGPUMemory]
-    nextFreeDirectMemoryOffset: seq[uint64]
     directBuffers: seq[Buffer[DirectGPUMemory]]
 
+template IsDirectMemory(gpuArray: GPUArray): untyped =
+  get(genericParams(typeof(gpuArray)), 1) is DirectGPUMemory
+template IsDirectMemory(gpuValue: GPUValue): untyped =
+  get(genericParams(typeof(gpuValue)), 1) is DirectGPUMemory
+
 converter toVkIndexType(indexType: IndexType): VkIndexType =
   case indexType:
     of None: VK_INDEX_TYPE_NONE_KHR
@@ -474,7 +482,6 @@
     bindingCount: uint32(layoutbindings.len),
     pBindings: layoutbindings.ToCPointer
   )
-  result.descriptorSetLayout: VkDescriptorSetLayout
   checkVkResult vkCreateDescriptorSetLayout(device, addr(layoutCreateInfo), nil, addr(result.descriptorSetLayout))
   let pipelineLayoutInfo = VkPipelineLayoutCreateInfo(
     sType: VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
@@ -611,36 +618,38 @@
     addr(result.pipeline)
   )
 
-proc AllocateIndirectMemory(device: VkDevice, pDevice: VkPhysicalDevice, allocationSize: uint64): IndirectGPUMemory =
+proc AllocateIndirectMemory(device: VkDevice, pDevice: VkPhysicalDevice, size: uint64): IndirectGPUMemory =
   # chooses biggest memory type that has NOT VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
-  result.size = allocationSize
+  result.size = size
+  result.needsTransfer = true
 
   # find a good memory type
   var physicalProperties: VkPhysicalDeviceMemoryProperties
   vkGetPhysicalDeviceMemoryProperties(pDevice, addr physicalProperties)
 
-  var biggestHeap: uint64 = -1
-  var memoryTypeIndex = -1
+  var biggestHeap: uint64 = 0
+  var memoryTypeIndex = high(uint32)
   # try to find non-host-visible type
-  for i in 0 ..< physicalProperties.memoryTypeCount:
-    if VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT not in toEnums(physicalProperties.memoryTypes[i].propertyFlags)
+  for i in 0'u32 ..< physicalProperties.memoryTypeCount:
+    if not (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT in toEnums(physicalProperties.memoryTypes[i].propertyFlags)):
       let size = physicalProperties.memoryHeaps[physicalProperties.memoryTypes[i].heapIndex].size
       if size > biggestHeap:
-        biggest = size
+        biggestHeap = size
         memoryTypeIndex = i
 
   # If we did not found a device-only memory type, let's just take the biggest overall
-  if memoryTypeIndex < 0:
-    for i in 0 ..< physicalProperties.memoryTypeCount:
-        let size = physicalProperties.memoryHeaps[physicalProperties.memoryTypes[i].heapIndex].size
-        if size > biggestHeap:
-          biggest = size
-          memoryTypeIndex = i
+  if memoryTypeIndex == high(uint32):
+    result.needsTransfer = false
+    for i in 0'u32 ..< physicalProperties.memoryTypeCount:
+      let size = physicalProperties.memoryHeaps[physicalProperties.memoryTypes[i].heapIndex].size
+      if size > biggestHeap:
+        biggestHeap = size
+        memoryTypeIndex = i
 
-  assert memoryTypeIndex >= 0, "Unable to find indirect memory type"
+  assert memoryTypeIndex != high(uint32), "Unable to find indirect memory type"
   var allocationInfo = VkMemoryAllocateInfo(
     sType: VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
-    allocationSize: initialAllocationSize,
+    allocationSize: result.size,
     memoryTypeIndex: memoryTypeIndex,
   )
   checkVkResult vkAllocateMemory(
@@ -650,27 +659,30 @@
     addr result.vk
   )
 
-proc AllocateDirectMemory(device: VkDevice, allocationSize: uint64): DirectGPUMemory =
-  result.size = allocationSize
+proc AllocateDirectMemory(device: VkDevice, pDevice: VkPhysicalDevice, size: uint64): DirectGPUMemory =
+  result.size = size
+  result.needsFlush = true
 
   # find a good memory type
   var physicalProperties: VkPhysicalDeviceMemoryProperties
   vkGetPhysicalDeviceMemoryProperties(pDevice, addr physicalProperties)
 
-  var biggestHeap: uint64 = -1
-  var memoryTypeIndex = -1
+  var biggestHeap: uint64 = 0
+  var memoryTypeIndex = high(uint32)
   # try to find host-visible type
   for i in 0 ..< physicalProperties.memoryTypeCount:
-    if VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT in toEnums(physicalProperties.memoryTypes[i].propertyFlags)
+    let flags = toEnums(physicalProperties.memoryTypes[i].propertyFlags)
+    if VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT in flags:
       let size = physicalProperties.memoryHeaps[physicalProperties.memoryTypes[i].heapIndex].size
       if size > biggestHeap:
-        biggest = size
+        biggestHeap = size
         memoryTypeIndex = i
+        result.needsFlush = not (VK_MEMORY_PROPERTY_HOST_COHERENT_BIT in flags)
 
-  assert memoryTypeIndex >= 0, "Unable to find direct (aka host-visible) memory type"
+  assert memoryTypeIndex != high(uint32), "Unable to find indirect memory type"
   var allocationInfo = VkMemoryAllocateInfo(
     sType: VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
-    allocationSize: initialAllocationSize,
+    allocationSize: result.size,
     memoryTypeIndex: memoryTypeIndex,
   )
   checkVkResult vkAllocateMemory(
@@ -679,7 +691,8 @@
     nil,
     addr result.vk
   )
-  checkVkResult result.device.vk.vkMapMemory(
+  checkVkResult vkMapMemory(
+    device = device,
     memory = result.vk,
     offset = 0'u64,
     size = result.size,
@@ -687,11 +700,75 @@
     ppData = addr(result.data)
   )
 
-proc InitRenderData(device: VkDevice, descriptorPoolLimit = 1024): RenderData =
+proc AllocateIndirectBuffer(device: VkDevice, renderData: var RenderData, size: uint64, usage: openArray[VkBufferUsageFlagBits]) =
+  assert size > 0, "Buffer sizes must be larger than 0"
+  var buffer = Buffer[IndirectGPUMemory](size: size)
+
+  # iterate through memory areas to find big enough free space
+  for (memory, offset) in renderData.indirectMemory.mitems:
+    if memory.size - offset >= size:
+      buffer.offset = offset
+      # create buffer
+      var createInfo = VkBufferCreateInfo(
+        sType: VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
+        flags: VkBufferCreateFlags(0),
+        size: buffer.size,
+        usage: toBits(@usage & @[VK_BUFFER_USAGE_TRANSFER_DST_BIT]), # ensure we can transfer to this buffer
+        sharingMode: VK_SHARING_MODE_EXCLUSIVE,
+      )
+      checkVkResult vkCreateBuffer(
+        device = device,
+        pCreateInfo = addr createInfo,
+        pAllocator = nil,
+        pBuffer = addr(buffer.vk)
+      )
+      checkVkResult vkBindBufferMemory(device, buffer.vk, memory.vk, buffer.offset)
+      renderData.indirectBuffers.add buffer
+      # update memory area offset
+      offset = offset + size
+      if offset mod MEMORY_ALIGNMENT != 0:
+        offset = offset + MEMORY_ALIGNMENT - (offset mod MEMORY_ALIGNMENT)
+      return
+
+  assert false, "Did not find allocated memory region with enough space"
+
+proc AllocateDirectBuffer(device: VkDevice, renderData: var RenderData, size: uint64, usage: openArray[VkBufferUsageFlagBits]) =
+  assert size > 0, "Buffer sizes must be larger than 0"
+  var buffer = Buffer[DirectGPUMemory](size: size)
+
+  # iterate through memory areas to find big enough free space
+  for (memory, offset) in renderData.directMemory.mitems:
+    if memory.size - offset >= size:
+      buffer.offset = offset
+      # create buffer
+      var createInfo = VkBufferCreateInfo(
+        sType: VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
+        flags: VkBufferCreateFlags(0),
+        size: buffer.size,
+        usage: toBits(usage),
+        sharingMode: VK_SHARING_MODE_EXCLUSIVE,
+      )
+      checkVkResult vkCreateBuffer(
+        device = device,
+        pCreateInfo = addr createInfo,
+        pAllocator = nil,
+        pBuffer = addr(buffer.vk)
+      )
+      checkVkResult vkBindBufferMemory(device, buffer.vk, memory.vk, buffer.offset)
+      renderData.directBuffers.add buffer
+      # update memory area offset
+      offset = offset + size
+      if offset mod MEMORY_ALIGNMENT != 0:
+        offset = offset + MEMORY_ALIGNMENT - (offset mod MEMORY_ALIGNMENT)
+      return
+
+  assert false, "Did not find allocated memory region with enough space"
+
+proc InitRenderData(device: VkDevice, pDevice: VkPhysicalDevice, descriptorPoolLimit = 1024'u32): RenderData =
   # allocate descriptor pools
   var poolSizes = [
-    VkDescriptorPoolSize(thetype: VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, descriptorCount: descriptorPoolLimit)
-    VkDescriptorPoolSize(thetype: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, descriptorCount: descriptorPoolLimit)
+    VkDescriptorPoolSize(thetype: VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, descriptorCount: descriptorPoolLimit),
+    VkDescriptorPoolSize(thetype: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, descriptorCount: descriptorPoolLimit),
   ]
   var poolInfo = VkDescriptorPoolCreateInfo(
     sType: VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
@@ -702,19 +779,57 @@
   checkVkResult vkCreateDescriptorPool(device, addr(poolInfo), nil, addr(result.descriptorPool))
 
   # allocate some memory
-  var initialAllocationSize: 1_000_000_000 # TODO: make this more dynamic or something
-  result.indirectMemory = @[AllocateIndirectMemory(device, size=initialAllocationSize)]
-  result.nextFreeIndirectMemoryOffset = @[0'u64]
-  result.directMemory = @[AllocateDirectMemory(device, size=initialAllocationSize)]
-  result.nextFreeDirectMemoryOffset = @[0'u64]
+  var initialAllocationSize = 1_000_000_000'u64 # TODO: make this more dynamic or something
+  result.indirectMemory = @[(AllocateIndirectMemory(device, pDevice, size = initialAllocationSize), 0'u64)]
+  result.directMemory = @[(AllocateDirectMemory(device, pDevice, size = initialAllocationSize), 0'u64)]
 
-proc WriteDescriptors[TShader](device: VkDevice, pipeline: Pipeline[TShader]) =
+proc GetIndirectBufferSizes[T](data: T): uint64 =
+  # return buffer sizes for direct and indirect buffers
+  # BUFFER_ALIGNMENT is just added for a rough estimate, to ensure we have enough space to align when binding
+  for name, value in fieldPairs(data):
+    when not hasCustomPragma(value, VertexIndices)
+      when typeof(value) is GPUArray:
+        if not IsDirectMemory(value):
+          result += (value.data.len * sizeof(elementType(value.data))).uint64 + BUFFER_ALIGNMENT
+      when typeof(value) is GPUValue:
+        if not IsDirectMemory(value):
+          result += sizeof(value.data).uint64 + BUFFER_ALIGNMENT
+proc GetDirectBufferSizes[T](data: T): uint64 =
+  # return buffer sizes for direct and indirect buffers
+  # BUFFER_ALIGNMENT is just added for a rough estimate, to ensure we have enough space to align when binding
+  for name, value in fieldPairs(data):
+    when not hasCustomPragma(value, VertexIndices)
+      when typeof(value) is GPUArray:
+        if IsDirectMemory(value):
+          result += (value.data.len * sizeof(elementType(value.data))).uint64 + BUFFER_ALIGNMENT
+      when typeof(value) is GPUValue:
+        if IsDirectMemory(value):
+          result += sizeof(value.data).uint64 + BUFFER_ALIGNMENT
+
+proc GetIndirectIndexBufferSizes[T](data: T): uint64 =
+  for name, value in fieldPairs(data):
+    when hasCustomPragma(value, VertexIndices):
+      static: assert typeof(value) is GPUArray, "Index buffers must be of type GPUArray"
+      static: assert elementType(value.data) is uint8 or elementType(value.data) is uint16 or elementType(value.data) is uint32
+      if not IsDirectMemory(value):
+        result += (value.data.len * sizeof(elementType(value.data))).uint64 + BUFFER_ALIGNMENT
+
+proc GetDirectIndexBufferSizes[T](data: T): uint64 =
+  for name, value in fieldPairs(data):
+    when hasCustomPragma(value, VertexIndices):
+      static: assert typeof(value) is GPUArray, "Index buffers must be of type GPUArray"
+      static: assert elementType(value.data) is uint8 or elementType(value.data) is uint16 or elementType(value.data) is uint32
+      if IsDirectMemory(value):
+        result += (value.data.len * sizeof(elementType(value.data))).uint64 + BUFFER_ALIGNMENT
+
+
+proc WriteDescriptors[TShader](device: VkDevice, descriptorSets: array[INFLIGHTFRAMES.int, VkDescriptorSet]) =
   var descriptorSetWrites: seq[VkWriteDescriptorSet]
   # map (buffer + offset + range) to descriptor
   # map (texture) to descriptor
   ForDescriptorFields(default(TShader), fieldName, descriptorType, descriptorCount, descriptorBindingNumber):
-    for frameInFlight in 0 ..< pipeline.descriptorSets.len:
-      if descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
+    for frameInFlight in 0 ..< descriptorSets.len:
+      when descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
         # TODO
         let bufferInfo = VkDescriptorBufferInfo(
           buffer: VkBuffer(0),
@@ -723,7 +838,7 @@
         )
         descriptorSetWrites.add VkWriteDescriptorSet(
           sType: VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
-          dstSet: pipeline.descriptorSets[frameInFlight],
+          dstSet: descriptorSets[frameInFlight],
           dstBinding: descriptorBindingNumber,
           dstArrayElement: uint32(0),
           descriptorType: descriptorType,
@@ -740,7 +855,7 @@
         )
         descriptorSetWrites.add VkWriteDescriptorSet(
           sType: VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
-          dstSet: pipeline.descriptorSets[frameInFlight],
+          dstSet: descriptorSets[frameInFlight],
           dstBinding: descriptorBindingNumber,
           dstArrayElement: uint32(0),
           descriptorType: descriptorType,
@@ -750,27 +865,21 @@
         )
   vkUpdateDescriptorSets(device, uint32(descriptorSetWrites.len), descriptorSetWrites.ToCPointer, 0, nil)
 
-proc CreateRenderable[TMesh, TInstance](
-  mesh: TMesh,
-  instance: TInstance,
-): Renderable[TMesh, TInstance] =
-  result.indexType = None
-
 proc Bind[T](pipeline: Pipeline[T], commandBuffer: VkCommandBuffer, currentFrameInFlight: int) =
-  let a = pipeline.descriptorSets
   commandBuffer.vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline)
-  if a[currentFrameInFlight] != VkDescriptorSet(0):
-    commandBuffer.vkCmdBindDescriptorSets(
-      VK_PIPELINE_BIND_POINT_GRAPHICS,
-      pipeline.layout,
-      0,
-      1,
-      addr pipeline.descriptorSets[currentFrameInFlight],
-      0,
-      nil,
-    )
+  #[
+  commandBuffer.vkCmdBindDescriptorSets(
+    VK_PIPELINE_BIND_POINT_GRAPHICS,
+    pipeline.layout,
+    0,
+    1,
+    addr pipeline.descriptorSets[currentFrameInFlight],
+    0,
+    nil,
+  )
+  ]#
 
-proc AssertCompatible(TShader, TMesh, TInstance, TGlobals: typedesc) =
+proc AssertCompatible(TShader, TMesh, TInstance, TUniforms, TGlobals: typedesc) =
   # assert seq-fields of TMesh|TInstance == seq-fields of TShader
   # assert normal fields of TMesh|Globals == normal fields of TShaderDescriptors
   for inputName, inputValue in default(TShader).fieldPairs:
@@ -800,10 +909,10 @@
 
     # Texture
     elif typeof(inputValue) is Texture:
-      for meshName, meshValue in default(TMesh).fieldPairs:
-        when meshName == inputName:
+      for uniformName, uniformValue in default(TUniforms).fieldPairs:
+        when uniformName == inputName:
           assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once"
-          assert typeof(meshValue) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but mesh attribute is of type '" & tt.name(elementType(meshValue)) & "'"
+          assert typeof(uniformValue) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but uniform attribute is of type '" & tt.name(typeof(uniformValue)) & "'"
           foundField = true
       for globalName, globalValue in default(TGlobals).fieldPairs:
         when globalName == inputName:
@@ -814,11 +923,11 @@
 
     # Uniform block
     elif typeof(inputValue) is object:
-      for meshName, meshValue in default(TMesh).fieldPairs:
-        when meshName == inputName:
-          assert meshValue is GPUValue, "Mesh attribute '" & meshName & "' must be of type 'GPUValue' but is of type " & tt.name(typeof(meshValue))
+      for uniformName, uniformValue in default(TUniforms).fieldPairs:
+        when uniformName == inputName:
+          assert uniformValue is GPUValue, "global attribute '" & uniformName & "' must be of type 'GPUValue' but is of type " & tt.name(typeof(uniformValue))
           assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once"
-          assert typeof(meshValue.data) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but mesh attribute is of type '" & tt.name(elementType(meshValue.data)) & "'"
+          assert typeof(uniformValue.data) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but uniform attribute is of type '" & tt.name(typeof(uniformValue.data)) & "'"
           foundField = true
       for globalName, globalValue in default(TGlobals).fieldPairs:
         when globalName == inputName:
@@ -833,10 +942,10 @@
 
       # texture-array
       when elementType(inputValue) is Texture:
-        for meshName, meshValue in default(TMesh).fieldPairs:
-          when meshName == inputName:
+        for uniformName, uniformValue in default(TUniforms).fieldPairs:
+          when uniformName == inputName:
             assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once"
-            assert typeof(meshValue) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but mesh attribute is of type '" & tt.name(elementType(meshValue)) & "'"
+            assert typeof(uniformValue) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but uniform attribute is of type '" & tt.name(typeof(uniformValue)) & "'"
             foundField = true
         for globalName, globalValue in default(TGlobals).fieldPairs:
           when globalName == inputName:
@@ -847,11 +956,11 @@
 
       # uniform-block array
       elif elementType(inputValue) is object:
-        for meshName, meshValue in default(TMesh).fieldPairs:
-          when meshName == inputName:
-            assert meshValue is GPUValue, "Mesh attribute '" & meshName & "' must be of type 'GPUValue' but is of type " & tt.name(typeof(meshValue))
+        for uniformName, uniformValue in default(TUniforms).fieldPairs:
+          when uniformName == inputName:
+            assert uniformValue is GPUValue, "global attribute '" & uniformName & "' must be of type 'GPUValue' but is of type " & tt.name(typeof(uniformValue))
             assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once"
-            assert typeof(meshValue.data) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but mesh attribute is of type '" & tt.name(elementType(meshValue.data)) & "'"
+            assert typeof(uniformValue.data) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but uniform attribute is of type '" & tt.name(typeof(uniformValue.data)) & "'"
             foundField = true
         for globalName, globalValue in default(TGlobals).fieldPairs:
           when globalName == inputName:
@@ -862,13 +971,14 @@
         assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TMesh) & "|" & tt.name(TGlobals) & "'"
 
 
-proc Render[TShader, TMesh, TInstance, TGlobals](
+proc Render[TShader, TMesh, TInstance, TUniforms, TGlobals](
   pipeline: Pipeline[TShader],
   renderable: Renderable[TMesh, TInstance],
+  uniforms: TUniforms,
   globals: TGlobals,
   commandBuffer: VkCommandBuffer,
 ) =
-  static: AssertCompatible(TShader, TMesh, TInstance, TGlobals)
+  static: AssertCompatible(TShader, TMesh, TInstance, TUniforms, TGlobals)
   if renderable.vertexBuffers.len > 0:
     commandBuffer.vkCmdBindVertexBuffers(
       firstBinding = 0'u32,
@@ -905,19 +1015,20 @@
   import std/options
 
   type
+    MeshA = object
+      position: GPUArray[Vec3f, IndirectGPUMemory]
+      indices {.VertexIndices.}: GPUArray[uint16, IndirectGPUMemory]
+    InstanceA = object
+      rotation: GPUArray[Vec4f, IndirectGPUMemory]
+      objPosition: GPUArray[Vec3f, IndirectGPUMemory]
     MaterialA = object
       reflection: float32
       baseColor: Vec3f
-    ShaderSettings = object
-      brightness: float32
-    MeshA = object
-      position: GPUArray[Vec3f, IndirectGPUMemory]
-    InstanceA = object
-      rotation: GPUArray[Vec4f, IndirectGPUMemory]
-      objPosition: GPUArray[Vec3f, IndirectGPUMemory]
     UniformsA = object
       materials: GPUValue[array[3, MaterialA], IndirectGPUMemory]
       materialTextures: array[3, Texture]
+    ShaderSettings = object
+      brightness: float32
     GlobalsA = object
       fontAtlas: Texture
       settings: GPUValue[ShaderSettings, IndirectGPUMemory]
@@ -990,9 +1101,10 @@
   let shaderObject = dev.vk.CompileShader(shader)
   var pipeline1 = CreatePipeline(dev.vk, renderPass = renderpass, shaderObject)
 
-  var renderdata = InitRenderData(dev.vk)
+  var renderdata = InitRenderData(dev.vk, dev.physicalDevice.vk)
 
   # create descriptor sets
+  #[
   var descriptorSets: array[INFLIGHTFRAMES.int, VkDescriptorSet]
   var layouts = newSeqWith(descriptorSets.len, pipeline.descriptorSetLayout)
   var allocInfo = VkDescriptorSetAllocateInfo(
@@ -1002,21 +1114,60 @@
     pSetLayouts: layouts.ToCPointer,
   )
   checkVkResult vkAllocateDescriptorSets(device, addr(allocInfo), descriptorSets.ToCPointer)
-
+  ]#
 
   #[
-  # TODO: probably here: allocate renderables, uniform buffers & textures
-  let meshBuffers: seq[(bool, uint64)] = GetBufferSizes[MeshA](item = myMesh1)
-  let instanceBuffers: seq[(bool, uint64)] = GetBufferSizes[InstanceA](item = instances1)
-  let globalBuffers: seq[(bool, uint64)] = GetBufferSizes[Globals](item = myGlobals)
-  var myRenderable = CreateRenderable()
-  UploadTextures[MeshA]()
-  UploadTextures[Globals]()
+  # TODO:
+  #
+  # assign indirect buffers to vertex data, can happen through the GPUArray/GPUValue-wrappers, they know buffers
+  # assign direct buffers to vertex data
+  # assign indirect buffers to uniform data
+  # assign direct buffers to uniform data
+  #
+  # upload all textures
+  # write descriptors for textures and uniform buffers
+  #
   ]#
   var myRenderable: Renderable[MeshA, InstanceA]
 
+  var
+    indirectVertexSizes = 0'u64
+    directVertexSizes = 0'u64
+    indirectIndexSizes = 0'u64
+    directIndexSizes = 0'u64
+    indirectUniformSizes = 0'u64
+    directUniformSizes = 0'u64
+
+  indirectVertexSizes += GetIndirectBufferSizes(myMesh1)
+  indirectVertexSizes += GetIndirectBufferSizes(instances1)
+  if indirectVertexSizes > 0:
+    AllocateIndirectBuffer(dev.vk, renderdata, indirectVertexSizes, [VK_BUFFER_USAGE_VERTEX_BUFFER_BIT])
+
+  directVertexSizes += GetDirectBufferSizes(myMesh1)
+  directVertexSizes += GetDirectBufferSizes(instances1)
+  if directVertexSizes > 0:
+    AllocateDirectBuffer(dev.vk, renderdata, directVertexSizes, [VK_BUFFER_USAGE_VERTEX_BUFFER_BIT])
+
+  indirectIndexSizes += GetIndirectIndexBufferSizes(myMesh1)
+  if indirectIndexSizes > 0:
+    AllocateIndirectBuffer(dev.vk, renderdata, indirectIndexSizes, [VK_BUFFER_USAGE_INDEX_BUFFER_BIT])
+
+  directIndexSizes += GetDirectIndexBufferSizes(myMesh1)
+  if directIndexSizes > 0:
+    AllocateIndirectBuffer(dev.vk, renderdata, directIndexSizes, [VK_BUFFER_USAGE_INDEX_BUFFER_BIT])
+
+  indirectUniformSizes += GetIndirectBufferSizes(uniforms1)
+  indirectUniformSizes += GetIndirectBufferSizes(myGlobals)
+  if indirectUniformSizes > 0:
+    AllocateIndirectBuffer(dev.vk, renderdata, indirectUniformSizes, [VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT])
+
+  directUniformSizes += GetDirectBufferSizes(uniforms1)
+  directUniformSizes += GetDirectBufferSizes(myGlobals)
+  if directUniformSizes > 0:
+    AllocateDirectBuffer(dev.vk, renderdata, directUniformSizes, [VK_BUFFER_USAGE_VERTEX_BUFFER_BIT])
+
   # descriptors
-  WriteDescriptors(dev.vk, pipeline1)
+  # WriteDescriptors(dev.vk, pipeline1)
 
   # command buffer
   var
@@ -1088,7 +1239,7 @@
 
         # render object, will be loop
         block:
-          Render(pipeline1, myRenderable, myGlobals, cmd)
+          Render(pipeline1, myRenderable, uniforms1, myGlobals, cmd)
 
       vkCmdEndRenderPass(cmd)
     checkVkResult cmd.vkEndCommandBuffer()