changeset 564:20837928dc63

add: finally working initial approach for shader definitions
author Sam <sam@basx.dev>
date Mon, 20 Mar 2023 10:25:50 +0700
parents fb42da98c1aa
children a0b01e84607d
files src/semicongine/descriptor.nim src/semicongine/vulkan/framebuffer.nim src/semicongine/vulkan/glsl.nim src/semicongine/vulkan/pipeline.nim src/semicongine/vulkan/shader.nim src/semicongine/vulkan/utils.nim src/semicongine/vulkan/vertex.nim tests/test_vulkan_wrapper.nim
diffstat 8 files changed, 280 insertions(+), 208 deletions(-) [+]
line wrap: on
line diff
--- a/src/semicongine/descriptor.nim	Fri Mar 17 01:11:58 2023 +0700
+++ b/src/semicongine/descriptor.nim	Mon Mar 20 10:25:50 2023 +0700
@@ -50,8 +50,7 @@
 
 template getDescriptorType*(v: Descriptor): auto = get(genericParams(typeof(v)), 0)
 
-func generateGLSLUniformDeclarations*[Uniforms](
-  binding: int = 0): string {.compileTime.} =
+func generateGLSLUniformDeclarations*[Uniforms](binding: int = 0): string {.compileTime.} =
   var stmtList: seq[string]
 
   when not (Uniforms is void):
--- a/src/semicongine/vulkan/framebuffer.nim	Fri Mar 17 01:11:58 2023 +0700
+++ b/src/semicongine/vulkan/framebuffer.nim	Mon Mar 20 10:25:50 2023 +0700
@@ -10,7 +10,7 @@
     vk*: VkFramebuffer
     device*: Device
 
-proc createFramebuffer*(device: Device, renderpass: RenderPass, attachments: openArray[ImageView], dimension: TVec2[uint32]): Framebuffer =
+proc createFramebuffer*(device: Device, renderPass: RenderPass, attachments: openArray[ImageView], dimension: TVec2[uint32]): Framebuffer =
   assert device.vk.valid
   assert renderpass.vk.valid
   var theattachments: seq[VkImageView]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/semicongine/vulkan/glsl.nim	Mon Mar 20 10:25:50 2023 +0700
@@ -0,0 +1,132 @@
+import std/typetraits
+import std/strformat
+import ../math/vector
+import ../math/matrix
+
+
+func getGLSLType*[T](t: T): string {.compileTime.} =
+  # todo: likely not correct as we would need to enable some 
+  # extensions somewhere (Vulkan/GLSL compiler?) to have 
+  # everything work as intended. Or maybe the GPU driver does
+  # some automagic conversion stuf..
+  when T is uint8:         "uint"
+  elif T is int8:          "int"
+  elif T is uint16:        "uint"
+  elif T is int16:         "int"
+  elif T is uint32:        "uint"
+  elif T is int32:         "int"
+  elif T is uint64:        "uint"
+  elif T is int64:         "int"
+  elif T is float32:       "float"
+  elif T is float64:       "double"
+
+  elif T is TVec2[uint8]:   "uvec2"
+  elif T is TVec2[int8]:    "ivec2"
+  elif T is TVec2[uint16]:  "uvec2"
+  elif T is TVec2[int16]:   "ivec2"
+  elif T is TVec2[uint32]:  "uvec2"
+  elif T is TVec2[int32]:   "ivec2"
+  elif T is TVec2[uint64]:  "uvec2"
+  elif T is TVec2[int64]:   "ivec2"
+  elif T is TVec2[float32]: "vec2"
+  elif T is TVec2[float64]: "dvec2"
+
+  elif T is TVec3[uint8]:   "uvec3"
+  elif T is TVec3[int8]:    "ivec3"
+  elif T is TVec3[uint16]:  "uvec3"
+  elif T is TVec3[int16]:   "ivec3"
+  elif T is TVec3[uint32]:  "uvec3"
+  elif T is TVec3[int32]:   "ivec3"
+  elif T is TVec3[uint64]:  "uvec3"
+  elif T is TVec3[int64]:   "ivec3"
+  elif T is TVec3[float32]: "vec3"
+  elif T is TVec3[float64]: "dvec3"
+
+  elif T is TVec4[uint8]:   "uvec4"
+  elif T is TVec4[int8]:    "ivec4"
+  elif T is TVec4[uint16]:  "uvec4"
+  elif T is TVec4[int16]:   "ivec4"
+  elif T is TVec4[uint32]:  "uvec4"
+  elif T is TVec4[int32]:   "ivec4"
+  elif T is TVec4[uint64]:  "uvec4"
+  elif T is TVec4[int64]:   "ivec4"
+  elif T is TVec4[float32]: "vec4"
+  elif T is TVec4[float64]: "dvec4"
+
+  elif T is TMat22[float32]: "mat2"
+  elif T is TMat23[float32]: "mat32"
+  elif T is TMat32[float32]: "mat23"
+  elif T is TMat33[float32]: "mat3"
+  elif T is TMat34[float32]: "mat43"
+  elif T is TMat43[float32]: "mat34"
+  elif T is TMat44[float32]: "mat4"
+
+  elif T is TMat22[float64]: "dmat2"
+  elif T is TMat23[float64]: "dmat32"
+  elif T is TMat32[float64]: "dmat23"
+  elif T is TMat33[float64]: "dmat3"
+  elif T is TMat34[float64]: "dmat43"
+  elif T is TMat43[float64]: "dmat34"
+  elif T is TMat44[float64]: "dmat4"
+
+
+# return the number of elements into which larger types are divided
+func compositeAttributesNumber*[T](value: T): int =
+  when T is TMat33[float32]:
+    3
+  elif T is TMat44[float32]:
+    4
+  else:
+    1
+
+
+# from https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap15.html
+func nLocationSlots*[T](value: T): uint32 =
+  when (T is TVec3[float64] or T is TVec3[uint64] or T is TVec4[float64] or T is TVec4[float64]):
+    return 2
+  elif T is SomeNumber or T is TVec:
+    return 1
+  else:
+    raise newException(Exception, "Unsupported vertex attribute type")
+
+
+# return the type into which larger types are divided
+func compositeAttribute*[T](value: T): auto =
+  when T is TMat33[float32]:
+    Vec3()
+  elif T is TMat44[float32]:
+    Vec4()
+  else:
+    value
+
+func glslInput*[T](): seq[string] {.compileTime.} =
+  when not (T is void):
+    var i = 0'u32
+    for fieldname, value in default(T).fieldPairs:
+      let glsltype = getGLSLType(value)
+      let thename = fieldname
+      result.add &"layout(location = {i}) in {glsltype} {thename};"
+      for j in 0 ..< compositeAttributesNumber(value):
+        i += nLocationSlots(compositeAttribute(value))
+
+func glslUniforms*[T](): seq[string] {.compileTime.} =
+  # currently only a single uniform block supported, therefore binding = 0
+  when not (T is void):
+    let uniformName = name(T)
+    result.add(&"layout(binding = 0) uniform T{uniformName} {{")
+    for fieldname, value in default(T).fieldPairs:
+      let glsltype = getGLSLType(value)
+      let thename = fieldname
+      result.add(&"    {glsltype} {thename};")
+    result.add(&"}} {uniformName};")
+
+func glslOutput*[T](): seq[string] {.compileTime.} =
+  when not (T is void):
+    var i = 0'u32
+    for fieldname, value in default(T).fieldPairs:
+      let glsltype = getGLSLType(value)
+      let thename = fieldname
+      result.add &"layout(location = {i}) out {glsltype} {thename};"
+      i += 1
+  else:
+    result
--- a/src/semicongine/vulkan/pipeline.nim	Fri Mar 17 01:11:58 2023 +0700
+++ b/src/semicongine/vulkan/pipeline.nim	Mon Mar 20 10:25:50 2023 +0700
@@ -13,7 +13,7 @@
     descriptorLayout: VkDescriptorSetLayout
 
 
-proc createPipeline*(renderPass: RenderPass, vertexShader: Shader, fragmentShader: Shader): Pipeline =
+proc createPipeline*[VertexShader: Shader, FragmentShader: Shader](renderPass: RenderPass, vertexShader: VertexShader, fragmentShader: FragmentShader): Pipeline =
   assert renderPass.vk.valid
   assert renderPass.device.vk.valid
   result.device = renderPass.device
--- a/src/semicongine/vulkan/shader.nim	Fri Mar 17 01:11:58 2023 +0700
+++ b/src/semicongine/vulkan/shader.nim	Mon Mar 20 10:25:50 2023 +0700
@@ -7,104 +7,39 @@
 import std/strutils
 import std/compilesettings
 
+import ../math
 import ./api
 import ./device
+import ./vertex
+import ./glsl
+import ./utils
 
 let logger = newConsoleLogger()
 addHandler(logger)
 
 type
-  Shader*[InputAttributes, Uniforms] = object
+  Shader*[Inputs, Uniforms, Outputs] = object
     device: Device
     vk*: VkShaderModule
     entrypoint*: string
-    inputs*: InputAttributes
-    uniforms*: Uniforms
-    binary*: seq[uint32]
+
 
-# produce ast for: static shader string, inputs, uniforms, entrypoint
-
-dumpAstGen:
-  block:
-    const test = 1
+proc compileGLSLToSPIRV*(stage: VkShaderStageFlagBits, shaderSource: string, entrypoint: string): seq[uint32] {.compileTime.} =
 
-macro shader*(inputattributes: typed, uniforms: typed, device: Device, body: untyped): untyped =
-  var shadertype: NimNode
-  var entrypoint: NimNode
-  var version: NimNode
-  var code: NimNode
-  for node in body:
-    if node.kind == nnkCall and node[0].kind == nnkIdent and node[0].strVal == "shadertype":
-      expectKind(node[1], nnkStmtList)
-      expectKind(node[1][0], nnkIdent)
-      shadertype = node[1][0]
-    if node.kind == nnkCall and node[0].kind == nnkIdent and node[0].strVal == "entrypoint":
-      expectKind(node[1], nnkStmtList)
-      expectKind(node[1][0], nnkStrLit)
-      entrypoint = node[1][0]
-    if node.kind == nnkCall and node[0].kind == nnkIdent and node[0].strVal == "version":
-      expectKind(node[1], nnkStmtList)
-      expectKind(node[1][0], nnkIntLit)
-      version = node[1][0]
-    if node.kind == nnkCall and node[0].kind == nnkIdent and node[0].strVal == "code":
-      expectKind(node[1], nnkStmtList)
-      expectKind(node[1][0], {nnkStrLit, nnkTripleStrLit})
-      code = node[1][0]
-  var shadercode: seq[string]
-  shadercode.add &"#version {version.intVal}"
-  shadercode.add &"""void {entrypoint.strVal}(){{
-{code}
-}}"""
-  
-  return nnkBlockStmt.newTree(
-    newEmptyNode(),
-    nnkStmtList.newTree(
-        nnkConstSection.newTree(
-          nnkConstDef.newTree(
-            newIdentNode("shaderbinary"),
-            newEmptyNode(),
-            newCall("compileGLSLToSPIRV", shadertype, newStrLitNode(shadercode.join("\n")), entrypoint)
-          )
-        ),
-        nnkObjConstr.newTree(
-          nnkBracketExpr.newTree(
-            newIdentNode("Shader"),
-            inputattributes,
-            uniforms,
-          ),
-          nnkExprColonExpr.newTree(newIdentNode("device"), device),
-          nnkExprColonExpr.newTree(newIdentNode("entrypoint"), entrypoint),
-          nnkExprColonExpr.newTree(newIdentNode("binary"), newIdentNode("shaderbinary")),
+  func stage2string(stage: VkShaderStageFlagBits): string {.compileTime.} =
+    case stage
+    of VK_SHADER_STAGE_VERTEX_BIT: "vert"
+    of VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: "tesc"
+    of VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: "tese"
+    of VK_SHADER_STAGE_GEOMETRY_BIT: "geom"
+    of VK_SHADER_STAGE_FRAGMENT_BIT: "frag"
+    of VK_SHADER_STAGE_COMPUTE_BIT: "comp"
+    else: ""
 
-          # vk*: VkShaderModule
-          # inputs*: InputAttributes
-          # uniforms*: Uniforms
-        )
-      )
-    )
-
-proc staticExecChecked(command: string, input = ""): string {.compileTime.} =
-  let (output, exitcode) = gorgeEx(
-      command = command,
-      input = input)
-  if exitcode != 0:
-    raise newException(Exception, &"Running '{command}' produced exit code: {exitcode}" & output)
-  return output
-
-func stage2string(stage: VkShaderStageFlagBits): string {.compileTime.} =
-  case stage
-  of VK_SHADER_STAGE_VERTEX_BIT: "vert"
-  of VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: "tesc"
-  of VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: "tese"
-  of VK_SHADER_STAGE_GEOMETRY_BIT: "geom"
-  of VK_SHADER_STAGE_FRAGMENT_BIT: "frag"
-  of VK_SHADER_STAGE_COMPUTE_BIT: "comp"
-  else: ""
-
-proc compileGLSLToSPIRV*(stage: static VkShaderStageFlagBits, shaderSource: static string, entrypoint: static string): seq[uint32] {.compileTime.} =
   when defined(nimcheck): # will not run if nimcheck is running
     return result
-  const
+
+  let
     stagename = stage2string(stage)
     shaderHash = hash(shaderSource)
     # cross compilation for windows workaround, sorry computer
@@ -135,13 +70,35 @@
     )
     i += 4
 
-proc loadShaderCode*(device: Device, binary: var seq[uint32]): VkShaderModule =
+
+proc shaderCode*[Inputs, Uniforms, Outputs](shadertype: VkShaderStageFlagBits, version: int, entrypoint: string, body: seq[string]): seq[uint32] {.compileTime.} =
+  var code = @[&"#version {version}", ""] &
+    glslInput[Inputs]() & @[""] &
+    glslUniforms[Uniforms]() & @[""] &
+    glslOutput[Outputs]() & @[""] &
+    @[&"void {entrypoint}(){{"] &
+    body &
+    @[&"}}"]
+  compileGLSLToSPIRV(shadertype, code.join("\n"), entrypoint)
+
+
+proc shaderCode*[Inputs, Uniforms, Outputs](shadertype: VkShaderStageFlagBits, version: int, entrypoint: string, body: string): seq[uint32] {.compileTime.} =
+  return shaderCode[Inputs, Uniforms, Outputs](shadertype, version, entrypoint, @[body])
+
+
+proc createShader*[Inputs, Uniforms, Outputs](device: Device, entrypoint: string, binary: seq[uint32]): Shader[Inputs, Uniforms, Outputs] =
+  assert device.vk.valid
+  assert len(binary) > 0
+
+  result.device = device
+  result.entrypoint = entrypoint
+  var bin = binary
   var createInfo = VkShaderModuleCreateInfo(
     sType: VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
-    codeSize: uint(binary.len * sizeof(uint32)),
-    pCode: if binary.len > 0: addr(binary[0]) else: nil,
+    codeSize: uint(bin.len * sizeof(uint32)),
+    pCode: addr(bin[0]),
   )
-  checkVkResult vkCreateShaderModule(device.vk, addr(createInfo), nil, addr(result))
+  checkVkResult vkCreateShaderModule(device.vk, addr(createInfo), nil, addr(result.vk))
 
 proc getPipelineInfo*(shader: Shader): VkPipelineShaderStageCreateInfo =
   VkPipelineShaderStageCreateInfo(
@@ -156,3 +113,81 @@
   assert shader.vk.valid
   shader.device.vk.vkDestroyShaderModule(shader.vk, nil)
   shader.vk.reset
+
+
+func getVkFormat[T](value: T): VkFormat =
+  when T is uint8: VK_FORMAT_R8_UINT
+  elif T is int8: VK_FORMAT_R8_SINT
+  elif T is uint16: VK_FORMAT_R16_UINT
+  elif T is int16: VK_FORMAT_R16_SINT
+  elif T is uint32: VK_FORMAT_R32_UINT
+  elif T is int32: VK_FORMAT_R32_SINT
+  elif T is uint64: VK_FORMAT_R64_UINT
+  elif T is int64: VK_FORMAT_R64_SINT
+  elif T is float32: VK_FORMAT_R32_SFLOAT
+  elif T is float64: VK_FORMAT_R64_SFLOAT
+  elif T is TVec2[uint8]: VK_FORMAT_R8G8_UINT
+  elif T is TVec2[int8]: VK_FORMAT_R8G8_SINT
+  elif T is TVec2[uint16]: VK_FORMAT_R16G16_UINT
+  elif T is TVec2[int16]: VK_FORMAT_R16G16_SINT
+  elif T is TVec2[uint32]: VK_FORMAT_R32G32_UINT
+  elif T is TVec2[int32]: VK_FORMAT_R32G32_SINT
+  elif T is TVec2[uint64]: VK_FORMAT_R64G64_UINT
+  elif T is TVec2[int64]: VK_FORMAT_R64G64_SINT
+  elif T is TVec2[float32]: VK_FORMAT_R32G32_SFLOAT
+  elif T is TVec2[float64]: VK_FORMAT_R64G64_SFLOAT
+  elif T is TVec3[uint8]: VK_FORMAT_R8G8B8_UINT
+  elif T is TVec3[int8]: VK_FORMAT_R8G8B8_SINT
+  elif T is TVec3[uint16]: VK_FORMAT_R16G16B16_UINT
+  elif T is TVec3[int16]: VK_FORMAT_R16G16B16_SINT
+  elif T is TVec3[uint32]: VK_FORMAT_R32G32B32_UINT
+  elif T is TVec3[int32]: VK_FORMAT_R32G32B32_SINT
+  elif T is TVec3[uint64]: VK_FORMAT_R64G64B64_UINT
+  elif T is TVec3[int64]: VK_FORMAT_R64G64B64_SINT
+  elif T is TVec3[float32]: VK_FORMAT_R32G32B32_SFLOAT
+  elif T is TVec3[float64]: VK_FORMAT_R64G64B64_SFLOAT
+  elif T is TVec4[uint8]: VK_FORMAT_R8G8B8A8_UINT
+  elif T is TVec4[int8]: VK_FORMAT_R8G8B8A8_SINT
+  elif T is TVec4[uint16]: VK_FORMAT_R16G16B16A16_UINT
+  elif T is TVec4[int16]: VK_FORMAT_R16G16B16A16_SINT
+  elif T is TVec4[uint32]: VK_FORMAT_R32G32B32A32_UINT
+  elif T is TVec4[int32]: VK_FORMAT_R32G32B32A32_SINT
+  elif T is TVec4[uint64]: VK_FORMAT_R64G64B64A64_UINT
+  elif T is TVec4[int64]: VK_FORMAT_R64G64B64A64_SINT
+  elif T is TVec4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT
+  elif T is TVec4[float64]: VK_FORMAT_R64G64B64A64_SFLOAT
+  else: {.error: "Unsupported vertex attribute type".}
+
+
+proc getVertexInputInfo*[Input, Uniforms, Output](
+  shader: Shader[Input, Uniforms, Output],
+  bindings: var seq[VkVertexInputBindingDescription],
+  attributes: var seq[VkVertexInputAttributeDescription],
+): VkPipelineVertexInputStateCreateInfo =
+  var location = 0'u32
+  var binding = 0'u32
+
+  for name, value in default(Input).fieldPairs:
+    bindings.add VkVertexInputBindingDescription(
+      binding: binding,
+      stride: uint32(sizeof(value)),
+      inputRate: if value.hasCustomPragma(PerInstance): VK_VERTEX_INPUT_RATE_INSTANCE else: VK_VERTEX_INPUT_RATE_VERTEX,
+    )
+    # allows to submit larger data structures like Mat44, for most other types will be 1
+    for i in 0 ..< compositeAttributesNumber(value):
+      attributes.add VkVertexInputAttributeDescription(
+        binding: binding,
+        location: location,
+        format: getVkFormat(compositeAttribute(value)),
+        offset: uint32(i * sizeof(compositeAttribute(value))),
+      )
+      location += nLocationSlots(compositeAttribute(value))
+    inc binding
+
+  return VkPipelineVertexInputStateCreateInfo(
+    sType: VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
+    vertexBindingDescriptionCount: uint32(bindings.len),
+    pVertexBindingDescriptions: bindings.toCPointer,
+    vertexAttributeDescriptionCount: uint32(attributes.len),
+    pVertexAttributeDescriptions: attributes.toCPointer,
+  )
--- a/src/semicongine/vulkan/utils.nim	Fri Mar 17 01:11:58 2023 +0700
+++ b/src/semicongine/vulkan/utils.nim	Mon Mar 20 10:25:50 2023 +0700
@@ -1,4 +1,6 @@
 import std/strutils
+import std/os
+import std/strformat
 
 func cleanString*(str: openArray[char]): string =
   for i in 0 ..< len(str):
@@ -8,3 +10,13 @@
 
 func toCPointer*[T](list: var seq[T]): ptr T =
   if list.len > 0: addr list[0] else: nil
+
+proc staticExecChecked*(command: string, input = ""): string {.compileTime.} =
+  let (output, exitcode) = gorgeEx(
+      command = command,
+      input = input)
+  if exitcode != 0:
+    raise newException(Exception, &"Running '{command}' produced exit code: {exitcode}" & output)
+  return output
+
+
--- a/src/semicongine/vulkan/vertex.nim	Fri Mar 17 01:11:58 2023 +0700
+++ b/src/semicongine/vulkan/vertex.nim	Mon Mar 20 10:25:50 2023 +0700
@@ -3,112 +3,8 @@
 
 import ../math
 import ./api
-import ./utils
-import ./shader
 
 
 # add pragma to fields of the VertexType that represent per instance attributes
 template PerInstance*() {.pragma.}
 
-# from https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap15.html
-func nLocationSlots[T](value: T): uint32 =
-  when (T is TVec3[float64] or T is TVec3[uint64] or T is TVec4[float64] or T is TVec4[float64]):
-    return 2
-  elif T is SomeNumber or T is TVec:
-    return 1
-  else:
-    raise newException(Exception, "Unsupported vertex attribute type")
-
-# return the type into which larger types are divided
-func compositeAttribute[T](value: T): auto =
-  when T is TMat33[float32]:
-    Vec3()
-  elif T is TMat44[float32]:
-    Vec4()
-  else:
-    value
-
-# return the number of elements into which larger types are divided
-func compositeAttributesNumber[T](value: T): int =
-  when T is TMat33[float32]:
-    3
-  elif T is TMat44[float32]:
-    4
-  else:
-    1
-
-func getVkFormat[T](value: T): VkFormat =
-  when T is uint8: VK_FORMAT_R8_UINT
-  elif T is int8: VK_FORMAT_R8_SINT
-  elif T is uint16: VK_FORMAT_R16_UINT
-  elif T is int16: VK_FORMAT_R16_SINT
-  elif T is uint32: VK_FORMAT_R32_UINT
-  elif T is int32: VK_FORMAT_R32_SINT
-  elif T is uint64: VK_FORMAT_R64_UINT
-  elif T is int64: VK_FORMAT_R64_SINT
-  elif T is float32: VK_FORMAT_R32_SFLOAT
-  elif T is float64: VK_FORMAT_R64_SFLOAT
-  elif T is TVec2[uint8]: VK_FORMAT_R8G8_UINT
-  elif T is TVec2[int8]: VK_FORMAT_R8G8_SINT
-  elif T is TVec2[uint16]: VK_FORMAT_R16G16_UINT
-  elif T is TVec2[int16]: VK_FORMAT_R16G16_SINT
-  elif T is TVec2[uint32]: VK_FORMAT_R32G32_UINT
-  elif T is TVec2[int32]: VK_FORMAT_R32G32_SINT
-  elif T is TVec2[uint64]: VK_FORMAT_R64G64_UINT
-  elif T is TVec2[int64]: VK_FORMAT_R64G64_SINT
-  elif T is TVec2[float32]: VK_FORMAT_R32G32_SFLOAT
-  elif T is TVec2[float64]: VK_FORMAT_R64G64_SFLOAT
-  elif T is TVec3[uint8]: VK_FORMAT_R8G8B8_UINT
-  elif T is TVec3[int8]: VK_FORMAT_R8G8B8_SINT
-  elif T is TVec3[uint16]: VK_FORMAT_R16G16B16_UINT
-  elif T is TVec3[int16]: VK_FORMAT_R16G16B16_SINT
-  elif T is TVec3[uint32]: VK_FORMAT_R32G32B32_UINT
-  elif T is TVec3[int32]: VK_FORMAT_R32G32B32_SINT
-  elif T is TVec3[uint64]: VK_FORMAT_R64G64B64_UINT
-  elif T is TVec3[int64]: VK_FORMAT_R64G64B64_SINT
-  elif T is TVec3[float32]: VK_FORMAT_R32G32B32_SFLOAT
-  elif T is TVec3[float64]: VK_FORMAT_R64G64B64_SFLOAT
-  elif T is TVec4[uint8]: VK_FORMAT_R8G8B8A8_UINT
-  elif T is TVec4[int8]: VK_FORMAT_R8G8B8A8_SINT
-  elif T is TVec4[uint16]: VK_FORMAT_R16G16B16A16_UINT
-  elif T is TVec4[int16]: VK_FORMAT_R16G16B16A16_SINT
-  elif T is TVec4[uint32]: VK_FORMAT_R32G32B32A32_UINT
-  elif T is TVec4[int32]: VK_FORMAT_R32G32B32A32_SINT
-  elif T is TVec4[uint64]: VK_FORMAT_R64G64B64A64_UINT
-  elif T is TVec4[int64]: VK_FORMAT_R64G64B64A64_SINT
-  elif T is TVec4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT
-  elif T is TVec4[float64]: VK_FORMAT_R64G64B64A64_SFLOAT
-  else: {.error: "Unsupported vertex attribute type".}
-
-proc getVertexInputInfo*(
-  shader: Shader,
-  bindings: var seq[VkVertexInputBindingDescription],
-  attributes: var seq[VkVertexInputAttributeDescription],
-): VkPipelineVertexInputStateCreateInfo =
-  var location = 0'u32
-  var binding = 0'u32
-
-  for name, value in shader.inputs.fieldPairs:
-    bindings.add VkVertexInputBindingDescription(
-      binding: binding,
-      stride: uint32(sizeof(value)),
-      inputRate: if value.hasCustomPragma(PerInstance): VK_VERTEX_INPUT_RATE_INSTANCE else: VK_VERTEX_INPUT_RATE_VERTEX,
-    )
-    # allows to submit larger data structures like Mat44, for most other types will be 1
-    for i in 0 ..< compositeAttributesNumber(value):
-      attributes.add VkVertexInputAttributeDescription(
-        binding: binding,
-        location: location,
-        format: getVkFormat(compositeAttribute(value)),
-        offset: uint32(i * sizeof(compositeAttribute(value))),
-      )
-      location += nLocationSlots(compositeAttribute(value))
-    inc binding
-
-  return VkPipelineVertexInputStateCreateInfo(
-    sType: VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
-    vertexBindingDescriptionCount: uint32(bindings.len),
-    pVertexBindingDescriptions: bindings.toCPointer,
-    vertexAttributeDescriptionCount: uint32(attributes.len),
-    pVertexAttributeDescriptions: attributes.toCPointer,
-  )
--- a/tests/test_vulkan_wrapper.nim	Fri Mar 17 01:11:58 2023 +0700
+++ b/tests/test_vulkan_wrapper.nim	Mon Mar 20 10:25:50 2023 +0700
@@ -7,8 +7,12 @@
 type
   Vertex = object
     pos: Vec3
+  FragmentInput = object
+    fragpos: Vec3
   Uniforms = object
     time: float32
+  Pixel = object
+    color: Vec4
 
 
 when isMainModule:
@@ -76,20 +80,12 @@
     renderFinished = device.createSemaphore()
     inflight = device.createFence()
 
-  var vertexshader = shader(Vertex, Uniforms, device):
-    shadertype: VK_SHADER_STAGE_VERTEX_BIT
-    entrypoint: "main"
-    version: 450
-    code: """"""
-  var fragmentshader = shader(Vertex, Uniforms, device):
-    shadertype: VK_SHADER_STAGE_FRAGMENT_BIT
-    entrypoint: "main"
-    version: 450
-    code: ""
+  const vertexBinary = shaderCode[Vertex, Uniforms, FragmentInput](shadertype=VK_SHADER_STAGE_VERTEX_BIT, version=450, entrypoint="main", "fragpos = pos;")
+  const fragmentBinary = shaderCode[FragmentInput, void, Pixel](shadertype=VK_SHADER_STAGE_FRAGMENT_BIT, version=450, entrypoint="main", "color = vec4(1, 1, 1, 0);")
+  var vertexshader = createShader[Vertex, Uniforms, FragmentInput](device, "main", vertexBinary)
+  var fragmentshader = createShader[FragmentInput, void, Pixel](device, "main", fragmentBinary)
 
-  #var vertexshader = loadShaderCode[Vertex, Uniforms](device, vertexshadercode)
-  #var fragmentshader = loadShaderCode[Vertex, Uniforms](device, fragmentshadercode)
-  #var pipeline = renderpass.createPipeline(vertexshaderhandle, fragmentshaderhandle)
+  var pipeline = renderpass.createPipeline(vertexshader, fragmentshader)
 
   echo "All successfull"
   echo "Start cleanup"
@@ -98,6 +94,8 @@
   #pipeline.destroy()
   #vertexshader.destroy()
   #fragmentshader.destroy()
+  vertexshader.destroy()
+  fragmentshader.destroy()
   inflight.destroy()
   imageAvailable.destroy()
   renderFinished.destroy()