changeset 1192:90bf0cab1d02 compiletime-tests

did: so many, many things
author sam <sam@basx.dev>
date Mon, 08 Jul 2024 16:05:06 +0700
parents 239adab121a3
children 5aa1184fa5eb
files semicongine/platform/linux/window.nim semicongine/platform/windows/surface.nim semicongine/rendering.nim semicongine/rendering/renderer.nim semicongine/rendering/shaders.nim semicongine/rendering/vulkan_wrappers.nim test1.nim
diffstat 7 files changed, 357 insertions(+), 305 deletions(-) [+]
line wrap: on
line diff
--- a/semicongine/platform/linux/window.nim	Mon Jul 08 01:16:53 2024 +0700
+++ b/semicongine/platform/linux/window.nim	Mon Jul 08 16:05:06 2024 +0700
@@ -21,7 +21,7 @@
         " returned " & $value)
 
 proc XErrorLogger(display: PDisplay, event: PXErrorEvent): cint {.cdecl.} =
-  error &"Xlib: {event[]}"
+  logging.error &"Xlib: {event[]}"
 
 proc CreateWindow*(title: string): NativeWindow =
   checkXlibResult XInitThreads()
--- a/semicongine/platform/windows/surface.nim	Mon Jul 08 01:16:53 2024 +0700
+++ b/semicongine/platform/windows/surface.nim	Mon Jul 08 16:05:06 2024 +0700
@@ -1,6 +1,3 @@
-import ../../core
-import ../../platform/window
-
 proc CreateNativeSurface*(instance: VkInstance, window: NativeWindow): VkSurfaceKHR =
   assert instance.Valid
   var surfaceCreateInfo = VkWin32SurfaceCreateInfoKHR(
--- a/semicongine/rendering.nim	Mon Jul 08 01:16:53 2024 +0700
+++ b/semicongine/rendering.nim	Mon Jul 08 16:05:06 2024 +0700
@@ -1,3 +1,12 @@
+# in this file:
+# - const defintions for rendering
+# - custom pragma defintions for rendering
+# - type defintions for rendering
+# - some utils code that is used in mutiple rendering files
+# - inclusion of all rendering files
+
+
+# const definitions
 const INFLIGHTFRAMES = 2'u32
 const BUFFER_ALIGNMENT = 64'u64 # align offsets inside buffers along this alignment
 const MEMORY_BLOCK_ALLOCATION_SIZE = 100_000_000'u64 # ca. 100mb per block, seems reasonable
@@ -23,7 +32,7 @@
     GlobalSet
     MaterialSet
   DescriptorSet*[T: object, sType: static DescriptorSetType] = object
-    data: T
+    data*: T
     vk: array[INFLIGHTFRAMES.int, VkDescriptorSet]
   Pipeline*[TShader] = object
     vk: VkPipeline
@@ -52,15 +61,15 @@
     vk: VkImage
     imageview: VkImageView
     sampler: VkSampler
-    width: uint32
-    height: uint32
-    data: seq[T]
+    width*: uint32
+    height*: uint32
+    data*: seq[T]
   GPUArray*[T: SupportedGPUType, TBuffer: static BufferType] = object
-    data: seq[T]
+    data*: seq[T]
     buffer: Buffer
     offset: uint64
   GPUValue*[T: object|array, TBuffer: static BufferType] = object
-    data: T
+    data*: T
     buffer: Buffer
     offset: uint64
   GPUData = GPUArray | GPUValue
@@ -70,6 +79,45 @@
     memory: array[VK_MAX_MEMORY_TYPES.int, seq[MemoryBlock]]
     buffers: array[BufferType, seq[Buffer]]
 
+template ForDescriptorFields(shader: typed, fieldname, valuename, typename, countname, bindingNumber, body: untyped): untyped =
+  var `bindingNumber` {.inject.} = 1'u32
+  for theFieldname, value in fieldPairs(shader):
+    when typeof(value) is Texture:
+      block:
+        const `fieldname` {.inject.} = theFieldname
+        const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
+        const `countname` {.inject.} = 1'u32
+        let `valuename` {.inject.} = value
+        body
+        `bindingNumber`.inc
+    elif typeof(value) is object:
+      block:
+        const `fieldname` {.inject.} = theFieldname
+        const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER
+        const `countname` {.inject.} = 1'u32
+        let `valuename` {.inject.} = value
+        body
+        `bindingNumber`.inc
+    elif typeof(value) is array:
+      when elementType(value) is Texture:
+        block:
+          const `fieldname` {.inject.} = theFieldname
+          const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
+          const `countname` {.inject.} = uint32(typeof(value).len)
+          let `valuename` {.inject.} = value
+          body
+          `bindingNumber`.inc
+      elif elementType(value) is object:
+        block:
+          const `fieldname` {.inject.} = theFieldname
+          const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER
+          const `countname` {.inject.} = uint32(typeof(value).len)
+          let `valuename` {.inject.} = value
+          body
+          `bindingNumber`.inc
+      else:
+        {.error: "Unsupported descriptor type: " & typetraits.name(typeof(value)).}
+
 include ./rendering/vulkan_wrappers
 include ./rendering/shaders
 include ./rendering/renderer
--- a/semicongine/rendering/renderer.nim	Mon Jul 08 01:16:53 2024 +0700
+++ b/semicongine/rendering/renderer.nim	Mon Jul 08 16:05:06 2024 +0700
@@ -56,168 +56,6 @@
   else:
     return value + alignment - remainder
 
-func VkType[T: SupportedGPUType](value: T): VkFormat =
-  when T is float32: VK_FORMAT_R32_SFLOAT
-  elif T is float64: VK_FORMAT_R64_SFLOAT
-  elif T is int8: VK_FORMAT_R8_SINT
-  elif T is int16: VK_FORMAT_R16_SINT
-  elif T is int32: VK_FORMAT_R32_SINT
-  elif T is int64: VK_FORMAT_R64_SINT
-  elif T is uint8: VK_FORMAT_R8_UINT
-  elif T is uint16: VK_FORMAT_R16_UINT
-  elif T is uint32: VK_FORMAT_R32_UINT
-  elif T is uint64: VK_FORMAT_R64_UINT
-  elif T is TVec2[int32]: VK_FORMAT_R32G32_SINT
-  elif T is TVec2[int64]: VK_FORMAT_R64G64_SINT
-  elif T is TVec3[int32]: VK_FORMAT_R32G32B32_SINT
-  elif T is TVec3[int64]: VK_FORMAT_R64G64B64_SINT
-  elif T is TVec4[int32]: VK_FORMAT_R32G32B32A32_SINT
-  elif T is TVec4[int64]: VK_FORMAT_R64G64B64A64_SINT
-  elif T is TVec2[uint32]: VK_FORMAT_R32G32_UINT
-  elif T is TVec2[uint64]: VK_FORMAT_R64G64_UINT
-  elif T is TVec3[uint32]: VK_FORMAT_R32G32B32_UINT
-  elif T is TVec3[uint64]: VK_FORMAT_R64G64B64_UINT
-  elif T is TVec4[uint32]: VK_FORMAT_R32G32B32A32_UINT
-  elif T is TVec4[uint64]: VK_FORMAT_R64G64B64A64_UINT
-  elif T is TVec2[float32]: VK_FORMAT_R32G32_SFLOAT
-  elif T is TVec2[float64]: VK_FORMAT_R64G64_SFLOAT
-  elif T is TVec3[float32]: VK_FORMAT_R32G32B32_SFLOAT
-  elif T is TVec3[float64]: VK_FORMAT_R64G64B64_SFLOAT
-  elif T is TVec4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT
-  elif T is TVec4[float64]: VK_FORMAT_R64G64B64A64_SFLOAT
-  elif T is TMat2[float32]: VK_FORMAT_R32G32_SFLOAT
-  elif T is TMat2[float64]: VK_FORMAT_R64G64_SFLOAT
-  elif T is TMat23[float32]: VK_FORMAT_R32G32B32_SFLOAT
-  elif T is TMat23[float64]: VK_FORMAT_R64G64B64_SFLOAT
-  elif T is TMat32[float32]: VK_FORMAT_R32G32_SFLOAT
-  elif T is TMat32[float64]: VK_FORMAT_R64G64_SFLOAT
-  elif T is TMat3[float32]: VK_FORMAT_R32G32B32_SFLOAT
-  elif T is TMat3[float64]: VK_FORMAT_R64G64B64_SFLOAT
-  elif T is TMat34[float32]: VK_FORMAT_R32G32B32A32_SFLOAT
-  elif T is TMat34[float64]: VK_FORMAT_R64G64B64A64_SFLOAT
-  elif T is TMat43[float32]: VK_FORMAT_R32G32B32_SFLOAT
-  elif T is TMat43[float64]: VK_FORMAT_R64G64B64_SFLOAT
-  elif T is TMat4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT
-  elif T is TMat4[float64]: VK_FORMAT_R64G64B64A64_SFLOAT
-  else: {.error: "Unsupported data type on GPU".}
-
-func GlslType[T: SupportedGPUType|Texture](value: T): string =
-  when T is float32: "float"
-  elif T is float64: "double"
-  elif T is int8 or T is int16 or T is int32 or T is int64: "int"
-  elif T is uint8 or T is uint16 or T is uint32 or T is uint64: "uint"
-  elif T is TVec2[int32]: "ivec2"
-  elif T is TVec2[int64]: "ivec2"
-  elif T is TVec3[int32]: "ivec3"
-  elif T is TVec3[int64]: "ivec3"
-  elif T is TVec4[int32]: "ivec4"
-  elif T is TVec4[int64]: "ivec4"
-  elif T is TVec2[uint32]: "uvec2"
-  elif T is TVec2[uint64]: "uvec2"
-  elif T is TVec3[uint32]: "uvec3"
-  elif T is TVec3[uint64]: "uvec3"
-  elif T is TVec4[uint32]: "uvec4"
-  elif T is TVec4[uint64]: "uvec4"
-  elif T is TVec2[float32]: "vec2"
-  elif T is TVec2[float64]: "dvec2"
-  elif T is TVec3[float32]: "vec3"
-  elif T is TVec3[float64]: "dvec3"
-  elif T is TVec4[float32]: "vec4"
-  elif T is TVec4[float64]: "dvec4"
-  elif T is TMat2[float32]: "mat2"
-  elif T is TMat2[float64]: "dmat2"
-  elif T is TMat23[float32]: "mat23"
-  elif T is TMat23[float64]: "dmat23"
-  elif T is TMat32[float32]: "mat32"
-  elif T is TMat32[float64]: "dmat32"
-  elif T is TMat3[float32]: "mat3"
-  elif T is TMat3[float64]: "dmat3"
-  elif T is TMat34[float32]: "mat34"
-  elif T is TMat34[float64]: "dmat34"
-  elif T is TMat43[float32]: "mat43"
-  elif T is TMat43[float64]: "dmat43"
-  elif T is TMat4[float32]: "mat4"
-  elif T is TMat4[float64]: "dmat4"
-  elif T is Texture: "sampler2D"
-  else: {.error: "Unsupported data type on GPU".}
-
-template ForDescriptorFields(shader: typed, fieldname, valuename, typename, countname, bindingNumber, body: untyped): untyped =
-  var `bindingNumber` {.inject.} = 1'u32
-  for theFieldname, value in fieldPairs(shader):
-    when typeof(value) is Texture:
-      block:
-        const `fieldname` {.inject.} = theFieldname
-        const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
-        const `countname` {.inject.} = 1'u32
-        let `valuename` {.inject.} = value
-        body
-        `bindingNumber`.inc
-    elif typeof(value) is object:
-      block:
-        const `fieldname` {.inject.} = theFieldname
-        const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER
-        const `countname` {.inject.} = 1'u32
-        let `valuename` {.inject.} = value
-        body
-        `bindingNumber`.inc
-    elif typeof(value) is array:
-      when elementType(value) is Texture:
-        block:
-          const `fieldname` {.inject.} = theFieldname
-          const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
-          const `countname` {.inject.} = uint32(typeof(value).len)
-          let `valuename` {.inject.} = value
-          body
-          `bindingNumber`.inc
-      elif elementType(value) is object:
-        block:
-          const `fieldname` {.inject.} = theFieldname
-          const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER
-          const `countname` {.inject.} = uint32(typeof(value).len)
-          let `valuename` {.inject.} = value
-          body
-          `bindingNumber`.inc
-      else:
-        {.error: "Unsupported descriptor type: " & typetraits.name(typeof(value)).}
-
-func NumberOfVertexInputAttributeDescriptors[T: SupportedGPUType|Texture](value: T): uint32 =
-  when T is TMat2[float32] or T is TMat2[float64] or T is TMat23[float32] or T is TMat23[float64]:
-    2
-  elif T is TMat32[float32] or T is TMat32[float64] or T is TMat3[float32] or T is TMat3[float64] or T is TMat34[float32] or T is TMat34[float64]:
-    3
-  elif T is TMat43[float32] or T is TMat43[float64] or T is TMat4[float32] or T is TMat4[float64]:
-    4
-  else:
-    1
-
-func NLocationSlots[T: SupportedGPUType|Texture](value: T): uint32 =
-  #[
-  single location:
-    - any scalar
-    - any 16-bit vector
-    - any 32-bit vector
-    - any 64-bit vector that has max. 2 components
-    16-bit scalar and vector types, and
-    32-bit scalar and vector types, and
-    64-bit scalar and 2-component vector types.
-  two locations
-    64-bit three- and four-component vectors
-  ]#
-  when T is TVec3[int64] or
-    T is TVec4[int64] or
-    T is TVec3[uint64] or
-    T is TVec4[uint64] or
-    T is TVec3[float64] or
-    T is TVec4[float64] or
-    T is TMat23[float64] or
-    T is TMat3[float64] or
-    T is TMat34[float64] or
-    T is TMat43[float64] or
-    T is TMat4[float64]:
-    return 2
-  else:
-    return 1
-
 template sType(descriptorSet: DescriptorSet): untyped =
   get(genericParams(typeof(descriptorSet)), 1)
 
@@ -245,53 +83,20 @@
 template rawPointer(gpuValue: GPUValue): pointer =
   addr(gpuValue.data)
 
-proc GetPhysicalDevice(instance: VkInstance): VkPhysicalDevice =
-  var nDevices: uint32
-  checkVkResult vkEnumeratePhysicalDevices(instance, addr(nDevices), nil)
-  var devices = newSeq[VkPhysicalDevice](nDevices)
-  checkVkResult vkEnumeratePhysicalDevices(instance, addr(nDevices), devices.ToCPointer)
-
-  var score = 0'u32
-  for pDevice in devices:
-    var props: VkPhysicalDeviceProperties
-    # CANNOT use svkGetPhysicalDeviceProperties (not initialized yet)
-    vkGetPhysicalDeviceProperties(pDevice, addr(props))
-    if props.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU and props.limits.maxImageDimension2D > score:
-      score = props.limits.maxImageDimension2D
-      result = pDevice
-
-  if score == 0:
-    for pDevice in devices:
-      var props: VkPhysicalDeviceProperties
-      # CANNOT use svkGetPhysicalDeviceProperties (not initialized yet)
-      vkGetPhysicalDeviceProperties(pDevice, addr(props))
-      if props.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU and props.limits.maxImageDimension2D > score:
-        score = props.limits.maxImageDimension2D
-        result = pDevice
-
-  assert score > 0, "Unable to find integrated or discrete GPU"
-
 proc IsMappable(memoryTypeIndex: uint32): bool =
   var physicalProperties: VkPhysicalDeviceMemoryProperties
   vkGetPhysicalDeviceMemoryProperties(vulkan.physicalDevice, addr(physicalProperties))
   let flags = toEnums(physicalProperties.memoryTypes[memoryTypeIndex].propertyFlags)
   return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT in flags
 
-proc GetQueueFamily(pDevice: VkPhysicalDevice, qType: VkQueueFlagBits): uint32 =
-  var nQueuefamilies: uint32
-  vkGetPhysicalDeviceQueueFamilyProperties(pDevice, addr nQueuefamilies, nil)
-  var queuFamilies = newSeq[VkQueueFamilyProperties](nQueuefamilies)
-  vkGetPhysicalDeviceQueueFamilyProperties(pDevice, addr nQueuefamilies, queuFamilies.ToCPointer)
-  for i in 0'u32 ..< nQueuefamilies:
-    if qType in toEnums(queuFamilies[i].queueFlags):
-      return i
-  assert false, &"Queue of type {qType} not found"
-
 proc GetSurfaceFormat(): VkFormat =
   # EVERY windows driver and almost every linux driver should support this
   VK_FORMAT_B8G8R8A8_SRGB
 
-proc InitDescriptorSet(
+proc GetLayoutFor*(pipeline: Pipeline, dType: DescriptorSetType): VkDescriptorSetLayout =
+  pipeline.descriptorSetLayouts[dType]
+
+proc InitDescriptorSet*(
   renderData: RenderData,
   layout: VkDescriptorSetLayout,
   descriptorSet: var DescriptorSet,
@@ -394,18 +199,30 @@
     of UInt16: VK_INDEX_TYPE_UINT16
     of UInt32: VK_INDEX_TYPE_UINT32
 
-proc CreateRenderPass(format: VkFormat): VkRenderPass =
+proc CreatePresentationRenderPass*(samples = VK_SAMPLE_COUNT_1_BIT): VkRenderPass =
+  let format = GetSurfaceFormat()
+  var attachments = @[VkAttachmentDescription(
+    format: format,
+    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: if samples == VK_SAMPLE_COUNT_1_BIT: VK_IMAGE_LAYOUT_PRESENT_SRC_KHR else: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+  )]
+  if samples != VK_SAMPLE_COUNT_1_BIT:
+    attachments.add VkAttachmentDescription(
+      format: 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_PRESENT_SRC_KHR,
+    )
   var
-    attachments = @[VkAttachmentDescription(
-        format: format,
-        samples: VK_SAMPLE_COUNT_1_BIT,
-        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_PRESENT_SRC_KHR,
-    )]
     dependencies = @[VkSubpassDependency(
       srcSubpass: VK_SUBPASS_EXTERNAL,
       dstSubpass: 0,
@@ -414,34 +231,33 @@
       dstStageMask: toBits [VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT],
       dstAccessMask: toBits [VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT],
     )]
-    outputs = @[
-      VkAttachmentReference(
-        attachment: 0,
-        layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
-      )
-    ]
+    colorAttachment = VkAttachmentReference(
+      attachment: 0,
+      layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+    )
+    resolveAttachment = VkAttachmentReference(
+      attachment: 1,
+      layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+    )
 
-  var subpassesList = [
-    VkSubpassDescription(
-      flags: VkSubpassDescriptionFlags(0),
-      pipelineBindPoint: VK_PIPELINE_BIND_POINT_GRAPHICS,
-      inputAttachmentCount: 0,
-      pInputAttachments: nil,
-      colorAttachmentCount: uint32(outputs.len),
-      pColorAttachments: outputs.ToCPointer,
-      pResolveAttachments: nil,
-      pDepthStencilAttachment: nil,
-      preserveAttachmentCount: 0,
-      pPreserveAttachments: nil,
-    )
-  ]
-
+  var subpass = VkSubpassDescription(
+    flags: VkSubpassDescriptionFlags(0),
+    pipelineBindPoint: VK_PIPELINE_BIND_POINT_GRAPHICS,
+    inputAttachmentCount: 0,
+    pInputAttachments: nil,
+    colorAttachmentCount: 1,
+    pColorAttachments: addr(colorAttachment),
+    pResolveAttachments: if samples == VK_SAMPLE_COUNT_1_BIT: nil else: addr(resolveAttachment),
+    pDepthStencilAttachment: nil,
+    preserveAttachmentCount: 0,
+    pPreserveAttachments: nil,
+  )
   var createInfo = VkRenderPassCreateInfo(
       sType: VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
       attachmentCount: uint32(attachments.len),
       pAttachments: attachments.ToCPointer,
-      subpassCount: uint32(subpassesList.len),
-      pSubpasses: subpassesList.ToCPointer,
+      subpassCount: 1,
+      pSubpasses: addr(subpass),
       dependencyCount: uint32(dependencies.len),
       pDependencies: dependencies.ToCPointer,
     )
@@ -465,7 +281,7 @@
       ppData = addr(result.rawPointer)
     )
 
-proc FlushAllMemory(renderData: RenderData) =
+proc FlushAllMemory*(renderData: RenderData) =
   var flushRegions = newSeq[VkMappedMemoryRange]()
   for memoryBlocks in renderData.memory:
     for memoryBlock in memoryBlocks:
@@ -517,7 +333,7 @@
   result.rawPointer = selectedBlock.rawPointer.pointerAddOffset(selectedBlock.offsetNextFree)
   renderData.memory[memoryType][selectedBlockI].offsetNextFree += memoryRequirements.size
 
-proc AssignBuffers[T](renderdata: var RenderData, data: var T) =
+proc AssignBuffers*[T](renderdata: var RenderData, data: var T) =
   for name, value in fieldPairs(data):
     when typeof(value) is GPUData:
 
@@ -545,7 +361,7 @@
       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) =
+proc AssignBuffers*(renderdata: var RenderData, descriptorSet: var DescriptorSet) =
   AssignBuffers(renderdata, descriptorSet.data)
 
 proc UpdateGPUBuffer(gpuData: GPUData) =
@@ -557,13 +373,13 @@
     WithStagingBuffer((gpuData.buffer.vk, gpuData.offset), gpuData.size, stagingPtr):
       copyMem(stagingPtr, gpuData.rawPointer, gpuData.size)
 
-proc UpdateAllGPUBuffers[T](value: T) =
+proc UpdateAllGPUBuffers*[T](value: T) =
   for name, fieldvalue in value.fieldPairs():
     when typeof(fieldvalue) is GPUData:
       UpdateGPUBuffer(fieldvalue)
 
 
-proc InitRenderData(descriptorPoolLimit = 1024'u32): RenderData =
+proc InitRenderData*(descriptorPoolLimit = 1024'u32): RenderData =
   # allocate descriptor pools
   var poolSizes = [
     VkDescriptorPoolSize(thetype: VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, descriptorCount: descriptorPoolLimit),
@@ -725,7 +541,7 @@
   TransitionImageLayout(texture.vk, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
 
 
-proc UploadTextures(renderdata: var RenderData, descriptorSet: var DescriptorSet) =
+proc UploadTextures*(renderdata: var RenderData, descriptorSet: var DescriptorSet) =
   for name, value in fieldPairs(descriptorSet.data):
     when typeof(value) is Texture:
       echo "Upload texture '", name, "'"
--- a/semicongine/rendering/shaders.nim	Mon Jul 08 01:16:53 2024 +0700
+++ b/semicongine/rendering/shaders.nim	Mon Jul 08 16:05:06 2024 +0700
@@ -1,7 +1,131 @@
 type
   ShaderObject[TShader] = object
-    vertexShader: VkShaderModule
-    fragmentShader: VkShaderModule
+    vertexShaderModule: VkShaderModule
+    fragmentShaderModule: VkShaderModule
+
+func GlslType[T: SupportedGPUType|Texture](value: T): string =
+  when T is float32: "float"
+  elif T is float64: "double"
+  elif T is int8 or T is int16 or T is int32 or T is int64: "int"
+  elif T is uint8 or T is uint16 or T is uint32 or T is uint64: "uint"
+  elif T is TVec2[int32]: "ivec2"
+  elif T is TVec2[int64]: "ivec2"
+  elif T is TVec3[int32]: "ivec3"
+  elif T is TVec3[int64]: "ivec3"
+  elif T is TVec4[int32]: "ivec4"
+  elif T is TVec4[int64]: "ivec4"
+  elif T is TVec2[uint32]: "uvec2"
+  elif T is TVec2[uint64]: "uvec2"
+  elif T is TVec3[uint32]: "uvec3"
+  elif T is TVec3[uint64]: "uvec3"
+  elif T is TVec4[uint32]: "uvec4"
+  elif T is TVec4[uint64]: "uvec4"
+  elif T is TVec2[float32]: "vec2"
+  elif T is TVec2[float64]: "dvec2"
+  elif T is TVec3[float32]: "vec3"
+  elif T is TVec3[float64]: "dvec3"
+  elif T is TVec4[float32]: "vec4"
+  elif T is TVec4[float64]: "dvec4"
+  elif T is TMat2[float32]: "mat2"
+  elif T is TMat2[float64]: "dmat2"
+  elif T is TMat23[float32]: "mat23"
+  elif T is TMat23[float64]: "dmat23"
+  elif T is TMat32[float32]: "mat32"
+  elif T is TMat32[float64]: "dmat32"
+  elif T is TMat3[float32]: "mat3"
+  elif T is TMat3[float64]: "dmat3"
+  elif T is TMat34[float32]: "mat34"
+  elif T is TMat34[float64]: "dmat34"
+  elif T is TMat43[float32]: "mat43"
+  elif T is TMat43[float64]: "dmat43"
+  elif T is TMat4[float32]: "mat4"
+  elif T is TMat4[float64]: "dmat4"
+  elif T is Texture: "sampler2D"
+  else: {.error: "Unsupported data type on GPU".}
+
+func VkType[T: SupportedGPUType](value: T): VkFormat =
+  when T is float32: VK_FORMAT_R32_SFLOAT
+  elif T is float64: VK_FORMAT_R64_SFLOAT
+  elif T is int8: VK_FORMAT_R8_SINT
+  elif T is int16: VK_FORMAT_R16_SINT
+  elif T is int32: VK_FORMAT_R32_SINT
+  elif T is int64: VK_FORMAT_R64_SINT
+  elif T is uint8: VK_FORMAT_R8_UINT
+  elif T is uint16: VK_FORMAT_R16_UINT
+  elif T is uint32: VK_FORMAT_R32_UINT
+  elif T is uint64: VK_FORMAT_R64_UINT
+  elif T is TVec2[int32]: VK_FORMAT_R32G32_SINT
+  elif T is TVec2[int64]: VK_FORMAT_R64G64_SINT
+  elif T is TVec3[int32]: VK_FORMAT_R32G32B32_SINT
+  elif T is TVec3[int64]: VK_FORMAT_R64G64B64_SINT
+  elif T is TVec4[int32]: VK_FORMAT_R32G32B32A32_SINT
+  elif T is TVec4[int64]: VK_FORMAT_R64G64B64A64_SINT
+  elif T is TVec2[uint32]: VK_FORMAT_R32G32_UINT
+  elif T is TVec2[uint64]: VK_FORMAT_R64G64_UINT
+  elif T is TVec3[uint32]: VK_FORMAT_R32G32B32_UINT
+  elif T is TVec3[uint64]: VK_FORMAT_R64G64B64_UINT
+  elif T is TVec4[uint32]: VK_FORMAT_R32G32B32A32_UINT
+  elif T is TVec4[uint64]: VK_FORMAT_R64G64B64A64_UINT
+  elif T is TVec2[float32]: VK_FORMAT_R32G32_SFLOAT
+  elif T is TVec2[float64]: VK_FORMAT_R64G64_SFLOAT
+  elif T is TVec3[float32]: VK_FORMAT_R32G32B32_SFLOAT
+  elif T is TVec3[float64]: VK_FORMAT_R64G64B64_SFLOAT
+  elif T is TVec4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT
+  elif T is TVec4[float64]: VK_FORMAT_R64G64B64A64_SFLOAT
+  elif T is TMat2[float32]: VK_FORMAT_R32G32_SFLOAT
+  elif T is TMat2[float64]: VK_FORMAT_R64G64_SFLOAT
+  elif T is TMat23[float32]: VK_FORMAT_R32G32B32_SFLOAT
+  elif T is TMat23[float64]: VK_FORMAT_R64G64B64_SFLOAT
+  elif T is TMat32[float32]: VK_FORMAT_R32G32_SFLOAT
+  elif T is TMat32[float64]: VK_FORMAT_R64G64_SFLOAT
+  elif T is TMat3[float32]: VK_FORMAT_R32G32B32_SFLOAT
+  elif T is TMat3[float64]: VK_FORMAT_R64G64B64_SFLOAT
+  elif T is TMat34[float32]: VK_FORMAT_R32G32B32A32_SFLOAT
+  elif T is TMat34[float64]: VK_FORMAT_R64G64B64A64_SFLOAT
+  elif T is TMat43[float32]: VK_FORMAT_R32G32B32_SFLOAT
+  elif T is TMat43[float64]: VK_FORMAT_R64G64B64_SFLOAT
+  elif T is TMat4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT
+  elif T is TMat4[float64]: VK_FORMAT_R64G64B64A64_SFLOAT
+  else: {.error: "Unsupported data type on GPU".}
+
+
+func NumberOfVertexInputAttributeDescriptors[T: SupportedGPUType|Texture](value: T): uint32 =
+  when T is TMat2[float32] or T is TMat2[float64] or T is TMat23[float32] or T is TMat23[float64]:
+    2
+  elif T is TMat32[float32] or T is TMat32[float64] or T is TMat3[float32] or T is TMat3[float64] or T is TMat34[float32] or T is TMat34[float64]:
+    3
+  elif T is TMat43[float32] or T is TMat43[float64] or T is TMat4[float32] or T is TMat4[float64]:
+    4
+  else:
+    1
+
+func NLocationSlots[T: SupportedGPUType|Texture](value: T): uint32 =
+  #[
+  single location:
+    - any scalar
+    - any 16-bit vector
+    - any 32-bit vector
+    - any 64-bit vector that has max. 2 components
+    16-bit scalar and vector types, and
+    32-bit scalar and vector types, and
+    64-bit scalar and 2-component vector types.
+  two locations
+    64-bit three- and four-component vectors
+  ]#
+  when T is TVec3[int64] or
+    T is TVec4[int64] or
+    T is TVec3[uint64] or
+    T is TVec4[uint64] or
+    T is TVec3[float64] or
+    T is TVec4[float64] or
+    T is TMat23[float64] or
+    T is TMat3[float64] or
+    T is TMat34[float64] or
+    T is TMat43[float64] or
+    T is TMat4[float64]:
+    return 2
+  else:
+    return 1
 
 proc generateShaderSource[TShader](shader: TShader): (string, string) {.compileTime.} =
   const GLSL_VERSION = "450"
@@ -39,7 +163,7 @@
     # descriptor sets
     # need to consider 4 cases: uniform block, texture, uniform block array, texture array
     elif typeof(value) is DescriptorSet:
-      assert descriptorSetCount <= DescriptorSetType.high.int, &"{typetraits.name(TShader)}: maximum {DescriptorSetType.high} allowed"
+      assert descriptorSetCount <= DescriptorSetType.high.int, typetraits.name(TShader) & ": maximum " & $DescriptorSetType.high & " allowed"
 
       var descriptorBinding = 0
       for descriptorName, descriptorValue in fieldPairs(value.data):
@@ -111,8 +235,7 @@
     echo "shader of type ", stage
     for i, line in enumerate(shaderSource.splitlines()):
       echo "  ", i + 1, " ", line
-    # var glslExe = currentSourcePath.parentDir.parentDir.parentDir / "tools" / "glslangValidator"
-    var glslExe = currentSourcePath.parentDir / "tools" / "glslangValidator"
+    var glslExe = currentSourcePath.parentDir.parentDir.parentDir / "tools" / "glslangValidator"
     when defined(windows):
       glslExe = glslExe & "." & ExeExt
     let command = &"{glslExe} --entry-point main -V --stdin -S {stagename} -o {shaderfile}"
@@ -151,13 +274,13 @@
     codeSize: csize_t(vertexBinary.len * sizeof(uint32)),
     pCode: vertexBinary.ToCPointer,
   )
-  checkVkResult vulkan.device.vkCreateShaderModule(addr(createInfoVertex), nil, addr(result.vertexShader))
+  checkVkResult vulkan.device.vkCreateShaderModule(addr(createInfoVertex), nil, addr(result.vertexShaderModule))
   var createInfoFragment = VkShaderModuleCreateInfo(
     sType: VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
     codeSize: csize_t(fragmentBinary.len * sizeof(uint32)),
     pCode: fragmentBinary.ToCPointer,
   )
-  checkVkResult vulkan.device.vkCreateShaderModule(addr(createInfoFragment), nil, addr(result.fragmentShader))
+  checkVkResult vulkan.device.vkCreateShaderModule(addr(createInfoFragment), nil, addr(result.fragmentShaderModule))
 
 template ForVertexDataFields(shader: typed, fieldname, valuename, isinstancename, body: untyped): untyped =
   for theFieldname, value in fieldPairs(shader):
@@ -172,9 +295,8 @@
         const `isinstancename` {.inject.} = hasCustomPragma(value, InstanceAttribute)
         body
 
-proc CreatePipeline[TShader](
+proc CreatePipeline*[TShader](
   renderPass: VkRenderPass,
-  shader: ShaderObject[TShader],
   topology: VkPrimitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
   polygonMode: VkPolygonMode = VK_POLYGON_MODE_FILL,
   cullMode: VkCullModeFlagBits = VK_CULL_MODE_BACK_BIT,
@@ -183,6 +305,9 @@
 ): Pipeline[TShader] =
   # create pipeline
 
+  const shader = default(TShader)
+  let shaderObject = CompileShader(shader)
+
   for theFieldname, value in fieldPairs(default(TShader)):
     when typeof(value) is DescriptorSet:
       var layoutbindings: seq[VkDescriptorSetLayoutBinding]
@@ -218,13 +343,13 @@
     VkPipelineShaderStageCreateInfo(
       sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
       stage: VK_SHADER_STAGE_VERTEX_BIT,
-      module: shader.vertexShader,
+      module: shaderObject.vertexShaderModule,
       pName: "main",
     ),
     VkPipelineShaderStageCreateInfo(
       sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
       stage: VK_SHADER_STAGE_FRAGMENT_BIT,
-      module: shader.fragmentShader,
+      module: shaderObject.fragmentShaderModule,
       pName: "main",
     ),
   ]
--- a/semicongine/rendering/vulkan_wrappers.nim	Mon Jul 08 01:16:53 2024 +0700
+++ b/semicongine/rendering/vulkan_wrappers.nim	Mon Jul 08 16:05:06 2024 +0700
@@ -9,12 +9,56 @@
     physicalDevice*: VkPhysicalDevice
     surface: VkSurfaceKHR
     window: NativeWindow
-    queueFamilyIndex*: uint32
-    queue*: VkQueue
+    graphicsQueueFamily*: uint32
+    graphicsQueue*: VkQueue
     anisotropy*: float32 = 0 # needs to be enable during device creation
 
 var vulkan*: VulkanGlobals
 
+proc GetBestPhysicalDevice(instance: VkInstance): VkPhysicalDevice =
+  var nDevices: uint32
+  checkVkResult vkEnumeratePhysicalDevices(instance, addr(nDevices), nil)
+  var devices = newSeq[VkPhysicalDevice](nDevices)
+  checkVkResult vkEnumeratePhysicalDevices(instance, addr(nDevices), devices.ToCPointer)
+
+  var score = 0'u32
+  for pDevice in devices:
+    var props: VkPhysicalDeviceProperties
+    # CANNOT use svkGetPhysicalDeviceProperties (not initialized yet)
+    vkGetPhysicalDeviceProperties(pDevice, addr(props))
+    if props.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU and props.limits.maxImageDimension2D > score:
+      score = props.limits.maxImageDimension2D
+      result = pDevice
+
+  if score == 0:
+    for pDevice in devices:
+      var props: VkPhysicalDeviceProperties
+      # CANNOT use svkGetPhysicalDeviceProperties (not initialized yet)
+      vkGetPhysicalDeviceProperties(pDevice, addr(props))
+      if props.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU and props.limits.maxImageDimension2D > score:
+        score = props.limits.maxImageDimension2D
+        result = pDevice
+
+  assert score > 0, "Unable to find integrated or discrete GPU"
+
+proc GetQueueFamily(pDevice: VkPhysicalDevice, qType: VkQueueFlagBits): uint32 =
+  var nQueuefamilies: uint32
+  vkGetPhysicalDeviceQueueFamilyProperties(pDevice, addr nQueuefamilies, nil)
+  var queuFamilies = newSeq[VkQueueFamilyProperties](nQueuefamilies)
+  vkGetPhysicalDeviceQueueFamilyProperties(pDevice, addr nQueuefamilies, queuFamilies.ToCPointer)
+  for i in 0'u32 ..< nQueuefamilies:
+    if qType in toEnums(queuFamilies[i].queueFlags):
+      return i
+  assert false, &"Queue of type {qType} not found"
+
+proc svkGetDeviceQueue*(device: VkDevice, queueFamilyIndex: uint32, qType: VkQueueFlagBits): VkQueue =
+  vkGetDeviceQueue(
+    device,
+    queueFamilyIndex,
+    0,
+    addr(result),
+  )
+
 proc hasValidationLayer*(): bool =
   var n_layers: uint32
   checkVkResult vkEnumerateInstanceLayerProperties(addr(n_layers), nil)
@@ -28,6 +72,8 @@
 
 proc initVulkan*(platformLayers: seq[string], appName: string = "semicongine app") =
 
+  # instance creation
+  #
   when not defined(release):
     let requiredExtensions = REQUIRED_PLATFORM_EXTENSIONS & @["VK_KHR_surface", "VK_EXT_debug_utils"]
     let layers: seq[string] = if hasValidationLayer(): @["VK_LAYER_KHRONOS_validation"] else: @[]
@@ -38,6 +84,11 @@
   var
     layersC = allocCStringArray(layers)
     instanceExtensionsC = allocCStringArray(requiredExtensions)
+  defer:
+    deallocCStringArray(layersC)
+    deallocCStringArray(instanceExtensionsC)
+
+  var
     appinfo = VkApplicationInfo(
       sType: VK_STRUCTURE_TYPE_APPLICATION_INFO,
       pApplicationName: appName,
@@ -54,14 +105,64 @@
     )
   checkVkResult vkCreateInstance(addr(createinfo), nil, addr(vulkan.instance))
   loadVulkan(vulkan.instance)
-  deallocCStringArray(layersC)
-  deallocCStringArray(instanceExtensionsC)
+
+  # load extensions
+  #
   for extension in requiredExtensions:
     loadExtension(vulkan.instance, $extension)
   vulkan.window = CreateWindow(appName)
   vulkan.surface = CreateNativeSurface(vulkan.instance, vulkan.window)
 
 
+
+
+  # logical device creation
+
+
+
+
+  # TODO: allowing support for physical devices without hasUniformBufferStandardLayout
+  # would require us to ship different shaders, so we don't support standard layout
+  # if that will be added, check the function vulkan/shaders.nim:glslUniforms and update accordingly
+  # let hasUniformBufferStandardLayout = "VK_KHR_uniform_buffer_standard_layout" in physicalDevice.getExtensions()
+  # var deviceExtensions  = @["VK_KHR_swapchain", "VK_KHR_uniform_buffer_standard_layout"]
+  var deviceExtensions = @["VK_KHR_swapchain"]
+  for extension in deviceExtensions:
+    loadExtension(vulkan.instance, extension)
+
+  # get physical device and graphics queue family
+  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: vulkan.graphicsQueueFamily,
+      queueCount: 1,
+      pQueuePriorities: addr(priority),
+    )
+    deviceExtensionsC = allocCStringArray(deviceExtensions)
+  defer: deallocCStringArray(deviceExtensionsC)
+  var createDeviceInfo = VkDeviceCreateInfo(
+    sType: VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
+    queueCreateInfoCount: 1,
+    pQueueCreateInfos: addr(queueInfo),
+    enabledLayerCount: 0,
+    ppEnabledLayerNames: nil,
+    enabledExtensionCount: uint32(deviceExtensions.len),
+    ppEnabledExtensionNames: deviceExtensionsC,
+    pEnabledFeatures: nil,
+  )
+  checkVkResult vkCreateDevice(
+    physicalDevice = vulkan.physicalDevice,
+    pCreateInfo = addr createDeviceInfo,
+    pAllocator = nil,
+    pDevice = addr vulkan.device
+  )
+  vulkan.graphicsQueue = svkGetDeviceQueue(vulkan.device, vulkan.graphicsQueueFamily, VK_QUEUE_GRAPHICS_BIT)
+
+
 proc svkGetPhysicalDeviceProperties*(): VkPhysicalDeviceProperties =
   vkGetPhysicalDeviceProperties(vulkan.physicalDevice, addr(result))
 
@@ -120,14 +221,6 @@
   )
   checkVkResult vkCreateImage(vulkan.device, addr imageInfo, nil, addr(result))
 
-proc svkGetDeviceQueue*(device: VkDevice, queueFamilyIndex: uint32, qType: VkQueueFlagBits): VkQueue =
-  vkGetDeviceQueue(
-    device,
-    queueFamilyIndex,
-    0,
-    addr(result),
-  )
-
 proc svkGetBufferMemoryRequirements*(buffer: VkBuffer): tuple[size: uint64, alignment: uint64, memoryTypes: seq[uint32]] =
   var reqs: VkMemoryRequirements
   vkGetBufferMemoryRequirements(vulkan.device, buffer, addr(reqs))
@@ -173,7 +266,7 @@
       createInfo = VkCommandPoolCreateInfo(
         sType: VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
         flags: toBits [VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT],
-        queueFamilyIndex: vulkan.queueFamilyIndex,
+        queueFamilyIndex: vulkan.graphicsQueueFamily,
       )
     checkVkResult vkCreateCommandPool(vulkan.device, addr createInfo, nil, addr(commandBufferPool))
     var
@@ -207,7 +300,7 @@
         # flags: toBits [VK_FENCE_CREATE_SIGNALED_BIT]
       )
     checkVkResult vulkan.device.vkCreateFence(addr(fenceInfo), nil, addr(fence))
-    checkVkResult vkQueueSubmit(vulkan.queue, 1, addr(submitInfo), fence)
+    checkVkResult vkQueueSubmit(vulkan.graphicsQueue, 1, addr(submitInfo), fence)
     checkVkResult vkWaitForFences(vulkan.device, 1, addr fence, false, high(uint64))
     vkDestroyCommandPool(vulkan.device, commandBufferPool, nil)
 
--- a/test1.nim	Mon Jul 08 01:16:53 2024 +0700
+++ b/test1.nim	Mon Jul 08 16:05:06 2024 +0700
@@ -40,35 +40,11 @@
     vertexCode: string = "void main() {}"
     fragmentCode: string = "void main() {}"
 
-let w = CreateWindow("test2")
 putEnv("VK_LAYER_ENABLES", "VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_AMD,VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_NVIDIA,VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXTVK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT,VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT")
 
-# TODO: remove those ugly wrappers
-let theInstance = w.CreateInstance(
-  vulkanVersion = VK_MAKE_API_VERSION(0, 1, 3, 0),
-  instanceExtensions = @[],
-  layers = @["VK_LAYER_KHRONOS_validation"],
-)
-
-let dev = theInstance.CreateDevice(
-  theInstance.GetPhysicalDevices().FilterBestGraphics(),
-  enabledExtensions = @[],
-  theInstance.GetPhysicalDevices().FilterBestGraphics().FilterForGraphicsPresentationQueues()
-).vk
 let frameWidth = 100'u32
 let frameHeight = 100'u32
 
-# TODO: pack this stuff into a setup method and condense everything a bit
-let pDevice = theInstance.vk.GetPhysicalDevice()
-let qfi = pDevice.GetQueueFamily(VK_QUEUE_GRAPHICS_BIT)
-vulkan = VulkanGlobals(
-  instance: theInstance.vk,
-  device: dev,
-  physicalDevice: pDevice,
-  queueFamilyIndex: qfi,
-  queue: svkGetDeviceQueue(dev, qfi, VK_QUEUE_GRAPHICS_BIT)
-)
-
 var myMesh1 = MeshA(
   position: GPUArray[Vec3f, VertexBuffer](data: @[NewVec3f(0, 0, ), NewVec3f(0, 0, ), NewVec3f(0, 0, )]),
 )
@@ -98,13 +74,10 @@
   )
 )
 
-# setup for rendering (TODO: swapchain & framebuffers)
-let renderpass = CreateRenderPass(GetSurfaceFormat())
+let renderpass = CreatePresentationRenderPass()
 
 # shaders
-const shader = ShaderA()
-let shaderObject = CompileShader(shader)
-var pipeline1 = CreatePipeline(renderPass = renderpass, shader = shaderObject)
+var pipeline1 = CreatePipeline[ShaderA](renderPass = renderpass)
 
 var renderdata = InitRenderData()
 
@@ -130,8 +103,8 @@
 
 # descriptors
 echo "Writing descriptors"
-InitDescriptorSet(renderdata, pipeline1.descriptorSetLayouts[GlobalSet], myGlobals)
-InitDescriptorSet(renderdata, pipeline1.descriptorSetLayouts[MaterialSet], uniforms1)
+InitDescriptorSet(renderdata, pipeline1.GetLayoutFor(GlobalSet), myGlobals)
+InitDescriptorSet(renderdata, pipeline1.GetLayoutFor(MaterialSet), uniforms1)
 
 
 # command buffer